OLD | NEW |
| (Empty) |
1 // Copyright (c) 2010 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 | |
30 // Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> | |
31 | |
32 // module_unittest.cc: Unit tests for google_breakpad::Module. | |
33 | |
34 #include <cerrno> | |
35 #include <cstdio> | |
36 #include <cstdlib> | |
37 #include <cstring> | |
38 | |
39 #include <algorithm> | |
40 #include <string> | |
41 | |
42 #include "breakpad_googletest_includes.h" | |
43 #include "common/module.h" | |
44 | |
45 using google_breakpad::Module; | |
46 using std::string; | |
47 using std::vector; | |
48 using testing::ContainerEq; | |
49 | |
50 // Return a FILE * referring to a temporary file that will be deleted | |
51 // automatically when the stream is closed or the program exits. | |
52 FILE *checked_tmpfile() { | |
53 FILE *f = tmpfile(); | |
54 if (!f) { | |
55 fprintf(stderr, "error creating temporary file: %s\n", strerror(errno)); | |
56 exit(1); | |
57 } | |
58 return f; | |
59 } | |
60 | |
61 // Read from STREAM until end of file, and return the contents as a | |
62 // string. | |
63 string checked_read(FILE *stream) { | |
64 string contents; | |
65 int c; | |
66 while ((c = getc(stream)) != EOF) | |
67 contents.push_back(c); | |
68 if (ferror(stream)) { | |
69 fprintf(stderr, "error reading temporary file contents: %s\n", | |
70 strerror(errno)); | |
71 exit(1); | |
72 } | |
73 return contents; | |
74 } | |
75 | |
76 // Apply 'fflush' to STREAM, and check for errors. | |
77 void checked_fflush(FILE *stream) { | |
78 if (fflush(stream) == EOF) { | |
79 fprintf(stderr, "error flushing temporary file stream: %s\n", | |
80 strerror(errno)); | |
81 exit(1); | |
82 } | |
83 } | |
84 | |
85 // Apply 'fclose' to STREAM, and check for errors. | |
86 void checked_fclose(FILE *stream) { | |
87 if (fclose(stream) == EOF) { | |
88 fprintf(stderr, "error closing temporary file stream: %s\n", | |
89 strerror(errno)); | |
90 exit(1); | |
91 } | |
92 } | |
93 | |
94 #define MODULE_NAME "name with spaces" | |
95 #define MODULE_OS "os-name" | |
96 #define MODULE_ARCH "architecture" | |
97 #define MODULE_ID "id-string" | |
98 | |
99 TEST(Write, Header) { | |
100 FILE *f = checked_tmpfile(); | |
101 Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); | |
102 m.Write(f); | |
103 checked_fflush(f); | |
104 rewind(f); | |
105 string contents = checked_read(f); | |
106 checked_fclose(f); | |
107 EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n", | |
108 contents.c_str()); | |
109 } | |
110 | |
111 TEST(Write, OneLineFunc) { | |
112 FILE *f = checked_tmpfile(); | |
113 Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); | |
114 | |
115 Module::File *file = m.FindFile("file_name.cc"); | |
116 Module::Function *function = new(Module::Function); | |
117 function->name = "function_name"; | |
118 function->address = 0xe165bf8023b9d9abLL; | |
119 function->size = 0x1e4bb0eb1cbf5b09LL; | |
120 function->parameter_size = 0x772beee89114358aLL; | |
121 Module::Line line = { 0xe165bf8023b9d9abLL, 0x1e4bb0eb1cbf5b09LL, | |
122 file, 67519080 }; | |
123 function->lines.push_back(line); | |
124 m.AddFunction(function); | |
125 | |
126 m.Write(f); | |
127 checked_fflush(f); | |
128 rewind(f); | |
129 string contents = checked_read(f); | |
130 checked_fclose(f); | |
131 EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n" | |
132 "FILE 0 file_name.cc\n" | |
133 "FUNC e165bf8023b9d9ab 1e4bb0eb1cbf5b09 772beee89114358a" | |
134 " function_name\n" | |
135 "e165bf8023b9d9ab 1e4bb0eb1cbf5b09 67519080 0\n", | |
136 contents.c_str()); | |
137 } | |
138 | |
139 TEST(Write, RelativeLoadAddress) { | |
140 FILE *f = checked_tmpfile(); | |
141 Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); | |
142 | |
143 m.SetLoadAddress(0x2ab698b0b6407073LL); | |
144 | |
145 // Some source files. We will expect to see them in lexicographic order. | |
146 Module::File *file1 = m.FindFile("filename-b.cc"); | |
147 Module::File *file2 = m.FindFile("filename-a.cc"); | |
148 | |
149 // A function. | |
150 Module::Function *function = new(Module::Function); | |
151 function->name = "A_FLIBBERTIJIBBET::a_will_o_the_wisp(a clown)"; | |
152 function->address = 0xbec774ea5dd935f3LL; | |
153 function->size = 0x2922088f98d3f6fcLL; | |
154 function->parameter_size = 0xe5e9aa008bd5f0d0LL; | |
155 | |
156 // Some source lines. The module should not sort these. | |
157 Module::Line line1 = { 0xbec774ea5dd935f3LL, 0x1c2be6d6c5af2611LL, | |
158 file1, 41676901 }; | |
159 Module::Line line2 = { 0xdaf35bc123885c04LL, 0xcf621b8d324d0ebLL, | |
160 file2, 67519080 }; | |
161 function->lines.push_back(line2); | |
162 function->lines.push_back(line1); | |
163 | |
164 m.AddFunction(function); | |
165 | |
166 // Some stack information. | |
167 Module::StackFrameEntry *entry = new Module::StackFrameEntry(); | |
168 entry->address = 0x30f9e5c83323973dULL; | |
169 entry->size = 0x49fc9ca7c7c13dc2ULL; | |
170 entry->initial_rules[".cfa"] = "he was a handsome man"; | |
171 entry->initial_rules["and"] = "what i want to know is"; | |
172 entry->rule_changes[0x30f9e5c83323973eULL]["how"] = | |
173 "do you like your blueeyed boy"; | |
174 entry->rule_changes[0x30f9e5c83323973eULL]["Mister"] = "Death"; | |
175 m.AddStackFrameEntry(entry); | |
176 | |
177 m.Write(f); | |
178 checked_fflush(f); | |
179 rewind(f); | |
180 string contents = checked_read(f); | |
181 checked_fclose(f); | |
182 EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n" | |
183 "FILE 0 filename-a.cc\n" | |
184 "FILE 1 filename-b.cc\n" | |
185 "FUNC 9410dc39a798c580 2922088f98d3f6fc e5e9aa008bd5f0d0" | |
186 " A_FLIBBERTIJIBBET::a_will_o_the_wisp(a clown)\n" | |
187 "b03cc3106d47eb91 cf621b8d324d0eb 67519080 0\n" | |
188 "9410dc39a798c580 1c2be6d6c5af2611 41676901 1\n" | |
189 "STACK CFI INIT 6434d177ce326ca 49fc9ca7c7c13dc2" | |
190 " .cfa: he was a handsome man" | |
191 " and: what i want to know is\n" | |
192 "STACK CFI 6434d177ce326cb" | |
193 " Mister: Death" | |
194 " how: do you like your blueeyed boy\n", | |
195 contents.c_str()); | |
196 } | |
197 | |
198 TEST(Write, OmitUnusedFiles) { | |
199 Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); | |
200 | |
201 // Create some source files. | |
202 Module::File *file1 = m.FindFile("filename1"); | |
203 m.FindFile("filename2"); // not used by any line | |
204 Module::File *file3 = m.FindFile("filename3"); | |
205 | |
206 // Create a function. | |
207 Module::Function *function = new(Module::Function); | |
208 function->name = "function_name"; | |
209 function->address = 0x9b926d464f0b9384LL; | |
210 function->size = 0x4f524a4ba795e6a6LL; | |
211 function->parameter_size = 0xbbe8133a6641c9b7LL; | |
212 | |
213 // Source files that refer to some files, but not others. | |
214 Module::Line line1 = { 0x595fa44ebacc1086LL, 0x1e1e0191b066c5b3LL, | |
215 file1, 137850127 }; | |
216 Module::Line line2 = { 0x401ce8c8a12d25e3LL, 0x895751c41b8d2ce2LL, | |
217 file3, 28113549 }; | |
218 function->lines.push_back(line1); | |
219 function->lines.push_back(line2); | |
220 m.AddFunction(function); | |
221 | |
222 m.AssignSourceIds(); | |
223 | |
224 vector<Module::File *> vec; | |
225 m.GetFiles(&vec); | |
226 EXPECT_EQ((size_t) 3, vec.size()); | |
227 EXPECT_STREQ("filename1", vec[0]->name.c_str()); | |
228 EXPECT_NE(-1, vec[0]->source_id); | |
229 // Expect filename2 not to be used. | |
230 EXPECT_STREQ("filename2", vec[1]->name.c_str()); | |
231 EXPECT_EQ(-1, vec[1]->source_id); | |
232 EXPECT_STREQ("filename3", vec[2]->name.c_str()); | |
233 EXPECT_NE(-1, vec[2]->source_id); | |
234 | |
235 FILE *f = checked_tmpfile(); | |
236 m.Write(f); | |
237 checked_fflush(f); | |
238 rewind(f); | |
239 string contents = checked_read(f); | |
240 checked_fclose(f); | |
241 EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n" | |
242 "FILE 0 filename1\n" | |
243 "FILE 1 filename3\n" | |
244 "FUNC 9b926d464f0b9384 4f524a4ba795e6a6 bbe8133a6641c9b7" | |
245 " function_name\n" | |
246 "595fa44ebacc1086 1e1e0191b066c5b3 137850127 0\n" | |
247 "401ce8c8a12d25e3 895751c41b8d2ce2 28113549 1\n", | |
248 contents.c_str()); | |
249 } | |
250 | |
251 TEST(Construct, AddFunctions) { | |
252 FILE *f = checked_tmpfile(); | |
253 Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); | |
254 | |
255 // Two functions. | |
256 Module::Function *function1 = new(Module::Function); | |
257 function1->name = "_without_form"; | |
258 function1->address = 0xd35024aa7ca7da5cLL; | |
259 function1->size = 0x200b26e605f99071LL; | |
260 function1->parameter_size = 0xf14ac4fed48c4a99LL; | |
261 | |
262 Module::Function *function2 = new(Module::Function); | |
263 function2->name = "_and_void"; | |
264 function2->address = 0x2987743d0b35b13fLL; | |
265 function2->size = 0xb369db048deb3010LL; | |
266 function2->parameter_size = 0x938e556cb5a79988LL; | |
267 | |
268 // Put them in a vector. | |
269 vector<Module::Function *> vec; | |
270 vec.push_back(function1); | |
271 vec.push_back(function2); | |
272 | |
273 m.AddFunctions(vec.begin(), vec.end()); | |
274 | |
275 m.Write(f); | |
276 checked_fflush(f); | |
277 rewind(f); | |
278 string contents = checked_read(f); | |
279 checked_fclose(f); | |
280 EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n" | |
281 "FUNC d35024aa7ca7da5c 200b26e605f99071 f14ac4fed48c4a99" | |
282 " _without_form\n" | |
283 "FUNC 2987743d0b35b13f b369db048deb3010 938e556cb5a79988" | |
284 " _and_void\n", | |
285 contents.c_str()); | |
286 | |
287 // Check that m.GetFunctions returns the functions we expect. | |
288 vec.clear(); | |
289 m.GetFunctions(&vec, vec.end()); | |
290 EXPECT_TRUE(vec.end() != find(vec.begin(), vec.end(), function1)); | |
291 EXPECT_TRUE(vec.end() != find(vec.begin(), vec.end(), function2)); | |
292 EXPECT_EQ((size_t) 2, vec.size()); | |
293 } | |
294 | |
295 TEST(Construct, AddFrames) { | |
296 FILE *f = checked_tmpfile(); | |
297 Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); | |
298 | |
299 // First STACK CFI entry, with no initial rules or deltas. | |
300 Module::StackFrameEntry *entry1 = new Module::StackFrameEntry(); | |
301 entry1->address = 0xddb5f41285aa7757ULL; | |
302 entry1->size = 0x1486493370dc5073ULL; | |
303 m.AddStackFrameEntry(entry1); | |
304 | |
305 // Second STACK CFI entry, with initial rules but no deltas. | |
306 Module::StackFrameEntry *entry2 = new Module::StackFrameEntry(); | |
307 entry2->address = 0x8064f3af5e067e38ULL; | |
308 entry2->size = 0x0de2a5ee55509407ULL; | |
309 entry2->initial_rules[".cfa"] = "I think that I shall never see"; | |
310 entry2->initial_rules["stromboli"] = "a poem lovely as a tree"; | |
311 entry2->initial_rules["cannoli"] = "a tree whose hungry mouth is prest"; | |
312 m.AddStackFrameEntry(entry2); | |
313 | |
314 // Third STACK CFI entry, with initial rules and deltas. | |
315 Module::StackFrameEntry *entry3 = new Module::StackFrameEntry(); | |
316 entry3->address = 0x5e8d0db0a7075c6cULL; | |
317 entry3->size = 0x1c7edb12a7aea229ULL; | |
318 entry3->initial_rules[".cfa"] = "Whose woods are these"; | |
319 entry3->rule_changes[0x47ceb0f63c269d7fULL]["calzone"] = | |
320 "the village though"; | |
321 entry3->rule_changes[0x47ceb0f63c269d7fULL]["cannoli"] = | |
322 "he will not see me stopping here"; | |
323 entry3->rule_changes[0x36682fad3763ffffULL]["stromboli"] = | |
324 "his house is in"; | |
325 entry3->rule_changes[0x36682fad3763ffffULL][".cfa"] = | |
326 "I think I know"; | |
327 m.AddStackFrameEntry(entry3); | |
328 | |
329 // Check that Write writes STACK CFI records properly. | |
330 m.Write(f); | |
331 checked_fflush(f); | |
332 rewind(f); | |
333 string contents = checked_read(f); | |
334 checked_fclose(f); | |
335 EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n" | |
336 "STACK CFI INIT ddb5f41285aa7757 1486493370dc5073 \n" | |
337 "STACK CFI INIT 8064f3af5e067e38 de2a5ee55509407" | |
338 " .cfa: I think that I shall never see" | |
339 " cannoli: a tree whose hungry mouth is prest" | |
340 " stromboli: a poem lovely as a tree\n" | |
341 "STACK CFI INIT 5e8d0db0a7075c6c 1c7edb12a7aea229" | |
342 " .cfa: Whose woods are these\n" | |
343 "STACK CFI 36682fad3763ffff" | |
344 " .cfa: I think I know" | |
345 " stromboli: his house is in\n" | |
346 "STACK CFI 47ceb0f63c269d7f" | |
347 " calzone: the village though" | |
348 " cannoli: he will not see me stopping here\n", | |
349 contents.c_str()); | |
350 | |
351 // Check that GetStackFrameEntries works. | |
352 vector<Module::StackFrameEntry *> entries; | |
353 m.GetStackFrameEntries(&entries); | |
354 ASSERT_EQ(3U, entries.size()); | |
355 // Check first entry. | |
356 EXPECT_EQ(0xddb5f41285aa7757ULL, entries[0]->address); | |
357 EXPECT_EQ(0x1486493370dc5073ULL, entries[0]->size); | |
358 ASSERT_EQ(0U, entries[0]->initial_rules.size()); | |
359 ASSERT_EQ(0U, entries[0]->rule_changes.size()); | |
360 // Check second entry. | |
361 EXPECT_EQ(0x8064f3af5e067e38ULL, entries[1]->address); | |
362 EXPECT_EQ(0x0de2a5ee55509407ULL, entries[1]->size); | |
363 ASSERT_EQ(3U, entries[1]->initial_rules.size()); | |
364 Module::RuleMap entry2_initial; | |
365 entry2_initial[".cfa"] = "I think that I shall never see"; | |
366 entry2_initial["stromboli"] = "a poem lovely as a tree"; | |
367 entry2_initial["cannoli"] = "a tree whose hungry mouth is prest"; | |
368 EXPECT_THAT(entries[1]->initial_rules, ContainerEq(entry2_initial)); | |
369 ASSERT_EQ(0U, entries[1]->rule_changes.size()); | |
370 // Check third entry. | |
371 EXPECT_EQ(0x5e8d0db0a7075c6cULL, entries[2]->address); | |
372 EXPECT_EQ(0x1c7edb12a7aea229ULL, entries[2]->size); | |
373 Module::RuleMap entry3_initial; | |
374 entry3_initial[".cfa"] = "Whose woods are these"; | |
375 EXPECT_THAT(entries[2]->initial_rules, ContainerEq(entry3_initial)); | |
376 Module::RuleChangeMap entry3_changes; | |
377 entry3_changes[0x36682fad3763ffffULL][".cfa"] = "I think I know"; | |
378 entry3_changes[0x36682fad3763ffffULL]["stromboli"] = "his house is in"; | |
379 entry3_changes[0x47ceb0f63c269d7fULL]["calzone"] = "the village though"; | |
380 entry3_changes[0x47ceb0f63c269d7fULL]["cannoli"] = | |
381 "he will not see me stopping here"; | |
382 EXPECT_THAT(entries[2]->rule_changes, ContainerEq(entry3_changes)); | |
383 } | |
384 | |
385 TEST(Construct, UniqueFiles) { | |
386 Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); | |
387 Module::File *file1 = m.FindFile("foo"); | |
388 Module::File *file2 = m.FindFile(string("bar")); | |
389 Module::File *file3 = m.FindFile(string("foo")); | |
390 Module::File *file4 = m.FindFile("bar"); | |
391 EXPECT_NE(file1, file2); | |
392 EXPECT_EQ(file1, file3); | |
393 EXPECT_EQ(file2, file4); | |
394 EXPECT_EQ(file1, m.FindExistingFile("foo")); | |
395 EXPECT_TRUE(m.FindExistingFile("baz") == NULL); | |
396 } | |
OLD | NEW |