/////////////////////////////////////////////////////////////////////////////
//
//  diagdump.cpp - Extension using the enumeration support for diagnosis
//  to dump the content of the tracing
//
//  Copyright Microsoft Corporation.  All rights reserved.
//

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

bool DumpAllEvents = FALSE;
bool DumpStackTraces = FALSE;
bool DumpDescriptions = FALSE;

//
//  Simple transforms on fields
//

//
//  Empty base class for common transform manipulation
//

class EntryTransform {

    public:
    virtual bool IsFieldHidden(EventTypeEntry * entryDescriptor, FieldEntry * fieldDescriptor)
    {

        // all fields are visilbe by default
        return FALSE;
    }

    virtual bool DoTransform(EventTypeEntry * entryDescriptor, FieldEntry * fieldDescriptor)
    {
        // no transform is applied on fields by default
        return FALSE;
    }

    virtual int GetFieldWidth(EventTypeEntry * entryDescriptor, FieldEntry * fieldDescriptor)
    {
        // no transform is applied on fields by default
        return 0;
    }
};

class StringFormatTransform : public EntryTransform {

    // Name of the field the transform will perform on
    char * FieldName;

    // the index of the first argument that will be used in string formatting
    int ArgumentBaseIndex;

    public:

    StringFormatTransform(char * fieldName, int argumentBase)
    {
        FieldName = _strdup(fieldName);
        ArgumentBaseIndex = argumentBase;
    }

    ~StringFormatTransform()
    {
        if (FieldName != NULL) free(FieldName);

    }

    virtual int GetFieldWidth(EventTypeEntry * entryDescriptor, FieldEntry * fieldDescriptor)
    {
        if (_stricmp(FieldName, fieldDescriptor->Name) == 0) {

            return 30;  // this is dependent of format and arguments. Just pick a value common
                        // for current usages
        }

        return 0;
    }

    virtual bool DoTransform(EventTypeEntry * entryDescriptor, FieldEntry * fieldDescriptor)
    {

        if (_stricmp(FieldName, fieldDescriptor->Name) == 0) {

            ExtOut("  ");
            entryDescriptor->Format(fieldDescriptor->ExtendedFieldIndex + 1, ArgumentBaseIndex);
            return true;
        }

        return false;
    }
};

class SymbolLookupTransform : public EntryTransform {

    // Name of the field the transform will perform on
    char * FieldName;

    public:

    SymbolLookupTransform(char * fieldName)
    {
        FieldName = _strdup(fieldName);
    }

    ~SymbolLookupTransform()
    {
        if (FieldName != NULL) free(FieldName);
    }

    virtual int GetFieldWidth(EventTypeEntry * entryDescriptor, FieldEntry * fieldDescriptor)
    {
        if (_stricmp(FieldName, fieldDescriptor->Name) == 0) {

            return 70;
        }
        return 0;
    }

    virtual bool DoTransform(EventTypeEntry * entryDescriptor, FieldEntry * fieldDescriptor)
    {

        if (_stricmp(FieldName, fieldDescriptor->Name) == 0) {

            char symbol[512];
            ULONG64 displacement = 0;
            ULONG64 fieldValue = fieldDescriptor->GetFieldNumericValue();

            HRESULT status = g_ExtSymbols->GetNameByOffset(fieldValue,
                                                           symbol,
                                                           arrayof(symbol),
                                                           NULL,
                                                           &displacement);
            if (status == S_OK) {

                ExtOut("  %-68s", symbol);

            } else {

                ExtOut("  %-68s", "");
            }

            return true;
        }

        return false;
    }
};

class RestrictToFieldsTransform : public EntryTransform {

    // The pattern containing the fields available for display
    // in the format "F1|F2|F3"

    char * AvailableFields;

    public:

    RestrictToFieldsTransform(char * fieldList)
    {
        AvailableFields = _strdup(fieldList);
    }

    ~RestrictToFieldsTransform()
    {
        if (AvailableFields != NULL) free(AvailableFields);

    }

    virtual bool IsFieldHidden(EventTypeEntry * entryDescriptor, FieldEntry * fieldDescriptor)
    {
        if ((AvailableFields != NULL) && (strstr(AvailableFields, fieldDescriptor->Name) != NULL)) {

            return TRUE;
        }

        return FALSE;
    }

};

const int MaxTransformAllowed = 16;

EntryTransform * Transforms[MaxTransformAllowed];
int InUseTransforms = 0;

