OLD | NEW |
---|---|
1 // Copyright (c) 2009, Google Inc. | 1 // Copyright (c) 2009, Google Inc. |
2 // All rights reserved. | 2 // All rights reserved. |
3 // | 3 // |
4 // Redistribution and use in source and binary forms, with or without | 4 // Redistribution and use in source and binary forms, with or without |
5 // modification, are permitted provided that the following conditions are | 5 // modification, are permitted provided that the following conditions are |
6 // met: | 6 // met: |
7 // | 7 // |
8 // * Redistributions of source code must retain the above copyright | 8 // * Redistributions of source code must retain the above copyright |
9 // notice, this list of conditions and the following disclaimer. | 9 // notice, this list of conditions and the following disclaimer. |
10 // * Redistributions in binary form must reproduce the above | 10 // * Redistributions in binary form must reproduce the above |
(...skipping 10 matching lines...) Expand all Loading... | |
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 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. | 28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
29 | 29 |
30 #include <unistd.h> | 30 #include <unistd.h> |
31 #include <signal.h> | |
32 #include <sys/types.h> | |
31 | 33 |
34 #include "breakpad_googletest_includes.h" | |
35 #include "common/linux/memory.h" | |
32 #include "client/linux/minidump_writer/linux_dumper.h" | 36 #include "client/linux/minidump_writer/linux_dumper.h" |
33 #include "breakpad_googletest_includes.h" | |
34 | 37 |
35 using namespace google_breakpad; | 38 using namespace google_breakpad; |
36 | 39 |
37 namespace { | 40 namespace { |
38 typedef testing::Test LinuxDumperTest; | 41 typedef testing::Test LinuxDumperTest; |
39 } | 42 } |
40 | 43 |
41 TEST(LinuxDumperTest, Setup) { | 44 TEST(LinuxDumperTest, Setup) { |
42 LinuxDumper dumper(getpid()); | 45 LinuxDumper dumper(getpid()); |
43 } | 46 } |
44 | 47 |
45 TEST(LinuxDumperTest, FindMappings) { | 48 TEST(LinuxDumperTest, FindMappings) { |
46 LinuxDumper dumper(getpid()); | 49 LinuxDumper dumper(getpid()); |
47 ASSERT_TRUE(dumper.Init()); | 50 ASSERT_TRUE(dumper.Init()); |
48 | 51 |
49 ASSERT_TRUE(dumper.FindMapping(reinterpret_cast<void*>(getpid))); | 52 ASSERT_TRUE(dumper.FindMapping(reinterpret_cast<void*>(getpid))); |
50 ASSERT_TRUE(dumper.FindMapping(reinterpret_cast<void*>(printf))); | 53 ASSERT_TRUE(dumper.FindMapping(reinterpret_cast<void*>(printf))); |
51 ASSERT_FALSE(dumper.FindMapping(NULL)); | 54 ASSERT_FALSE(dumper.FindMapping(NULL)); |
52 } | 55 } |
53 | 56 |
54 TEST(LinuxDumperTest, ThreadList) { | 57 TEST(LinuxDumperTest, ThreadList) { |
55 LinuxDumper dumper(getpid()); | 58 LinuxDumper dumper(getpid()); |
56 ASSERT_TRUE(dumper.Init()); | 59 ASSERT_TRUE(dumper.Init()); |
57 | 60 |
58 ASSERT_GE(dumper.threads().size(), 1); | 61 ASSERT_GE(dumper.threads().size(), (size_t)1); |
59 bool found = false; | 62 bool found = false; |
60 for (size_t i = 0; i < dumper.threads().size(); ++i) { | 63 for (size_t i = 0; i < dumper.threads().size(); ++i) { |
61 if (dumper.threads()[i] == getpid()) { | 64 if (dumper.threads()[i] == getpid()) { |
62 found = true; | 65 found = true; |
63 break; | 66 break; |
64 } | 67 } |
65 } | 68 } |
66 } | 69 } |
67 | 70 |
71 TEST(LinuxDumperTest, VerifyStackReadWithMultipleThreads) { | |
72 static const int kNumberOfThreadsInHelperProgram = 5; | |
73 char kNumberOfThreadsArgument[2]; | |
74 sprintf(kNumberOfThreadsArgument, "%d", kNumberOfThreadsInHelperProgram); | |
awong
2010/02/08 20:52:55
Can you use StrintPrintf instead (if you have it)?
| |
75 pid_t child_pid = fork(); | |
76 if (child_pid == 0) { | |
77 // Set the number of threads | |
78 execl("./linux_dumper_unittest_helper", | |
79 "linux_dumper_unittest_helper", | |
80 kNumberOfThreadsArgument, | |
81 NULL); | |
82 // Kill if we get here. | |
83 printf("Errno from exec: %d", errno); | |
84 FAIL() << "Exec failed: " << strerror(errno); | |
85 } | |
86 // The sleep is flaky, but prevents us from reading | |
87 // the child process before all threads have been created. | |
88 sleep(1); | |
89 LinuxDumper dumper(child_pid); | |
90 EXPECT_TRUE(dumper.Init()); | |
91 EXPECT_EQ((size_t)kNumberOfThreadsInHelperProgram, dumper.threads().size()); | |
92 EXPECT_TRUE(dumper.ThreadsSuspend()); | |
93 | |
94 ThreadInfo one_thread; | |
95 for(size_t i = 0; i < dumper.threads().size(); ++i) { | |
96 EXPECT_TRUE(dumper.ThreadInfoGet(dumper.threads()[i], &one_thread)); | |
97 // We know the threads are in a function which has allocated exactly | |
98 // one word off the stack to store its thread id. | |
99 #if defined(__ARM_EABI__) | |
100 void* process_tid_location = (void *)(one_thread.regs.uregs[11] - 8); | |
101 #elif defined(__i386) | |
102 void* process_tid_location = (void *)(one_thread.regs.ebp - 4); | |
103 #elif defined(__x86_64) | |
104 void* process_tid_location = (void *)(one_thread.regs.ebp - 8); | |
Ted Mielczarek
2010/02/05 17:04:07
This should be one_thread.regs.rbp. Also, this tes
| |
105 #else | |
106 #error Platform not supported! | |
107 #endif | |
108 pid_t one_thread_id; | |
109 dumper.CopyFromProcess(&one_thread_id, | |
110 dumper.threads()[i], | |
111 process_tid_location, | |
112 4); | |
113 EXPECT_EQ(dumper.threads()[i], one_thread_id); | |
114 } | |
115 kill(child_pid, SIGKILL); | |
116 } | |
117 | |
68 TEST(LinuxDumperTest, BuildProcPath) { | 118 TEST(LinuxDumperTest, BuildProcPath) { |
69 const pid_t pid = getpid(); | 119 const pid_t pid = getpid(); |
70 LinuxDumper dumper(pid); | 120 LinuxDumper dumper(pid); |
71 | 121 |
72 char maps_path[256] = "dummymappath"; | 122 char maps_path[256] = "dummymappath"; |
73 char maps_path_expected[256]; | 123 char maps_path_expected[256]; |
74 snprintf(maps_path_expected, sizeof(maps_path_expected), | 124 snprintf(maps_path_expected, sizeof(maps_path_expected), |
75 "/proc/%d/maps", pid); | 125 "/proc/%d/maps", pid); |
76 dumper.BuildProcPath(maps_path, pid, "maps"); | 126 dumper.BuildProcPath(maps_path, pid, "maps"); |
77 ASSERT_STREQ(maps_path, maps_path_expected); | 127 ASSERT_STREQ(maps_path, maps_path_expected); |
78 | 128 |
79 // In release mode, we expect BuildProcPath to handle the invalid | 129 // In release mode, we expect BuildProcPath to handle the invalid |
80 // parameters correctly and fill map_path with an empty | 130 // parameters correctly and fill map_path with an empty |
81 // NULL-terminated string. | 131 // NULL-terminated string. |
82 #ifdef NDEBUG | 132 #ifdef NDEBUG |
83 snprintf(maps_path, sizeof(maps_path), "dummymappath"); | 133 snprintf(maps_path, sizeof(maps_path), "dummymappath"); |
84 dumper.BuildProcPath(maps_path, 0, "maps"); | 134 dumper.BuildProcPath(maps_path, 0, "maps"); |
85 EXPECT_STREQ(maps_path, ""); | 135 EXPECT_STREQ(maps_path, ""); |
86 | 136 |
87 snprintf(maps_path, sizeof(maps_path), "dummymappath"); | 137 snprintf(maps_path, sizeof(maps_path), "dummymappath"); |
88 dumper.BuildProcPath(maps_path, getpid(), ""); | 138 dumper.BuildProcPath(maps_path, getpid(), ""); |
89 EXPECT_STREQ(maps_path, ""); | 139 EXPECT_STREQ(maps_path, ""); |
90 | 140 |
91 snprintf(maps_path, sizeof(maps_path), "dummymappath"); | 141 snprintf(maps_path, sizeof(maps_path), "dummymappath"); |
92 dumper.BuildProcPath(maps_path, getpid(), NULL); | 142 dumper.BuildProcPath(maps_path, getpid(), NULL); |
93 EXPECT_STREQ(maps_path, ""); | 143 EXPECT_STREQ(maps_path, ""); |
94 #endif | 144 #endif |
95 } | 145 } |
96 | 146 |
147 #if !defined(__ARM_EABI__) | |
97 TEST(LinuxDumperTest, MappingsIncludeLinuxGate) { | 148 TEST(LinuxDumperTest, MappingsIncludeLinuxGate) { |
98 LinuxDumper dumper(getpid()); | 149 LinuxDumper dumper(getpid()); |
99 ASSERT_TRUE(dumper.Init()); | 150 ASSERT_TRUE(dumper.Init()); |
100 | 151 |
101 void* linux_gate_loc = dumper.FindBeginningOfLinuxGateSharedLibrary(getpid()); | 152 void* linux_gate_loc = dumper.FindBeginningOfLinuxGateSharedLibrary(getpid()); |
102 if (linux_gate_loc) { | 153 ASSERT_TRUE(linux_gate_loc); |
103 bool found_linux_gate = false; | 154 bool found_linux_gate = false; |
104 | 155 |
105 const wasteful_vector<MappingInfo*> mappings = dumper.mappings(); | 156 const wasteful_vector<MappingInfo*> mappings = dumper.mappings(); |
106 const MappingInfo* mapping; | 157 const MappingInfo* mapping; |
107 for (unsigned i = 0; i < mappings.size(); ++i) { | 158 for (unsigned i = 0; i < mappings.size(); ++i) { |
108 mapping = mappings[i]; | 159 mapping = mappings[i]; |
109 if (!strcmp(mapping->name, kLinuxGateLibraryName)) { | 160 if (!strcmp(mapping->name, kLinuxGateLibraryName)) { |
110 found_linux_gate = true; | 161 found_linux_gate = true; |
111 break; | 162 break; |
112 } | |
113 } | 163 } |
114 EXPECT_TRUE(found_linux_gate); | |
115 EXPECT_EQ(linux_gate_loc, reinterpret_cast<void*>(mapping->start_addr)); | |
116 EXPECT_EQ(0, memcmp(linux_gate_loc, ELFMAG, SELFMAG)); | |
117 } | 164 } |
165 EXPECT_TRUE(found_linux_gate); | |
166 EXPECT_EQ(linux_gate_loc, reinterpret_cast<void*>(mapping->start_addr)); | |
167 EXPECT_EQ(0, memcmp(linux_gate_loc, ELFMAG, SELFMAG)); | |
118 } | 168 } |
169 #endif | |
OLD | NEW |