/////////////////////////////////////////////////////////////////////////////
//
//  diagnose.cpp - Extension to find parse Singularity trace log.
//
//  Copyright Microsoft Corporation.  All rights reserved.
//

#include "singx86.h"
#include "diagnose.h"

ULONG pointerSize;

volatile BOOL BreakOnInternalError = FALSE;
extern bool DumpAllEvents;
extern bool DumpStackTraces;
extern bool DumpDescriptions;
bool DumpKernelOnly;

#include "..\..\Kernel\native\Csformat.inc"

//////////////////////////////////////////////////////////////////////////////
//
static HRESULT Usage()
{
    ExtOut("Usage:\n"
           "    !diagnose {options}\n"
           "Options:\n"
           "    Tracing commands:\n"
           "    [-s <SourcePatternFilter>] [-t <TypePatternFilter>] [-o <SortArgument>] [-f <Filter1>] [-f <Filter2>] [...]\n"
           "\n"
           "    Filter format:\n"
           "        -f <Field_Name>[<=,<,>,!> <Field_Value>]\n"
           "        See below sample usages. If no value is specified, it will select all types\n"
           "        defining that field\n"
           "\n"
           "    Sample commands:\n"
           "        !diagnose -n : dumps a sumary of basic types and sources available in the system\n"
           "        !diagnose -t *: Type filtering. The output will organize all entries by type\n"
           "                     : all events of all types will be displayed\n"
           "        !diagnose -s *Tracing* -t *LOG*: Same as above, but only types which will have the string\n"
           "                         : LOG anywhere in the name will be selected\n"
           "        !diagnose -t *LOG* -o Timestamp : Same as above, but the output will have the entries\n"
           "                                       : sorted by the column Timestamp. Any numeric column\n"
           "                                       : name is alowed for sorting. Strings not yet ...\n"
           "        !diagnose -t *LOG* -f Msg=\"*alarm*\" : Filter all entries of type containing LOG\n"
           "                                       : and arguments defined as Msg to contain alarm\n"
           "                                       : substring. The output can be combined with\n"
           "                                       : the sort option or other filtering options\n"
           "        !diagnose -t *LOG* -f Size>10 -f Size<40: Filter all entries of size between\n"
           "                                       : (10,40). Multiple conditions on the same field\n"
           "                                       : are allowed\n"
           "    Garbage collector commands:\n"
           "    -g <prefix>           : Write all profile logs that have been collected in files\n"
           "                            named based upon provided prefix\n"
           "    -ek <MemSize> [break] : Enable kernel GC profiling with a buffer of specified size in MBytes.\n"
           "                            Optionally break into the debugger before recycling buffers\n"
           "    -es <MemSize> [break] : As above, but for SIP GC profiling rather than Kernel GC profiling\n"
           "    -dk                   : Disable the kernel GC profiling\n"
           "    -ds                   : Disable the SIP GC profiling. Applies only for new SIPs\n"
          );
    return S_FALSE;
}

//
//  EVENTING AND TRACING SUPPORT
//  To keep consistency with the Singularity structures
//  the code leverages the same definitions present in the SystemEvents.inl file
//  TODO: This can be improved further to take less dependencies with valid symbols for kernel

#define TYPE_UIntPtr ULONG64
#define TYPE_uint8 ULONG64
#define TYPE_uint16 ULONG64
#define TYPE_uint32 ULONG64
#define TYPE_uint64 ULONG64
#define TYPE_PCHAR ULONG64
#define TYPE_BOOL ULONG64

//  Define the structure

#define DECLARE_STRUCTURE_BEGIN(x,d) struct S##x {
#define DECLARE_STRUCTURE_END(x)   };
#define DECLARE_FIELD(c,t,n)   t n;
#define DECLARE_SPECIAL_FIELD(c,t,n) ULONG64 n;
#define _CONSTANT_DEFINITIONS 1
#define DECLARE_EXTENDED_ARRAY_FIELD(c,t,n) ULONG64 n;
#define DECLARE_GENERIC_FIELD(s,t,sz,t1,n) ULONG64 n;

    #include "..\..\Kernel\native\SystemEvents.inl"

// Declare the fields descriptors

#define DECLARE_STRUCTURE_BEGIN(x,d) FieldType _Fields##x[] = {
#define DECLARE_STRUCTURE_END(x)    FIELDEND(), };

#define DECLARE_FIELD(c,t,n) { NULL, #n, offsetof(S##c,##n), 0, 0, 0, 0 },
#define DECLARE_GENERIC_FIELD(c,t,sz,t1,n) { NULL, #n, offsetof(S##c,##n), 0, 0, 0, 0 },
#define DECLARE_SPECIAL_FIELD DECLARE_FIELD
#define DECLARE_EXTENDED_ARRAY_FIELD DECLARE_FIELD

    #include "..\..\Kernel\native\SystemEvents.inl"


// Instantiate the log object for each defined structure.

#define DECLARE_STRUCTURE_BEGIN(x,d) StructType Struct##x= StructType("_"#x,sizeof(S##x), _Fields##x);

    #include "..\..\Kernel\native\SystemEvents.inl"


int gFieldTypeSizes[] = {0,1,1, 2, 2, 4, 4, 8, 8, 4, 4, 0};

char * gTypeNames[] = {
    "unknown",
    "int8",
    "uint8",
    "int16",
    "uint16",
    "int32",
    "uint32",
    "int64",
    "uint64",
    "IntPtr",
    "UIntPtr",
    "OutOfRangeType"};


//  Utility functions to access the fields and types definitions

int GetPtrSize()
{
    if (g_ExtControl->IsPointer64Bit() == S_OK) {

        return 8;
    }
    return 4;
}

int GetFieldSize(int type)
{
    if (type < sizeof(gFieldTypeSizes) / sizeof(gFieldTypeSizes[0])) {

        if ((type == FIELD_TYPE__IntPtr) || (type == FIELD_TYPE__UIntPtr)) {

            return GetPtrSize();
        }

        return gFieldTypeSizes[type];
    }

    if (type & FIELD_TYPE_VARIABLE_SIZE) {

        return sizeof(USHORT);
    }

    return 0;
}

char * GetFieldTypeName(int type)
{
    if (type < sizeof(gTypeNames) / sizeof(gTypeNames[0])) {

        return gTypeNames[type];
    }

    if (type & FIELD_TYPE_VARIABLE_ANY_STRING) {

        return "String";
    }

    if (type & FIELD_TYPE__arrayType) {

        return "Array";
    }

    return "";
}

bool MatchPatternString(PatternMatch matchMode, char * p, char* pattern)
{
    switch (matchMode) {

        case exact:
            return (_stricmp(p, pattern) == 0);
            break;
        case anywhere:
            return (strstr(p, pattern) != NULL);
            break;
        case begin:
            return (strstr(p, pattern) == p);
            break;
    }

    return FALSE;
}

char * ReadToken(PCSTR& args)
{

    char * arg = (char *)args;
    char fn[256];
    char *tmpChar = fn;
    char *limit = fn + sizeof(fn) - 1;

    SKIP_WHITESPACES(arg);

    while (*arg && (strchr("=<>! ",*arg) == NULL) && (tmpChar < limit)) {

        *tmpChar++ = *arg++;
    }
    *tmpChar = 0;

    SKIP_WHITESPACES(arg);
    args = arg;
    return _strdup(fn);
}


//  Local buffers and constants for extension


char EntryBuffer[4096];
ULONG_PTR HeaderSize = 0;
ULONG_PTR Stacksize = 0;
ULONG_PTR MetadataSize = 0;
ULONG64 ReadSize;
EventTypeEntry * SystemHeaderType = NULL;
EventTypeEntry * SystemTypeDescriptor = NULL;

UINT64 GetStackTrace(int index) {

    if (Stacksize == 0) return 0;

    if (pointerSize == sizeof(ULONG)) {

        int pos = index * sizeof(ULONG);

        if ((pos + sizeof(ULONG)) > Stacksize) return 0;

        ULONG * ptr = (ULONG * )(EntryBuffer + StructMEMORY_HEADER.size + pos);

        return *ptr;

    } else {

        int pos = index * sizeof(ULONG64);

        if ((pos + sizeof(ULONG64)) > Stacksize) return 0;

        ULONG64 * ptr = (ULONG64 * )(EntryBuffer + StructMEMORY_HEADER.size + pos);

        return *ptr;
    }

    return 0;
}

//
//  VariableArray class implementation
//

VariableArray::VariableArray(){

    Size = 1024;
    Array = (UINT64 *)malloc(Size * sizeof(UINT64));
    InUse = 0;
}

VariableArray::~VariableArray()
{
    if (Array != NULL) {

        free(Array);
    }
}

bool VariableArray::Extend()
{
    int newSize = Size * 2;
    if (newSize > 1024*1024) {

        newSize = Size + 1024*1024;
    }

    UINT64 *newArray = (UINT64 *)malloc(newSize * sizeof(UINT64));

    if (newArray == NULL) {

        return FALSE;
    }

    memcpy(newArray, Array, sizeof(UINT64) * Size);

    Size = newSize;
    free(Array);
    Array = newArray;
    return TRUE;
}

