[mono][eventpipe] Implementing MethodDetails and BulkType events (#68571)
authorMitchell Hwang <16830051+mdh1418@users.noreply.github.com>
Thu, 19 May 2022 16:26:53 +0000 (12:26 -0400)
committerGitHub <noreply@github.com>
Thu, 19 May 2022 16:26:53 +0000 (12:26 -0400)
* [coreclr]Extend lttngDataTypeMapping for mono

Writing arrays in generateWriteEventBody had not been previously hit on Mono.
With a goal of emitting MethodDetails events on Mono, lttngDataTypeMapping
needs to be extended to be compatible with mono types.

* [mono][eventpipe] Implement SendMethodDetailsEvents on mono

* [mono][eventpipe] Utilize method signature getter to avoid uncreated signature

* [mono][eventpipe] Implement BulkType on mono

* debug traces

* Address struct tab formatting feedback

* Address rg_type_parameter preallocated stack array size

* [mono][eventpipe] Add helper function to get mono type unique type identifier

* [mono][eventpipe] Differentiate MonoType and TypeID and pass separately

* Address array max rank feedback

* Address byte_count bug feedback

* Address Etw Type Flags feedback

* Address cor_element_type underlying type feedback

* Address logger instance and if_necessary feedback

* Fix s_name

* Cleanup comment tab spacing

* Address feedback

Fix bulk type event buffer

Fix mono type check

Fix mono type check

Clear bulk type event logger

Debug bulktype event firing

Fix tab bulktype firing

Fix fixed_sized_data byte copy

Address constants feedback

Add maximum event size constant
Add maximum bulk type value type parameters constant
Add maximum method type argument count constant

Fix typo

Address BulkType Value s_name type size feedback

Address softcode buffer size increment

Address switch case feedback

Address fixed sized array feedback

Fix spacing

Add init/fini for BulkTypeEventLogger and BulkTypeValue instances

Fix return type ep_rt_mono_log_single_type

Add get_typeid_for_class helper

Add assertion for large types

Fix lttng map

Use helper methods to write into bulk type event logger buffer

Fix typo ep_rt_mono_send_method_details_event definition

Change s_name to ep_char8_t and write with gunichar2

* Removed temporary debug traces

Set BulkTypeValue s_name to null for now

Fixed debug trace formatting for pointers

Fix type_name_id for types with no token

Remove write_event_buffer_intptr as the size is different on Windows x86

Rename ep_rt_bulk_type_value_init to ep_rt_bulk_type_value_clear

* Utilize macros for array size initialization

Cleanup formatting and style

Remove unused write_event_buffer_utf16_str

Add BulkTypeEventLogger pointer to function description

* [mono] Mark mono_class_has_finalizer as MONO_COMPONENT_API

* Set global static const to macros

Change ep_rt_mono_get_byte_count_in_event type

* Address feedback

Drop variable prefixes

Clear BulkTypeValue via memset

Rename bulk_type_event_logger helpers to alloc and free

Remove redundant bulk type value byte count calculation

Utilize memcpy to copy type parameter arrays

Reduce padding by moving bulk_type_event_buffer field in struct

* Change Event struct parameter _ElementSize type to size_t

* Remove parallel type id array to reduce bookkeeping and create type ids from mono types when needed

Change type id type from intptr_t to uint64_t as ids in eventpipe are standardized to uint64_t

* [mono][eventpipe] Add dedicated mempool for BulkTypeEventLogger to dynamically allocate enough space for type parameters

* Revert "Change Event struct parameter _ElementSize type to size_t"

This reverts commit 87f87f2f93457bed292d979668dbad01beb1e5f1.

* Fix write_event_buffer parameter type to avoid C4267 error on windows

* Move mem_pool under BulkTypeEventLogger

src/coreclr/scripts/genEventPipe.py
src/coreclr/scripts/genLttngProvider.py
src/mono/mono/eventpipe/ep-rt-mono.c
src/mono/mono/eventpipe/ep-rt-mono.h
src/mono/mono/eventpipe/gen-eventing-event-inc.lst
src/mono/mono/metadata/class-internals.h

index 934b6cd..2f46735 100644 (file)
@@ -278,7 +278,7 @@ def generateWriteEventBody(template, providerName, eventName, runtimeFlavor):
                 emittedWriteToBuffer = True
         elif paramName in template.arrays:
             size = "sizeof(%s) * (int)%s" % (
-                lttngDataTypeMapping[parameter.winType],
+                getLttngDataTypeMapping(runtimeFlavor)[parameter.winType],
                 parameter.prop)
             if template.name in specialCaseSizes and paramName in specialCaseSizes[template.name]:
                 size = "(int)(%s)" % specialCaseSizes[template.name][paramName]
@@ -1041,7 +1041,7 @@ def generateEventPipeImplFiles(
                 )
 
                 eventpipeImpl.write(
-                    "EventPipeProvider *EventPipeProvider" + providerPrettyName + 
+                    "EventPipeProvider *EventPipeProvider" + providerPrettyName +
                     (" = nullptr;\n" if target_cpp else " = NULL;\n")
                 )
                 templateNodes = providerNode.getElementsByTagName('template')
index 16f849f..c5b16e6 100644 (file)
@@ -66,7 +66,7 @@ This file is generated using the logic from <root>/src/scripts/genLttngProvider.
 
 specialCaseSizes = { "BulkType" : { "Values" : "Values_ElementSize" }, "GCBulkRootCCW" : { "Values" : "Values_ElementSize" }, "GCBulkRCW" : { "Values" : "Values_ElementSize" }, "GCBulkRootStaticVar" : { "Values" : "Values_ElementSize" } }
 
-lttngDataTypeMapping ={
+coreCLRLttngDataTypeMapping ={
         #constructed types
         "win:null"          :" ",
         "win:Int64"         :"const __int64",
@@ -88,6 +88,34 @@ lttngDataTypeMapping ={
         "win:Binary"        :"const BYTE"
         }
 
+monoLttngDataTypeMapping ={
+        #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
+    elif runtimeFlavor.mono:
+        return monoLttngDataTypeMapping
+
 ctfDataTypeMapping ={
         #constructed types
         "win:Int64"         :"ctf_integer",
@@ -114,7 +142,7 @@ 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):
+def generateArgList(template, runtimeFlavor):
     header = "TP_ARGS( \\\n"
     footer = ")\n"
 
@@ -131,9 +159,9 @@ def generateArgList(template):
         for params in fnSig.paramlist:
             fnparam     = fnSig.getParam(params)
             wintypeName = fnparam.winType
-            typewName   = lttngDataTypeMapping[wintypeName]
+            typewName   = getLttngDataTypeMapping(runtimeFlavor)[wintypeName]
             winCount    = fnparam.count
-            countw      = lttngDataTypeMapping[winCount]
+            countw      = getLttngDataTypeMapping(runtimeFlavor)[winCount]
 
             arg = "        " + typewName
             if countw != " ":
@@ -145,7 +173,7 @@ def generateArgList(template):
     return header + args + footer
 
 
-def generateFieldList(template):
+def generateFieldList(template, runtimeFlavor):
     header = "    " + " TP_FIELDS(\n"
     footer = "\n    )\n)\n"
 
@@ -160,8 +188,8 @@ def generateFieldList(template):
             fnparam     = fnSig.getParam(params)
             wintypeName = fnparam.winType
             winCount    = fnparam.count
-            countw      = lttngDataTypeMapping[winCount]
-            typewName   = lttngDataTypeMapping[wintypeName].replace("const ","")
+            countw      = getLttngDataTypeMapping(runtimeFlavor)[winCount]
+            typewName   = getLttngDataTypeMapping(runtimeFlavor)[wintypeName].replace("const ","")
 
             field_body  = None
             ctf_type    = None
@@ -193,7 +221,7 @@ def generateFieldList(template):
 
     return header + field_list + footer
 
-def generateLttngHeader(providerName, allTemplates, eventNodes):
+def generateLttngHeader(providerName, allTemplates, eventNodes, runtimeFlavor):
     lTTngHdr = []
     for templateName in allTemplates:
         template = allTemplates[templateName]
@@ -202,7 +230,7 @@ def generateLttngHeader(providerName, allTemplates, eventNodes):
         lTTngHdr.append("\n#define " + templateName + "_TRACEPOINT_ARGS \\\n")
 
 #TP_ARGS
-        tp_args = generateArgList(template)
+        tp_args = generateArgList(template, runtimeFlavor)
         lTTngHdr.append(tp_args)
 
 #TP_EVENT_CLASS
@@ -212,7 +240,7 @@ def generateLttngHeader(providerName, allTemplates, eventNodes):
         lTTngHdr.append("    " + templateName + "_TRACEPOINT_ARGS,\n")
 
 #TP_FIELDS
-        tp_fields = generateFieldList(template)
+        tp_fields = generateFieldList(template, runtimeFlavor)
         lTTngHdr.append(tp_fields)
 
 # Macro for defining event instance
@@ -270,7 +298,7 @@ TRACEPOINT_EVENT_INSTANCE(\\
     return ''.join(lTTngHdr)
 
 
-def generateMethodBody(template, providerName, eventName):
+def generateMethodBody(template, providerName, eventName, runtimeFlavor):
     #emit code to init variables convert unicode to ansi string
     result = []
 
@@ -331,9 +359,9 @@ def generateMethodBody(template, providerName, eventName):
                 continue
 
             elif ctf_type == "ctf_sequence" or wintypeName == "win:Pointer":
-                line += "(" + lttngDataTypeMapping[wintypeName]
-                if not lttngDataTypeMapping[winCount] == " ":
-                    line += lttngDataTypeMapping[winCount]
+                line += "(" + getLttngDataTypeMapping(runtimeFlavor)[wintypeName]
+                if not getLttngDataTypeMapping(runtimeFlavor)[winCount] == " ":
+                    line += getLttngDataTypeMapping(runtimeFlavor)[winCount]
 
                 line += ") "
                 linefnbody.append(line + paramname)
@@ -358,7 +386,7 @@ def generateMethodBody(template, providerName, eventName):
                 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" % (lttngDataTypeMapping[parameter.winType], parameter.prop)
+                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))
@@ -451,7 +479,7 @@ def generateLttngTpProvider(providerName, eventNodes, allTemplates, runtimeFlavo
         lTTngImpl.append("    if (!EventXplatEnabled%s())\n" % (eventName,))
         lTTngImpl.append("        return ERROR_SUCCESS;\n")
 
-        result = generateMethodBody(template, providerName, eventName)
+        result = generateMethodBody(template, providerName, eventName, runtimeFlavor)
         lTTngImpl.append(result)
 
         lTTngImpl.append("\n    return ERROR_SUCCESS;\n}\n\n")
@@ -529,7 +557,7 @@ def generateLttngFiles(etwmanifest, eventprovider_directory, runtimeFlavor, dryR
 
                 lttnghdr_file.write("\n#include <lttng/tracepoint.h>\n\n")
 
-                lttnghdr_file.write(generateLttngHeader(providerName,allTemplates,eventNodes) + "\n")
+                lttnghdr_file.write(generateLttngHeader(providerName,allTemplates,eventNodes,runtimeFlavor) + "\n")
 
             with open_for_update(lttngevntprov) as lttngimpl_file:
                 lttngimpl_file.write(stdprolog + "\n")
index edf3ce8..0163f13 100644 (file)
@@ -1443,7 +1443,7 @@ eventpipe_fire_method_events (
 
                if (verbose) {
                        method_name = method->name;
-                       method_signature = mono_signature_full_name (method->signature);
+                       method_signature = mono_signature_full_name (mono_method_signature_internal (method));
                        if (method->klass)
                                method_namespace = mono_type_get_name_full (m_class_get_byval_arg (method->klass), MONO_TYPE_NAME_FORMAT_IL);
                }
@@ -2857,6 +2857,583 @@ ep_rt_mono_write_event_ee_startup_start (void)
                NULL);
 }
 
+#define STACK_ALLOC 256
+
+// The maximum number of type parameters for a BulkTypeValue instance
+// Aligned with coreCLR StackSArray<ULONGLONG> rgTypeParameters
+#define INIT_SIZE_OF_TYPE_PARAMETER_ARRAY ((uint32_t)(STACK_ALLOC / sizeof (intptr_t)))
+
+// !!!!!!! NOTE !!!!!!!!
+// The flags must match those in the ETW manifest exactly
+// !!!!!!! NOTE !!!!!!!!
+
+typedef enum {
+       TYPE_FLAGS_DELEGATE = 0x1,
+       TYPE_FLAGS_FINALIZABLE = 0x2,
+       TYPE_FLAGS_EXTERNALLY_IMPLEMENTED_COM_OBJECT = 0x4,
+       TYPE_FLAGS_ARRAY = 0x8,
+
+       TYPE_FLAGS_ARRAY_RANK_MASK = 0x3F00,
+       TYPE_FLAGS_ARRAY_RANK_SHIFT = 8,
+       TYPE_FLAGS_ARRAY_RANK_MAX = TYPE_FLAGS_ARRAY_RANK_MASK >> TYPE_FLAGS_ARRAY_RANK_SHIFT
+} TypeFlags;
+
+// This only contains the fixed-size data at the top of each struct in
+// the bulk type event.  These fields must still match exactly the initial
+// fields of the struct described in the manifest.
+typedef struct _EventStructBulkTypeFixedSizedData {
+       uint64_t type_id;
+       uint64_t module_id;
+       uint32_t type_name_id;
+       uint32_t flags;
+       uint8_t cor_element_type;
+} EventStructBulkTypeFixedSizedData;
+
+// Represents one instance of the Value struct inside a single BulkType event
+typedef struct _BulkTypeValue {
+       EventStructBulkTypeFixedSizedData fixed_sized_data;
+       uint32_t type_parameters_count;
+       MonoType **mono_type_parameters;
+       ep_char8_t *name; // Currently should only be NULL, TODO if we want to provide the name in the BulkTypeEvent data, figure out memory management to use
+} BulkTypeValue;
+
+static
+void
+ep_rt_bulk_type_value_clear (BulkTypeValue *bulk_type_value);
+
+static
+int
+ep_rt_mono_get_byte_count_in_event (BulkTypeValue *bulk_type_value);
+
+static
+BulkTypeEventLogger*
+ep_rt_bulk_type_event_logger_alloc (void);
+
+static
+void
+ep_rt_bulk_type_event_logger_free (BulkTypeEventLogger *type_logger);
+
+static
+int
+write_event_buffer (
+       const uint8_t *val,
+       int size,
+       char *buf_start,
+       char **buf_next);
+
+static
+int
+write_event_buffer_int8 (
+       int8_t val,
+       char *buf_start,
+       char **buf_next);
+
+static
+int
+write_event_buffer_int16 (
+       int16_t val,
+       char *buf_start,
+       char **buf_next);
+
+static
+int
+write_event_buffer_int32 (
+       int32_t val,
+       char *buf_start,
+       char **buf_next);
+
+static
+int
+write_event_buffer_int64 (
+       int64_t val,
+       char *buf_start,
+       char **buf_next);
+
+static
+uint64_t
+get_typeid_for_type (MonoType *t);
+
+static
+uint64_t
+get_typeid_for_class (MonoClass *c);
+
+// Clear out BulkTypeValue before filling it out (array elements can get reused if there
+// are enough types that we need to flush to multiple events).
+static
+void
+ep_rt_bulk_type_value_clear (BulkTypeValue *bulk_type_value)
+{
+       memset (bulk_type_value, 0, sizeof(BulkTypeValue));
+}
+
+static
+int
+ep_rt_mono_get_byte_count_in_event (BulkTypeValue *bulk_type_value)
+{
+       int name_len = 0;
+
+       return sizeof (bulk_type_value->fixed_sized_data.type_id) +     // Fixed Sized Data
+               sizeof (bulk_type_value->fixed_sized_data.module_id) +
+               sizeof (bulk_type_value->fixed_sized_data.type_name_id) +
+               sizeof (bulk_type_value->fixed_sized_data.flags) +
+               sizeof (bulk_type_value->fixed_sized_data.cor_element_type) +
+               sizeof (bulk_type_value->type_parameters_count) +               // Type parameters
+               (name_len + 1) * sizeof (ep_char8_t) +          // Size of name, including null terminator
+               bulk_type_value->type_parameters_count * sizeof (uint64_t);     // Type parameters
+}
+
+// ETW has a limitation of 64K for TOTAL event Size, however there is overhead associated with
+// the event headers.   It is unclear exactly how much that is, but 1K should be sufficiently
+// far away to avoid problems without sacrificing the perf of bulk processing.
+#define MAX_EVENT_BYTE_COUNT (63 * 1024)
+
+// The maximum event size, and the size of the buffer that we allocate to hold the event contents.
+#define MAX_SIZE_OF_EVENT_BUFFER 65536
+
+// Estimate of how many bytes we can squeeze in the event data for the value struct
+// array. (Intentionally overestimate the size of the non-array parts to keep it safe.)
+// This follows CoreCLR's kMaxBytesTypeValues.
+#define MAX_TYPE_VALUES_BYTES (MAX_EVENT_BYTE_COUNT - 0x30)
+
+// Estimate of how many type value elements we can put into the struct array, while
+// staying under the ETW event size limit. Note that this is impossible to calculate
+// perfectly, since each element of the struct array has variable size.
+//
+// In addition to the byte-size limit per event, Windows always forces on us a
+// max-number-of-descriptors per event, which in the case of BulkType, will kick in
+// far sooner. There's a max number of 128 descriptors allowed per event. 2 are used
+// for Count + ClrInstanceID. Then 4 per batched value. (Might actually be 3 if there
+// are no type parameters to log, but let's overestimate at 4 per value).
+#define K_MAX_COUNT_TYPE_VALUES ((uint32_t)(128 - 2) / 4)
+
+struct _BulkTypeEventLogger {
+       BulkTypeValue bulk_type_values [K_MAX_COUNT_TYPE_VALUES];
+       uint8_t *bulk_type_event_buffer;
+       uint32_t bulk_type_value_count;
+       uint32_t bulk_type_value_byte_count;
+       MonoMemPool *mem_pool;
+};
+
+static
+BulkTypeEventLogger*
+ep_rt_bulk_type_event_logger_alloc ()
+{
+       BulkTypeEventLogger *type_logger = g_malloc0 (sizeof (BulkTypeEventLogger));
+       type_logger->bulk_type_event_buffer = g_malloc0 (sizeof (uint8_t) * MAX_SIZE_OF_EVENT_BUFFER);
+       type_logger->mem_pool = mono_mempool_new ();
+       return type_logger;
+}
+
+static
+void
+ep_rt_bulk_type_event_logger_free (BulkTypeEventLogger *type_logger)
+{
+       mono_mempool_destroy (type_logger->mem_pool);
+       g_free (type_logger->bulk_type_event_buffer);
+       g_free (type_logger);
+}
+
+static
+int
+write_event_buffer (
+       const uint8_t *val,
+       int size,
+       char *buf_start,
+       char **buf_next)
+{
+       memcpy (buf_start, val, size);
+       *buf_next = buf_start + size;
+       return size;
+}
+
+static
+int
+write_event_buffer_int8 (
+       int8_t val,
+       char *buf_start,
+       char **buf_next)
+{
+       return write_event_buffer ((const uint8_t *)&val, sizeof (int8_t), buf_start, buf_next);
+}
+
+static
+int
+write_event_buffer_int16 (
+       int16_t val,
+       char *buf_start,
+       char **buf_next)
+{
+       return write_event_buffer ((const uint8_t *)&val, sizeof (int16_t), buf_start, buf_next);
+}
+
+static
+int
+write_event_buffer_int32 (
+       int32_t val,
+       char *buf_start,
+       char **buf_next)
+{
+       return write_event_buffer ((const uint8_t *)&val, sizeof (int32_t), buf_start, buf_next);
+}
+
+static
+int
+write_event_buffer_int64 (
+       int64_t val,
+       char *buf_start,
+       char **buf_next)
+{
+       return write_event_buffer ((const uint8_t *)&val, sizeof (int64_t), buf_start, buf_next);
+}
+
+//---------------------------------------------------------------------------------------
+//
+// ep_rt_mono_fire_bulk_type_event fires an ETW event for all the types batched so far,
+// it then resets the state to start batching new types at the beginning of the
+// bulk_type_values array.
+//
+// This follows CoreCLR's BulkTypeEventLogger::FireBulkTypeEvent
+
+void
+ep_rt_mono_fire_bulk_type_event (BulkTypeEventLogger *type_logger)
+{
+       if (type_logger->bulk_type_value_count == 0)
+               return;
+
+       uint16_t clr_instance_id = clr_instance_get_id ();
+
+       uint32_t values_element_size = 0;
+
+       char *ptr = (char *)type_logger->bulk_type_event_buffer;
+
+       for (int type_value_index = 0; type_value_index < type_logger->bulk_type_value_count; type_value_index++) {
+               BulkTypeValue *target = &type_logger->bulk_type_values [type_value_index];
+
+               values_element_size += write_event_buffer_int64 (target->fixed_sized_data.type_id, ptr, &ptr);
+               values_element_size += write_event_buffer_int64 (target->fixed_sized_data.module_id, ptr, &ptr);
+               values_element_size += write_event_buffer_int32 (target->fixed_sized_data.type_name_id, ptr, &ptr);
+               values_element_size += write_event_buffer_int32 (target->fixed_sized_data.flags, ptr, &ptr);
+               values_element_size += write_event_buffer_int8 (target->fixed_sized_data.cor_element_type, ptr, &ptr);
+
+               g_assert (target->name == NULL);
+               values_element_size += write_event_buffer_int16 (0, ptr, &ptr);
+
+               values_element_size += write_event_buffer_int32 (target->type_parameters_count, ptr, &ptr);
+
+               for (int i = 0; i < target->type_parameters_count; i++) {
+                       uint64_t type_parameter = get_typeid_for_type (target->mono_type_parameters [i]);
+                       values_element_size += write_event_buffer_int64 ((int64_t)type_parameter, ptr, &ptr);
+               }
+       }
+
+       FireEtwBulkType (
+               type_logger->bulk_type_value_count,
+               clr_instance_id,
+               values_element_size,
+               type_logger->bulk_type_event_buffer,
+               NULL,
+               NULL);
+
+       memset (type_logger->bulk_type_event_buffer, 0, sizeof (uint8_t) * MAX_SIZE_OF_EVENT_BUFFER);
+       type_logger->bulk_type_value_count = 0;
+       type_logger->bulk_type_value_byte_count = 0;
+}
+
+//---------------------------------------------------------------------------------------
+//
+// get_typeid_for_type is responsible for obtaining the unique type identifier for a
+// particular MonoType. MonoTypes are structs that are not unique pointers. There
+// can be two different MonoTypes that both System.Thread or int32 or bool []. There
+// is exactly one MonoClass * for any type, so we leverage the MonoClass a MonoType
+// points to in order to obtain a unique type identifier in mono. With that unique
+// MonoClass, its fields this_arg and _byval_arg are unique as well.
+//
+// Arguments:
+//      * mono_type - MonoType to be logged
+//
+// Return Value:
+//      type_id - Unique type identifier of mono_type
+
+static
+uint64_t
+get_typeid_for_type (MonoType *t)
+{
+       if (m_type_is_byref (t))
+               return (uint64_t)m_class_get_this_arg (mono_class_from_mono_type_internal (t));
+       else
+               return (uint64_t)m_class_get_byval_arg (mono_class_from_mono_type_internal (t));
+}
+
+static
+uint64_t
+get_typeid_for_class (MonoClass *c)
+{
+       return get_typeid_for_type (m_class_get_byval_arg (c));
+}
+
+//---------------------------------------------------------------------------------------
+//
+// ep_rt_mono_log_single_type batches a single type into the bulk type array and flushes
+// the array to ETW if it fills up. Most interaction with the type system (type analysis)
+// is done here. This does not recursively batch up any parameter types (arrays or generics),
+// but does add their unique identifiers to the mono_type_parameters array.
+// ep_rt_mono_log_type_and_parameters is responsible for initiating any recursive calls to
+// deal with type parameters.
+//
+// Arguments:
+//     * type_logger - BulkTypeEventLogger instance
+//      * mono_type - MonoType to be logged
+//
+// Return Value:
+//      Index into array of where this type got batched. -1 if there was a failure.
+//
+// This follows CoreCLR's BulkTypeEventLogger::LogSingleType
+
+int
+ep_rt_mono_log_single_type (
+       BulkTypeEventLogger *type_logger,
+       MonoType *mono_type)
+{
+       // If there's no room for another type, flush what we've got
+       if (type_logger->bulk_type_value_count == K_MAX_COUNT_TYPE_VALUES)
+               ep_rt_mono_fire_bulk_type_event (type_logger);
+
+       EP_ASSERT (type_logger->bulk_type_value_count < K_MAX_COUNT_TYPE_VALUES);
+
+       BulkTypeValue *val = &type_logger->bulk_type_values [type_logger->bulk_type_value_count];
+       ep_rt_bulk_type_value_clear (val);
+
+       MonoClass *klass = mono_class_from_mono_type_internal (mono_type);
+       MonoType *mono_underlying_type = mono_type_get_underlying_type (mono_type);
+
+       // Initialize val fixed_sized_data
+       val->fixed_sized_data.type_id = get_typeid_for_type (mono_type);
+       val->fixed_sized_data.module_id = (uint64_t)m_class_get_image (klass);
+       val->fixed_sized_data.type_name_id = m_class_get_type_token (klass) ? mono_metadata_make_token (MONO_TABLE_TYPEDEF, mono_metadata_token_index (m_class_get_type_token (klass))) : 0;
+       if (mono_class_has_finalizer (klass))
+               val->fixed_sized_data.flags |= TYPE_FLAGS_FINALIZABLE;
+       if (m_class_is_delegate (klass))
+               val->fixed_sized_data.flags |= TYPE_FLAGS_DELEGATE;
+       if (mono_class_is_com_object (klass))
+               val->fixed_sized_data.flags |= TYPE_FLAGS_EXTERNALLY_IMPLEMENTED_COM_OBJECT;
+       val->fixed_sized_data.cor_element_type = (uint8_t)mono_underlying_type->type;
+
+       // Sets val variable sized parameter type data, type_parameters_count, and mono_type_parameters associated
+       // with arrays or generics to be recursively batched in the same ep_rt_mono_log_type_and_parameters call
+       switch (mono_underlying_type->type) {
+       case MONO_TYPE_ARRAY:
+       case MONO_TYPE_SZARRAY:
+       {
+               MonoArrayType *mono_array_type = mono_type_get_array_type (mono_type);
+               val->fixed_sized_data.flags |= TYPE_FLAGS_ARRAY;
+               if (mono_underlying_type->type == MONO_TYPE_ARRAY) {
+                       // Only ranks less than TypeFlagsArrayRankMax are supported.
+                       // Fortunately TypeFlagsArrayRankMax should be greater than the
+                       // number of ranks the type loader will support
+                       uint32_t rank = mono_array_type->rank;
+                       if (rank < TYPE_FLAGS_ARRAY_RANK_MAX) {
+                               rank <<= 8;
+                               val->fixed_sized_data.flags |= rank;
+                       }
+               }
+
+               // mono arrays are always arrays of by value types
+               val->mono_type_parameters = mono_mempool_alloc0 (type_logger->mem_pool, 1 * sizeof (MonoType*));
+               *val->mono_type_parameters = m_class_get_byval_arg (mono_array_type->eklass);
+               val->type_parameters_count++;
+               break;
+       }
+       case MONO_TYPE_GENERICINST:
+       {
+               MonoGenericInst *class_inst = mono_type->data.generic_class->context.class_inst;
+               val->type_parameters_count = class_inst->type_argc;
+               val->mono_type_parameters = mono_mempool_alloc0 (type_logger->mem_pool, val->type_parameters_count * sizeof (MonoType*));
+               memcpy (val->mono_type_parameters, class_inst->type_argv, val->type_parameters_count * sizeof (MonoType*));
+               break;
+       }
+       case MONO_TYPE_CLASS:
+       case MONO_TYPE_VALUETYPE:
+       case MONO_TYPE_PTR:
+       case MONO_TYPE_BYREF:
+       {
+               if (mono_underlying_type == mono_type)
+                       break;
+               val->mono_type_parameters = mono_mempool_alloc0 (type_logger->mem_pool, 1 * sizeof (MonoType*));
+               *val->mono_type_parameters = mono_underlying_type;
+               val->type_parameters_count++;
+               break;
+       }
+       default:
+               break;
+       }
+
+       // Now that we know the full size of this type's data, see if it fits in our
+       // batch or whether we need to flush
+       int val_byte_count = ep_rt_mono_get_byte_count_in_event (val);
+       if (val_byte_count > MAX_TYPE_VALUES_BYTES) {
+               // NOTE: If name is actively used, set it to NULL and relevant memory management to reduce byte count
+               // This type is apparently so huge, it's too big to squeeze into an event, even
+               // if it were the only type batched in the whole event.  Bail
+               mono_trace (G_LOG_LEVEL_ERROR, MONO_TRACE_DIAGNOSTICS, "Failed to log single mono type %p with typeID %llu. Type is too large for the BulkType Event.\n", (gpointer)mono_type, val->fixed_sized_data.type_id);
+               return -1;
+       }
+
+       if (type_logger->bulk_type_value_byte_count + val_byte_count > MAX_TYPE_VALUES_BYTES) {
+               // Although this type fits into the array, its size is so big that the entire
+               // array can't be logged via ETW. So flush the array, and start over by
+               // calling ourselves--this refetches the type info and puts it at the
+               // beginning of the array.  Since we know this type is small enough to be
+               // batched into an event on its own, this recursive call will not try to
+               // call itself again.
+               g_assert (type_logger->bulk_type_value_byte_count + val_byte_count > MAX_TYPE_VALUES_BYTES);
+               ep_rt_mono_fire_bulk_type_event (type_logger);
+               return ep_rt_mono_log_single_type (type_logger, mono_type);
+       }
+
+       // The type fits into the batch, so update our state
+       type_logger->bulk_type_value_count++;
+       type_logger->bulk_type_value_byte_count += val_byte_count;
+       return type_logger->bulk_type_value_count - 1;
+}
+
+//---------------------------------------------------------------------------------------
+//
+// High-level method to batch a type and (recursively) its type parameters, flushing to
+// ETW as needed.  This is called by ep_rt_mono_log_type_and_parameters_if_necessary.
+//
+// Arguments:
+//     * type_logger - BulkTypeEventLogger instance
+//      * mono_type - MonoType to be logged
+//
+// This follows CoreCLR's BulkTypeEventLogger::LogTypeAndParameter
+
+void
+ep_rt_mono_log_type_and_parameters (
+       BulkTypeEventLogger *type_logger,
+       MonoType *mono_type)
+{
+       // Batch up this type.  This grabs useful info about the type, including any
+       // type parameters it may have, and sticks it in bulk_type_values
+       int bulk_type_value_index = ep_rt_mono_log_single_type (type_logger, mono_type);
+       if (bulk_type_value_index == -1) {
+               // There was a failure trying to log the type, so don't bother with its type
+               // parameters
+               return;
+       }
+
+       // Look at the type info we just batched, so we can get the type parameters
+       BulkTypeValue *val = &type_logger->bulk_type_values [bulk_type_value_index];
+
+       // We're about to recursively call ourselves for the type parameters, so make a
+       // local copy of their type handles first (else, as we log them we could flush
+       // and clear out bulk_type_values, thus trashing val)
+       uint32_t param_count = val->type_parameters_count;
+       if (param_count == 0)
+               return;
+
+       MonoType **mono_type_parameters = mono_mempool_alloc0 (type_logger->mem_pool, param_count * sizeof (MonoType*));
+       memcpy (mono_type_parameters, val->mono_type_parameters, sizeof (MonoType*) * param_count);
+
+       for (uint32_t i = 0; i < param_count; i++)
+               ep_rt_mono_log_type_and_parameters_if_necessary (type_logger, mono_type_parameters [i]);
+}
+
+//---------------------------------------------------------------------------------------
+//
+// Outermost level of ETW-type-logging.  This method is used to log a unique type identifier
+// (in this case a MonoType) and (recursively) its type parameters when present.
+//
+// Arguments:
+//     * type_logger - BulkTypeEventLogger instance
+//      * mono_type - MonoType to be logged
+//
+// This follows CoreCLR's BulkTypeEventLogger::LogTypeAndParameters
+
+void
+ep_rt_mono_log_type_and_parameters_if_necessary (
+       BulkTypeEventLogger *type_logger,
+       MonoType *mono_type)
+{
+       // TODO Log the type if necessary
+
+       ep_rt_mono_log_type_and_parameters (type_logger, mono_type);
+}
+
+// ETW has a limit for maximum event size. Do not log overly large method type argument sets
+static const uint32_t MAX_METHOD_TYPE_ARGUMENT_COUNT = 1024;
+
+//---------------------------------------------------------------------------------------
+//
+// ep_rt_mono_send_method_details_event is the method responsible for sending details of
+// methods involved in events such as JitStart, Load/Unload, Rundown, R2R, and other
+// eventpipe events. It calls ep_rt_mono_log_type_and_parameters_if_necessary to log
+// unique types from the method type and available method instantiation parameter types
+// that are ultimately emitted as a BulkType event in ep_rt_mono_fire_bulk_type_event.
+// After appropraitely logging type information, it sends method details outlined by
+// the generated dotnetruntime.c and ClrEtwAll manifest.
+//
+// Arguments:
+//      * method - a MonoMethod hit during an eventpipe event
+//
+// This follows CoreCLR's ETW::MethodLog::SendMethodDetailsEvent
+
+void
+ep_rt_mono_send_method_details_event (MonoMethod *method)
+{
+       if (method->wrapper_type != MONO_WRAPPER_NONE || method->dynamic)
+               return;
+
+       MonoGenericContext *method_ctx = mono_method_get_context (method);
+
+       MonoGenericInst *method_inst = NULL;
+       if (method_ctx)
+               method_inst = method_ctx->method_inst;
+
+       if (method_inst && method_inst->type_argc > MAX_METHOD_TYPE_ARGUMENT_COUNT)
+               return;
+
+       BulkTypeEventLogger *type_logger = ep_rt_bulk_type_event_logger_alloc ();
+
+       uint64_t method_type_id = 0;
+       g_assert (mono_metadata_token_index (method->token) != 0);
+       uint32_t method_token = mono_metadata_make_token (MONO_TABLE_METHOD, mono_metadata_token_index (method->token));
+       uint64_t loader_module_id = 0;
+       MonoClass *klass = method->klass;
+       if (klass) {
+               MonoType *method_mono_type = m_class_get_byval_arg (klass);
+               method_type_id = get_typeid_for_class (klass);
+
+               ep_rt_mono_log_type_and_parameters_if_necessary (type_logger, method_mono_type);
+
+               loader_module_id = (uint64_t)mono_class_get_image (klass);
+       }
+
+       uint32_t method_inst_parameter_types_count = 0;
+       if (method_inst)
+               method_inst_parameter_types_count = method_inst->type_argc;
+
+       uint64_t *method_inst_parameters_type_ids = mono_mempool_alloc0 (type_logger->mem_pool, method_inst_parameter_types_count * sizeof (uint64_t));
+       for (int i = 0; i < method_inst_parameter_types_count; i++) {
+               method_inst_parameters_type_ids [i] = get_typeid_for_type (method_inst->type_argv [i]);
+
+               ep_rt_mono_log_type_and_parameters_if_necessary (type_logger, method_inst->type_argv [i]);
+       }
+
+       ep_rt_mono_fire_bulk_type_event (type_logger);
+
+       FireEtwMethodDetails (
+               (uint64_t)method,
+               method_type_id,
+               method_token,
+               method_inst_parameter_types_count,
+               loader_module_id,
+               (uint64_t*)method_inst_parameters_type_ids,
+               NULL,
+               NULL);
+
+       ep_rt_bulk_type_event_logger_free (type_logger);
+}
+
 bool
 ep_rt_mono_write_event_jit_start (MonoMethod *method)
 {
@@ -2873,7 +3450,7 @@ ep_rt_mono_write_event_jit_start (MonoMethod *method)
                const char *method_name = NULL;
                char *method_signature = NULL;
 
-               //TODO: SendMethodDetailsEvent
+               ep_rt_mono_send_method_details_event(method);
 
                method_id = (uint64_t)method;
 
@@ -2888,7 +3465,7 @@ ep_rt_mono_write_event_jit_start (MonoMethod *method)
                }
 
                method_name = method->name;
-               method_signature = mono_signature_full_name (method->signature);
+               method_signature = mono_signature_full_name (mono_method_signature_internal (method));
 
                if (method->klass) {
                        module_id = (uint64_t)m_class_get_image (method->klass);
@@ -3039,11 +3616,11 @@ ep_rt_mono_write_event_method_load (
                                method_flags |= METHOD_FLAGS_GENERIC_METHOD;
                }
 
-               //TODO: SendMethodDetailsEvent
+               ep_rt_mono_send_method_details_event(method);
 
                if (verbose) {
                        method_name = method->name;
-                       method_signature = mono_signature_full_name (method->signature);
+                       method_signature = mono_signature_full_name (mono_method_signature_internal (method));
 
                        if (method->klass)
                                method_namespace = mono_type_get_name_full (m_class_get_byval_arg (method->klass), MONO_TYPE_NAME_FORMAT_IL);
@@ -5319,7 +5896,7 @@ mono_profiler_jit_done (
        if (verbose) {
                //TODO: Optimize string formatting into functions accepting GString to reduce heap alloc.
                method_name = method->name;
-               method_signature = mono_signature_full_name (method->signature);
+               method_signature = mono_signature_full_name (mono_method_signature_internal (method));
                if (method->klass)
                        method_namespace = mono_type_get_name_full (m_class_get_byval_arg (method->klass), MONO_TYPE_NAME_FORMAT_IL);
        }
index 6c954f1..d50d019 100644 (file)
@@ -2187,6 +2187,29 @@ ep_rt_volatile_store_ptr_without_barrier (
 bool
 ep_rt_mono_write_event_ee_startup_start (void);
 
+typedef struct _BulkTypeEventLogger BulkTypeEventLogger;
+
+void
+ep_rt_mono_fire_bulk_type_event (BulkTypeEventLogger *p_type_logger);
+
+int
+ep_rt_mono_log_single_type (
+       BulkTypeEventLogger *p_type_logger,
+       MonoType *mono_type);
+
+void
+ep_rt_mono_log_type_and_parameters (
+       BulkTypeEventLogger *p_type_logger,
+       MonoType *mono_type);
+
+void
+ep_rt_mono_log_type_and_parameters_if_necessary (
+       BulkTypeEventLogger *p_type_logger,
+       MonoType *mono_type);
+
+void
+ep_rt_mono_send_method_details_event (MonoMethod *method);
+
 bool
 ep_rt_mono_write_event_jit_start (MonoMethod *method);
 
index 42086b3..487d9d2 100644 (file)
@@ -4,6 +4,7 @@ AppDomainDCEnd_V1
 AssemblyDCEnd_V1
 AssemblyLoad_V1
 AssemblyUnload_V1
+BulkType
 ContentionStart_V1
 ContentionStop
 DCEndComplete_V1
@@ -28,6 +29,7 @@ MethodJitMemoryAllocatedForCode
 MethodJittingStarted_V1
 MethodLoad_V1
 MethodLoadVerbose_V1
+MethodDetails
 ModuleDCEnd_V2
 ModuleLoad_V2
 ModuleUnload_V2
index c0c447f..8574b19 100644 (file)
@@ -1226,7 +1226,7 @@ mono_class_get_fields_lazy (MonoClass* klass, gpointer *iter);
 gboolean
 mono_class_check_vtable_constraints (MonoClass *klass, GList *in_setup);
 
-gboolean
+MONO_COMPONENT_API gboolean
 mono_class_has_finalizer (MonoClass *klass);
 
 void