root/src/kits/debugger/user_interface/util/UiUtils.cpp
/*
 * Copyright 2012, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Copyright 2012-2016, Rene Gollent, rene@gollent.com.
 * Distributed under the terms of the MIT License.
 */


#include "UiUtils.h"

#include <ctype.h>
#include <stdio.h>

#include <DateTime.h>
#include <KernelExport.h>
#include <Path.h>
#include <String.h>
#include <Variant.h>

#include <vm_defs.h>

#include "FunctionInstance.h"
#include "Image.h"
#include "RangeList.h"
#include "SignalDispositionTypes.h"
#include "StackFrame.h"
#include "Team.h"
#include "TeamMemoryBlock.h"
#include "Thread.h"
#include "Type.h"
#include "Value.h"
#include "ValueNode.h"


/*static*/ const char*
UiUtils::ThreadStateToString(int state, int stoppedReason)
{
        switch (state) {
                case THREAD_STATE_RUNNING:
                        return "Running";
                case THREAD_STATE_STOPPED:
                        break;
                case THREAD_STATE_UNKNOWN:
                default:
                        return "?";
        }

        // thread is stopped -- get the reason
        switch (stoppedReason) {
                case THREAD_STOPPED_DEBUGGER_CALL:
                        return "Call";
                case THREAD_STOPPED_EXCEPTION:
                        return "Exception";
                case THREAD_STOPPED_BREAKPOINT:
                case THREAD_STOPPED_WATCHPOINT:
                case THREAD_STOPPED_SINGLE_STEP:
                case THREAD_STOPPED_DEBUGGED:
                case THREAD_STOPPED_UNKNOWN:
                default:
                        return "Debugged";
        }
}


/*static*/ const char*
UiUtils::VariantToString(const BVariant& value, char* buffer,
        size_t bufferSize)
{
        if (!value.IsNumber())
                return value.ToString();

        switch (value.Type()) {
                case B_FLOAT_TYPE:
                case B_DOUBLE_TYPE:
                        snprintf(buffer, bufferSize, "%.3g", value.ToDouble());
                        break;
                case B_INT8_TYPE:
                case B_UINT8_TYPE:
                        snprintf(buffer, bufferSize, "0x%02x", value.ToUInt8());
                        break;
                case B_INT16_TYPE:
                case B_UINT16_TYPE:
                        snprintf(buffer, bufferSize, "0x%04x", value.ToUInt16());
                        break;
                case B_INT32_TYPE:
                case B_UINT32_TYPE:
                        snprintf(buffer, bufferSize, "0x%08" B_PRIx32,
                                value.ToUInt32());
                        break;
                case B_INT64_TYPE:
                case B_UINT64_TYPE:
                default:
                        snprintf(buffer, bufferSize, "0x%016" B_PRIx64,
                                value.ToUInt64());
                        break;
        }

        return buffer;
}


/*static*/ const char*
UiUtils::FunctionNameForFrame(StackFrame* frame, char* buffer,
        size_t bufferSize)
{
        Image* image = frame->GetImage();
        FunctionInstance* function = frame->Function();
        if (image == NULL && function == NULL) {
                snprintf(buffer, bufferSize, "?");
                return buffer;
        }

        BString name;
        target_addr_t baseAddress;
        if (function != NULL) {
                name = function->PrettyName();
                baseAddress = function->Address();
        } else {
                name = image->Name();
                baseAddress = image->Info().TextBase();
        }

        snprintf(buffer, bufferSize, "%s + %#" B_PRIx64,
                name.String(), frame->InstructionPointer() - baseAddress);

        return buffer;
}


/*static*/ const char*
UiUtils::ImageTypeToString(image_type type, char* buffer, size_t bufferSize)
{
        switch (type) {
                case B_APP_IMAGE:
                        snprintf(buffer, bufferSize, "app");
                        break;
                case B_LIBRARY_IMAGE:
                        snprintf(buffer, bufferSize, "lib");
                        break;
                case B_ADD_ON_IMAGE:
                        snprintf(buffer, bufferSize, "add-on");
                        break;
                case B_SYSTEM_IMAGE:
                        snprintf(buffer, bufferSize, "system");
                        break;
                default:
                        snprintf(buffer, bufferSize, "unknown");
                        break;
        }

        return buffer;
}


