root/src/bin/debug/strace/MemoryReader.cpp
/*
 * Copyright 2005-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Distributed under the terms of the MIT License.
 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <debugger.h>

#include "MemoryReader.h"


MemoryReader::MemoryReader()
        :
        fNubPort(-1),
        fReplyPort(-1)
{
}


MemoryReader::~MemoryReader()
{
        if (fReplyPort >= 0)
                delete_port(fReplyPort);
}


status_t
MemoryReader::Init(port_id nubPort)
{
        if (fReplyPort >= 0)
                delete_port(fReplyPort);

        fNubPort = nubPort;

        fReplyPort = create_port(1, "memory reader reply");
        if (fReplyPort < 0) {
                fprintf(stderr, "Failed to create memory reader reply port: %s\n",
                        strerror(fReplyPort));
                return fReplyPort;
        }

        return B_OK;
}


status_t
MemoryReader::Read(void *_address, void *_buffer, int32 size, int32 &bytesRead)
{
        char *address = (char*)_address;
        char *buffer = (char*)_buffer;
        bytesRead = 0;

        while (size > 0) {
                int32 toRead = size;
                if (toRead > B_MAX_READ_WRITE_MEMORY_SIZE)
                        toRead = B_MAX_READ_WRITE_MEMORY_SIZE;

                int32 actuallyRead = 0;
                status_t error = _Read(address, buffer, toRead, actuallyRead);

                // If reading fails, we only fail, if we haven't read anything yet.
                if (error != B_OK) {
                        if (bytesRead > 0)
                                return B_OK;
                        return error;
                }

                bytesRead += actuallyRead;
                address += actuallyRead;
                buffer += actuallyRead;
                size -= actuallyRead;
        }

        return B_OK;
}


status_t
MemoryReader::_Read(void *address, void *buffer, int32 size, int32 &bytesRead)
{
        // prepare message
        debug_nub_read_memory message;
        message.reply_port = fReplyPort;
        message.address = address;
        message.size = size;

        // send message
        while (true) {
                status_t error = write_port(fNubPort, B_DEBUG_MESSAGE_READ_MEMORY,
                        &message, sizeof(message));
                if (error == B_OK)
                        break;
                if (error != B_INTERRUPTED)
                        return error;
        }

        // get reply
        int32 code;
        debug_nub_read_memory_reply reply;
        while (true) {
                ssize_t bytesRead = read_port(fReplyPort, &code, &reply, sizeof(reply));
                if (bytesRead > 0)
                        break;
                if (bytesRead != B_INTERRUPTED)
                        return bytesRead;
        }

        if (reply.error != B_OK)
                return reply.error;

        bytesRead = reply.size;
        memcpy(buffer, reply.data, bytesRead);

        return B_OK;
}