/////////////////////////////////////////////////////////////////////////////
//
//  procs.cpp - Extension to find Singularity Processes.
//
//  Copyright Microsoft Corporation.  All rights reserved.
//
#include "singx86.h"

HRESULT DumpProcess(ULONG64 address, bool stack, bool detail)
{
    if (address == 0) {
        return S_FALSE;
    }

    HRESULT status = S_FALSE;
#if 0 // TODO: This needs to be implemented!
    HRESULT status = S_OK;
    Thread thread;

    EXT_CHECK(ThreadStruct->Read(address, &thread));

    if (stack) {
        ExtOut("  thread=%p { tid=%03x pid=%03x ebp=%p, esp=%p, eip=%p }\n",
               address,
               (ULONG)(thread.context.threadIndex == 0xffff ? 0xfff : thread.context.threadIndex),
               (ULONG)(thread.context.processId == 0xffff ? 0xfff : thread.context.processId),
               thread.context.threadRecord.spill.ip,
               thread.context.threadRecord.spill.bp,
               thread.context.threadRecord.spill.sp);

        DEBUG_STACK_FRAME frames[10];
        ULONG filled = 0;

        status = g_ExtControl->GetStackTrace(thread.context.threadRecord.spill.bp,
                                             thread.context.threadRecord.spill.sp,
                                             thread.context.threadRecord.spill.ip,
                                             frames,
                                             arrayof(frames),
                                             &filled);

        if (status == S_OK) {
            g_ExtClient->SetOutputLinePrefix("      ");
            g_ExtControl->OutputStackTrace(DEBUG_OUTPUT_NORMAL,
                                           frames,
                                           filled,
                                           detail ? (DEBUG_STACK_FRAME_ADDRESSES |
                                                     DEBUG_STACK_COLUMN_NAMES |
                                                     DEBUG_STACK_SOURCE_LINE) : 0);
        }
    }
    else {
        CHAR name[512];
        ULONG64 displacement = 0;

        status = g_ExtSymbols->GetNameByOffset((ULONG64)thread.context.threadRecord.spill.ip,
                                               name,
                                               arrayof(name),
                                               NULL,
                                               &displacement);
        if (status == S_OK) {
            ExtOut("  thread=%p { tid=%03x pid=%03x ebp=%p, esp=%p, eip=%p } %s+%I64d\n",
                   address,
                   (ULONG)(thread.context.threadIndex == 0xffff ? 0xfff : thread.context.threadIndex),
                   (ULONG)(thread.context.processId == 0xffff ? 0xfff : thread.context.processId),
                   thread.context.threadRecord.spill.ip,
                   thread.context.threadRecord.spill.bp,
                   thread.context.threadRecord.spill.sp,
                   name,
                   (LONG64)displacement);
        }
        else {
            ExtOut("  thread=%p { tid=%03x pid=%03x ebp=%p, esp=%p, eip=%p }\n",
                   address,
                   (ULONG)(thread.context.threadIndex == 0xffff ? 0xfff : thread.context.threadIndex),
                   (ULONG)(thread.context.processId == 0xffff ? 0xfff : thread.context.processId),
                   thread.context.threadRecord.spill.ip,
                   thread.context.threadRecord.spill.bp,
                   thread.context.threadRecord.spill.sp);
        }
    }
  Exit:
#endif
    return status;
}


EXT_DECL(procs) // Defines: PDEBUG_CLIENT Client, PCSTR args
{
    EXT_ENTER();    // Defines: HRESULT status = S_OK;
    bool stack = false;
    bool detail = false;

    while (*args != '\0') {
        if (*args == '-' || *args == '/') {
            args++;
            switch (*args++) {
              case 'd': // detail
              case 'D':
                detail = !detail;
                break;
              case 's': // stack
              case 'S':
                stack = !stack;
                break;
            }
            while (*args != ' ') {
                args++;
            }
        }
        else {
            break;
        }
    }

    ULONG pointerSize = (g_ExtControl->IsPointer64Bit() == S_OK) ? 8 : 4;
    ULONG64 address = 0;
    ULONG64 procs = 0;
    ULONG type = 0;
    ULONG subtype = 0;
    ULONG64 module = 0;

    EXT_CHECK(g_ExtSymbols->GetOffsetByName(kernel_processTable, &address));
    EXT_CHECK(g_ExtSymbols->GetOffsetTypeId(address, &type, &module));
    EXT_CHECK(TraceReadPointer(1, address, &procs));
    ExtVerb("processTable: %p\n", procs);

    CHAR name[512];

    EXT_CHECK(g_ExtSymbols->GetTypeName(module, type, name, arrayof(name), NULL));
    ExtVerb("  processTable type: %s\n", name);
    int len = (int)strlen(name);
    if (len > 3 &&
        name[len-3] == '[' &&
        name[len-2] == ']' &&
        name[len-1] == '*') {
        name[len-3] = '\0';

        EXT_CHECK(g_ExtSymbols->GetTypeId(module, name, &subtype));
        EXT_CHECK(g_ExtSymbols->GetTypeName(module, subtype, name, arrayof(name), NULL));
        ExtVerb("  processTable[] type: %s\n", name);
    }

    ULONG lengthOffset = 0;
    EXT_CHECK(g_ExtSymbols->GetFieldOffset(module, type, "overall_length", &lengthOffset));

    ULONG valuesOffset = 0;
    EXT_CHECK(g_ExtSymbols->GetFieldOffset(module, type, "values", &valuesOffset));

    ULONG length = 0;
    EXT_CHECK(TraceRead(procs + lengthOffset, &length, sizeof(length)));
    ExtOut("processTable: %p [maximum %d entries]\n", procs, length);

    for (ULONG id = 0; id < length; id++) {
        ULONG64 process = 0;

        EXT_CHECK(TraceReadPointer(1, procs + valuesOffset + id * pointerSize, &process));
        if (process != 0) {
            if (detail) {
                ExtOut("      %p\n", process);
            }
            else {
                ExtOut("      %p\n", process);
            }
        }
    }

    EXT_LEAVE();    // Macro includes: return status;
}
