/*
* 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 "StringUtils.h"
#include "Type.h"
#include "TypeLookupConstraints.h"
// #pragma mark - FunctionHashDefinition
struct TeamDebugInfo::FunctionHashDefinition {
typedef const FunctionInstance* KeyType;
typedef Function ValueType;
size_t HashKey(const FunctionInstance* key) const
{
// Instances without source file only equal themselves.
if (key->SourceFile() == NULL)
return (uint32)(addr_t)key;
uint32 hash = StringUtils::HashValue(key->Name());
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
{
// source file must be the same
if (key->SourceFile() != value->SourceFile())
return false;
// Instances without source file only equal themselves.
if (key->SourceFile() == NULL)
return key == value->FirstInstance();
// Source location and function name must also match.
return key->GetSourceLocation() == value->GetSourceLocation()
&& key->Name() == value->Name();
}
Function*& GetLink(Function* value) const
{
return value->fNext;
}
};
// #pragma mark - SourceFileEntry
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);
// No exact match, so we return the previous function which might still
// contain the location.
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
{
// TODO: That's not exactly optimal.
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;
// if the locations match we still need to compare by name to be
// certain, since differently typed instantiations of template
// functions will have the same source file and location
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;
};
// #pragma mark - SourceFileHashDefinition
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;
}
};
// #pragma mark - TeamDebugInfo
TeamDebugInfo::TeamDebugInfo(DebuggerInterface* debuggerInterface,
Architecture* architecture, FileManager* fileManager)
:
fLock("team debug info"),
fDebuggerInterface(debuggerInterface),
fArchitecture(architecture),
fFileManager(fileManager),
fSpecificInfos(10, true),
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()
{
// check the lock
status_t error = fLock.InitCheck();
if (error != B_OK)
return error;
// create function hash table
fFunctions = new(std::nothrow) FunctionTable;
if (fFunctions == NULL)
return B_NO_MEMORY;
error = fFunctions->Init();
if (error != B_OK)
return error;
// create source file hash table
fSourceFiles = new(std::nothrow) SourceFileTable;
if (fSourceFiles == NULL)
return B_NO_MEMORY;
error = fSourceFiles->Init();
if (error != B_OK)
return error;
// create a type cache
fTypeCache = new(std::nothrow) GlobalTypeCache;
if (fTypeCache == NULL)
return B_NO_MEMORY;
error = fTypeCache->Init();
if (error != B_OK)
return error;
// Create specific infos for all types of debug info we support, in
// descending order of expressiveness.
// DWARF
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;
// debugger based info
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)
{
// maybe the type is already cached
AutoLocker<GlobalTypeCache> cacheLocker(cache);
Type* type = cache->GetType(name, constraints);
if (type != NULL) {
type->AcquireReference();
_type = type;
return B_OK;
}
cacheLocker.Unlock();
// Clone the image list and get references to the images, so we can iterate
// through them without locking.
AutoLocker<BLocker> locker(fLock);
ImageList images;
for (int32 i = 0; ImageDebugInfo* imageDebugInfo = fImages.ItemAt(i); i++) {
if (images.AddItem(imageDebugInfo))
imageDebugInfo->AcquireReference();
}
locker.Unlock();
// get the type
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;
}
}
// release the references
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)
{
// maybe the type is already cached
AutoLocker<GlobalTypeCache> cacheLocker(cache);
Type* type = cache->GetType(name, constraints);
if (type != NULL)
return true;
cacheLocker.Unlock();
// Clone the image list and get references to the images, so we can iterate
// through them without locking.
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;
}
}
// release the references
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) {
// if the function's source state is not loaded, check
// if we already know the file anyways. Currently, when
// a source code job runs, it does so on behalf of a specific
// function, and consequently only sets the loaded source code
// on that particular function at that point in time, rather
// than all others sharing that same file. Consequently,
// set it lazily here.
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;
// fail only when out of memory
_state.ClearSpecificDebugInfoLoadingState();
// if we made it this far, then we're done with current specific
// info, and its corresponding state object, if any, is no longer
// needed
}
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);
// If we don't know the source file, there's nothing we can do.
SourceFileEntry* entry = fSourceFiles->Lookup(file);
if (entry == NULL)
return B_ENTRY_NOT_FOUND;
// the source might already be loaded
FileSourceCode* sourceCode = entry->GetSourceCode();
if (sourceCode != NULL) {
sourceCode->AcquireReference();
_sourceCode = sourceCode;
return B_OK;
}
// get the source language from some function's image debug info
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);
// no source code yet
// locker.Unlock();
// TODO: It would be nice to unlock here, but we need to iterate through
// the images below. We could clone the list, acquire references, and
// unlock. Then we have to compare the list with the then current list when
// we're done loading.
// load the source file
SourceFile* sourceFile;
error = fFileManager->LoadSourceFile(file, sourceFile);
if (error != B_OK)
return error;
// create the source code
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;
// Iterate through all images that know the source file and ask them to add
// information.
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)
{
// allocate a buffer for the function code
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);
// read the function code
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);
// We have both locks now, so that for read-only access either lock
// suffices.
if (!fImages.AddItem(imageDebugInfo))
return B_NO_MEMORY;
// Match all of the image debug info's functions instances with functions.
BObjectList<SourceFileEntry> sourceFileEntries;
for (int32 i = 0;
FunctionInstance* instance = imageDebugInfo->FunctionAt(i); i++) {
// lookup the function or create it, if it doesn't exist yet
Function* function = fFunctions->Lookup(instance);
if (function != NULL) {
// TODO: Also update possible user breakpoints in this function!
function->AddInstance(instance);
instance->SetFunction(function);
// The new image debug info might have additional information about
// the source file of the function, so remember the source file
// entry.
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);
// Insert after adding the instance. Otherwise the function
// wouldn't be hashable/comparable.
if (error != B_OK) {
function->RemoveInstance(instance);
instance->SetFunction(NULL);
RemoveImageDebugInfo(imageDebugInfo);
return error;
}
}
}
// update the source files the image debug info knows about
for (int32 i = 0; SourceFileEntry* entry = sourceFileEntries.ItemAt(i);
i++) {
FileSourceCode* sourceCode = entry->GetSourceCode();
sourceCode->Lock();
if (imageDebugInfo->AddSourceCodeInfo(entry->SourceFile(),
sourceCode) == B_OK) {
// TODO: Notify interesting parties! Iterate through all functions
// for this source file?
}
sourceCode->Unlock();
}
return B_OK;
}
void
TeamDebugInfo::RemoveImageDebugInfo(ImageDebugInfo* imageDebugInfo)
{
AutoLocker<BLocker> locker(fLock);
// We have both locks now, so that for read-only access either lock
// suffices.
// Remove the functions from all of the image debug info's functions
// instances.
for (int32 i = 0;
FunctionInstance* instance = imageDebugInfo->FunctionAt(i); i++) {
if (Function* function = instance->GetFunction()) {
// TODO: Also update possible user breakpoints in this function!
if (function->FirstInstance() == function->LastInstance()) {
// function unused -- remove it
// Note, that we have to remove it from the hash before removing
// the instance, since otherwise the function cannot be compared
// anymore.
_RemoveFunction(function);
function->ReleaseReference();
// The instance still has a reference.
}
function->RemoveInstance(instance);
instance->SetFunction(NULL);
// If this was the last instance, it will remove the last
// reference to the function.
}
}
// remove cached types from that image
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)) {
// get the source file
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 the function refers to a source file, add it to the respective entry.
if (LocatableFile* sourceFile = function->SourceFile()) {
SourceFileEntry* entry = fSourceFiles->Lookup(sourceFile);
if (entry == NULL) {
// no entry for the source file yet -- create on
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);
}
// add the function
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 the function refers to a source file, remove it from the respective
// entry.
if (LocatableFile* sourceFile = function->SourceFile()) {
if (SourceFileEntry* entry = fSourceFiles->Lookup(sourceFile))
entry->RemoveFunction(function);
}
}
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fNext.