root/src/system/kernel/debug/core_dump.cpp
/*
 * Copyright 2016, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Distributed under the terms of the MIT License.
 */


#include <core_dump.h>

#include <errno.h>
#include <string.h>

#include <algorithm>
#include <new>

#include <BeBuild.h>
#include <ByteOrder.h>

#include <AutoDeleter.h>

#include <commpage.h>
#include <condition_variable.h>
#include <elf.h>
#include <kimage.h>
#include <ksignal.h>
#include <team.h>
#include <thread.h>
#include <user_debugger.h>
#include <util/AutoLock.h>
#include <util/ThreadAutoLock.h>
#include <util/DoublyLinkedList.h>
#include <vm/vm.h>
#include <vm/VMArea.h>
#include <vm/VMCache.h>

#include "../cache/vnode_store.h"
#include "../vm/VMAddressSpaceLocking.h"


//#define TRACE_CORE_DUMP
#ifdef TRACE_CORE_DUMP
#       define TRACE(...) dprintf(__VA_ARGS__)
#else
#       define TRACE(...) do {} while (false)
#endif


namespace {


static const size_t kBufferSize = 1024 * 1024;
static const char* const kCoreNote = ELF_NOTE_CORE;
static const char* const kHaikuNote = ELF_NOTE_HAIKU;


struct Allocator {
        Allocator()
                :
                fAligned(NULL),
                fStrings(NULL),
                fAlignedCapacity(0),
                fStringCapacity(0),
                fAlignedSize(0),
                fStringSize(0)
        {
        }

        ~Allocator()
        {
                free(fAligned);
        }

        bool HasMissingAllocations() const
        {
                return fAlignedSize > fAlignedCapacity || fStringSize > fStringCapacity;
        }

        bool Reallocate()
        {
                free(fAligned);

                fAlignedCapacity = fAlignedSize;
                fStringCapacity = fStringSize;
                fAlignedSize = 0;
                fStringSize = 0;

                fAligned = (uint8*)malloc(fAlignedCapacity + fStringCapacity);
                if (fAligned == NULL)
                        return false;
                fStrings = (char*)(fAligned + fAlignedCapacity);

                return true;
        }

        void* AllocateAligned(size_t size)
        {
                size_t offset = fAlignedSize;
                fAlignedSize += (size + 7) / 8 * 8;
                if (fAlignedSize <= fAlignedCapacity)
                        return fAligned + offset;
                return NULL;
        }

        char* AllocateString(size_t length)
        {
                size_t offset = fStringSize;
                fStringSize += length + 1;
                if (fStringSize <= fStringCapacity)
                        return fStrings + offset;
                return NULL;
        }

        template <typename Type>
        Type* New()
        {
                void* buffer = AllocateAligned(sizeof(Type));
                if (buffer == NULL)
                        return NULL;
                return new(buffer) Type;
        }

        char* DuplicateString(const char* string)
        {
                if (string == NULL)
                        return NULL;
                char* newString = AllocateString(strlen(string));
                if (newString != NULL)
                        strcpy(newString, string);
                return newString;
        }

private:
        uint8*  fAligned;
        char*   fStrings;
        size_t  fAlignedCapacity;
        size_t  fStringCapacity;
        size_t  fAlignedSize;
        size_t  fStringSize;
};


struct TeamInfo : team_info {
};


struct ThreadState : DoublyLinkedListLinkImpl<ThreadState> {
        ThreadState()
                :
                fThread(NULL),
                fComplete(false)
        {
        }

        ~ThreadState()
        {
                SetThread(NULL);
        }

        static ThreadState* Create()
        {
                ThreadState* state = new(std::nothrow) ThreadState;
                if (state == NULL)
                        return NULL;
                return state;
        }

        Thread* GetThread() const
        {
                return fThread;
        }

        void SetThread(Thread* thread)
        {
                if (fThread != NULL)
                        fThread->ReleaseReference();

                fThread = thread;

                if (fThread != NULL)
                        fThread->AcquireReference();
        }

        /*!     Invoke with thread lock and scheduler lock being held. */
        void GetState()
        {
                fState = fThread->state;
                fPriority = fThread->priority;
                fStackBase = fThread->user_stack_base;
                fStackEnd = fStackBase + fThread->user_stack_size;
                strlcpy(fName, fThread->name, sizeof(fName));
                if (arch_get_thread_debug_cpu_state(fThread, &fCpuState) != B_OK)
                        memset(&fCpuState, 0, sizeof(fCpuState));
        }

        bool IsComplete() const
        {
                return fComplete;
        }

        void SetComplete(bool complete)
        {
                fComplete = complete;
        }

        int32 State() const
        {
                return fState;
        }

        int32 Priority() const
        {
                return fPriority;
        }

        addr_t StackBase() const
        {
                return fStackBase;
        }

        addr_t StackEnd() const
        {
                return fStackEnd;
        }

        const char* Name() const
        {
                return fName;
        }