void FlushTransforms()
{
    for (int i = 0; i < InUseTransforms; i++) {

        delete Transforms[i];
        Transforms[i] = NULL;
    }
    InUseTransforms = 0;
}

bool AddTransform(EntryTransform * transform)
{
    if (InUseTransforms < MaxTransformAllowed) {

        Transforms[InUseTransforms++] = transform;
        return true;
    }

    return false;
}

bool ApplyTransforms(EventTypeEntry * entryDescriptor, FieldEntry * fieldDescriptor)
{
    for (int i = 0; i < InUseTransforms; i++) {

        if (Transforms[i]->DoTransform(entryDescriptor, fieldDescriptor)) {

            return true;
        }
    }
    return false;
}

int GetTransformFieldWidth(EventTypeEntry * entryDescriptor, FieldEntry * fieldDescriptor)
{
    int width = 0;

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

        int crtWidth = Transforms[i]->GetFieldWidth(entryDescriptor, fieldDescriptor);

        if (width < crtWidth) {

            width = crtWidth;
        }
    }

    return width;
}


bool TestVisibility(EventTypeEntry * entryDescriptor, FieldEntry * fieldDescriptor)
{
    for (int i = 0; i < InUseTransforms; i++) {

        if (Transforms[i]->IsFieldHidden(entryDescriptor, fieldDescriptor)) {

            return false;
        }
    }
    return true;
}

void ParseStringFormatTransform(PCSTR & args)
{
    char * str = ReadToken(args);

    if (str != NULL) {

        StringFormatTransform * tr = new StringFormatTransform(str, 3/*FIXME*/);
        if (!AddTransform(tr)) delete tr;
        free(str);
    }
}

void ParseVisibleFields(PCSTR & args)
{
    char * str = ReadToken(args);

    if (str != NULL) {

        RestrictToFieldsTransform * tr = new RestrictToFieldsTransform (str);

        if (!AddTransform(tr)) delete tr;
        free(str);
    }
}

void ParseSymbolLookupTransform(PCSTR & args)
{
    char * str = ReadToken(args);

    if (str != NULL) {

        SymbolLookupTransform * tr = new SymbolLookupTransform(str);
        if (!AddTransform(tr)) delete tr;
        free(str);
    }
}


void WriteLine(int length)
{
    while (length--) ExtOut("-");
}


void DumpStackTrace()
{
    for (int i = 1; ; i++) {

        UINT64 ipValue = GetStackTrace(i);

        if (ipValue == 0) {
            break;
        }

        CHAR name[512];

        if (g_ExtSymbols->GetNameByOffset(ipValue,
                                          (PSTR)&name,
                                          sizeof(name),
                                          NULL, NULL) == S_OK) {
            ExtOut("        %p %s\n", ipValue, name);

        } else {

            ExtOut("        %p {invalid symbol}\n", ipValue);
        }
    }
}

//
//  Implementation of DumpTracing methods
//


bool DumpTracing::TypeCallout(EventTypeEntry * entryDescriptor)
{
    if (!EventingEnumerator::TypeCallout(entryDescriptor)) {

        if (!EventingEnumerator::TypeCallout(SystemHeaderType)) {
            return FALSE;
        }
    }

    if (LastItem != type) {

        if (DumpSummary) ExtOut("Registered event types:\n");
        LastItem = type;
    }

    if (DumpSummary) ExtOut("    %s: (Key=%p, FieldsCount=%ld) - %s\n",
                            entryDescriptor->Name,
                            (UINT64)entryDescriptor->Key,
                            entryDescriptor->NumFields,
                            entryDescriptor->Descriptor);

    return FALSE;

}

void DumpTracing::SourcesCallout(SourceEntry * source)
{
    if (LastItem != DumpTracing::source) {

        if (DumpSummary) ExtOut("Registered sources:\n");
        LastItem = DumpTracing::source;
    }

    if (DumpSummary) ExtOut("    %s: (Key=%p, Storage=%p, ControlFlags=%lx)\n",
                            source->Name,
                            (UINT64)source->Key,
                            source->StorageHandle,
                            source->ControlFlags);
}