bool VariableArray::Add(UINT64 value) {

    if (InUse >= Size) {

        if (!Extend()) return FALSE;
    }

    Array[InUse++] = value;
    return TRUE;
}

//
//  Symbolic enum and flag support
//

EnumType::~EnumType()
{
    while (ValuesLists) {
        SymbolicValue * crt = ValuesLists;
        ValuesLists = ValuesLists->Next;
        delete crt;
    }

    if (Name) free (Name);
}

void EnumType::AddSymbolicValue(SymbolicValue * symValue)
{
    if (symValue->FlagChar) {
        NumFlags += 1;
        FlagsMask += symValue->Value;
    } else {

        int len = (int)strlen(symValue->Name);
        if (PrintWidth < len) {

            PrintWidth = len;
        }
    }

    symValue->Next = ValuesLists;
    ValuesLists = symValue;
}

int EnumType::GetFieldPrintWidth()
{
    if (FlagsMask) {

        return NumFlags + 2 + PrintWidth;

    } else {
        return PrintWidth;
    }
}

ULONG64 EnumType::GetFieldNumericValue(int offset) {

    switch (BasicType) {

        case FIELD_TYPE__int8:
        case FIELD_TYPE__uint8:
            return *((BYTE *)(EntryBuffer + MetadataSize + offset));

        case FIELD_TYPE__int16:
        case FIELD_TYPE__uint16:
            return *((USHORT *)(EntryBuffer + MetadataSize + offset));

        case FIELD_TYPE__int32:
        case FIELD_TYPE__uint32:
            return *((UINT *)(EntryBuffer + MetadataSize + offset));

        case FIELD_TYPE__int64:
        case FIELD_TYPE__uint64:
            return *((UINT64 *)(EntryBuffer + MetadataSize + offset));

        case FIELD_TYPE__IntPtr:
        case FIELD_TYPE__UIntPtr:
            return *((UINT_PTR *)(EntryBuffer + MetadataSize + offset));

    }

    // Non-numeric values return 0
    return 0;
}


int EnumType::PrintValue(int printWidth, int offset)
{
    ULONG64 value;

    value = GetFieldNumericValue(offset);
    ExtOut("  ");

    if (FlagsMask) {

        for (SymbolicValue * crt = ValuesLists; crt != NULL; crt = crt->Next) {

            if (crt->Value & value) {

                ExtOut("%c",crt->FlagChar);
            } else {
                ExtOut(".",crt->FlagChar);
            }
        }
    }

    if (PrintWidth > 0) {

        value &= ~FlagsMask;

        for (SymbolicValue * crt = ValuesLists; crt != NULL; crt = crt->Next) {

            if (crt->Value == value) {

                char buff[20];
                _snprintf(buff, sizeof(buff), "%%%lds", printWidth);
                ExtOut(buff, crt->Name);
            }
        }
    }

    return 0;
}

PCHAR EnumType::GetStringSymbol(int offset)
{
    ULONG64 value;

    value = GetFieldNumericValue(offset);

    if (FlagsMask) {

        CHAR buffer[65];
        int crtPos = 0;

        for (SymbolicValue * crt = ValuesLists; crt != NULL; crt = crt->Next) {

            if (crt->Value & value) {

                buffer[crtPos++] = crt->FlagChar;

            } else {
                buffer[crtPos++] = '.';
            }
        }

        buffer[crtPos] = 0;
        return _strdup(buffer);
    }

    if (PrintWidth > 0) {

        value &= ~FlagsMask;

        for (SymbolicValue * crt = ValuesLists; crt != NULL; crt = crt->Next) {

            if (crt->Value == value) {

                return _strdup(crt->Name);
            }
        }
    }

    return _strdup("???");
}

//
//  FieldEntry class implementation
//

FieldEntry::FieldEntry()
{
    Name=NULL;
    PrintWidth = 0;
    SymType = NULL;
}

FieldEntry::~FieldEntry()
{
    if (Name) {
        free(Name);
    }
}

int FieldEntry::GetArraySize(void * buffer)
{
    if ((Type & FIELD_TYPE_VARIABLE_SIZE) == 0) {

        //  Not a variable size array. return size 0

        return 0;
    }

    USHORT length = *(USHORT *)buffer;
    int basicType = Type & ~FIELD_TYPE_VARIABLE_SIZE;

    return length / GetFieldSize(basicType);
}

ULONG64 FieldEntry::GetFieldNumericValue(void * buffer, int index) {

    if ((Type & FIELD_TYPE_VARIABLE_SIZE) == 0) {

        //  Not a variable size array. return size 0

        return 0;
    }

    USHORT length = *(USHORT *)buffer;
    int basicType = Type & ~FIELD_TYPE_VARIABLE_SIZE;

    int count = length / GetFieldSize(basicType);

    if ((index < 0) || (index >= count)) {
        return 0;
    }

    buffer = (UCHAR*)buffer + sizeof(USHORT) + index * GetFieldSize(basicType);

    switch (basicType) {

        case FIELD_TYPE__int8:
        case FIELD_TYPE__uint8:
            return *((BYTE *)(buffer));

        case FIELD_TYPE__int16:
        case FIELD_TYPE__uint16:
            return *((USHORT *)(buffer));

        case FIELD_TYPE__int32:
        case FIELD_TYPE__uint32:
            return *((UINT *)(buffer));

        case FIELD_TYPE__int64:
        case FIELD_TYPE__uint64:
            return *((UINT64 *)(buffer));

        case FIELD_TYPE__IntPtr:
        case FIELD_TYPE__UIntPtr:
            return *((UINT_PTR *)(buffer));

    }

    // Non-numeric values return 0
    return 0;
}



ULONG64 FieldEntry::GetFieldNumericValue() {

    if ((Type != FIELD_TYPE_GENERIC_TYPE) && (Type & FIELD_TYPE_VARIABLE_ANY_STRING)) {

        return *((USHORT *)(EntryBuffer + MetadataSize + Offset));
    }

    switch (Type) {

        case FIELD_TYPE__int8:
        case FIELD_TYPE__uint8:
            return *((BYTE *)(EntryBuffer + MetadataSize + Offset));

        case FIELD_TYPE__int16:
        case FIELD_TYPE__uint16:
            return *((USHORT *)(EntryBuffer + MetadataSize + Offset));

        case FIELD_TYPE__int32:
        case FIELD_TYPE__uint32:
            return *((UINT *)(EntryBuffer + MetadataSize + Offset));

        case FIELD_TYPE__int64:
        case FIELD_TYPE__uint64:
            return *((UINT64 *)(EntryBuffer + MetadataSize + Offset));

        case FIELD_TYPE__IntPtr:
        case FIELD_TYPE__UIntPtr:
            return *((UINT_PTR *)(EntryBuffer + MetadataSize + Offset));

        case FIELD_TYPE_GENERIC_TYPE:
        {
            if (SymType == NULL) {

                SymType = Controller->FindEnum(TypeKey);

                EventTypeEntry * desc = Controller->FindType(ParentKey);

                if (desc) {

                    desc->UpdateFieldsOffsets();
                }
            }

            if (SymType != NULL) {

                return SymType->GetFieldNumericValue(Offset);
            }
        }
        break;

    }

    // Non-numeric values return 0
    return 0;
}

ULONG64 FieldEntry::PrintValue(EventTypeEntry * type)
{
    if (PrintWidth == 0) {

        GetFieldPrintWidth();
    }

    if (Type == FIELD_TYPE_GENERIC_TYPE) {

        if (SymType == NULL) {

            SymType = Controller->FindEnum(TypeKey);

            EventTypeEntry * desc = Controller->FindType(ParentKey);

            if (desc) {

                desc->UpdateFieldsOffsets();
            }
        }

        if (SymType != NULL) {

            SymType->PrintValue(PrintWidth, Offset);

        }
        return 0;
    }

    if (Type & FIELD_TYPE_VARIABLE_ANY_STRING) {

        int index = (int)GetFieldNumericValue();
        char * str = type->GetExtendedString(index);
        if (str) ExtOut(" \"%s\" ",str);
        return 0;
    }

    char format[20];


    switch (Type) {

        case FIELD_TYPE__int8:
        case FIELD_TYPE__uint8:
             _snprintf(format, sizeof(format), "%%%ldx", PrintWidth);
            ExtOut(format,(int)(*((byte *)(EntryBuffer + MetadataSize + Offset))));
        break;

        case FIELD_TYPE__int16:
        case FIELD_TYPE__uint16:
             _snprintf(format, sizeof(format), "%%%ldx", PrintWidth);
            ExtOut(format,(int)*((USHORT *)(EntryBuffer + MetadataSize + Offset)));
        break;

        case FIELD_TYPE__int32:
        case FIELD_TYPE__uint32:
             _snprintf(format, sizeof(format), "%%%ldx", PrintWidth);
            ExtOut(format, *((int *)(EntryBuffer + MetadataSize + Offset)));
        break;

        case FIELD_TYPE__IntPtr:
        case FIELD_TYPE__UIntPtr:
            ExtOut("  %p",*((UINT_PTR *)(EntryBuffer + MetadataSize + Offset)));
        break;

        case FIELD_TYPE__int64:
        case FIELD_TYPE__uint64:
             _snprintf(format, sizeof(format), "%%%ldI64x", PrintWidth);
            ExtOut(format, *((UINT64 *)(EntryBuffer + MetadataSize + Offset)));
        break;

    }

    // Non-numeric values return 0
    return 0;
}

