## ## Licensed to the .NET Foundation under one or more agreements. ## The .NET Foundation licenses this file to you under the MIT license. ## See the LICENSE file in the project root for more information. ## ## 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. // See the LICENSE file in the project root for more information. /****************************************************************** DO NOT MODIFY. AUTOGENERATED FILE. This file is generated using the logic from /src/scripts/genLttngProvider.py ******************************************************************/ """ specialCaseSizes = { "BulkType" : { "Values" : "Values_ElementSize" }, "GCBulkRootCCW" : { "Values" : "Values_ElementSize" }, "GCBulkRCW" : { "Values" : "Values_ElementSize" }, "GCBulkRootStaticVar" : { "Values" : "Values_ElementSize" } } lttngDataTypeMapping ={ #constructed types "win:null" :" ", "win:Int64" :"const __int64", "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 unsigned __int64", "win:UInt32" :"const unsigned int", "win:UInt16" :"const unsigned short", "win:UInt8" :"const unsigned char", "win:Pointer" :"const size_t", "win:Binary" :"const BYTE" } 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): 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 = lttngDataTypeMapping[wintypeName] winCount = fnparam.count countw = lttngDataTypeMapping[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): 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 = lttngDataTypeMapping[winCount] typewName = lttngDataTypeMapping[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): 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) 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) 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 ") return ''.join(lTTngHdr) def generateMethodBody(template, providerName, eventName): #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(" = (wcslen(" + 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 += "(" + lttngDataTypeMapping[wintypeName] if not lttngDataTypeMapping[winCount] == " ": line += lttngDataTypeMapping[winCount] line += ") " linefnbody.append(line + paramname) else: linefnbody.append(line + paramname) linefnbody = ",\n".join(linefnbody) + ");\n" result.append(linefnbody) return ''.join(result) else: header = """ char stackBuffer[%s]; char *buffer = stackBuffer; size_t offset = 0; size_t size = %s; bool fixedBuffer = true; bool success = true; """ % (template.estimated_size, template.estimated_size) footer = """ if (!fixedBuffer) delete[] buffer; """ pack_list = [] 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)) elif paramName in template.arrays: size = "sizeof(%s) * (int)%s" % (lttngDataTypeMapping[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)) elif parameter.winType == "win:GUID": pack_list.append(" success &= WriteToBuffer(*%s, buffer, offset, size, fixedBuffer);" % (parameter.name,)) else: pack_list.append(" success &= WriteToBuffer(%s, buffer, offset, size, fixedBuffer);" % (parameter.name,)) code = "\n".join(pack_list) + "\n\n" tracepoint = """ if (!success) { if (!fixedBuffer) delete[] buffer; return ERROR_WRITE_FAULT; } do_tracepoint(%s, %s, offset, buffer);\n""" % (providerName, eventName) return header + code + tracepoint + footer def generateLttngTpProvider(providerName, eventNodes, allTemplates): lTTngImpl = [] 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 = palDataTypeMapping[wintypeName] winCount = fnparam.count countw = palDataTypeMapping[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) lTTngImpl.append(result) lTTngImpl.append("\n return ERROR_SUCCESS;\n}\n\n") return ''.join(lTTngImpl) def generateLttngFiles(etwmanifest,eventprovider_directory, 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 \n\n") lttnghdr_file.write(generateLttngHeader(providerName,allTemplates,eventNodes) + "\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" #define PAL_free free #define PAL_realloc realloc #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 #define wcslen PAL_wcslen 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 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) + "\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 containig the description of events') required.add_argument('--intermediate', type=str, required=True, help='full path to eventprovider intermediate directory') 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 dryRun = args.dry_run generateLttngFiles(sClrEtwAllMan,intermediate, dryRun) if __name__ == '__main__': return_code = main(sys.argv[1:]) sys.exit(return_code)