        const debug_cpu_state* CpuState() const
        {
                return &fCpuState;
        }

private:
        Thread*                 fThread;
        int32                   fState;
        int32                   fPriority;
        addr_t                  fStackBase;
        addr_t                  fStackEnd;
        char                    fName[B_OS_NAME_LENGTH];
        debug_cpu_state fCpuState;
        bool                    fComplete;
};


typedef DoublyLinkedList<ThreadState> ThreadStateList;


struct ImageInfo : DoublyLinkedListLinkImpl<ImageInfo> {
        ImageInfo(struct image* image)
                :
                fId(image->info.basic_info.id),
                fType(image->info.basic_info.type),
                fDeviceId(image->info.basic_info.device),
                fNodeId(image->info.basic_info.node),
                fName(strdup(image->info.basic_info.name)),
                fInitRoutine((addr_t)image->info.basic_info.init_routine),
                fTermRoutine((addr_t)image->info.basic_info.term_routine),
                fText((addr_t)image->info.basic_info.text),
                fData((addr_t)image->info.basic_info.data),
                fTextSize(image->info.basic_info.text_size),
                fDataSize(image->info.basic_info.data_size),
                fTextDelta(image->info.text_delta),
                fSymbolTable((addr_t)image->info.symbol_table),
                fSymbolHash((addr_t)image->info.symbol_hash),
                fStringTable((addr_t)image->info.string_table),
                fSymbolTableData(NULL),
                fStringTableData(NULL),
                fSymbolCount(0),
                fStringTableSize(0)
        {
                if (fName != NULL && strcmp(fName, "commpage") == 0)
                        _GetCommpageSymbols();
        }

        ~ImageInfo()
        {
                free(fName);
                _FreeSymbolData();
        }

        static ImageInfo* Create(struct image* image)
        {
                ImageInfo* imageInfo = new(std::nothrow) ImageInfo(image);
                if (imageInfo == NULL || imageInfo->fName == NULL) {
                        delete imageInfo;
                        return NULL;
                }

                return imageInfo;
        }

        image_id Id() const
        {
                return fId;
        }

        image_type Type() const
        {
                return fType;
        }

        const char* Name() const
        {
                return fName;
        }

        dev_t DeviceId() const
        {
                return fDeviceId;
        }

        ino_t NodeId() const
        {
                return fNodeId;
        }

        addr_t InitRoutine() const
        {
                return fInitRoutine;
        }

        addr_t TermRoutine() const
        {
                return fTermRoutine;
        }

        addr_t TextBase() const
        {
                return fText;
        }

        size_t TextSize() const
        {
                return fTextSize;
        }

        ssize_t TextDelta() const
        {
                return fTextDelta;
        }

        addr_t DataBase() const
        {
                return fData;
        }

        size_t DataSize() const
        {
                return fDataSize;
        }

        addr_t SymbolTable() const
        {
                return fSymbolTable;
        }

        addr_t SymbolHash() const
        {
                return fSymbolHash;
        }

        addr_t StringTable() const
        {
                return fStringTable;
        }

        elf_sym* SymbolTableData() const
        {
                return fSymbolTableData;
        }

        char* StringTableData() const
        {
                return fStringTableData;
        }

        uint32 SymbolCount() const
        {
                return fSymbolCount;
        }

        size_t StringTableSize() const
        {
                return fStringTableSize;
        }

private:
        void _GetCommpageSymbols()
        {
                image_id commpageId = get_commpage_image();

                // get the size of the tables
                int32 symbolCount = 0;
                size_t stringTableSize = 0;
                status_t error = elf_read_kernel_image_symbols(commpageId, NULL,
                        &symbolCount, NULL, &stringTableSize,
                        NULL, true);
                if (error != B_OK)
                        return;
                if (symbolCount == 0 || stringTableSize == 0)
                        return;

                // allocate the tables
                fSymbolTableData = (elf_sym*)malloc(sizeof(elf_sym) * symbolCount);
                fStringTableData = (char*)malloc(stringTableSize);
                if (fSymbolTableData == NULL || fStringTableData == NULL) {
                        _FreeSymbolData();
                        return;
                }

                fSymbolCount = symbolCount;
                fStringTableSize = stringTableSize;

                // get the data
                error = elf_read_kernel_image_symbols(commpageId,
                        fSymbolTableData, &symbolCount, fStringTableData, &stringTableSize,
                        NULL, true);
                if (error != B_OK)
                        _FreeSymbolData();
        }

        void _FreeSymbolData()
        {
                free(fSymbolTableData);
                free(fStringTableData);

                fSymbolTableData = NULL;
                fStringTableData = NULL;
                fSymbolCount = 0;
                fStringTableSize = 0;
        }

private:
        image_id        fId;
        image_type      fType;
        dev_t           fDeviceId;
        ino_t           fNodeId;
        char*           fName;
        addr_t          fInitRoutine;
        addr_t          fTermRoutine;
        addr_t          fText;
        addr_t          fData;
        size_t          fTextSize;
        size_t          fDataSize;
        ssize_t         fTextDelta;
        addr_t          fSymbolTable;
        addr_t          fSymbolHash;
        addr_t          fStringTable;
        // for commpage image
        elf_sym*        fSymbolTableData;
        char*           fStringTableData;
        uint32          fSymbolCount;
        size_t          fStringTableSize;
};


typedef DoublyLinkedList<ImageInfo> ImageInfoList;


struct AreaInfo : DoublyLinkedListLinkImpl<AreaInfo> {
        static AreaInfo* Create(Allocator& allocator, VMArea* area, size_t ramSize,
                dev_t deviceId, ino_t nodeId)
        {
                AreaInfo* areaInfo = allocator.New<AreaInfo>();
                const char* name = allocator.DuplicateString(area->name);

                if (areaInfo != NULL) {
                        areaInfo->fId = area->id;
                        areaInfo->fName = name;
                        areaInfo->fBase = area->Base();
                        areaInfo->fSize = area->Size();
                        areaInfo->fLock = B_FULL_LOCK;
                        areaInfo->fProtection = area->protection;
                        areaInfo->fRamSize = ramSize;
                        areaInfo->fDeviceId = deviceId;
                        areaInfo->fNodeId = nodeId;
                        areaInfo->fCacheOffset = area->cache_offset;
                        areaInfo->fImageInfo = NULL;
                }

                return areaInfo;
        }