bool DumpTracing::ActiveSourceCallout(SourceEntry * source, bool finished, ControllerObject *controller)
{
    if (LastItem != DumpTracing::activesource) {

        if (DumpSummary) ExtOut("Registered active sources:\n");
        LastItem = DumpTracing::activesource;
    }

    if (!finished) {

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

        if (typeEntry != NULL) {

            if (DumpSummary) ExtOut("    %s: (Key=%p, Type=%p, Buffer=%p, %ld %s items)\n",
                                    source->Name,
                                    source->Key,
                                    source->EventTypeHandle,
                                    source->DebuggerBufferAddress,
                                    (int)source->Count,
                                    typeEntry->Name);

        } else {

            if (DumpSummary) ExtOut("    %s: (Key=%p, Type=%p, Buffer=%p, %ld unknown type items)\n",
                                    source->Name,
                                    source->Key,
                                    source->EventTypeHandle,
                                    source->DebuggerBufferAddress,
                                    (int)source->Count);
        }


    }
    return FALSE;
}

bool DumpTracing::ControllerCallout( ControllerObject *ctrl, bool finished)
{
    if (!EventingEnumerator::ControllerCallout(ctrl, finished)) {

        return FALSE;
    }
    if (DumpSummary) {

        if (!finished) {
            ExtOut("********************************************\n");
            ExtOut("*\n");
            ExtOut("* Controller %p (context %p)\n",
                    ctrl->ControllerHandle,
                    ctrl->ContextHandle);
            ExtOut("*\n");
            ExtOut("********************************************\n");
        } else {
            ExtOut("\n\n");
        }
    }
    LastItem = controller;
    return TRUE;
}


bool DumpTracing::FieldCallout(EventTypeEntry * entryDescriptor, FieldEntry * fieldDescriptor)
{
    if (!TestVisibility(entryDescriptor, fieldDescriptor)) {

        return TRUE;
    }

    if (PrintPhase == 0) {

        if (!DumpDescriptions || (MetadataSize == 0)) {

            fieldDescriptor->PrintValue(entryDescriptor);
        }

    } else if (MetadataSize != 0){

        if (FieldIndex != 0) ExtOut(",");
        ExtOut("%s", fieldDescriptor->Name);
        FieldIndex += 1;
    }

    return TRUE;
}

bool DumpTracing::EntryCallout(EntryHeader *header, EventTypeEntry * entryDescriptor)
{
    if (entryDescriptor == NULL) {

        return FALSE;
    }

    if (!entryDescriptor->FilterMatch()) {

        return FALSE;
    }

    if (DumpAllEvents) {

        PrintPhase = 0;

        ExtOut("%6ld %p", CrtEntryIndex++, header->StackAddress);
        entryDescriptor->WalkFields(this);

        if (DumpDescriptions) {

            ExtOut("  ");
            entryDescriptor->PrintDescription();
            ExtOut("\n");

        } else {

            PrintPhase = 1;
            FieldIndex = 0;
            ExtOut(" : %s(", entryDescriptor->Name);
            entryDescriptor->WalkFields(this);
            ExtOut(")\n");
        }

        if (header->StackSize && DumpStackTraces) {

            DumpStackTrace();
            ExtOut("\n");
        }
    }

    return TRUE;

}



//
//  Implementation of DumpType class
//

DumpType::DumpType(UINT64 handle)
{
    typeHandle = handle;
    printHeader = 1;
}


bool DumpType::FieldCallout(EventTypeEntry * entryDescriptor, FieldEntry * fieldDescriptor)
{
    if (!TestVisibility(entryDescriptor, fieldDescriptor)) {

        return TRUE;
    }

    if (printHeader < 3) {

        int width = fieldDescriptor->GetFieldPrintWidth();
        int transformWidth = GetTransformFieldWidth(entryDescriptor, fieldDescriptor);

        if (transformWidth > width) {

            width = transformWidth;
        }

        if (printHeader == 1) {

            char buff[100];

            _snprintf(buff, sizeof(buff), "%%%lds", width);
            ExtOut(buff, fieldDescriptor->Name);

        } else if (printHeader == 2) {

            WriteLine(width);
        }
    } else {

        if (!ApplyTransforms(entryDescriptor, fieldDescriptor)) {

            fieldDescriptor->PrintValue(entryDescriptor);
        }
    }

    return TRUE;
}