/*static*/ const char*
UiUtils::AreaLockingFlagsToString(uint32 flags, char* buffer,
        size_t bufferSize)
{
        switch (flags) {
                case B_NO_LOCK:
                        snprintf(buffer, bufferSize, "none");
                        break;
                case B_LAZY_LOCK:
                        snprintf(buffer, bufferSize, "lazy");
                        break;
                case B_FULL_LOCK:
                        snprintf(buffer, bufferSize, "full");
                        break;
                case B_CONTIGUOUS:
                        snprintf(buffer, bufferSize, "contiguous");
                        break;
                case B_LOMEM:
                        snprintf(buffer, bufferSize, "lo-mem");
                        break;
                case B_32_BIT_FULL_LOCK:
                        snprintf(buffer, bufferSize, "32-bit full");
                        break;
                case B_32_BIT_CONTIGUOUS:
                        snprintf(buffer, bufferSize, "32-bit contig.");
                        break;
                default:
                        snprintf(buffer, bufferSize, "unknown");
                        break;
        }

        return buffer;
}


/*static*/ const BString&
UiUtils::AreaProtectionFlagsToString(uint32 protection, BString& _output)
{
        #undef ADD_AREA_FLAG_IF_PRESENT
        #define ADD_AREA_FLAG_IF_PRESENT(flag, protection, name, output, missing)\
                if ((protection & flag) != 0) { \
                        _output += name; \
                        protection &= ~flag; \
                } else \
                        _output += missing; \

        _output.Truncate(0);
        uint32 userFlags = protection & B_USER_PROTECTION;
        bool userProtectionPresent = userFlags != 0;
        ADD_AREA_FLAG_IF_PRESENT(B_READ_AREA, protection, "r", _output,
                userProtectionPresent ? "-" : " ");
        ADD_AREA_FLAG_IF_PRESENT(B_WRITE_AREA, protection, "w", _output,
                userProtectionPresent ? "-" : " ");
        ADD_AREA_FLAG_IF_PRESENT(B_EXECUTE_AREA, protection, "x", _output,
                userProtectionPresent ? "-" : " ");

        // if the user versions of these flags are present,
        // filter out their kernel equivalents since they're implied.
        if ((userFlags & B_READ_AREA) != 0)
                protection &= ~B_KERNEL_READ_AREA;
        if ((userFlags & B_WRITE_AREA) != 0)
                protection &= ~B_KERNEL_WRITE_AREA;
        if ((userFlags & B_EXECUTE_AREA) != 0)
                protection &= ~B_KERNEL_EXECUTE_AREA;

        if ((protection & B_KERNEL_PROTECTION) != 0) {
                ADD_AREA_FLAG_IF_PRESENT(B_KERNEL_READ_AREA, protection, "r",
                        _output, "-");
                ADD_AREA_FLAG_IF_PRESENT(B_KERNEL_WRITE_AREA, protection, "w",
                        _output, "-");
                ADD_AREA_FLAG_IF_PRESENT(B_KERNEL_EXECUTE_AREA, protection, "x",
                        _output, "-");
        }

        ADD_AREA_FLAG_IF_PRESENT(B_STACK_AREA, protection, "s", _output, "");
        ADD_AREA_FLAG_IF_PRESENT(B_KERNEL_STACK_AREA, protection, "s", _output, "");
        ADD_AREA_FLAG_IF_PRESENT(B_OVERCOMMITTING_AREA, protection, _output, "o",
                "");
        ADD_AREA_FLAG_IF_PRESENT(B_CLONEABLE_AREA, protection, "c", _output, "");
        ADD_AREA_FLAG_IF_PRESENT(B_SHARED_AREA, protection, "S", _output, "");
        ADD_AREA_FLAG_IF_PRESENT(B_KERNEL_AREA, protection, "k", _output, "");

        if (protection != 0) {
                char buffer[32];
                snprintf(buffer, sizeof(buffer), ", u:(%#04" B_PRIx32 ")",
                        protection);
                _output += buffer;
        }

        return _output;
}


/*static*/ const char*
UiUtils::ReportNameForTeam(::Team* team, char* buffer, size_t bufferSize)
{
        BPath teamPath(team->Name());
        BDateTime currentTime;
        currentTime.SetTime_t(time(NULL));
        snprintf(buffer, bufferSize, "%s-%" B_PRId32 "-debug-%02" B_PRId32 "-%02"
                B_PRId32 "-%02" B_PRId32 "-%02" B_PRId32 "-%02" B_PRId32 "-%02"
                B_PRId32 ".report", teamPath.Leaf(), team->ID(),
                currentTime.Date().Day(), currentTime.Date().Month(),
                currentTime.Date().Year(), currentTime.Time().Hour(),
                currentTime.Time().Minute(), currentTime.Time().Second());

        return buffer;
}


