root/src/kits/debugger/value/ValueLoader.cpp
/*
 * Copyright 2009-2012, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Copyright 2013-2015, Rene Gollent, rene@gollent.com.
 * Distributed under the terms of the MIT License.
 */


#include "ValueLoader.h"

#include "Architecture.h"
#include "BitBuffer.h"
#include "CpuState.h"
#include "Register.h"
#include "TeamMemory.h"
#include "Tracing.h"
#include "ValueLocation.h"


ValueLoader::ValueLoader(Architecture* architecture, TeamMemory* teamMemory,
        CpuState* cpuState)
        :
        fArchitecture(architecture),
        fTeamMemory(teamMemory),
        fCpuState(cpuState)
{
        fArchitecture->AcquireReference();
        fTeamMemory->AcquireReference();
        if (fCpuState != NULL)
                fCpuState->AcquireReference();
}


ValueLoader::~ValueLoader()
{
        fArchitecture->ReleaseReference();
        fTeamMemory->ReleaseReference();
        if (fCpuState != NULL)
                fCpuState->ReleaseReference();
}


status_t
ValueLoader::LoadValue(ValueLocation* location, type_code valueType,
        bool shortValueIsFine, BVariant& _value)
{
        static const size_t kMaxPieceSize = 16;
        uint64 totalBitSize = 0;
        int32 count = location->CountPieces();
        for (int32 i = 0; i < count; i++) {
                ValuePieceLocation piece = location->PieceAt(i);
                switch (piece.type) {
                        case VALUE_PIECE_LOCATION_INVALID:
                        case VALUE_PIECE_LOCATION_UNKNOWN:
                                return B_ENTRY_NOT_FOUND;
                        case VALUE_PIECE_LOCATION_MEMORY:
                        case VALUE_PIECE_LOCATION_REGISTER:
                        case VALUE_PIECE_LOCATION_IMPLICIT:
                                break;
                }

                if (piece.size > kMaxPieceSize) {
                        TRACE_LOCALS("  -> overly long piece size (%" B_PRIu64 " bytes)\n",
                                piece.size);
                        return B_UNSUPPORTED;
                }

                totalBitSize += piece.bitSize;
        }

        TRACE_LOCALS("  -> totalBitSize: %" B_PRIu64 "\n", totalBitSize);

        if (totalBitSize == 0) {
                TRACE_LOCALS("  -> no size\n");
                return B_ENTRY_NOT_FOUND;
        }

        if (totalBitSize > 64) {
                TRACE_LOCALS("  -> longer than 64 bits: unsupported\n");
                return B_UNSUPPORTED;
        }

        uint64 valueBitSize = BVariant::SizeOfType(valueType) * 8;
        if (!shortValueIsFine && totalBitSize < valueBitSize) {
                TRACE_LOCALS("  -> too short for value type (%" B_PRIu64 " vs. %"
                        B_PRIu64 " bits)\n", totalBitSize, valueBitSize);
                return B_BAD_VALUE;
        }

        // Load the data. Since the BitBuffer class we're using only supports big
        // endian bit semantics, we convert all data to big endian before pushing
        // them to the buffer. For later conversion to BVariant we need to make sure
        // the final buffer has the size of the value type, so we pad the most
        // significant bits with zeros.
        BitBuffer valueBuffer;
        if (totalBitSize < valueBitSize)
                valueBuffer.AddZeroBits(valueBitSize - totalBitSize);

        bool bigEndian = fArchitecture->IsBigEndian();
        const Register* registers = fArchitecture->Registers();
        for (int32 i = 0; i < count; i++) {
                ValuePieceLocation piece = location->PieceAt(
                        bigEndian ? i : count - i - 1);
                uint32 bytesToRead = piece.size;
                uint32 bitSize = piece.bitSize;
                uint8 bitOffset = piece.bitOffset;
                        // TODO: the offset's ordinal position and direction aren't
                        // specified by DWARF, and simply follow the target language.
                        // To handle non C/C++ languages properly, the corresponding
                        // SourceLanguage will need to be passed in and extended to
                        // return the relevant information.

                switch (piece.type) {
                        case VALUE_PIECE_LOCATION_INVALID:
                        case VALUE_PIECE_LOCATION_UNKNOWN:
                                return B_ENTRY_NOT_FOUND;
                        case VALUE_PIECE_LOCATION_MEMORY:
                        case VALUE_PIECE_LOCATION_IMPLICIT:
                        {
                                target_addr_t address = piece.address;

                                if (piece.type == VALUE_PIECE_LOCATION_MEMORY) {
                                        TRACE_LOCALS("  piece %" B_PRId32 ": memory address: %#"
                                                B_PRIx64 ", bits: %" B_PRIu32 "\n", i, address,
                                                bitSize);
                                } else {
                                        TRACE_LOCALS("  piece %" B_PRId32 ": implicit value, "
                                                "bits: %" B_PRIu32 "\n", i, bitSize);
                                }

                                uint8 pieceBuffer[kMaxPieceSize];
                                ssize_t bytesRead;
                                if (piece.type == VALUE_PIECE_LOCATION_MEMORY) {
                                        bytesRead = fTeamMemory->ReadMemory(address,
                                                pieceBuffer, bytesToRead);
                                } else {
                                        memcpy(pieceBuffer, piece.value, piece.size);
                                        bytesRead = piece.size;
                                }

                                if (bytesRead < 0)
                                        return bytesRead;
                                if ((uint32)bytesRead != bytesToRead)
                                        return B_BAD_ADDRESS;

                                TRACE_LOCALS_ONLY(
                                        TRACE_LOCALS("  -> read: ");
                                        for (ssize_t k = 0; k < bytesRead; k++)
                                                TRACE_LOCALS("%02x", pieceBuffer[k]);
                                        TRACE_LOCALS("\n");
                                )

                                // convert to big endian
                                if (!bigEndian) {
                                        for (int32 k = bytesRead / 2 - 1; k >= 0; k--) {
                                                std::swap(pieceBuffer[k],
                                                        pieceBuffer[bytesRead - k - 1]);
                                        }
                                }

                                valueBuffer.AddBits(pieceBuffer, bitSize, bitOffset);
                                break;
                        }
                        case VALUE_PIECE_LOCATION_REGISTER:
                        {
                                TRACE_LOCALS("  piece %" B_PRId32 ": register: %" B_PRIu32
                                        ", bits: %" B_PRIu32 "\n", i, piece.reg, bitSize);

                                if (fCpuState == NULL) {
                                        WARNING("ValueLoader::LoadValue(): register piece, but no "
                                                "CpuState\n");
                                        return B_UNSUPPORTED;
                                }

                                BVariant registerValue;
                                if (!fCpuState->GetRegisterValue(registers + piece.reg,
                                                registerValue)) {
                                        return B_ENTRY_NOT_FOUND;
                                }
                                if (registerValue.Size() < bytesToRead)
                                        return B_ENTRY_NOT_FOUND;

                                if (!bigEndian) {
                                        registerValue.SwapEndianess();
                                        bitOffset = registerValue.Size() * 8 - bitOffset - bitSize;
                                }
                                valueBuffer.AddBits(registerValue.Bytes(), bitSize, bitOffset);
                                break;
                        }
                }
        }

        // If we don't have enough bits in the buffer apparently adding some failed.
        if (valueBuffer.BitSize() < valueBitSize)
                return B_NO_MEMORY;

        // convert the bits into something we can work with
        BVariant value;
        status_t error = value.SetToTypedData(valueBuffer.Bytes(), valueType);
        if (error != B_OK) {
                TRACE_LOCALS("  -> failed to set typed data: %s\n", strerror(error));
                return error;
        }

        // convert to host endianess
        #if B_HOST_IS_LENDIAN
                value.SwapEndianess();
        #endif

        _value = value;
        return B_OK;
}


status_t
ValueLoader::LoadRawValue(BVariant& location, size_t bytesToRead, void* _value)
{
        ssize_t bytesRead = fTeamMemory->ReadMemory(location.ToUInt64(),
                _value, bytesToRead);
        if (bytesRead < 0)
                return bytesRead;
        if ((uint32)bytesRead != bytesToRead)
                return B_BAD_ADDRESS;
        return B_OK;
}


status_t
ValueLoader::LoadStringValue(BVariant& location, size_t maxSize, BString& _value)
{
        static const size_t kMaxStringSize = 255;

        return fTeamMemory->ReadMemoryString(location.ToUInt64(),
                std::min(maxSize, kMaxStringSize), _value);
}