blob: f527890db921f0981456ac929e7e8e69588599dc [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"
benchan@chromium.orgc9fb1f62011-12-21 17:51:40 +000045#include "common/linux/safe_readlink.h"
qsr@chromium.orgb12089f2011-11-23 14:22:05 +000046#include "common/tests/auto_tempdir.h"
ted.mielczarekef7262d2010-12-13 22:10:23 +000047#include "google_breakpad/processor/minidump.h"
nealsidb0baafc2009-08-17 23:12:53 +000048
49using namespace google_breakpad;
50
ted.mielczarekef7262d2010-12-13 22:10:23 +000051// Length of a formatted GUID string =
52// sizeof(MDGUID) * 2 + 4 (for dashes) + 1 (null terminator)
53const int kGUIDStringSize = 37;
54
nealsidb0baafc2009-08-17 23:12:53 +000055namespace {
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
qsr@chromium.orgb12089f2011-11-23 14:22:05 +000076 AutoTempDir temp_dir;
77 std::string templ = temp_dir.path() + "/minidump-writer-unittest";
kmixter@chromium.orgb5dfa282010-12-08 22:26:20 +000078 // Set a non-zero tid to avoid tripping asserts.
79 context.tid = 1;
qsr@chromium.orgb12089f2011-11-23 14:22:05 +000080 ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context)));
nealsidb0baafc2009-08-17 23:12:53 +000081 struct stat st;
qsr@chromium.orgb12089f2011-11-23 14:22:05 +000082 ASSERT_EQ(stat(templ.c_str(), &st), 0);
nealsidb0baafc2009-08-17 23:12:53 +000083 ASSERT_GT(st.st_size, 0u);
nealsidb0baafc2009-08-17 23:12:53 +000084
85 close(fds[1]);
86}
ted.mielczarekef7262d2010-12-13 22:10:23 +000087
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";
thestig@chromium.org972ae492011-01-12 00:14:27 +0000115
ted.mielczarekef7262d2010-12-13 22:10:23 +0000116 // 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));
Michael Krebs0efd9152012-01-25 19:11:10 -0800124 const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
ted.mielczarekef7262d2010-12-13 22:10:23 +0000125 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
qsr@chromium.orgb12089f2011-11-23 14:22:05 +0000141 AutoTempDir temp_dir;
142 std::string templ = temp_dir.path() + "/minidump-writer-unittest";
ted.mielczarekef7262d2010-12-13 22:10:23 +0000143
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);
thestig@chromium.org972ae492011-01-12 00:14:27 +0000150
ted.mielczarekef7262d2010-12-13 22:10:23 +0000151 MappingList mappings;
152 MappingEntry mapping;
153 mapping.first = info;
154 memcpy(mapping.second, kModuleGUID, sizeof(MDGUID));
155 mappings.push_back(mapping);
qsr@chromium.orgb12089f2011-11-23 14:22:05 +0000156 ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context),
157 mappings));
ted.mielczarekef7262d2010-12-13 22:10:23 +0000158
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.
qsr@chromium.orgb12089f2011-11-23 14:22:05 +0000162 Minidump minidump(templ.c_str());
ted.mielczarekef7262d2010-12-13 22:10:23 +0000163 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
ted.mielczarekef7262d2010-12-13 22:10:23 +0000176 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";
thestig@chromium.org972ae492011-01-12 00:14:27 +0000207
ted.mielczarekef7262d2010-12-13 22:10:23 +0000208 // mmap a file
qsr@chromium.orgb12089f2011-11-23 14:22:05 +0000209 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);
ted.mielczarekef7262d2010-12-13 22:10:23 +0000212 ASSERT_NE(-1, fd);
qsr@chromium.orgb12089f2011-11-23 14:22:05 +0000213 unlink(tempfile.c_str());
ted.mielczarekef7262d2010-12-13 22:10:23 +0000214 // 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));
Michael Krebs0efd9152012-01-25 19:11:10 -0800227 const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
ted.mielczarekef7262d2010-12-13 22:10:23 +0000228 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
qsr@chromium.orgb12089f2011-11-23 14:22:05 +0000245 std::string dumpfile = temp_dir.path() + "/minidump-writer-unittest";
ted.mielczarekef7262d2010-12-13 22:10:23 +0000246
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);
thestig@chromium.org972ae492011-01-12 00:14:27 +0000260 ASSERT_TRUE(
qsr@chromium.orgb12089f2011-11-23 14:22:05 +0000261 WriteMinidump(dumpfile.c_str(), child, &context, sizeof(context),
262 mappings));
ted.mielczarekef7262d2010-12-13 22:10:23 +0000263
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.
qsr@chromium.orgb12089f2011-11-23 14:22:05 +0000267 Minidump minidump(dumpfile.c_str());
ted.mielczarekef7262d2010-12-13 22:10:23 +0000268 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
ted.mielczarekef7262d2010-12-13 22:10:23 +0000281 close(fds[1]);
282}
283
Mike Frysingerb5cba2c2012-03-20 15:02:27 -0400284#if 0 // Disabled; see: http://crosbug.com/25355
ted.mielczarekef7262d2010-12-13 22:10:23 +0000285TEST(MinidumpWriterTest, DeletedBinary) {
286 static const int kNumberOfThreadsInHelperProgram = 1;
287 char kNumberOfThreadsArgument[2];
288 sprintf(kNumberOfThreadsArgument, "%d", kNumberOfThreadsInHelperProgram);
289
290 // Locate helper binary next to the current binary.
291 char self_path[PATH_MAX];
benchan@chromium.orgc9fb1f62011-12-21 17:51:40 +0000292 if (!SafeReadLink("/proc/self/exe", self_path)) {
293 FAIL() << "readlink failed";
ted.mielczarekef7262d2010-12-13 22:10:23 +0000294 exit(1);
295 }
296 string helper_path(self_path);
297 size_t pos = helper_path.rfind('/');
298 if (pos == string::npos) {
299 FAIL() << "no trailing slash in path: " << helper_path;
300 exit(1);
301 }
302 helper_path.erase(pos + 1);
303 helper_path += "linux_dumper_unittest_helper";
304
305 // Copy binary to a temp file.
qsr@chromium.orgb12089f2011-11-23 14:22:05 +0000306 AutoTempDir temp_dir;
307 std::string binpath = temp_dir.path() + "/linux-dumper-unittest-helper";
ted.mielczarekef7262d2010-12-13 22:10:23 +0000308 char cmdline[2 * PATH_MAX];
qsr@chromium.orgb12089f2011-11-23 14:22:05 +0000309 sprintf(cmdline, "/bin/cp \"%s\" \"%s\"", helper_path.c_str(),
310 binpath.c_str());
ted.mielczarekef7262d2010-12-13 22:10:23 +0000311 ASSERT_EQ(0, system(cmdline));
qsr@chromium.orgb12089f2011-11-23 14:22:05 +0000312 ASSERT_EQ(0, chmod(binpath.c_str(), 0755));
ted.mielczarekef7262d2010-12-13 22:10:23 +0000313
314 int fds[2];
315 ASSERT_NE(-1, pipe(fds));
316
317 pid_t child_pid = fork();
318 if (child_pid == 0) {
319 // In child process.
320 close(fds[0]);
321
322 // Pass the pipe fd and the number of threads as arguments.
323 char pipe_fd_string[8];
324 sprintf(pipe_fd_string, "%d", fds[1]);
qsr@chromium.orgb12089f2011-11-23 14:22:05 +0000325 execl(binpath.c_str(),
326 binpath.c_str(),
ted.mielczarekef7262d2010-12-13 22:10:23 +0000327 pipe_fd_string,
328 kNumberOfThreadsArgument,
329 NULL);
330 }
331 close(fds[1]);
332 // Wait for the child process to signal that it's ready.
333 struct pollfd pfd;
334 memset(&pfd, 0, sizeof(pfd));
335 pfd.fd = fds[0];
336 pfd.events = POLLIN | POLLERR;
337
338 const int r = HANDLE_EINTR(poll(&pfd, 1, 1000));
339 ASSERT_EQ(1, r);
340 ASSERT_TRUE(pfd.revents & POLLIN);
341 uint8_t junk;
342 read(fds[0], &junk, sizeof(junk));
343 close(fds[0]);
344
345 // Child is ready now.
346 // Unlink the test binary.
qsr@chromium.orgb12089f2011-11-23 14:22:05 +0000347 unlink(binpath.c_str());
ted.mielczarekef7262d2010-12-13 22:10:23 +0000348
349 ExceptionHandler::CrashContext context;
350 memset(&context, 0, sizeof(context));
351
qsr@chromium.orgb12089f2011-11-23 14:22:05 +0000352 std::string templ = temp_dir.path() + "/minidump-writer-unittest";
ted.mielczarekef7262d2010-12-13 22:10:23 +0000353 // Set a non-zero tid to avoid tripping asserts.
354 context.tid = 1;
qsr@chromium.orgb12089f2011-11-23 14:22:05 +0000355 ASSERT_TRUE(WriteMinidump(templ.c_str(), child_pid, &context,
356 sizeof(context)));
ted.mielczarekef7262d2010-12-13 22:10:23 +0000357 kill(child_pid, SIGKILL);
358
359 struct stat st;
qsr@chromium.orgb12089f2011-11-23 14:22:05 +0000360 ASSERT_EQ(stat(templ.c_str(), &st), 0);
ted.mielczarekef7262d2010-12-13 22:10:23 +0000361 ASSERT_GT(st.st_size, 0u);
362
363
364
qsr@chromium.orgb12089f2011-11-23 14:22:05 +0000365 Minidump minidump(templ.c_str());
ted.mielczarekef7262d2010-12-13 22:10:23 +0000366 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();
qsr@chromium.orgb12089f2011-11-23 14:22:05 +0000372 EXPECT_STREQ(binpath.c_str(), module->code_file().c_str());
ted.mielczarekef7262d2010-12-13 22:10:23 +0000373 // 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());
ted.mielczarekef7262d2010-12-13 22:10:23 +0000390}
Mike Frysingerb5cba2c2012-03-20 15:02:27 -0400391#endif