root/src/kits/debugger/debug_info/TeamDebugInfo.cpp
/*
 * 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"


// #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 = 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
        {
                // 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),
        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);
        }
}