/*static*/ const char*
UiUtils::CoreFileNameForTeam(::Team* team, char* buffer, size_t bufferSize)
{
        BPath teamPath(team->Name());
        BDateTime currentTime;
        currentTime.SetTime_t(time(NULL));
        snprintf(buffer, bufferSize, "%s-%" B_PRId32 "-debug-%02" B_PRId32 "-%02"
                B_PRId32 "-%02" B_PRId32 "-%02" B_PRId32 "-%02" B_PRId32 "-%02"
                B_PRId32 ".core", teamPath.Leaf(), team->ID(),
                currentTime.Date().Day(), currentTime.Date().Month(),
                currentTime.Date().Year(), currentTime.Time().Hour(),
                currentTime.Time().Minute(), currentTime.Time().Second());

        return buffer;

}


/*static*/ void
UiUtils::PrintValueNodeGraph(BString& _output, ValueNodeChild* child,
        int32 indentLevel, int32 maxDepth)
{
        _output.Append('\t', indentLevel);
        _output << child->Name();

        ValueNode* node = child->Node();
        if (node == NULL) {
                _output << ": Unavailable\n";
                return;
        }

        if (node->GetType()->Kind() != TYPE_COMPOUND) {
                _output << ": ";
                status_t resolutionState = node->LocationAndValueResolutionState();
                if (resolutionState == VALUE_NODE_UNRESOLVED)
                        _output << "Unresolved";
                else if (resolutionState == B_OK) {
                        Value* value = node->GetValue();
                        if (value != NULL) {
                                BString valueData;
                                value->ToString(valueData);
                                _output << valueData;
                        } else
                                _output << "Unavailable";
                } else
                        _output << strerror(resolutionState);
        }

        if (maxDepth == 0 || node->CountChildren() == 0) {
                _output << "\n";
                return;
        }

        if (node->CountChildren() == 1
                && node->GetType()->ResolveRawType(false)->Kind() == TYPE_ADDRESS
                && node->ChildAt(0)->GetType()->ResolveRawType(false)->Kind()
                        == TYPE_COMPOUND) {
                // for the case of a pointer to a compound type,
                // we want to hide the intervening compound node and print
                // the children directly.
                node = node->ChildAt(0)->Node();
        }

        if (node != NULL) {
                _output << " {\n";

                for (int32 i = 0; i < node->CountChildren(); i++) {
                        // don't dump compound nodes if our depth limit won't allow
                        // us to traverse into their children anyways, and the top
                        // level node contains no data of intereest.
                        if (node->ChildAt(i)->GetType()->Kind() != TYPE_COMPOUND
                                || maxDepth > 1) {
                                PrintValueNodeGraph(_output, node->ChildAt(i),
                                        indentLevel + 1, maxDepth - 1);
                        }
                }
                _output.Append('\t', indentLevel);
                _output << "}\n";
        } else
                _output << "\n";

        return;
}


/*static*/ void
UiUtils::DumpMemory(BString& _output, int32 indentLevel,
        TeamMemoryBlock* block, target_addr_t address, int32 itemSize,
        int32 displayWidth, int32 count)
{
        BString data;

        int32 j;
        _output.Append('\t', indentLevel);
        for (int32 i = 0; i < count; i++) {
                if (!block->Contains(address + i * itemSize))
                        break;

                uint8* value;

                if ((i % displayWidth) == 0) {
                        int32 displayed = min_c(displayWidth, (count-i)) * itemSize;
                        if (i != 0) {
                                _output.Append("\n");
                                _output.Append('\t', indentLevel);
                        }

                        data.SetToFormat("[%#" B_PRIx64 "]  ", address + i * itemSize);
                        _output += data;
                        char c;
                        for (j = 0; j < displayed; j++) {
                                c = *(block->Data() + address - block->BaseAddress()
                                        + (i * itemSize) + j);
                                if (!isprint(c))
                                        c = '.';

                                _output += c;
                        }
                        if (count > displayWidth) {
                                // make sure the spacing in the last line is correct
                                for (j = displayed; j < displayWidth * itemSize; j++)
                                        _output += ' ';
                        }
                        _output.Append("  ");
                }

                value = block->Data() + address - block->BaseAddress()
                        + i * itemSize;

                switch (itemSize) {
                        case 1:
                                data.SetToFormat(" %02" B_PRIx8, *(uint8*)value);
                                break;
                        case 2:
                                data.SetToFormat(" %04" B_PRIx16, *(uint16*)value);
                                break;
                        case 4:
                                data.SetToFormat(" %08" B_PRIx32, *(uint32*)value);
                                break;
                        case 8:
                                data.SetToFormat(" %016" B_PRIx64, *(uint64*)value);
                                break;
                }

                _output += data;
        }

        _output.Append("\n");
}


