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 (at) 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: 72 explicit TestCodeModule(string code_file) : code_file_(code_file) {} 73 virtual ~TestCodeModule() {} 74 75 virtual uint64_t base_address() const { return 0; } 76 virtual uint64_t size() const { return 0xb000; } 77 virtual string code_file() const { return code_file_; } 78 virtual string code_identifier() const { return ""; } 79 virtual string debug_file() const { return ""; } 80 virtual string debug_identifier() const { return ""; } 81 virtual string version() const { return ""; } 82 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 { 92 uint64_t GetBase() const { return 0x10000; } 93 uint32_t GetSize() const { return 0x01000; } 94 bool GetMemoryAtAddress(uint64_t address, uint8_t *value) const { 95 *value = address & 0xff; 96 return true; 97 } 98 bool GetMemoryAtAddress(uint64_t address, uint16_t *value) const { 99 *value = address & 0xffff; 100 return true; 101 } 102 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 } 113 bool GetMemoryAtAddress(uint64_t address, uint64_t *value) const { 114 *value = address; 115 return true; 116 } 117 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". 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 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 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: 175 void SetUp() { 176 testdata_dir = string(getenv("srcdir") ? getenv("srcdir") : ".") + 177 "/src/processor/testdata"; 178 } 179 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. 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 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 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 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 486 int main(int argc, char *argv[]) { 487 ::testing::InitGoogleTest(&argc, argv); 488 return RUN_ALL_TESTS(); 489 } 490