* Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
* Copyright 2012-2016, Rene Gollent, rene@gollent.com.
* Distributed under the terms of the MIT License.
*/
#include "TeamDebugInfo.h"
#include <stdio.h>
#include <new>
#include <AutoDeleter.h>
#include <AutoLocker.h>
#include "Architecture.h"
#include "DebuggerInterface.h"
#include "DebuggerTeamDebugInfo.h"
#include "DisassembledCode.h"
#include "DwarfTeamDebugInfo.h"
#include "FileManager.h"
#include "FileSourceCode.h"
#include "Function.h"
#include "FunctionID.h"
#include "ImageDebugInfo.h"
#include "ImageDebugInfoLoadingState.h"
#include "LocatableFile.h"
#include "SourceFile.h"
#include "SourceLanguage.h"
#include "SpecificImageDebugInfo.h"
#include "Type.h"
#include "TypeLookupConstraints.h"
struct TeamDebugInfo::FunctionHashDefinition {
typedef const FunctionInstance* KeyType;
typedef Function ValueType;
size_t HashKey(const FunctionInstance* key) const
{
if (key->SourceFile() == NULL)
return (uint32)(addr_t)key;
uint32 hash = key->Name().HashValue();
hash = hash * 17 + (uint32)(addr_t)key->SourceFile();
SourceLocation location = key->GetSourceLocation();
hash = hash * 17 + location.Line();
hash = hash * 17 + location.Column();
return hash;
}
size_t Hash(const Function* value) const
{
return HashKey(value->FirstInstance());
}
bool Compare(const FunctionInstance* key, const Function* value) const
{
if (key->SourceFile() != value->SourceFile())
return false;
if (key->SourceFile() == NULL)
return key == value->FirstInstance();
return key->GetSourceLocation() == value->GetSourceLocation()
&& key->Name() == value->Name();
}
Function*& GetLink(Function* value) const
{
return value->fNext;
}
};
struct TeamDebugInfo::SourceFileEntry {
SourceFileEntry(LocatableFile* sourceFile)
:
fSourceFile(sourceFile),
fSourceCode(NULL)
{
fSourceFile->AcquireReference();
}
~SourceFileEntry()
{
SetSourceCode(NULL);
fSourceFile->ReleaseReference();
}
status_t Init()
{
return B_OK;
}
LocatableFile* SourceFile() const
{
return fSourceFile;
}
FileSourceCode* GetSourceCode() const
{
return fSourceCode;
}
void SetSourceCode(FileSourceCode* sourceCode)
{
if (sourceCode == fSourceCode)
return;
if (fSourceCode != NULL)
fSourceCode->ReleaseReference();
fSourceCode = sourceCode;
if (fSourceCode != NULL)
fSourceCode->AcquireReference();
}
bool IsUnused() const
{
return fFunctions.IsEmpty();
}
status_t AddFunction(Function* function)
{
if (!fFunctions.BinaryInsert(function, &_CompareFunctions))
return B_NO_MEMORY;
return B_OK;
}
void RemoveFunction(Function* function)
{
int32 index = fFunctions.BinarySearchIndex(*function,
&_CompareFunctions);
if (index >= 0)
fFunctions.RemoveItemAt(index);
}
Function* FunctionAtLocation(const SourceLocation& location) const
{
int32 index = fFunctions.BinarySearchIndexByKey(location,
&_CompareLocationFunction);
if (index >= 0)
return fFunctions.ItemAt(index);
index = -index - 1;
if (index == 0)
return NULL;
return fFunctions.ItemAt(index - 1);
}
Function* FunctionAt(int32 index) const
{
return fFunctions.ItemAt(index);
}
Function* FunctionByName(const BString& name) const
{
for (int32 i = 0; Function* function = fFunctions.ItemAt(i); i++) {
if (name == function->Name())
return function;
}
return NULL;
}
private:
typedef BObjectList<Function> FunctionList;
private:
static int _CompareFunctions(const Function* a, const Function* b)
{
SourceLocation locationA = a->GetSourceLocation();
SourceLocation locationB = b->GetSourceLocation();
if (locationA < locationB)
return -1;
if (locationA != locationB )
return 1;
return a->Name().Compare(b->Name());
}
static int _CompareLocationFunction(const SourceLocation* location,
const Function* function)
{
SourceLocation functionLocation = function->GetSourceLocation();
if (*location < functionLocation)
return -1;
return *location == functionLocation ? 0 : 1;
}
private:
LocatableFile* fSourceFile;
FileSourceCode* fSourceCode;
FunctionList fFunctions;
public:
SourceFileEntry* fNext;
};
struct TeamDebugInfo::SourceFileHashDefinition {
typedef const LocatableFile* KeyType;
typedef SourceFileEntry ValueType;
size_t HashKey(const LocatableFile* key) const
{
return (size_t)(addr_t)key;
}
size_t Hash(const SourceFileEntry* value) const
{
return HashKey(value->SourceFile());
}
bool Compare(const LocatableFile* key, const SourceFileEntry* value) const
{
return key == value->SourceFile();
}
SourceFileEntry*& GetLink(SourceFileEntry* value) const
{
return value->fNext;
}
};
TeamDebugInfo::TeamDebugInfo(DebuggerInterface* debuggerInterface,
Architecture* architecture, FileManager* fileManager)
:
fLock("team debug info"),
fDebuggerInterface(debuggerInterface),
fArchitecture(architecture),
fFileManager(fileManager),
fSpecificInfos(10),
fFunctions(NULL),
fSourceFiles(NULL),
fTypeCache(NULL),
fMainFunction(NULL)
{
fDebuggerInterface->AcquireReference();
}
TeamDebugInfo::~TeamDebugInfo()
{
if (fTypeCache != NULL)
fTypeCache->ReleaseReference();
if (fSourceFiles != NULL) {
SourceFileEntry* entry = fSourceFiles->Clear(true);
while (entry != NULL) {
SourceFileEntry* next = entry->fNext;
delete entry;
entry = next;
}
delete fSourceFiles;
}
if (fFunctions != NULL) {
Function* function = fFunctions->Clear(true);
while (function != NULL) {
Function* next = function->fNext;
function->ReleaseReference();
function = next;
}
delete fFunctions;
}
fDebuggerInterface->ReleaseReference();
}
status_t
TeamDebugInfo::Init()
{
status_t error = fLock.InitCheck();
if (error != B_OK)
return error;
fFunctions = new(std::nothrow) FunctionTable;
if (fFunctions == NULL)
return B_NO_MEMORY;
error = fFunctions->Init();
if (error != B_OK)
return error;
fSourceFiles = new(std::nothrow) SourceFileTable;
if (fSourceFiles == NULL)
return B_NO_MEMORY;
error = fSourceFiles->Init();
if (error != B_OK)
return error;
fTypeCache = new(std::nothrow) GlobalTypeCache;
if (fTypeCache == NULL)
return B_NO_MEMORY;
error = fTypeCache->Init();
if (error != B_OK)
return error;
DwarfTeamDebugInfo* dwarfInfo = new(std::nothrow) DwarfTeamDebugInfo(
fArchitecture, fDebuggerInterface, fFileManager, this, this,
fTypeCache);
if (dwarfInfo == NULL || !fSpecificInfos.AddItem(dwarfInfo)) {
delete dwarfInfo;
return B_NO_MEMORY;
}
error = dwarfInfo->Init();
if (error != B_OK)
return error;
DebuggerTeamDebugInfo* debuggerInfo
= new(std::nothrow) DebuggerTeamDebugInfo(fDebuggerInterface,
fArchitecture);
if (debuggerInfo == NULL || !fSpecificInfos.AddItem(debuggerInfo)) {
delete debuggerInfo;
return B_NO_MEMORY;
}
error = debuggerInfo->Init();
if (error != B_OK)
return error;
return B_OK;
}
status_t
TeamDebugInfo::LookupTypeByName(const BString& name,
const TypeLookupConstraints& constraints, Type*& _type)
{
return GetType(fTypeCache, name, constraints, _type);
}
bool
TeamDebugInfo::TypeExistsByName(const BString& name,
const TypeLookupConstraints& constraints)
{
return HasType(fTypeCache, name, constraints);
}
status_t
TeamDebugInfo::GetType(GlobalTypeCache* cache, const BString& name,
const TypeLookupConstraints& constraints, Type*& _type)
{
AutoLocker<GlobalTypeCache> cacheLocker(cache);
Type* type = cache->GetType(name, constraints);
if (type != NULL) {
type->AcquireReference();
_type = type;
return B_OK;
}
cacheLocker.Unlock();
AutoLocker<BLocker> locker(fLock);
ImageList images;
for (int32 i = 0; ImageDebugInfo* imageDebugInfo = fImages.ItemAt(i); i++) {
if (images.AddItem(imageDebugInfo))
imageDebugInfo->AcquireReference();
}
locker.Unlock();
status_t error = B_ENTRY_NOT_FOUND;
for (int32 i = 0; ImageDebugInfo* imageDebugInfo = images.ItemAt(i); i++) {
error = imageDebugInfo->GetType(cache, name, constraints, type);
if (error == B_OK) {
_type = type;
break;
}
}
for (int32 i = 0; ImageDebugInfo* imageDebugInfo = images.ItemAt(i); i++)
imageDebugInfo->ReleaseReference();
return error;
}
bool
TeamDebugInfo::HasType(GlobalTypeCache* cache, const BString& name,
const TypeLookupConstraints& constraints)
{
AutoLocker<GlobalTypeCache> cacheLocker(cache);
Type* type = cache->GetType(name, constraints);
if (type != NULL)
return true;
cacheLocker.Unlock();
AutoLocker<BLocker> locker(fLock);
ImageList images;
for (int32 i = 0; ImageDebugInfo* imageDebugInfo = fImages.ItemAt(i); i++) {
if (images.AddItem(imageDebugInfo))
imageDebugInfo->AcquireReference();
}
locker.Unlock();
bool found = false;
for (int32 i = 0; ImageDebugInfo* imageDebugInfo = images.ItemAt(i); i++) {
if (imageDebugInfo->HasType(name, constraints)) {
found = true;
break;
}
}
for (int32 i = 0; ImageDebugInfo* imageDebugInfo = images.ItemAt(i); i++)
imageDebugInfo->ReleaseReference();
return found;
}
status_t
TeamDebugInfo::GetActiveSourceCode(FunctionDebugInfo* info, SourceCode*& _code)
{
AutoLocker<BLocker> locker(fLock);
LocatableFile* file = info->SourceFile();
if (file != NULL) {
Function* function = FunctionAtSourceLocation(file,
info->SourceStartLocation());
if (function != NULL) {
function_source_state state = function->SourceCodeState();
if (function->SourceCodeState() == FUNCTION_SOURCE_LOADED) {
_code = function->GetSourceCode();
_code->AcquireReference();
return B_OK;
} else if (state == FUNCTION_SOURCE_NOT_LOADED) {
SourceFileEntry* entry = fSourceFiles->Lookup(file);
if (entry != NULL) {
FileSourceCode* sourceCode = entry->GetSourceCode();
if (sourceCode != NULL) {
function->SetSourceCode(sourceCode,
FUNCTION_SOURCE_LOADED);
_code = sourceCode;
_code->AcquireReference();
return B_OK;
}
}
}
}
}
for (int32 i = 0; i < fImages.CountItems(); i++) {
ImageDebugInfo* imageInfo = fImages.ItemAt(i);
FunctionInstance* instance = imageInfo->FunctionAtAddress(
info->Address());
if (instance != NULL && instance->SourceCodeState()
== FUNCTION_SOURCE_LOADED) {
_code = instance->GetSourceCode();
_code->AcquireReference();
return B_OK;
}
}
return B_ENTRY_NOT_FOUND;
}
status_t
TeamDebugInfo::LoadImageDebugInfo(const ImageInfo& imageInfo,
LocatableFile* imageFile, ImageDebugInfoLoadingState& _state,
ImageDebugInfo*& _imageDebugInfo)
{
ImageDebugInfo* imageDebugInfo = new(std::nothrow) ImageDebugInfo(
imageInfo);
if (imageDebugInfo == NULL)
return B_NO_MEMORY;
BReference<ImageDebugInfo> imageDebugInfoReference(imageDebugInfo, true);
for (int32 i = 0; SpecificTeamDebugInfo* specificTeamInfo
= fSpecificInfos.ItemAt(i); i++) {
SpecificImageDebugInfo* specificImageInfo;
status_t error = specificTeamInfo->CreateImageDebugInfo(imageInfo,
imageFile, _state, specificImageInfo);
if (error == B_OK) {
if (!imageDebugInfo->AddSpecificInfo(specificImageInfo)) {
delete specificImageInfo;
return B_NO_MEMORY;
}
} else if (_state.UserInputRequired()) {
_state.SetSpecificInfoIndex(i);
return error;
} else if (error == B_NO_MEMORY)
return error;
_state.ClearSpecificDebugInfoLoadingState();
}
status_t error = imageDebugInfo->FinishInit(fDebuggerInterface);
if (error != B_OK)
return error;
if (fMainFunction == NULL) {
FunctionInstance* instance = imageDebugInfo->MainFunction();
if (instance != NULL)
fMainFunction = instance;
}
_imageDebugInfo = imageDebugInfoReference.Detach();
return B_OK;
}
status_t
TeamDebugInfo::LoadSourceCode(LocatableFile* file, FileSourceCode*& _sourceCode)
{
AutoLocker<BLocker> locker(fLock);
SourceFileEntry* entry = fSourceFiles->Lookup(file);
if (entry == NULL)
return B_ENTRY_NOT_FOUND;
FileSourceCode* sourceCode = entry->GetSourceCode();
if (sourceCode != NULL) {
sourceCode->AcquireReference();
_sourceCode = sourceCode;
return B_OK;
}
Function* function = entry->FunctionAt(0);
if (function == NULL)
return B_ENTRY_NOT_FOUND;
FunctionDebugInfo* functionDebugInfo
= function->FirstInstance()->GetFunctionDebugInfo();
SourceLanguage* language;
status_t error = functionDebugInfo->GetSpecificImageDebugInfo()
->GetSourceLanguage(functionDebugInfo, language);
if (error != B_OK)
return error;
BReference<SourceLanguage> languageReference(language, true);
SourceFile* sourceFile;
error = fFileManager->LoadSourceFile(file, sourceFile);
if (error != B_OK)
return error;
sourceCode = new(std::nothrow) FileSourceCode(file, sourceFile, language);
sourceFile->ReleaseReference();
if (sourceCode == NULL)
return B_NO_MEMORY;
BReference<FileSourceCode> sourceCodeReference(sourceCode, true);
error = sourceCode->Init();
if (error != B_OK)
return error;
bool anyInfo = false;
for (int32 i = 0; ImageDebugInfo* imageDebugInfo = fImages.ItemAt(i); i++)
anyInfo |= imageDebugInfo->AddSourceCodeInfo(file, sourceCode) == B_OK;
if (!anyInfo)
return B_ENTRY_NOT_FOUND;
entry->SetSourceCode(sourceCode);
_sourceCode = sourceCodeReference.Detach();
return B_OK;
}
void
TeamDebugInfo::ClearSourceCode(LocatableFile* sourceFile)
{
AutoLocker<BLocker> locker(fLock);
SourceFileEntry* entry = fSourceFiles->Lookup(sourceFile);
if (entry != NULL)
entry->SetSourceCode(NULL);
}
status_t
TeamDebugInfo::DisassembleFunction(FunctionInstance* functionInstance,
DisassembledCode*& _sourceCode)
{
static const target_size_t kMaxBufferSize = 64 * 1024;
target_size_t bufferSize = std::min(functionInstance->Size(),
kMaxBufferSize);
void* buffer = malloc(bufferSize);
if (buffer == NULL)
return B_NO_MEMORY;
MemoryDeleter bufferDeleter(buffer);
FunctionDebugInfo* functionDebugInfo
= functionInstance->GetFunctionDebugInfo();
ssize_t bytesRead = functionDebugInfo->GetSpecificImageDebugInfo()
->ReadCode(functionInstance->Address(), buffer, bufferSize);
if (bytesRead < 0)
return bytesRead;
return fArchitecture->DisassembleCode(functionDebugInfo, buffer, bytesRead,
_sourceCode);
}
status_t
TeamDebugInfo::AddImageDebugInfo(ImageDebugInfo* imageDebugInfo)
{
AutoLocker<BLocker> locker(fLock);
if (!fImages.AddItem(imageDebugInfo))
return B_NO_MEMORY;
BObjectList<SourceFileEntry> sourceFileEntries;
for (int32 i = 0;
FunctionInstance* instance = imageDebugInfo->FunctionAt(i); i++) {
Function* function = fFunctions->Lookup(instance);
if (function != NULL) {
function->AddInstance(instance);
instance->SetFunction(function);
if (LocatableFile* sourceFile = function->SourceFile()) {
SourceFileEntry* entry = fSourceFiles->Lookup(sourceFile);
if (entry != NULL && entry->GetSourceCode() != NULL)
sourceFileEntries.AddItem(entry);
}
} else {
function = new(std::nothrow) Function;
if (function == NULL) {
RemoveImageDebugInfo(imageDebugInfo);
return B_NO_MEMORY;
}
function->AddInstance(instance);
instance->SetFunction(function);
status_t error = _AddFunction(function);
if (error != B_OK) {
function->RemoveInstance(instance);
instance->SetFunction(NULL);
RemoveImageDebugInfo(imageDebugInfo);
return error;
}
}
}
for (int32 i = 0; SourceFileEntry* entry = sourceFileEntries.ItemAt(i);
i++) {
FileSourceCode* sourceCode = entry->GetSourceCode();
sourceCode->Lock();
if (imageDebugInfo->AddSourceCodeInfo(entry->SourceFile(),
sourceCode) == B_OK) {
}
sourceCode->Unlock();
}
return B_OK;
}
void
TeamDebugInfo::RemoveImageDebugInfo(ImageDebugInfo* imageDebugInfo)
{
AutoLocker<BLocker> locker(fLock);
for (int32 i = 0;
FunctionInstance* instance = imageDebugInfo->FunctionAt(i); i++) {
if (Function* function = instance->GetFunction()) {
if (function->FirstInstance() == function->LastInstance()) {
_RemoveFunction(function);
function->ReleaseReference();
}
function->RemoveInstance(instance);
instance->SetFunction(NULL);
}
}
fTypeCache->RemoveTypes(imageDebugInfo->GetImageInfo().ImageID());
fImages.RemoveItem(imageDebugInfo);
}
ImageDebugInfo*
TeamDebugInfo::ImageDebugInfoByName(const char* name) const
{
for (int32 i = 0; ImageDebugInfo* imageDebugInfo = fImages.ItemAt(i); i++) {
if (imageDebugInfo->GetImageInfo().Name() == name)
return imageDebugInfo;
}
return NULL;
}
Function*
TeamDebugInfo::FunctionAtSourceLocation(LocatableFile* file,
const SourceLocation& location) const
{
if (SourceFileEntry* entry = fSourceFiles->Lookup(file))
return entry->FunctionAtLocation(location);
return NULL;
}
Function*
TeamDebugInfo::FunctionByID(FunctionID* functionID) const
{
if (SourceFunctionID* sourceFunctionID
= dynamic_cast<SourceFunctionID*>(functionID)) {
LocatableFile* file = fFileManager->GetSourceFile(
sourceFunctionID->SourceFilePath());
if (file == NULL)
return NULL;
BReference<LocatableFile> fileReference(file, true);
if (SourceFileEntry* entry = fSourceFiles->Lookup(file))
return entry->FunctionByName(functionID->FunctionName());
return NULL;
}
ImageFunctionID* imageFunctionID
= dynamic_cast<ImageFunctionID*>(functionID);
if (imageFunctionID == NULL)
return NULL;
ImageDebugInfo* imageDebugInfo
= ImageDebugInfoByName(imageFunctionID->ImageName());
if (imageDebugInfo == NULL)
return NULL;
FunctionInstance* functionInstance = imageDebugInfo->FunctionByName(
functionID->FunctionName());
return functionInstance != NULL ? functionInstance->GetFunction() : NULL;
}
status_t
TeamDebugInfo::_AddFunction(Function* function)
{
if (LocatableFile* sourceFile = function->SourceFile()) {
SourceFileEntry* entry = fSourceFiles->Lookup(sourceFile);
if (entry == NULL) {
entry = new(std::nothrow) SourceFileEntry(sourceFile);
if (entry == NULL)
return B_NO_MEMORY;
status_t error = entry->Init();
if (error != B_OK) {
delete entry;
return error;
}
fSourceFiles->Insert(entry);
}
status_t error = entry->AddFunction(function);
if (error != B_OK) {
if (entry->IsUnused()) {
fSourceFiles->Remove(entry);
delete entry;
}
return error;
}
}
fFunctions->Insert(function);
return B_OK;
}
void
TeamDebugInfo::_RemoveFunction(Function* function)
{
fFunctions->Remove(function);
if (LocatableFile* sourceFile = function->SourceFile()) {
if (SourceFileEntry* entry = fSourceFiles->Lookup(sourceFile))
entry->RemoveFunction(function);
}
}