bool DumpType::EntryCallout(EntryHeader *header, EventTypeEntry * entryDescriptor)
{
    if ((entryDescriptor == NULL) || (entryDescriptor->Key != typeHandle)) {

        return FALSE;
    }

    if (!entryDescriptor->FilterMatch()) {

        return FALSE;
    }

    if (DumpAllEvents) {

        if (printHeader == 1) {

            PrintHeader(entryDescriptor);

        } else if ((CrtEntryIndex % 50) == 0){

            printHeader = 1;
            ExtOut(" Index   Stacks");
            entryDescriptor->WalkFields(this);
            ExtOut("\n");
            printHeader += 1;
            WriteLine(6);
            entryDescriptor->WalkFields(this);
            ExtOut("\n");
            printHeader += 1;
        }

        INT64 delta;

        if (lastTimeStamp == 0) {

            delta = 0;
        } else {

            delta = header->timestamp - lastTimeStamp;
        }

        ExtOut("%6ld %p", CrtEntryIndex++, header->StackAddress);
        lastTimeStamp = header->timestamp;
        entryDescriptor->WalkFields(this);
        ExtOut("\n");

        if (header->StackSize && DumpStackTraces) {

            DumpStackTrace();
            ExtOut("\n");
        }
    }

    return TRUE;

}

bool DumpType::PrintHeader(EventTypeEntry * entryDescriptor)
{
    ExtOut("TYPE: %p Key=%p FieldsCount=%ld (%s-%s)\n",
           (UINT64)entryDescriptor,
           (UINT64)entryDescriptor->Key,
           entryDescriptor->NumFields,
           entryDescriptor->Name,
           entryDescriptor->Descriptor);

    ExtOut(" Index   Stacks");
    entryDescriptor->WalkFields(this);
    ExtOut("\n");

    printHeader += 1;
    WriteLine(16);
    entryDescriptor->WalkFields(this);
    ExtOut("\n");
    printHeader += 1;
    CrtEntryIndex = 0;
    return TRUE;

}


bool DumpType::TypeCallout(EventTypeEntry * entryDescriptor)
{

    if (!EventingEnumerator::TypeCallout(entryDescriptor)) {

        if (!EventingEnumerator::TypeCallout(SystemHeaderType)) {
            return FALSE;
        }
    }

    if (entryDescriptor->Key != typeHandle) {

        return FALSE;
    }
    lastTimeStamp = 0;
    return TRUE;

}

bool DumpType::ActiveEntryCallout(EventTypeEntry * entryDescriptor, BOOL finished)
{
    if (finished) {
        ExtOut("\n");
        return TRUE;
    }

    if (entryDescriptor->Key != typeHandle) {

        return FALSE;
    }

    if (!entryDescriptor->FilterMatch()) {

        return FALSE;
    }

    if (printHeader == 1) {

        PrintHeader(entryDescriptor);
    }

    ExtOut("%6ld", CrtEntryIndex++);

    return TRUE;

}


bool DumpType::ActiveSourceCallout(SourceEntry * source, bool finished, ControllerObject *controller)
{
    if (!finished) {

        CrtEntryIndex = 0;
        printHeader = 1;

        if ((source->EventTypeHandle == typeHandle)) {

            ExtOut("ACTIVE SOURCE: %p Key=%p Type=%p Buffer=%p (%ld items)(%s)\n",
                (UINT64)source,
                source->Key,
                source->EventTypeHandle,
                source->DebuggerBufferAddress,
                (int)source->Count,
                source->Name);

            return TRUE;
        }

        return FALSE;

    } else {
        ExtOut("\n");
    }
    return TRUE;
}

//
//  Implementation of the TypeFilterEnumerator methods
//

TypeFilterEnumerator::TypeFilterEnumerator()
{
    Array = new VariableArray();
    Pattern = NULL;
}

TypeFilterEnumerator::~TypeFilterEnumerator()
{
    if (Array != NULL) delete (Array);
    if (Pattern != NULL) delete (Pattern);

}

void TypeFilterEnumerator::SetPattern(StringPattern * pattern) {

    Pattern = pattern;
}


bool TypeFilterEnumerator::TypeCallout(EventTypeEntry * entryDescriptor)
{
    if (Pattern->IsMatch(entryDescriptor->Name)) {

        Array->Add(entryDescriptor->Key);
    }

    return FALSE;
}

//
//  Implementation of the TypeEntryCollector methods
//

TypeEntryCollector::TypeEntryCollector(UINT64 typeHandle, char * field)
    : TypeFilterEnumerator()
{
    TypeHandle = typeHandle;
    sortField = field;
    cascadeEnumerator = NULL;
    GroupByController = FALSE;
}

TypeEntryCollector::~TypeEntryCollector() {

}

bool TypeEntryCollector::MedataCallout(ControllerObject *controller)
{
    if (cascadeEnumerator) {

        return cascadeEnumerator->MedataCallout(controller);
    }

    return TRUE;
}

void TypeEntryCollector::SourcesCallout(SourceEntry * source)
{
    if (cascadeEnumerator) {

        return cascadeEnumerator->SourcesCallout(source);
    }
}

