blob: c537ce93e58ffcc2be262e2a6ff87cacf3ba0517 [file] [log] [blame]
nealsidb0baafc2009-08-17 23:12:53 +00001// Copyright (c) 2009, 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
ted.mielczarekcfc86282010-10-20 15:51:38 +000030#include <string>
31
jessicag.feedback@gmail.com3b3f0c32011-03-22 02:50:41 +000032#include <fcntl.h>
ted.mielczarek0a5fc5d2009-12-23 17:09:27 +000033#include <limits.h>
nealsidb0baafc2009-08-17 23:12:53 +000034#include <unistd.h>
nealsidde545c02010-03-02 00:39:48 +000035#include <signal.h>
ted.mielczarekef7262d2010-12-13 22:10:23 +000036#include <stdint.h>
ted.mielczarekb0201df2011-03-14 17:04:09 +000037#include <sys/mman.h>
ted.mielczarekef7262d2010-12-13 22:10:23 +000038#include <sys/poll.h>
jessicag.feedback@gmail.com3b3f0c32011-03-22 02:50:41 +000039#include <sys/stat.h>
nealsidde545c02010-03-02 00:39:48 +000040#include <sys/types.h>
nealsidb0baafc2009-08-17 23:12:53 +000041
nealsidde545c02010-03-02 00:39:48 +000042#include "breakpad_googletest_includes.h"
nealsidb0baafc2009-08-17 23:12:53 +000043#include "client/linux/minidump_writer/linux_dumper.h"
ted.mielczarekef7262d2010-12-13 22:10:23 +000044#include "common/linux/eintr_wrapper.h"
ted.mielczarek0a5fc5d2009-12-23 17:09:27 +000045#include "common/linux/file_id.h"
ted.mielczarek4621ee02010-09-23 14:55:50 +000046#include "common/memory.h"
nealsidb0baafc2009-08-17 23:12:53 +000047
ted.mielczarekcfc86282010-10-20 15:51:38 +000048using std::string;
nealsidb0baafc2009-08-17 23:12:53 +000049using namespace google_breakpad;
50
51namespace {
52typedef testing::Test LinuxDumperTest;
ted.mielczarekb0201df2011-03-14 17:04:09 +000053
54string GetHelperBinary() {
55 // Locate helper binary next to the current binary.
56 char self_path[PATH_MAX];
57 if (readlink("/proc/self/exe", self_path, sizeof(self_path) - 1) == -1) {
58 return "";
59 }
60 string helper_path(self_path);
61 size_t pos = helper_path.rfind('/');
62 if (pos == string::npos) {
63 return "";
64 }
65 helper_path.erase(pos + 1);
66 helper_path += "linux_dumper_unittest_helper";
67
68 return helper_path;
69}
70
nealsidb0baafc2009-08-17 23:12:53 +000071}
72
73TEST(LinuxDumperTest, Setup) {
74 LinuxDumper dumper(getpid());
75}
76
77TEST(LinuxDumperTest, FindMappings) {
78 LinuxDumper dumper(getpid());
79 ASSERT_TRUE(dumper.Init());
80
81 ASSERT_TRUE(dumper.FindMapping(reinterpret_cast<void*>(getpid)));
82 ASSERT_TRUE(dumper.FindMapping(reinterpret_cast<void*>(printf)));
83 ASSERT_FALSE(dumper.FindMapping(NULL));
84}
85
86TEST(LinuxDumperTest, ThreadList) {
87 LinuxDumper dumper(getpid());
88 ASSERT_TRUE(dumper.Init());
89
nealsidde545c02010-03-02 00:39:48 +000090 ASSERT_GE(dumper.threads().size(), (size_t)1);
nealsidb0baafc2009-08-17 23:12:53 +000091 bool found = false;
92 for (size_t i = 0; i < dumper.threads().size(); ++i) {
93 if (dumper.threads()[i] == getpid()) {
94 found = true;
95 break;
96 }
97 }
98}
99
ted.mielczarekb0201df2011-03-14 17:04:09 +0000100// Helper stack class to close a file descriptor and unmap
101// a mmap'ed mapping.
102class StackHelper {
103public:
104 StackHelper(int fd, char* mapping, size_t size)
105 : fd_(fd), mapping_(mapping), size_(size) {}
106 ~StackHelper() {
107 munmap(mapping_, size_);
108 close(fd_);
109 }
110
111private:
112 int fd_;
113 char* mapping_;
114 size_t size_;
115};
116
117TEST(LinuxDumperTest, MergedMappings) {
118 string helper_path(GetHelperBinary());
119 if (helper_path.empty()) {
120 FAIL() << "Couldn't find helper binary";
121 exit(1);
122 }
123
124 // mmap two segments out of the helper binary, one
125 // enclosed in the other, but with different protections.
126 const size_t kPageSize = sysconf(_SC_PAGESIZE);
127 const size_t kMappingSize = 3 * kPageSize;
128 int fd = open(helper_path.c_str(), O_RDONLY);
129 ASSERT_NE(-1, fd);
130 char* mapping =
131 reinterpret_cast<char*>(mmap(NULL,
132 kMappingSize,
133 PROT_READ,
134 MAP_SHARED,
135 fd,
136 0));
137 ASSERT_TRUE(mapping);
138
139 const u_int64_t kMappingAddress = reinterpret_cast<u_int64_t>(mapping);
140
141 // Ensure that things get cleaned up.
142 StackHelper helper(fd, mapping, kMappingSize);
143
144 // Carve a page out of the first mapping with different permissions.
145 char* inside_mapping = reinterpret_cast<char*>(mmap(mapping + 2 *kPageSize,
146 kPageSize,
147 PROT_NONE,
148 MAP_SHARED | MAP_FIXED,
149 fd,
150 // Map a different offset just to
151 // better test real-world conditions.
152 kPageSize));
153 ASSERT_TRUE(inside_mapping);
154
155 // Now check that LinuxDumper interpreted the mappings properly.
156 LinuxDumper dumper(getpid());
157 ASSERT_TRUE(dumper.Init());
158 int mapping_count = 0;
159 for (unsigned i = 0; i < dumper.mappings().size(); ++i) {
160 const MappingInfo& mapping = *dumper.mappings()[i];
161 if (strcmp(mapping.name, helper_path.c_str()) == 0) {
162 // This mapping should encompass the entire original mapped
163 // range.
164 EXPECT_EQ(kMappingAddress, mapping.start_addr);
165 EXPECT_EQ(kMappingSize, mapping.size);
166 EXPECT_EQ(0, mapping.offset);
167 mapping_count++;
168 }
169 }
170 EXPECT_EQ(1, mapping_count);
171}
172
nealsidde545c02010-03-02 00:39:48 +0000173TEST(LinuxDumperTest, VerifyStackReadWithMultipleThreads) {
174 static const int kNumberOfThreadsInHelperProgram = 5;
175 char kNumberOfThreadsArgument[2];
176 sprintf(kNumberOfThreadsArgument, "%d", kNumberOfThreadsInHelperProgram);
177
ted.mielczarekef7262d2010-12-13 22:10:23 +0000178 int fds[2];
179 ASSERT_NE(-1, pipe(fds));
180
nealsidde545c02010-03-02 00:39:48 +0000181 pid_t child_pid = fork();
182 if (child_pid == 0) {
ted.mielczarekef7262d2010-12-13 22:10:23 +0000183 // In child process.
184 close(fds[0]);
185
ted.mielczarekb0201df2011-03-14 17:04:09 +0000186 string helper_path(GetHelperBinary());
187 if (helper_path.empty()) {
188 FAIL() << "Couldn't find helper binary";
ted.mielczarekcfc86282010-10-20 15:51:38 +0000189 exit(1);
190 }
ted.mielczarekcfc86282010-10-20 15:51:38 +0000191
ted.mielczarekef7262d2010-12-13 22:10:23 +0000192 // Pass the pipe fd and the number of threads as arguments.
193 char pipe_fd_string[8];
194 sprintf(pipe_fd_string, "%d", fds[1]);
ted.mielczarekcfc86282010-10-20 15:51:38 +0000195 execl(helper_path.c_str(),
nealsidde545c02010-03-02 00:39:48 +0000196 "linux_dumper_unittest_helper",
ted.mielczarekef7262d2010-12-13 22:10:23 +0000197 pipe_fd_string,
nealsidde545c02010-03-02 00:39:48 +0000198 kNumberOfThreadsArgument,
199 NULL);
200 // Kill if we get here.
201 printf("Errno from exec: %d", errno);
ted.mielczarekcfc86282010-10-20 15:51:38 +0000202 FAIL() << "Exec of " << helper_path << " failed: " << strerror(errno);
nealsidde545c02010-03-02 00:39:48 +0000203 exit(0);
204 }
ted.mielczarekef7262d2010-12-13 22:10:23 +0000205 close(fds[1]);
206 // Wait for the child process to signal that it's ready.
207 struct pollfd pfd;
208 memset(&pfd, 0, sizeof(pfd));
209 pfd.fd = fds[0];
210 pfd.events = POLLIN | POLLERR;
211
212 const int r = HANDLE_EINTR(poll(&pfd, 1, 1000));
213 ASSERT_EQ(1, r);
214 ASSERT_TRUE(pfd.revents & POLLIN);
215 uint8_t junk;
216 read(fds[0], &junk, sizeof(junk));
217 close(fds[0]);
218
219 // Child is ready now.
nealsidde545c02010-03-02 00:39:48 +0000220 LinuxDumper dumper(child_pid);
ted.mielczarekcfc86282010-10-20 15:51:38 +0000221 ASSERT_TRUE(dumper.Init());
nealsidde545c02010-03-02 00:39:48 +0000222 EXPECT_EQ((size_t)kNumberOfThreadsInHelperProgram, dumper.threads().size());
223 EXPECT_TRUE(dumper.ThreadsSuspend());
224
225 ThreadInfo one_thread;
226 for(size_t i = 0; i < dumper.threads().size(); ++i) {
227 EXPECT_TRUE(dumper.ThreadInfoGet(dumper.threads()[i], &one_thread));
jimblandyb68b8002010-03-29 18:23:42 +0000228 // In the helper program, we stored a pointer to the thread id in a
229 // specific register. Check that we can recover its value.
nealsidde545c02010-03-02 00:39:48 +0000230#if defined(__ARM_EABI__)
jimblandyb68b8002010-03-29 18:23:42 +0000231 pid_t *process_tid_location = (pid_t *)(one_thread.regs.uregs[3]);
nealsidde545c02010-03-02 00:39:48 +0000232#elif defined(__i386)
jimblandyb68b8002010-03-29 18:23:42 +0000233 pid_t *process_tid_location = (pid_t *)(one_thread.regs.ecx);
nealsidde545c02010-03-02 00:39:48 +0000234#elif defined(__x86_64)
jimblandyb68b8002010-03-29 18:23:42 +0000235 pid_t *process_tid_location = (pid_t *)(one_thread.regs.rcx);
nealsidde545c02010-03-02 00:39:48 +0000236#else
jimblandyb68b8002010-03-29 18:23:42 +0000237#error This test has not been ported to this platform.
nealsidde545c02010-03-02 00:39:48 +0000238#endif
239 pid_t one_thread_id;
240 dumper.CopyFromProcess(&one_thread_id,
241 dumper.threads()[i],
242 process_tid_location,
243 4);
244 EXPECT_EQ(dumper.threads()[i], one_thread_id);
245 }
246 kill(child_pid, SIGKILL);
247}
248
nealsidb0baafc2009-08-17 23:12:53 +0000249TEST(LinuxDumperTest, BuildProcPath) {
250 const pid_t pid = getpid();
251 LinuxDumper dumper(pid);
252
253 char maps_path[256] = "dummymappath";
254 char maps_path_expected[256];
255 snprintf(maps_path_expected, sizeof(maps_path_expected),
256 "/proc/%d/maps", pid);
257 dumper.BuildProcPath(maps_path, pid, "maps");
258 ASSERT_STREQ(maps_path, maps_path_expected);
259
260 // In release mode, we expect BuildProcPath to handle the invalid
261 // parameters correctly and fill map_path with an empty
262 // NULL-terminated string.
263#ifdef NDEBUG
264 snprintf(maps_path, sizeof(maps_path), "dummymappath");
265 dumper.BuildProcPath(maps_path, 0, "maps");
266 EXPECT_STREQ(maps_path, "");
267
268 snprintf(maps_path, sizeof(maps_path), "dummymappath");
269 dumper.BuildProcPath(maps_path, getpid(), "");
270 EXPECT_STREQ(maps_path, "");
271
272 snprintf(maps_path, sizeof(maps_path), "dummymappath");
273 dumper.BuildProcPath(maps_path, getpid(), NULL);
274 EXPECT_STREQ(maps_path, "");
275#endif
276}
277
nealsidde545c02010-03-02 00:39:48 +0000278#if !defined(__ARM_EABI__)
nealsidb0baafc2009-08-17 23:12:53 +0000279TEST(LinuxDumperTest, MappingsIncludeLinuxGate) {
280 LinuxDumper dumper(getpid());
281 ASSERT_TRUE(dumper.Init());
282
283 void* linux_gate_loc = dumper.FindBeginningOfLinuxGateSharedLibrary(getpid());
nealsidde545c02010-03-02 00:39:48 +0000284 ASSERT_TRUE(linux_gate_loc);
285 bool found_linux_gate = false;
nealsidb0baafc2009-08-17 23:12:53 +0000286
nealsidde545c02010-03-02 00:39:48 +0000287 const wasteful_vector<MappingInfo*> mappings = dumper.mappings();
288 const MappingInfo* mapping;
289 for (unsigned i = 0; i < mappings.size(); ++i) {
290 mapping = mappings[i];
291 if (!strcmp(mapping->name, kLinuxGateLibraryName)) {
292 found_linux_gate = true;
293 break;
nealsidb0baafc2009-08-17 23:12:53 +0000294 }
nealsidb0baafc2009-08-17 23:12:53 +0000295 }
nealsidde545c02010-03-02 00:39:48 +0000296 EXPECT_TRUE(found_linux_gate);
297 EXPECT_EQ(linux_gate_loc, reinterpret_cast<void*>(mapping->start_addr));
298 EXPECT_EQ(0, memcmp(linux_gate_loc, ELFMAG, SELFMAG));
nealsidb0baafc2009-08-17 23:12:53 +0000299}
nealsidde545c02010-03-02 00:39:48 +0000300#endif
ted.mielczarek0a5fc5d2009-12-23 17:09:27 +0000301
302TEST(LinuxDumperTest, FileIDsMatch) {
303 // Calculate the File ID of our binary using both
304 // FileID::ElfFileIdentifier and LinuxDumper::ElfFileIdentifierForMapping
305 // and ensure that we get the same result from both.
306 char exe_name[PATH_MAX];
307 ssize_t len = readlink("/proc/self/exe", exe_name, PATH_MAX - 1);
308 ASSERT_NE(len, -1);
309 exe_name[len] = '\0';
310
311 int fds[2];
312 ASSERT_NE(-1, pipe(fds));
313
314 // fork a child so we can ptrace it
315 const pid_t child = fork();
316 if (child == 0) {
317 close(fds[1]);
318 // now wait forever for the parent
319 char b;
320 HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
321 close(fds[0]);
322 syscall(__NR_exit);
323 }
324 close(fds[0]);
325
326 LinuxDumper dumper(child);
327 ASSERT_TRUE(dumper.Init());
328 const wasteful_vector<MappingInfo*> mappings = dumper.mappings();
329 bool found_exe = false;
330 unsigned i;
331 for (i = 0; i < mappings.size(); ++i) {
332 const MappingInfo* mapping = mappings[i];
333 if (!strcmp(mapping->name, exe_name)) {
334 found_exe = true;
335 break;
336 }
337 }
338 ASSERT_TRUE(found_exe);
339
340 uint8_t identifier1[sizeof(MDGUID)];
341 uint8_t identifier2[sizeof(MDGUID)];
jessicag.feedback@gmail.com23c82992011-03-30 21:42:27 +0000342 EXPECT_TRUE(dumper.ElfFileIdentifierForMapping(*mappings[i], true, i,
ted.mielczarekef7262d2010-12-13 22:10:23 +0000343 identifier1));
ted.mielczarek0a5fc5d2009-12-23 17:09:27 +0000344 FileID fileid(exe_name);
345 EXPECT_TRUE(fileid.ElfFileIdentifier(identifier2));
346 char identifier_string1[37];
347 char identifier_string2[37];
348 FileID::ConvertIdentifierToString(identifier1, identifier_string1,
349 37);
350 FileID::ConvertIdentifierToString(identifier2, identifier_string2,
351 37);
352 EXPECT_STREQ(identifier_string1, identifier_string2);
353 close(fds[1]);
354}