(file) Return to ManifestGen.c CVS log (file) (dir) Up to [OMI] / omi / oi / gen_c / common

File: [OMI] / omi / oi / gen_c / common / ManifestGen.c (download)
Revision: 1.1, Mon Apr 20 17:19:54 2015 UTC (9 years, 2 months ago) by krisbash
Branch: MAIN
CVS Tags: OMI_1_0_8_2, OMI_1_0_8_1, HEAD
OMI 1.0.8-1

/*
**==============================================================================
**
** Open Management Infrastructure (OMI)
**
** Copyright (c) Microsoft Corporation
** 
** Licensed under the Apache License, Version 2.0 (the "License"); you may not 
** use this file except in compliance with the License. You may obtain a copy 
** of the License at 
**
**     http://www.apache.org/licenses/LICENSE-2.0 
**
** THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
** KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED 
** WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, 
** MERCHANTABLITY OR NON-INFRINGEMENT. 
**
** See the Apache 2 License for the specific language governing permissions 
** and limitations under the License.
**
**==============================================================================
*/

#include "ManifestGen.h"
#include "oicommon.h"
#include <pal/strings.h>

static const char MANIFEST_HEADER[] = 
    "<?xml version='1.0' encoding='utf-8' standalone='yes'?>" NL \
    "<instrumentationManifest xmlns=\"http://schemas.microsoft.com/win/2004/08/events\">" NL \
    "  <instrumentation" NL \
    "      xmlns:win=\"http://manifests.microsoft.com/win/2004/08/windows/events\"" NL \
    "      xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"" NL \
    "      xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" NL \
    "      >" NL;

static const char MANIFEST_PROVIDERDECL[] =
    "    <events xmlns=\"http://schemas.microsoft.com/win/2004/08/events\">" NL \
    "      <provider" NL \
    "          guid=\"%s\"" NL \
    "          messageFileName=\"%s\"" NL \
    "          name=\"%s\"" NL \
    "          parameterFileName=\"%s\"" NL \
    "          resourceFileName=\"%s\"" NL \
    "          symbol=\"%s\"" NL \
    "          >" NL;

static const char MANIFEST_CHANNEL_TAG[] =
    "        <channels>" NL;

static const char MANIFEST_CHANNEL[] =
    "          <channel" NL \
    "              chid=\"%s\"" NL \
    "              enabled=\"%s\"" NL \
    "              name=\"%s/%s\"" NL \
    "              type=\"%s\"" NL \
    "              />" NL;

static const char MANIFEST_CHANNEL_CLOSE[] =
    "        </channels>" NL;

static const char MANIFEST_TASK_OPEN[] =
    "        <tasks>" NL;

static const char MANIFEST_TASK_DEF[] =
    "          <task" NL \
    "              message=\"$(string.Task.%s)\"" NL \
    "              name=\"%s\"" NL \
    "              symbol=\"TASK_%s\"" NL \
    "              value=\"%d\"/>" NL;

static const char MANIFEST_TASK_CLOSE[] =
    "        </tasks>" NL;

static const char MANIFEST_OPCODE_OPEN[] =
    "        <opcodes>" NL;

static const char MANIFEST_OPCODE_DEF[] =
    "          <opcode" NL \
    "              message=\"$(string.Opcode.%s)\"" NL \
    "              name=\"%s\"" NL \
    "              symbol=\"OPCODE_%s\"" NL \
    "              value=\"%d\"/>" NL;

static const char MANIFEST_OPCODE_CLOSE[] =
    "        </opcodes>" NL;

static const char MANIFEST_KEYWORD_OPEN[] =
    "        <keywords>" NL;

static const char MANIFEST_KEYWORD_DEF[] =
    "          <keyword" NL \
    "              mask=\"0x%I64x\"" NL \
    "              name=\"%s\"" NL \
    "              symbol=\"KEYWORD_%s\"/>" NL;

static const char MANIFEST_KEYWORD_CLOSE[] =
    "        </keywords>" NL;

static const char MANIFEST_EVENTSTAG[] =
    "        <events>" NL;

static const char EVENT_START[] =  
    "          <event" NL;
static const char EVENT_CHANNEL[] =  
    "              channel=\"%s\"" NL;
static const char EVENT_LEVELMESSAGESYMBOL[] =  
    "              level=\"%s\"" NL \
    "              message=\"$(string.%s)\"" NL \
    "              symbol=\"%s\"" NL;