int FieldEntry::GetFieldPrintWidth()
{
    if ((Type != FIELD_TYPE_GENERIC_TYPE) && (Type & FIELD_TYPE_VARIABLE_ANY_STRING)) {

        return 30;
    }

    if (PrintWidth > 0) return PrintWidth;

    switch (Type) {

        case FIELD_TYPE__int8:
        case FIELD_TYPE__uint8:
            PrintWidth = 4;
        break;

        case FIELD_TYPE__int16:
        case FIELD_TYPE__uint16:
            PrintWidth = 6;
        break;

        case FIELD_TYPE__int32:
        case FIELD_TYPE__uint32:
            PrintWidth = 10;
        break;

        case FIELD_TYPE__int64:
        case FIELD_TYPE__uint64:
            PrintWidth = 18;
        break;

        case FIELD_TYPE__IntPtr:
        case FIELD_TYPE__UIntPtr:
            PrintWidth = GetPtrSize() + 6;
        break;

        case FIELD_TYPE_GENERIC_TYPE:
        {
            if (SymType == NULL) {

                SymType = Controller->FindEnum(TypeKey);
                EventTypeEntry * desc = Controller->FindType(ParentKey);

                if (desc) {

                    desc->UpdateFieldsOffsets();
                }
            }

            if (SymType != NULL) {

                PrintWidth = SymType->GetFieldPrintWidth() + 2;
            }
        }
        break;

    }

    if ((PrintWidth > 0) && ((strlen(Name) + 1) > PrintWidth)) {

        PrintWidth = (unsigned int)strlen(Name) + 1;
    }

    return PrintWidth;
}


//
//  StringPattern class implementation
//

StringPattern::StringPattern()
{
    Pattern = NULL;
}

StringPattern::~StringPattern()
{
    Cleanup();
}

void StringPattern::Cleanup()
{
    if (Pattern) {

        free(Pattern);
    }
    Pattern = NULL;
}

bool StringPattern::IsMatch(char * p)
{
    return MatchPatternString(MatchMode, p, Pattern);
}

char * StringPattern::ParseInput(char * arg)
{
    char fn[256];
    char *tmpChar = fn;
    char *limit = fn + sizeof(fn) - 1;
    char endChar = ' ';

    Cleanup();

    SKIP_WHITESPACES(arg);

    MatchMode = exact;

    if (*arg == '\"') {

        endChar = '\"';
        arg++;
    }

    if (*arg == '*') {

        MatchMode = anywhere;
        arg++;
    }

    while (*arg && (*arg != endChar) && (tmpChar < limit)) {

        if (*arg != '*') {

            *tmpChar++ = *arg++;

        } else {
            arg++;

            if (MatchMode == exact) {

                MatchMode = begin;
            }
        }
    }

    *tmpChar = 0;

    Pattern = _strdup(fn);

    if (*arg == '\"') {

        arg++;
    }

    SKIP_WHITESPACES(arg);

    return arg;
}

//
//  FieldFilter class implementation
//

FieldFilter::FieldFilter()
{
    FieldNamePattern = NULL;
    StringValuePattern = NULL;
}

FieldFilter::~FieldFilter()
{
    Cleanup();
}

void FieldFilter::Cleanup()
{
    if (FieldNamePattern) free(FieldNamePattern);
    if (StringValuePattern) free(StringValuePattern);

    FieldNamePattern= NULL;
    StringValuePattern = NULL;
    NumericValuePattern = 0;
}

FieldFilter::FieldFilter(PatternMatch matchMode,
            char * fieldPattern,
            FilterOperation op,
            UINT64 value,
            char * strValue)
{
    FieldMatchMode = matchMode;
    Operation = op;
    FieldNamePattern = fieldPattern;
    NumericValuePattern = value;
    StringValuePattern = strValue;
}


bool FieldFilter::MatchFieldName(char * p)
{
    return MatchPatternString(FieldMatchMode, p, FieldNamePattern);
}

bool FieldFilter::MatchFieldValue(INT64 value)
{
    switch (Operation) {
        case all:
            return TRUE;
        case equal:
            return (NumericValuePattern == value);
        case less:
            return (NumericValuePattern > value);
        case greater:
            return (NumericValuePattern < value);
        case different:
            return (value != NumericValuePattern);
    }

    return FALSE;
}

bool FieldFilter::MatchFieldValue(char * value)
{
    switch (Operation){
        case all:
            return TRUE;
        case equal:
            return (StringValuePattern != NULL) && (_stricmp(value, StringValuePattern) == 0);
        case less:
            return (StringValuePattern != NULL) && (_stricmp(value, StringValuePattern) < 0);
        case greater:
            return (StringValuePattern != NULL) && (_stricmp(value, StringValuePattern) > 0);
        case contains:
            return (StringValuePattern != NULL) && (strstr(value, StringValuePattern) != NULL);
        case different:
            return (StringValuePattern != NULL) && (strstr(value, StringValuePattern) == NULL);
    }

    return FALSE;
}

char * FieldFilter::ParseInput(char * arg)
{
    char fn[256];
    char *tmpChar = fn;
    char *limit = fn + sizeof(fn) - 1;

    Cleanup();

    SKIP_WHITESPACES(arg);

    FieldMatchMode = exact;
    Operation = all;

    if (*arg == '*') {

        FieldMatchMode = anywhere;
        arg++;
    }

    while (*arg && (strchr("=<>!",*arg) == NULL) && (tmpChar < limit)) {

        if (*arg != '*') {

            *tmpChar++ = *arg++;

        } else {
            arg++;

            if (FieldMatchMode == exact) {

                FieldMatchMode = begin;
            }
        }
    }
    *tmpChar = 0;

    FieldNamePattern = _strdup(fn);

    SKIP_WHITESPACES(arg);

    if (*arg == 0) {

        return arg;
    }

    if (strchr("=<>!",*arg)) {

        switch (*arg) {

            case '=' :
                Operation = equal;
                break;
            case '<' :
                Operation = less;
                break;
            case '>' :
                Operation = greater;
                break;
            case '!' :
                Operation = different;
                break;
        }

        arg++;

        if (*arg == '\"') {

            arg++;

            if (*arg == '*') {

                Operation = contains;
                arg++;
            }
            tmpChar = fn;

            while (*arg  && (*arg != '\"') && (tmpChar < limit)) {

                if (*arg != '*') {


                    *tmpChar++ = *arg++;

                } else {
                    arg++;
                    Operation = contains;
                }
            }
            *tmpChar = 0;
            if (*arg) arg++;
            StringValuePattern = _strdup(fn);
        } else if (*arg) {

            PCSTR tmpChar = (PCSTR)arg;

            NumericValuePattern = GetValue(tmpChar, true);
            arg = (char*)tmpChar;
        }
    }

    SKIP_WHITESPACES(arg);

    return arg;
}

//  EventTypeEntry class implementation

EventTypeEntry::EventTypeEntry()
{
    Name=NULL;
    Descriptor = NULL;
    size = 0;
    Key = 0;
    NumFields = 0;
    filterCount = 0;
    FilterApplied = FALSE;
}

EventTypeEntry::~EventTypeEntry() {

    for (int i = 0; i < NumFields; i++) {
        delete Fields[i];
    }

    if (Name) {
        free(Name);
    }

    if (Descriptor) {
        free(Descriptor);
    }
}

bool EventTypeEntry::TestFilter(FieldFilter * newFilter)
{
    FilterApplied = TRUE;

    if (filterCount >= MAX_FILTERS) {
        return false;
    }

    // Test whether any of the fields inside this type entry would match
    // at least the fieldNamePattern;

    for (int i = 0; i < NumFields; i++) {

        FieldEntry * field = Fields[i];

        if (newFilter->MatchFieldName(field->Name)) {

            return TRUE;
        }
    }

    return FALSE;
}

FieldEntry * EventTypeEntry::GetField(PCHAR name)
{
    for (int i = 0; i < NumFields; i++) {

        FieldEntry * field = Fields[i];

        if (_stricmp(field->Name, name) == 0) {

            return field;
        }
    }

    return NULL;
}

int EventTypeEntry::GetFieldIndex(PCHAR name)
{
    for (int i = 0; i < NumFields; i++) {

        FieldEntry * field = Fields[i];

        if (_stricmp(field->Name, name) == 0) {

            return i;
        }
    }

    return -1;
}


bool EventTypeEntry::ApplyFilter(FieldFilter * newFilter)
{
    FilterApplied = TRUE;

    if (filterCount >= MAX_FILTERS) {
        return false;
    }

    // Test whether any of the fields inside this type entry would match
    // at least the fieldNamePattern;

    for (int i = 0; i < NumFields; i++) {

        FieldEntry * field = Fields[i];

        if (newFilter->MatchFieldName(field->Name)) {

            fieldsFilterList[filterCount] = field;
            filterList[filterCount++] = newFilter;
            return TRUE;
        }
    }

    return FALSE;
}