        area_id Id() const
        {
                return fId;
        }

        const char* Name() const
        {
                return fName;
        }

        addr_t Base() const
        {
                return fBase;
        }

        size_t Size() const
        {
                return fSize;
        }

        uint32 Lock() const
        {
                return fLock;
        }

        uint32 Protection() const
        {
                return fProtection;
        }

        size_t RamSize() const
        {
                return fRamSize;
        }

        off_t CacheOffset() const
        {
                return fCacheOffset;
        }

        dev_t DeviceId() const
        {
                return fDeviceId;
        }

        ino_t NodeId() const
        {
                return fNodeId;
        }

        ImageInfo* GetImageInfo() const
        {
                return fImageInfo;
        }

        void SetImageInfo(ImageInfo* imageInfo)
        {
                fImageInfo = imageInfo;
        }

private:
        area_id         fId;
        const char*     fName;
        addr_t          fBase;
        size_t          fSize;
        uint32          fLock;
        uint32          fProtection;
        size_t          fRamSize;
        dev_t           fDeviceId;
        ino_t           fNodeId;
        off_t           fCacheOffset;
        ImageInfo*      fImageInfo;
};


typedef DoublyLinkedList<AreaInfo> AreaInfoList;


struct BufferedFile {
        BufferedFile()
                :
                fFd(-1),
                fBuffer(NULL),
                fCapacity(0),
                fOffset(0),
                fBuffered(0),
                fStatus(B_NO_INIT)
        {
        }

        ~BufferedFile()
        {
                if (fFd >= 0)
                        close(fFd);

                free(fBuffer);
        }

        status_t Init(const char* path)
        {
                fCapacity = kBufferSize;
                fBuffer = (uint8*)malloc(fCapacity);
                if (fBuffer == NULL)
                        return B_NO_MEMORY;

                fFd = open(path, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR);
                if (fFd < 0)
                        return errno;

                fStatus = B_OK;
                return B_OK;
        }

        status_t Status() const
        {
                return fStatus;
        }

        off_t EndOffset() const
        {
                return fOffset + (off_t)fBuffered;
        }

        status_t Flush()
        {
                if (fStatus != B_OK)
                        return fStatus;

                if (fBuffered == 0)
                        return B_OK;

                ssize_t written = pwrite(fFd, fBuffer, fBuffered, fOffset);
                if (written < 0)
                        return fStatus = errno;
                if ((size_t)written != fBuffered)
                        return fStatus = B_IO_ERROR;

                fOffset += (off_t)fBuffered;
                fBuffered = 0;
                return B_OK;
        }

        status_t Seek(off_t offset)
        {
                if (fStatus != B_OK)
                        return fStatus;

                if (fBuffered == 0) {
                        fOffset = offset;
                } else if (offset != fOffset + (off_t)fBuffered) {
                        status_t error = Flush();
                        if (error != B_OK)
                                return fStatus = error;
                        fOffset = offset;
                }

                return B_OK;
        }

        status_t Write(const void* data, size_t size)
        {
                if (fStatus != B_OK)
                        return fStatus;

                if (size == 0)
                        return B_OK;

                while (size > 0) {
                        size_t toWrite = std::min(size, fCapacity - fBuffered);
                        if (toWrite == 0) {
                                status_t error = Flush();
                                if (error != B_OK)
                                        return fStatus = error;
                                continue;
                        }

                        memcpy(fBuffer + fBuffered, data, toWrite);
                        fBuffered += toWrite;
                        size -= toWrite;
                }

                return B_OK;
        }

        template<typename Data>
        status_t Write(const Data& data)
        {
                return Write(&data, sizeof(data));
        }

        status_t WriteAt(off_t offset, const void* data, size_t size)
        {
                if (Seek(offset) != B_OK)
                        return fStatus;

                return Write(data, size);
        }

        status_t WriteUserArea(addr_t base, size_t size)
        {
                uint8* data = (uint8*)base;
                size = size / B_PAGE_SIZE * B_PAGE_SIZE;

                // copy the area page-wise into the buffer, flushing when necessary
                while (size > 0) {
                        if (fBuffered + B_PAGE_SIZE > fCapacity) {
                                status_t error = Flush();
                                if (error != B_OK)
                                        return error;
                        }

                        if (user_memcpy(fBuffer + fBuffered, data, B_PAGE_SIZE) != B_OK)
                                memset(fBuffer + fBuffered, 0, B_PAGE_SIZE);

                        fBuffered += B_PAGE_SIZE;
                        data += B_PAGE_SIZE;
                        size -= B_PAGE_SIZE;
                }

                return B_OK;
        }

private:
        int                     fFd;
        uint8*          fBuffer;
        size_t          fCapacity;
        off_t           fOffset;
        size_t          fBuffered;
        status_t        fStatus;
};


struct DummyWriter {
        DummyWriter()
                :
                fWritten(0)
        {
        }

        status_t Status() const
        {
                return B_OK;
        }

        size_t BytesWritten() const
        {
                return fWritten;
        }

        status_t Write(const void* data, size_t size)
        {
                fWritten += size;
                return B_OK;
        }

