blob: 4146526883540850c4ec492e7209b2eafdd1f381 [file] [log] [blame]
thestig@chromium.org972ae492011-01-12 00:14:27 +00001// Copyright (c) 2011 Google Inc.
nealsidb0baafc2009-08-17 23:12:53 +00002// 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
thestig@chromium.org972ae492011-01-12 00:14:27 +000030#include <fcntl.h>
ted.mielczarekef7262d2010-12-13 22:10:23 +000031#include <sys/poll.h>
32#include <sys/stat.h>
nealsidb0baafc2009-08-17 23:12:53 +000033#include <sys/syscall.h>
thestig@chromium.org972ae492011-01-12 00:14:27 +000034#include <sys/types.h>
35#include <unistd.h>
36
37#include <string>
nealsidb0baafc2009-08-17 23:12:53 +000038
ted.mielczarekef7262d2010-12-13 22:10:23 +000039#include "breakpad_googletest_includes.h"
nealsidb0baafc2009-08-17 23:12:53 +000040#include "client/linux/handler/exception_handler.h"
ted.mielczarekef7262d2010-12-13 22:10:23 +000041#include "client/linux/minidump_writer/linux_dumper.h"
nealsidb0baafc2009-08-17 23:12:53 +000042#include "client/linux/minidump_writer/minidump_writer.h"
ted.mielczarekf480ba12010-02-05 18:21:31 +000043#include "common/linux/eintr_wrapper.h"
ted.mielczarekef7262d2010-12-13 22:10:23 +000044#include "common/linux/file_id.h"
qsr@chromium.orgb12089f2011-11-23 14:22:05 +000045#include "common/tests/auto_tempdir.h"
ted.mielczarekef7262d2010-12-13 22:10:23 +000046#include "google_breakpad/processor/minidump.h"
nealsidb0baafc2009-08-17 23:12:53 +000047
48using namespace google_breakpad;
49
ted.mielczarekef7262d2010-12-13 22:10:23 +000050// Length of a formatted GUID string =
51// sizeof(MDGUID) * 2 + 4 (for dashes) + 1 (null terminator)
52const int kGUIDStringSize = 37;
53
nealsidb0baafc2009-08-17 23:12:53 +000054namespace {
55typedef testing::Test MinidumpWriterTest;
56}
57
58TEST(MinidumpWriterTest, Setup) {
59 int fds[2];
60 ASSERT_NE(-1, pipe(fds));
61
62 const pid_t child = fork();
63 if (child == 0) {
64 close(fds[1]);
65 char b;
66 HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
67 close(fds[0]);
68 syscall(__NR_exit);
69 }
70 close(fds[0]);
71
72 ExceptionHandler::CrashContext context;
73 memset(&context, 0, sizeof(context));
74
qsr@chromium.orgb12089f2011-11-23 14:22:05 +000075 AutoTempDir temp_dir;
76 std::string templ = temp_dir.path() + "/minidump-writer-unittest";
kmixter@chromium.orgb5dfa282010-12-08 22:26:20 +000077 // Set a non-zero tid to avoid tripping asserts.
78 context.tid = 1;
qsr@chromium.orgb12089f2011-11-23 14:22:05 +000079 ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context)));
nealsidb0baafc2009-08-17 23:12:53 +000080 struct stat st;
qsr@chromium.orgb12089f2011-11-23 14:22:05 +000081 ASSERT_EQ(stat(templ.c_str(), &st), 0);
nealsidb0baafc2009-08-17 23:12:53 +000082 ASSERT_GT(st.st_size, 0u);
nealsidb0baafc2009-08-17 23:12:53 +000083
84 close(fds[1]);
85}
ted.mielczarekef7262d2010-12-13 22:10:23 +000086
87// Test that mapping info can be specified when writing a minidump,
88// and that it ends up in the module list of the minidump.
89TEST(MinidumpWriterTest, MappingInfo) {
90 int fds[2];
91 ASSERT_NE(-1, pipe(fds));
92
93 // These are defined here so the parent can use them to check the
94 // data from the minidump afterwards.
95 const u_int32_t kMemorySize = sysconf(_SC_PAGESIZE);
96 const char* kMemoryName = "a fake module";
97 const u_int8_t kModuleGUID[sizeof(MDGUID)] = {
98 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
99 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
100 };
101 char module_identifier_buffer[kGUIDStringSize];
102 FileID::ConvertIdentifierToString(kModuleGUID,
103 module_identifier_buffer,
104 sizeof(module_identifier_buffer));
105 string module_identifier(module_identifier_buffer);
106 // Strip out dashes
107 size_t pos;
108 while ((pos = module_identifier.find('-')) != string::npos) {
109 module_identifier.erase(pos, 1);
110 }
111 // And append a zero, because module IDs include an "age" field
112 // which is always zero on Linux.
113 module_identifier += "0";
thestig@chromium.org972ae492011-01-12 00:14:27 +0000114
ted.mielczarekef7262d2010-12-13 22:10:23 +0000115 // Get some memory.
116 char* memory =
117 reinterpret_cast<char*>(mmap(NULL,
118 kMemorySize,
119 PROT_READ | PROT_WRITE,
120 MAP_PRIVATE | MAP_ANON,
121 -1,
122 0));
123 const u_int64_t kMemoryAddress = reinterpret_cast<u_int64_t>(memory);
124 ASSERT_TRUE(memory);
125
126 const pid_t child = fork();
127 if (child == 0) {
128 close(fds[1]);
129 char b;
130 HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
131 close(fds[0]);
132 syscall(__NR_exit);
133 }
134 close(fds[0]);
135
136 ExceptionHandler::CrashContext context;
137 memset(&context, 0, sizeof(context));
138 context.tid = 1;
139
qsr@chromium.orgb12089f2011-11-23 14:22:05 +0000140 AutoTempDir temp_dir;
141 std::string templ = temp_dir.path() + "/minidump-writer-unittest";
ted.mielczarekef7262d2010-12-13 22:10:23 +0000142
143 // Add information about the mapped memory.
144 MappingInfo info;
145 info.start_addr = kMemoryAddress;
146 info.size = kMemorySize;
147 info.offset = 0;
148 strcpy(info.name, kMemoryName);
thestig@chromium.org972ae492011-01-12 00:14:27 +0000149
ted.mielczarekef7262d2010-12-13 22:10:23 +0000150 MappingList mappings;
151 MappingEntry mapping;
152 mapping.first = info;
153 memcpy(mapping.second, kModuleGUID, sizeof(MDGUID));
154 mappings.push_back(mapping);
qsr@chromium.orgb12089f2011-11-23 14:22:05 +0000155 ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context),
156 mappings));
ted.mielczarekef7262d2010-12-13 22:10:23 +0000157
158 // Read the minidump. Load the module list, and ensure that
159 // the mmap'ed |memory| is listed with the given module name
160 // and debug ID.
qsr@chromium.orgb12089f2011-11-23 14:22:05 +0000161 Minidump minidump(templ.c_str());
ted.mielczarekef7262d2010-12-13 22:10:23 +0000162 ASSERT_TRUE(minidump.Read());
163
164 MinidumpModuleList* module_list = minidump.GetModuleList();
165 ASSERT_TRUE(module_list);
166 const MinidumpModule* module =
167 module_list->GetModuleForAddress(kMemoryAddress);
168 ASSERT_TRUE(module);
169
170 EXPECT_EQ(kMemoryAddress, module->base_address());
171 EXPECT_EQ(kMemorySize, module->size());
172 EXPECT_EQ(kMemoryName, module->code_file());
173 EXPECT_EQ(module_identifier, module->debug_identifier());
174
ted.mielczarekef7262d2010-12-13 22:10:23 +0000175 close(fds[1]);
176}
177
178// Test that mapping info can be specified, and that it overrides
179// existing mappings that are wholly contained within the specified
180// range.
181TEST(MinidumpWriterTest, MappingInfoContained) {
182 int fds[2];
183 ASSERT_NE(-1, pipe(fds));
184
185 // These are defined here so the parent can use them to check the
186 // data from the minidump afterwards.
187 const u_int32_t kMemorySize = sysconf(_SC_PAGESIZE);
188 const char* kMemoryName = "a fake module";
189 const u_int8_t kModuleGUID[sizeof(MDGUID)] = {
190 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
191 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
192 };
193 char module_identifier_buffer[kGUIDStringSize];
194 FileID::ConvertIdentifierToString(kModuleGUID,
195 module_identifier_buffer,
196 sizeof(module_identifier_buffer));
197 string module_identifier(module_identifier_buffer);
198 // Strip out dashes
199 size_t pos;
200 while ((pos = module_identifier.find('-')) != string::npos) {
201 module_identifier.erase(pos, 1);
202 }
203 // And append a zero, because module IDs include an "age" field
204 // which is always zero on Linux.
205 module_identifier += "0";
thestig@chromium.org972ae492011-01-12 00:14:27 +0000206
ted.mielczarekef7262d2010-12-13 22:10:23 +0000207 // mmap a file
qsr@chromium.orgb12089f2011-11-23 14:22:05 +0000208 AutoTempDir temp_dir;
209 std::string tempfile = temp_dir.path() + "/minidump-writer-unittest-temp";
210 int fd = open(tempfile.c_str(), O_RDWR | O_CREAT, 0);
ted.mielczarekef7262d2010-12-13 22:10:23 +0000211 ASSERT_NE(-1, fd);
qsr@chromium.orgb12089f2011-11-23 14:22:05 +0000212 unlink(tempfile.c_str());
ted.mielczarekef7262d2010-12-13 22:10:23 +0000213 // fill with zeros
214 char buffer[kMemorySize];
215 memset(buffer, 0, kMemorySize);
216 ASSERT_EQ(kMemorySize, write(fd, buffer, kMemorySize));
217 lseek(fd, 0, SEEK_SET);
218
219 char* memory =
220 reinterpret_cast<char*>(mmap(NULL,
221 kMemorySize,
222 PROT_READ | PROT_WRITE,
223 MAP_PRIVATE,
224 fd,
225 0));
226 const u_int64_t kMemoryAddress = reinterpret_cast<u_int64_t>(memory);
227 ASSERT_TRUE(memory);
228 close(fd);
229
230 const pid_t child = fork();
231 if (child == 0) {
232 close(fds[1]);
233 char b;
234 HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
235 close(fds[0]);
236 syscall(__NR_exit);
237 }
238 close(fds[0]);
239
240 ExceptionHandler::CrashContext context;
241 memset(&context, 0, sizeof(context));
242 context.tid = 1;
243
qsr@chromium.orgb12089f2011-11-23 14:22:05 +0000244 std::string dumpfile = temp_dir.path() + "/minidump-writer-unittest";
ted.mielczarekef7262d2010-12-13 22:10:23 +0000245
246 // Add information about the mapped memory. Report it as being larger than
247 // it actually is.
248 MappingInfo info;
249 info.start_addr = kMemoryAddress - kMemorySize;
250 info.size = kMemorySize * 3;
251 info.offset = 0;
252 strcpy(info.name, kMemoryName);
253
254 MappingList mappings;
255 MappingEntry mapping;
256 mapping.first = info;
257 memcpy(mapping.second, kModuleGUID, sizeof(MDGUID));
258 mappings.push_back(mapping);
thestig@chromium.org972ae492011-01-12 00:14:27 +0000259 ASSERT_TRUE(
qsr@chromium.orgb12089f2011-11-23 14:22:05 +0000260 WriteMinidump(dumpfile.c_str(), child, &context, sizeof(context),
261 mappings));
ted.mielczarekef7262d2010-12-13 22:10:23 +0000262
263 // Read the minidump. Load the module list, and ensure that
264 // the mmap'ed |memory| is listed with the given module name
265 // and debug ID.
qsr@chromium.orgb12089f2011-11-23 14:22:05 +0000266 Minidump minidump(dumpfile.c_str());
ted.mielczarekef7262d2010-12-13 22:10:23 +0000267 ASSERT_TRUE(minidump.Read());
268
269 MinidumpModuleList* module_list = minidump.GetModuleList();
270 ASSERT_TRUE(module_list);
271 const MinidumpModule* module =
272 module_list->GetModuleForAddress(kMemoryAddress);
273 ASSERT_TRUE(module);
274
275 EXPECT_EQ(info.start_addr, module->base_address());
276 EXPECT_EQ(info.size, module->size());
277 EXPECT_EQ(kMemoryName, module->code_file());
278 EXPECT_EQ(module_identifier, module->debug_identifier());
279
ted.mielczarekef7262d2010-12-13 22:10:23 +0000280 close(fds[1]);
281}
282
283TEST(MinidumpWriterTest, DeletedBinary) {
284 static const int kNumberOfThreadsInHelperProgram = 1;
285 char kNumberOfThreadsArgument[2];
286 sprintf(kNumberOfThreadsArgument, "%d", kNumberOfThreadsInHelperProgram);
287
288 // Locate helper binary next to the current binary.
289 char self_path[PATH_MAX];
290 if (readlink("/proc/self/exe", self_path, sizeof(self_path) - 1) == -1) {
291 FAIL() << "readlink failed: " << strerror(errno);
292 exit(1);
293 }
294 string helper_path(self_path);
295 size_t pos = helper_path.rfind('/');
296 if (pos == string::npos) {
297 FAIL() << "no trailing slash in path: " << helper_path;
298 exit(1);
299 }
300 helper_path.erase(pos + 1);
301 helper_path += "linux_dumper_unittest_helper";
302
303 // Copy binary to a temp file.
qsr@chromium.orgb12089f2011-11-23 14:22:05 +0000304 AutoTempDir temp_dir;
305 std::string binpath = temp_dir.path() + "/linux-dumper-unittest-helper";
ted.mielczarekef7262d2010-12-13 22:10:23 +0000306 char cmdline[2 * PATH_MAX];
qsr@chromium.orgb12089f2011-11-23 14:22:05 +0000307 sprintf(cmdline, "/bin/cp \"%s\" \"%s\"", helper_path.c_str(),
308 binpath.c_str());
ted.mielczarekef7262d2010-12-13 22:10:23 +0000309 ASSERT_EQ(0, system(cmdline));
qsr@chromium.orgb12089f2011-11-23 14:22:05 +0000310 ASSERT_EQ(0, chmod(binpath.c_str(), 0755));
ted.mielczarekef7262d2010-12-13 22:10:23 +0000311
312 int fds[2];
313 ASSERT_NE(-1, pipe(fds));
314
315 pid_t child_pid = fork();
316 if (child_pid == 0) {
317 // In child process.
318 close(fds[0]);
319
320 // Pass the pipe fd and the number of threads as arguments.
321 char pipe_fd_string[8];
322 sprintf(pipe_fd_string, "%d", fds[1]);
qsr@chromium.orgb12089f2011-11-23 14:22:05 +0000323 execl(binpath.c_str(),
324 binpath.c_str(),
ted.mielczarekef7262d2010-12-13 22:10:23 +0000325 pipe_fd_string,
326 kNumberOfThreadsArgument,
327 NULL);
328 }
329 close(fds[1]);
330 // Wait for the child process to signal that it's ready.
331 struct pollfd pfd;
332 memset(&pfd, 0, sizeof(pfd));
333 pfd.fd = fds[0];
334 pfd.events = POLLIN | POLLERR;
335
336 const int r = HANDLE_EINTR(poll(&pfd, 1, 1000));
337 ASSERT_EQ(1, r);
338 ASSERT_TRUE(pfd.revents & POLLIN);
339 uint8_t junk;
340 read(fds[0], &junk, sizeof(junk));
341 close(fds[0]);
342
343 // Child is ready now.
344 // Unlink the test binary.
qsr@chromium.orgb12089f2011-11-23 14:22:05 +0000345 unlink(binpath.c_str());
ted.mielczarekef7262d2010-12-13 22:10:23 +0000346
347 ExceptionHandler::CrashContext context;
348 memset(&context, 0, sizeof(context));
349
qsr@chromium.orgb12089f2011-11-23 14:22:05 +0000350 std::string templ = temp_dir.path() + "/minidump-writer-unittest";
ted.mielczarekef7262d2010-12-13 22:10:23 +0000351 // Set a non-zero tid to avoid tripping asserts.
352 context.tid = 1;
qsr@chromium.orgb12089f2011-11-23 14:22:05 +0000353 ASSERT_TRUE(WriteMinidump(templ.c_str(), child_pid, &context,
354 sizeof(context)));
ted.mielczarekef7262d2010-12-13 22:10:23 +0000355 kill(child_pid, SIGKILL);
356
357 struct stat st;
qsr@chromium.orgb12089f2011-11-23 14:22:05 +0000358 ASSERT_EQ(stat(templ.c_str(), &st), 0);
ted.mielczarekef7262d2010-12-13 22:10:23 +0000359 ASSERT_GT(st.st_size, 0u);
360
361
362
qsr@chromium.orgb12089f2011-11-23 14:22:05 +0000363 Minidump minidump(templ.c_str());
ted.mielczarekef7262d2010-12-13 22:10:23 +0000364 ASSERT_TRUE(minidump.Read());
365
366 // Check that the main module filename is correct.
367 MinidumpModuleList* module_list = minidump.GetModuleList();
368 ASSERT_TRUE(module_list);
369 const MinidumpModule* module = module_list->GetMainModule();
qsr@chromium.orgb12089f2011-11-23 14:22:05 +0000370 EXPECT_STREQ(binpath.c_str(), module->code_file().c_str());
ted.mielczarekef7262d2010-12-13 22:10:23 +0000371 // Check that the file ID is correct.
372 FileID fileid(helper_path.c_str());
373 uint8_t identifier[sizeof(MDGUID)];
374 EXPECT_TRUE(fileid.ElfFileIdentifier(identifier));
375 char identifier_string[kGUIDStringSize];
376 FileID::ConvertIdentifierToString(identifier,
377 identifier_string,
378 kGUIDStringSize);
379 string module_identifier(identifier_string);
380 // Strip out dashes
381 while ((pos = module_identifier.find('-')) != string::npos) {
382 module_identifier.erase(pos, 1);
383 }
384 // And append a zero, because module IDs include an "age" field
385 // which is always zero on Linux.
386 module_identifier += "0";
387 EXPECT_EQ(module_identifier, module->debug_identifier());
ted.mielczarekef7262d2010-12-13 22:10:23 +0000388}