static status_t ParseRangeString(BString& rangeString, int32& lowerBound,
        int32& upperBound)
{
        lowerBound = atoi(rangeString.String());
        int32 index = rangeString.FindFirst('-');
        if (index >= 0) {
                rangeString.Remove(0, index + 1);
                upperBound = atoi(rangeString.String());
        } else
                upperBound = lowerBound;

        if (lowerBound > upperBound)
                return B_BAD_VALUE;

        return B_OK;
}


/*static*/ status_t
UiUtils::ParseRangeExpression(const BString& rangeExpression, int32 lowerBound,
        int32 upperBound, bool fixedRange, RangeList& _output)
{
        if (rangeExpression.IsEmpty())
                return B_BAD_DATA;

        BString dataString = rangeExpression;
        dataString.RemoveAll(" ");

        // first, tokenize the range list to its constituent child ranges.
        int32 index;
        int32 lowValue;
        int32 highValue;
        BString tempRange;
        while (!dataString.IsEmpty()) {
                index = dataString.FindFirst(',');
                if (index == 0)
                        return B_BAD_VALUE;
                else if (index > 0) {
                        dataString.MoveInto(tempRange, 0, index);
                        dataString.Remove(0, 1);
                } else {
                        tempRange = dataString;
                        dataString.Truncate(0);
                }

                status_t result = ParseRangeString(tempRange, lowValue, highValue);
                if (result != B_OK)
                        return result;


                if (fixedRange && (lowValue < lowerBound || highValue > upperBound))
                        return B_BAD_VALUE;

                result = _output.AddRange(lowValue, highValue);
                if (result != B_OK)
                        return result;

                tempRange.Truncate(0);
        }

        return B_OK;
}


/*static*/ const char*
UiUtils::TypeCodeToString(type_code type)
{
        switch (type) {
                case B_INT8_TYPE:
                        return "int8";
                case B_UINT8_TYPE:
                        return "uint8";
                case B_INT16_TYPE:
                        return "int16";
                case B_UINT16_TYPE:
                        return "uint16";
                case B_INT32_TYPE:
                        return "int32";
                case B_UINT32_TYPE:
                        return "uint32";
                case B_INT64_TYPE:
                        return "int64";
                case B_UINT64_TYPE:
                        return "uint64";
                case B_FLOAT_TYPE:
                        return "float";
                case B_DOUBLE_TYPE:
                        return "double";
                case B_STRING_TYPE:
                        return "string";
                default:
                        return "unknown";
        }
}


template<typename T>
T GetSIMDValueAtOffset(char* data, int32 index)
{
        return ((T*)data)[index];
}


static int32 GetSIMDFormatByteSize(uint32 format)
{
        switch (format) {
                case SIMD_RENDER_FORMAT_INT8:
                        return sizeof(char);
                case SIMD_RENDER_FORMAT_INT16:
                        return sizeof(int16);
                case SIMD_RENDER_FORMAT_INT32:
                        return sizeof(int32);
                case SIMD_RENDER_FORMAT_INT64:
                        return sizeof(int64);
                case SIMD_RENDER_FORMAT_FLOAT:
                        return sizeof(float);
                case SIMD_RENDER_FORMAT_DOUBLE:
                        return sizeof(double);
        }

        return 0;
}