        template<typename Data>
        status_t Write(const Data& data)
        {
                return Write(&data, sizeof(data));
        }

private:
        size_t  fWritten;
};


struct CoreDumper {
        CoreDumper()
                :
                fCurrentThread(thread_get_current_thread()),
                fTeam(fCurrentThread->team),
                fFile(),
                fThreadCount(0),
                fThreadStates(),
                fPreAllocatedThreadStates(),
                fAreaInfoAllocator(),
                fAreaInfos(),
                fImageInfos(),
                fThreadBlockCondition()
        {
                fThreadBlockCondition.Init(this, "core dump");
        }

        ~CoreDumper()
        {
                while (ThreadState* state = fThreadStates.RemoveHead())
                        delete state;
                while (ThreadState* state = fPreAllocatedThreadStates.RemoveHead())
                        delete state;
                while (ImageInfo* info = fImageInfos.RemoveHead())
                        delete info;
        }

        status_t Dump(const char* path, bool killTeam)
        {
                // gcc thinks fTeam may be null in atomic_or
        // and warn which causes error on some configs
                if (fTeam == NULL)
                        return B_ERROR;

                // the path must be absolute
                if (path[0] != '/')
                        return B_BAD_VALUE;

                AutoLocker<Team> teamLocker(fTeam);

                // indicate that we're dumping core
                if ((atomic_or(&fTeam->flags, TEAM_FLAG_DUMP_CORE)
                                & TEAM_FLAG_DUMP_CORE) != 0) {
                        return B_BUSY;
                }

                fTeam->SetCoreDumpCondition(&fThreadBlockCondition);

                int32 threadCount = _SetThreadsCoreDumpFlag(true);

                teamLocker.Unlock();

                // write the core file
                status_t error = _Dump(path, threadCount);

                // send kill signal, if requested
                if (killTeam)
                        kill_team(fTeam->id);

                // clean up the team state and wake up waiting threads
                teamLocker.Lock();

                fTeam->SetCoreDumpCondition(NULL);

                atomic_and(&fTeam->flags, ~(int32)TEAM_FLAG_DUMP_CORE);

                _SetThreadsCoreDumpFlag(false);

                fThreadBlockCondition.NotifyAll();

                return error;
        }

private:
        status_t _Dump(const char* path, int32 threadCount)
        {
                status_t error = _GetTeamInfo();
                if (error != B_OK)
                        return error;

                // pre-allocate a list of thread states
                if (!_AllocateThreadStates(threadCount))
                        return B_NO_MEMORY;

                // collect the threads states
                _GetThreadStates();

                // collect the other team information
                if (!_GetAreaInfos() || !_GetImageInfos())
                        return B_NO_MEMORY;

                // open the file
                error = fFile.Init(path);
                if (error != B_OK)
                        return error;

                _PrepareCoreFileInfo();

                // write ELF header
                error = _WriteElfHeader();
                if (error != B_OK)
                        return error;

                // write note segment
                error = _WriteNotes();
                if (error != B_OK)
                        return error;

                size_t notesEndOffset = (size_t)fFile.EndOffset();
                fNoteSegmentSize = notesEndOffset - fNoteSegmentOffset;
                fFirstAreaSegmentOffset = (notesEndOffset + B_PAGE_SIZE - 1)
                        / B_PAGE_SIZE * B_PAGE_SIZE;

                error = _WriteProgramHeaders();
                if (error != B_OK)
                        return error;

                // write area segments
                error = _WriteAreaSegments();
                if (error != B_OK)
                        return error;

                return _WriteElfHeader();
        }

        int32 _SetThreadsCoreDumpFlag(bool setFlag)
        {
                int32 count = 0;

                for (Thread* thread = fTeam->thread_list.First(); thread != NULL;
                                thread = fTeam->thread_list.GetNext(thread)) {
                        count++;
                        if (setFlag) {
                                atomic_or(&thread->flags, THREAD_FLAGS_TRAP_FOR_CORE_DUMP);
                        } else {
                                atomic_and(&thread->flags,
                                        ~(int32)THREAD_FLAGS_TRAP_FOR_CORE_DUMP);
                        }
                }

                return count;
        }

        status_t _GetTeamInfo()
        {
                return get_team_info(fTeam->id, &fTeamInfo);
        }

        bool _AllocateThreadStates(int32 count)
        {
                if (!_PreAllocateThreadStates(count))
                        return false;

                TeamLocker teamLocker(fTeam);

                for (;;) {
                        fThreadCount = 0;
                        int32 missing = 0;

                        for (Thread* thread = fTeam->thread_list.First(); thread != NULL;
                                        thread = fTeam->thread_list.GetNext(thread)) {
                                fThreadCount++;
                                ThreadState* state = fPreAllocatedThreadStates.RemoveHead();
                                if (state != NULL) {
                                        state->SetThread(thread);
                                        fThreadStates.Insert(state);
                                } else
                                        missing++;
                        }

                        if (missing == 0)
                                break;

                        teamLocker.Unlock();

                        fPreAllocatedThreadStates.TakeFrom(&fThreadStates);
                        if (!_PreAllocateThreadStates(missing))
                                return false;

                        teamLocker.Lock();
                }

                return true;
        }

        bool _PreAllocateThreadStates(int32 count)
        {
                for (int32 i = 0; i < count; i++) {
                        ThreadState* state = ThreadState::Create();
                        if (state == NULL)
                                return false;
                        fPreAllocatedThreadStates.Insert(state);
                }

                return true;
        }