bool TypeEntryCollector::StorageCallout(UINT64 storageAddress, bool finished)
{
    if (cascadeEnumerator) {

        return cascadeEnumerator->StorageCallout(storageAddress, finished);
    }

    return TRUE;
}


bool TypeEntryCollector::FieldCallout(EventTypeEntry * entryDescriptor,
                                      FieldEntry * fieldDescriptor)
{
    //  NOTE this assumes inserting FieldsWidth elements to the array

    if (_stricmp(sortField, fieldDescriptor->Name) == 0) {

        Array->Add(crtHandle);
        Array->Add(fieldDescriptor->GetFieldNumericValue());
        Array->Add((UINT64)currentController);
    }

    return TRUE;
}

bool TypeEntryCollector::EntryCallout(EntryHeader *header,
                                      EventTypeEntry * entryDescriptor)
{
    if ((entryDescriptor == NULL) ||
        ((TypeHandle != 0) && (entryDescriptor->Key != TypeHandle))) {

        return FALSE;
    }

    if (!entryDescriptor->FilterMatch()) {

        return FALSE;
    }

    crtHandle = header->address;
    entryDescriptor->WalkFields(this);

    return TRUE;

}

bool TypeEntryCollector::TypeCallout(EventTypeEntry * entryDescriptor)
{

    if (!EventingEnumerator::TypeCallout(entryDescriptor)) {

        if (!EventingEnumerator::TypeCallout(SystemHeaderType)) {
            return FALSE;
        }
    }

    if (cascadeEnumerator) {

        cascadeEnumerator->TypeCallout(entryDescriptor);
    }

    if ((TypeHandle != 0) && (entryDescriptor->Key != TypeHandle)) {

        return FALSE;
    }
    return TRUE;

}

static int ItemCompare(const void * e1, const void * e2)
{
    UINT64 *ptr1 = (UINT64 *) e1;
    UINT64 *ptr2 = (UINT64 *) e2;

    if (ptr1[1] < ptr2[1]) {
        return -1;
    } else if (ptr1[1] == ptr2[1]) {
        return 0;
    }

    return 1;
}

void TypeEntryCollector::SortItems()
{
    qsort(Array->Array, Array->InUse / FieldsWidth, FieldsWidth*sizeof(UINT64), ItemCompare);
}

void TypeEntryCollector::WalkItems(EventingEnumerator * enumerator)
{

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

        ControllerObject * ctrl = (ControllerObject *)Array->Array[i+2];

        if (ctrl != NULL) {

            ctrl->ReadMemoryHeader(enumerator, Array->Array[i]);
        }
    }
}

bool TypeEntryCollector::ControllerCallout(ControllerObject *controller, bool finished)
{
    if (!EventingEnumerator::ControllerCallout(controller, finished)) {

        return FALSE;
    }

    if (finished) {

        if (GroupByController) {

            SortItems();

            if (cascadeEnumerator) {

                WalkItems(cascadeEnumerator);
                cascadeEnumerator->ControllerCallout(controller, finished);
            }

            //
            //  Recycle the elements captured so far since the cascade enumerator processed them
            //  already
            //

            if (Array != NULL) delete (Array);
            Array = new VariableArray();
        }
        currentController = 0;

    } else {

        currentController = controller;
        if (GroupByController) {
            if (cascadeEnumerator) {

                cascadeEnumerator->ControllerCallout(controller, finished);
            }
        }
    }
    return TRUE;
}

bool TypeEntryCollector::SystemCallout(bool finished)
{

    if (finished) {

        SortItems();

        if (cascadeEnumerator) {

            WalkItems(cascadeEnumerator);
        }
    }
    return TRUE;
}

//
//  Source collector methods.
//

SourceCollector::SourceCollector(char * sourceName, char * field)
    : TypeEntryCollector(0, field)
{
    SourcePattern = NULL;
    storageHandle = 0;

    if (sourceName != NULL) {
        SourcePattern = new StringPattern();

        if (SourcePattern) {

            SourcePattern->ParseInput(sourceName);
        }
    }
}

void SourceCollector::SourcesCallout(SourceEntry * source)
{
    if (SourcePattern) {

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

            storageHandle = source->StorageHandle;
        }
    }
}


bool SourceCollector::StorageCallout(UINT64 storageAddress, bool finished)
{
    if ((storageHandle == 0) || (storageAddress == storageHandle)) {

        return TRUE;
    }

    return FALSE;
}