static const char EVENT_TASK[] =  
    "              task=\"%s\"" NL;
static const char EVENT_TEMPLATEVALUE[] =  
    "              template=\"tid_%s\"" NL \
    "              value=\"%s\"" NL \
    "              />" NL;

static const char TEMPLATES_HEADER[] = 
    "        </events>" NL \
    "        <templates>" NL;

static const char TEMPLATE_HEADER[] = 
    "          <template tid=\"tid_%s\">" NL;

static const char DATA_TEMPLATE[] = 
    "            <data" NL \
    "                inType=\"%s\"" NL \
    "                name=\"%s\"" NL \
    "                />" NL;

static const char TEMPLATE_TAIL[] = 
    "          </template>" NL;

static const char TEMPLATE_EMPTY[] = 
    "          <template tid=\"tid_%s\"/>" NL;

static const char STRINGTABLE_HEADER[] = 
    "        </templates>" NL \
    "      </provider>" NL \
    "    </events>" NL \
    "  </instrumentation>" NL \
    "  <localization>" NL \
    "    <resources culture=\"en-US\">" NL \
    "      <stringTable>" NL;

static const char STRING_TEMPLATE[] = 
    "        <string" NL \
    "            id=\"%s\"" NL \
    "            value=%s" NL \
    "            />" NL;

static const char STRING_TEMPLATE_QUOTES[] = 
    "        <string" NL \
    "            id=\"%s\"" NL \
    "            value=\"%s\"" NL \
    "            />" NL;

static const char STRING_TASKOPCODE_TEMPLATE[] = 
    "        <string" NL \
    "            id=\"%s.%s\"" NL \
    "            value=\"%s\"" NL \
    "            />" NL;

static const char MANIFEST_TAIL[] = 
    "      </stringTable>" NL\
    "    </resources>" NL\
    "  </localization>" NL\
    "</instrumentationManifest>" NL;

static const char *TypeMapManifest[] = { 
    "const char *",     "win:AnsiString",
    "const char*",      "win:AnsiString",
    "char",             "win:AnsiString",
    "char*",            "win:AnsiString",
    "const wchar_t *",  "win:UnicodeString",
    "const wchar_t*",   "win:UnicodeString",
    "wchar_t*",         "win:UnicodeString",
    "TChar",            "win:UnicodeString",
    "const TChar *",    "win:UnicodeString",
    "const TChar*",     "win:UnicodeString",
    "const MI_Char *",  "win:UnicodeString", 
    "const MI_Char*",   "win:UnicodeString", 
    "MI_Sint8",         "win:Int8", 
    "MI_Uint8",         "win:UInt8", 
    "MI_Sint16",        "win:Int16", 
    "MI_Uint16",        "win:UInt16", 
    "unsigned short",   "win:UInt16",
    "int",              "win:Int32", 
    "MI_Sint32",        "win:Int32",
    "MI_Uint32",        "win:UInt32", 
    "MI_Result",        "win:UInt32",
    "unsigned int",     "win:UInt32", 
    "MI_Sint64",        "win:Int64",
    "ptrdiff_t",        "win:Int64",
    "MI_Uint64",        "win:UInt64", 
    "float",            "win:Float",
    "double",           "win:Double",
    "MI_Boolean",       "win:Boolean",
    "void *",           "win:Pointer",
    "Strand *",         "win:Pointer",
    "Interaction *",    "win:Pointer",
    "Message *",        "win:Pointer",
    "StrandMany *",     "win:Pointer",
    "StrandEntry *",    "win:Pointer",
};

static char * ETWChannelIDs[] = {
    "Admin",        "c1",
    "Operational",  "c2",
    "Analytic",     "c3",
    "Debug",        "c4",
};

static char * gTransferEvent[] = {
    "EventTransfer",        /* name */
    "500",                  /* event id */
    "c2",                   /* channel */
    "win:Informational",    /* level */
};

static const char * _GetManifestType(_In_z_ const char * in)
{
    int i;
    int count = (int)(sizeof(TypeMapManifest)/sizeof(char *));

    for(i = 0; i + 1 < count; i += 2)
    {
        if (Strcmp(in, TypeMapManifest[i]) == 0)
        {
            return TypeMapManifest[i+1];
        }
    }

    OIERROR1("Unknown type for ETW manifest [%s]\n", in);
    return NULL;
}