        void _GetThreadStates()
        {
                for (;;) {
                        bool missing = false;
                        for (ThreadStateList::Iterator it = fThreadStates.GetIterator();
                                        ThreadState* state = it.Next();) {
                                if (state->IsComplete())
                                        continue;

                                Thread* thread = state->GetThread();
                                AutoLocker<Thread> threadLocker(thread);
                                if (thread->team != fTeam) {
                                        // no longer in our team -- i.e. dying and transferred to
                                        // the kernel team
                                        threadLocker.Unlock();
                                        it.Remove();
                                        delete state;
                                        fThreadCount--;
                                        continue;
                                }

                                InterruptsSpinLocker schedulerLocker(&thread->scheduler_lock);
                                if (thread != fCurrentThread
                                                && thread->state == B_THREAD_RUNNING) {
                                        missing = true;
                                        continue;
                                }

                                state->GetState();
                                state->SetComplete(true);
                        }

                        if (!missing)
                                break;

                        // We still haven't got a state for all threads. Wait a moment and
                        // try again.
                        snooze(10000);
                }
        }

        bool _GetAreaInfos()
        {
                for (;;) {
                        AddressSpaceReadLocker addressSpaceLocker(fTeam->address_space,
                                true);

                        for (VMAddressSpace::AreaIterator it
                                                = addressSpaceLocker.AddressSpace()->GetAreaIterator();
                                        VMArea* area = it.Next();) {

                                VMCache* cache = vm_area_get_locked_cache(area);
                                size_t ramSize = (size_t)cache->page_count * B_PAGE_SIZE;
                                        // simplified, but what the kernel uses as well ATM

                                // iterate to the root cache and, if it is a mapped file, get
                                // the file's node_ref
                                while (VMCache* source = cache->source) {
                                        source->Lock();
                                        source->AcquireRefLocked();
                                        cache->ReleaseRefAndUnlock();
                                        cache = source;
                                }

                                dev_t deviceId = -1;
                                ino_t nodeId = -1;
                                if (cache->type == CACHE_TYPE_VNODE) {
                                        VMVnodeCache* vnodeCache = (VMVnodeCache*)cache;
                                        deviceId = vnodeCache->DeviceId();
                                        nodeId = vnodeCache->InodeId();
                                }

                                cache->ReleaseRefAndUnlock();

                                AreaInfo* areaInfo = AreaInfo::Create(fAreaInfoAllocator, area,
                                        ramSize, deviceId, nodeId);

                                if (areaInfo != NULL)
                                        fAreaInfos.Insert(areaInfo);
                        }

                        addressSpaceLocker.Unlock();

                        if (!fAreaInfoAllocator.HasMissingAllocations())
                                return true;

                        if (!fAreaInfoAllocator.Reallocate())
                                return false;
                }
        }

        bool _GetImageInfos()
        {
                return image_iterate_through_team_images(fTeam->id,
                        &_GetImageInfoCallback, this) == NULL;
        }

        static bool _GetImageInfoCallback(struct image* image, void* cookie)
        {
                return ((CoreDumper*)cookie)->_GetImageInfo(image);
        }

        bool _GetImageInfo(struct image* image)
        {
                ImageInfo* info = ImageInfo::Create(image);
                if (info == NULL)
                        return true;

                fImageInfos.Insert(info);
                return false;
        }

        void _PrepareCoreFileInfo()
        {
                // assign image infos to area infos where possible
                fAreaCount = 0;
                fMappedFilesCount = 0;
                for (AreaInfoList::Iterator it = fAreaInfos.GetIterator();
                                AreaInfo* areaInfo = it.Next();) {
                        fAreaCount++;
                        dev_t deviceId = areaInfo->DeviceId();
                        if (deviceId < 0)
                                continue;
                        ImageInfo* imageInfo = _FindImageInfo(deviceId, areaInfo->NodeId());
                        if (imageInfo != NULL) {
                                areaInfo->SetImageInfo(imageInfo);
                                fMappedFilesCount++;
                        }
                }

                fImageCount = fImageInfos.Count();
                fSegmentCount = 1 + fAreaCount;
                fProgramHeadersOffset = sizeof(elf_ehdr);
                fNoteSegmentOffset = fProgramHeadersOffset
                        + sizeof(elf_phdr) * fSegmentCount;
        }

        ImageInfo* _FindImageInfo(dev_t deviceId, ino_t nodeId) const
        {
                for (ImageInfoList::ConstIterator it = fImageInfos.GetIterator();
                                ImageInfo* info = it.Next();) {
                        if (info->DeviceId() == deviceId && info->NodeId() == nodeId)
                                return info;
                }

                return NULL;
        }

