| package etw |
| |
| import ( |
| "bytes" |
| "encoding/binary" |
| ) |
| |
| // inType indicates the type of data contained in the ETW event. |
| type inType byte |
| |
| // Various inType definitions for TraceLogging. These must match the definitions |
| // found in TraceLoggingProvider.h in the Windows SDK. |
| const ( |
| inTypeNull inType = iota |
| inTypeUnicodeString |
| inTypeANSIString |
| inTypeInt8 |
| inTypeUint8 |
| inTypeInt16 |
| inTypeUint16 |
| inTypeInt32 |
| inTypeUint32 |
| inTypeInt64 |
| inTypeUint64 |
| inTypeFloat |
| inTypeDouble |
| inTypeBool32 |
| inTypeBinary |
| inTypeGUID |
| inTypePointerUnsupported |
| inTypeFileTime |
| inTypeSystemTime |
| inTypeSID |
| inTypeHexInt32 |
| inTypeHexInt64 |
| inTypeCountedString |
| inTypeCountedANSIString |
| inTypeStruct |
| inTypeCountedBinary |
| inTypeCountedArray inType = 32 |
| inTypeArray inType = 64 |
| ) |
| |
| // outType specifies a hint to the event decoder for how the value should be |
| // formatted. |
| type outType byte |
| |
| // Various outType definitions for TraceLogging. These must match the |
| // definitions found in TraceLoggingProvider.h in the Windows SDK. |
| const ( |
| // outTypeDefault indicates that the default formatting for the inType will |
| // be used by the event decoder. |
| outTypeDefault outType = iota |
| outTypeNoPrint |
| outTypeString |
| outTypeBoolean |
| outTypeHex |
| outTypePID |
| outTypeTID |
| outTypePort |
| outTypeIPv4 |
| outTypeIPv6 |
| outTypeSocketAddress |
| outTypeXML |
| outTypeJSON |
| outTypeWin32Error |
| outTypeNTStatus |
| outTypeHResult |
| outTypeFileTime |
| outTypeSigned |
| outTypeUnsigned |
| outTypeUTF8 outType = 35 |
| outTypePKCS7WithTypeInfo outType = 36 |
| outTypeCodePointer outType = 37 |
| outTypeDateTimeUTC outType = 38 |
| ) |
| |
| // eventMetadata maintains a buffer which builds up the metadata for an ETW |
| // event. It needs to be paired with EventData which describes the event. |
| type eventMetadata struct { |
| buffer bytes.Buffer |
| } |
| |
| // bytes returns the raw binary data containing the event metadata. Before being |
| // returned, the current size of the buffer is written to the start of the |
| // buffer. The returned value is not copied from the internal buffer, so it can |
| // be mutated by the eventMetadata object after it is returned. |
| func (em *eventMetadata) bytes() []byte { |
| // Finalize the event metadata buffer by filling in the buffer length at the |
| // beginning. |
| binary.LittleEndian.PutUint16(em.buffer.Bytes(), uint16(em.buffer.Len())) |
| return em.buffer.Bytes() |
| } |
| |
| // writeEventHeader writes the metadata for the start of an event to the buffer. |
| // This specifies the event name and tags. |
| func (em *eventMetadata) writeEventHeader(name string, tags uint32) { |
| binary.Write(&em.buffer, binary.LittleEndian, uint16(0)) // Length placeholder |
| em.writeTags(tags) |
| em.buffer.WriteString(name) |
| em.buffer.WriteByte(0) // Null terminator for name |
| } |
| |
| func (em *eventMetadata) writeFieldInner(name string, inType inType, outType outType, tags uint32, arrSize uint16) { |
| em.buffer.WriteString(name) |
| em.buffer.WriteByte(0) // Null terminator for name |
| |
| if outType == outTypeDefault && tags == 0 { |
| em.buffer.WriteByte(byte(inType)) |
| } else { |
| em.buffer.WriteByte(byte(inType | 128)) |
| if tags == 0 { |
| em.buffer.WriteByte(byte(outType)) |
| } else { |
| em.buffer.WriteByte(byte(outType | 128)) |
| em.writeTags(tags) |
| } |
| } |
| |
| if arrSize != 0 { |
| binary.Write(&em.buffer, binary.LittleEndian, arrSize) |
| } |
| } |
| |
| // writeTags writes out the tags value to the event metadata. Tags is a 28-bit |
| // value, interpreted as bit flags, which are only relevant to the event |
| // consumer. The event consumer may choose to attribute special meaning to tags |
| // (e.g. 0x4 could mean the field contains PII). Tags are written as a series of |
| // bytes, each containing 7 bits of tag value, with the high bit set if there is |
| // more tag data in the following byte. This allows for a more compact |
| // representation when not all of the tag bits are needed. |
| func (em *eventMetadata) writeTags(tags uint32) { |
| // Only use the top 28 bits of the tags value. |
| tags &= 0xfffffff |
| |
| for { |
| // Tags are written with the most significant bits (e.g. 21-27) first. |
| val := tags >> 21 |
| |
| if tags&0x1fffff == 0 { |
| // If there is no more data to write after this, write this value |
| // without the high bit set, and return. |
| em.buffer.WriteByte(byte(val & 0x7f)) |
| return |
| } |
| |
| em.buffer.WriteByte(byte(val | 0x80)) |
| |
| tags <<= 7 |
| } |
| } |
| |
| // writeField writes the metadata for a simple field to the buffer. |
| func (em *eventMetadata) writeField(name string, inType inType, outType outType, tags uint32) { |
| em.writeFieldInner(name, inType, outType, tags, 0) |
| } |
| |
| // writeArray writes the metadata for an array field to the buffer. The number |
| // of elements in the array must be written as a uint16 in the event data, |
| // immediately preceeding the event data. |
| func (em *eventMetadata) writeArray(name string, inType inType, outType outType, tags uint32) { |
| em.writeFieldInner(name, inType|inTypeArray, outType, tags, 0) |
| } |
| |
| // writeCountedArray writes the metadata for an array field to the buffer. The |
| // size of a counted array is fixed, and the size is written into the metadata |
| // directly. |
| func (em *eventMetadata) writeCountedArray(name string, count uint16, inType inType, outType outType, tags uint32) { |
| em.writeFieldInner(name, inType|inTypeCountedArray, outType, tags, count) |
| } |
| |
| // writeStruct writes the metadata for a nested struct to the buffer. The struct |
| // contains the next N fields in the metadata, where N is specified by the |
| // fieldCount argument. |
| func (em *eventMetadata) writeStruct(name string, fieldCount uint8, tags uint32) { |
| em.writeFieldInner(name, inTypeStruct, outType(fieldCount), tags, 0) |
| } |