/ src / client / linux / minidump_writer / linux_core_dumper_unittest.cc
linux_core_dumper_unittest.cc
  1  // Copyright 2012 Google LLC
  2  //
  3  // Redistribution and use in source and binary forms, with or without
  4  // modification, are permitted provided that the following conditions are
  5  // met:
  6  //
  7  //     * Redistributions of source code must retain the above copyright
  8  // notice, this list of conditions and the following disclaimer.
  9  //     * Redistributions in binary form must reproduce the above
 10  // copyright notice, this list of conditions and the following disclaimer
 11  // in the documentation and/or other materials provided with the
 12  // distribution.
 13  //     * Neither the name of Google LLC nor the names of its
 14  // contributors may be used to endorse or promote products derived from
 15  // this software without specific prior written permission.
 16  //
 17  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 18  // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 19  // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 20  // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 21  // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 22  // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 23  // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 24  // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 25  // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 26  // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 27  // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 28  
 29  // linux_core_dumper_unittest.cc:
 30  // Unit tests for google_breakpad::LinuxCoreDumoer.
 31  
 32  #ifdef HAVE_CONFIG_H
 33  #include <config.h>  // Must come first
 34  #endif
 35  
 36  #include <string>
 37  
 38  #include "breakpad_googletest_includes.h"
 39  #include "client/linux/minidump_writer/linux_core_dumper.h"
 40  #include "common/linux/tests/crash_generator.h"
 41  #include "common/using_std_string.h"
 42  
 43  using namespace google_breakpad;
 44  
 45  TEST(LinuxCoreDumperTest, GetMappingAbsolutePath) {
 46    const LinuxCoreDumper dumper(getpid(), "core", "/tmp", "/mnt/root");
 47    const MappingInfo mapping = {0, 0, {0, 0}, 0, false, "/usr/lib/libc.so"};
 48  
 49    char path[PATH_MAX];
 50    dumper.GetMappingAbsolutePath(mapping, path);
 51  
 52    EXPECT_STREQ("/mnt/root/usr/lib/libc.so", path);
 53  }
 54  
 55  TEST(LinuxCoreDumperTest, BuildProcPath) {
 56    const pid_t pid = getpid();
 57    const char procfs_path[] = "/procfs_copy";
 58    LinuxCoreDumper dumper(getpid(), "core_file", procfs_path);
 59  
 60    char maps_path[NAME_MAX] = "";
 61    char maps_path_expected[NAME_MAX];
 62    snprintf(maps_path_expected, sizeof(maps_path_expected),
 63             "%s/maps", procfs_path);
 64    EXPECT_TRUE(dumper.BuildProcPath(maps_path, pid, "maps"));
 65    EXPECT_STREQ(maps_path_expected, maps_path);
 66  
 67    EXPECT_FALSE(dumper.BuildProcPath(NULL, pid, "maps"));
 68    EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, ""));
 69    EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, NULL));
 70  
 71    char long_node[NAME_MAX];
 72    size_t long_node_len = NAME_MAX - strlen(procfs_path) - 1;
 73    memset(long_node, 'a', long_node_len);
 74    long_node[long_node_len] = '\0';
 75    EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, long_node));
 76  }
 77  
 78  TEST(LinuxCoreDumperTest, VerifyDumpWithMultipleThreads) {
 79    CrashGenerator crash_generator;
 80    if (!crash_generator.HasDefaultCorePattern()) {
 81      fprintf(stderr, "LinuxCoreDumperTest.VerifyDumpWithMultipleThreads test "
 82              "is skipped due to non-default core pattern\n");
 83      return;
 84    }
 85  
 86    const unsigned kNumOfThreads = 3;
 87    const unsigned kCrashThread = 1;
 88    const int kCrashSignal = SIGABRT;
 89    pid_t child_pid;
 90    ASSERT_TRUE(crash_generator.CreateChildCrash(kNumOfThreads, kCrashThread,
 91                                                 kCrashSignal, &child_pid));
 92  
 93    const string core_file = crash_generator.GetCoreFilePath();
 94    const string procfs_path = crash_generator.GetDirectoryOfProcFilesCopy();
 95  
 96  #if defined(__ANDROID__)
 97    struct stat st;
 98    if (stat(core_file.c_str(), &st) != 0) {
 99      fprintf(stderr, "LinuxCoreDumperTest.VerifyDumpWithMultipleThreads test is "
100              "skipped due to no core file being generated\n");
101      return;
102    }
103  #endif
104  
105    LinuxCoreDumper dumper(child_pid, core_file.c_str(), procfs_path.c_str());
106  
107    EXPECT_TRUE(dumper.Init());
108  
109    EXPECT_TRUE(dumper.IsPostMortem());
110  
111    // These are no-ops and should always return true.
112    EXPECT_TRUE(dumper.ThreadsSuspend());
113    EXPECT_TRUE(dumper.ThreadsResume());
114  
115    // Linux does not set the crash address with SIGABRT, so make sure it always
116    // sets the crash address to 0.
117    EXPECT_EQ(0U, dumper.crash_address());
118    EXPECT_EQ(kCrashSignal, dumper.crash_signal());
119    EXPECT_EQ(crash_generator.GetThreadId(kCrashThread),
120              dumper.crash_thread());
121  
122  #if defined(THREAD_SANITIZER)
123    EXPECT_GE(dumper.threads().size(), kNumOfThreads);
124  #else
125    EXPECT_EQ(dumper.threads().size(), kNumOfThreads);
126  #endif
127    for (unsigned i = 0; i < kNumOfThreads; ++i) {
128      ThreadInfo info;
129      EXPECT_TRUE(dumper.GetThreadInfoByIndex(i, &info));
130      const void* stack;
131      size_t stack_len;
132      EXPECT_TRUE(dumper.GetStackInfo(&stack, &stack_len, info.stack_pointer));
133      EXPECT_EQ(getpid(), info.ppid);
134    }
135  }
136  
137  TEST(LinuxCoreDumperTest, VerifyExceptionDetails) {
138    CrashGenerator crash_generator;
139    if (!crash_generator.HasDefaultCorePattern()) {
140      fprintf(stderr, "LinuxCoreDumperTest.VerifyDumpWithMultipleThreads test "
141              "is skipped due to non-default core pattern\n");
142      return;
143    }
144  
145  #ifndef si_syscall
146    fprintf(stderr, "LinuxCoreDumperTest.VerifyDumpWithMultipleThreads test is "
147            "skipped due to old kernel/C library headers\n");
148    return;
149  #endif
150  
151    const unsigned kNumOfThreads = 2;
152    const unsigned kCrashThread = 1;
153    const int kCrashSignal = SIGSYS;
154    pid_t child_pid;
155    ASSERT_TRUE(crash_generator.CreateChildCrash(kNumOfThreads, kCrashThread,
156                                                 kCrashSignal, &child_pid));
157  
158    const string core_file = crash_generator.GetCoreFilePath();
159    const string procfs_path = crash_generator.GetDirectoryOfProcFilesCopy();
160  
161  #if defined(__ANDROID__)
162    struct stat st;
163    if (stat(core_file.c_str(), &st) != 0) {
164      fprintf(stderr, "LinuxCoreDumperTest.VerifyExceptionDetails test is "
165              "skipped due to no core file being generated\n");
166      return;
167    }
168  #endif
169  
170    LinuxCoreDumper dumper(child_pid, core_file.c_str(), procfs_path.c_str());
171  
172    EXPECT_TRUE(dumper.Init());
173  
174    EXPECT_TRUE(dumper.IsPostMortem());
175  
176  #if defined(__ANDROID__)
177    // TODO: For some reason, Android doesn't seem to pass this.
178    if (!dumper.crash_address()) {
179      fprintf(stderr, "LinuxCoreDumperTest.VerifyExceptionDetails test is "
180              "skipped due to missing signal details on Android\n");
181      return;
182    }
183  #endif
184  
185    // Check the exception details.
186    EXPECT_NE(0U, dumper.crash_address());
187    EXPECT_EQ(kCrashSignal, dumper.crash_signal());
188    EXPECT_EQ(crash_generator.GetThreadId(kCrashThread),
189              dumper.crash_thread());
190  
191    // We check the length, but not the actual fields.  We sent SIGSYS ourselves
192    // instead of the kernel, so the extended fields are garbage.
193    const std::vector<uint64_t> info(dumper.crash_exception_info());
194    EXPECT_EQ(2U, info.size());
195  }