PageRenderTime 64ms CodeModel.GetById 2ms app.highlight 57ms RepoModel.GetById 2ms app.codeStats 0ms

/thirdparty/breakpad/client/linux/minidump_writer/minidump_writer_unittest.cc

http://github.com/tomahawk-player/tomahawk
C++ | 390 lines | 268 code | 51 blank | 71 comment | 17 complexity | 9fff6321e2071169d5f183d46529e400 MD5 | raw file
  1// Copyright (c) 2011 Google Inc.
  2// All rights reserved.
  3//
  4// Redistribution and use in source and binary forms, with or without
  5// modification, are permitted provided that the following conditions are
  6// met:
  7//
  8//     * Redistributions of source code must retain the above copyright
  9// notice, this list of conditions and the following disclaimer.
 10//     * Redistributions in binary form must reproduce the above
 11// copyright notice, this list of conditions and the following disclaimer
 12// in the documentation and/or other materials provided with the
 13// distribution.
 14//     * Neither the name of Google Inc. nor the names of its
 15// contributors may be used to endorse or promote products derived from
 16// this software without specific prior written permission.
 17//
 18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 29
 30#include <fcntl.h>
 31#include <sys/poll.h>
 32#include <sys/stat.h>
 33#include <sys/syscall.h>
 34#include <sys/types.h>
 35#include <unistd.h>
 36
 37#include <string>
 38
 39#include "breakpad_googletest_includes.h"
 40#include "client/linux/handler/exception_handler.h"
 41#include "client/linux/minidump_writer/linux_dumper.h"
 42#include "client/linux/minidump_writer/minidump_writer.h"
 43#include "common/linux/eintr_wrapper.h"
 44#include "common/linux/file_id.h"
 45#include "common/linux/safe_readlink.h"
 46#include "common/tests/auto_tempdir.h"
 47#include "google_breakpad/processor/minidump.h"
 48
 49using namespace google_breakpad;
 50
 51// Length of a formatted GUID string =
 52// sizeof(MDGUID) * 2 + 4 (for dashes) + 1 (null terminator)
 53const int kGUIDStringSize = 37;
 54
 55namespace {
 56typedef testing::Test MinidumpWriterTest;
 57}
 58
 59TEST(MinidumpWriterTest, Setup) {
 60  int fds[2];
 61  ASSERT_NE(-1, pipe(fds));
 62
 63  const pid_t child = fork();
 64  if (child == 0) {
 65    close(fds[1]);
 66    char b;
 67    HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
 68    close(fds[0]);
 69    syscall(__NR_exit);
 70  }
 71  close(fds[0]);
 72
 73  ExceptionHandler::CrashContext context;
 74  memset(&context, 0, sizeof(context));
 75
 76  AutoTempDir temp_dir;
 77  std::string templ = temp_dir.path() + "/minidump-writer-unittest";
 78  // Set a non-zero tid to avoid tripping asserts.
 79  context.tid = 1;
 80  ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context)));
 81  struct stat st;
 82  ASSERT_EQ(stat(templ.c_str(), &st), 0);
 83  ASSERT_GT(st.st_size, 0u);
 84
 85  close(fds[1]);
 86}
 87
 88// Test that mapping info can be specified when writing a minidump,
 89// and that it ends up in the module list of the minidump.
 90TEST(MinidumpWriterTest, MappingInfo) {
 91  int fds[2];
 92  ASSERT_NE(-1, pipe(fds));
 93
 94  // These are defined here so the parent can use them to check the
 95  // data from the minidump afterwards.
 96  const u_int32_t kMemorySize = sysconf(_SC_PAGESIZE);
 97  const char* kMemoryName = "a fake module";
 98  const u_int8_t kModuleGUID[sizeof(MDGUID)] = {
 99    0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
100    0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
101  };
102  char module_identifier_buffer[kGUIDStringSize];
103  FileID::ConvertIdentifierToString(kModuleGUID,
104                                    module_identifier_buffer,
105                                    sizeof(module_identifier_buffer));
106  string module_identifier(module_identifier_buffer);
107  // Strip out dashes
108  size_t pos;
109  while ((pos = module_identifier.find('-')) != string::npos) {
110    module_identifier.erase(pos, 1);
111  }
112  // And append a zero, because module IDs include an "age" field
113  // which is always zero on Linux.
114  module_identifier += "0";
115
116  // Get some memory.
117  char* memory =
118    reinterpret_cast<char*>(mmap(NULL,
119                                 kMemorySize,
120                                 PROT_READ | PROT_WRITE,
121                                 MAP_PRIVATE | MAP_ANON,
122                                 -1,
123                                 0));
124  const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
125  ASSERT_TRUE(memory);
126
127  const pid_t child = fork();
128  if (child == 0) {
129    close(fds[1]);
130    char b;
131    HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
132    close(fds[0]);
133    syscall(__NR_exit);
134  }
135  close(fds[0]);
136
137  ExceptionHandler::CrashContext context;
138  memset(&context, 0, sizeof(context));
139  context.tid = 1;
140
141  AutoTempDir temp_dir;
142  std::string templ = temp_dir.path() + "/minidump-writer-unittest";
143
144  // Add information about the mapped memory.
145  MappingInfo info;
146  info.start_addr = kMemoryAddress;
147  info.size = kMemorySize;
148  info.offset = 0;
149  strcpy(info.name, kMemoryName);
150
151  MappingList mappings;
152  MappingEntry mapping;
153  mapping.first = info;
154  memcpy(mapping.second, kModuleGUID, sizeof(MDGUID));
155  mappings.push_back(mapping);
156  ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context),
157                            mappings));
158
159  // Read the minidump. Load the module list, and ensure that
160  // the mmap'ed |memory| is listed with the given module name
161  // and debug ID.
162  Minidump minidump(templ.c_str());
163  ASSERT_TRUE(minidump.Read());
164
165  MinidumpModuleList* module_list = minidump.GetModuleList();
166  ASSERT_TRUE(module_list);
167  const MinidumpModule* module =
168    module_list->GetModuleForAddress(kMemoryAddress);
169  ASSERT_TRUE(module);
170
171  EXPECT_EQ(kMemoryAddress, module->base_address());
172  EXPECT_EQ(kMemorySize, module->size());
173  EXPECT_EQ(kMemoryName, module->code_file());
174  EXPECT_EQ(module_identifier, module->debug_identifier());
175
176  close(fds[1]);
177}
178
179// Test that mapping info can be specified, and that it overrides
180// existing mappings that are wholly contained within the specified
181// range.
182TEST(MinidumpWriterTest, MappingInfoContained) {
183  int fds[2];
184  ASSERT_NE(-1, pipe(fds));
185
186  // These are defined here so the parent can use them to check the
187  // data from the minidump afterwards.
188  const u_int32_t kMemorySize = sysconf(_SC_PAGESIZE);
189  const char* kMemoryName = "a fake module";
190  const u_int8_t kModuleGUID[sizeof(MDGUID)] = {
191    0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
192    0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
193  };
194  char module_identifier_buffer[kGUIDStringSize];
195  FileID::ConvertIdentifierToString(kModuleGUID,
196                                    module_identifier_buffer,
197                                    sizeof(module_identifier_buffer));
198  string module_identifier(module_identifier_buffer);
199  // Strip out dashes
200  size_t pos;
201  while ((pos = module_identifier.find('-')) != string::npos) {
202    module_identifier.erase(pos, 1);
203  }
204  // And append a zero, because module IDs include an "age" field
205  // which is always zero on Linux.
206  module_identifier += "0";
207
208  // mmap a file
209  AutoTempDir temp_dir;
210  std::string tempfile = temp_dir.path() + "/minidump-writer-unittest-temp";
211  int fd = open(tempfile.c_str(), O_RDWR | O_CREAT, 0);
212  ASSERT_NE(-1, fd);
213  unlink(tempfile.c_str());
214  // fill with zeros
215  char buffer[kMemorySize];
216  memset(buffer, 0, kMemorySize);
217  ASSERT_EQ(kMemorySize, write(fd, buffer, kMemorySize));
218  lseek(fd, 0, SEEK_SET);
219
220  char* memory =
221    reinterpret_cast<char*>(mmap(NULL,
222                                 kMemorySize,
223                                 PROT_READ | PROT_WRITE,
224                                 MAP_PRIVATE,
225                                 fd,
226                                 0));
227  const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
228  ASSERT_TRUE(memory);
229  close(fd);
230
231  const pid_t child = fork();
232  if (child == 0) {
233    close(fds[1]);
234    char b;
235    HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
236    close(fds[0]);
237    syscall(__NR_exit);
238  }
239  close(fds[0]);
240
241  ExceptionHandler::CrashContext context;
242  memset(&context, 0, sizeof(context));
243  context.tid = 1;
244
245  std::string dumpfile = temp_dir.path() + "/minidump-writer-unittest";
246
247  // Add information about the mapped memory. Report it as being larger than
248  // it actually is.
249  MappingInfo info;
250  info.start_addr = kMemoryAddress - kMemorySize;
251  info.size = kMemorySize * 3;
252  info.offset = 0;
253  strcpy(info.name, kMemoryName);
254
255  MappingList mappings;
256  MappingEntry mapping;
257  mapping.first = info;
258  memcpy(mapping.second, kModuleGUID, sizeof(MDGUID));
259  mappings.push_back(mapping);
260  ASSERT_TRUE(
261      WriteMinidump(dumpfile.c_str(), child, &context, sizeof(context),
262                    mappings));
263
264  // Read the minidump. Load the module list, and ensure that
265  // the mmap'ed |memory| is listed with the given module name
266  // and debug ID.
267  Minidump minidump(dumpfile.c_str());
268  ASSERT_TRUE(minidump.Read());
269
270  MinidumpModuleList* module_list = minidump.GetModuleList();
271  ASSERT_TRUE(module_list);
272  const MinidumpModule* module =
273    module_list->GetModuleForAddress(kMemoryAddress);
274  ASSERT_TRUE(module);
275
276  EXPECT_EQ(info.start_addr, module->base_address());
277  EXPECT_EQ(info.size, module->size());
278  EXPECT_EQ(kMemoryName, module->code_file());
279  EXPECT_EQ(module_identifier, module->debug_identifier());
280
281  close(fds[1]);
282}
283
284TEST(MinidumpWriterTest, DeletedBinary) {
285  static const int kNumberOfThreadsInHelperProgram = 1;
286  char kNumberOfThreadsArgument[2];
287  sprintf(kNumberOfThreadsArgument, "%d", kNumberOfThreadsInHelperProgram);
288
289  // Locate helper binary next to the current binary.
290  char self_path[PATH_MAX];
291  if (!SafeReadLink("/proc/self/exe", self_path)) {
292    FAIL() << "readlink failed";
293    exit(1);
294  }
295  string helper_path(self_path);
296  size_t pos = helper_path.rfind('/');
297  if (pos == string::npos) {
298    FAIL() << "no trailing slash in path: " << helper_path;
299    exit(1);
300  }
301  helper_path.erase(pos + 1);
302  helper_path += "linux_dumper_unittest_helper";
303
304  // Copy binary to a temp file.
305  AutoTempDir temp_dir;
306  std::string binpath = temp_dir.path() + "/linux-dumper-unittest-helper";
307  char cmdline[2 * PATH_MAX];
308  sprintf(cmdline, "/bin/cp \"%s\" \"%s\"", helper_path.c_str(),
309          binpath.c_str());
310  ASSERT_EQ(0, system(cmdline));
311  ASSERT_EQ(0, chmod(binpath.c_str(), 0755));
312
313  int fds[2];
314  ASSERT_NE(-1, pipe(fds));
315
316  pid_t child_pid = fork();
317  if (child_pid == 0) {
318    // In child process.
319    close(fds[0]);
320
321    // Pass the pipe fd and the number of threads as arguments.
322    char pipe_fd_string[8];
323    sprintf(pipe_fd_string, "%d", fds[1]);
324    execl(binpath.c_str(),
325          binpath.c_str(),
326          pipe_fd_string,
327          kNumberOfThreadsArgument,
328          NULL);
329  }
330  close(fds[1]);
331  // Wait for the child process to signal that it's ready.
332  struct pollfd pfd;
333  memset(&pfd, 0, sizeof(pfd));
334  pfd.fd = fds[0];
335  pfd.events = POLLIN | POLLERR;
336
337  const int r = HANDLE_EINTR(poll(&pfd, 1, 1000));
338  ASSERT_EQ(1, r);
339  ASSERT_TRUE(pfd.revents & POLLIN);
340  uint8_t junk;
341  const int nr = HANDLE_EINTR(read(fds[0], &junk, sizeof(junk)));
342  ASSERT_EQ(sizeof(junk), nr);
343  close(fds[0]);
344
345  // Child is ready now.
346  // Unlink the test binary.
347  unlink(binpath.c_str());
348
349  ExceptionHandler::CrashContext context;
350  memset(&context, 0, sizeof(context));
351
352  std::string templ = temp_dir.path() + "/minidump-writer-unittest";
353  // Set a non-zero tid to avoid tripping asserts.
354  context.tid = 1;
355  ASSERT_TRUE(WriteMinidump(templ.c_str(), child_pid, &context,
356                            sizeof(context)));
357  kill(child_pid, SIGKILL);
358
359  struct stat st;
360  ASSERT_EQ(stat(templ.c_str(), &st), 0);
361  ASSERT_GT(st.st_size, 0u);
362
363
364
365  Minidump minidump(templ.c_str());
366  ASSERT_TRUE(minidump.Read());
367
368  // Check that the main module filename is correct.
369  MinidumpModuleList* module_list = minidump.GetModuleList();
370  ASSERT_TRUE(module_list);
371  const MinidumpModule* module = module_list->GetMainModule();
372  EXPECT_STREQ(binpath.c_str(), module->code_file().c_str());
373  // Check that the file ID is correct.
374  FileID fileid(helper_path.c_str());
375  uint8_t identifier[sizeof(MDGUID)];
376  EXPECT_TRUE(fileid.ElfFileIdentifier(identifier));
377  char identifier_string[kGUIDStringSize];
378  FileID::ConvertIdentifierToString(identifier,
379                                    identifier_string,
380                                    kGUIDStringSize);
381  string module_identifier(identifier_string);
382  // Strip out dashes
383  while ((pos = module_identifier.find('-')) != string::npos) {
384    module_identifier.erase(pos, 1);
385  }
386  // And append a zero, because module IDs include an "age" field
387  // which is always zero on Linux.
388  module_identifier += "0";
389  EXPECT_EQ(module_identifier, module->debug_identifier());
390}