void EventTypeEntry::ClearFilters()
{
    filterCount = 0;
}

void EventTypeEntry::AddNewField(FieldEntry * field) {
    if (NumFields < MAX_FIELDS) {

        Fields[NumFields++] = field;
    }
}

void EventTypeEntry::UpdateFieldsOffsets() {

    int offset = 0;
    ExtendedFieldsCount = 0;

    for (int i = 0; i < NumFields; i++) {

        FieldEntry * field = Fields[i];

        field->ExtendedFieldIndex = 0;


        if (field->Type == FIELD_TYPE_GENERIC_TYPE) {

            if (field->SymType != NULL) {

                if (field->Offset == 0) {
                    field->Offset = offset;
                }
                field->Size = GetFieldSize(field->SymType->BasicType);
                offset = field->Offset + GetFieldSize(field->SymType->BasicType);
            }

        } else {

            if (field->Offset == 0) {
                field->Offset = offset;
            }
            field->Size = GetFieldSize(field->Type);
            offset = field->Offset + GetFieldSize(field->Type);
        }

        if (field->Type & FIELD_TYPE_VARIABLE_SIZE) {

            field->ExtendedFieldIndex = ExtendedFieldsCount++;
        }
    }
    size = offset;
}

ULONG_PTR SWITCH_TO_METADATA_CURSOR()
{
    ULONG_PTR savedMetadataSize = MetadataSize;
    MetadataSize = 0;
    return savedMetadataSize;
}

void RESTORE_CURSOR(ULONG_PTR savedCursor)
{
    MetadataSize = savedCursor;
}


bool EventTypeEntry::FilterMatch()
{
    if ((SystemHeaderType != NULL) && (SystemHeaderType != this)) {

        ULONG_PTR savedMetadataSize = MetadataSize;
        MetadataSize = 0;
        bool success = SystemHeaderType->FilterMatch();
        MetadataSize = savedMetadataSize;

        if (success && (filterCount == 0)) {

            return TRUE;
        }
    }

    if (FilterApplied && (filterCount == 0)) {

        return FALSE;
    }


    for (int i = 0; i < filterCount; i++) {

        FieldEntry * field = fieldsFilterList[i];

        if (field->Type & FIELD_TYPE_VARIABLE_ANY_STRING) {

            int index = (int)field->GetFieldNumericValue();

            char * str = NULL;

            if (index) {

                str = GetExtendedString(index);
            }

            if (!filterList[i]->MatchFieldValue(str)) {
                return FALSE;
            }

        } else {

            if (!filterList[i]->MatchFieldValue(field->GetFieldNumericValue())) {
                return FALSE;
            }
        }
    }

    return TRUE;
}

static int argIndexOffset = 0;

int PrintEventField(void * context, char *pszOut, int bufferSize, int aln, int wid, char fmt, int argIdx)
{
    EventTypeEntry * typeEntry = (EventTypeEntry *) context;
    argIdx += argIndexOffset;

    if (argIdx >= typeEntry->NumFields) {

        return 0;
    }

    FieldEntry * field = typeEntry->Fields[argIdx];
    int retValue = 0;
    char * str = NULL;

    if (field->Type == FIELD_TYPE_GENERIC_TYPE) {

        //  Force fetching the symtype if needed
        field->GetFieldNumericValue();

        PCHAR str = field->SymType->GetStringSymbol(field->Offset);

        if (str != NULL) {

            if (wid > 0) {
                if (aln < wid) {
                    aln = wid;
                }
                retValue = _snprintf(pszOut, bufferSize, "%*.*s", aln, wid, str);
            }
            else {
                retValue = _snprintf(pszOut, bufferSize, "%*s", aln, str);
            }

            free(str);
        }

    } else if (field->Type & FIELD_TYPE_VARIABLE_ANY_STRING) {

        int index = (int)field->GetFieldNumericValue();
        char * str = typeEntry->GetExtendedString(index);

        if (wid > 0) {
            if (aln < wid) {
                aln = wid;
            }
            retValue = _snprintf(pszOut, bufferSize, "%*.*s", aln, wid, str);
        }
        else {
            retValue = _snprintf(pszOut, bufferSize, "%*s", aln, str);
        }

    } else {

        if (aln < wid) {
            aln = wid;
        }

        if (fmt == 'x') {
            if (wid > 0) {
                retValue = _snprintf(pszOut, bufferSize, "%0*x", aln,
                            field->GetFieldNumericValue());
            }
            else {
                retValue = _snprintf(pszOut, bufferSize, "%*x", aln,
                            field->GetFieldNumericValue());
            }
        }
        else {
            retValue = _snprintf(pszOut, bufferSize, "%*d", aln,
                        field->GetFieldNumericValue());
        }

    }

    return retValue;
}

void EventTypeEntry::Format(int formatStringIndex, int argOffset)
{
    CHAR msg[512];
    argIndexOffset = argOffset + 1;
    char * text = GetExtendedString(formatStringIndex);

    FormatCSOutput(this, text, msg, sizeof(msg),PrintEventField, NULL);
    ExtOut("%s", msg);
}


void EventTypeEntry::PrintDescription()
{
    if (Descriptor == NULL) return;

    CHAR msg[512];
    argIndexOffset = 0;

    FormatCSOutput(this, Descriptor, msg, sizeof(msg),PrintEventField, NULL);
    ExtOut("%s", msg);
}

void EventTypeEntry::WalkFields(EventingEnumerator * enumerator) {

    if ((SystemHeaderType != NULL) && (SystemHeaderType != this) && (MetadataSize != 0)) {

        ULONG_PTR savedMetadataSize = MetadataSize;
        MetadataSize = 0;
        SystemHeaderType->WalkFields(enumerator);
        MetadataSize = savedMetadataSize;
    }

    for (int i = 0; i < NumFields; i++) {

        enumerator->FieldCallout(this, Fields[i]);
    }
}

char * GetExtendedStringUnsafe(int offset, int index)
{
    offset = ROUND_UP_TO_POWER2(offset, pointerSize);

    for (int i = 1; i < index; i++) {

        USHORT length;

        // copy the structure localy since it may not be aligned.

        memcpy(&length, EntryBuffer + offset, sizeof(USHORT));
        offset += length + sizeof(USHORT);
    }

    char * ptr = EntryBuffer + offset + sizeof(USHORT); // skip also the size of the string
    return ptr;
}


char * EventTypeEntry::GetExtendedString(int index)
{
    if ((index > 0) && (index <= ExtendedFieldsCount)) {

        int offset = (int)MetadataSize + size;

        offset = ROUND_UP_TO_POWER2(offset, pointerSize);

        for (int i = 1; i < index; i++) {

            USHORT length;

            // copy the structure localy since it may not be aligned.

            memcpy(&length, EntryBuffer + offset, sizeof(USHORT));
            offset += length + sizeof(USHORT);
        }

        char * ptr = EntryBuffer + offset + sizeof(USHORT); // skip also the size of the string
        return ptr;
    }

    return NULL;
}

void * EventTypeEntry::GetFieldArray(int index)
{
    if ((index >= 0) && (index <= ExtendedFieldsCount)) {

        int offset = (int)MetadataSize + size;

        offset = ROUND_UP_TO_POWER2(offset, pointerSize);

        for (int i = 1; i < index; i++) {

            USHORT length;

            // copy the structure localy since it may not be aligned.

            memcpy(&length, EntryBuffer + offset, sizeof(USHORT));
            offset += length;
        }

        return (char *)EntryBuffer + offset;
    }

    return NULL;
}


void EventTypeEntry::EnumerateEntries(EventingEnumerator * enumerator, UINT64 address, ULONG count) {

    ULONG readSize = size;

    if (readSize > sizeof(EntryBuffer)) {

        readSize = sizeof(EntryBuffer);
    }

    for (ULONG j = 0; j < count; j++) {

        TraceRead(address, EntryBuffer, (ULONG)readSize);
        MetadataSize = 0;

        if (enumerator->ActiveEntryCallout(this, FALSE)) {

            for (int i = 0; i < NumFields; i++) {

                enumerator->FieldCallout(this, Fields[i]);
            }
            enumerator->ActiveEntryCallout(this, TRUE);
        }

        address += size;
    }
}

//
//  SourceEntry class implementation
//

SourceEntry::SourceEntry()
{
    Name=NULL;
}

SourceEntry::~SourceEntry()
{

    if (Name) {
        free(Name);
    }
}

void SourceEntry::EnumerateEntries(EventingEnumerator * enumerator, ControllerObject *controller) {

    if (StorageHandle == 0) {

        if (enumerator->ActiveSourceCallout(this, FALSE, controller)) {

            EventTypeEntry * typeEntry = controller->FindType(EventTypeHandle);

            if (typeEntry != NULL) {

                typeEntry->EnumerateEntries(enumerator,
                    DebuggerBufferAddress,
                    (ULONG)Count);
            }

            enumerator->ActiveSourceCallout(this, TRUE, controller);
        }
    }
}

//
//  EventingEnumerator class implementation
//


EventingEnumerator::EventingEnumerator()
{
    FiltersCount = 0;
    Filters = NULL;
    SourcePattern = NULL;
    usedStorages = 0;
}

