From 82ad3816992a97d89fbc2cf50a33eff2b733edb4 Mon Sep 17 00:00:00 2001 From: Kevin Parsons Date: Thu, 20 Dec 2018 01:28:55 -0800 Subject: [PATCH] Add ETW support for out type and tags --- internal/etw/eventdata.go | 2 +- internal/etw/eventmetadata.go | 133 +++++++++++++++++++++++++++++++--- internal/etw/provider.go | 4 +- pkg/etwlogrus/hook.go | 6 +- 4 files changed, 130 insertions(+), 15 deletions(-) diff --git a/internal/etw/eventdata.go b/internal/etw/eventdata.go index ecd2ca9..7d62799 100644 --- a/internal/etw/eventdata.go +++ b/internal/etw/eventdata.go @@ -17,6 +17,6 @@ func NewEventData() *EventData { // AddString appends the data for a string to the end of the buffer. func (ed *EventData) AddString(data string) { - ed.buffer.Write([]byte(data)) + ed.buffer.WriteString(data) ed.buffer.WriteByte(0) } diff --git a/internal/etw/eventmetadata.go b/internal/etw/eventmetadata.go index 2539941..31ad41a 100644 --- a/internal/etw/eventmetadata.go +++ b/internal/etw/eventmetadata.go @@ -13,7 +13,7 @@ type InType byte const ( InTypeNull InType = iota InTypeUnicodeString - InTypeAnsiString + InTypeANSIString InTypeInt8 InTypeUint8 InTypeInt16 @@ -25,6 +25,52 @@ const ( InTypeFloat InTypeDouble InTypeBool32 + InTypeBinary + InTypeGUID + InTypePointerUnsupported + InTypeFileTime + InTypeSystemTime + InTypeSID + InTypeHexInt32 + InTypeHexInt64 + InTypeCountedString + InTypeCountedANSIString + InTypeStruct + InTypeCountedBinary +) + +// 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 in type 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 metadatadata for an ETW @@ -37,16 +83,85 @@ type EventMetadata struct { // metadata written to the buffer. func NewEventMetadata(name string) *EventMetadata { em := EventMetadata{} - binary.Write(&em.buffer, binary.LittleEndian, uint16(0)) // Length placeholder - binary.Write(&em.buffer, binary.LittleEndian, byte(0)) // Tags - binary.Write(&em.buffer, binary.LittleEndian, []byte(name)) // Event name - binary.Write(&em.buffer, binary.LittleEndian, byte(0)) // Null terminator for name + binary.Write(&em.buffer, binary.LittleEndian, uint16(0)) // Length placeholder + em.writeTags(0) + em.buffer.WriteString(name) + em.buffer.WriteByte(0) // Null terminator for name return &em } +type field struct { + name string + inType InType + outType OutType + tags uint32 +} + +func (em *EventMetadata) writeField(f field) { + em.buffer.WriteString(f.name) + em.buffer.WriteByte(0) // Null terminator for name + + if f.outType == OutTypeDefault && f.tags == 0 { + em.buffer.WriteByte(byte(f.inType)) + } else { + em.buffer.WriteByte(byte(f.inType | 128)) + if f.tags == 0 { + em.buffer.WriteByte(byte(f.outType)) + } else { + em.buffer.WriteByte(byte(f.outType | 128)) + em.writeTags(f.tags) + } + } +} + +func (em *EventMetadata) writeTags(tags uint32) { + tags &= 0xfffffff + + for { + val := tags >> 21 + + if tags&0x1fffff == 0 { + em.buffer.WriteByte(byte(val & 0x7f)) + return + } + + em.buffer.WriteByte(byte(val | 0x80)) + + tags <<= 7 + } +} + +type fieldOpt func(f *field) + +// WithOutType specifies the out type for the field. This value is used as a +// hint by the event decoder for how the field value should be formatted. If no +// out type is specified, a default formatting based on the in type will be +// used. +func WithOutType(outType OutType) fieldOpt { + return func(f *field) { + f.outType = outType + } +} + +// WithTags adds a tag to the field. Tags are 28-bit values that have meaning +// only to the event consumer. The top 4 bits of the value will be ignored. +// Multiple uses of this option will cause the tags to be OR'd together. +func WithTags(tags uint32) fieldOpt { + return func(f *field) { + f.tags |= tags + } +} + // AddField appends a single field to the end of the event metadata buffer. -func (em *EventMetadata) AddField(name string, inType InType) { - binary.Write(&em.buffer, binary.LittleEndian, []byte(name)) // Field name - binary.Write(&em.buffer, binary.LittleEndian, byte(0)) // Null terminator for name - binary.Write(&em.buffer, binary.LittleEndian, byte(inType)) // In type +func (em *EventMetadata) AddField(name string, inType InType, opts ...fieldOpt) { + f := field{ + name: name, + inType: inType, + } + + for _, opt := range opts { + opt(&f) + } + + em.writeField(f) } diff --git a/internal/etw/provider.go b/internal/etw/provider.go index 4490b08..5e6f65d 100644 --- a/internal/etw/provider.go +++ b/internal/etw/provider.go @@ -153,8 +153,8 @@ func NewProvider(name string, id *windows.GUID, callback EnableCallback) (provid } metadata := &bytes.Buffer{} - binary.Write(metadata, binary.LittleEndian, uint16(0)) // Write empty size for buffer (to update later) - metadata.WriteString(name) // Provider name + binary.Write(metadata, binary.LittleEndian, uint16(0)) // Write empty size for buffer (to update later) + metadata.WriteString(name) metadata.WriteByte(0) // Null terminator for name binary.LittleEndian.PutUint16(metadata.Bytes(), uint16(metadata.Len())) // Update the size at the beginning of the buffer provider.metadata = metadata.Bytes() diff --git a/pkg/etwlogrus/hook.go b/pkg/etwlogrus/hook.go index e64abf7..9a3c966 100644 --- a/pkg/etwlogrus/hook.go +++ b/pkg/etwlogrus/hook.go @@ -57,16 +57,16 @@ func (h *Hook) Fire(e *logrus.Entry) error { event := etw.NewEvent("LogrusEntry", descriptor) - event.Metadata.AddField("Message", etw.InTypeAnsiString) + event.Metadata.AddField("Message", etw.InTypeANSIString, etw.WithOutType(etw.OutTypeUTF8)) event.Data.AddString(e.Message) for k, v := range e.Data { switch v := v.(type) { case string: - event.Metadata.AddField(k, etw.InTypeAnsiString) + event.Metadata.AddField(k, etw.InTypeANSIString, etw.WithOutType(etw.OutTypeUTF8)) event.Data.AddString(v) default: - event.Metadata.AddField(k, etw.InTypeAnsiString) + event.Metadata.AddField(k, etw.InTypeANSIString, etw.WithOutType(etw.OutTypeUTF8)) event.Data.AddString(fmt.Sprintf(" %v", reflect.TypeOf(v), v)) } }