        status_t _WriteElfHeader()
        {
                elf_ehdr header;
                memset(&header, 0, sizeof(header));

                // e_ident
                header.e_ident[EI_MAG0] = ELFMAG[0];
                header.e_ident[EI_MAG1] = ELFMAG[1];
                header.e_ident[EI_MAG2] = ELFMAG[2];
                header.e_ident[EI_MAG3] = ELFMAG[3];
#ifdef B_HAIKU_64_BIT
                header.e_ident[EI_CLASS] = ELFCLASS64;
#else
                header.e_ident[EI_CLASS] = ELFCLASS32;
#endif
#if B_HOST_IS_LENDIAN
                header.e_ident[EI_DATA] = ELFDATA2LSB;
#else
                header.e_ident[EI_DATA] = ELFDATA2MSB;
#endif
                header.e_ident[EI_VERSION] = EV_CURRENT;

                // e_type
                header.e_type = ET_CORE;

                // e_machine
#if defined(__HAIKU_ARCH_X86)
                header.e_machine = EM_386;
#elif defined(__HAIKU_ARCH_X86_64)
                header.e_machine = EM_X86_64;
#elif defined(__HAIKU_ARCH_PPC)
                header.e_machine = EM_PPC64;
#elif defined(__HAIKU_ARCH_M68K)
                header.e_machine = EM_68K;
#elif defined(__HAIKU_ARCH_MIPSEL)
                header.e_machine = EM_MIPS;
#elif defined(__HAIKU_ARCH_ARM)
                header.e_machine = EM_ARM;
#elif defined(__HAIKU_ARCH_ARM64)
                header.e_machine = EM_AARCH64;
#elif defined(__HAIKU_ARCH_SPARC)
                header.e_machine = EM_SPARCV9;
#elif defined(__HAIKU_ARCH_RISCV64)
                header.e_machine = EM_RISCV;
#else
#       error Unsupported architecture!
#endif

                header.e_version = EV_CURRENT;
                header.e_entry = 0;
                header.e_phoff = sizeof(header);
                header.e_shoff = 0;
                header.e_flags = 0;
                header.e_ehsize = sizeof(header);
                header.e_phentsize = sizeof(elf_phdr);
                header.e_phnum = fSegmentCount;
                header.e_shentsize = sizeof(elf_shdr);
                header.e_shnum = 0;
                header.e_shstrndx = SHN_UNDEF;

                return fFile.WriteAt(0, &header, sizeof(header));
        }

        status_t _WriteProgramHeaders()
        {
                fFile.Seek(fProgramHeadersOffset);

                // write the header for the notes segment
                elf_phdr header;
                memset(&header, 0, sizeof(header));
                header.p_type = PT_NOTE;
                header.p_flags = 0;
                header.p_offset = fNoteSegmentOffset;
                header.p_vaddr = 0;
                header.p_paddr = 0;
                header.p_filesz = fNoteSegmentSize;
                header.p_memsz = 0;
                header.p_align = 0;
                fFile.Write(header);

                // write the headers for the area segments
                size_t segmentOffset = fFirstAreaSegmentOffset;
                for (AreaInfoList::Iterator it = fAreaInfos.GetIterator();
                                AreaInfo* areaInfo = it.Next();) {
                        memset(&header, 0, sizeof(header));
                        header.p_type = PT_LOAD;
                        header.p_flags = 0;
                        uint32 protection = areaInfo->Protection();
                        if ((protection & B_READ_AREA) != 0)
                                header.p_flags |= PF_READ;
                        if ((protection & B_WRITE_AREA) != 0)
                                header.p_flags |= PF_WRITE;
                        if ((protection & B_EXECUTE_AREA) != 0)
                                header.p_flags |= PF_EXECUTE;
                        header.p_offset = segmentOffset;
                        header.p_vaddr = areaInfo->Base();
                        header.p_paddr = 0;
                        header.p_filesz = areaInfo->Size();
                        header.p_memsz = areaInfo->Size();
                        header.p_align = 0;
                        fFile.Write(header);

                        segmentOffset += areaInfo->Size();
                }

                return fFile.Status();
        }

        status_t _WriteAreaSegments()
        {
                fFile.Seek(fFirstAreaSegmentOffset);

                for (AreaInfoList::Iterator it = fAreaInfos.GetIterator();
                                AreaInfo* areaInfo = it.Next();) {
                        status_t error = fFile.WriteUserArea(areaInfo->Base(),
                                areaInfo->Size());
                        if (error != B_OK)
                                return error;
                }

                return fFile.Status();
        }

        status_t _WriteNotes()
        {
                status_t error = fFile.Seek((off_t)fNoteSegmentOffset);
                if (error != B_OK)
                        return error;

                error = _WriteFilesNote();
                if (error != B_OK)
                        return error;

                error = _WriteTeamNote();
                if (error != B_OK)
                        return error;

                error = _WriteAreasNote();
                if (error != B_OK)
                        return error;

                error = _WriteImagesNote();
                if (error != B_OK)
                        return error;

                error = _WriteImageSymbolsNotes();
                if (error != B_OK)
                        return error;

                error = _WriteThreadsNote();
                if (error != B_OK)
                        return error;

                return B_OK;
        }

        template<typename Writer>
        void _WriteTeamNote(Writer& writer)
        {
                elf_note_team note;
                memset(&note, 0, sizeof(note));
                note.nt_id = fTeamInfo.team;
                note.nt_uid = fTeamInfo.uid;
                note.nt_gid = fTeamInfo.gid;
                writer.Write((uint32)sizeof(note));
                writer.Write(note);

                // write args
                const char* args = fTeamInfo.args;
                writer.Write(args, strlen(args) + 1);
        }

        status_t _WriteTeamNote()
        {
                // determine needed size for the note's data
                DummyWriter dummyWriter;
                _WriteTeamNote(dummyWriter);
                size_t dataSize = dummyWriter.BytesWritten();

                // write the note header
                _WriteNoteHeader(kHaikuNote, NT_TEAM, dataSize);

                // write the note data
                _WriteTeamNote(fFile);

                // padding
                _WriteNotePadding(dataSize);

                return fFile.Status();
        }