static const char * _GetChannelID(_In_z_ const char * in)
{
    int i;
    int count = (int)(sizeof(ETWChannelIDs)/sizeof(char *));

    for(i = 0; i + 1 < count; i += 2)
    {
        if (Strcmp(in, ETWChannelIDs[i]) == 0)
        {
            return ETWChannelIDs[i+1];
        }
    }

    OIERROR1("Unknown channel type for ETW [%s]\n", in);
    return NULL;
}

static MI_Boolean _WriteTaskOpcodeKeywords(_In_ FILE * out, _In_ Buckets * etwInfo)
{
    SigBucket * e;

    /* Tasks */
    if (fprintf(out, MANIFEST_TASK_OPEN) == -1)
        goto error;
    e = etwInfo->tasks;
    while(e)
    {
        SigBucket * next = e->next;

        if (fprintf(out, MANIFEST_TASK_DEF, e->key, e->key, e->key, (USHORT) e->value) == -1)
            goto error;

        e = next;
    }
    if (fprintf(out, MANIFEST_TASK_CLOSE) == -1)
        goto error;

    /* Opcodes */
    if (fprintf(out, MANIFEST_OPCODE_OPEN) == -1)
        goto error;
    e = etwInfo->opcodes;
    while(e)
    {
        SigBucket * next = e->next;

        if (fprintf(out, MANIFEST_OPCODE_DEF, e->key, e->key, e->key, (UCHAR) e->value) == -1)
            goto error;

        e = next;
    }
    if (fprintf(out, MANIFEST_OPCODE_CLOSE) == -1)
        goto error;

    /* Keywords */
    if (fprintf(out, MANIFEST_KEYWORD_OPEN) == -1)
        goto error;
    e = etwInfo->keywords;
    while(e)
    {
        SigBucket * next = e->next;

        if (fprintf(out, MANIFEST_KEYWORD_DEF, (ULONGLONG) e->value, e->key, e->key) == -1)
            goto error;

        e = next;
    }
    if (fprintf(out, MANIFEST_KEYWORD_CLOSE) == -1)
        goto error;

    return MI_TRUE;

error:
    OIERROR("_WriteTaskOpcodeKeywords failed to write to file");
    return MI_FALSE;
}

static MI_Boolean _WriteChannels(_In_ FILE * out, _In_z_ const char * providerName)
{
    int i;
    int count = (int)(sizeof(ETWChannelIDs)/sizeof(char *));

    if (fprintf(out, MANIFEST_CHANNEL_TAG) == -1)
    {
        OIERROR("Failed to write to file\n");
        return MI_FALSE;
    }

    for(i = 0; i + 1 < count; i += 2)
    {
        if (fprintf(out, MANIFEST_CHANNEL, ETWChannelIDs[i+1], "true", providerName, ETWChannelIDs[i], ETWChannelIDs[i]) == -1)
        {
            OIERROR1("Failed to write to file, channel = [%s]\n", ETWChannelIDs[i]);
            return MI_FALSE;
        }
    }

    if (fprintf(out, MANIFEST_CHANNEL_CLOSE) == -1)
    {
        OIERROR("Failed to write to file\n");
        return MI_FALSE;
    }

    return MI_TRUE;
}

