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 // fast_source_line_resolver_unittest.cc: Unit tests for FastSourceLineResolver.
31 // Two different approaches for testing fast source line resolver:
32 // First, use the same unit test data for basic source line resolver.
33 // Second, read data from symbol files, load them as basic modules, and then
34 // serialize them and load the serialized data as fast modules. Then compare
35 // modules to assure the fast module contains exactly the same data as
36 // basic module.
37 //
38 // Author: Siyang Xie (lambxsy@google.com)
39
40 #include <assert.h>
41 #include <stdio.h>
42
43 #include <sstream>
44 #include <string>
45
46 #include "breakpad_googletest_includes.h"
47 #include "common/using_std_string.h"
48 #include "google_breakpad/processor/code_module.h"
49 #include "google_breakpad/processor/stack_frame.h"
50 #include "google_breakpad/processor/memory_region.h"
51 #include "processor/logging.h"
52 #include "processor/module_serializer.h"
53 #include "processor/module_comparer.h"
54
55 namespace {
56
57 using google_breakpad::SourceLineResolverBase;
58 using google_breakpad::BasicSourceLineResolver;
59 using google_breakpad::FastSourceLineResolver;
60 using google_breakpad::ModuleSerializer;
61 using google_breakpad::ModuleComparer;
62 using google_breakpad::CFIFrameInfo;
63 using google_breakpad::CodeModule;
64 using google_breakpad::MemoryRegion;
65 using google_breakpad::StackFrame;
66 using google_breakpad::WindowsFrameInfo;
67 using google_breakpad::linked_ptr;
68 using google_breakpad::scoped_ptr;
69
70 class TestCodeModule : public CodeModule {
71 public:
TestCodeModule(string code_file)72 explicit TestCodeModule(string code_file) : code_file_(code_file) {}
~TestCodeModule()73 virtual ~TestCodeModule() {}
74
base_address() const75 virtual uint64_t base_address() const { return 0; }
size() const76 virtual uint64_t size() const { return 0xb000; }
code_file() const77 virtual string code_file() const { return code_file_; }
code_identifier() const78 virtual string code_identifier() const { return ""; }
debug_file() const79 virtual string debug_file() const { return ""; }
debug_identifier() const80 virtual string debug_identifier() const { return ""; }
version() const81 virtual string version() const { return ""; }
Copy() const82 virtual const CodeModule* Copy() const {
83 return new TestCodeModule(code_file_);
84 }
85
86 private:
87 string code_file_;
88 };
89
90 // A mock memory region object, for use by the STACK CFI tests.
91 class MockMemoryRegion: public MemoryRegion {
GetBase() const92 uint64_t GetBase() const { return 0x10000; }
GetSize() const93 uint32_t GetSize() const { return 0x01000; }
GetMemoryAtAddress(uint64_t address,uint8_t * value) const94 bool GetMemoryAtAddress(uint64_t address, uint8_t *value) const {
95 *value = address & 0xff;
96 return true;
97 }
GetMemoryAtAddress(uint64_t address,uint16_t * value) const98 bool GetMemoryAtAddress(uint64_t address, uint16_t *value) const {
99 *value = address & 0xffff;
100 return true;
101 }
GetMemoryAtAddress(uint64_t address,uint32_t * value) const102 bool GetMemoryAtAddress(uint64_t address, uint32_t *value) const {
103 switch (address) {
104 case 0x10008: *value = 0x98ecadc3; break; // saved %ebx
105 case 0x1000c: *value = 0x878f7524; break; // saved %esi
106 case 0x10010: *value = 0x6312f9a5; break; // saved %edi
107 case 0x10014: *value = 0x10038; break; // caller's %ebp
108 case 0x10018: *value = 0xf6438648; break; // return address
109 default: *value = 0xdeadbeef; break; // junk
110 }
111 return true;
112 }
GetMemoryAtAddress(uint64_t address,uint64_t * value) const113 bool GetMemoryAtAddress(uint64_t address, uint64_t *value) const {
114 *value = address;
115 return true;
116 }
Print() const117 void Print() const {
118 assert(false);
119 }
120 };
121
122 // Verify that, for every association in ACTUAL, EXPECTED has the same
123 // association. (That is, ACTUAL's associations should be a subset of
124 // EXPECTED's.) Also verify that ACTUAL has associations for ".ra" and
125 // ".cfa".
VerifyRegisters(const char * file,int line,const CFIFrameInfo::RegisterValueMap<uint32_t> & expected,const CFIFrameInfo::RegisterValueMap<uint32_t> & actual)126 static bool VerifyRegisters(
127 const char *file, int line,
128 const CFIFrameInfo::RegisterValueMap<uint32_t> &expected,
129 const CFIFrameInfo::RegisterValueMap<uint32_t> &actual) {
130 CFIFrameInfo::RegisterValueMap<uint32_t>::const_iterator a;
131 a = actual.find(".cfa");
132 if (a == actual.end())
133 return false;
134 a = actual.find(".ra");
135 if (a == actual.end())
136 return false;
137 for (a = actual.begin(); a != actual.end(); a++) {
138 CFIFrameInfo::RegisterValueMap<uint32_t>::const_iterator e =
139 expected.find(a->first);
140 if (e == expected.end()) {
141 fprintf(stderr, "%s:%d: unexpected register '%s' recovered, value 0x%x\n",
142 file, line, a->first.c_str(), a->second);
143 return false;
144 }
145 if (e->second != a->second) {
146 fprintf(stderr,
147 "%s:%d: register '%s' recovered value was 0x%x, expected 0x%x\n",
148 file, line, a->first.c_str(), a->second, e->second);
149 return false;
150 }
151 // Don't complain if this doesn't recover all registers. Although
152 // the DWARF spec says that unmentioned registers are undefined,
153 // GCC uses omission to mean that they are unchanged.
154 }
155 return true;
156 }
157
VerifyEmpty(const StackFrame & frame)158 static bool VerifyEmpty(const StackFrame &frame) {
159 if (frame.function_name.empty() &&
160 frame.source_file_name.empty() &&
161 frame.source_line == 0)
162 return true;
163 return false;
164 }
165
ClearSourceLineInfo(StackFrame * frame)166 static void ClearSourceLineInfo(StackFrame *frame) {
167 frame->function_name.clear();
168 frame->module = NULL;
169 frame->source_file_name.clear();
170 frame->source_line = 0;
171 }
172
173 class TestFastSourceLineResolver : public ::testing::Test {
174 public:
SetUp()175 void SetUp() {
176 testdata_dir = string(getenv("srcdir") ? getenv("srcdir") : ".") +
177 "/src/processor/testdata";
178 }
179
symbol_file(int file_index)180 string symbol_file(int file_index) {
181 std::stringstream ss;
182 ss << testdata_dir << "/module" << file_index << ".out";
183 return ss.str();
184 }
185
186 ModuleSerializer serializer;
187 BasicSourceLineResolver basic_resolver;
188 FastSourceLineResolver fast_resolver;
189 ModuleComparer module_comparer;
190
191 string testdata_dir;
192 };
193
194 // Test adapted from basic_source_line_resolver_unittest.
TEST_F(TestFastSourceLineResolver,TestLoadAndResolve)195 TEST_F(TestFastSourceLineResolver, TestLoadAndResolve) {
196 TestCodeModule module1("module1");
197 ASSERT_TRUE(basic_resolver.LoadModule(&module1, symbol_file(1)));
198 ASSERT_TRUE(basic_resolver.HasModule(&module1));
199 // Convert module1 to fast_module:
200 ASSERT_TRUE(serializer.ConvertOneModule(
201 module1.code_file(), &basic_resolver, &fast_resolver));
202 ASSERT_TRUE(fast_resolver.HasModule(&module1));
203
204 TestCodeModule module2("module2");
205 ASSERT_TRUE(basic_resolver.LoadModule(&module2, symbol_file(2)));
206 ASSERT_TRUE(basic_resolver.HasModule(&module2));
207 // Convert module2 to fast_module:
208 ASSERT_TRUE(serializer.ConvertOneModule(
209 module2.code_file(), &basic_resolver, &fast_resolver));
210 ASSERT_TRUE(fast_resolver.HasModule(&module2));
211
212 StackFrame frame;
213 scoped_ptr<WindowsFrameInfo> windows_frame_info;
214 scoped_ptr<CFIFrameInfo> cfi_frame_info;
215 frame.instruction = 0x1000;
216 frame.module = NULL;
217 fast_resolver.FillSourceLineInfo(&frame);
218 ASSERT_FALSE(frame.module);
219 ASSERT_TRUE(frame.function_name.empty());
220 ASSERT_EQ(frame.function_base, 0U);
221 ASSERT_TRUE(frame.source_file_name.empty());
222 ASSERT_EQ(frame.source_line, 0);
223 ASSERT_EQ(frame.source_line_base, 0U);
224
225 frame.module = &module1;
226 fast_resolver.FillSourceLineInfo(&frame);
227 ASSERT_EQ(frame.function_name, "Function1_1");
228 ASSERT_TRUE(frame.module);
229 ASSERT_EQ(frame.module->code_file(), "module1");
230 ASSERT_EQ(frame.function_base, 0x1000U);
231 ASSERT_EQ(frame.source_file_name, "file1_1.cc");
232 ASSERT_EQ(frame.source_line, 44);
233 ASSERT_EQ(frame.source_line_base, 0x1000U);
234 windows_frame_info.reset(fast_resolver.FindWindowsFrameInfo(&frame));
235 ASSERT_TRUE(windows_frame_info.get());
236 ASSERT_FALSE(windows_frame_info->allocates_base_pointer);
237 ASSERT_EQ(windows_frame_info->program_string,
238 "$eip 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ =");
239
240 ClearSourceLineInfo(&frame);
241 frame.instruction = 0x800;
242 frame.module = &module1;
243 fast_resolver.FillSourceLineInfo(&frame);
244 ASSERT_TRUE(VerifyEmpty(frame));
245 windows_frame_info.reset(fast_resolver.FindWindowsFrameInfo(&frame));
246 ASSERT_FALSE(windows_frame_info.get());
247
248 frame.instruction = 0x1280;
249 fast_resolver.FillSourceLineInfo(&frame);
250 ASSERT_EQ(frame.function_name, "Function1_3");
251 ASSERT_TRUE(frame.source_file_name.empty());
252 ASSERT_EQ(frame.source_line, 0);
253 windows_frame_info.reset(fast_resolver.FindWindowsFrameInfo(&frame));
254 ASSERT_TRUE(windows_frame_info.get());
255 ASSERT_EQ(windows_frame_info->type_, WindowsFrameInfo::STACK_INFO_UNKNOWN);
256 ASSERT_FALSE(windows_frame_info->allocates_base_pointer);
257 ASSERT_TRUE(windows_frame_info->program_string.empty());
258
259 frame.instruction = 0x1380;
260 fast_resolver.FillSourceLineInfo(&frame);
261 ASSERT_EQ(frame.function_name, "Function1_4");
262 ASSERT_TRUE(frame.source_file_name.empty());
263 ASSERT_EQ(frame.source_line, 0);
264 windows_frame_info.reset(fast_resolver.FindWindowsFrameInfo(&frame));
265 ASSERT_TRUE(windows_frame_info.get());
266 ASSERT_EQ(windows_frame_info->type_, WindowsFrameInfo::STACK_INFO_FRAME_DATA);
267 ASSERT_FALSE(windows_frame_info->allocates_base_pointer);
268 ASSERT_FALSE(windows_frame_info->program_string.empty());
269
270 frame.instruction = 0x2000;
271 windows_frame_info.reset(fast_resolver.FindWindowsFrameInfo(&frame));
272 ASSERT_FALSE(windows_frame_info.get());
273
274 // module1 has STACK CFI records covering 3d40..3def;
275 // module2 has STACK CFI records covering 3df0..3e9f;
276 // check that FindCFIFrameInfo doesn't claim to find any outside those ranges.
277 frame.instruction = 0x3d3f;
278 frame.module = &module1;
279 cfi_frame_info.reset(fast_resolver.FindCFIFrameInfo(&frame));
280 ASSERT_FALSE(cfi_frame_info.get());
281
282 frame.instruction = 0x3e9f;
283 frame.module = &module1;
284 cfi_frame_info.reset(fast_resolver.FindCFIFrameInfo(&frame));
285 ASSERT_FALSE(cfi_frame_info.get());
286
287 CFIFrameInfo::RegisterValueMap<uint32_t> current_registers;
288 CFIFrameInfo::RegisterValueMap<uint32_t> caller_registers;
289 CFIFrameInfo::RegisterValueMap<uint32_t> expected_caller_registers;
290 MockMemoryRegion memory;
291
292 // Regardless of which instruction evaluation takes place at, it
293 // should produce the same values for the caller's registers.
294 expected_caller_registers[".cfa"] = 0x1001c;
295 expected_caller_registers[".ra"] = 0xf6438648;
296 expected_caller_registers["$ebp"] = 0x10038;
297 expected_caller_registers["$ebx"] = 0x98ecadc3;
298 expected_caller_registers["$esi"] = 0x878f7524;
299 expected_caller_registers["$edi"] = 0x6312f9a5;
300
301 frame.instruction = 0x3d40;
302 frame.module = &module1;
303 current_registers.clear();
304 current_registers["$esp"] = 0x10018;
305 current_registers["$ebp"] = 0x10038;
306 current_registers["$ebx"] = 0x98ecadc3;
307 current_registers["$esi"] = 0x878f7524;
308 current_registers["$edi"] = 0x6312f9a5;
309 cfi_frame_info.reset(fast_resolver.FindCFIFrameInfo(&frame));
310 ASSERT_TRUE(cfi_frame_info.get());
311 ASSERT_TRUE(cfi_frame_info.get()
312 ->FindCallerRegs<uint32_t>(current_registers, memory,
313 &caller_registers));
314 ASSERT_TRUE(VerifyRegisters(__FILE__, __LINE__,
315 expected_caller_registers, caller_registers));
316
317 frame.instruction = 0x3d41;
318 current_registers["$esp"] = 0x10014;
319 cfi_frame_info.reset(fast_resolver.FindCFIFrameInfo(&frame));
320 ASSERT_TRUE(cfi_frame_info.get());
321 ASSERT_TRUE(cfi_frame_info.get()
322 ->FindCallerRegs<uint32_t>(current_registers, memory,
323 &caller_registers));
324 ASSERT_TRUE(VerifyRegisters(__FILE__, __LINE__,
325 expected_caller_registers, caller_registers));
326
327 frame.instruction = 0x3d43;
328 current_registers["$ebp"] = 0x10014;
329 cfi_frame_info.reset(fast_resolver.FindCFIFrameInfo(&frame));
330 ASSERT_TRUE(cfi_frame_info.get());
331 ASSERT_TRUE(cfi_frame_info.get()
332 ->FindCallerRegs<uint32_t>(current_registers, memory,
333 &caller_registers));
334 VerifyRegisters(__FILE__, __LINE__,
335 expected_caller_registers, caller_registers);
336
337 frame.instruction = 0x3d54;
338 current_registers["$ebx"] = 0x6864f054U;
339 cfi_frame_info.reset(fast_resolver.FindCFIFrameInfo(&frame));
340 ASSERT_TRUE(cfi_frame_info.get());
341 ASSERT_TRUE(cfi_frame_info.get()
342 ->FindCallerRegs<uint32_t>(current_registers, memory,
343 &caller_registers));
344 VerifyRegisters(__FILE__, __LINE__,
345 expected_caller_registers, caller_registers);
346
347 frame.instruction = 0x3d5a;
348 current_registers["$esi"] = 0x6285f79aU;
349 cfi_frame_info.reset(fast_resolver.FindCFIFrameInfo(&frame));
350 ASSERT_TRUE(cfi_frame_info.get());
351 ASSERT_TRUE(cfi_frame_info.get()
352 ->FindCallerRegs<uint32_t>(current_registers, memory,
353 &caller_registers));
354 VerifyRegisters(__FILE__, __LINE__,
355 expected_caller_registers, caller_registers);
356
357 frame.instruction = 0x3d84;
358 current_registers["$edi"] = 0x64061449U;
359 cfi_frame_info.reset(fast_resolver.FindCFIFrameInfo(&frame));
360 ASSERT_TRUE(cfi_frame_info.get());
361 ASSERT_TRUE(cfi_frame_info.get()
362 ->FindCallerRegs<uint32_t>(current_registers, memory,
363 &caller_registers));
364 VerifyRegisters(__FILE__, __LINE__,
365 expected_caller_registers, caller_registers);
366
367 frame.instruction = 0x2900;
368 frame.module = &module1;
369 fast_resolver.FillSourceLineInfo(&frame);
370 ASSERT_EQ(frame.function_name, string("PublicSymbol"));
371
372 frame.instruction = 0x4000;
373 frame.module = &module1;
374 fast_resolver.FillSourceLineInfo(&frame);
375 ASSERT_EQ(frame.function_name, string("LargeFunction"));
376
377 frame.instruction = 0x2181;
378 frame.module = &module2;
379 fast_resolver.FillSourceLineInfo(&frame);
380 ASSERT_EQ(frame.function_name, "Function2_2");
381 ASSERT_EQ(frame.function_base, 0x2170U);
382 ASSERT_TRUE(frame.module);
383 ASSERT_EQ(frame.module->code_file(), "module2");
384 ASSERT_EQ(frame.source_file_name, "file2_2.cc");
385 ASSERT_EQ(frame.source_line, 21);
386 ASSERT_EQ(frame.source_line_base, 0x2180U);
387 windows_frame_info.reset(fast_resolver.FindWindowsFrameInfo(&frame));
388 ASSERT_TRUE(windows_frame_info.get());
389 ASSERT_EQ(windows_frame_info->type_, WindowsFrameInfo::STACK_INFO_FRAME_DATA);
390 ASSERT_EQ(windows_frame_info->prolog_size, 1U);
391
392 frame.instruction = 0x216f;
393 fast_resolver.FillSourceLineInfo(&frame);
394 ASSERT_EQ(frame.function_name, "Public2_1");
395
396 ClearSourceLineInfo(&frame);
397 frame.instruction = 0x219f;
398 frame.module = &module2;
399 fast_resolver.FillSourceLineInfo(&frame);
400 ASSERT_TRUE(frame.function_name.empty());
401
402 frame.instruction = 0x21a0;
403 frame.module = &module2;
404 fast_resolver.FillSourceLineInfo(&frame);
405 ASSERT_EQ(frame.function_name, "Public2_2");
406 }
407
TEST_F(TestFastSourceLineResolver,TestInvalidLoads)408 TEST_F(TestFastSourceLineResolver, TestInvalidLoads) {
409 TestCodeModule module3("module3");
410 ASSERT_TRUE(basic_resolver.LoadModule(&module3,
411 testdata_dir + "/module3_bad.out"));
412 ASSERT_TRUE(basic_resolver.HasModule(&module3));
413 ASSERT_TRUE(basic_resolver.IsModuleCorrupt(&module3));
414 // Convert module3 to fast_module:
415 ASSERT_TRUE(serializer.ConvertOneModule(module3.code_file(),
416 &basic_resolver,
417 &fast_resolver));
418 ASSERT_TRUE(fast_resolver.HasModule(&module3));
419 ASSERT_TRUE(fast_resolver.IsModuleCorrupt(&module3));
420
421 TestCodeModule module4("module4");
422 ASSERT_TRUE(basic_resolver.LoadModule(&module4,
423 testdata_dir + "/module4_bad.out"));
424 ASSERT_TRUE(basic_resolver.HasModule(&module4));
425 ASSERT_TRUE(basic_resolver.IsModuleCorrupt(&module4));
426 // Convert module4 to fast_module:
427 ASSERT_TRUE(serializer.ConvertOneModule(module4.code_file(),
428 &basic_resolver,
429 &fast_resolver));
430 ASSERT_TRUE(fast_resolver.HasModule(&module4));
431 ASSERT_TRUE(fast_resolver.IsModuleCorrupt(&module4));
432
433 TestCodeModule module5("module5");
434 ASSERT_FALSE(fast_resolver.LoadModule(&module5,
435 testdata_dir + "/invalid-filename"));
436 ASSERT_FALSE(fast_resolver.HasModule(&module5));
437
438 TestCodeModule invalidmodule("invalid-module");
439 ASSERT_FALSE(fast_resolver.HasModule(&invalidmodule));
440 }
441
TEST_F(TestFastSourceLineResolver,TestUnload)442 TEST_F(TestFastSourceLineResolver, TestUnload) {
443 TestCodeModule module1("module1");
444 ASSERT_FALSE(basic_resolver.HasModule(&module1));
445
446 ASSERT_TRUE(basic_resolver.LoadModule(&module1, symbol_file(1)));
447 ASSERT_TRUE(basic_resolver.HasModule(&module1));
448 // Convert module1 to fast_module.
449 ASSERT_TRUE(serializer.ConvertOneModule(module1.code_file(),
450 &basic_resolver,
451 &fast_resolver));
452 ASSERT_TRUE(fast_resolver.HasModule(&module1));
453 basic_resolver.UnloadModule(&module1);
454 fast_resolver.UnloadModule(&module1);
455 ASSERT_FALSE(fast_resolver.HasModule(&module1));
456
457 ASSERT_TRUE(basic_resolver.LoadModule(&module1, symbol_file(1)));
458 ASSERT_TRUE(basic_resolver.HasModule(&module1));
459 // Convert module1 to fast_module.
460 ASSERT_TRUE(serializer.ConvertOneModule(module1.code_file(),
461 &basic_resolver,
462 &fast_resolver));
463 ASSERT_TRUE(fast_resolver.HasModule(&module1));
464 }
465
TEST_F(TestFastSourceLineResolver,CompareModule)466 TEST_F(TestFastSourceLineResolver, CompareModule) {
467 char *symbol_data;
468 size_t symbol_data_size;
469 string symbol_data_string;
470 string filename;
471
472 for (int module_index = 0; module_index < 3; ++module_index) {
473 std::stringstream ss;
474 ss << testdata_dir << "/module" << module_index << ".out";
475 filename = ss.str();
476 ASSERT_TRUE(SourceLineResolverBase::ReadSymbolFile(
477 symbol_file(module_index), &symbol_data, &symbol_data_size));
478 symbol_data_string.assign(symbol_data, symbol_data_size);
479 delete [] symbol_data;
480 ASSERT_TRUE(module_comparer.Compare(symbol_data_string));
481 }
482 }
483
484 } // namespace
485
main(int argc,char * argv[])486 int main(int argc, char *argv[]) {
487 ::testing::InitGoogleTest(&argc, argv);
488 return RUN_ALL_TESTS();
489 }
490