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