bool EventingEnumerator::AddStorageFilter(ULONG64 storageHandle)
{
    if (usedStorages >= MAX_STORAGE_FILTERS) return false;

    StorageHandles[usedStorages++] = storageHandle;
    return true;
}

bool EventingEnumerator::MatchStorageHandle(ULONG64 storageHandle)
{
    if (SourcePattern == NULL) return true;

    for (int i = 0; i < usedStorages; i++) {

        if (StorageHandles[i] == storageHandle) return true;
    }

    return false;
}


bool EventingEnumerator::ApplyFilters(FieldFilter ** filters, int Count) {

    FiltersCount = Count;
    Filters = filters;
    return TRUE;
}

bool EventingEnumerator::ApplySourceSelector(StringPattern * sourcePattern) {

    SourcePattern = sourcePattern;

    return TRUE;
}

bool EventingEnumerator::TypeCallout(EventTypeEntry * entryDescriptor){

    for (int i = 0; i < FiltersCount; i++) {

        if (!entryDescriptor->ApplyFilter(Filters[i])) {

            // This filter does not apply for the basic type
            // test it against the header

            if (!SystemHeaderType->TestFilter(Filters[i])) {
                return FALSE;
            }
        }
    }

    return TRUE;
}

bool EventingEnumerator::MedataCallout(ControllerObject *controller)
{
    if (SourcePattern) {

        ClearStorageFilters();

        for (int i = 0; i < controller->SourcesCount; i++) {

            SourceEntry *src = controller->RegisteredSources[i];

            if (SourcePattern->IsMatch(src->Name)) {

                AddStorageFilter(src->StorageHandle);
            }
        }
    }

    return TRUE;
}

bool EventingEnumerator::StorageCallout(UINT64 storageAddress, bool finished)
{
    if ((SourcePattern == NULL) || MatchStorageHandle(storageAddress)) return TRUE;

    return FALSE;
}



//
//  Controller objects managements
//

ControllerObject * Controllers[MAX_CONTROLLERS];
int ControllersCount = 0;

ControllerObject * AllocateController(UINT64 handle,
                                      UINT64 contextHandle,
                                      UINT64 storageAddress,
                                      UINT64 storageListHead)
{
    if (ControllersCount >= MAX_CONTROLLERS) {

        return NULL;
    }

    ControllerObject * ctrl = new ControllerObject(handle,
                                                   contextHandle,
                                                   storageAddress,
                                                   storageListHead);

    Controllers[ControllersCount++] = ctrl;
    return ctrl;
}

void FlushControllers()
{
    for (int i = 0; i < ControllersCount; i++) {

        delete Controllers[i];
        Controllers[i] = NULL;
    }

    ControllersCount = 0;
}
ControllerObject::ControllerObject(UINT64 handle,
                                   UINT64 contextHandle,
                                   UINT64 storageAddress,
                                   UINT64 storageListHead)
{
    TypesCount = 0;
    EnumCount = 0;
    SourcesCount = 0;
    ControllerHandle = handle;
    RepositoryAddress = storageAddress;
    StorageListHead = storageListHead;
    ContextHandle = contextHandle;

    for (int i = 0; i < MAX_FIELDS; i++) {

        RegisteredFieldsTypes[i] = NULL;
        RegisteredSymbolicValues[i] = NULL;
    }
}

ControllerObject::~ControllerObject() {

    FlushTypeArray();
    FlushSourceArray();
}

void ControllerObject::AddTempField(FieldEntry * field)
{
    for (int i = 0; i < MAX_FIELDS; i++) {

        if (RegisteredFieldsTypes[i] == NULL) {

            RegisteredFieldsTypes[i] = field;
            return;
        }
    }
    ERRORBREAK("Registering field failed\n");
}

void ControllerObject::AddTempField(SymbolicValue * field)
{
    for (int i = 0; i < MAX_FIELDS; i++) {

        if (RegisteredSymbolicValues[i] == NULL) {

            RegisteredSymbolicValues[i] = field;
            return;
        }
    }
    ERRORBREAK("Registering field failed\n");
}

void ControllerObject::PopulateFields(EnumType * type)
{
    for (int i = MAX_FIELDS - 1; i >= 0 ; i--) {

        if ((RegisteredSymbolicValues[i] != NULL) &&
            (RegisteredSymbolicValues[i]->ParentKey == type->Key)) {

            type->AddSymbolicValue(RegisteredSymbolicValues[i]);
            RegisteredSymbolicValues[i] = NULL;
        }
    }
}


void ControllerObject::PopulateFields(EventTypeEntry * type)
{
    for (int i = MAX_FIELDS - 1; i >= 0 ; i--) {

        if ((RegisteredFieldsTypes[i] != NULL) &&
            (RegisteredFieldsTypes[i]->ParentKey == type->Key)) {

            type->AddNewField(RegisteredFieldsTypes[i]);
            RegisteredFieldsTypes[i] = NULL;
        }
    }

    type->UpdateFieldsOffsets();
}


EventTypeEntry * ControllerObject::FindType(ULONG64 Key)
{
    for (int i = 0; i < TypesCount; i++) {

        if (RegisteredTypes[i]->Key == Key) {
            return RegisteredTypes[i];
        }
    }
    return NULL;
}

void ControllerObject::AddewType(EventTypeEntry * newType)
{
    if (TypesCount < MAX_TYPES) {

        RegisteredTypes[TypesCount++] = newType;
    }
}


EnumType * ControllerObject::FindEnum(ULONG64 Key)
{
    for (int i = 0; i < EnumCount; i++) {

        if (RegisteredEnums[i]->Key == Key) {
            return RegisteredEnums[i];
        }
    }
    return NULL;
}

void ControllerObject::AddNewEnum(EnumType * newEnum)
{
    if (EnumCount < MAX_TYPES) {

        RegisteredEnums[EnumCount++] = newEnum;
    }
}


void ControllerObject::FlushTypeArray()
{

    for (int i = 0; i < TypesCount; i++) {

        delete RegisteredTypes[i];
    }
    TypesCount = 0;

    for (int i = 0; i < EnumCount; i++) {

         delete RegisteredEnums[i];
    }
    EnumCount = 0;

    for (int i = 0; i < MAX_FIELDS; i++) {

         RegisteredFieldsTypes[i] = NULL;
    }


    SystemHeaderType = NULL;
}

SourceEntry * ControllerObject::FindSource(ULONG64 Key)
{
    for (int i = 0; i < SourcesCount; i++) {

        if (RegisteredSources[i]->Key == Key) {
            return RegisteredSources[i];
        }
    }
    return NULL;
}

char * ControllerObject::GetControllerName()
{
    #define CONTROLLER_NAME_PREFIX "ControllerLog{"
    for (int i = 0; i < SourcesCount; i++) {
        if (strstr(RegisteredSources[i]->Name, "ControllerLog{") == RegisteredSources[i]->Name) {

            char * crt = RegisteredSources[i]->Name + strlen("ControllerLog{");
            char tmpname[128];
            char * cursor = tmpname;
            char * end = cursor + sizeof(tmpname) - 1;
            while (*crt && (cursor < end)) {

                if (isalnum(*crt)) {
                    *cursor = *crt;
                } else {
                    if (*crt == '}') {

                        if (char * pos = strchr(crt, ':')) {
                            crt = pos;

                        } else {
                            break;
                        }
                    }
                    *cursor = '_';
                }

                crt++;
                cursor++;
            }
            *cursor = 0;
            return _strdup(tmpname);
        }
    }
    return NULL;
}


void ControllerObject::AddNewSource(SourceEntry * newSource)
{
    if (SourcesCount < MAX_SOURCES) {

        RegisteredSources[SourcesCount++] = newSource;
    }
}

void ControllerObject::FlushSourceArray()
{

    for (int i = 0; i < SourcesCount; i++) {

        delete RegisteredSources[i];
    }

    SourcesCount = 0;
}

void ControllerObject::WalkTypes(EventingEnumerator * enumerator)
{
    for (int i = 0; i < TypesCount; i++) {

        enumerator->TypeCallout(RegisteredTypes[i]);
    }
}

void ControllerObject::WalkSources(EventingEnumerator * enumerator)
{
    for (int i = 0; i < SourcesCount; i++) {

        enumerator->SourcesCallout(RegisteredSources[i]);
    }
}

void ControllerObject::WalkActiveSourcesEntries(EventingEnumerator * enumerator)
{
    for (int i = 0; i < SourcesCount; i++) {

        RegisteredSources[i]->EnumerateEntries(enumerator, this);
    }
}

void ControllerObject::ReadSource(UINT64 entryAddress)
{
    HRESULT status;

    SSOURCE_DESCRIPTOR log;

    EXT_CHECK(StructSOURCE_DESCRIPTOR.Read(entryAddress, &log));

    SourceEntry * sourceEntry;
    sourceEntry = new SourceEntry();

    if (sourceEntry != NULL) {

        sourceEntry->Name = _strdup(EntryBuffer + StructMEMORY_HEADER.size + StructSOURCE_DESCRIPTOR.size);
        sourceEntry->Key = entryAddress - StructMEMORY_HEADER.size;
        sourceEntry->StorageHandle = log.StorageHandle;
        sourceEntry->ControlFlags = (ULONG)log.ControlFlags;
        sourceEntry->EventTypeHandle = log.EventTypeHandle;
        sourceEntry->DebuggerBufferAddress = log.DebuggerBufferAddress;
        sourceEntry->Count = log.Count;
        sourceEntry->EntrySize = log.EntrySize;

        AddNewSource(sourceEntry);
    }

    return ;

Exit:
    //  Error path

    ERRORBREAK("Invalid symbols information for reading source structures\n");
    return ;
}

