internal TraceEventNativeMethods.EVENT_RECORD *ReadEvent(PinnedStreamReader reader) { EventPipeEventHeader *eventData = (EventPipeEventHeader *)reader.GetPointer(EventPipeEventHeader.HeaderSize); eventData = (EventPipeEventHeader *)reader.GetPointer(eventData->TotalEventSize); // now we now the real size and get read entire event // Basic sanity checks. Are the timestamps and sizes sane. Debug.Assert(sessionEndTimeQPC <= eventData->TimeStamp); Debug.Assert(sessionEndTimeQPC == 0 || eventData->TimeStamp - sessionEndTimeQPC < _QPCFreq * 24 * 3600); Debug.Assert(0 <= eventData->PayloadSize && eventData->PayloadSize <= eventData->TotalEventSize); Debug.Assert(0 < eventData->TotalEventSize && eventData->TotalEventSize < 0x20000); // TODO really should be 64K but BulkSurvivingObjectRanges needs fixing. Debug.Assert(_fileFormatVersionNumber < 3 || ((int)EventPipeEventHeader.PayloadBytes(eventData) % 4 == 0 && eventData->TotalEventSize % 4 == 0)); // ensure 4 byte alignment StreamLabel eventDataEnd = reader.Current.Add(eventData->TotalEventSize); Debug.Assert(0 <= EventPipeEventHeader.StackBytesSize(eventData) && EventPipeEventHeader.StackBytesSize(eventData) <= eventData->TotalEventSize); TraceEventNativeMethods.EVENT_RECORD *ret = null; if (eventData->IsMetadata()) { int totalEventSize = eventData->TotalEventSize; int payloadSize = eventData->PayloadSize; // Note that this skip invalidates the eventData pointer, so it is important to pull any fields out we need first. reader.Skip(EventPipeEventHeader.HeaderSize); StreamLabel metaDataEnd = reader.Current.Add(payloadSize); // Read in the header (The header does not include payload parameter information) var metaDataHeader = new EventPipeEventMetaDataHeader(reader, payloadSize, _fileFormatVersionNumber, PointerSize, _processId); _eventMetadataDictionary.Add(metaDataHeader.MetaDataId, metaDataHeader); // Tell the parser about this new event _eventParser.OnNewEventPipeEventDefinition(metaDataHeader, reader); Debug.Assert(reader.Current == metaDataEnd); // We should have read all the meta-data. int stackBytes = reader.ReadInt32(); Debug.Assert(stackBytes == 0, "Meta-data events should always have a empty stack"); } else { if (_eventMetadataDictionary.TryGetValue(eventData->MetaDataId, out var metaData)) { ret = metaData.GetEventRecordForEventData(eventData); } else { Debug.Assert(false, "Warning can't find metaData for ID " + eventData->MetaDataId.ToString("x")); } } reader.Goto(eventDataEnd); return(ret); }
private TraceEventNativeMethods.EVENT_RECORD *ReadEvent(PinnedStreamReader reader) { // Guess that the event is < 1000 bytes or whatever is left in the stream. int eventSizeGuess = Math.Min(1000, _endOfEventStream.Sub(reader.Current)); EventPipeEventHeader *eventData = (EventPipeEventHeader *)reader.GetPointer(eventSizeGuess); // Basic sanity checks. Are the timestamps and sizes sane. Debug.Assert(sessionEndTimeQPC <= eventData->TimeStamp); Debug.Assert(sessionEndTimeQPC == 0 || eventData->TimeStamp - sessionEndTimeQPC < _QPCFreq * 24 * 3600); Debug.Assert(0 <= eventData->PayloadSize && eventData->PayloadSize <= eventData->TotalEventSize); Debug.Assert(eventData->MetaDataId <= reader.Current); // IDs are the location in the of of the data, so it comes before Debug.Assert(0 < eventData->TotalEventSize && eventData->TotalEventSize < 0x20000); // TODO really should be 64K but BulkSurvivingObjectRanges needs fixing. if (eventSizeGuess < eventData->TotalEventSize) { eventData = (EventPipeEventHeader *)reader.GetPointer(eventData->TotalEventSize); } Debug.Assert(0 <= EventPipeEventHeader.StackBytesSize(eventData) && EventPipeEventHeader.StackBytesSize(eventData) <= eventData->TotalEventSize); // This asserts that the header size + payload + stackSize field + StackSize == TotalEventSize; Debug.Assert(eventData->PayloadSize + EventPipeEventHeader.HeaderSize + sizeof(int) + EventPipeEventHeader.StackBytesSize(eventData) == eventData->TotalEventSize); TraceEventNativeMethods.EVENT_RECORD *ret = null; EventPipeEventMetaData metaData; if (eventData->MetaDataId == 0) // Is this a Meta-data event? { int totalEventSize = eventData->TotalEventSize; int payloadSize = eventData->PayloadSize; StreamLabel metaDataStreamOffset = reader.Current; // Used as the 'id' for the meta-data // Note that this skip invalidates the eventData pointer, so it is important to pull any fields out we need first. reader.Skip(EventPipeEventHeader.HeaderSize); metaData = new EventPipeEventMetaData(reader, payloadSize, _fileFormatVersionNumber, PointerSize, _processId); _eventMetadataDictionary.Add(metaDataStreamOffset, metaData); _eventParser.AddTemplate(metaData); int stackBytes = reader.ReadInt32(); // Meta-data events should always have a empty stack. Debug.Assert(stackBytes == 0); // We have read all the bytes in the event Debug.Assert(reader.Current == metaDataStreamOffset.Add(totalEventSize)); } else { if (_eventMetadataDictionary.TryGetValue(eventData->MetaDataId, out metaData)) { ret = metaData.GetEventRecordForEventData(eventData); } else { Debug.Assert(false, "Warning can't find metaData for ID " + eventData->MetaDataId.ToString("x")); } reader.Skip(eventData->TotalEventSize); } return(ret); }
internal TraceEventNativeMethods.EVENT_RECORD *ReadEvent(PinnedStreamReader reader) { EventPipeEventHeader *eventData = (EventPipeEventHeader *)reader.GetPointer(EventPipeEventHeader.HeaderSize); eventData = (EventPipeEventHeader *)reader.GetPointer(eventData->TotalEventSize); // now we now the real size and get read entire event // Basic sanity checks. Are the timestamps and sizes sane. Debug.Assert(sessionEndTimeQPC <= eventData->TimeStamp); Debug.Assert(sessionEndTimeQPC == 0 || eventData->TimeStamp - sessionEndTimeQPC < _QPCFreq * 24 * 3600); Debug.Assert(0 <= eventData->PayloadSize && eventData->PayloadSize <= eventData->TotalEventSize); Debug.Assert(0 < eventData->TotalEventSize && eventData->TotalEventSize < 0x20000); // TODO really should be 64K but BulkSurvivingObjectRanges needs fixing. Debug.Assert(_fileFormatVersionNumber < 3 || ((int)EventPipeEventHeader.PayloadBytes(eventData) % 4 == 0 && eventData->TotalEventSize % 4 == 0)); // ensure 4 byte alignment StreamLabel eventDataEnd = reader.Current.Add(eventData->TotalEventSize); Debug.Assert(0 <= EventPipeEventHeader.StackBytesSize(eventData) && EventPipeEventHeader.StackBytesSize(eventData) <= eventData->TotalEventSize); TraceEventNativeMethods.EVENT_RECORD *ret = null;; if (eventData->IsMetadata()) { int totalEventSize = eventData->TotalEventSize; int payloadSize = eventData->PayloadSize; // Note that this skip invalidates the eventData pointer, so it is important to pull any fields out we need first. reader.Skip(EventPipeEventHeader.HeaderSize); var metaData = new EventPipeEventMetaData(reader, payloadSize, _fileFormatVersionNumber, PointerSize, _processId); _eventMetadataDictionary.Add(metaData.MetaDataId, metaData); _eventParser.AddTemplate(metaData); // if we don't add the templates to this parse, we are going to have unhadled events (see https://github.com/Microsoft/perfview/issues/461) int stackBytes = reader.ReadInt32(); Debug.Assert(stackBytes == 0, "Meta-data events should always have a empty stack"); } else { if (_eventMetadataDictionary.TryGetValue(eventData->MetaDataId, out var metaData)) { ret = metaData.GetEventRecordForEventData(eventData); } else { Debug.Assert(false, "Warning can't find metaData for ID " + eventData->MetaDataId.ToString("x")); } } reader.Goto(eventDataEnd); return(ret); }
/// <summary> /// Given the EventPipe metaData header and a stream pointing at the serialized meta-data for the parameters for the /// event, create a new DynamicTraceEventData that knows how to parse that event. /// ReaderForParameters.Current is advanced past the parameter information. /// </summary> private DynamicTraceEventData ReadEventParametersAndBuildTemplate(EventPipeEventMetaDataHeader eventMetaDataHeader, PinnedStreamReader readerForParameters) { int opcode; string opcodeName; GetOpcodeFromEventName(eventMetaDataHeader.EventName, out opcode, out opcodeName); DynamicTraceEventData.PayloadFetchClassInfo classInfo = null; DynamicTraceEventData template = new DynamicTraceEventData(null, eventMetaDataHeader.EventId, 0, eventMetaDataHeader.EventName, Guid.Empty, opcode, opcodeName, eventMetaDataHeader.ProviderId, eventMetaDataHeader.ProviderName); // If the metadata contains no parameter metadata, don't attempt to read it. if (!eventMetaDataHeader.ContainsParameterMetadata) { template.payloadNames = new string[0]; template.payloadFetches = new DynamicTraceEventData.PayloadFetch[0]; return(template); } // Read the count of event payload fields. int fieldCount = readerForParameters.ReadInt32(); Debug.Assert(0 <= fieldCount && fieldCount < 0x4000); if (fieldCount > 0) { // Recursively parse the metadata, building up a list of payload names and payload field fetch objects. classInfo = ParseFields(readerForParameters, fieldCount); } else { classInfo = new DynamicTraceEventData.PayloadFetchClassInfo() { FieldNames = new string[0], FieldFetches = new DynamicTraceEventData.PayloadFetch[0] }; } template.payloadNames = classInfo.FieldNames; template.payloadFetches = classInfo.FieldFetches; return(template); }
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); }