1/* 2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Copyright 2012, Rene Gollent, rene@gollent.com. 4 * Distributed under the terms of the MIT License. 5 */ 6 7 8#include "TeamDebugInfo.h" 9 10#include <stdio.h> 11 12#include <new> 13 14#include <AutoDeleter.h> 15#include <AutoLocker.h> 16 17#include "Architecture.h" 18#include "DebuggerInterface.h" 19#include "DebuggerTeamDebugInfo.h" 20#include "DisassembledCode.h" 21#include "DwarfTeamDebugInfo.h" 22#include "FileManager.h" 23#include "FileSourceCode.h" 24#include "Function.h" 25#include "FunctionID.h" 26#include "ImageDebugInfo.h" 27#include "LocatableFile.h" 28#include "SourceFile.h" 29#include "SourceLanguage.h" 30#include "SpecificImageDebugInfo.h" 31#include "StringUtils.h" 32#include "Type.h" 33#include "TypeLookupConstraints.h" 34 35 36// #pragma mark - FunctionHashDefinition 37 38 39struct TeamDebugInfo::FunctionHashDefinition { 40 typedef const FunctionInstance* KeyType; 41 typedef Function ValueType; 42 43 size_t HashKey(const FunctionInstance* key) const 44 { 45 // Instances without source file only equal themselves. 46 if (key->SourceFile() == NULL) 47 return (uint32)(addr_t)key; 48 49 uint32 hash = StringUtils::HashValue(key->Name()); 50 hash = hash * 17 + (uint32)(addr_t)key->SourceFile(); 51 SourceLocation location = key->GetSourceLocation(); 52 hash = hash * 17 + location.Line(); 53 hash = hash * 17 + location.Column(); 54 55 return hash; 56 } 57 58 size_t Hash(const Function* value) const 59 { 60 return HashKey(value->FirstInstance()); 61 } 62 63 bool Compare(const FunctionInstance* key, const Function* value) const 64 { 65 // source file must be the same 66 if (key->SourceFile() != value->SourceFile()) 67 return false; 68 69 // Instances without source file only equal themselves. 70 if (key->SourceFile() == NULL) 71 return key == value->FirstInstance(); 72 73 // Source location and function name must also match. 74 return key->GetSourceLocation() == value->GetSourceLocation() 75 && key->Name() == value->Name(); 76 } 77 78 Function*& GetLink(Function* value) const 79 { 80 return value->fNext; 81 } 82}; 83 84 85// #pragma mark - SourceFileEntry 86 87 88struct TeamDebugInfo::SourceFileEntry { 89 SourceFileEntry(LocatableFile* sourceFile) 90 : 91 fSourceFile(sourceFile), 92 fSourceCode(NULL) 93 { 94 fSourceFile->AcquireReference(); 95 } 96 97 ~SourceFileEntry() 98 { 99 SetSourceCode(NULL); 100 fSourceFile->ReleaseReference(); 101 } 102 103 status_t Init() 104 { 105 return B_OK; 106 } 107 108 LocatableFile* SourceFile() const 109 { 110 return fSourceFile; 111 } 112 113 FileSourceCode* GetSourceCode() const 114 { 115 return fSourceCode; 116 } 117 118 void SetSourceCode(FileSourceCode* sourceCode) 119 { 120 if (sourceCode == fSourceCode) 121 return; 122 123 if (fSourceCode != NULL) 124 fSourceCode->ReleaseReference(); 125 126 fSourceCode = sourceCode; 127 128 if (fSourceCode != NULL) 129 fSourceCode->AcquireReference(); 130 } 131 132 133 bool IsUnused() const 134 { 135 return fFunctions.IsEmpty(); 136 } 137 138 status_t AddFunction(Function* function) 139 { 140 if (!fFunctions.BinaryInsert(function, &_CompareFunctions)) 141 return B_NO_MEMORY; 142 143 return B_OK; 144 } 145 146 void RemoveFunction(Function* function) 147 { 148 int32 index = fFunctions.BinarySearchIndex(*function, 149 &_CompareFunctions); 150 if (index >= 0) 151 fFunctions.RemoveItemAt(index); 152 } 153 154 Function* FunctionAtLocation(const SourceLocation& location) const 155 { 156 int32 index = fFunctions.BinarySearchIndexByKey(location, 157 &_CompareLocationFunction); 158 if (index >= 0) 159 return fFunctions.ItemAt(index); 160 161 // No exact match, so we return the previous function which might still 162 // contain the location. 163 index = -index - 1; 164 165 if (index == 0) 166 return NULL; 167 168 return fFunctions.ItemAt(index - 1); 169 } 170 171 Function* FunctionAt(int32 index) const 172 { 173 return fFunctions.ItemAt(index); 174 } 175 176 Function* FunctionByName(const BString& name) const 177 { 178 // TODO: That's not exactly optimal. 179 for (int32 i = 0; Function* function = fFunctions.ItemAt(i); i++) { 180 if (name == function->Name()) 181 return function; 182 } 183 return NULL; 184 } 185 186private: 187 typedef BObjectList<Function> FunctionList; 188 189private: 190 static int _CompareFunctions(const Function* a, const Function* b) 191 { 192 SourceLocation locationA = a->GetSourceLocation(); 193 SourceLocation locationB = b->GetSourceLocation(); 194 195 if (locationA < locationB) 196 return -1; 197 198 if (locationA != locationB ) 199 return 1; 200 201 // if the locations match we still need to compare by name to be 202 // certain, since differently typed instantiations of template 203 // functions will have the same source file and location 204 return a->Name().Compare(b->Name()); 205 } 206 207 static int _CompareLocationFunction(const SourceLocation* location, 208 const Function* function) 209 { 210 SourceLocation functionLocation = function->GetSourceLocation(); 211 212 if (*location < functionLocation) 213 return -1; 214 215 return *location == functionLocation ? 0 : 1; 216 } 217 218private: 219 LocatableFile* fSourceFile; 220 FileSourceCode* fSourceCode; 221 FunctionList fFunctions; 222 223public: 224 SourceFileEntry* fNext; 225}; 226 227 228// #pragma mark - SourceFileHashDefinition 229 230 231struct TeamDebugInfo::SourceFileHashDefinition { 232 typedef const LocatableFile* KeyType; 233 typedef SourceFileEntry ValueType; 234 235 size_t HashKey(const LocatableFile* key) const 236 { 237 return (size_t)(addr_t)key; 238 } 239 240 size_t Hash(const SourceFileEntry* value) const 241 { 242 return HashKey(value->SourceFile()); 243 } 244 245 bool Compare(const LocatableFile* key, const SourceFileEntry* value) const 246 { 247 return key == value->SourceFile(); 248 } 249 250 SourceFileEntry*& GetLink(SourceFileEntry* value) const 251 { 252 return value->fNext; 253 } 254}; 255 256 257// #pragma mark - TeamDebugInfo 258 259 260TeamDebugInfo::TeamDebugInfo(DebuggerInterface* debuggerInterface, 261 Architecture* architecture, FileManager* fileManager) 262 : 263 fLock("team debug info"), 264 fDebuggerInterface(debuggerInterface), 265 fArchitecture(architecture), 266 fFileManager(fileManager), 267 fSpecificInfos(10, true), 268 fFunctions(NULL), 269 fSourceFiles(NULL), 270 fTypeCache(NULL) 271{ 272 fDebuggerInterface->AcquireReference(); 273} 274 275 276TeamDebugInfo::~TeamDebugInfo() 277{ 278 if (fTypeCache != NULL) 279 fTypeCache->ReleaseReference(); 280 281 if (fSourceFiles != NULL) { 282 SourceFileEntry* entry = fSourceFiles->Clear(true); 283 while (entry != NULL) { 284 SourceFileEntry* next = entry->fNext; 285 delete entry; 286 entry = next; 287 } 288 289 delete fSourceFiles; 290 } 291 292 if (fFunctions != NULL) { 293 Function* function = fFunctions->Clear(true); 294 while (function != NULL) { 295 Function* next = function->fNext; 296 function->ReleaseReference(); 297 function = next; 298 } 299 300 delete fFunctions; 301 } 302 303 fDebuggerInterface->ReleaseReference(); 304} 305 306 307status_t 308TeamDebugInfo::Init() 309{ 310 // check the lock 311 status_t error = fLock.InitCheck(); 312 if (error != B_OK) 313 return error; 314 315 // create function hash table 316 fFunctions = new(std::nothrow) FunctionTable; 317 if (fFunctions == NULL) 318 return B_NO_MEMORY; 319 320 error = fFunctions->Init(); 321 if (error != B_OK) 322 return error; 323 324 // create source file hash table 325 fSourceFiles = new(std::nothrow) SourceFileTable; 326 if (fSourceFiles == NULL) 327 return B_NO_MEMORY; 328 329 error = fSourceFiles->Init(); 330 if (error != B_OK) 331 return error; 332 333 // create a type cache 334 fTypeCache = new(std::nothrow) GlobalTypeCache; 335 if (fTypeCache == NULL) 336 return B_NO_MEMORY; 337 338 error = fTypeCache->Init(); 339 if (error != B_OK) 340 return error; 341 342 // Create specific infos for all types of debug info we support, in 343 // descending order of expressiveness. 344 345 // DWARF 346 DwarfTeamDebugInfo* dwarfInfo = new(std::nothrow) DwarfTeamDebugInfo( 347 fArchitecture, fDebuggerInterface, fFileManager, this, fTypeCache); 348 if (dwarfInfo == NULL || !fSpecificInfos.AddItem(dwarfInfo)) { 349 delete dwarfInfo; 350 return B_NO_MEMORY; 351 } 352 353 error = dwarfInfo->Init(); 354 if (error != B_OK) 355 return error; 356 357 // debugger based info 358 DebuggerTeamDebugInfo* debuggerInfo 359 = new(std::nothrow) DebuggerTeamDebugInfo(fDebuggerInterface, 360 fArchitecture); 361 if (debuggerInfo == NULL || !fSpecificInfos.AddItem(debuggerInfo)) { 362 delete debuggerInfo; 363 return B_NO_MEMORY; 364 } 365 366 error = debuggerInfo->Init(); 367 if (error != B_OK) 368 return error; 369 370 return B_OK; 371} 372 373 374status_t 375TeamDebugInfo::LookupTypeByName(const BString& name, 376 const TypeLookupConstraints& constraints, Type*& _type) 377{ 378 return GetType(fTypeCache, name, constraints, _type); 379} 380 381status_t 382TeamDebugInfo::GetType(GlobalTypeCache* cache, const BString& name, 383 const TypeLookupConstraints& constraints, Type*& _type) 384{ 385 // maybe the type is already cached 386 AutoLocker<GlobalTypeCache> cacheLocker(cache); 387 Type* type = cache->GetType(name, constraints); 388 if (type != NULL) { 389 type->AcquireReference(); 390 _type = type; 391 return B_OK; 392 } 393 394 cacheLocker.Unlock(); 395 396 // Clone the image list and get references to the images, so we can iterate 397 // through them without locking. 398 AutoLocker<BLocker> locker(fLock); 399 400 ImageList images; 401 for (int32 i = 0; ImageDebugInfo* imageDebugInfo = fImages.ItemAt(i); i++) { 402 if (images.AddItem(imageDebugInfo)) 403 imageDebugInfo->AcquireReference(); 404 } 405 406 locker.Unlock(); 407 408 // get the type 409 status_t error = B_ENTRY_NOT_FOUND; 410 for (int32 i = 0; ImageDebugInfo* imageDebugInfo = images.ItemAt(i); i++) { 411 error = imageDebugInfo->GetType(cache, name, constraints, type); 412 if (error == B_OK) { 413 _type = type; 414 break; 415 } 416 } 417 418 // release the references 419 for (int32 i = 0; ImageDebugInfo* imageDebugInfo = images.ItemAt(i); i++) 420 imageDebugInfo->ReleaseReference(); 421 422 return error; 423} 424 425 426status_t 427TeamDebugInfo::LoadImageDebugInfo(const ImageInfo& imageInfo, 428 LocatableFile* imageFile, ImageDebugInfo*& _imageDebugInfo) 429{ 430 ImageDebugInfo* imageDebugInfo = new(std::nothrow) ImageDebugInfo( 431 imageInfo); 432 if (imageDebugInfo == NULL) 433 return B_NO_MEMORY; 434 ObjectDeleter<ImageDebugInfo> imageDebugInfoDeleter(imageDebugInfo); 435 436 for (int32 i = 0; SpecificTeamDebugInfo* specificTeamInfo 437 = fSpecificInfos.ItemAt(i); i++) { 438 SpecificImageDebugInfo* specificImageInfo; 439 status_t error = specificTeamInfo->CreateImageDebugInfo(imageInfo, 440 imageFile, specificImageInfo); 441 if (error == B_OK) { 442 if (!imageDebugInfo->AddSpecificInfo(specificImageInfo)) { 443 delete specificImageInfo; 444 return B_NO_MEMORY; 445 } 446 } else if (error == B_NO_MEMORY) 447 return error; 448 // fail only when out of memory 449 } 450 451 status_t error = imageDebugInfo->FinishInit(); 452 if (error != B_OK) 453 return error; 454 455 _imageDebugInfo = imageDebugInfoDeleter.Detach(); 456 return B_OK; 457} 458 459 460status_t 461TeamDebugInfo::LoadSourceCode(LocatableFile* file, FileSourceCode*& _sourceCode) 462{ 463 AutoLocker<BLocker> locker(fLock); 464 465 // If we don't know the source file, there's nothing we can do. 466 SourceFileEntry* entry = fSourceFiles->Lookup(file); 467 if (entry == NULL) 468 return B_ENTRY_NOT_FOUND; 469 470 // the source might already be loaded 471 FileSourceCode* sourceCode = entry->GetSourceCode(); 472 if (sourceCode != NULL) { 473 sourceCode->AcquireReference(); 474 _sourceCode = sourceCode; 475 return B_OK; 476 } 477 478 // get the source language from some function's image debug info 479 Function* function = entry->FunctionAt(0); 480 if (function == NULL) 481 return B_ENTRY_NOT_FOUND; 482 483 FunctionDebugInfo* functionDebugInfo 484 = function->FirstInstance()->GetFunctionDebugInfo(); 485 SourceLanguage* language; 486 status_t error = functionDebugInfo->GetSpecificImageDebugInfo() 487 ->GetSourceLanguage(functionDebugInfo, language); 488 if (error != B_OK) 489 return error; 490 BReference<SourceLanguage> languageReference(language, true); 491 492 // no source code yet 493// locker.Unlock(); 494 // TODO: It would be nice to unlock here, but we need to iterate through 495 // the images below. We could clone the list, acquire references, and 496 // unlock. Then we have to compare the list with the then current list when 497 // we're done loading. 498 499 // load the source file 500 SourceFile* sourceFile; 501 error = fFileManager->LoadSourceFile(file, sourceFile); 502 if (error != B_OK) 503 return error; 504 505 // create the source code 506 sourceCode = new(std::nothrow) FileSourceCode(file, sourceFile, language); 507 sourceFile->ReleaseReference(); 508 if (sourceCode == NULL) 509 return B_NO_MEMORY; 510 BReference<FileSourceCode> sourceCodeReference(sourceCode, true); 511 512 error = sourceCode->Init(); 513 if (error != B_OK) 514 return error; 515 516 // Iterate through all images that know the source file and ask them to add 517 // information. 518 bool anyInfo = false; 519 for (int32 i = 0; ImageDebugInfo* imageDebugInfo = fImages.ItemAt(i); i++) 520 anyInfo |= imageDebugInfo->AddSourceCodeInfo(file, sourceCode) == B_OK; 521 522 if (!anyInfo) 523 return B_ENTRY_NOT_FOUND; 524 525 entry->SetSourceCode(sourceCode); 526 527 _sourceCode = sourceCodeReference.Detach(); 528 return B_OK; 529} 530 531 532status_t 533TeamDebugInfo::DisassembleFunction(FunctionInstance* functionInstance, 534 DisassembledCode*& _sourceCode) 535{ 536 // allocate a buffer for the function code 537 static const target_size_t kMaxBufferSize = 64 * 1024; 538 target_size_t bufferSize = std::min(functionInstance->Size(), 539 kMaxBufferSize); 540 void* buffer = malloc(bufferSize); 541 if (buffer == NULL) 542 return B_NO_MEMORY; 543 MemoryDeleter bufferDeleter(buffer); 544 545 // read the function code 546 FunctionDebugInfo* functionDebugInfo 547 = functionInstance->GetFunctionDebugInfo(); 548 ssize_t bytesRead = functionDebugInfo->GetSpecificImageDebugInfo() 549 ->ReadCode(functionInstance->Address(), buffer, bufferSize); 550 if (bytesRead < 0) 551 return bytesRead; 552 553 return fArchitecture->DisassembleCode(functionDebugInfo, buffer, bytesRead, 554 _sourceCode); 555} 556 557 558status_t 559TeamDebugInfo::AddImageDebugInfo(ImageDebugInfo* imageDebugInfo) 560{ 561 AutoLocker<BLocker> locker(fLock); 562 // We have both locks now, so that for read-only access either lock 563 // suffices. 564 565 if (!fImages.AddItem(imageDebugInfo)) 566 return B_NO_MEMORY; 567 568 // Match all of the image debug info's functions instances with functions. 569 BObjectList<SourceFileEntry> sourceFileEntries; 570 for (int32 i = 0; 571 FunctionInstance* instance = imageDebugInfo->FunctionAt(i); i++) { 572 // lookup the function or create it, if it doesn't exist yet 573 Function* function = fFunctions->Lookup(instance); 574 if (function != NULL) { 575// TODO: Also update possible user breakpoints in this function! 576 function->AddInstance(instance); 577 instance->SetFunction(function); 578 579 // The new image debug info might have additional information about 580 // the source file of the function, so remember the source file 581 // entry. 582 if (LocatableFile* sourceFile = function->SourceFile()) { 583 SourceFileEntry* entry = fSourceFiles->Lookup(sourceFile); 584 if (entry != NULL && entry->GetSourceCode() != NULL) 585 sourceFileEntries.AddItem(entry); 586 } 587 } else { 588 function = new(std::nothrow) Function; 589 if (function == NULL) { 590 RemoveImageDebugInfo(imageDebugInfo); 591 return B_NO_MEMORY; 592 } 593 function->AddInstance(instance); 594 instance->SetFunction(function); 595 596 status_t error = _AddFunction(function); 597 // Insert after adding the instance. Otherwise the function 598 // wouldn't be hashable/comparable. 599 if (error != B_OK) { 600 function->RemoveInstance(instance); 601 instance->SetFunction(NULL); 602 RemoveImageDebugInfo(imageDebugInfo); 603 return error; 604 } 605 } 606 } 607 608 // update the source files the image debug info knows about 609 for (int32 i = 0; SourceFileEntry* entry = sourceFileEntries.ItemAt(i); 610 i++) { 611 FileSourceCode* sourceCode = entry->GetSourceCode(); 612 sourceCode->Lock(); 613 if (imageDebugInfo->AddSourceCodeInfo(entry->SourceFile(), 614 sourceCode) == B_OK) { 615 // TODO: Notify interesting parties! Iterate through all functions 616 // for this source file? 617 } 618 sourceCode->Unlock(); 619 } 620 621 return B_OK; 622} 623 624 625void 626TeamDebugInfo::RemoveImageDebugInfo(ImageDebugInfo* imageDebugInfo) 627{ 628 AutoLocker<BLocker> locker(fLock); 629 // We have both locks now, so that for read-only access either lock 630 // suffices. 631 632 // Remove the functions from all of the image debug info's functions 633 // instances. 634 for (int32 i = 0; 635 FunctionInstance* instance = imageDebugInfo->FunctionAt(i); i++) { 636 if (Function* function = instance->GetFunction()) { 637// TODO: Also update possible user breakpoints in this function! 638 if (function->FirstInstance() == function->LastInstance()) { 639 // function unused -- remove it 640 // Note, that we have to remove it from the hash before removing 641 // the instance, since otherwise the function cannot be compared 642 // anymore. 643 _RemoveFunction(function); 644 function->ReleaseReference(); 645 // The instance still has a reference. 646 } 647 648 function->RemoveInstance(instance); 649 instance->SetFunction(NULL); 650 // If this was the last instance, it will remove the last 651 // reference to the function. 652 } 653 } 654 655 // remove cached types from that image 656 fTypeCache->RemoveTypes(imageDebugInfo->GetImageInfo().ImageID()); 657 658 fImages.RemoveItem(imageDebugInfo); 659} 660 661 662ImageDebugInfo* 663TeamDebugInfo::ImageDebugInfoByName(const char* name) const 664{ 665 for (int32 i = 0; ImageDebugInfo* imageDebugInfo = fImages.ItemAt(i); i++) { 666 if (imageDebugInfo->GetImageInfo().Name() == name) 667 return imageDebugInfo; 668 } 669 670 return NULL; 671} 672 673 674Function* 675TeamDebugInfo::FunctionAtSourceLocation(LocatableFile* file, 676 const SourceLocation& location) const 677{ 678 if (SourceFileEntry* entry = fSourceFiles->Lookup(file)) 679 return entry->FunctionAtLocation(location); 680 return NULL; 681} 682 683 684Function* 685TeamDebugInfo::FunctionByID(FunctionID* functionID) const 686{ 687 if (SourceFunctionID* sourceFunctionID 688 = dynamic_cast<SourceFunctionID*>(functionID)) { 689 // get the source file 690 LocatableFile* file = fFileManager->GetSourceFile( 691 sourceFunctionID->SourceFilePath()); 692 if (file == NULL) 693 return NULL; 694 BReference<LocatableFile> fileReference(file, true); 695 696 if (SourceFileEntry* entry = fSourceFiles->Lookup(file)) 697 return entry->FunctionByName(functionID->FunctionName()); 698 return NULL; 699 } 700 701 ImageFunctionID* imageFunctionID 702 = dynamic_cast<ImageFunctionID*>(functionID); 703 if (imageFunctionID == NULL) 704 return NULL; 705 706 ImageDebugInfo* imageDebugInfo 707 = ImageDebugInfoByName(imageFunctionID->ImageName()); 708 if (imageDebugInfo == NULL) 709 return NULL; 710 711 FunctionInstance* functionInstance = imageDebugInfo->FunctionByName( 712 functionID->FunctionName()); 713 return functionInstance != NULL ? functionInstance->GetFunction() : NULL; 714} 715 716 717status_t 718TeamDebugInfo::_AddFunction(Function* function) 719{ 720 // If the function refers to a source file, add it to the respective entry. 721 if (LocatableFile* sourceFile = function->SourceFile()) { 722 SourceFileEntry* entry = fSourceFiles->Lookup(sourceFile); 723 if (entry == NULL) { 724 // no entry for the source file yet -- create on 725 entry = new(std::nothrow) SourceFileEntry(sourceFile); 726 if (entry == NULL) 727 return B_NO_MEMORY; 728 729 status_t error = entry->Init(); 730 if (error != B_OK) { 731 delete entry; 732 return error; 733 } 734 735 fSourceFiles->Insert(entry); 736 } 737 738 // add the function 739 status_t error = entry->AddFunction(function); 740 if (error != B_OK) { 741 if (entry->IsUnused()) { 742 fSourceFiles->Remove(entry); 743 delete entry; 744 } 745 return error; 746 } 747 } 748 749 fFunctions->Insert(function); 750 751 return B_OK; 752} 753 754 755void 756TeamDebugInfo::_RemoveFunction(Function* function) 757{ 758 fFunctions->Remove(function); 759 760 // If the function refers to a source file, remove it from the respective 761 // entry. 762 if (LocatableFile* sourceFile = function->SourceFile()) { 763 if (SourceFileEntry* entry = fSourceFiles->Lookup(sourceFile)) 764 entry->RemoveFunction(function); 765 } 766} 767