/*static*/
const BString&
UiUtils::FormatSIMDValue(const BVariant& value, uint32 bitSize,
        uint32 format, BString& _output)
{
        _output.SetTo("{");
        char* data = (char*)value.ToPointer();
        uint32 count = bitSize / (GetSIMDFormatByteSize(format) * 8);
        for (uint32 i = 0; i < count; i ++) {
                BString temp;
                switch (format) {
                        case SIMD_RENDER_FORMAT_INT8:
                                temp.SetToFormat("%#" B_PRIx8,
                                        GetSIMDValueAtOffset<uint8>(data, i));
                                break;
                        case SIMD_RENDER_FORMAT_INT16:
                                temp.SetToFormat("%#" B_PRIx16,
                                        GetSIMDValueAtOffset<uint16>(data, i));
                                break;
                        case SIMD_RENDER_FORMAT_INT32:
                                temp.SetToFormat("%#" B_PRIx32,
                                        GetSIMDValueAtOffset<uint32>(data, i));
                                break;
                        case SIMD_RENDER_FORMAT_INT64:
                                temp.SetToFormat("%#" B_PRIx64,
                                        GetSIMDValueAtOffset<uint64>(data, i));
                                break;
                        case SIMD_RENDER_FORMAT_FLOAT:
                                temp.SetToFormat("%.3g",
                                        (double)GetSIMDValueAtOffset<float>(data, i));
                                break;
                        case SIMD_RENDER_FORMAT_DOUBLE:
                                temp.SetToFormat("%.3g",
                                        GetSIMDValueAtOffset<double>(data, i));
                                break;
                }
                _output += temp;
                if (i < count - 1)
                        _output += ", ";
        }
        _output += "}";

        return _output;
}


const char*
UiUtils::SignalNameToString(int32 signal, BString& _output)
{
        #undef DEFINE_SIGNAL_STRING
        #define DEFINE_SIGNAL_STRING(x)                                                                         \
                case x:                                                                                                                 \
                        _output = #x;                                                                                           \
                        return _output.String();

        switch (signal) {
                DEFINE_SIGNAL_STRING(SIGHUP)
                DEFINE_SIGNAL_STRING(SIGINT)
                DEFINE_SIGNAL_STRING(SIGQUIT)
                DEFINE_SIGNAL_STRING(SIGILL)
                DEFINE_SIGNAL_STRING(SIGCHLD)
                DEFINE_SIGNAL_STRING(SIGABRT)
                DEFINE_SIGNAL_STRING(SIGPIPE)
                DEFINE_SIGNAL_STRING(SIGFPE)
                DEFINE_SIGNAL_STRING(SIGKILL)
                DEFINE_SIGNAL_STRING(SIGSTOP)
                DEFINE_SIGNAL_STRING(SIGSEGV)
                DEFINE_SIGNAL_STRING(SIGCONT)
                DEFINE_SIGNAL_STRING(SIGTSTP)
                DEFINE_SIGNAL_STRING(SIGALRM)
                DEFINE_SIGNAL_STRING(SIGTERM)
                DEFINE_SIGNAL_STRING(SIGTTIN)
                DEFINE_SIGNAL_STRING(SIGTTOU)
                DEFINE_SIGNAL_STRING(SIGUSR1)
                DEFINE_SIGNAL_STRING(SIGUSR2)
                DEFINE_SIGNAL_STRING(SIGWINCH)
                DEFINE_SIGNAL_STRING(SIGKILLTHR)
                DEFINE_SIGNAL_STRING(SIGTRAP)
                DEFINE_SIGNAL_STRING(SIGPOLL)
                DEFINE_SIGNAL_STRING(SIGPROF)
                DEFINE_SIGNAL_STRING(SIGSYS)
                DEFINE_SIGNAL_STRING(SIGURG)
                DEFINE_SIGNAL_STRING(SIGVTALRM)
                DEFINE_SIGNAL_STRING(SIGXCPU)
                DEFINE_SIGNAL_STRING(SIGXFSZ)
                DEFINE_SIGNAL_STRING(SIGBUS)
                default:
                        break;
        }

        if (signal == SIGRTMIN)
                _output = "SIGRTMIN";
        else if (signal == SIGRTMAX)
                _output = "SIGRTMAX";
        else
                _output.SetToFormat("SIGRTMIN+%" B_PRId32, signal - SIGRTMIN);

        return _output.String();
}


const char*
UiUtils::SignalDispositionToString(int disposition)
{
        switch (disposition) {
                case SIGNAL_DISPOSITION_IGNORE:
                        return "Ignore";
                case SIGNAL_DISPOSITION_STOP_AT_RECEIPT:
                        return "Stop at receipt";
                case SIGNAL_DISPOSITION_STOP_AT_SIGNAL_HANDLER:
                        return "Stop at signal handler";
                default:
                        break;
        }

        return "Unknown";
}