private DynamicTraceEventData.PayloadFetchClassInfo ParseFields(PinnedStreamReader reader, int numFields) { string[] fieldNames = new string[numFields]; DynamicTraceEventData.PayloadFetch[] fieldFetches = new DynamicTraceEventData.PayloadFetch[numFields]; ushort offset = 0; for (int fieldIndex = 0; fieldIndex < numFields; fieldIndex++) { DynamicTraceEventData.PayloadFetch payloadFetch = new DynamicTraceEventData.PayloadFetch(); // Read the TypeCode for the current field. TypeCode typeCode = (TypeCode)reader.ReadInt32(); // Fill out the payload fetch object based on the TypeCode. switch (typeCode) { case TypeCode.Boolean: { payloadFetch.Type = typeof(bool); payloadFetch.Size = 4; // We follow windows conventions and use 4 bytes for bool. payloadFetch.Offset = offset; break; } case TypeCode.Char: { payloadFetch.Type = typeof(char); payloadFetch.Size = sizeof(char); payloadFetch.Offset = offset; break; } case TypeCode.SByte: { payloadFetch.Type = typeof(SByte); payloadFetch.Size = sizeof(SByte); payloadFetch.Offset = offset; break; } case TypeCode.Byte: { payloadFetch.Type = typeof(byte); payloadFetch.Size = sizeof(byte); payloadFetch.Offset = offset; break; } case TypeCode.Int16: { payloadFetch.Type = typeof(Int16); payloadFetch.Size = sizeof(Int16); payloadFetch.Offset = offset; break; } case TypeCode.UInt16: { payloadFetch.Type = typeof(UInt16); payloadFetch.Size = sizeof(UInt16); payloadFetch.Offset = offset; break; } case TypeCode.Int32: { payloadFetch.Type = typeof(Int32); payloadFetch.Size = sizeof(Int32); payloadFetch.Offset = offset; break; } case TypeCode.UInt32: { payloadFetch.Type = typeof(UInt32); payloadFetch.Size = sizeof(UInt32); payloadFetch.Offset = offset; break; } case TypeCode.Int64: { payloadFetch.Type = typeof(Int64); payloadFetch.Size = sizeof(Int64); payloadFetch.Offset = offset; break; } case TypeCode.UInt64: { payloadFetch.Type = typeof(UInt64); payloadFetch.Size = sizeof(UInt64); payloadFetch.Offset = offset; break; } case TypeCode.Single: { payloadFetch.Type = typeof(Single); payloadFetch.Size = sizeof(Single); payloadFetch.Offset = offset; break; } case TypeCode.Double: { payloadFetch.Type = typeof(Double); payloadFetch.Size = sizeof(Double); payloadFetch.Offset = offset; break; } case TypeCode.Decimal: { payloadFetch.Type = typeof(Decimal); payloadFetch.Size = sizeof(Decimal); payloadFetch.Offset = offset; break; } case TypeCode.DateTime: { payloadFetch.Type = typeof(DateTime); payloadFetch.Size = 8; payloadFetch.Offset = offset; break; } case EventPipeEventSource.GuidTypeCode: { payloadFetch.Type = typeof(Guid); payloadFetch.Size = 16; payloadFetch.Offset = offset; break; } case TypeCode.String: { payloadFetch.Type = typeof(String); payloadFetch.Size = DynamicTraceEventData.NULL_TERMINATED; payloadFetch.Offset = offset; break; } case TypeCode.Object: { // TypeCode.Object represents an embedded struct. // Read the number of fields in the struct. Each of these fields could be an embedded struct, // but these embedded structs are still counted as single fields. They will be expanded when they are handled. int structFieldCount = reader.ReadInt32(); DynamicTraceEventData.PayloadFetchClassInfo embeddedStructClassInfo = ParseFields(reader, structFieldCount); if (embeddedStructClassInfo == null) { throw new Exception("Unable to parse metadata for embedded struct."); } payloadFetch = DynamicTraceEventData.PayloadFetch.StructPayloadFetch(offset, embeddedStructClassInfo); break; } default: { throw new NotSupportedException($"{typeCode} is not supported."); } } // Read the string name of the event payload field. fieldNames[fieldIndex] = reader.ReadNullTerminatedUnicodeString(); // Update the offset into the event for the next payload fetch. if (payloadFetch.Size >= DynamicTraceEventData.SPECIAL_SIZES || offset == ushort.MaxValue) { offset = ushort.MaxValue; // Indicate that the offset must be computed at run time. } else { offset += payloadFetch.Size; } // Save the current payload fetch. fieldFetches[fieldIndex] = payloadFetch; } return(new DynamicTraceEventData.PayloadFetchClassInfo() { FieldNames = fieldNames, FieldFetches = fieldFetches }); }
/// <summary> /// Creates a new MetaData instance from the serialized data at the current position of 'reader' /// of length 'length'. This typically points at the PAYLOAD AREA of a meta-data events) /// 'fileFormatVersionNumber' is the version number of the file as a whole /// (since that affects the parsing of this data) and 'processID' is the process ID for the /// whole stream (since it needs to be put into the EVENT_RECORD. /// /// When this constructor returns the reader has read all data given to it (thus it has /// move the read pointer by 'length') /// </summary> public EventPipeEventMetaData(PinnedStreamReader reader, int length, int fileFormatVersionNumber, int pointerSize, int processId) { StreamLabel eventDataEnd = reader.Current.Add(length); _eventRecord = (TraceEventNativeMethods.EVENT_RECORD *)Marshal.AllocHGlobal(sizeof(TraceEventNativeMethods.EVENT_RECORD)); ClearMemory(_eventRecord, sizeof(TraceEventNativeMethods.EVENT_RECORD)); if (pointerSize == 4) { _eventRecord->EventHeader.Flags = TraceEventNativeMethods.EVENT_HEADER_FLAG_32_BIT_HEADER; } else { _eventRecord->EventHeader.Flags = TraceEventNativeMethods.EVENT_HEADER_FLAG_64_BIT_HEADER; } _eventRecord->EventHeader.ProcessId = processId; StreamLabel metaDataStart = reader.Current; if (fileFormatVersionNumber == 1) { _eventRecord->EventHeader.ProviderId = reader.ReadGuid(); } else { ProviderName = reader.ReadNullTerminatedUnicodeString(); _eventRecord->EventHeader.ProviderId = GetProviderGuidFromProviderName(ProviderName); } var eventId = (ushort)reader.ReadInt32(); _eventRecord->EventHeader.Id = eventId; Debug.Assert(_eventRecord->EventHeader.Id == eventId); // No truncation var version = reader.ReadInt32(); _eventRecord->EventHeader.Version = (byte)version; Debug.Assert(_eventRecord->EventHeader.Version == version); // No truncation if (fileFormatVersionNumber >= 3) { long keywords = reader.ReadInt64(); _eventRecord->EventHeader.Keyword = (ulong)keywords; } int metadataLength = reader.ReadInt32(); Debug.Assert(0 <= metadataLength && metadataLength < length); if (0 < metadataLength) { // TODO why do we repeat the event number it is redundant. eventId = (ushort)reader.ReadInt32(); Debug.Assert(_eventRecord->EventHeader.Id == eventId); // No truncation EventName = reader.ReadNullTerminatedUnicodeString(); Debug.Assert(EventName.Length < length / 2); // Deduce the opcode from the name. if (EventName.EndsWith("Start", StringComparison.OrdinalIgnoreCase)) { _eventRecord->EventHeader.Opcode = (byte)TraceEventOpcode.Start; } else if (EventName.EndsWith("Stop", StringComparison.OrdinalIgnoreCase)) { _eventRecord->EventHeader.Opcode = (byte)TraceEventOpcode.Stop; } _eventRecord->EventHeader.Keyword = (ulong)reader.ReadInt64(); // TODO why do we repeat the event number it is redundant. version = reader.ReadInt32(); Debug.Assert(_eventRecord->EventHeader.Version == version); // No truncation _eventRecord->EventHeader.Level = (byte)reader.ReadInt32(); Debug.Assert(_eventRecord->EventHeader.Level <= 5); // Fetch the parameter information int parameterCount = reader.ReadInt32(); Debug.Assert(0 <= parameterCount && parameterCount < length / 8); // Each parameter takes at least 8 bytes. if (parameterCount > 0) { ParameterDefinitions = new Tuple <TypeCode, string> [parameterCount]; for (int i = 0; i < parameterCount; i++) { var type = (TypeCode)reader.ReadInt32(); Debug.Assert((uint)type < 24); // There only a handful of type codes. var name = reader.ReadNullTerminatedUnicodeString(); ParameterDefinitions[i] = new Tuple <TypeCode, string>(type, name); Debug.Assert(reader.Current <= eventDataEnd); } } } Debug.Assert(reader.Current == eventDataEnd); }