static MI_Boolean _WriteEvents(_In_ FILE * out, _In_ OIEvent * events)
{
    OIEvent * e = events;

    if (fprintf(out, MANIFEST_EVENTSTAG) == -1)
    {
        OIERROR("Failed to write to file\n");
        return MI_FALSE;
    }

    /* activity transfer event */
    if (fprintf(out, EVENT_START) == -1)
        goto error;
    if (fprintf(out, EVENT_CHANNEL, gTransferEvent[2]) == -1)
        goto error;
    if (fprintf(out, EVENT_LEVELMESSAGESYMBOL, gTransferEvent[3], gTransferEvent[0], gTransferEvent[0]) == -1)
        goto error;
    if (fprintf(out, EVENT_TEMPLATEVALUE, gTransferEvent[1], gTransferEvent[1]) == -1)
        goto error;

    /* rest of events */
    while(e)
    {
        OIEvent * next = e->next;

        if (fprintf(out, EVENT_START) == -1)
        {
            OIERROR2("Failed to write to file, eventid = [%s] format = [%s]\n", e->EventId, e->Format);
            return MI_FALSE;
        }

        if (e->Channel && Strlen(e->Channel) > 0)
        {
            const char * chid = _GetChannelID(e->Channel);
            if (!chid)
            {
                OIERROR1("Failed to find channel id for channel [%s]\n", e->Channel);
                return MI_FALSE;
            }

            if (fprintf(out, EVENT_CHANNEL, chid) == -1)
            {
                OIERROR2("Failed to write to file, eventid = [%s] format = [%s]\n", e->EventId, e->Format);
                return MI_FALSE;
            }
        }

        /* 
        Note, keyword attribute is not supported in the schema for <event> element.
        Instead, it gets passed during API call to ETW in the generated code.
        */

        if (e->Severity && Strlen(e->Severity) > 0)
        {
            if (fprintf(out, EVENT_LEVELMESSAGESYMBOL, e->Severity, e->Name, e->Name) == -1)
            {
                OIERROR2("Failed to write to file, eventid = [%s] format = [%s]\n", e->EventId, e->Format);
                return MI_FALSE;
            }
        }

        if (e->Task && Strlen(e->Task) > 0)
        {
            if (fprintf(out, EVENT_TASK, e->Task) == -1)
            {
                OIERROR2("Failed to write to file, eventid = [%s] format = [%s]\n", e->EventId, e->Format);
                return MI_FALSE;
            }
        }

        if (fprintf(out, EVENT_TEMPLATEVALUE, e->EventId, e->EventId) == -1)
        {
            OIERROR2("Failed to write to file, eventid = [%s] format = [%s]\n", e->EventId, e->Format);
            return MI_FALSE;
        }

        e = next;
    }

    return MI_TRUE;

error:
    OIERROR("Failed to write to file");
    return MI_FALSE;
}

static MI_Boolean _WriteTemplates(_In_ FILE * out, _In_ OIEvent * events)
{
    OIEvent * e = events;

    /* activity transfer event */
    if (fprintf(out, TEMPLATE_EMPTY, gTransferEvent[1]) == -1)
    {
        OIERROR("Failed to write to file");
        return MI_FALSE;
    }

    while(e)
    {
        OIEvent * next = e->next;
        OIArgument * arg = e->Argument;

        if (!arg)
        {
            if (fprintf(out, TEMPLATE_EMPTY, e->EventId) == -1)
            {
                OIERROR1("Failed to write to file %s\n", e->EventId);
                return MI_FALSE;
            }
        }
        else
        {
            if (fprintf(out, TEMPLATE_HEADER, e->EventId) == -1)
            {
                OIERROR1("Failed to write to file %s\n", e->EventId);
                return MI_FALSE;
            }

            while(arg)
            {
                OIArgument * next = arg->next;
                const char * manifestType = _GetManifestType(arg->Type);
                if (!manifestType)
                {
                    OIERROR1("Failed to convert to manifest type for type %s\n", arg->Type);
                    return MI_FALSE;
                }

                if (fprintf(out, DATA_TEMPLATE, manifestType, arg->Name) == -1)
                {
                    OIERROR1("Failed to write to file %s\n", e->EventId);
                    return MI_FALSE;
                }

                arg = next;
            }

            if (fprintf(out, TEMPLATE_TAIL) == -1)
            {
                OIERROR1("Failed to write to file %s\n", e->EventId);
                return MI_FALSE;
            }
        }

        e = next;
    }

    return MI_TRUE;
}

/*
Replaces %[{h | l | I | I32 | I64}]type with %N, where N is incremented for each argument, starting with 1
*/
static MI_Boolean _PrintfFormatToManifestFormat(_In_z_ const char * in, _Out_writes_z_(size) char * out, int size)
{
    int argCount = 1; /* manifest argument count starts at 1 */
    int copySize = 0;
    const char * original = in;

    /* 
        state 0 - normal reading 
        state 1 - found %, checking to see if another '%' follows, otherwise it's an argument

        note %% is not an argument insertion
    */
    int state = 0;

    *out = 0;

    while(*in && copySize < size - 1)
    {
        if (state == 0)
        {
            if (*in == '%')
            {
                state = 1;
            }

            /* copy char and move on */
            *out = *in;
            copySize++;
            in++;
            out++;
        }
        else
        {
            state = 0;

            /* we have just passed '%' */
            if(*in == '%')
            {
                /* We found '%%', just copy char and move on */
                *out = *in;                
            }   
            else
            {
                switch (*in)
                {
                case 'l':
                case 'h':
                    /* skip over one more char */
                    in++;
                    if (*in == 0)
                    {
                        OIERROR1("Unexpected end of string while parsing format [%s]!\n", original);
                        return MI_FALSE;
                    }
                    break;
                case 'I':
                    /* I or I32 or I64 are possible here */
                    in++; /* skip over I */
                    if (*in == 0)
                    {
                        OIERROR1("Unexpected end of string while parsing format [%s]!\n", original);
                        return MI_FALSE;
                    }
                    if (*in == '3' || *in == '6')
                    {
                        /* skip over 32 or 64 */
                        in++;
                        if (*in == 0)
                        {
                            OIERROR1("Unexpected end of string while parsing format [%s]!\n", original);
                            return MI_FALSE;
                        }
                        in++;
                        if (*in == 0)
                        {
                            OIERROR1("Unexpected end of string while parsing format [%s]!\n", original);
                            return MI_FALSE;
                        }
                    }
                    break;
                default:
                    break;
                }

                /* insert arg number */
                *out = '0' + argCount;
                argCount++;
            }
            
            copySize++;
            in++;
            out++;
        }
    }

    *out = 0;

    if (copySize >= size - 1)
    {
        OIERROR("_PrintfFormatToManifestFormat internal error!\n");
        return MI_FALSE;
    }
    else
        return MI_TRUE;
}