        template<typename Writer>
        void _WriteFilesNote(Writer& writer)
        {
                // file count and table size
                writer.Write(fMappedFilesCount);
                writer.Write((size_t)B_PAGE_SIZE);

                // write table
                for (AreaInfoList::Iterator it = fAreaInfos.GetIterator();
                                AreaInfo* areaInfo = it.Next();) {
                        if (areaInfo->GetImageInfo() == NULL)
                                continue;

                        // start address, end address, and file offset in pages
                        writer.Write(areaInfo->Base());
                        writer.Write(areaInfo->Base() + areaInfo->Size());
                        writer.Write(size_t(areaInfo->CacheOffset() / B_PAGE_SIZE));
                }

                // write strings
                for (AreaInfoList::Iterator it = fAreaInfos.GetIterator();
                                AreaInfo* areaInfo = it.Next();) {
                        ImageInfo* imageInfo = areaInfo->GetImageInfo();
                        if (imageInfo == NULL)
                                continue;

                        const char* name = imageInfo->Name();
                        writer.Write(name, strlen(name) + 1);
                }
        }

        status_t _WriteFilesNote()
        {
                // determine needed size for the note's data
                DummyWriter dummyWriter;
                _WriteFilesNote(dummyWriter);
                size_t dataSize = dummyWriter.BytesWritten();

                // write the note header
                _WriteNoteHeader(kCoreNote, NT_FILE, dataSize);

                // write the note data
                _WriteFilesNote(fFile);

                // padding
                _WriteNotePadding(dataSize);

                return fFile.Status();
        }

        template<typename Writer>
        void _WriteAreasNote(Writer& writer)
        {
                // area count
                writer.Write((uint32)fAreaCount);
                writer.Write((uint32)sizeof(elf_note_area_entry));

                // write table
                for (AreaInfoList::Iterator it = fAreaInfos.GetIterator();
                                AreaInfo* areaInfo = it.Next();) {
                        elf_note_area_entry entry;
                        memset(&entry, 0, sizeof(entry));
                        entry.na_id = areaInfo->Id();
                        entry.na_lock = areaInfo->Lock();
                        entry.na_protection = areaInfo->Protection();
                        entry.na_base = areaInfo->Base();
                        entry.na_size = areaInfo->Size();
                        entry.na_ram_size = areaInfo->RamSize();
                        writer.Write(entry);
                }

                // write strings
                for (AreaInfoList::Iterator it = fAreaInfos.GetIterator();
                                AreaInfo* areaInfo = it.Next();) {
                        const char* name = areaInfo->Name();
                        writer.Write(name, strlen(name) + 1);
                }
        }

        status_t _WriteAreasNote()
        {
                // determine needed size for the note's data
                DummyWriter dummyWriter;
                _WriteAreasNote(dummyWriter);
                size_t dataSize = dummyWriter.BytesWritten();

                // write the note header
                _WriteNoteHeader(kHaikuNote, NT_AREAS, dataSize);

                // write the note data
                _WriteAreasNote(fFile);

                // padding
                _WriteNotePadding(dataSize);

                return fFile.Status();
        }

        template<typename Writer>
        void _WriteImagesNote(Writer& writer)
        {
                // image count
                writer.Write((uint32)fImageCount);
                writer.Write((uint32)sizeof(elf_note_image_entry));

                // write table
                for (ImageInfoList::Iterator it = fImageInfos.GetIterator();
                                ImageInfo* imageInfo = it.Next();) {
                        elf_note_image_entry entry;
                        memset(&entry, 0, sizeof(entry));
                        entry.ni_id = imageInfo->Id();
                        entry.ni_type = imageInfo->Type();
                        entry.ni_init_routine = imageInfo->InitRoutine();
                        entry.ni_term_routine = imageInfo->TermRoutine();
                        entry.ni_device = imageInfo->DeviceId();
                        entry.ni_node = imageInfo->NodeId();
                        entry.ni_text_base = imageInfo->TextBase();
                        entry.ni_text_size = imageInfo->TextSize();
                        entry.ni_data_base = imageInfo->DataBase();
                        entry.ni_data_size = imageInfo->DataSize();
                        entry.ni_text_delta = imageInfo->TextDelta();
                        entry.ni_symbol_table = imageInfo->SymbolTable();
                        entry.ni_symbol_hash = imageInfo->SymbolHash();
                        entry.ni_string_table = imageInfo->StringTable();
                        writer.Write(entry);
                }

                // write strings
                for (ImageInfoList::Iterator it = fImageInfos.GetIterator();
                                ImageInfo* imageInfo = it.Next();) {
                        const char* name = imageInfo->Name();
                        writer.Write(name, strlen(name) + 1);
                }
        }

        status_t _WriteImagesNote()
        {
                // determine needed size for the note's data
                DummyWriter dummyWriter;
                _WriteImagesNote(dummyWriter);
                size_t dataSize = dummyWriter.BytesWritten();

                // write the note header
                _WriteNoteHeader(kHaikuNote, NT_IMAGES, dataSize);

                // write the note data
                _WriteImagesNote(fFile);

                // padding
                _WriteNotePadding(dataSize);

                return fFile.Status();
        }

        status_t _WriteImageSymbolsNotes()
        {
                // write table
                for (ImageInfoList::Iterator it = fImageInfos.GetIterator();
                                ImageInfo* imageInfo = it.Next();) {
                        if (imageInfo->SymbolTableData() == NULL
                                        || imageInfo->StringTableData() == NULL) {
                                continue;
                        }

                        status_t error = _WriteImageSymbolsNote(imageInfo);
                        if (error != B_OK)
                                return error;
                }

                return B_OK;
        }

