1
0
Fork 0
mirror of https://github.com/VSadov/Satori.git synced 2025-06-08 11:37:04 +09:00
Satori/src/coreclr/scripts/genLttngProvider.py

655 lines
24 KiB
Python

##
## Licensed to the .NET Foundation under one or more agreements.
## The .NET Foundation licenses this file to you under the MIT license.
##
## Sample LTTng Instrumentation code that is generated:
##
## HEADER:
## #define GCFinalizersEnd_TRACEPOINT_ARGS \
##TP_ARGS(\
## const unsigned int ,Count\
##)
##TRACEPOINT_EVENT_CLASS(
## DotNETRuntime,
## GCFinalizersEnd,
## GCFinalizersEnd_TRACEPOINT_ARGS,
## TP_FIELDS(
## ctf_integer(unsigned int, Count, Count)
## )
##)
##
##CPP :
##
##extern "C" BOOL EventXplatEnabledGCFinalizersEnd(){ return TRUE;}
##extern "C" ULONG FireEtXplatGCFinalizersEnd(
## const unsigned int Count
##)
##{
## ULONG Error = ERROR_WRITE_FAULT;
## if (!EventXplatEnabledGCFinalizersEnd()){ return ERROR_SUCCESS;}
##
##
## tracepoint(
## DotNETRuntime,
## GCFinalizersEnd,
## Count
## );
## Error = ERROR_SUCCESS;
##
##return Error;
##}
##
###define GCFinalizersEndT_TRACEPOINT_INSTANCE(name) \
##TRACEPOINT_EVENT_INSTANCE(\
## DotNETRuntime,\
## GCFinalizersEnd,\
## name ,\
## GCFinalizersEnd_TRACEPOINT_ARGS \
##)
#
import os
from genEventing import *
from utilities import open_for_update
stdprolog="""
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
/******************************************************************
DO NOT MODIFY. AUTOGENERATED FILE.
This file is generated using the logic from <root>/src/scripts/genLttngProvider.py
******************************************************************/
"""
specialCaseSizes = { "BulkType" : { "Values" : "Values_ElementSize" }, "GCBulkRootCCW" : { "Values" : "Values_ElementSize" }, "GCBulkRCW" : { "Values" : "Values_ElementSize" }, "GCBulkRootStaticVar" : { "Values" : "Values_ElementSize" } }
coreCLRLttngDataTypeMapping ={
#constructed types
"win:null" :" ",
"win:Int64" :"const int64_t",
"win:ULong" :"const ULONG",
"win:count" :"*",
"win:Struct" :"const BYTE *",
#actual spec
"win:GUID" :"const int",
"win:AnsiString" :"const char*",
"win:UnicodeString" :"const char*",
"win:Double" :"const double",
"win:Int32" :"const signed int",
"win:Boolean" :"const BOOL",
"win:UInt64" :"const uint64_t",
"win:UInt32" :"const unsigned int",
"win:UInt16" :"const unsigned short",
"win:UInt8" :"const unsigned char",
"win:Pointer" :"const size_t",
"win:Binary" :"const BYTE"
}
portableLttngDataTypeMapping ={
#constructed types
"win:null" :" ",
"win:Int64" :"const int64_t",
"win:ULong" :"const uint32_t",
"win:count" :"*",
"win:Struct" :"const uint8_t *",
#actual spec
"win:GUID" :"const int32_t",
"win:AnsiString" :"const char*",
"win:UnicodeString" :"const ep_char8_t*",
"win:Double" :"const double",
"win:Int32" :"const int32_t",
"win:Boolean" :"const bool",
"win:UInt64" :"const uint64_t",
"win:UInt32" :"const uint32_t",
"win:UInt16" :"const uint16_t",
"win:UInt8" :"const uint8_t",
"win:Pointer" :"const void*",
"win:Binary" :"const uint8_t"
}
def getLttngDataTypeMapping(runtimeFlavor):
if runtimeFlavor.coreclr:
return coreCLRLttngDataTypeMapping
else:
return portableLttngDataTypeMapping
ctfDataTypeMapping ={
#constructed types
"win:Int64" :"ctf_integer",
"win:ULong" :"ctf_integer",
"win:count" :"ctf_sequence",
"win:Struct" :"ctf_sequence",
#actual spec
"win:GUID" :"ctf_sequence",
"win:AnsiString" :"ctf_string",
"win:UnicodeString" :"ctf_string",
"win:Double" :"ctf_float",
"win:Int32" :"ctf_integer",
"win:Boolean" :"ctf_integer",
"win:UInt64" :"ctf_integer",
"win:UInt32" :"ctf_integer",
"win:UInt16" :"ctf_integer",
"win:UInt8" :"ctf_integer", #actually a character
"win:Pointer" :"ctf_integer",
"win:Binary" :"ctf_sequence"
}
MAX_LTTNG_ARGS = 9
def shouldPackTemplate(template):
return template.num_params > MAX_LTTNG_ARGS or len(template.structs) > 0 or len(template.arrays) > 0
def generateArgList(template, runtimeFlavor):
header = "TP_ARGS( \\\n"
footer = ")\n"
if "MethodILToNative" in template.name:
pass
if shouldPackTemplate(template):
args = " const unsigned int, length, \\\n"
args += " const char *, __data__ \\\n"
else:
fnSig = template.signature
args = []
for params in fnSig.paramlist:
fnparam = fnSig.getParam(params)
wintypeName = fnparam.winType
typewName = getLttngDataTypeMapping(runtimeFlavor)[wintypeName]
winCount = fnparam.count
countw = getLttngDataTypeMapping(runtimeFlavor)[winCount]
arg = " " + typewName
if countw != " ":
arg += countw
arg += ", " + fnparam.name
args.append(arg)
args = ", \\\n".join(args) + " \\\n"
return header + args + footer
def generateFieldList(template, runtimeFlavor):
header = " " + " TP_FIELDS(\n"
footer = "\n )\n)\n"
if shouldPackTemplate(template):
field_list = " ctf_integer(ULONG, length, length)\n"
field_list += " ctf_sequence(char, __data__, __data__, ULONG, length)"
else:
fnSig = template.signature
field_list = []
for params in fnSig.paramlist:
fnparam = fnSig.getParam(params)
wintypeName = fnparam.winType
winCount = fnparam.count
countw = getLttngDataTypeMapping(runtimeFlavor)[winCount]
typewName = getLttngDataTypeMapping(runtimeFlavor)[wintypeName].replace("const ","")
field_body = None
ctf_type = None
varname = fnparam.name
if fnparam.prop:
#this is an explicit struct treat as a sequence
ctf_type = "ctf_sequence"
sizeofseq = fnparam.prop
field_body = ", ".join((typewName, varname, varname, "size_t", sizeofseq))
else:
ctf_type = ctfDataTypeMapping[wintypeName]
if ctf_type == "ctf_string":
field_body = ", ".join((varname, varname))
elif ctf_type == "ctf_integer" or ctf_type == "ctf_float":
field_body = ", ".join((typewName, varname, varname))
elif ctf_type == "ctf_sequence":
raise Exception("ctf_sequence needs to have its memory expilicitly laid out")
else:
raise Exception("no such ctf intrinsic called: " + ctf_type)
field_list.append(" %s(%s)" % (ctf_type, field_body))
field_list = "\n".join(field_list)
return header + field_list + footer
def generateLttngHeader(providerName, allTemplates, eventNodes, runtimeFlavor):
lTTngHdr = []
for templateName in allTemplates:
template = allTemplates[templateName]
fnSig = allTemplates[templateName].signature
lTTngHdr.append("\n#define " + templateName + "_TRACEPOINT_ARGS \\\n")
#TP_ARGS
tp_args = generateArgList(template, runtimeFlavor)
lTTngHdr.append(tp_args)
#TP_EVENT_CLASS
lTTngHdr.append("TRACEPOINT_EVENT_CLASS(\n")
lTTngHdr.append(" " + providerName + ",\n")
lTTngHdr.append(" " + templateName + ",\n")
lTTngHdr.append(" " + templateName + "_TRACEPOINT_ARGS,\n")
#TP_FIELDS
tp_fields = generateFieldList(template, runtimeFlavor)
lTTngHdr.append(tp_fields)
# Macro for defining event instance
lTTngHdr.append("\n#define " + templateName)
lTTngHdr.append("""T_TRACEPOINT_INSTANCE(name) \\
TRACEPOINT_EVENT_INSTANCE(\\
""")
lTTngHdr.append(" "+providerName + ",\\\n")
lTTngHdr.append(" " + templateName + ",\\\n")
lTTngHdr.append(" name ,\\\n")
lTTngHdr.append(" " + templateName + "_TRACEPOINT_ARGS \\\n)")
#add an empty template node to just specify the event name in the event stream
lTTngHdr.append("\n\nTRACEPOINT_EVENT_CLASS(\n")
lTTngHdr.append(" " + providerName + ",\n")
lTTngHdr.append(" emptyTemplate ,\n")
lTTngHdr.append(""" TP_ARGS(),
TP_FIELDS()
)
#define T_TRACEPOINT_INSTANCE(name) \\
TRACEPOINT_EVENT_INSTANCE(\\
""")
lTTngHdr.append(" " + providerName + ",\\\n")
lTTngHdr.append(" emptyTemplate,\\\n")
lTTngHdr.append(""" name ,\\
TP_ARGS()\\
)""")
#end of empty template
# create the event instance in headers
lTTngHdr.append("\n")
for eventNode in eventNodes:
eventName = eventNode.getAttribute('symbol');
templateName = eventNode.getAttribute('template');
if not eventName :
raise Exception(eventNode + " event does not have a symbol")
if not templateName:
lTTngHdr.append("T_TRACEPOINT_INSTANCE(")
lTTngHdr.append(eventName +")\n")
continue
subevent = templateName.replace(templateName,'')
lTTngHdr.append(templateName)
lTTngHdr.append("T_TRACEPOINT_INSTANCE(")
lTTngHdr.append(eventName + subevent + ")\n")
lTTngHdr.append("\n#endif /* LTTNG_CORECLR_H")
lTTngHdr.append(providerName + " */\n")
lTTngHdr.append("#include <lttng/tracepoint-event.h>")
return ''.join(lTTngHdr)
def generateMethodBody(template, providerName, eventName, runtimeFlavor):
#emit code to init variables convert unicode to ansi string
result = []
if template is None:
return "\n do_tracepoint(%s, %s);\n" % (providerName, eventName)
fnSig = template.signature
for paramName in fnSig.paramlist:
fnparam = fnSig.getParam(paramName)
paramname = fnparam.name
if fnparam.winType == "win:UnicodeString":
result.append(" INT " + paramname + "_path_size = -1;\n")
result.append(" PathCharString " + paramname + "_PS;\n")
result.append(" INT " + paramname + "_full_name_path_size")
result.append(" = (lttng_strlen16(" + paramname + ") + 1)*sizeof(WCHAR);\n")
result.append(" CHAR* " + paramname + "_full_name = ")
result.append(paramname + "_PS.OpenStringBuffer(" + paramname + "_full_name_path_size );\n")
result.append(" if (" + paramname + "_full_name == NULL )")
result.append(" { return ERROR_WRITE_FAULT; }\n")
result.append("\n")
#emit tracepoints
fnSig = template.signature
if not shouldPackTemplate(template):
linefnbody = [" do_tracepoint(%s,\n %s" % (providerName, eventName)]
for params in fnSig.paramlist:
fnparam = fnSig.getParam(params)
wintypeName = fnparam.winType
winCount = fnparam.count
paramname = fnparam.name
ctf_type = ctfDataTypeMapping.get(winCount)
line = " "
if not ctf_type:
ctf_type = ctfDataTypeMapping[wintypeName]
if ctf_type == "ctf_string" and wintypeName == "win:UnicodeString":
#emit code to convert unicode to ansi string
result.append(" " + paramname+ "_path_size = WideCharToMultiByte( CP_ACP, 0, ")
result.append(paramname + ", -1, ")
result.append(paramname + "_full_name, ")
result.append(paramname + "_full_name_path_size, NULL, NULL );\n")
result.append(" _ASSERTE(" +paramname+ "_path_size < " )
result.append(paramname + "_full_name_path_size );\n ")
result.append(paramname + "_PS.CloseBuffer(" + paramname + "_path_size );\n")
result.append(" if( " + paramname + "_path_size == 0 ){ return ERROR_INVALID_PARAMETER; }\n")
line += paramname + "_full_name"
linefnbody.append(line)
continue
elif ctf_type == "ctf_sequence" or wintypeName == "win:Pointer":
line += "(" + getLttngDataTypeMapping(runtimeFlavor)[wintypeName]
if not getLttngDataTypeMapping(runtimeFlavor)[winCount] == " ":
line += getLttngDataTypeMapping(runtimeFlavor)[winCount]
line += ") "
linefnbody.append(line + paramname)
else:
linefnbody.append(line + paramname)
linefnbody = ",\n".join(linefnbody) + ");\n"
result.append(linefnbody)
return ''.join(result)
else:
pack_list = []
emittedWriteToBuffer = False
for paramName in fnSig.paramlist:
parameter = fnSig.getParam(paramName)
if paramName in template.structs:
size = "(int)%s_ElementSize * (int)%s" % (paramName, parameter.prop)
if template.name in specialCaseSizes and paramName in specialCaseSizes[template.name]:
size = "(int)(%s)" % specialCaseSizes[template.name][paramName]
pack_list.append(" success &= WriteToBuffer((const BYTE *)%s, %s, buffer, offset, size, fixedBuffer);" % (paramName, size))
emittedWriteToBuffer = True
elif paramName in template.arrays:
size = "sizeof(%s) * (int)%s" % (getLttngDataTypeMapping(runtimeFlavor)[parameter.winType], parameter.prop)
if template.name in specialCaseSizes and paramName in specialCaseSizes[template.name]:
size = "(int)(%s)" % specialCaseSizes[template.name][paramName]
pack_list.append(" success &= WriteToBuffer((const BYTE *)%s, %s, buffer, offset, size, fixedBuffer);" % (paramName, size))
emittedWriteToBuffer = True
elif parameter.winType == "win:GUID":
pack_list.append(" success &= WriteToBuffer(*%s, buffer, offset, size, fixedBuffer);" % (parameter.name,))
emittedWriteToBuffer = True
else:
pack_list.append(" success &= WriteToBuffer(%s, buffer, offset, size, fixedBuffer);" % (parameter.name,))
emittedWriteToBuffer = True
header = """
size_t size = {0:d};
char stackBuffer[{0:d}];
char *buffer = stackBuffer;
size_t offset = 0;
""".format(template.estimated_size)
code = "\n".join(pack_list) + "\n\n"
tracepoint = ""
footer = ""
if emittedWriteToBuffer:
header += """
bool fixedBuffer = true;
bool success = true;
"""
tracepoint = """ if (!success)
{
if (!fixedBuffer)
delete[] buffer;
return ERROR_WRITE_FAULT;
}
do_tracepoint(%s, %s, offset, buffer);\n""" % (providerName, eventName)
footer = """
if (!fixedBuffer)
delete[] buffer;
"""
return header + code + tracepoint + footer
def generateLttngTpProvider(providerName, eventNodes, allTemplates, runtimeFlavor):
lTTngImpl = []
lTTngImpl.append("""// Local implementation of WCHAR (UTF-16) string length
static size_t lttng_strlen16(const WCHAR* string)
{
size_t nChar = 0;
while (*string++)
nChar++;
return nChar;
}
""")
for eventNode in eventNodes:
eventName = eventNode.getAttribute('symbol')
templateName = eventNode.getAttribute('template')
#generate EventXplatEnabled
lTTngImpl.append("extern \"C\" BOOL EventXplatEnabled%s(){ return tracepoint_enabled(%s, %s); }\n\n" % (eventName, providerName, eventName))
#generate FireEtw functions
fnptype = []
linefnptype = []
fnptype.append("extern \"C\" ULONG FireEtXplat")
fnptype.append(eventName)
fnptype.append("(\n")
if templateName:
template = allTemplates[templateName]
else:
template = None
if template:
fnSig = template.signature
for paramName in fnSig.paramlist:
fnparam = fnSig.getParam(paramName)
wintypeName = fnparam.winType
typewName = getPalDataTypeMapping(runtimeFlavor)[wintypeName]
winCount = fnparam.count
countw = getPalDataTypeMapping(runtimeFlavor)[winCount]
if paramName in template.structs:
linefnptype.append("%sint %s_ElementSize,\n" % (lindent, paramName))
linefnptype.append(lindent)
linefnptype.append(typewName)
if countw != " ":
linefnptype.append(countw)
linefnptype.append(" ")
linefnptype.append(fnparam.name)
linefnptype.append(",\n")
if len(linefnptype) > 0 :
del linefnptype[-1]
fnptype.extend(linefnptype)
fnptype.append(")\n{\n")
lTTngImpl.extend(fnptype)
#start of fn body
lTTngImpl.append(" if (!EventXplatEnabled%s())\n" % (eventName,))
lTTngImpl.append(" return ERROR_SUCCESS;\n")
result = generateMethodBody(template, providerName, eventName, runtimeFlavor)
lTTngImpl.append(result)
lTTngImpl.append("\n return ERROR_SUCCESS;\n}\n\n")
return ''.join(lTTngImpl)
def generateLttngFiles(etwmanifest, eventprovider_directory, runtimeFlavor, dryRun):
eventprovider_directory = eventprovider_directory + "/"
tree = DOM.parse(etwmanifest)
#keep these relative
tracepointprovider_directory = "tracepointprovider"
lttng_directory = "lttng"
lttngevntprovPre = lttng_directory + "/eventprov"
lttngevntprovTpPre = lttng_directory + "/traceptprov"
if not os.path.exists(eventprovider_directory):
os.makedirs(eventprovider_directory)
if not os.path.exists(eventprovider_directory + lttng_directory):
os.makedirs(eventprovider_directory + lttng_directory)
if not os.path.exists(eventprovider_directory + tracepointprovider_directory):
os.makedirs(eventprovider_directory + tracepointprovider_directory)
# Generate Lttng specific instrumentation
for providerNode in tree.getElementsByTagName('provider'):
providerName = providerNode.getAttribute('name')
providerName = providerName.replace("Windows-",'')
providerName = providerName.replace("Microsoft-",'')
providerName_File = providerName.replace('-','')
providerName_File = providerName_File.lower()
providerName = providerName.replace('-','_')
lttngevntheadershortname = "tp" + providerName_File +".h"
lttngevntheader = eventprovider_directory + "lttng/" + lttngevntheadershortname
lttngevntprov = eventprovider_directory + lttngevntprovPre + providerName_File + ".cpp"
lttngevntprovTp = eventprovider_directory + lttngevntprovTpPre + providerName_File +".cpp"
templateNodes = providerNode.getElementsByTagName('template')
eventNodes = providerNode.getElementsByTagName('event')
allTemplates = parseTemplateNodes(templateNodes)
if dryRun:
print(lttngevntheader)
print(lttngevntprov)
print(lttngevntprovTp)
else:
with open_for_update(lttngevntheader) as lttnghdr_file:
lttnghdr_file.write(stdprolog + "\n")
lttnghdr_file.write("""
#include "palrt.h"
#include "pal.h"
#undef TRACEPOINT_PROVIDER
""")
lttnghdr_file.write("#define TRACEPOINT_PROVIDER " + providerName + "\n")
lttnghdr_file.write("""
#undef TRACEPOINT_INCLUDE
""")
lttnghdr_file.write("#define TRACEPOINT_INCLUDE \"./" + lttngevntheadershortname + "\"\n\n")
lttnghdr_file.write("#if !defined(LTTNG_CORECLR_H" + providerName + ") || defined(TRACEPOINT_HEADER_MULTI_READ)\n\n")
lttnghdr_file.write("#define LTTNG_CORECLR_H" + providerName + "\n")
lttnghdr_file.write("\n#include <lttng/tracepoint.h>\n\n")
lttnghdr_file.write(generateLttngHeader(providerName,allTemplates,eventNodes,runtimeFlavor) + "\n")
with open_for_update(lttngevntprov) as lttngimpl_file:
lttngimpl_file.write(stdprolog + "\n")
lttngimpl_file.write("""
#define TRACEPOINT_DEFINE
#define TRACEPOINT_PROBE_DYNAMIC_LINKAGE
#include "stdlib.h"
#include "pal_mstypes.h"
#include "pal_error.h"
#include "pal.h"
#include "pal/stackstring.hpp"
""")
lttngimpl_file.write("#include \"" + lttngevntheadershortname + "\"\n\n")
lttngimpl_file.write("""#ifndef tracepoint_enabled
extern "C" bool XplatEventLoggerIsEnabled();
#define tracepoint_enabled(provider, name) XplatEventLoggerIsEnabled()
#define do_tracepoint tracepoint
#endif
bool ResizeBuffer(char *&buffer, size_t& size, size_t currLen, size_t newSize, bool &fixedBuffer);
bool WriteToBuffer(PCWSTR str, char *&buffer, size_t& offset, size_t& size, bool &fixedBuffer);
bool WriteToBuffer(const char *str, char *&buffer, size_t& offset, size_t& size, bool &fixedBuffer);
bool WriteToBuffer(const BYTE *src, size_t len, char *&buffer, size_t& offset, size_t& size, bool &fixedBuffer);
template <typename T>
bool WriteToBuffer(const T &value, char *&buffer, size_t& offset, size_t& size, bool &fixedBuffer)
{
if (sizeof(T) + offset > size)
{
if (!ResizeBuffer(buffer, size, offset, size + sizeof(T), fixedBuffer))
return false;
}
memcpy(buffer + offset, (char *)&value, sizeof(T));
offset += sizeof(T);
return true;
}
""")
lttngimpl_file.write(generateLttngTpProvider(providerName,eventNodes,allTemplates,runtimeFlavor) + "\n")
with open_for_update(lttngevntprovTp) as tpimpl_file:
tpimpl_file.write(stdprolog + "\n")
tpimpl_file.write("\n#define TRACEPOINT_CREATE_PROBES\n")
tpimpl_file.write("#include \"./"+lttngevntheadershortname + "\"\n")
import argparse
import sys
def main(argv):
#parse the command line
parser = argparse.ArgumentParser(description="Generates the Code required to instrument LTTtng logging mechanism")
required = parser.add_argument_group('required arguments')
required.add_argument('--man', type=str, required=True,
help='full path to manifest containing the description of events')
required.add_argument('--intermediate', type=str, required=True,
help='full path to eventprovider intermediate directory')
required.add_argument('--runtimeflavor', type=str,default="CoreCLR",
help='runtime flavor')
required.add_argument('--dry-run', action='store_true',
help='if specified, will output the names of the generated files instead of generating the files' )
args, unknown = parser.parse_known_args(argv)
if unknown:
print('Unknown argument(s): ', ', '.join(unknown))
return 1
sClrEtwAllMan = args.man
intermediate = args.intermediate
runtimeFlavor = RuntimeFlavor(args.runtimeflavor)
dryRun = args.dry_run
generateLttngFiles(sClrEtwAllMan, intermediate, runtimeFlavor, dryRun)
if __name__ == '__main__':
return_code = main(sys.argv[1:])
sys.exit(return_code)