static MI_Boolean _WriteStrings(_In_ FILE * out, _In_ OIEvent * events, _In_z_ const char * providerName, _In_ Buckets * etwInfo)
{
    SigBucket * i;
    OIEvent * e = events;

    if (fprintf(out, STRING_TEMPLATE_QUOTES, "Provider.Name", providerName) == -1)
    {
        OIERROR1("Failed to write to file while [%s]\n", providerName);
        return MI_FALSE;
    }

    /* Tasks */
    i = etwInfo->tasks;
    while(i)
    {
        SigBucket * next = i->next;
        if (fprintf(out, STRING_TASKOPCODE_TEMPLATE, "Task", i->key, i->key) == -1)
            goto error;
        i = next;
    }

    /* Opcodes */
    i = etwInfo->opcodes;
    while(i)
    {
        SigBucket * next = i->next;
        if (fprintf(out, STRING_TASKOPCODE_TEMPLATE, "Opcode", i->key, i->key) == -1)
            goto error;
        i = next;
    }

    /* activity transfer event */
    if (fprintf(out, STRING_TEMPLATE_QUOTES, gTransferEvent[0], gTransferEvent[0]) == -1)
        goto error;

    /* Event format strings */
    while(e)
    {
        OIEvent * next = e->next;
        char buf[BUFFER_SIZE];
        buf[0] = 0;

        if (!_PrintfFormatToManifestFormat(e->Format, buf, BUFFER_SIZE))
        {
            OIERROR1("_PrintfFormatToManifestFormat failed for event [%s]\n", e->EventId);
            return MI_FALSE;
        }

        if (fprintf(out, STRING_TEMPLATE, e->Name, buf) == -1)
            goto error;

        e = next;
    }

    return MI_TRUE;
error:
    OIERROR("_WriteStrings failed to write to file");
    return MI_FALSE;
}

/********* PUBLIC DEFINITIONS ***************************************/

_Use_decl_annotations_
MI_Boolean GenerateManifest(
    OIEvent * events,
    Buckets * etwInfo,
    char * providerName,
    char * providerId,
    char * binary,
    char * target)
{
    /* overwrite existing file */
    char buf[BUFFER_SIZE];
    FILE* out = fopen(target, "w");
    if (!out)
    {
        OIERROR1("Failed to open file [%s]\n", target);
        return MI_FALSE;
    }

    if (fprintf(out, MANIFEST_HEADER) == -1)
        goto error;

    if (fprintf(out, MANIFEST_PROVIDERDECL, providerId, binary, providerName, binary, binary, providerName) == -1)
        goto error;

    if (!_WriteChannels(out, providerName))
        goto error;

    if (!_WriteTaskOpcodeKeywords(out, etwInfo))
        goto error;

    if (!_WriteEvents(out, events))
        goto error;

    if (fprintf(out, TEMPLATES_HEADER) == -1)
        goto error;

    if (!_WriteTemplates(out, events))
        goto error;

    if (fprintf(out, STRINGTABLE_HEADER) == -1)
        goto error;

    if (!_WriteStrings(out, events, providerName, etwInfo))
        goto error;

    if (fprintf(out, MANIFEST_TAIL) == -1)
        goto error;

    fclose(out);
    return MI_TRUE;

error:
    OIERROR("GenerateManifest failed!\n");
    fclose(out);
    return MI_FALSE;
}

ViewCVS 0.9.2