blob: a3e84dbc276097248d449ecd98ad31b04e511b61 [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
ted.mielczarek0a5fc5d2009-12-23 17:09:27 +000032#include <limits.h>
nealsidb0baafc2009-08-17 23:12:53 +000033#include <unistd.h>
nealsidde545c02010-03-02 00:39:48 +000034#include <signal.h>
ted.mielczarekef7262d2010-12-13 22:10:23 +000035#include <stdint.h>
36#include <sys/poll.h>
nealsidde545c02010-03-02 00:39:48 +000037#include <sys/types.h>
nealsidb0baafc2009-08-17 23:12:53 +000038
nealsidde545c02010-03-02 00:39:48 +000039#include "breakpad_googletest_includes.h"
nealsidb0baafc2009-08-17 23:12:53 +000040#include "client/linux/minidump_writer/linux_dumper.h"
ted.mielczarekef7262d2010-12-13 22:10:23 +000041#include "common/linux/eintr_wrapper.h"
ted.mielczarek0a5fc5d2009-12-23 17:09:27 +000042#include "common/linux/file_id.h"
ted.mielczarek4621ee02010-09-23 14:55:50 +000043#include "common/memory.h"
nealsidb0baafc2009-08-17 23:12:53 +000044
ted.mielczarekcfc86282010-10-20 15:51:38 +000045using std::string;
nealsidb0baafc2009-08-17 23:12:53 +000046using namespace google_breakpad;
47
48namespace {
49typedef testing::Test LinuxDumperTest;
50}
51
52TEST(LinuxDumperTest, Setup) {
53 LinuxDumper dumper(getpid());
54}
55
56TEST(LinuxDumperTest, FindMappings) {
57 LinuxDumper dumper(getpid());
58 ASSERT_TRUE(dumper.Init());
59
60 ASSERT_TRUE(dumper.FindMapping(reinterpret_cast<void*>(getpid)));
61 ASSERT_TRUE(dumper.FindMapping(reinterpret_cast<void*>(printf)));
62 ASSERT_FALSE(dumper.FindMapping(NULL));
63}
64
65TEST(LinuxDumperTest, ThreadList) {
66 LinuxDumper dumper(getpid());
67 ASSERT_TRUE(dumper.Init());
68
nealsidde545c02010-03-02 00:39:48 +000069 ASSERT_GE(dumper.threads().size(), (size_t)1);
nealsidb0baafc2009-08-17 23:12:53 +000070 bool found = false;
71 for (size_t i = 0; i < dumper.threads().size(); ++i) {
72 if (dumper.threads()[i] == getpid()) {
73 found = true;
74 break;
75 }
76 }
77}
78
nealsidde545c02010-03-02 00:39:48 +000079TEST(LinuxDumperTest, VerifyStackReadWithMultipleThreads) {
80 static const int kNumberOfThreadsInHelperProgram = 5;
81 char kNumberOfThreadsArgument[2];
82 sprintf(kNumberOfThreadsArgument, "%d", kNumberOfThreadsInHelperProgram);
83
ted.mielczarekef7262d2010-12-13 22:10:23 +000084 int fds[2];
85 ASSERT_NE(-1, pipe(fds));
86
nealsidde545c02010-03-02 00:39:48 +000087 pid_t child_pid = fork();
88 if (child_pid == 0) {
ted.mielczarekef7262d2010-12-13 22:10:23 +000089 // In child process.
90 close(fds[0]);
91
ted.mielczarekcfc86282010-10-20 15:51:38 +000092 // Locate helper binary next to the current binary.
93 char self_path[PATH_MAX];
94 if (readlink("/proc/self/exe", self_path, sizeof(self_path) - 1) == -1) {
95 FAIL() << "readlink failed: " << strerror(errno);
96 exit(1);
97 }
98 string helper_path(self_path);
99 size_t pos = helper_path.rfind('/');
100 if (pos == string::npos) {
101 FAIL() << "no trailing slash in path: " << helper_path;
102 exit(1);
103 }
104 helper_path.erase(pos + 1);
105 helper_path += "linux_dumper_unittest_helper";
106
ted.mielczarekef7262d2010-12-13 22:10:23 +0000107 // Pass the pipe fd and the number of threads as arguments.
108 char pipe_fd_string[8];
109 sprintf(pipe_fd_string, "%d", fds[1]);
ted.mielczarekcfc86282010-10-20 15:51:38 +0000110 execl(helper_path.c_str(),
nealsidde545c02010-03-02 00:39:48 +0000111 "linux_dumper_unittest_helper",
ted.mielczarekef7262d2010-12-13 22:10:23 +0000112 pipe_fd_string,
nealsidde545c02010-03-02 00:39:48 +0000113 kNumberOfThreadsArgument,
114 NULL);
115 // Kill if we get here.
116 printf("Errno from exec: %d", errno);
ted.mielczarekcfc86282010-10-20 15:51:38 +0000117 FAIL() << "Exec of " << helper_path << " failed: " << strerror(errno);
nealsidde545c02010-03-02 00:39:48 +0000118 exit(0);
119 }
ted.mielczarekef7262d2010-12-13 22:10:23 +0000120 close(fds[1]);
121 // Wait for the child process to signal that it's ready.
122 struct pollfd pfd;
123 memset(&pfd, 0, sizeof(pfd));
124 pfd.fd = fds[0];
125 pfd.events = POLLIN | POLLERR;
126
127 const int r = HANDLE_EINTR(poll(&pfd, 1, 1000));
128 ASSERT_EQ(1, r);
129 ASSERT_TRUE(pfd.revents & POLLIN);
130 uint8_t junk;
131 read(fds[0], &junk, sizeof(junk));
132 close(fds[0]);
133
134 // Child is ready now.
nealsidde545c02010-03-02 00:39:48 +0000135 LinuxDumper dumper(child_pid);
ted.mielczarekcfc86282010-10-20 15:51:38 +0000136 ASSERT_TRUE(dumper.Init());
nealsidde545c02010-03-02 00:39:48 +0000137 EXPECT_EQ((size_t)kNumberOfThreadsInHelperProgram, dumper.threads().size());
138 EXPECT_TRUE(dumper.ThreadsSuspend());
139
140 ThreadInfo one_thread;
141 for(size_t i = 0; i < dumper.threads().size(); ++i) {
142 EXPECT_TRUE(dumper.ThreadInfoGet(dumper.threads()[i], &one_thread));
jimblandyb68b8002010-03-29 18:23:42 +0000143 // In the helper program, we stored a pointer to the thread id in a
144 // specific register. Check that we can recover its value.
nealsidde545c02010-03-02 00:39:48 +0000145#if defined(__ARM_EABI__)
jimblandyb68b8002010-03-29 18:23:42 +0000146 pid_t *process_tid_location = (pid_t *)(one_thread.regs.uregs[3]);
nealsidde545c02010-03-02 00:39:48 +0000147#elif defined(__i386)
jimblandyb68b8002010-03-29 18:23:42 +0000148 pid_t *process_tid_location = (pid_t *)(one_thread.regs.ecx);
nealsidde545c02010-03-02 00:39:48 +0000149#elif defined(__x86_64)
jimblandyb68b8002010-03-29 18:23:42 +0000150 pid_t *process_tid_location = (pid_t *)(one_thread.regs.rcx);
nealsidde545c02010-03-02 00:39:48 +0000151#else
jimblandyb68b8002010-03-29 18:23:42 +0000152#error This test has not been ported to this platform.
nealsidde545c02010-03-02 00:39:48 +0000153#endif
154 pid_t one_thread_id;
155 dumper.CopyFromProcess(&one_thread_id,
156 dumper.threads()[i],
157 process_tid_location,
158 4);
159 EXPECT_EQ(dumper.threads()[i], one_thread_id);
160 }
161 kill(child_pid, SIGKILL);
162}
163
nealsidb0baafc2009-08-17 23:12:53 +0000164TEST(LinuxDumperTest, BuildProcPath) {
165 const pid_t pid = getpid();
166 LinuxDumper dumper(pid);
167
168 char maps_path[256] = "dummymappath";
169 char maps_path_expected[256];
170 snprintf(maps_path_expected, sizeof(maps_path_expected),
171 "/proc/%d/maps", pid);
172 dumper.BuildProcPath(maps_path, pid, "maps");
173 ASSERT_STREQ(maps_path, maps_path_expected);
174
175 // In release mode, we expect BuildProcPath to handle the invalid
176 // parameters correctly and fill map_path with an empty
177 // NULL-terminated string.
178#ifdef NDEBUG
179 snprintf(maps_path, sizeof(maps_path), "dummymappath");
180 dumper.BuildProcPath(maps_path, 0, "maps");
181 EXPECT_STREQ(maps_path, "");
182
183 snprintf(maps_path, sizeof(maps_path), "dummymappath");
184 dumper.BuildProcPath(maps_path, getpid(), "");
185 EXPECT_STREQ(maps_path, "");
186
187 snprintf(maps_path, sizeof(maps_path), "dummymappath");
188 dumper.BuildProcPath(maps_path, getpid(), NULL);
189 EXPECT_STREQ(maps_path, "");
190#endif
191}
192
nealsidde545c02010-03-02 00:39:48 +0000193#if !defined(__ARM_EABI__)
nealsidb0baafc2009-08-17 23:12:53 +0000194TEST(LinuxDumperTest, MappingsIncludeLinuxGate) {
195 LinuxDumper dumper(getpid());
196 ASSERT_TRUE(dumper.Init());
197
198 void* linux_gate_loc = dumper.FindBeginningOfLinuxGateSharedLibrary(getpid());
nealsidde545c02010-03-02 00:39:48 +0000199 ASSERT_TRUE(linux_gate_loc);
200 bool found_linux_gate = false;
nealsidb0baafc2009-08-17 23:12:53 +0000201
nealsidde545c02010-03-02 00:39:48 +0000202 const wasteful_vector<MappingInfo*> mappings = dumper.mappings();
203 const MappingInfo* mapping;
204 for (unsigned i = 0; i < mappings.size(); ++i) {
205 mapping = mappings[i];
206 if (!strcmp(mapping->name, kLinuxGateLibraryName)) {
207 found_linux_gate = true;
208 break;
nealsidb0baafc2009-08-17 23:12:53 +0000209 }
nealsidb0baafc2009-08-17 23:12:53 +0000210 }
nealsidde545c02010-03-02 00:39:48 +0000211 EXPECT_TRUE(found_linux_gate);
212 EXPECT_EQ(linux_gate_loc, reinterpret_cast<void*>(mapping->start_addr));
213 EXPECT_EQ(0, memcmp(linux_gate_loc, ELFMAG, SELFMAG));
nealsidb0baafc2009-08-17 23:12:53 +0000214}
nealsidde545c02010-03-02 00:39:48 +0000215#endif
ted.mielczarek0a5fc5d2009-12-23 17:09:27 +0000216
217TEST(LinuxDumperTest, FileIDsMatch) {
218 // Calculate the File ID of our binary using both
219 // FileID::ElfFileIdentifier and LinuxDumper::ElfFileIdentifierForMapping
220 // and ensure that we get the same result from both.
221 char exe_name[PATH_MAX];
222 ssize_t len = readlink("/proc/self/exe", exe_name, PATH_MAX - 1);
223 ASSERT_NE(len, -1);
224 exe_name[len] = '\0';
225
226 int fds[2];
227 ASSERT_NE(-1, pipe(fds));
228
229 // fork a child so we can ptrace it
230 const pid_t child = fork();
231 if (child == 0) {
232 close(fds[1]);
233 // now wait forever for the parent
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 LinuxDumper dumper(child);
242 ASSERT_TRUE(dumper.Init());
243 const wasteful_vector<MappingInfo*> mappings = dumper.mappings();
244 bool found_exe = false;
245 unsigned i;
246 for (i = 0; i < mappings.size(); ++i) {
247 const MappingInfo* mapping = mappings[i];
248 if (!strcmp(mapping->name, exe_name)) {
249 found_exe = true;
250 break;
251 }
252 }
253 ASSERT_TRUE(found_exe);
254
255 uint8_t identifier1[sizeof(MDGUID)];
256 uint8_t identifier2[sizeof(MDGUID)];
ted.mielczarekef7262d2010-12-13 22:10:23 +0000257 EXPECT_TRUE(dumper.ElfFileIdentifierForMapping(*mappings[i], i,
258 identifier1));
ted.mielczarek0a5fc5d2009-12-23 17:09:27 +0000259 FileID fileid(exe_name);
260 EXPECT_TRUE(fileid.ElfFileIdentifier(identifier2));
261 char identifier_string1[37];
262 char identifier_string2[37];
263 FileID::ConvertIdentifierToString(identifier1, identifier_string1,
264 37);
265 FileID::ConvertIdentifierToString(identifier2, identifier_string2,
266 37);
267 EXPECT_STREQ(identifier_string1, identifier_string2);
268 close(fds[1]);
269}