        template<typename Writer>
        void _WriteImageSymbolsNote(const ImageInfo* imageInfo, Writer& writer)
        {
                uint32 symbolCount = imageInfo->SymbolCount();
                uint32 symbolEntrySize = (uint32)sizeof(elf_sym);

                writer.Write((int32)imageInfo->Id());
                writer.Write(symbolCount);
                writer.Write(symbolEntrySize);
                writer.Write(imageInfo->SymbolTableData(),
                        symbolCount * symbolEntrySize);
                writer.Write(imageInfo->StringTableData(),
                        imageInfo->StringTableSize());
        }

        status_t _WriteImageSymbolsNote(const ImageInfo* imageInfo)
        {
                // determine needed size for the note's data
                DummyWriter dummyWriter;
                _WriteImageSymbolsNote(imageInfo, dummyWriter);
                size_t dataSize = dummyWriter.BytesWritten();

                // write the note header
                _WriteNoteHeader(kHaikuNote, NT_SYMBOLS, dataSize);

                // write the note data
                _WriteImageSymbolsNote(imageInfo, fFile);

                // padding
                _WriteNotePadding(dataSize);

                return fFile.Status();
        }

        template<typename Writer>
        void _WriteThreadsNote(Writer& writer)
        {
                // thread count and size of CPU state
                writer.Write((uint32)fThreadCount);
                writer.Write((uint32)sizeof(elf_note_thread_entry));
                writer.Write((uint32)sizeof(debug_cpu_state));

                // write table
                for (ThreadStateList::Iterator it = fThreadStates.GetIterator();
                                ThreadState* state = it.Next();) {
                        elf_note_thread_entry entry;
                        memset(&entry, 0, sizeof(entry));
                        entry.nth_id = state->GetThread()->id;
                        entry.nth_state = state->State();
                        entry.nth_priority = state->Priority();
                        entry.nth_stack_base = state->StackBase();
                        entry.nth_stack_end = state->StackEnd();
                        writer.Write(&entry, sizeof(entry));
                        writer.Write(state->CpuState(), sizeof(debug_cpu_state));
                }

                // write strings
                for (ThreadStateList::Iterator it = fThreadStates.GetIterator();
                                ThreadState* state = it.Next();) {
                        const char* name = state->Name();
                        writer.Write(name, strlen(name) + 1);
                }
        }

        status_t _WriteThreadsNote()
        {
                // determine needed size for the note's data
                DummyWriter dummyWriter;
                _WriteThreadsNote(dummyWriter);
                size_t dataSize = dummyWriter.BytesWritten();

                // write the note header
                _WriteNoteHeader(kHaikuNote, NT_THREADS, dataSize);

                // write the note data
                _WriteThreadsNote(fFile);

                // padding
                _WriteNotePadding(dataSize);

                return fFile.Status();
        }

        status_t _WriteNoteHeader(const char* name, uint32 type, uint32 dataSize)
        {
                // prepare and write the header
                Elf32_Nhdr noteHeader;
                memset(&noteHeader, 0, sizeof(noteHeader));
                size_t nameSize = strlen(name) + 1;
                noteHeader.n_namesz = nameSize;
                noteHeader.n_descsz = dataSize;
                noteHeader.n_type = type;
                fFile.Write(noteHeader);

                // write the name
                fFile.Write(name, nameSize);
                // pad the name to 4 byte alignment
                _WriteNotePadding(nameSize);
                return fFile.Status();
        }

        status_t _WriteNotePadding(size_t sizeToPad)
        {
                if (sizeToPad % 4 != 0) {
                        uint8 pad[3] = {};
                        fFile.Write(&pad, 4 - sizeToPad % 4);
                }
                return fFile.Status();
        }

private:
        Thread*                         fCurrentThread;
        Team*                           fTeam;
        BufferedFile            fFile;
        TeamInfo                        fTeamInfo;
        size_t                          fThreadCount;
        ThreadStateList         fThreadStates;
        ThreadStateList         fPreAllocatedThreadStates;
        Allocator                       fAreaInfoAllocator;
        AreaInfoList            fAreaInfos;
        ImageInfoList           fImageInfos;
        ConditionVariable       fThreadBlockCondition;
        size_t                          fSegmentCount;
        size_t                          fProgramHeadersOffset;
        size_t                          fNoteSegmentOffset;
        size_t                          fNoteSegmentSize;
        size_t                          fFirstAreaSegmentOffset;
        size_t                          fAreaCount;
        size_t                          fImageCount;
        size_t                          fMappedFilesCount;
};


} // unnamed namespace


status_t
core_dump_write_core_file(const char* path, bool killTeam)
{
        TRACE("core_dump_write_core_file(\"%s\", %d): team: %" B_PRId32 "\n", path,
                killTeam, team_get_current_team_id());

        CoreDumper* coreDumper = new(std::nothrow) CoreDumper();
        if (coreDumper == NULL)
                return B_NO_MEMORY;
        ObjectDeleter<CoreDumper> coreDumperDeleter(coreDumper);
        return coreDumper->Dump(path, killTeam);
}


void
core_dump_trap_thread()
{
        Thread* thread = thread_get_current_thread();
        ConditionVariableEntry conditionVariableEntry;
        TeamLocker teamLocker(thread->team);

        while ((atomic_get(&thread->flags) & THREAD_FLAGS_TRAP_FOR_CORE_DUMP)
                        != 0) {
                thread->team->CoreDumpCondition()->Add(&conditionVariableEntry);
                teamLocker.Unlock();
                conditionVariableEntry.Wait();
                teamLocker.Lock();
        }
}