void ControllerObject::ReadEventDescriptor(UINT64 entryAddress)
{
    HRESULT status;

    SEVENT_DESCRIPTOR log;

    EXT_CHECK(StructEVENT_DESCRIPTOR.Read(entryAddress, &log));

    EventTypeEntry * TypeEntry;
    TypeEntry = new EventTypeEntry();

    if (TypeEntry != NULL) {

        TypeEntry->Key = entryAddress - StructMEMORY_HEADER.size;
        TypeEntry->NumFields = 0;
        TypeEntry->size = StructEVENT_DESCRIPTOR.size;
        TypeEntry->ExtendedFieldsCount = 2;
        TypeEntry->Name = _strdup(TypeEntry->GetExtendedString((int)log.Name));

        if (log.Description > 0) {

            TypeEntry->Descriptor = _strdup(TypeEntry->GetExtendedString((int)log.Description));
        }

        if (SystemHeaderType == NULL) {

            if (_stricmp(TypeEntry->Name, "System.MEMORY_HEADER") == 0) {
                SystemHeaderType = TypeEntry;
            }

        }

        AddewType(TypeEntry);
        PopulateFields(TypeEntry);
    }

    return ;

Exit:
    //  Error path

    ERRORBREAK("Invalid symbols information\n");
    return ;
}

void ControllerObject::ReadEnumDescriptor(UINT64 entryAddress)
{
    HRESULT status;

    SENUM_DESCRIPTOR log;

    EXT_CHECK(StructENUM_DESCRIPTOR.Read(entryAddress, &log));

    EnumType * TypeEntry;
    TypeEntry = new EnumType();

    if (TypeEntry != NULL) {

        TypeEntry->Key = entryAddress - StructMEMORY_HEADER.size;

        TypeEntry->BasicType = (int)log.Type;
        TypeEntry->Name = _strdup(GetExtendedStringUnsafe(
                                    StructMEMORY_HEADER.size + StructENUM_DESCRIPTOR.size,
                                    (int)log.Name));

        AddNewEnum(TypeEntry);
        PopulateFields(TypeEntry);
    }

    return ;

Exit:
    //  Error path

    ERRORBREAK("Invalid symbols information\n");
    return ;
}

void ControllerObject::ReadValueDescriptor(UINT64 entryAddress, UINT64 parent)
{
    HRESULT status;

    SEVENT_VALUE_DESCRIPTOR log;

    EXT_CHECK(StructEVENT_VALUE_DESCRIPTOR.Read(entryAddress, &log));

    SymbolicValue * fieldEntry;
    fieldEntry = new SymbolicValue;

    if (fieldEntry != NULL) {

        fieldEntry->Name = _strdup(GetExtendedStringUnsafe(
                                    StructMEMORY_HEADER.size + StructEVENT_VALUE_DESCRIPTOR.size,
                                    (int)log.Name));
        fieldEntry->Value = (int)log.Value;
        fieldEntry->FlagChar = (UCHAR)log.FlagLetter;
        fieldEntry->ParentKey = parent;

        EnumType * parentType = FindEnum(parent);

        if (parentType == NULL) {

            AddTempField(fieldEntry);

        } else {

            parentType->AddSymbolicValue(fieldEntry);
        }
    }

    return;

Exit:
    //  Error path

    ERRORBREAK("Invalid symbols information\n");
}


void ControllerObject::ReadFieldDescriptor(UINT64 entryAddress, UINT64 parent)
{
    HRESULT status;

    SEVENT_FIELD_DESCRIPTOR log;

    EXT_CHECK(StructEVENT_FIELD_DESCRIPTOR.Read(entryAddress, &log));

    FieldEntry * fieldEntry;
    fieldEntry = new FieldEntry;

    if (fieldEntry != NULL) {

        fieldEntry->Name = _strdup(GetExtendedStringUnsafe(
                                    StructMEMORY_HEADER.size + StructEVENT_FIELD_DESCRIPTOR.size,
                                    (int)log.Name));

        fieldEntry->Offset = (int)log.Offset;
        fieldEntry->Type = (int)log.Type;
        fieldEntry->ParentKey = parent;

        EventTypeEntry * parentType = FindType(parent);

        if (parentType == NULL) {

            AddTempField(fieldEntry);

        } else {

            parentType->AddNewField(fieldEntry);
        }
    }

    return;

Exit:
    //  Error path

    ERRORBREAK("Invalid symbols information\n");
}

void ControllerObject::ReadGenericFieldDescriptor(UINT64 entryAddress, UINT64 parent)
{
    HRESULT status;

    SEVENT_GENERIC_TYPE_DESCRIPTOR log;

    EXT_CHECK(StructEVENT_GENERIC_TYPE_DESCRIPTOR.Read(entryAddress, &log));

    FieldEntry * fieldEntry;
    fieldEntry = new FieldEntry;

    if (fieldEntry != NULL) {

        fieldEntry->Name = _strdup(GetExtendedStringUnsafe(
                                    StructMEMORY_HEADER.size + StructEVENT_GENERIC_TYPE_DESCRIPTOR.size,
                                    (int)log.Name));

        fieldEntry->Offset = (int)log.Offset;
        fieldEntry->Type = FIELD_TYPE_GENERIC_TYPE;
        fieldEntry->ParentKey = parent;
        fieldEntry->TypeKey = log.GenericTypeHandle;
        fieldEntry->SymType = FindEnum(log.Type);
        fieldEntry->Controller = this;

        EventTypeEntry * parentType = FindType(parent);

        if (parentType == NULL) {

            AddTempField(fieldEntry);

        } else {

            parentType->AddNewField(fieldEntry);
        }
    }

    return;

Exit:
    //  Error path

    ERRORBREAK("Invalid symbols information\n");
}


UINT64 ControllerObject::ReadMemoryHeader(EventingEnumerator * enumerator,
                                          UINT64 entryAddress)
{
    HRESULT status;

    SMEMORY_HEADER log;
    ULONG64 CrtOffset;

    EntryHeader header;

    ReadSize = sizeof(EntryBuffer);

    EXT_CHECK(StructMEMORY_HEADER.Read(entryAddress, &log));

    header.address = entryAddress;
    header.size = (int)log.Size;
    header.timestamp = log.Timestamp;
    header.Flags = (int)log.Flags;
    header.TID = (int)log.TID;
    header.Cpu = (int)log.Cpu;

    header.StackSize = 0;
    header.StackAddress = 0;;

    CrtOffset = StructMEMORY_HEADER.size;

    if ((log.Size) < ReadSize) {

        ReadSize = log.Size;
    }

    Stacksize = 0;
    MetadataSize = (ULONG_PTR)CrtOffset;
    EXT_CHECK(TraceRead(entryAddress, EntryBuffer, (ULONG)ReadSize));


    if (log.Flags & RECORD_STACK_TRACES) {

        Stacksize = *(ULONG_PTR*)(EntryBuffer + (ULONG_PTR)CrtOffset) + 1;

        header.StackSize = (int)Stacksize;
        header.StackAddress = entryAddress + CrtOffset;

        Stacksize *= sizeof(ULONG_PTR);
        CrtOffset += Stacksize;
        MetadataSize = (ULONG_PTR)CrtOffset;
    }

    if (log.Flags == RECORD_EVENT_TYPE) {


        // Ignore the other internal structures that are not relevant for this extension

    } else if (log.Flags == RECORD_EVENT_FIELD) {

        // Ignore the other internal structures that are not relevant for this extension


    } else if (log.Flags == RECORD_EVENT_SOURCE) {

        // Ignore the other internal structures that are not relevant for this extension

    } else if (log.Flags & RECORD_EVENT_METADATA_FLAG) {

        // Ignore the other internal structures that are not relevant for this extension

    } else if (log.Flags & RECORD_EVENT_TYPE_ARRAY_FLAG) {

        EventTypeEntry * type = FindType(log.Type);

        if (type != NULL) {
            type->EnumerateEntries(enumerator, entryAddress + CrtOffset, (ULONG)log.TID);
        }

    } else {
        EventTypeEntry * type = FindType(log.Type);
        enumerator->EntryCallout(&header, type);
    }

    return log.Link;

Exit:
    //  Error path

    ERRORBREAK("Invalid symbols information\n");
    return 0;
}

