From 593fa6776aecf8e722941718824b08338991555d Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Fri, 3 Jul 2009 00:56:39 +0000 Subject: [PATCH] * DWARF: - DwarfUtils::GetDeclarationLocation(): Line/column numbers are one-based. - Added line number program interpretation (LineNumberProgram). * FunctionDebugInfo: Return the source file (LocatableFile) instead of the file name. * FileManager/LocatableEntry: Fixed handling when a LocatableEntry is unreferenced. There was a race condition before, since an unreferenced entry could be referenced and unreferenced again before removing it from the hash table, which could lead to double deletion. Now we never reuse an unreferenced entry and just remove it from the hash table when encountering one. * FileManager/SourceFile: Added class SourceFile which loads a source file from disk and slices it into lines. Managed by FileManager. * Added class FileSourceCode, a SourceCode implementation using a SourceFile as line provider. The statement management works pretty much exactly as in DissassembledCode. * DwarfImageDebugInfo: Implemented LoadSourceCode for real. It creates a FileSourceCode and uses the DWARF line number information for the statement information. This basically gets the source level view going, though there are still several problems -- stepping doesn't work perfectly yet, the source isn't found for all functions, there's no handling of duplicate functions (no idea why gcc generates them in the first place), etc. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@31382 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/apps/debugger/Jamfile | 2 + src/apps/debugger/TeamDebugger.cpp | 3 +- .../debug_info/BasicFunctionDebugInfo.cpp | 4 +- .../debug_info/BasicFunctionDebugInfo.h | 2 +- .../debug_info/DwarfFunctionDebugInfo.cpp | 29 +- .../debug_info/DwarfFunctionDebugInfo.h | 12 +- .../debug_info/DwarfImageDebugInfo.cpp | 286 ++++++++++++++++-- .../debugger/debug_info/DwarfImageDebugInfo.h | 27 +- .../debug_info/DwarfTeamDebugInfo.cpp | 6 +- .../debugger/debug_info/DwarfTeamDebugInfo.h | 5 +- .../debugger/debug_info/FunctionDebugInfo.h | 3 +- .../debugger/debug_info/TeamDebugInfo.cpp | 5 +- src/apps/debugger/debug_info/TeamDebugInfo.h | 5 +- src/apps/debugger/dwarf/CompilationUnit.h | 5 + src/apps/debugger/dwarf/DwarfFile.cpp | 21 +- src/apps/debugger/dwarf/DwarfUtils.cpp | 4 +- src/apps/debugger/dwarf/Jamfile | 1 + src/apps/debugger/dwarf/LineNumberProgram.cpp | 208 +++++++++++++ src/apps/debugger/dwarf/LineNumberProgram.h | 65 ++++ src/apps/debugger/files/FileManager.cpp | 186 +++++++++++- src/apps/debugger/files/FileManager.h | 16 + src/apps/debugger/files/LocatableEntry.cpp | 4 +- src/apps/debugger/files/LocatableEntry.h | 2 +- src/apps/debugger/files/SourceFile.cpp | 139 +++++++++ src/apps/debugger/files/SourceFile.h | 46 +++ .../gui/team_window/ImageFunctionsView.cpp | 46 ++- src/apps/debugger/model/FileSourceCode.cpp | 144 +++++++++ src/apps/debugger/model/FileSourceCode.h | 53 ++++ 28 files changed, 1249 insertions(+), 80 deletions(-) create mode 100644 src/apps/debugger/dwarf/LineNumberProgram.cpp create mode 100644 src/apps/debugger/dwarf/LineNumberProgram.h create mode 100644 src/apps/debugger/files/SourceFile.cpp create mode 100644 src/apps/debugger/files/SourceFile.h create mode 100644 src/apps/debugger/model/FileSourceCode.cpp create mode 100644 src/apps/debugger/model/FileSourceCode.h diff --git a/src/apps/debugger/Jamfile b/src/apps/debugger/Jamfile index 4994da8cda..b56fdba480 100644 --- a/src/apps/debugger/Jamfile +++ b/src/apps/debugger/Jamfile @@ -75,6 +75,7 @@ Application Debugger : LocatableDirectory.cpp LocatableEntry.cpp LocatableFile.cpp + SourceFile.cpp # gui/team_window ImageFunctionsView.cpp @@ -88,6 +89,7 @@ Application Debugger : # model Breakpoint.cpp DisassembledCode.cpp + FileSourceCode.cpp Image.cpp ImageInfo.cpp SourceCode.cpp diff --git a/src/apps/debugger/TeamDebugger.cpp b/src/apps/debugger/TeamDebugger.cpp index 4ce7e9df51..ebc223de19 100644 --- a/src/apps/debugger/TeamDebugger.cpp +++ b/src/apps/debugger/TeamDebugger.cpp @@ -208,7 +208,8 @@ TeamDebugger::Init(team_id teamID, thread_id threadID, bool stopInMain) // create team debug info TeamDebugInfo* teamDebugInfo = new(std::nothrow) TeamDebugInfo( - fDebuggerInterface, fDebuggerInterface->GetArchitecture()); + fDebuggerInterface, fDebuggerInterface->GetArchitecture(), + fFileManager); if (teamDebugInfo == NULL) return B_NO_MEMORY; Reference teamDebugInfoReference(teamDebugInfo); diff --git a/src/apps/debugger/debug_info/BasicFunctionDebugInfo.cpp b/src/apps/debugger/debug_info/BasicFunctionDebugInfo.cpp index 0818347c81..f08e6b5f7d 100644 --- a/src/apps/debugger/debug_info/BasicFunctionDebugInfo.cpp +++ b/src/apps/debugger/debug_info/BasicFunctionDebugInfo.cpp @@ -63,8 +63,8 @@ BasicFunctionDebugInfo::PrettyName() const } -const char* -BasicFunctionDebugInfo::SourceFileName() const +LocatableFile* +BasicFunctionDebugInfo::SourceFile() const { return NULL; } diff --git a/src/apps/debugger/debug_info/BasicFunctionDebugInfo.h b/src/apps/debugger/debug_info/BasicFunctionDebugInfo.h index 3c574b1757..969213b3f7 100644 --- a/src/apps/debugger/debug_info/BasicFunctionDebugInfo.h +++ b/src/apps/debugger/debug_info/BasicFunctionDebugInfo.h @@ -26,7 +26,7 @@ public: virtual const char* Name() const; virtual const char* PrettyName() const; - virtual const char* SourceFileName() const; + virtual LocatableFile* SourceFile() const; virtual SourceLocation SourceStartLocation() const; virtual SourceLocation SourceEndLocation() const; diff --git a/src/apps/debugger/debug_info/DwarfFunctionDebugInfo.cpp b/src/apps/debugger/debug_info/DwarfFunctionDebugInfo.cpp index 6ab6651b91..e64295a883 100644 --- a/src/apps/debugger/debug_info/DwarfFunctionDebugInfo.cpp +++ b/src/apps/debugger/debug_info/DwarfFunctionDebugInfo.cpp @@ -7,29 +7,38 @@ #include "DebugInfoEntries.h" #include "DwarfImageDebugInfo.h" +#include "LocatableFile.h" #include "TargetAddressRangeList.h" DwarfFunctionDebugInfo::DwarfFunctionDebugInfo( - DwarfImageDebugInfo* imageDebugInfo, DIESubprogram* subprogramEntry, - TargetAddressRangeList* addressRanges, const BString& name, - const BString& sourceFile, const SourceLocation& sourceLocation) + DwarfImageDebugInfo* imageDebugInfo, CompilationUnit* compilationUnit, + DIESubprogram* subprogramEntry, TargetAddressRangeList* addressRanges, + const BString& name, LocatableFile* sourceFile, + const SourceLocation& sourceLocation) : fImageDebugInfo(imageDebugInfo), + fCompilationUnit(compilationUnit), fAddressRanges(addressRanges), fName(name), fSourceFile(sourceFile), fSourceLocation(sourceLocation) { - fImageDebugInfo->AddReference(); - fAddressRanges->AddReference(); + fImageDebugInfo->AcquireReference(); + fAddressRanges->AcquireReference(); + + if (fSourceFile != NULL) + fSourceFile->AcquireReference(); } DwarfFunctionDebugInfo::~DwarfFunctionDebugInfo() { - fAddressRanges->RemoveReference(); - fImageDebugInfo->RemoveReference(); + if (fSourceFile != NULL) + fSourceFile->ReleaseReference(); + + fAddressRanges->ReleaseReference(); + fImageDebugInfo->ReleaseReference(); } @@ -68,10 +77,10 @@ DwarfFunctionDebugInfo::PrettyName() const } -const char* -DwarfFunctionDebugInfo::SourceFileName() const +LocatableFile* +DwarfFunctionDebugInfo::SourceFile() const { - return fSourceFile.Length() > 0 ? fSourceFile.String() : NULL; + return fSourceFile; } diff --git a/src/apps/debugger/debug_info/DwarfFunctionDebugInfo.h b/src/apps/debugger/debug_info/DwarfFunctionDebugInfo.h index 32e660a458..7190f9074d 100644 --- a/src/apps/debugger/debug_info/DwarfFunctionDebugInfo.h +++ b/src/apps/debugger/debug_info/DwarfFunctionDebugInfo.h @@ -11,6 +11,7 @@ #include "SourceLocation.h" +class CompilationUnit; class DIESubprogram; class DwarfImageDebugInfo; class TargetAddressRangeList; @@ -20,10 +21,11 @@ class DwarfFunctionDebugInfo : public FunctionDebugInfo { public: DwarfFunctionDebugInfo( DwarfImageDebugInfo* imageDebugInfo, + CompilationUnit* compilationUnit, DIESubprogram* subprogramEntry, TargetAddressRangeList* addressRanges, const BString& name, - const BString& sourceFile, + LocatableFile* sourceFile, const SourceLocation& sourceLocation); virtual ~DwarfFunctionDebugInfo(); @@ -33,15 +35,19 @@ public: virtual const char* Name() const; virtual const char* PrettyName() const; - virtual const char* SourceFileName() const; + virtual LocatableFile* SourceFile() const; virtual SourceLocation SourceStartLocation() const; virtual SourceLocation SourceEndLocation() const; + CompilationUnit* GetCompilationUnit() const + { return fCompilationUnit; } + private: DwarfImageDebugInfo* fImageDebugInfo; + CompilationUnit* fCompilationUnit; TargetAddressRangeList* fAddressRanges; BString fName; - BString fSourceFile; + LocatableFile* fSourceFile; SourceLocation fSourceLocation; }; diff --git a/src/apps/debugger/debug_info/DwarfImageDebugInfo.cpp b/src/apps/debugger/debug_info/DwarfImageDebugInfo.cpp index 594b258613..5f2abe8806 100644 --- a/src/apps/debugger/debug_info/DwarfImageDebugInfo.cpp +++ b/src/apps/debugger/debug_info/DwarfImageDebugInfo.cpp @@ -11,6 +11,7 @@ #include #include +#include #include "Architecture.h" #include "CompilationUnit.h" @@ -20,28 +21,136 @@ #include "DwarfFunctionDebugInfo.h" #include "DwarfUtils.h" #include "ElfFile.h" +#include "FileManager.h" +#include "FileSourceCode.h" +#include "LocatableFile.h" +#include "SourceFile.h" +#include "Statement.h" +#include "StringUtils.h" + + +// #pragma mark - SourceCodeEntry + + +struct DwarfImageDebugInfo::SourceCodeKey { + CompilationUnit* unit; + BString filePath; + + SourceCodeKey(CompilationUnit* unit, const BString& filePath) + : + unit(unit), + filePath(filePath) + { + } + + SourceCodeKey(CompilationUnit* unit, LocatableFile* file) + : + unit(unit) + { + file->GetLocatedPath(filePath); + } + + uint32 HashValue() const + { + return (uint32)(addr_t)unit ^ StringUtils::HashValue(filePath); + } + + bool operator==(const SourceCodeKey& other) const + { + return unit == other.unit && filePath == other.filePath; + } +}; + + +struct DwarfImageDebugInfo::SourceCodeEntry : SourceCodeKey, + HashTableLink { + FileSourceCode* sourceCode; + + SourceCodeEntry(CompilationUnit* unit, LocatableFile* file, + FileSourceCode* sourceCode) + : + SourceCodeKey(unit, file), + sourceCode(sourceCode) + { + } +}; + + +// #pragma mark - SourceCodeHashDefinition + + +struct DwarfImageDebugInfo::SourceCodeHashDefinition { + typedef SourceCodeKey KeyType; + typedef SourceCodeEntry ValueType; + + size_t HashKey(const SourceCodeKey& key) const + { + return key.HashValue(); + } + + size_t Hash(const SourceCodeEntry* value) const + { + return value->HashValue(); + } + + bool Compare(const SourceCodeKey& key, const SourceCodeEntry* value) const + { + return key == *value; + } + + HashTableLink* GetLink(SourceCodeEntry* value) const + { + return value; + } +}; + + +// #pragma mark - DwarfImageDebugInfo DwarfImageDebugInfo::DwarfImageDebugInfo(const ImageInfo& imageInfo, - Architecture* architecture, DwarfFile* file) + Architecture* architecture, FileManager* fileManager, DwarfFile* file) : + fLock("dwarf image debug info"), fImageInfo(imageInfo), fArchitecture(architecture), + fFileManager(fileManager), fFile(file), fTextSegment(NULL), - fRelocationDelta(0) + fRelocationDelta(0), + fSourceCodes(NULL) { } DwarfImageDebugInfo::~DwarfImageDebugInfo() { + SourceCodeEntry* entry = fSourceCodes->Clear(true); + while (entry != NULL) { + SourceCodeEntry* next = entry->fNext; + entry->sourceCode->ReleaseReference(); + entry = next; + } + + delete fSourceCodes; } status_t DwarfImageDebugInfo::Init() { + status_t error = fLock.InitCheck(); + if (error != B_OK) + return error; + + fSourceCodes = new (std::nothrow) SourceCodeTable; + if (fSourceCodes == NULL) + return B_NO_MEMORY; + + error = fSourceCodes->Init(); + if (error != B_OK) + return error; + fTextSegment = fFile->GetElfFile()->TextSegment(); if (fTextSegment == NULL) return B_ENTRY_NOT_FOUND; @@ -118,27 +227,24 @@ printf(" %ld compilation units\n", fFile->CountCompilationUnits()); } // get the source location - const char* directory = NULL; - const char* file = NULL; - uint32 line = 0; - uint32 column = 0; + const char* directoryPath = NULL; + const char* fileName = NULL; + uint32 line = -1; + uint32 column = -1; DwarfUtils::GetDeclarationLocation(fFile, subprogramEntry, - directory, file, line, column); - BString fileName; - if (file != NULL) { - if (directory != NULL) - fileName << directory << '/' << file; - else - fileName << file; + directoryPath, fileName, line, column); + + LocatableFile* file = NULL; + if (fileName != NULL) { + file = fFileManager->GetSourceFile(directoryPath, + fileName); } - // TODO: Avoid unnessecary string allocation! The source file name - // is the same for all contained functions, so they could share the - // string. + Reference fileReference(file, true); // create and add the functions DwarfFunctionDebugInfo* function - = new(std::nothrow) DwarfFunctionDebugInfo(this, - subprogramEntry, rangeList, name, fileName, + = new(std::nothrow) DwarfFunctionDebugInfo(this, unit, + subprogramEntry, rangeList, name, file, SourceLocation(line, column)); if (function == NULL || !functions.AddItem(function)) { delete function; @@ -185,7 +291,14 @@ status_t DwarfImageDebugInfo::LoadSourceCode(FunctionDebugInfo* function, SourceCode*& _sourceCode) { + AutoLocker locker(fLock); + // TODO: Load the actual source code! + status_t error = _LoadSourceCode(function, _sourceCode); + if (error == B_OK) + return B_OK; + + // fall back to disassembling static const target_size_t kMaxBufferSize = 64 * 1024; target_size_t bufferSize = std::min(function->Size(), kMaxBufferSize); @@ -214,3 +327,140 @@ DwarfImageDebugInfo::GetStatement(FunctionDebugInfo* function, // TODO:... return fArchitecture->GetStatement(function, address, _statement); } + + +status_t +DwarfImageDebugInfo::_LoadSourceCode(FunctionDebugInfo* _function, + SourceCode*& _sourceCode) +{ + DwarfFunctionDebugInfo* function + = dynamic_cast(_function); + if (function == NULL) + return B_BAD_VALUE; + + // get the source file + LocatableFile* file = function->SourceFile(); + if (file == NULL) + return B_ENTRY_NOT_FOUND; + + // maybe it's already loaded + CompilationUnit* unit = function->GetCompilationUnit(); + FileSourceCode* sourceCode = _LookupSourceCode(unit, file); + if (sourceCode) { + sourceCode->AcquireReference(); + _sourceCode = sourceCode; + return B_OK; + } + + // get the index of the source file in the compilation unit for cheaper + // comparison below + const char* directory; + int32 fileIndex = -1; + for (int32 i = 0; const char* fileName = unit->FileAt(i, &directory); i++) { + LocatableFile* file = fFileManager->GetSourceFile(directory, fileName); + if (file != NULL) { + file->ReleaseReference(); + if (file == function->SourceFile()) { + fileIndex = i + 1; + // indices are one-based + break; + } + } + } +printf("DwarfImageDebugInfo::_LoadSourceCode(), file: %ld, function at: %#llx\n", fileIndex, function->Address()); + +for (int32 i = 0; const char* fileName = unit->FileAt(i, &directory); i++) { +printf(" file %ld: %s\n", i, fileName); +} + + + // not loaded yet -- get the source file + SourceFile* sourceFile; + status_t error = fFileManager->LoadSourceFile(file, sourceFile); + if (error != B_OK) + return error; + + // create the source code + sourceCode = new(std::nothrow) FileSourceCode(sourceFile); + sourceFile->ReleaseReference(); + if (sourceCode == NULL) + return B_NO_MEMORY; + + error = sourceCode->Init(); + if (error != B_OK) + return error; + ObjectDeleter sourceCodeDeleter(sourceCode); + + // Get the statements by executing the line number program for the + // compilation unit and filtering the rows for our source file. + LineNumberProgram& program = unit->GetLineNumberProgram(); + if (!program.IsValid()) + return B_BAD_DATA; + + LineNumberProgram::State state; + program.GetInitialState(state); + + target_addr_t statementAddress = 0; + int32 statementLine = -1; + int32 statementColumn = -1; + while (program.GetNextRow(state)) { +printf(" %#lx (%ld, %ld, %ld) %d\n", state.address, state.file, state.line, state.column, state.isStatement); + bool isOurFile = state.file == fileIndex; + + if (statementAddress != 0 + && (!isOurFile || state.isStatement || state.isSequenceEnd)) { + target_addr_t endAddress = state.address; + if (endAddress > statementAddress) { + // add the statement + ContiguousStatement* statement = new(std::nothrow) + ContiguousStatement( + SourceLocation(statementLine, statementColumn), + SourceLocation(statementLine, statementColumn), + TargetAddressRange(fRelocationDelta + statementAddress, + endAddress - statementAddress), true); + if (statement == NULL) + return B_NO_MEMORY; + + error = sourceCode->AddStatement(statement); + if (error != B_OK) { + delete statement; + return error; + } +printf(" -> statement: %#llx - %#llx, line: %ld\n", statement->AddressRange().Start(), +statement->AddressRange().End(), statementLine); + } + + statementAddress = 0; + } + + // skip statements of other files + if (!isOurFile) + continue; + + if (state.isStatement) { + statementAddress = state.address; + statementLine = state.line - 1; + statementColumn = state.column - 1; + } + } + + SourceCodeEntry* entry = new(std::nothrow) SourceCodeEntry(unit, file, + sourceCode); + if (entry == NULL) + return B_NO_MEMORY; + + fSourceCodes->Insert(entry); + + _sourceCode = sourceCodeDeleter.Detach(); + _sourceCode->AcquireReference(); + return B_OK; +} + + +FileSourceCode* +DwarfImageDebugInfo::_LookupSourceCode(CompilationUnit* unit, + LocatableFile* file) +{ + SourceCodeEntry* entry = fSourceCodes->Lookup(SourceCodeKey(unit, file)); + return entry != NULL ? entry->sourceCode : NULL; +} diff --git a/src/apps/debugger/debug_info/DwarfImageDebugInfo.h b/src/apps/debugger/debug_info/DwarfImageDebugInfo.h index f5cad8a14d..0bdacbcccb 100644 --- a/src/apps/debugger/debug_info/DwarfImageDebugInfo.h +++ b/src/apps/debugger/debug_info/DwarfImageDebugInfo.h @@ -5,20 +5,29 @@ #ifndef DWARF_IMAGE_DEBUG_INFO_H #define DWARF_IMAGE_DEBUG_INFO_H +#include + +#include + #include "ImageInfo.h" #include "SpecificImageDebugInfo.h" class Architecture; +class CompilationUnit; class DwarfFile; class ElfSegment; +class FileManager; +class FileSourceCode; +class LocatableFile; +class SourceCode; class DwarfImageDebugInfo : public SpecificImageDebugInfo { public: DwarfImageDebugInfo(const ImageInfo& imageInfo, Architecture* architecture, - DwarfFile* file); + FileManager* fileManager, DwarfFile* file); virtual ~DwarfImageDebugInfo(); status_t Init(); @@ -40,11 +49,27 @@ public: Statement*& _statement); private: + struct SourceCodeKey; + struct SourceCodeEntry; + struct SourceCodeHashDefinition; + + typedef OpenHashTable SourceCodeTable; + +private: + status_t _LoadSourceCode(FunctionDebugInfo* function, + SourceCode*& _sourceCode); + FileSourceCode* _LookupSourceCode(CompilationUnit* unit, + LocatableFile* file); + +private: + BLocker fLock; ImageInfo fImageInfo; Architecture* fArchitecture; + FileManager* fFileManager; DwarfFile* fFile; ElfSegment* fTextSegment; target_addr_t fRelocationDelta; + SourceCodeTable* fSourceCodes; }; diff --git a/src/apps/debugger/debug_info/DwarfTeamDebugInfo.cpp b/src/apps/debugger/debug_info/DwarfTeamDebugInfo.cpp index 9bfb3a6937..99c1f81380 100644 --- a/src/apps/debugger/debug_info/DwarfTeamDebugInfo.cpp +++ b/src/apps/debugger/debug_info/DwarfTeamDebugInfo.cpp @@ -14,9 +14,11 @@ #include "LocatableFile.h" -DwarfTeamDebugInfo::DwarfTeamDebugInfo(Architecture* architecture) +DwarfTeamDebugInfo::DwarfTeamDebugInfo(Architecture* architecture, + FileManager* fileManager) : fArchitecture(architecture), + fFileManager(fileManager), fManager(NULL) { } @@ -62,7 +64,7 @@ DwarfTeamDebugInfo::CreateImageDebugInfo(const ImageInfo& imageInfo, // create the image debug info DwarfImageDebugInfo* debuggerInfo = new(std::nothrow) DwarfImageDebugInfo( - imageInfo, fArchitecture, file); + imageInfo, fArchitecture, fFileManager, file); if (debuggerInfo == NULL) return B_NO_MEMORY; diff --git a/src/apps/debugger/debug_info/DwarfTeamDebugInfo.h b/src/apps/debugger/debug_info/DwarfTeamDebugInfo.h index 9e21119f1c..555dab45f6 100644 --- a/src/apps/debugger/debug_info/DwarfTeamDebugInfo.h +++ b/src/apps/debugger/debug_info/DwarfTeamDebugInfo.h @@ -10,12 +10,14 @@ class Architecture; class DwarfManager; +class FileManager; class ImageInfo; class DwarfTeamDebugInfo : public SpecificTeamDebugInfo { public: - DwarfTeamDebugInfo(Architecture* architecture); + DwarfTeamDebugInfo(Architecture* architecture, + FileManager* fileManager); virtual ~DwarfTeamDebugInfo(); status_t Init(); @@ -26,6 +28,7 @@ public: private: Architecture* fArchitecture; + FileManager* fFileManager; DwarfManager* fManager; }; diff --git a/src/apps/debugger/debug_info/FunctionDebugInfo.h b/src/apps/debugger/debug_info/FunctionDebugInfo.h index 63181814a1..242b45f1aa 100644 --- a/src/apps/debugger/debug_info/FunctionDebugInfo.h +++ b/src/apps/debugger/debug_info/FunctionDebugInfo.h @@ -20,6 +20,7 @@ enum function_source_state { }; +class LocatableFile; class SourceCode; class SpecificImageDebugInfo; @@ -38,7 +39,7 @@ public: virtual const char* Name() const = 0; virtual const char* PrettyName() const = 0; - virtual const char* SourceFileName() const = 0; + virtual LocatableFile* SourceFile() const = 0; virtual SourceLocation SourceStartLocation() const = 0; virtual SourceLocation SourceEndLocation() const = 0; diff --git a/src/apps/debugger/debug_info/TeamDebugInfo.cpp b/src/apps/debugger/debug_info/TeamDebugInfo.cpp index 3c04965004..6f1036f6bb 100644 --- a/src/apps/debugger/debug_info/TeamDebugInfo.cpp +++ b/src/apps/debugger/debug_info/TeamDebugInfo.cpp @@ -16,10 +16,11 @@ TeamDebugInfo::TeamDebugInfo(DebuggerInterface* debuggerInterface, - Architecture* architecture) + Architecture* architecture, FileManager* fileManager) : fDebuggerInterface(debuggerInterface), fArchitecture(architecture), + fFileManager(fileManager), fSpecificInfos(10, true) { } @@ -38,7 +39,7 @@ TeamDebugInfo::Init() // DWARF DwarfTeamDebugInfo* dwarfInfo = new(std::nothrow) DwarfTeamDebugInfo( - fArchitecture); + fArchitecture, fFileManager); if (dwarfInfo == NULL || !fSpecificInfos.AddItem(dwarfInfo)) { delete dwarfInfo; return B_NO_MEMORY; diff --git a/src/apps/debugger/debug_info/TeamDebugInfo.h b/src/apps/debugger/debug_info/TeamDebugInfo.h index 72e089e4e1..6f713aa355 100644 --- a/src/apps/debugger/debug_info/TeamDebugInfo.h +++ b/src/apps/debugger/debug_info/TeamDebugInfo.h @@ -13,6 +13,7 @@ class Architecture; class DebuggerInterface; +class FileManager; class ImageDebugInfo; class ImageInfo; class LocatableFile; @@ -23,7 +24,8 @@ class TeamDebugInfo : public Referenceable { public: TeamDebugInfo( DebuggerInterface* debuggerInterface, - Architecture* architecture); + Architecture* architecture, + FileManager* fileManager); ~TeamDebugInfo(); status_t Init(); @@ -38,6 +40,7 @@ private: private: DebuggerInterface* fDebuggerInterface; Architecture* fArchitecture; + FileManager* fFileManager; SpecificInfoList fSpecificInfos; }; diff --git a/src/apps/debugger/dwarf/CompilationUnit.h b/src/apps/debugger/dwarf/CompilationUnit.h index 8864358a51..5f2e7b490a 100644 --- a/src/apps/debugger/dwarf/CompilationUnit.h +++ b/src/apps/debugger/dwarf/CompilationUnit.h @@ -11,6 +11,7 @@ #include "Array.h" #include "DwarfTypes.h" +#include "LineNumberProgram.h" class AbbreviationTable; @@ -45,6 +46,9 @@ public: DIECompileUnitBase* UnitEntry() const { return fUnitEntry; } void SetUnitEntry(DIECompileUnitBase* entry); + LineNumberProgram& GetLineNumberProgram() + { return fLineNumberProgram; } + status_t AddDebugInfoEntry(DebugInfoEntry* entry, dwarf_off_t offset); int CountEntries() const; @@ -77,6 +81,7 @@ private: Array fEntryOffsets; DirectoryList fDirectories; FileList fFiles; + LineNumberProgram fLineNumberProgram; }; diff --git a/src/apps/debugger/dwarf/DwarfFile.cpp b/src/apps/debugger/dwarf/DwarfFile.cpp index f6249d895c..a60f888a30 100644 --- a/src/apps/debugger/dwarf/DwarfFile.cpp +++ b/src/apps/debugger/dwarf/DwarfFile.cpp @@ -5,6 +5,7 @@ #include "DwarfFile.h" +#include #include #include @@ -631,6 +632,9 @@ printf("DwarfFile::_ParseLineInfo(%p), offset: %lu\n", unit, offset); // unit length bool dwarf64; uint64 unitLength = dataReader.ReadInitialLength(dwarf64); + if (unitLength > (uint64)dataReader.BytesRemaining()) + return B_BAD_DATA; + off_t unitOffset = dataReader.Offset(); // version (uhalf) uint16 version = dataReader.Read(0); @@ -638,6 +642,10 @@ printf("DwarfFile::_ParseLineInfo(%p), offset: %lu\n", unit, offset); // header_length (4/8) uint64 headerLength = dwarf64 ? dataReader.Read(0) : (uint64)dataReader.Read(0); + off_t headerOffset = dataReader.Offset(); + + if ((uint64)dataReader.BytesRemaining() < headerLength) + return B_BAD_DATA; // minimum instruction length uint8 minInstructionLength = dataReader.Read(0); @@ -655,6 +663,7 @@ printf("DwarfFile::_ParseLineInfo(%p), offset: %lu\n", unit, offset); uint8 opcodeBase = dataReader.Read(0); // standard_opcode_lengths (ubyte[]) + const uint8* standardOpcodeLengths = (const uint8*)dataReader.Data(); dataReader.Skip(opcodeBase - 1); if (dataReader.HasOverflow()) @@ -702,7 +711,17 @@ printf("DwarfFile::_ParseLineInfo(%p), offset: %lu\n", unit, offset); return B_NO_MEMORY; } - return B_OK; + off_t readerOffset = dataReader.Offset(); + if ((uint64)readerOffset > readerOffset + headerLength) + return B_BAD_DATA; + off_t offsetToProgram = headerOffset + headerLength - readerOffset; + + const uint8* program = (uint8*)dataReader.Data() + offsetToProgram; + size_t programSize = unitLength - (readerOffset - unitOffset); + + return unit->GetLineNumberProgram().Init(program, programSize, + minInstructionLength, defaultIsStatement, lineBase, lineRange, + opcodeBase, standardOpcodeLengths); } diff --git a/src/apps/debugger/dwarf/DwarfUtils.cpp b/src/apps/debugger/dwarf/DwarfUtils.cpp index 459d93186b..bd7c39d46f 100644 --- a/src/apps/debugger/dwarf/DwarfUtils.cpp +++ b/src/apps/debugger/dwarf/DwarfUtils.cpp @@ -128,7 +128,7 @@ DwarfUtils::GetDeclarationLocation(DwarfFile* dwarfFile, _directory = directoryName; _file = fileName; - _line = line; - _column = column; + _line = line - 1; + _column = column - 1; return true; } diff --git a/src/apps/debugger/dwarf/Jamfile b/src/apps/debugger/dwarf/Jamfile index 7cc3444a76..74504a80b8 100644 --- a/src/apps/debugger/dwarf/Jamfile +++ b/src/apps/debugger/dwarf/Jamfile @@ -23,6 +23,7 @@ MergeObject Debugger_dwarf.o DwarfFile.cpp DwarfManager.cpp DwarfUtils.cpp + LineNumberProgram.cpp SourceLanguageInfo.cpp TagNames.cpp ; diff --git a/src/apps/debugger/dwarf/LineNumberProgram.cpp b/src/apps/debugger/dwarf/LineNumberProgram.cpp new file mode 100644 index 0000000000..1ac3361adb --- /dev/null +++ b/src/apps/debugger/dwarf/LineNumberProgram.cpp @@ -0,0 +1,208 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + +#include "LineNumberProgram.h" + +#include + +#include +#include + +#include "Dwarf.h" + + +static const uint8 kLineNumberStandardOpcodeOperands[] + = { 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 }; +static const uint32 kLineNumberStandardOpcodeCount = 12; + + +LineNumberProgram::LineNumberProgram() + : + fProgram(NULL), + fProgramSize(0), + fMinInstructionLength(0), + fDefaultIsStatement(0), + fLineBase(0), + fLineRange(0), + fOpcodeBase(0), + fStandardOpcodeLengths(NULL) +{ +} + + +LineNumberProgram::~LineNumberProgram() +{ +} + + +status_t +LineNumberProgram::Init(const void* program, size_t programSize, + uint8 minInstructionLength, bool defaultIsStatement, int8 lineBase, + uint8 lineRange, uint8 opcodeBase, const uint8* standardOpcodeLengths) +{ + // first check the operand counts for the standard opcodes + uint8 standardOpcodeCount = std::min((uint32)opcodeBase - 1, + kLineNumberStandardOpcodeCount); + for (uint8 i = 0; i < standardOpcodeCount; i++) { + if (standardOpcodeLengths[i] != kLineNumberStandardOpcodeOperands[i]) { +printf("operand count for standard opcode %u does not what we expect\n", i + 1); + return B_BAD_DATA; + } + } + + fProgram = program; + fProgramSize = programSize; + fMinInstructionLength = minInstructionLength; + fDefaultIsStatement = defaultIsStatement; + fLineBase = lineBase; + fLineRange = lineRange; + fOpcodeBase = opcodeBase; + fStandardOpcodeLengths = standardOpcodeLengths; + + return B_OK; +} + + +void +LineNumberProgram::GetInitialState(State& state) const +{ + if (!IsValid()) + return; + + _SetToInitial(state); + state.dataReader.SetTo(fProgram, fProgramSize); +} + + +bool +LineNumberProgram::GetNextRow(State& state) const +{ + if (state.isSequenceEnd) + _SetToInitial(state); + + DataReader& dataReader = state.dataReader; + + while (dataReader.BytesRemaining() > 0) { + bool appendRow = false; + uint8 opcode = dataReader.Read(0); + if (opcode >= fOpcodeBase) { + // special opcode + uint adjustedOpcode = opcode - fOpcodeBase; + state.address += (adjustedOpcode / fLineRange) + * fMinInstructionLength; + state.line += adjustedOpcode % fLineRange + fLineBase; + state.isBasicBlock = false; + state.isPrologueEnd = false; + state.isEpilogueBegin = false; + appendRow = true; + } else if (opcode > 0) { + // standard opcode + switch (opcode) { + case DW_LNS_copy: + state.isBasicBlock = false; + state.isPrologueEnd = false; + state.isEpilogueBegin = false; + appendRow = true; + break; + case DW_LNS_advance_pc: + state.address += dataReader.ReadUnsignedLEB128(0) + * fMinInstructionLength; + break; + case DW_LNS_advance_line: + state.line += dataReader.ReadSignedLEB128(0); + break; + case DW_LNS_set_file: + state.file = dataReader.ReadUnsignedLEB128(0); + break; + case DW_LNS_set_column: + state.column = dataReader.ReadUnsignedLEB128(0); + break; + case DW_LNS_negate_stmt: + state.isStatement = !state.isStatement; + break; + case DW_LNS_set_basic_block: + state.isBasicBlock = true; + break; + case DW_LNS_const_add_pc: + state.address += ((255 - fOpcodeBase) / fLineRange) + * fMinInstructionLength; + break; + case DW_LNS_fixed_advance_pc: + state.address += dataReader.Read(0); + break; + case DW_LNS_set_prologue_end: + state.isPrologueEnd = true; + break; + case DW_LNS_set_epilogue_begin: + state.isEpilogueBegin = true; + break; + case DW_LNS_set_isa: + state.instructionSet = dataReader.ReadUnsignedLEB128(0); + break; + default: +printf("unsupported standard opcode %u\n", opcode); + for (int32 i = 0; i < fStandardOpcodeLengths[opcode - 1]; + i++) { + dataReader.ReadUnsignedLEB128(0); + } + } + } else { + // extended opcode + uint32 instructionLength = dataReader.ReadUnsignedLEB128(0); + off_t instructionOffset = dataReader.Offset(); + uint8 extendedOpcode = dataReader.Read(0); + + switch (extendedOpcode) { + case DW_LNE_end_sequence: + state.isSequenceEnd = true; + appendRow = true; + break; + case DW_LNE_set_address: + state.address = dataReader.Read(0); + break; + case DW_LNE_define_file: + { + state.explicitFile = dataReader.ReadString(); + state.explicitFileDirIndex + = dataReader.ReadUnsignedLEB128(0); + dataReader.ReadUnsignedLEB128(0); // modification time + dataReader.ReadUnsignedLEB128(0); // file length + state.file = -1; + break; + } + default: +printf("unsupported extended opcode: %u\n", extendedOpcode); + break; + } + + dataReader.Skip(instructionLength + - (dataReader.Offset() - instructionOffset)); + } + + if (dataReader.HasOverflow()) + return false; + + if (appendRow) + return true; + } + + return false; +} + + +void +LineNumberProgram::_SetToInitial(State& state) const +{ + state.address = 0; + state.file = 1; + state.line = 1; + state.column = 0; + state.isStatement = fDefaultIsStatement; + state.isBasicBlock = false; + state.isSequenceEnd = false; + state.isPrologueEnd = false; + state.isEpilogueBegin = false; + state.instructionSet = 0; +} diff --git a/src/apps/debugger/dwarf/LineNumberProgram.h b/src/apps/debugger/dwarf/LineNumberProgram.h new file mode 100644 index 0000000000..24426f86a8 --- /dev/null +++ b/src/apps/debugger/dwarf/LineNumberProgram.h @@ -0,0 +1,65 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef LINE_NUMBER_PROGRAM_H +#define LINE_NUMBER_PROGRAM_H + +#include "DataReader.h" +#include "DwarfTypes.h" + + +class LineNumberProgram { +public: + struct State; + +public: + LineNumberProgram(); + ~LineNumberProgram(); + + status_t Init(const void* program, size_t programSize, + uint8 minInstructionLength, + bool defaultIsStatement, int8 lineBase, + uint8 lineRange, uint8 opcodeBase, + const uint8* standardOpcodeLengths); + + bool IsValid() const { return fProgram != NULL; } + void GetInitialState(State& state) const; + bool GetNextRow(State& state) const; + +private: + void _SetToInitial(State& state) const; + +private: + const void* fProgram; + size_t fProgramSize; + uint8 fMinInstructionLength; + bool fDefaultIsStatement; + int8 fLineBase; + uint8 fLineRange; + uint8 fOpcodeBase; + const uint8* fStandardOpcodeLengths; +}; + + +struct LineNumberProgram::State { + dwarf_addr_t address; + int32 file; + int32 line; + int32 column; + bool isStatement; + bool isBasicBlock; + bool isSequenceEnd; + bool isPrologueEnd; + bool isEpilogueBegin; + uint32 instructionSet; + + // when file is set to -1 + const char* explicitFile; + uint32 explicitFileDirIndex; + + DataReader dataReader; +}; + + +#endif // LINE_NUMBER_PROGRAM_H diff --git a/src/apps/debugger/files/FileManager.cpp b/src/apps/debugger/files/FileManager.cpp index 07caa80760..3cf0eb4cc4 100644 --- a/src/apps/debugger/files/FileManager.cpp +++ b/src/apps/debugger/files/FileManager.cpp @@ -7,10 +7,12 @@ #include +#include #include #include "LocatableDirectory.h" #include "LocatableFile.h" +#include "SourceFile.h" #include "StringUtils.h" @@ -111,12 +113,6 @@ public: ~Domain() { - LocatableEntry* entry = fEntries.Clear(true); - while (entry != NULL) { - LocatableEntry* next = entry->fNext; - entry->RemoveReference(); - entry = next; - } } status_t Init() @@ -168,7 +164,7 @@ public: BString name; _SplitPath(path, directory, name); - LocatableEntry* entry = fEntries.Lookup(EntryPath(directory, name)); + LocatableEntry* entry = _LookupEntry(EntryPath(directory, name)); if (entry == NULL) return; @@ -186,10 +182,12 @@ private: fManager->Unlock(); } - virtual bool LocatableEntryUnused(LocatableEntry* entry) + virtual void LocatableEntryUnused(LocatableEntry* entry) { - fEntries.Remove(entry); - return true; + fManager->Lock(); + if (fEntries.Lookup(EntryPath(entry)) == entry) + fEntries.Remove(entry); + fManager->Unlock(); } bool _LocateDirectory(LocatableDirectory* directory, @@ -285,7 +283,7 @@ private: LocatableFile* _GetFile(const BString& directoryPath, const BString& name) { // if already know return the file - LocatableEntry* entry = fEntries.Lookup(EntryPath(directoryPath, name)); + LocatableEntry* entry = _LookupEntry(EntryPath(directoryPath, name)); if (entry != NULL) { LocatableFile* file = dynamic_cast(entry); if (file == NULL) @@ -321,7 +319,7 @@ private: // if already know return the directory LocatableEntry* entry - = fEntries.Lookup(EntryPath(directoryPath, fileName)); + = _LookupEntry(EntryPath(directoryPath, fileName)); if (entry != NULL) { LocatableDirectory* directory = dynamic_cast(entry); @@ -354,13 +352,30 @@ private: directory->SetLocatedPath(dirPath, false); } else if (parentDirectory != NULL && parentDirectory->State() != LOCATABLE_ENTRY_UNLOCATED) { -// TODO:... + BString locatedDirectoryPath; + if (parentDirectory->GetLocatedPath(locatedDirectoryPath)) + _LocateEntryInParentDir(directory, locatedDirectoryPath); } fEntries.Insert(directory); return directory; } + LocatableEntry* _LookupEntry(const EntryPath& entryPath) + { + LocatableEntry* entry = fEntries.Lookup(entryPath); + if (entry == NULL) + return NULL; + + // if already unreferenced, remove it + if (entry->CountReferences() == 0) { + fEntries.Remove(entry); + return NULL; + } + + return entry; + } + void _NormalizePath(const BString& path, BString& _normalizedPath) { BString normalizedPath; @@ -445,6 +460,66 @@ private: }; +// #pragma mark - SourceFileEntry + + +struct FileManager::SourceFileEntry : public SourceFileOwner, + public HashTableLink { + + FileManager* manager; + BString path; + SourceFile* file; + + SourceFileEntry(FileManager* manager, const BString& path) + : + manager(manager), + path(path), + file(NULL) + { + } + + virtual void SourceFileUnused(SourceFile* sourceFile) + { + manager->_SourceFileUnused(this); + } + + virtual void SourceFileDeleted(SourceFile* sourceFile) + { + // We have already been removed from the table, so commit suicide. + delete this; + } +}; + + +// #pragma mark - SourceFileHashDefinition + + +struct FileManager::SourceFileHashDefinition { + typedef BString KeyType; + typedef SourceFileEntry ValueType; + + size_t HashKey(const BString& key) const + { + return StringUtils::HashValue(key); + } + + size_t Hash(const SourceFileEntry* value) const + { + return HashKey(value->path); + } + + bool Compare(const BString& key, const SourceFileEntry* value) const + { + return value->path == key; + } + + HashTableLink* GetLink(SourceFileEntry* value) const + { + return value; + } +}; + + // #pragma mark - FileManager @@ -452,7 +527,8 @@ FileManager::FileManager() : fLock("file manager"), fTargetDomain(NULL), - fSourceDomain(NULL) + fSourceDomain(NULL), + fSourceFiles(NULL) { } @@ -461,6 +537,7 @@ FileManager::~FileManager() { delete fTargetDomain; delete fSourceDomain; + delete fSourceFiles; } @@ -489,6 +566,15 @@ FileManager::Init(bool targetIsLocal) if (error != B_OK) return error; + // create source file table + fSourceFiles = new(std::nothrow) SourceFileTable; + if (fSourceFiles == NULL) + return B_NO_MEMORY; + + error = fSourceFiles->Init(); + if (error != B_OK) + return error; + return B_OK; } @@ -539,3 +625,75 @@ FileManager::SourceEntryLocated(const BString& path, const BString& locatedPath) AutoLocker locker(this); fSourceDomain->EntryLocated(path, locatedPath); } + + +status_t +FileManager::LoadSourceFile(LocatableFile* file, SourceFile*& _sourceFile) +{ + AutoLocker locker(this); + + // get the path + BString path; + if (!file->GetLocatedPath(path)) + return B_ENTRY_NOT_FOUND; + + // we might already know the source file + SourceFileEntry* entry = _LookupSourceFile(path); + if (entry != NULL) { + entry->file->AcquireReference(); + _sourceFile = entry->file; + return B_OK; + } + + // create the hash table entry + entry = new(std::nothrow) SourceFileEntry(this, path); + if (entry == NULL) + return B_NO_MEMORY; + + // load the file + SourceFile* sourceFile = new(std::nothrow) SourceFile(entry); + if (sourceFile == NULL) { + delete entry; + return B_NO_MEMORY; + } + ObjectDeleter sourceFileDeleter(sourceFile); + + entry->file = sourceFile; + + status_t error = sourceFile->Init(path); + if (error != B_OK) + return error; + + fSourceFiles->Insert(entry); + + _sourceFile = sourceFileDeleter.Detach(); + return B_OK; +} + + +FileManager::SourceFileEntry* +FileManager::_LookupSourceFile(const BString& path) +{ + SourceFileEntry* entry = fSourceFiles->Lookup(path); + if (entry == NULL) + return NULL; + + // the entry might be unused already -- in that case remove it + if (entry->file->CountReferences() == 0) { + fSourceFiles->Remove(entry); + return NULL; + } + + return entry; +} + + +void +FileManager::_SourceFileUnused(SourceFileEntry* entry) +{ + AutoLocker locker(this); + + SourceFileEntry* otherEntry = fSourceFiles->Lookup(entry->path); + if (otherEntry == entry) + fSourceFiles->Remove(entry); +} diff --git a/src/apps/debugger/files/FileManager.h b/src/apps/debugger/files/FileManager.h index 5c329b2392..89d2ff26e1 100644 --- a/src/apps/debugger/files/FileManager.h +++ b/src/apps/debugger/files/FileManager.h @@ -12,6 +12,7 @@ class LocatableFile; +class SourceFile; class FileManager { @@ -40,17 +41,32 @@ public: void SourceEntryLocated(const BString& path, const BString& locatedPath); + status_t LoadSourceFile(LocatableFile* file, + SourceFile*& _sourceFile); + // returns a reference + private: struct EntryPath; struct EntryHashDefinition; class Domain; + struct SourceFileEntry; + struct SourceFileHashDefinition; typedef OpenHashTable LocatableEntryTable; + typedef OpenHashTable SourceFileTable; + + friend struct SourceFileEntry; + // for gcc 2 + +private: + SourceFileEntry* _LookupSourceFile(const BString& path); + void _SourceFileUnused(SourceFileEntry* entry); private: BLocker fLock; Domain* fTargetDomain; Domain* fSourceDomain; + SourceFileTable* fSourceFiles; }; diff --git a/src/apps/debugger/files/LocatableEntry.cpp b/src/apps/debugger/files/LocatableEntry.cpp index d2f62e71fb..a4e2247961 100644 --- a/src/apps/debugger/files/LocatableEntry.cpp +++ b/src/apps/debugger/files/LocatableEntry.cpp @@ -43,7 +43,5 @@ LocatableEntry::~LocatableEntry() void LocatableEntry::LastReferenceReleased() { - AutoLocker locker(fOwner); - if (CountReferences() == 0 && fOwner->LocatableEntryUnused(this)) - delete this; + fOwner->LocatableEntryUnused(this); } diff --git a/src/apps/debugger/files/LocatableEntry.h b/src/apps/debugger/files/LocatableEntry.h index 2e8f4be4fc..a1f6f5fc71 100644 --- a/src/apps/debugger/files/LocatableEntry.h +++ b/src/apps/debugger/files/LocatableEntry.h @@ -30,7 +30,7 @@ public: virtual bool Lock() = 0; virtual void Unlock() = 0; - virtual bool LocatableEntryUnused(LocatableEntry* entry) = 0; + virtual void LocatableEntryUnused(LocatableEntry* entry) = 0; }; diff --git a/src/apps/debugger/files/SourceFile.cpp b/src/apps/debugger/files/SourceFile.cpp new file mode 100644 index 0000000000..f940717a43 --- /dev/null +++ b/src/apps/debugger/files/SourceFile.cpp @@ -0,0 +1,139 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + +#include "SourceFile.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + + +static const int32 kMaxSourceFileSize = 10 * 1024 * 1024; + + +// #pragma mark - SourceFileOwner + + +SourceFileOwner::~SourceFileOwner() +{ +} + + +// #pragma mark - SourceFile + + +SourceFile::SourceFile(SourceFileOwner* owner) + : + fOwner(owner), + fFileContent(NULL), + fLineOffsets(NULL), + fLineCount(0) +{ +} + + +SourceFile::~SourceFile() +{ + free(fFileContent); + delete[] fLineOffsets; + fOwner->SourceFileDeleted(this); +} + + +status_t +SourceFile::Init(const char* path) +{ + // open the file + int fd = open(path, O_RDONLY); + if (fd < 0) + return errno; + + // stat the file to get its size + struct stat st; + if (fstat(fd, &st) < 0) { + close(fd); + return errno; + } + + if (st.st_size > kMaxSourceFileSize) + return B_FILE_TOO_LARGE; + size_t fileSize = st.st_size; + + if (fileSize == 0) + return B_BAD_VALUE; + + // allocate the content buffer + fFileContent = (char*)malloc(fileSize + 1); + // one more byte for a terminating null + if (fFileContent == NULL) { + close(fd); + return B_NO_MEMORY; + } + + // read the file + ssize_t bytesRead = read(fd, fFileContent, fileSize); + close(fd); + if (bytesRead < 0 || (size_t)bytesRead != fileSize) + return bytesRead < 0 ? errno : B_FILE_ERROR; + + // null-terminate + fFileContent[fileSize] = '\0'; + + // count lines + fLineCount = 0; + for (size_t i = 0; i < fileSize; i++) { + if (fFileContent[i] == '\n') + fLineCount++; + } + + if (fFileContent[fileSize - 1] != '\n') + fLineCount++; + + // allocate line offset array + fLineOffsets = new(std::nothrow) int32[fLineCount]; + if (fLineOffsets == NULL) + return B_NO_MEMORY; + + // get the line offsets and null-terminate the lines + int32 lineIndex = 0; + fLineOffsets[lineIndex++] = 0; + for (size_t i = 0; i < fileSize; i++) { + if (fFileContent[i] == '\n') { + fFileContent[i] = '\0'; + fLineOffsets[lineIndex++] = i + 1; + } + } + + return B_OK; +} + + +int32 +SourceFile::CountLines() const +{ + return fLineCount; +} + + +const char* +SourceFile::LineAt(int32 index) const +{ + return index >= 0 && index < fLineCount + ? fFileContent + fLineOffsets[index] : NULL; +} + + +void +SourceFile::LastReferenceReleased() +{ + fOwner->SourceFileUnused(this); + delete this; +} diff --git a/src/apps/debugger/files/SourceFile.h b/src/apps/debugger/files/SourceFile.h new file mode 100644 index 0000000000..8b757c9569 --- /dev/null +++ b/src/apps/debugger/files/SourceFile.h @@ -0,0 +1,46 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef SOURCE_FILE_H +#define SOURCE_FILE_H + +#include + + +class SourceFile; + + +class SourceFileOwner { +public: + virtual ~SourceFileOwner(); + + virtual void SourceFileUnused(SourceFile* sourceFile) = 0; + virtual void SourceFileDeleted(SourceFile* sourceFile) = 0; +}; + + + +class SourceFile : public Referenceable { +public: + SourceFile(SourceFileOwner* owner); + ~SourceFile(); + + status_t Init(const char* path); + + int32 CountLines() const; + const char* LineAt(int32 index) const; + +protected: + virtual void LastReferenceReleased(); + +private: + SourceFileOwner* fOwner; + char* fFileContent; + int32* fLineOffsets; + int32 fLineCount; +}; + + + +#endif // SOURCE_FILE_H diff --git a/src/apps/debugger/gui/team_window/ImageFunctionsView.cpp b/src/apps/debugger/gui/team_window/ImageFunctionsView.cpp index 97f79b21ad..6cb0d9111b 100644 --- a/src/apps/debugger/gui/team_window/ImageFunctionsView.cpp +++ b/src/apps/debugger/gui/team_window/ImageFunctionsView.cpp @@ -17,6 +17,7 @@ #include "FunctionDebugInfo.h" #include "Image.h" #include "ImageDebugInfo.h" +#include "LocatableFile.h" // #pragma mark - FunctionsTableModel @@ -77,8 +78,8 @@ public: // count the different source files int32 sourceFileCount = 1; for (int32 i = 1; i < functionCount; i++) { - if (_CompareSourceFileNames(functions[i - 1]->SourceFileName(), - functions[i]->SourceFileName()) != 0) { + if (_CompareSourceFileNames(functions[i - 1]->SourceFile(), + functions[i]->SourceFile()) != 0) { sourceFileCount++; } } @@ -93,8 +94,8 @@ public: int32 sourceFileIndex = 1; for (int32 i = 1; i < functionCount; i++) { - if (_CompareSourceFileNames(functions[i - 1]->SourceFileName(), - functions[i]->SourceFileName()) != 0) { + if (_CompareSourceFileNames(functions[i - 1]->SourceFile(), + functions[i]->SourceFile()) != 0) { fSourceFileIndices[sourceFileIndex++] = i; } } @@ -158,9 +159,14 @@ public: if (object >= fSourceFileIndices && object < fSourceFileIndices + fSourceFileCount) { - const char* name = fFunctions[*(int32*)object]->SourceFileName(); - value.SetTo(name != NULL ? name : "", - B_VARIANT_DONT_COPY_DATA); + int32 index = *(int32*)object; + if (LocatableFile* file = fFunctions[index]->SourceFile()) { + BString path; + file->GetPath(path); + value.SetTo(path); + } else + value.SetTo("", B_VARIANT_DONT_COPY_DATA); + return true; } @@ -191,8 +197,8 @@ public: && _path.AddComponent(index - fSourceFileIndices[sourceIndex]); } - bool GetObjectForPath(const TreeTablePath& path, const char*& _sourceFile, - FunctionDebugInfo*& _function) + bool GetObjectForPath(const TreeTablePath& path, + LocatableFile*& _sourceFile, FunctionDebugInfo*& _function) { int32 componentCount = path.CountComponents(); if (componentCount == 0 || componentCount > 2) @@ -202,8 +208,7 @@ public: if (sourceIndex < 0 || sourceIndex >= fSourceFileCount) return false; - _sourceFile = fFunctions[fSourceFileIndices[sourceIndex]] - ->SourceFileName(); + _sourceFile = fFunctions[fSourceFileIndices[sourceIndex]]->SourceFile(); _function = NULL; @@ -232,7 +237,7 @@ private: return nextFunctionIndex - fSourceFileIndices[sourceIndex]; } - static int _CompareSourceFileNames(const char* a, const char* b) + static int _CompareSourceFileNames(LocatableFile* a, LocatableFile* b) { if (a == b) return 0; @@ -242,15 +247,21 @@ private: if (b == NULL) return -1; - return strcmp(a, b); + BString pathA; + a->GetPath(pathA); + + BString pathB; + b->GetPath(pathB); + + return pathA.Compare(pathB); } static bool _FunctionLess(const FunctionDebugInfo* a, const FunctionDebugInfo* b) { // compare source file name first - int compared = _CompareSourceFileNames(a->SourceFileName(), - b->SourceFileName()); + int compared = _CompareSourceFileNames(a->SourceFile(), + b->SourceFile()); if (compared != 0) return compared < 0; @@ -338,6 +349,9 @@ printf("ImageFunctionsView::SetImageDebugInfo(%p)\n", imageDebugInfo); fFunctionsTable->SetNodeExpanded(path, true, false); } + if (fImageDebugInfo != NULL) + fFunctionsTable->ResizeAllColumnsToPreferred(); + printf("ImageFunctionsView::SetImageDebugInfo(%p) done\n", imageDebugInfo); } @@ -362,7 +376,7 @@ ImageFunctionsView::TreeTableSelectionChanged(TreeTable* table) if (fListener == NULL) return; - const char* sourceFile = NULL; + LocatableFile* sourceFile = NULL; FunctionDebugInfo* function = NULL; TreeTablePath path; if (table->SelectionModel()->GetPathAt(0, path)) diff --git a/src/apps/debugger/model/FileSourceCode.cpp b/src/apps/debugger/model/FileSourceCode.cpp new file mode 100644 index 0000000000..d4677bb28c --- /dev/null +++ b/src/apps/debugger/model/FileSourceCode.cpp @@ -0,0 +1,144 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + +#include "FileSourceCode.h" + +#include + +#include "SourceFile.h" +#include "Statement.h" + + +// TODO: Lot's of code duplication from DissassembledCode! + + +FileSourceCode::FileSourceCode(SourceFile* file) + : + fFile(file), + fLineStatements(NULL) +{ + fFile->AcquireReference(); +} + + +FileSourceCode::~FileSourceCode() +{ + for (int32 i = 0; Statement* statement = fStatements.ItemAt(i); i++) + statement->RemoveReference(); + + delete[] fLineStatements; + + fFile->ReleaseReference(); +} + + +status_t +FileSourceCode::Init() +{ + fLineStatements = new(std::nothrow) Statement*[fFile->CountLines()]; + if (fLineStatements == NULL) + return B_NO_MEMORY; + + memset(fLineStatements, 0, fFile->CountLines() * sizeof(Statement*)); + + return B_OK; +} + + +status_t +FileSourceCode::AddStatement(ContiguousStatement* statement) +{ + if (!fStatements.BinaryInsert(statement, &_CompareStatements)) + return B_NO_MEMORY; + + int32 line = statement->StartSourceLocation().Line(); + if (line >= 0 && line < fFile->CountLines() + && fLineStatements[line] == NULL) { + fLineStatements[line] = statement; + } + + statement->AcquireReference(); + return B_OK; +} + + +int32 +FileSourceCode::CountLines() const +{ + return fFile->CountLines(); +} + + +const char* +FileSourceCode::LineAt(int32 index) const +{ + return fFile->LineAt(index); +} + + +int32 +FileSourceCode::CountStatements() const +{ + return fStatements.CountItems(); +} + + +Statement* +FileSourceCode::StatementAt(int32 index) const +{ + return fStatements.ItemAt(index); +} + + +Statement* +FileSourceCode::StatementAtLine(int32 index) const +{ + return index >= 0 && index < CountLines() ? fLineStatements[index] : NULL; +} + + +Statement* +FileSourceCode::StatementAtAddress(target_addr_t address) const +{ + return fStatements.BinarySearchByKey(address, &_CompareAddressStatement); +} + + +TargetAddressRange +FileSourceCode::StatementAddressRange() const +{ + if (fStatements.IsEmpty()) + return TargetAddressRange(); + + ContiguousStatement* first = fStatements.ItemAt(0); + ContiguousStatement* last + = fStatements.ItemAt(fStatements.CountItems() - 1); + return TargetAddressRange(first->AddressRange().Start(), + last->AddressRange().End()); +} + + +/*static*/ int +FileSourceCode::_CompareStatements(const ContiguousStatement* a, + const ContiguousStatement* b) +{ + target_addr_t addressA = a->AddressRange().Start(); + target_addr_t addressB = b->AddressRange().Start(); + if (addressA < addressB) + return -1; + return addressA == addressB ? 0 : 1; +} + + +/*static*/ int +FileSourceCode::_CompareAddressStatement(const target_addr_t* address, + const ContiguousStatement* statement) +{ + const TargetAddressRange& range = statement->AddressRange(); + + if (*address < range.Start()) + return -1; + return *address < range.End() ? 0 : 1; +} diff --git a/src/apps/debugger/model/FileSourceCode.h b/src/apps/debugger/model/FileSourceCode.h new file mode 100644 index 0000000000..04103f0291 --- /dev/null +++ b/src/apps/debugger/model/FileSourceCode.h @@ -0,0 +1,53 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef FILE_SOURCE_CODE_H +#define FILE_SOURCE_CODE_H + +#include + +#include "SourceCode.h" + + +class ContiguousStatement; +class SourceFile; + + +class FileSourceCode : public SourceCode { +public: + FileSourceCode(SourceFile* file); + virtual ~FileSourceCode(); + + status_t Init(); + status_t AddStatement(ContiguousStatement* statement); + + virtual int32 CountLines() const; + virtual const char* LineAt(int32 index) const; + + virtual int32 CountStatements() const; + virtual Statement* StatementAt(int32 index) const; + virtual Statement* StatementAtLine(int32 index) const; + virtual Statement* StatementAtAddress(target_addr_t address) const; + + virtual TargetAddressRange StatementAddressRange() const; + +private: + typedef BObjectList StatementList; + +private: + static int _CompareStatements( + const ContiguousStatement* a, + const ContiguousStatement* b); + static int _CompareAddressStatement( + const target_addr_t* address, + const ContiguousStatement* statement); + +private: + SourceFile* fFile; + Statement** fLineStatements; + StatementList fStatements; +}; + + +#endif // FILE_BASED_SOURCE_CODE_H