UINT64 ControllerObject::ReadMetadataMemoryHeader(EventingEnumerator * enumerator,
                                                  UINT64 entryAddress)
{
    HRESULT status;

    SMEMORY_HEADER log;
    ULONG64 CrtOffset;

    EntryHeader header;

    ReadSize = sizeof(EntryBuffer);

    EXT_CHECK(StructMEMORY_HEADER.Read(entryAddress, &log));

    header.address = entryAddress;
    header.size = (int)log.Size;
    header.timestamp = log.Timestamp;
    header.Flags = (int)log.Flags;
    header.TID = (int)log.TID;
    header.Cpu = (int)log.Cpu;

    CrtOffset = StructMEMORY_HEADER.size;

    if ((log.Size) < ReadSize) {

        ReadSize = log.Size;
    }

    Stacksize = 0;
    MetadataSize = (ULONG_PTR)CrtOffset;
    EXT_CHECK(TraceRead(entryAddress, EntryBuffer, (ULONG)ReadSize));

    ExtVerb("Reading entry %p\n", entryAddress);

    if (log.Flags & RECORD_STACK_TRACES) {

        Stacksize = *(ULONG_PTR*)(EntryBuffer + (ULONG_PTR)CrtOffset) + 1;
        Stacksize *= sizeof(ULONG_PTR);
        CrtOffset += Stacksize;
        MetadataSize = (ULONG_PTR)CrtOffset;
    }

    if (log.Flags == RECORD_EVENT_TYPE) {

        ReadEventDescriptor(entryAddress + CrtOffset);

    } else if (log.Flags == RECORD_EVENT_FIELD) {

        ReadFieldDescriptor(entryAddress + CrtOffset, log.Type);

    } else if (log.Flags == RECORD_EVENT_SOURCE) {

        ReadSource(entryAddress + CrtOffset);

    } else if (log.Flags == RECORD_EVENT_GENERIC_FIELD) {

        ReadGenericFieldDescriptor(entryAddress + CrtOffset, log.Type);

    } else if (log.Flags == RECORD_EVENT_ENUM) {

        ReadEnumDescriptor(entryAddress + CrtOffset);

    } else if (log.Flags == RECORD_EVENT_VALUE) {

        ReadValueDescriptor(entryAddress + CrtOffset, log.Type);
    }

    return log.Link;

Exit:
    //  Error path

    ERRORBREAK("Invalid symbols information\n");
    return 0;
}


UINT64 ControllerObject::ReadMemoryZone(EventingEnumerator * enumerator, UINT64 zoneAddress)
{
    HRESULT status;

    SMEMORY_ZONE log;

    EXT_CHECK(StructMEMORY_ZONE.Read(zoneAddress, &log));
    UINT64 Blocks = (USHORT)log.ReadyList;

    while (Blocks) {

        Blocks = ReadMetadataMemoryHeader(enumerator, zoneAddress + Blocks);
    }

    Blocks = (USHORT)log.ReadyList;

    while (Blocks) {

        Blocks = ReadMemoryHeader(enumerator, zoneAddress + Blocks);
    }

    return log.Link;

Exit:
    //  Error path

    ERRORBREAK("Invalid symbols information\n");
    return 0;
}

UINT64 ControllerObject::ReadStorage(EventingEnumerator * enumerator,
                                     UINT64 storageAddress,
                                     bool isRepository)
{
    HRESULT status;
    SMEMORY_STORAGE log;

    if (storageAddress == 0) {

        return 0;
    }

    EXT_CHECK(StructMEMORY_STORAGE.Read(storageAddress, &log));

    ULONG64 CrtZone = log.MemoryZoneLink;

    //
    //  We need to walk the repository storages anyway, regardless the
    //  the aditional filtering on storages.
    //

    if (enumerator->StorageCallout(storageAddress, FALSE) || isRepository) {

        ExtVerb("Walking storage %p\n", storageAddress);

        while (CrtZone) {

            if (enumerator->ZoneCallout(CrtZone, FALSE)) {

                ULONG64 NextZone = ReadMemoryZone(enumerator, CrtZone);

                enumerator->ZoneCallout(CrtZone, TRUE);

                if (CrtZone == NextZone) {

                    break;
                }

                CrtZone = NextZone;

            } else {

                break;
            }
        }

        enumerator->StorageCallout(storageAddress, TRUE);
    } else {
        ExtVerb("Skipping storage %p\n", storageAddress);
    }


    return log.Link;

Exit:
    //  Error path

    ERRORBREAK("Invalid symbols information\n");
    return 0;
}


ULONG64 SwitchToDomainAddressSpace(ULONG64 Context)
{
    if (Context == 0) {

        // Nothing to do if paging is not enabled

        return 0;
    }

    //  TODO: Implement me to handle the paging case

    return 0;
}

void RevertToSystemDomainAddressSpace(ULONG64 Context)
{
    if (Context == 0) {

        // Nothing to do if paging is not enabled

        return;
    }

    //  TODO: Implement me to handle the paging case
}


void ControllerObject::FetchMetadata(EventingEnumerator * enumerator)
{
    ReadStorage(enumerator, RepositoryAddress, true);
    WalkTypes(enumerator);
    WalkSources(enumerator);
    WalkActiveSourcesEntries(enumerator);
}

void ControllerObject::WalkEntries(EventingEnumerator * enumerator)
{
    UINT64 CrtStorage;

    CrtStorage = StorageListHead;

    while (CrtStorage) {

        CrtStorage = ReadStorage(enumerator, CrtStorage, false);
    }
}


void WalkTracingDatabase(EventingEnumerator * enumerator, bool typesOnly)
{
    HRESULT status;
    UINT64 controllerAddress = 0;
    SSOURCE_CONTROLLER log;
    ULONG64 SystemContextHandle;
    ULONG64 ControllerLink;

    FlushControllers();

    if (!enumerator->SystemCallout(FALSE)) {

        return;
    }

    EXT_CHECK(g_ExtSymbols->GetOffsetByName(kernel_SourceController, &controllerAddress));

    EXT_CHECK(StructSOURCE_CONTROLLER.Read(controllerAddress, &log));

    ControllerLink = log.ExternalControllers;

    ControllerObject * ctrl = AllocateController(controllerAddress,
                                                 0,
                                                 log.SourceRepository,
                                                 log.StorageList);

    if (enumerator->ControllerCallout(ctrl, FALSE)) {


        if (ctrl != NULL) {

            ctrl->FetchMetadata(enumerator);

            if (enumerator->MedataCallout(ctrl) && !typesOnly) {

                ctrl->WalkEntries(enumerator);
            }
        }

        enumerator->ControllerCallout(ctrl, TRUE);
    }

    while ((DumpKernelOnly == FALSE) && (ControllerLink != 0)){

        SEXTERNAL_CONTROLLER_DESCRIPTOR extController;
        EXT_CHECK(StructEXTERNAL_CONTROLLER_DESCRIPTOR.Read(ControllerLink, &extController));

        ControllerLink = extController.Link;

        controllerAddress = extController.ControllerHandle;
        SystemContextHandle = SwitchToDomainAddressSpace(extController.ContextHandle);

        EXT_CHECK(StructSOURCE_CONTROLLER.Read(controllerAddress, &log));

        ControllerObject * ctrl = AllocateController(controllerAddress,
                                                     extController.ContextHandle,
                                                     log.SourceRepository,
                                                     log.StorageList);

        if (enumerator->ControllerCallout(ctrl, FALSE)) {

            if (ctrl != NULL) {

                ctrl->FetchMetadata(enumerator);

                if (enumerator->MedataCallout(ctrl) && !typesOnly) {

                    ctrl->WalkEntries(enumerator);
                }
            }

            enumerator->ControllerCallout(ctrl, TRUE);
        }

        RevertToSystemDomainAddressSpace(SystemContextHandle);

    }

    enumerator->SystemCallout(TRUE);
    return;

Exit:
    //  Error path

    ERRORBREAK("Invalid symbols information\n");
    return;
}


UINT64 eventTypeHandle;
FieldFilter * filters[MAX_FILTERS];
int filtersCount = 0;

void CleanupFilters()
{
    for (int i = 0; i < filtersCount; i++) {
        delete (filters[i]);
    }
    filtersCount = 0;
}


class SourceFlagsEnumerator : public EventingEnumerator
{

    StringPattern * FilterCollector;
    StringPattern * FilterSource;
    UINT Flags;

public:

    SourceFlagsEnumerator(StringPattern * filterCollector, StringPattern * filterSource, UINT flags) {
        FilterCollector = filterCollector;
        FilterSource = filterSource;
        Flags = flags;
    }

    bool virtual MedataCallout(ControllerObject *controller);
};


void
WriteULONG(ULONG64 address, ULONG value)
{
    g_ExtData->WriteVirtualUncached(address, &value, sizeof(value), NULL);
}


bool SourceFlagsEnumerator::MedataCallout(ControllerObject *controller)
{
    if (FilterCollector) {

        if (!FilterCollector->IsMatch(controller->GetControllerName())) {

            return FALSE;
        }
    }

    if (FilterSource) {

        for (int i = 0; i < controller->SourcesCount; i++) {

            SourceEntry *src = controller->RegisteredSources[i];

            if (FilterSource->IsMatch(src->Name)) {

                ExtOut("Matched source (%s) (%s) %p %p\n",
                    controller->GetControllerName(),
                    src->Name,
                    src->Key,
                    (src->Key + _FieldsSOURCE_DESCRIPTOR[5].offset + StructMEMORY_HEADER.size));

                WriteULONG((src->Key + _FieldsSOURCE_DESCRIPTOR[5].offset + StructMEMORY_HEADER.size),
                            Flags);
            }
        }
    }

    return FALSE;
}


void Tracing(PCSTR args)
{
    PCSTR defaultVisibleFields = "|Timestamp|Cpu|TID|";
    HRESULT status;
    TypeFilterEnumerator * typeFilter = NULL;
    CleanupFilters();
    FlushTransforms();
    eventTypeHandle = 0;
    TypeEntryCollector * collector = NULL;
    char * sortField = NULL;
    EventingEnumerator * dump = NULL;
    StringPattern * sourcePattern = NULL;
    bool DumpSummary = false;

    //
    //  Parse the remaining arguments. This stuff needs to be cleaned up or moved
    //  to dscript at some point.
    //

    DumpAllEvents = FALSE;
    DumpStackTraces = FALSE;
    DumpKernelOnly = FALSE;
    DumpDescriptions = FALSE;
    SKIP_WHITESPACES(args);

    while (*args != '\0') {

        SKIP_WHITESPACES(args);

        // process argument
        if (*args == '-' || *args == '/') {
            args++;
            switch (toupper(*args++)) {
              case 'G':{

                if ((*args == 'c') || (*args == 'C'))
                {
                    EXT_CHECK(SetBound("Microsoft_Singularity_V1_Services_DiagnosisService::DeferedCommand", 2));
                    ExtOut("Kernel GC collection triggered. Press \'g\' to resume the execution.\n"
                        "It will break in debugger when GS collection completes\n");
                }
                else
                {
                    SKIP_WHITESPACES(args);

                    //  Write the GC logs buffer, with the memory size in MBytes
                    WriteCLRProfileFile(args, false);
                }
                goto CleanupAndExit;
              }
              break;

              case 'E':{
                bool enableKernel;
                bool breakOnRecycle = false;
                if ((*args == 'k') || (*args == 'K'))
                {
                    enableKernel = true;
                }
                else if ((*args == 's') || (*args == 'S'))
                {
                    enableKernel = false;
                }
                else
                {
                    Usage();
                    goto CleanupAndExit;
                }
                //  Consume the 'k' or 's' and skip whitespaces to the memory value
                args += 1;
                SKIP_WHITESPACES(args);
                int MemorySize = (int)GetValue(args, false);

                // There is an optional 'break' keyword
                SKIP_WHITESPACES(args);
                if (_stricmp(args, "break") == 0)
                {
                    breakOnRecycle = true;
                }

                //  Set the appropriate profiler buffer, with the memory size in MBytes & flags
                if (enableKernel)
                {
                    EnableKernelGCProfiler(MemorySize * 1024 * 1024, breakOnRecycle);
                }
                else
                {
                    //  Set the SIP profiler buffer, with the memory size in MBytes
                    EnableSIPGCProfiler(MemorySize * 1024 * 1024, breakOnRecycle);
                }
                goto CleanupAndExit;
              }
              break;

              case 'D':{

                if ((*args == 'k') || (*args == 'K'))
                {
                    //  Consume the k and skip whitespaces to the memory value
                    args += 1;
                    SKIP_WHITESPACES(args);

                    //  Disable the profiler by setting the buffer size to 0
                    DisableKernelGCProfiler();
                }
                else if ((*args == 's') || (*args == 'S'))
                {
                    //  Consume the s and skip whitespaces to the memory value
                    args += 1;
                    SKIP_WHITESPACES(args);
                    int MemorySize = (int)GetValue(args, true);

                    //  Disable the SIP profiler by resetting the buffer size
                    EnableSIPGCProfiler(0, false);
                }
                else
                {
                    Usage();
                }
                goto CleanupAndExit;
              }
              break;

              case 'M':
              break;

              case 'N':

                DumpSummary = true;
              break;

              case 'A':{
                DumpAllEvents = TRUE;
              }
              break;

              case 'P':{
                  DumpDescriptions = TRUE;
              }
              break;

              case 'K':{
                DumpStackTraces= TRUE;
              }
              break;

              case 'X':{
                DumpKernelOnly = TRUE;
              }
              break;

              case 'Y':{
                ParseStringFormatTransform(args);
              }
              break;

              case 'L':{
                ParseSymbolLookupTransform(args);
              }
              break;

              case 'R':{

                defaultVisibleFields = NULL;
                ParseVisibleFields(args);
              }
              break;

              case 'T':{
                SKIP_WHITESPACES(args);

                DumpAllEvents = TRUE;
                StringPattern * filter = new StringPattern();
                if (filter == NULL) {
                    goto CleanupAndExit;
                }

                args = filter->ParseInput((char*)args);

                typeFilter = new TypeFilterEnumerator();

                if (typeFilter == NULL) {

                    delete filter;
                    goto CleanupAndExit;
                }

                typeFilter->SetPattern(filter);
                WalkTracingDatabase(typeFilter, TRUE);

              }
              break;
              case 'O':{

                SKIP_WHITESPACES(args);

                DumpAllEvents = TRUE;
                char * sortField = ReadToken(args);

                if (sortField != NULL) {

                    collector = new TypeEntryCollector(0, sortField);
                }

              }
              break;
              case 'Z':{

                SKIP_WHITESPACES(args);

                StringPattern * filterCollector = new StringPattern();

                if (filterCollector == NULL) {
                    goto CleanupAndExit;
                }

                StringPattern * filterSource = new StringPattern();

                if (filterSource == NULL) {

                    delete filterCollector;
                    goto CleanupAndExit;
                }

                args = filterCollector->ParseInput((char*)args);
                args = filterSource->ParseInput((char*)args);

                ULONG Flags = (int)GetValue(args, true);

                SourceFlagsEnumerator *enumerator = new SourceFlagsEnumerator(filterCollector,
                                                                              filterSource,
                                                                              Flags);

                if (enumerator != NULL) {

                    WalkTracingDatabase(enumerator, TRUE);
                    delete enumerator;
                }

                delete filterSource;
                delete filterCollector;
                goto CleanupAndExit;
              }

              break;
              case 'S':{

                SKIP_WHITESPACES(args);
                DumpAllEvents = TRUE;

                sourcePattern = new StringPattern();

                if (sourcePattern == NULL) {

                    goto CleanupAndExit;
                }

                args = sourcePattern->ParseInput((char*)args);
              }

              break;
              case 'F':{
                SKIP_WHITESPACES(args);

                DumpAllEvents = TRUE;
                FieldFilter * filter = new FieldFilter();
                if (filter == NULL) {
                    goto CleanupAndExit;
                }

                args = filter->ParseInput((char*)args);

                filters[filtersCount++] = filter;
              }
              break;
            }
            SKIP_WHITESPACES(args);
        } else {
            Usage();
            break;
        }
    }

    if (defaultVisibleFields != NULL) {

        defaultVisibleFields = NULL;
        ParseVisibleFields(args);
    }

    if (typeFilter != 0) {

        for (int i = 0; i < typeFilter->Array->InUse; i++) {

            dump = new DumpType(typeFilter->Array->Array[i]);

            if (dump) {

                dump->ApplyFilters(filters, filtersCount);
                dump->ApplySourceSelector(sourcePattern);

                if (collector == NULL) {

                    //
                    //  If no other order criteria has been established
                    //  default to sort by timestamp
                    //

                    collector = new TypeEntryCollector(0, "Timestamp");
                }

                if (collector) {

                    collector->TypeHandle = typeFilter->Array->Array[i];
                    collector->cascadeEnumerator = dump;
                    collector->GroupByController = TRUE;

                    WalkTracingDatabase(collector, FALSE);
                } else {
                    WalkTracingDatabase(dump, FALSE);
                }

                delete dump;
                dump = NULL;
            }
        }

    } else {

        dump = new DumpTracing();
        if (dump) {

            ((DumpTracing*)dump)->DumpSummary = DumpSummary;

            dump->ApplyFilters(filters, filtersCount);
            dump->ApplySourceSelector(sourcePattern);

            if (collector == NULL) {

                //
                //  If no other order criteria has been established
                //  default to sort by timestamp
                //

                collector = new TypeEntryCollector(0, "Timestamp");
            }

            if (collector) {

                collector->TypeHandle = 0;
                collector->cascadeEnumerator = dump;
                if (DumpSummary) {
                    collector->GroupByController = TRUE;
                }

                WalkTracingDatabase(collector, FALSE);

            } else {

                WalkTracingDatabase(dump, !DumpAllEvents);
            }
        }
    }

Exit:
CleanupAndExit:

    if (sortField) free(sortField);
    if (dump) delete dump;
    if (typeFilter) delete typeFilter;
    if (sourcePattern) delete sourcePattern;

    return;
}



EXT_DECL(diagnose) // Defines: PDEBUG_CLIENT Client, PCSTR args
{
    EXT_ENTER();    // Defines: HRESULT status = S_OK;
    bool KernelProfile = true;
    pointerSize = (g_ExtControl->IsPointer64Bit() == S_OK) ? 8 : 4;

    SKIP_WHITESPACES(args);

    if (*args == '\0')
    {
        Usage();

    } else {

        Tracing(args);
    }

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

