diff --git a/internal/etw/etw.go b/internal/etw/etw.go
index 5bddae26..88214fba 100644
--- a/internal/etw/etw.go
+++ b/internal/etw/etw.go
@@ -1,15 +1,15 @@
-// Package etw provides support for TraceLogging-based ETW (Event Tracing
-// for Windows). TraceLogging is a format of ETW events that are self-describing
-// (the event contains information on its own schema). This allows them to be
-// decoded without needing a separate manifest with event information. The
-// implementation here is based on the information found in
-// TraceLoggingProvider.h in the Windows SDK, which implements TraceLogging as a
-// set of C macros.
-package etw
-
-//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go etw.go
-
-//sys eventRegister(providerId *windows.GUID, callback uintptr, callbackContext uintptr, providerHandle *providerHandle) (win32err error) = advapi32.EventRegister
-//sys eventUnregister(providerHandle providerHandle) (win32err error) = advapi32.EventUnregister
-//sys eventWriteTransfer(providerHandle providerHandle, descriptor *EventDescriptor, activityID *windows.GUID, relatedActivityID *windows.GUID, dataDescriptorCount uint32, dataDescriptors *eventDataDescriptor) (win32err error) = advapi32.EventWriteTransfer
-//sys eventSetInformation(providerHandle providerHandle, class eventInfoClass, information uintptr, length uint32) (win32err error) = advapi32.EventSetInformation
+// Package etw provides support for TraceLogging-based ETW (Event Tracing
+// for Windows). TraceLogging is a format of ETW events that are self-describing
+// (the event contains information on its own schema). This allows them to be
+// decoded without needing a separate manifest with event information. The
+// implementation here is based on the information found in
+// TraceLoggingProvider.h in the Windows SDK, which implements TraceLogging as a
+// set of C macros.
+package etw
+
+//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go etw.go
+
+//sys eventRegister(providerId *windows.GUID, callback uintptr, callbackContext uintptr, providerHandle *providerHandle) (win32err error) = advapi32.EventRegister
+//sys eventUnregister(providerHandle providerHandle) (win32err error) = advapi32.EventUnregister
+//sys eventWriteTransfer(providerHandle providerHandle, descriptor *EventDescriptor, activityID *windows.GUID, relatedActivityID *windows.GUID, dataDescriptorCount uint32, dataDescriptors *eventDataDescriptor) (win32err error) = advapi32.EventWriteTransfer
+//sys eventSetInformation(providerHandle providerHandle, class eventInfoClass, information uintptr, length uint32) (win32err error) = advapi32.EventSetInformation
diff --git a/internal/etw/eventdata.go b/internal/etw/eventdata.go
index 85307343..32cf5681 100644
--- a/internal/etw/eventdata.go
+++ b/internal/etw/eventdata.go
@@ -1,65 +1,65 @@
-package etw
-
-import (
- "bytes"
- "encoding/binary"
-)
-
-// EventData maintains a buffer which builds up the data for an ETW event. It
-// needs to be paired with EventMetadata which describes the event.
-type EventData struct {
- buffer bytes.Buffer
-}
-
-// Bytes returns the raw binary data containing the event data. The returned
-// value is not copied from the internal buffer, so it can be mutated by the
-// EventData object after it is returned.
-func (ed *EventData) Bytes() []byte {
- return ed.buffer.Bytes()
-}
-
-// WriteString appends a string, including the null terminator, to the buffer.
-func (ed *EventData) WriteString(data string) {
- ed.buffer.WriteString(data)
- ed.buffer.WriteByte(0)
-}
-
-// WriteInt8 appends a int8 to the buffer.
-func (ed *EventData) WriteInt8(value int8) {
- ed.buffer.WriteByte(uint8(value))
-}
-
-// WriteInt16 appends a int16 to the buffer.
-func (ed *EventData) WriteInt16(value int16) {
- binary.Write(&ed.buffer, binary.LittleEndian, value)
-}
-
-// WriteInt32 appends a int32 to the buffer.
-func (ed *EventData) WriteInt32(value int32) {
- binary.Write(&ed.buffer, binary.LittleEndian, value)
-}
-
-// WriteInt64 appends a int64 to the buffer.
-func (ed *EventData) WriteInt64(value int64) {
- binary.Write(&ed.buffer, binary.LittleEndian, value)
-}
-
-// WriteUint8 appends a uint8 to the buffer.
-func (ed *EventData) WriteUint8(value uint8) {
- ed.buffer.WriteByte(value)
-}
-
-// WriteUint16 appends a uint16 to the buffer.
-func (ed *EventData) WriteUint16(value uint16) {
- binary.Write(&ed.buffer, binary.LittleEndian, value)
-}
-
-// WriteUint32 appends a uint32 to the buffer.
-func (ed *EventData) WriteUint32(value uint32) {
- binary.Write(&ed.buffer, binary.LittleEndian, value)
-}
-
-// WriteUint64 appends a uint64 to the buffer.
-func (ed *EventData) WriteUint64(value uint64) {
- binary.Write(&ed.buffer, binary.LittleEndian, value)
-}
+package etw
+
+import (
+ "bytes"
+ "encoding/binary"
+)
+
+// EventData maintains a buffer which builds up the data for an ETW event. It
+// needs to be paired with EventMetadata which describes the event.
+type EventData struct {
+ buffer bytes.Buffer
+}
+
+// Bytes returns the raw binary data containing the event data. The returned
+// value is not copied from the internal buffer, so it can be mutated by the
+// EventData object after it is returned.
+func (ed *EventData) Bytes() []byte {
+ return ed.buffer.Bytes()
+}
+
+// WriteString appends a string, including the null terminator, to the buffer.
+func (ed *EventData) WriteString(data string) {
+ ed.buffer.WriteString(data)
+ ed.buffer.WriteByte(0)
+}
+
+// WriteInt8 appends a int8 to the buffer.
+func (ed *EventData) WriteInt8(value int8) {
+ ed.buffer.WriteByte(uint8(value))
+}
+
+// WriteInt16 appends a int16 to the buffer.
+func (ed *EventData) WriteInt16(value int16) {
+ binary.Write(&ed.buffer, binary.LittleEndian, value)
+}
+
+// WriteInt32 appends a int32 to the buffer.
+func (ed *EventData) WriteInt32(value int32) {
+ binary.Write(&ed.buffer, binary.LittleEndian, value)
+}
+
+// WriteInt64 appends a int64 to the buffer.
+func (ed *EventData) WriteInt64(value int64) {
+ binary.Write(&ed.buffer, binary.LittleEndian, value)
+}
+
+// WriteUint8 appends a uint8 to the buffer.
+func (ed *EventData) WriteUint8(value uint8) {
+ ed.buffer.WriteByte(value)
+}
+
+// WriteUint16 appends a uint16 to the buffer.
+func (ed *EventData) WriteUint16(value uint16) {
+ binary.Write(&ed.buffer, binary.LittleEndian, value)
+}
+
+// WriteUint32 appends a uint32 to the buffer.
+func (ed *EventData) WriteUint32(value uint32) {
+ binary.Write(&ed.buffer, binary.LittleEndian, value)
+}
+
+// WriteUint64 appends a uint64 to the buffer.
+func (ed *EventData) WriteUint64(value uint64) {
+ binary.Write(&ed.buffer, binary.LittleEndian, value)
+}
diff --git a/internal/etw/eventdatadescriptor.go b/internal/etw/eventdatadescriptor.go
index e7c53cf0..8b0ad481 100644
--- a/internal/etw/eventdatadescriptor.go
+++ b/internal/etw/eventdatadescriptor.go
@@ -1,29 +1,29 @@
-package etw
-
-import (
- "unsafe"
-)
-
-type eventDataDescriptorType uint8
-
-const (
- eventDataDescriptorTypeUserData eventDataDescriptorType = iota
- eventDataDescriptorTypeEventMetadata
- eventDataDescriptorTypeProviderMetadata
-)
-
-type eventDataDescriptor struct {
- ptr ptr64
- size uint32
- dataType eventDataDescriptorType
- reserved1 uint8
- reserved2 uint16
-}
-
-func newEventDataDescriptor(dataType eventDataDescriptorType, buffer []byte) eventDataDescriptor {
- return eventDataDescriptor{
- ptr: ptr64{ptr: unsafe.Pointer(&buffer[0])},
- size: uint32(len(buffer)),
- dataType: dataType,
- }
-}
+package etw
+
+import (
+ "unsafe"
+)
+
+type eventDataDescriptorType uint8
+
+const (
+ eventDataDescriptorTypeUserData eventDataDescriptorType = iota
+ eventDataDescriptorTypeEventMetadata
+ eventDataDescriptorTypeProviderMetadata
+)
+
+type eventDataDescriptor struct {
+ ptr ptr64
+ size uint32
+ dataType eventDataDescriptorType
+ reserved1 uint8
+ reserved2 uint16
+}
+
+func newEventDataDescriptor(dataType eventDataDescriptorType, buffer []byte) eventDataDescriptor {
+ return eventDataDescriptor{
+ ptr: ptr64{ptr: unsafe.Pointer(&buffer[0])},
+ size: uint32(len(buffer)),
+ dataType: dataType,
+ }
+}
diff --git a/internal/etw/eventdescriptor.go b/internal/etw/eventdescriptor.go
index 39467651..23980b38 100644
--- a/internal/etw/eventdescriptor.go
+++ b/internal/etw/eventdescriptor.go
@@ -1,67 +1,67 @@
-package etw
-
-// Channel represents the ETW logging channel that is used. It can be used by
-// event consumers to give an event special treatment.
-type Channel uint8
-
-const (
- // ChannelTraceLogging is the default channel for TraceLogging events. It is
- // not required to be used for TraceLogging, but will prevent decoding
- // issues for these events on older operating systems.
- ChannelTraceLogging Channel = 11
-)
-
-// Level represents the ETW logging level. There are several predefined levels
-// that are commonly used, but technically anything from 0-255 is allowed.
-// Lower levels indicate more important events, and 0 indicates an event that
-// will always be collected.
-type Level uint8
-
-// Predefined ETW log levels.
-const (
- LevelAlways Level = iota
- LevelCritical
- LevelError
- LevelWarning
- LevelInfo
- LevelVerbose
-)
-
-// EventDescriptor represents various metadata for an ETW event.
-type EventDescriptor struct {
- id uint16
- version uint8
- Channel Channel
- Level Level
- Opcode uint8
- Task uint16
- Keyword uint64
-}
-
-// NewEventDescriptor returns an EventDescriptor initialized for use with
-// TraceLogging.
-func NewEventDescriptor() *EventDescriptor {
- // Standard TraceLogging events default to the TraceLogging channel, and
- // verbose level.
- return &EventDescriptor{
- Channel: ChannelTraceLogging,
- Level: LevelVerbose,
- }
-}
-
-// Identity returns the identity of the event. If the identity is not 0, it
-// should uniquely identify the other event metadata (contained in
-// EventDescriptor, and field metadata). Only the lower 24 bits of this value
-// are relevant.
-func (ed *EventDescriptor) Identity() uint32 {
- return (uint32(ed.version) << 16) | uint32(ed.id)
-}
-
-// SetIdentity sets the identity of the event. If the identity is not 0, it
-// should uniquely identify the other event metadata (contained in
-// EventDescriptor, and field metadata). Only the lower 24 bits of this value
-// are relevant.
-func (ed *EventDescriptor) SetIdentity(identity uint32) {
- ed.id = uint16(identity)
- ed.version = uint8(identity >> 16)
-}
+package etw
+
+// Channel represents the ETW logging channel that is used. It can be used by
+// event consumers to give an event special treatment.
+type Channel uint8
+
+const (
+ // ChannelTraceLogging is the default channel for TraceLogging events. It is
+ // not required to be used for TraceLogging, but will prevent decoding
+ // issues for these events on older operating systems.
+ ChannelTraceLogging Channel = 11
+)
+
+// Level represents the ETW logging level. There are several predefined levels
+// that are commonly used, but technically anything from 0-255 is allowed.
+// Lower levels indicate more important events, and 0 indicates an event that
+// will always be collected.
+type Level uint8
+
+// Predefined ETW log levels.
+const (
+ LevelAlways Level = iota
+ LevelCritical
+ LevelError
+ LevelWarning
+ LevelInfo
+ LevelVerbose
+)
+
+// EventDescriptor represents various metadata for an ETW event.
+type EventDescriptor struct {
+ id uint16
+ version uint8
+ Channel Channel
+ Level Level
+ Opcode uint8
+ Task uint16
+ Keyword uint64
+}
+
+// NewEventDescriptor returns an EventDescriptor initialized for use with
+// TraceLogging.
+func NewEventDescriptor() *EventDescriptor {
+ // Standard TraceLogging events default to the TraceLogging channel, and
+ // verbose level.
+ return &EventDescriptor{
+ Channel: ChannelTraceLogging,
+ Level: LevelVerbose,
+ }
+}
+
+// Identity returns the identity of the event. If the identity is not 0, it
+// should uniquely identify the other event metadata (contained in
+// EventDescriptor, and field metadata). Only the lower 24 bits of this value
+// are relevant.
+func (ed *EventDescriptor) Identity() uint32 {
+ return (uint32(ed.version) << 16) | uint32(ed.id)
+}
+
+// SetIdentity sets the identity of the event. If the identity is not 0, it
+// should uniquely identify the other event metadata (contained in
+// EventDescriptor, and field metadata). Only the lower 24 bits of this value
+// are relevant.
+func (ed *EventDescriptor) SetIdentity(identity uint32) {
+ ed.id = uint16(identity)
+ ed.version = uint8(identity >> 16)
+}
diff --git a/internal/etw/eventmetadata.go b/internal/etw/eventmetadata.go
index d294027f..e97ede03 100644
--- a/internal/etw/eventmetadata.go
+++ b/internal/etw/eventmetadata.go
@@ -1,177 +1,177 @@
-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) writeField(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.writeField(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.writeField(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.writeField(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.writeField(name, InTypeStruct, OutType(fieldCount), tags, 0)
-}
+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) writeField(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.writeField(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.writeField(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.writeField(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.writeField(name, InTypeStruct, OutType(fieldCount), tags, 0)
+}
diff --git a/internal/etw/eventopt.go b/internal/etw/eventopt.go
index 447cec3e..2c82eddc 100644
--- a/internal/etw/eventopt.go
+++ b/internal/etw/eventopt.go
@@ -1,63 +1,63 @@
-package etw
-
-import (
- "golang.org/x/sys/windows"
-)
-
-type eventOptions struct {
- descriptor *EventDescriptor
- activityID *windows.GUID
- relatedActivityID *windows.GUID
- tags uint32
-}
-
-// EventOpt defines the option function type that can be passed to
-// Provider.WriteEvent to specify general event options, such as level and
-// keyword.
-type EventOpt func(options *eventOptions)
-
-// WithEventOpts returns the variadic arguments as a single slice.
-func WithEventOpts(opts ...EventOpt) []EventOpt {
- return opts
-}
-
-// WithLevel specifies the level of the event to be written.
-func WithLevel(level Level) EventOpt {
- return func(options *eventOptions) {
- options.descriptor.Level = level
- }
-}
-
-// WithKeyword specifies the keywords of the event to be written. Multiple uses
-// of this option are OR'd together.
-func WithKeyword(keyword uint64) EventOpt {
- return func(options *eventOptions) {
- options.descriptor.Keyword |= keyword
- }
-}
-
-func WithChannel(channel Channel) EventOpt {
- return func(options *eventOptions) {
- options.descriptor.Channel = channel
- }
-}
-
-// WithTags specifies the tags of the event to be written. Tags is a 28-bit
-// value (top 4 bits are ignored) which are interpreted by the event consumer.
-func WithTags(newTags uint32) EventOpt {
- return func(options *eventOptions) {
- options.tags |= newTags
- }
-}
-
-func WithActivityID(activityID *windows.GUID) EventOpt {
- return func(options *eventOptions) {
- options.activityID = activityID
- }
-}
-
-func WithRelatedActivityID(activityID *windows.GUID) EventOpt {
- return func(options *eventOptions) {
- options.relatedActivityID = activityID
- }
-}
+package etw
+
+import (
+ "golang.org/x/sys/windows"
+)
+
+type eventOptions struct {
+ descriptor *EventDescriptor
+ activityID *windows.GUID
+ relatedActivityID *windows.GUID
+ tags uint32
+}
+
+// EventOpt defines the option function type that can be passed to
+// Provider.WriteEvent to specify general event options, such as level and
+// keyword.
+type EventOpt func(options *eventOptions)
+
+// WithEventOpts returns the variadic arguments as a single slice.
+func WithEventOpts(opts ...EventOpt) []EventOpt {
+ return opts
+}
+
+// WithLevel specifies the level of the event to be written.
+func WithLevel(level Level) EventOpt {
+ return func(options *eventOptions) {
+ options.descriptor.Level = level
+ }
+}
+
+// WithKeyword specifies the keywords of the event to be written. Multiple uses
+// of this option are OR'd together.
+func WithKeyword(keyword uint64) EventOpt {
+ return func(options *eventOptions) {
+ options.descriptor.Keyword |= keyword
+ }
+}
+
+func WithChannel(channel Channel) EventOpt {
+ return func(options *eventOptions) {
+ options.descriptor.Channel = channel
+ }
+}
+
+// WithTags specifies the tags of the event to be written. Tags is a 28-bit
+// value (top 4 bits are ignored) which are interpreted by the event consumer.
+func WithTags(newTags uint32) EventOpt {
+ return func(options *eventOptions) {
+ options.tags |= newTags
+ }
+}
+
+func WithActivityID(activityID *windows.GUID) EventOpt {
+ return func(options *eventOptions) {
+ options.activityID = activityID
+ }
+}
+
+func WithRelatedActivityID(activityID *windows.GUID) EventOpt {
+ return func(options *eventOptions) {
+ options.relatedActivityID = activityID
+ }
+}
diff --git a/internal/etw/fieldopt.go b/internal/etw/fieldopt.go
index 76f63bcb..5d5b4254 100644
--- a/internal/etw/fieldopt.go
+++ b/internal/etw/fieldopt.go
@@ -1,379 +1,379 @@
-package etw
-
-import (
- "math"
- "unsafe"
-)
-
-// FieldOpt defines the option function type that can be passed to
-// Provider.WriteEvent to add fields to the event.
-type FieldOpt func(em *EventMetadata, ed *EventData)
-
-// WithFields returns the variadic arguments as a single slice.
-func WithFields(opts ...FieldOpt) []FieldOpt {
- return opts
-}
-
-// BoolField adds a single bool field to the event.
-func BoolField(name string, value bool) FieldOpt {
- return func(em *EventMetadata, ed *EventData) {
- em.WriteField(name, InTypeUint8, OutTypeBoolean, 0)
- bool8 := uint8(0)
- if value {
- bool8 = uint8(1)
- }
- ed.WriteUint8(bool8)
- }
-}
-
-// BoolArray adds an array of bool to the event.
-func BoolArray(name string, values []bool) FieldOpt {
- return func(em *EventMetadata, ed *EventData) {
- em.WriteArray(name, InTypeUint8, OutTypeBoolean, 0)
- ed.WriteUint16(uint16(len(values)))
- for _, v := range values {
- bool8 := uint8(0)
- if v {
- bool8 = uint8(1)
- }
- ed.WriteUint8(bool8)
- }
- }
-}
-
-// StringField adds a single string field to the event.
-func StringField(name string, value string) FieldOpt {
- return func(em *EventMetadata, ed *EventData) {
- em.WriteField(name, InTypeANSIString, OutTypeUTF8, 0)
- ed.WriteString(value)
- }
-}
-
-// StringArray adds an array of string to the event.
-func StringArray(name string, values []string) FieldOpt {
- return func(em *EventMetadata, ed *EventData) {
- em.WriteArray(name, InTypeANSIString, OutTypeUTF8, 0)
- ed.WriteUint16(uint16(len(values)))
- for _, v := range values {
- ed.WriteString(v)
- }
- }
-}
-
-// IntField adds a single int field to the event.
-func IntField(name string, value int) FieldOpt {
- switch unsafe.Sizeof(value) {
- case 4:
- return Int32Field(name, int32(value))
- case 8:
- return Int64Field(name, int64(value))
- default:
- panic("Unsupported int size")
- }
-}
-
-// IntArray adds an array of int to the event.
-func IntArray(name string, values []int) FieldOpt {
- inType := InTypeNull
- var writeItem func(*EventData, int)
- switch unsafe.Sizeof(values[0]) {
- case 4:
- inType = InTypeInt32
- writeItem = func(ed *EventData, item int) { ed.WriteInt32(int32(item)) }
- case 8:
- inType = InTypeInt64
- writeItem = func(ed *EventData, item int) { ed.WriteInt64(int64(item)) }
- default:
- panic("Unsupported int size")
- }
-
- return func(em *EventMetadata, ed *EventData) {
- em.WriteArray(name, inType, OutTypeDefault, 0)
- ed.WriteUint16(uint16(len(values)))
- for _, v := range values {
- writeItem(ed, v)
- }
- }
-}
-
-// Int8Field adds a single int8 field to the event.
-func Int8Field(name string, value int8) FieldOpt {
- return func(em *EventMetadata, ed *EventData) {
- em.WriteField(name, InTypeInt8, OutTypeDefault, 0)
- ed.WriteInt8(value)
- }
-}
-
-// Int8Array adds an array of int8 to the event.
-func Int8Array(name string, values []int8) FieldOpt {
- return func(em *EventMetadata, ed *EventData) {
- em.WriteArray(name, InTypeInt8, OutTypeDefault, 0)
- ed.WriteUint16(uint16(len(values)))
- for _, v := range values {
- ed.WriteInt8(v)
- }
- }
-}
-
-// Int16Field adds a single int16 field to the event.
-func Int16Field(name string, value int16) FieldOpt {
- return func(em *EventMetadata, ed *EventData) {
- em.WriteField(name, InTypeInt16, OutTypeDefault, 0)
- ed.WriteInt16(value)
- }
-}
-
-// Int16Array adds an array of int16 to the event.
-func Int16Array(name string, values []int16) FieldOpt {
- return func(em *EventMetadata, ed *EventData) {
- em.WriteArray(name, InTypeInt16, OutTypeDefault, 0)
- ed.WriteUint16(uint16(len(values)))
- for _, v := range values {
- ed.WriteInt16(v)
- }
- }
-}
-
-// Int32Field adds a single int32 field to the event.
-func Int32Field(name string, value int32) FieldOpt {
- return func(em *EventMetadata, ed *EventData) {
- em.WriteField(name, InTypeInt32, OutTypeDefault, 0)
- ed.WriteInt32(value)
- }
-}
-
-// Int32Array adds an array of int32 to the event.
-func Int32Array(name string, values []int32) FieldOpt {
- return func(em *EventMetadata, ed *EventData) {
- em.WriteArray(name, InTypeInt32, OutTypeDefault, 0)
- ed.WriteUint16(uint16(len(values)))
- for _, v := range values {
- ed.WriteInt32(v)
- }
- }
-}
-
-// Int64Field adds a single int64 field to the event.
-func Int64Field(name string, value int64) FieldOpt {
- return func(em *EventMetadata, ed *EventData) {
- em.WriteField(name, InTypeInt64, OutTypeDefault, 0)
- ed.WriteInt64(value)
- }
-}
-
-// Int64Array adds an array of int64 to the event.
-func Int64Array(name string, values []int64) FieldOpt {
- return func(em *EventMetadata, ed *EventData) {
- em.WriteArray(name, InTypeInt64, OutTypeDefault, 0)
- ed.WriteUint16(uint16(len(values)))
- for _, v := range values {
- ed.WriteInt64(v)
- }
- }
-}
-
-// UintField adds a single uint field to the event.
-func UintField(name string, value uint) FieldOpt {
- switch unsafe.Sizeof(value) {
- case 4:
- return Uint32Field(name, uint32(value))
- case 8:
- return Uint64Field(name, uint64(value))
- default:
- panic("Unsupported uint size")
- }
-}
-
-// UintArray adds an array of uint to the event.
-func UintArray(name string, values []uint) FieldOpt {
- inType := InTypeNull
- var writeItem func(*EventData, uint)
- switch unsafe.Sizeof(values[0]) {
- case 4:
- inType = InTypeUint32
- writeItem = func(ed *EventData, item uint) { ed.WriteUint32(uint32(item)) }
- case 8:
- inType = InTypeUint64
- writeItem = func(ed *EventData, item uint) { ed.WriteUint64(uint64(item)) }
- default:
- panic("Unsupported uint size")
- }
-
- return func(em *EventMetadata, ed *EventData) {
- em.WriteArray(name, inType, OutTypeDefault, 0)
- ed.WriteUint16(uint16(len(values)))
- for _, v := range values {
- writeItem(ed, v)
- }
- }
-}
-
-// Uint8Field adds a single uint8 field to the event.
-func Uint8Field(name string, value uint8) FieldOpt {
- return func(em *EventMetadata, ed *EventData) {
- em.WriteField(name, InTypeUint8, OutTypeDefault, 0)
- ed.WriteUint8(value)
- }
-}
-
-// Uint8Array adds an array of uint8 to the event.
-func Uint8Array(name string, values []uint8) FieldOpt {
- return func(em *EventMetadata, ed *EventData) {
- em.WriteArray(name, InTypeUint8, OutTypeDefault, 0)
- ed.WriteUint16(uint16(len(values)))
- for _, v := range values {
- ed.WriteUint8(v)
- }
- }
-}
-
-// Uint16Field adds a single uint16 field to the event.
-func Uint16Field(name string, value uint16) FieldOpt {
- return func(em *EventMetadata, ed *EventData) {
- em.WriteField(name, InTypeUint16, OutTypeDefault, 0)
- ed.WriteUint16(value)
- }
-}
-
-// Uint16Array adds an array of uint16 to the event.
-func Uint16Array(name string, values []uint16) FieldOpt {
- return func(em *EventMetadata, ed *EventData) {
- em.WriteArray(name, InTypeUint16, OutTypeDefault, 0)
- ed.WriteUint16(uint16(len(values)))
- for _, v := range values {
- ed.WriteUint16(v)
- }
- }
-}
-
-// Uint32Field adds a single uint32 field to the event.
-func Uint32Field(name string, value uint32) FieldOpt {
- return func(em *EventMetadata, ed *EventData) {
- em.WriteField(name, InTypeUint32, OutTypeDefault, 0)
- ed.WriteUint32(value)
- }
-}
-
-// Uint32Array adds an array of uint32 to the event.
-func Uint32Array(name string, values []uint32) FieldOpt {
- return func(em *EventMetadata, ed *EventData) {
- em.WriteArray(name, InTypeUint32, OutTypeDefault, 0)
- ed.WriteUint16(uint16(len(values)))
- for _, v := range values {
- ed.WriteUint32(v)
- }
- }
-}
-
-// Uint64Field adds a single uint64 field to the event.
-func Uint64Field(name string, value uint64) FieldOpt {
- return func(em *EventMetadata, ed *EventData) {
- em.WriteField(name, InTypeUint64, OutTypeDefault, 0)
- ed.WriteUint64(value)
- }
-}
-
-// Uint64Array adds an array of uint64 to the event.
-func Uint64Array(name string, values []uint64) FieldOpt {
- return func(em *EventMetadata, ed *EventData) {
- em.WriteArray(name, InTypeUint64, OutTypeDefault, 0)
- ed.WriteUint16(uint16(len(values)))
- for _, v := range values {
- ed.WriteUint64(v)
- }
- }
-}
-
-// UintptrField adds a single uintptr field to the event.
-func UintptrField(name string, value uintptr) FieldOpt {
- inType := InTypeNull
- var writeItem func(*EventData, uintptr)
- switch unsafe.Sizeof(value) {
- case 4:
- inType = InTypeHexInt32
- writeItem = func(ed *EventData, item uintptr) { ed.WriteUint32(uint32(item)) }
- case 8:
- inType = InTypeHexInt64
- writeItem = func(ed *EventData, item uintptr) { ed.WriteUint64(uint64(item)) }
- default:
- panic("Unsupported uintptr size")
- }
-
- return func(em *EventMetadata, ed *EventData) {
- em.WriteField(name, inType, OutTypeDefault, 0)
- writeItem(ed, value)
- }
-}
-
-// UintptrArray adds an array of uintptr to the event.
-func UintptrArray(name string, values []uintptr) FieldOpt {
- inType := InTypeNull
- var writeItem func(*EventData, uintptr)
- switch unsafe.Sizeof(values[0]) {
- case 4:
- inType = InTypeHexInt32
- writeItem = func(ed *EventData, item uintptr) { ed.WriteUint32(uint32(item)) }
- case 8:
- inType = InTypeHexInt64
- writeItem = func(ed *EventData, item uintptr) { ed.WriteUint64(uint64(item)) }
- default:
- panic("Unsupported uintptr size")
- }
-
- return func(em *EventMetadata, ed *EventData) {
- em.WriteArray(name, inType, OutTypeDefault, 0)
- ed.WriteUint16(uint16(len(values)))
- for _, v := range values {
- writeItem(ed, v)
- }
- }
-}
-
-// Float32Field adds a single float32 field to the event.
-func Float32Field(name string, value float32) FieldOpt {
- return func(em *EventMetadata, ed *EventData) {
- em.WriteField(name, InTypeFloat, OutTypeDefault, 0)
- ed.WriteUint32(math.Float32bits(value))
- }
-}
-
-// Float32Array adds an array of float32 to the event.
-func Float32Array(name string, values []float32) FieldOpt {
- return func(em *EventMetadata, ed *EventData) {
- em.WriteArray(name, InTypeFloat, OutTypeDefault, 0)
- ed.WriteUint16(uint16(len(values)))
- for _, v := range values {
- ed.WriteUint32(math.Float32bits(v))
- }
- }
-}
-
-// Float64Field adds a single float64 field to the event.
-func Float64Field(name string, value float64) FieldOpt {
- return func(em *EventMetadata, ed *EventData) {
- em.WriteField(name, InTypeDouble, OutTypeDefault, 0)
- ed.WriteUint64(math.Float64bits(value))
- }
-}
-
-// Float64Array adds an array of float64 to the event.
-func Float64Array(name string, values []float64) FieldOpt {
- return func(em *EventMetadata, ed *EventData) {
- em.WriteArray(name, InTypeDouble, OutTypeDefault, 0)
- ed.WriteUint16(uint16(len(values)))
- for _, v := range values {
- ed.WriteUint64(math.Float64bits(v))
- }
- }
-}
-
-// Struct adds a nested struct to the event, the FieldOpts in the opts argument
-// are used to specify the fields of the struct.
-func Struct(name string, opts ...FieldOpt) FieldOpt {
- return func(em *EventMetadata, ed *EventData) {
- em.WriteStruct(name, uint8(len(opts)), 0)
- for _, opt := range opts {
- opt(em, ed)
- }
- }
-}
+package etw
+
+import (
+ "math"
+ "unsafe"
+)
+
+// FieldOpt defines the option function type that can be passed to
+// Provider.WriteEvent to add fields to the event.
+type FieldOpt func(em *EventMetadata, ed *EventData)
+
+// WithFields returns the variadic arguments as a single slice.
+func WithFields(opts ...FieldOpt) []FieldOpt {
+ return opts
+}
+
+// BoolField adds a single bool field to the event.
+func BoolField(name string, value bool) FieldOpt {
+ return func(em *EventMetadata, ed *EventData) {
+ em.WriteField(name, InTypeUint8, OutTypeBoolean, 0)
+ bool8 := uint8(0)
+ if value {
+ bool8 = uint8(1)
+ }
+ ed.WriteUint8(bool8)
+ }
+}
+
+// BoolArray adds an array of bool to the event.
+func BoolArray(name string, values []bool) FieldOpt {
+ return func(em *EventMetadata, ed *EventData) {
+ em.WriteArray(name, InTypeUint8, OutTypeBoolean, 0)
+ ed.WriteUint16(uint16(len(values)))
+ for _, v := range values {
+ bool8 := uint8(0)
+ if v {
+ bool8 = uint8(1)
+ }
+ ed.WriteUint8(bool8)
+ }
+ }
+}
+
+// StringField adds a single string field to the event.
+func StringField(name string, value string) FieldOpt {
+ return func(em *EventMetadata, ed *EventData) {
+ em.WriteField(name, InTypeANSIString, OutTypeUTF8, 0)
+ ed.WriteString(value)
+ }
+}
+
+// StringArray adds an array of string to the event.
+func StringArray(name string, values []string) FieldOpt {
+ return func(em *EventMetadata, ed *EventData) {
+ em.WriteArray(name, InTypeANSIString, OutTypeUTF8, 0)
+ ed.WriteUint16(uint16(len(values)))
+ for _, v := range values {
+ ed.WriteString(v)
+ }
+ }
+}
+
+// IntField adds a single int field to the event.
+func IntField(name string, value int) FieldOpt {
+ switch unsafe.Sizeof(value) {
+ case 4:
+ return Int32Field(name, int32(value))
+ case 8:
+ return Int64Field(name, int64(value))
+ default:
+ panic("Unsupported int size")
+ }
+}
+
+// IntArray adds an array of int to the event.
+func IntArray(name string, values []int) FieldOpt {
+ inType := InTypeNull
+ var writeItem func(*EventData, int)
+ switch unsafe.Sizeof(values[0]) {
+ case 4:
+ inType = InTypeInt32
+ writeItem = func(ed *EventData, item int) { ed.WriteInt32(int32(item)) }
+ case 8:
+ inType = InTypeInt64
+ writeItem = func(ed *EventData, item int) { ed.WriteInt64(int64(item)) }
+ default:
+ panic("Unsupported int size")
+ }
+
+ return func(em *EventMetadata, ed *EventData) {
+ em.WriteArray(name, inType, OutTypeDefault, 0)
+ ed.WriteUint16(uint16(len(values)))
+ for _, v := range values {
+ writeItem(ed, v)
+ }
+ }
+}
+
+// Int8Field adds a single int8 field to the event.
+func Int8Field(name string, value int8) FieldOpt {
+ return func(em *EventMetadata, ed *EventData) {
+ em.WriteField(name, InTypeInt8, OutTypeDefault, 0)
+ ed.WriteInt8(value)
+ }
+}
+
+// Int8Array adds an array of int8 to the event.
+func Int8Array(name string, values []int8) FieldOpt {
+ return func(em *EventMetadata, ed *EventData) {
+ em.WriteArray(name, InTypeInt8, OutTypeDefault, 0)
+ ed.WriteUint16(uint16(len(values)))
+ for _, v := range values {
+ ed.WriteInt8(v)
+ }
+ }
+}
+
+// Int16Field adds a single int16 field to the event.
+func Int16Field(name string, value int16) FieldOpt {
+ return func(em *EventMetadata, ed *EventData) {
+ em.WriteField(name, InTypeInt16, OutTypeDefault, 0)
+ ed.WriteInt16(value)
+ }
+}
+
+// Int16Array adds an array of int16 to the event.
+func Int16Array(name string, values []int16) FieldOpt {
+ return func(em *EventMetadata, ed *EventData) {
+ em.WriteArray(name, InTypeInt16, OutTypeDefault, 0)
+ ed.WriteUint16(uint16(len(values)))
+ for _, v := range values {
+ ed.WriteInt16(v)
+ }
+ }
+}
+
+// Int32Field adds a single int32 field to the event.
+func Int32Field(name string, value int32) FieldOpt {
+ return func(em *EventMetadata, ed *EventData) {
+ em.WriteField(name, InTypeInt32, OutTypeDefault, 0)
+ ed.WriteInt32(value)
+ }
+}
+
+// Int32Array adds an array of int32 to the event.
+func Int32Array(name string, values []int32) FieldOpt {
+ return func(em *EventMetadata, ed *EventData) {
+ em.WriteArray(name, InTypeInt32, OutTypeDefault, 0)
+ ed.WriteUint16(uint16(len(values)))
+ for _, v := range values {
+ ed.WriteInt32(v)
+ }
+ }
+}
+
+// Int64Field adds a single int64 field to the event.
+func Int64Field(name string, value int64) FieldOpt {
+ return func(em *EventMetadata, ed *EventData) {
+ em.WriteField(name, InTypeInt64, OutTypeDefault, 0)
+ ed.WriteInt64(value)
+ }
+}
+
+// Int64Array adds an array of int64 to the event.
+func Int64Array(name string, values []int64) FieldOpt {
+ return func(em *EventMetadata, ed *EventData) {
+ em.WriteArray(name, InTypeInt64, OutTypeDefault, 0)
+ ed.WriteUint16(uint16(len(values)))
+ for _, v := range values {
+ ed.WriteInt64(v)
+ }
+ }
+}
+
+// UintField adds a single uint field to the event.
+func UintField(name string, value uint) FieldOpt {
+ switch unsafe.Sizeof(value) {
+ case 4:
+ return Uint32Field(name, uint32(value))
+ case 8:
+ return Uint64Field(name, uint64(value))
+ default:
+ panic("Unsupported uint size")
+ }
+}
+
+// UintArray adds an array of uint to the event.
+func UintArray(name string, values []uint) FieldOpt {
+ inType := InTypeNull
+ var writeItem func(*EventData, uint)
+ switch unsafe.Sizeof(values[0]) {
+ case 4:
+ inType = InTypeUint32
+ writeItem = func(ed *EventData, item uint) { ed.WriteUint32(uint32(item)) }
+ case 8:
+ inType = InTypeUint64
+ writeItem = func(ed *EventData, item uint) { ed.WriteUint64(uint64(item)) }
+ default:
+ panic("Unsupported uint size")
+ }
+
+ return func(em *EventMetadata, ed *EventData) {
+ em.WriteArray(name, inType, OutTypeDefault, 0)
+ ed.WriteUint16(uint16(len(values)))
+ for _, v := range values {
+ writeItem(ed, v)
+ }
+ }
+}
+
+// Uint8Field adds a single uint8 field to the event.
+func Uint8Field(name string, value uint8) FieldOpt {
+ return func(em *EventMetadata, ed *EventData) {
+ em.WriteField(name, InTypeUint8, OutTypeDefault, 0)
+ ed.WriteUint8(value)
+ }
+}
+
+// Uint8Array adds an array of uint8 to the event.
+func Uint8Array(name string, values []uint8) FieldOpt {
+ return func(em *EventMetadata, ed *EventData) {
+ em.WriteArray(name, InTypeUint8, OutTypeDefault, 0)
+ ed.WriteUint16(uint16(len(values)))
+ for _, v := range values {
+ ed.WriteUint8(v)
+ }
+ }
+}
+
+// Uint16Field adds a single uint16 field to the event.
+func Uint16Field(name string, value uint16) FieldOpt {
+ return func(em *EventMetadata, ed *EventData) {
+ em.WriteField(name, InTypeUint16, OutTypeDefault, 0)
+ ed.WriteUint16(value)
+ }
+}
+
+// Uint16Array adds an array of uint16 to the event.
+func Uint16Array(name string, values []uint16) FieldOpt {
+ return func(em *EventMetadata, ed *EventData) {
+ em.WriteArray(name, InTypeUint16, OutTypeDefault, 0)
+ ed.WriteUint16(uint16(len(values)))
+ for _, v := range values {
+ ed.WriteUint16(v)
+ }
+ }
+}
+
+// Uint32Field adds a single uint32 field to the event.
+func Uint32Field(name string, value uint32) FieldOpt {
+ return func(em *EventMetadata, ed *EventData) {
+ em.WriteField(name, InTypeUint32, OutTypeDefault, 0)
+ ed.WriteUint32(value)
+ }
+}
+
+// Uint32Array adds an array of uint32 to the event.
+func Uint32Array(name string, values []uint32) FieldOpt {
+ return func(em *EventMetadata, ed *EventData) {
+ em.WriteArray(name, InTypeUint32, OutTypeDefault, 0)
+ ed.WriteUint16(uint16(len(values)))
+ for _, v := range values {
+ ed.WriteUint32(v)
+ }
+ }
+}
+
+// Uint64Field adds a single uint64 field to the event.
+func Uint64Field(name string, value uint64) FieldOpt {
+ return func(em *EventMetadata, ed *EventData) {
+ em.WriteField(name, InTypeUint64, OutTypeDefault, 0)
+ ed.WriteUint64(value)
+ }
+}
+
+// Uint64Array adds an array of uint64 to the event.
+func Uint64Array(name string, values []uint64) FieldOpt {
+ return func(em *EventMetadata, ed *EventData) {
+ em.WriteArray(name, InTypeUint64, OutTypeDefault, 0)
+ ed.WriteUint16(uint16(len(values)))
+ for _, v := range values {
+ ed.WriteUint64(v)
+ }
+ }
+}
+
+// UintptrField adds a single uintptr field to the event.
+func UintptrField(name string, value uintptr) FieldOpt {
+ inType := InTypeNull
+ var writeItem func(*EventData, uintptr)
+ switch unsafe.Sizeof(value) {
+ case 4:
+ inType = InTypeHexInt32
+ writeItem = func(ed *EventData, item uintptr) { ed.WriteUint32(uint32(item)) }
+ case 8:
+ inType = InTypeHexInt64
+ writeItem = func(ed *EventData, item uintptr) { ed.WriteUint64(uint64(item)) }
+ default:
+ panic("Unsupported uintptr size")
+ }
+
+ return func(em *EventMetadata, ed *EventData) {
+ em.WriteField(name, inType, OutTypeDefault, 0)
+ writeItem(ed, value)
+ }
+}
+
+// UintptrArray adds an array of uintptr to the event.
+func UintptrArray(name string, values []uintptr) FieldOpt {
+ inType := InTypeNull
+ var writeItem func(*EventData, uintptr)
+ switch unsafe.Sizeof(values[0]) {
+ case 4:
+ inType = InTypeHexInt32
+ writeItem = func(ed *EventData, item uintptr) { ed.WriteUint32(uint32(item)) }
+ case 8:
+ inType = InTypeHexInt64
+ writeItem = func(ed *EventData, item uintptr) { ed.WriteUint64(uint64(item)) }
+ default:
+ panic("Unsupported uintptr size")
+ }
+
+ return func(em *EventMetadata, ed *EventData) {
+ em.WriteArray(name, inType, OutTypeDefault, 0)
+ ed.WriteUint16(uint16(len(values)))
+ for _, v := range values {
+ writeItem(ed, v)
+ }
+ }
+}
+
+// Float32Field adds a single float32 field to the event.
+func Float32Field(name string, value float32) FieldOpt {
+ return func(em *EventMetadata, ed *EventData) {
+ em.WriteField(name, InTypeFloat, OutTypeDefault, 0)
+ ed.WriteUint32(math.Float32bits(value))
+ }
+}
+
+// Float32Array adds an array of float32 to the event.
+func Float32Array(name string, values []float32) FieldOpt {
+ return func(em *EventMetadata, ed *EventData) {
+ em.WriteArray(name, InTypeFloat, OutTypeDefault, 0)
+ ed.WriteUint16(uint16(len(values)))
+ for _, v := range values {
+ ed.WriteUint32(math.Float32bits(v))
+ }
+ }
+}
+
+// Float64Field adds a single float64 field to the event.
+func Float64Field(name string, value float64) FieldOpt {
+ return func(em *EventMetadata, ed *EventData) {
+ em.WriteField(name, InTypeDouble, OutTypeDefault, 0)
+ ed.WriteUint64(math.Float64bits(value))
+ }
+}
+
+// Float64Array adds an array of float64 to the event.
+func Float64Array(name string, values []float64) FieldOpt {
+ return func(em *EventMetadata, ed *EventData) {
+ em.WriteArray(name, InTypeDouble, OutTypeDefault, 0)
+ ed.WriteUint16(uint16(len(values)))
+ for _, v := range values {
+ ed.WriteUint64(math.Float64bits(v))
+ }
+ }
+}
+
+// Struct adds a nested struct to the event, the FieldOpts in the opts argument
+// are used to specify the fields of the struct.
+func Struct(name string, opts ...FieldOpt) FieldOpt {
+ return func(em *EventMetadata, ed *EventData) {
+ em.WriteStruct(name, uint8(len(opts)), 0)
+ for _, opt := range opts {
+ opt(em, ed)
+ }
+ }
+}
diff --git a/internal/etw/provider.go b/internal/etw/provider.go
index 41abf347..452c860f 100644
--- a/internal/etw/provider.go
+++ b/internal/etw/provider.go
@@ -1,279 +1,279 @@
-package etw
-
-import (
- "bytes"
- "crypto/sha1"
- "encoding/binary"
- "encoding/hex"
- "fmt"
- "strings"
- "unicode/utf16"
- "unsafe"
-
- "golang.org/x/sys/windows"
-)
-
-// Provider represents an ETW event provider. It is identified by a provider
-// name and ID (GUID), which should always have a 1:1 mapping to each other
-// (e.g. don't use multiple provider names with the same ID, or vice versa).
-type Provider struct {
- ID *windows.GUID
- handle providerHandle
- metadata []byte
- callback EnableCallback
- index uint
- enabled bool
- level Level
- keywordAny uint64
- keywordAll uint64
-}
-
-// String returns the `provider`.ID as a string
-func (provider *Provider) String() string {
- data1 := make([]byte, 4)
- binary.BigEndian.PutUint32(data1, provider.ID.Data1)
- data2 := make([]byte, 2)
- binary.BigEndian.PutUint16(data2, provider.ID.Data2)
- data3 := make([]byte, 2)
- binary.BigEndian.PutUint16(data3, provider.ID.Data3)
- return fmt.Sprintf(
- "%s-%s-%s-%s-%s",
- hex.EncodeToString(data1),
- hex.EncodeToString(data2),
- hex.EncodeToString(data3),
- hex.EncodeToString(provider.ID.Data4[:2]),
- hex.EncodeToString(provider.ID.Data4[2:]))
-}
-
-type providerHandle windows.Handle
-
-// ProviderState informs the provider EnableCallback what action is being
-// performed.
-type ProviderState uint32
-
-const (
- // ProviderStateDisable indicates the provider is being disabled.
- ProviderStateDisable ProviderState = iota
- // ProviderStateEnable indicates the provider is being enabled.
- ProviderStateEnable
- // ProviderStateCaptureState indicates the provider is having its current
- // state snap-shotted.
- ProviderStateCaptureState
-)
-
-type eventInfoClass uint32
-
-const (
- eventInfoClassProviderBinaryTrackInfo eventInfoClass = iota
- eventInfoClassProviderSetReserved1
- eventInfoClassProviderSetTraits
- eventInfoClassProviderUseDescriptorType
-)
-
-// EnableCallback is the form of the callback function that receives provider
-// enable/disable notifications from ETW.
-type EnableCallback func(*windows.GUID, ProviderState, Level, uint64, uint64, uintptr)
-
-func providerCallback(sourceID *windows.GUID, state ProviderState, level Level, matchAnyKeyword uint64, matchAllKeyword uint64, filterData uintptr, i uintptr) {
- provider := providers.getProvider(uint(i))
-
- switch state {
- case ProviderStateDisable:
- provider.enabled = false
- case ProviderStateEnable:
- provider.enabled = true
- provider.level = level
- provider.keywordAny = matchAnyKeyword
- provider.keywordAll = matchAllKeyword
- }
-
- if provider.callback != nil {
- provider.callback(sourceID, state, level, matchAnyKeyword, matchAllKeyword, filterData)
- }
-}
-
-// providerCallbackAdapter acts as the first-level callback from the C/ETW side
-// for provider notifications. Because Go has trouble with callback arguments of
-// different size, it has only pointer-sized arguments, which are then cast to
-// the appropriate types when calling providerCallback.
-func providerCallbackAdapter(sourceID *windows.GUID, state uintptr, level uintptr, matchAnyKeyword uintptr, matchAllKeyword uintptr, filterData uintptr, i uintptr) uintptr {
- providerCallback(sourceID, ProviderState(state), Level(level), uint64(matchAnyKeyword), uint64(matchAllKeyword), filterData, i)
- return 0
-}
-
-// providerIDFromName generates a provider ID based on the provider name. It
-// uses the same algorithm as used by .NET's EventSource class, which is based
-// on RFC 4122. More information on the algorithm can be found here:
-// https://blogs.msdn.microsoft.com/dcook/2015/09/08/etw-provider-names-and-guids/
-// The algorithm is roughly:
-// Hash = Sha1(namespace + arg.ToUpper().ToUtf16be())
-// Guid = Hash[0..15], with Hash[7] tweaked according to RFC 4122
-func providerIDFromName(name string) *windows.GUID {
- buffer := sha1.New()
-
- namespace := []byte{0x48, 0x2C, 0x2D, 0xB2, 0xC3, 0x90, 0x47, 0xC8, 0x87, 0xF8, 0x1A, 0x15, 0xBF, 0xC1, 0x30, 0xFB}
- buffer.Write(namespace)
-
- binary.Write(buffer, binary.BigEndian, utf16.Encode([]rune(strings.ToUpper(name))))
-
- sum := buffer.Sum(nil)
- sum[7] = (sum[7] & 0xf) | 0x50
-
- return &windows.GUID{
- Data1: binary.LittleEndian.Uint32(sum[0:4]),
- Data2: binary.LittleEndian.Uint16(sum[4:6]),
- Data3: binary.LittleEndian.Uint16(sum[6:8]),
- Data4: [8]byte{sum[8], sum[9], sum[10], sum[11], sum[12], sum[13], sum[14], sum[15]},
- }
-}
-
-// NewProvider creates and registers a new ETW provider. The provider ID is
-// generated based on the provider name.
-func NewProvider(name string, callback EnableCallback) (provider *Provider, err error) {
- return NewProviderWithID(name, providerIDFromName(name), callback)
-}
-
-// NewProviderWithID creates and registers a new ETW provider, allowing the
-// provider ID to be manually specified. This is most useful when there is an
-// existing provider ID that must be used to conform to existing diagnostic
-// infrastructure.
-func NewProviderWithID(name string, id *windows.GUID, callback EnableCallback) (provider *Provider, err error) {
- providerCallbackOnce.Do(func() {
- globalProviderCallback = windows.NewCallback(providerCallbackAdapter)
- })
-
- provider = providers.newProvider()
- defer func() {
- if err != nil {
- providers.removeProvider(provider)
- }
- }()
- provider.ID = id
- provider.callback = callback
-
- if err := eventRegister(provider.ID, globalProviderCallback, uintptr(provider.index), &provider.handle); err != nil {
- return nil, err
- }
-
- metadata := &bytes.Buffer{}
- 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()
-
- if err := eventSetInformation(
- provider.handle,
- eventInfoClassProviderSetTraits,
- uintptr(unsafe.Pointer(&provider.metadata[0])),
- uint32(len(provider.metadata))); err != nil {
-
- return nil, err
- }
-
- return provider, nil
-}
-
-// Close unregisters the provider.
-func (provider *Provider) Close() error {
- providers.removeProvider(provider)
- return eventUnregister(provider.handle)
-}
-
-// IsEnabled calls IsEnabledForLevelAndKeywords with LevelAlways and all
-// keywords set.
-func (provider *Provider) IsEnabled() bool {
- return provider.IsEnabledForLevelAndKeywords(LevelAlways, ^uint64(0))
-}
-
-// IsEnabledForLevel calls IsEnabledForLevelAndKeywords with the specified level
-// and all keywords set.
-func (provider *Provider) IsEnabledForLevel(level Level) bool {
- return provider.IsEnabledForLevelAndKeywords(level, ^uint64(0))
-}
-
-// IsEnabledForLevelAndKeywords allows event producer code to check if there are
-// any event sessions that are interested in an event, based on the event level
-// and keywords. Although this check happens automatically in the ETW
-// infrastructure, it can be useful to check if an event will actually be
-// consumed before doing expensive work to build the event data.
-func (provider *Provider) IsEnabledForLevelAndKeywords(level Level, keywords uint64) bool {
- if !provider.enabled {
- return false
- }
-
- // ETW automatically sets the level to 255 if it is specified as 0, so we
- // don't need to worry about the level=0 (all events) case.
- if level > provider.level {
- return false
- }
-
- if keywords != 0 && (keywords&provider.keywordAny == 0 || keywords&provider.keywordAll != provider.keywordAll) {
- return false
- }
-
- return true
-}
-
-// WriteEvent writes a single ETW event from the provider. The event is
-// constructed based on the EventOpt and FieldOpt values that are passed as
-// opts.
-func (provider *Provider) WriteEvent(name string, eventOpts []EventOpt, fieldOpts []FieldOpt) error {
- options := eventOptions{descriptor: NewEventDescriptor()}
- em := &EventMetadata{}
- ed := &EventData{}
-
- // We need to evaluate the EventOpts first since they might change tags, and
- // we write out the tags before evaluating FieldOpts.
- for _, opt := range eventOpts {
- opt(&options)
- }
-
- if !provider.IsEnabledForLevelAndKeywords(options.descriptor.Level, options.descriptor.Keyword) {
- return nil
- }
-
- em.WriteEventHeader(name, options.tags)
-
- for _, opt := range fieldOpts {
- opt(em, ed)
- }
-
- // Don't pass a data blob if there is no event data. There will always be
- // event metadata (e.g. for the name) so we don't need to do this check for
- // the metadata.
- dataBlobs := [][]byte{}
- if len(ed.Bytes()) > 0 {
- dataBlobs = [][]byte{ed.Bytes()}
- }
-
- return provider.WriteEventRaw(options.descriptor, nil, nil, [][]byte{em.Bytes()}, dataBlobs)
-}
-
-// WriteEventRaw writes a single ETW event from the provider. This function is
-// less abstracted than WriteEvent, and presents a fairly direct interface to
-// the event writing functionality. It expects a series of event metadata and
-// event data blobs to be passed in, which must conform to the TraceLogging
-// schema. The functions on EventMetadata and EventData can help with creating
-// these blobs. The blobs of each type are effectively concatenated together by
-// the ETW infrastructure.
-func (provider *Provider) WriteEventRaw(
- descriptor *EventDescriptor,
- activityID *windows.GUID,
- relatedActivityID *windows.GUID,
- metadataBlobs [][]byte,
- dataBlobs [][]byte) error {
-
- dataDescriptorCount := uint32(1 + len(metadataBlobs) + len(dataBlobs))
- dataDescriptors := make([]eventDataDescriptor, 0, dataDescriptorCount)
-
- dataDescriptors = append(dataDescriptors, newEventDataDescriptor(eventDataDescriptorTypeProviderMetadata, provider.metadata))
- for _, blob := range metadataBlobs {
- dataDescriptors = append(dataDescriptors, newEventDataDescriptor(eventDataDescriptorTypeEventMetadata, blob))
- }
- for _, blob := range dataBlobs {
- dataDescriptors = append(dataDescriptors, newEventDataDescriptor(eventDataDescriptorTypeUserData, blob))
- }
-
- return eventWriteTransfer(provider.handle, descriptor, activityID, relatedActivityID, dataDescriptorCount, &dataDescriptors[0])
-}
+package etw
+
+import (
+ "bytes"
+ "crypto/sha1"
+ "encoding/binary"
+ "encoding/hex"
+ "fmt"
+ "strings"
+ "unicode/utf16"
+ "unsafe"
+
+ "golang.org/x/sys/windows"
+)
+
+// Provider represents an ETW event provider. It is identified by a provider
+// name and ID (GUID), which should always have a 1:1 mapping to each other
+// (e.g. don't use multiple provider names with the same ID, or vice versa).
+type Provider struct {
+ ID *windows.GUID
+ handle providerHandle
+ metadata []byte
+ callback EnableCallback
+ index uint
+ enabled bool
+ level Level
+ keywordAny uint64
+ keywordAll uint64
+}
+
+// String returns the `provider`.ID as a string
+func (provider *Provider) String() string {
+ data1 := make([]byte, 4)
+ binary.BigEndian.PutUint32(data1, provider.ID.Data1)
+ data2 := make([]byte, 2)
+ binary.BigEndian.PutUint16(data2, provider.ID.Data2)
+ data3 := make([]byte, 2)
+ binary.BigEndian.PutUint16(data3, provider.ID.Data3)
+ return fmt.Sprintf(
+ "%s-%s-%s-%s-%s",
+ hex.EncodeToString(data1),
+ hex.EncodeToString(data2),
+ hex.EncodeToString(data3),
+ hex.EncodeToString(provider.ID.Data4[:2]),
+ hex.EncodeToString(provider.ID.Data4[2:]))
+}
+
+type providerHandle windows.Handle
+
+// ProviderState informs the provider EnableCallback what action is being
+// performed.
+type ProviderState uint32
+
+const (
+ // ProviderStateDisable indicates the provider is being disabled.
+ ProviderStateDisable ProviderState = iota
+ // ProviderStateEnable indicates the provider is being enabled.
+ ProviderStateEnable
+ // ProviderStateCaptureState indicates the provider is having its current
+ // state snap-shotted.
+ ProviderStateCaptureState
+)
+
+type eventInfoClass uint32
+
+const (
+ eventInfoClassProviderBinaryTrackInfo eventInfoClass = iota
+ eventInfoClassProviderSetReserved1
+ eventInfoClassProviderSetTraits
+ eventInfoClassProviderUseDescriptorType
+)
+
+// EnableCallback is the form of the callback function that receives provider
+// enable/disable notifications from ETW.
+type EnableCallback func(*windows.GUID, ProviderState, Level, uint64, uint64, uintptr)
+
+func providerCallback(sourceID *windows.GUID, state ProviderState, level Level, matchAnyKeyword uint64, matchAllKeyword uint64, filterData uintptr, i uintptr) {
+ provider := providers.getProvider(uint(i))
+
+ switch state {
+ case ProviderStateDisable:
+ provider.enabled = false
+ case ProviderStateEnable:
+ provider.enabled = true
+ provider.level = level
+ provider.keywordAny = matchAnyKeyword
+ provider.keywordAll = matchAllKeyword
+ }
+
+ if provider.callback != nil {
+ provider.callback(sourceID, state, level, matchAnyKeyword, matchAllKeyword, filterData)
+ }
+}
+
+// providerCallbackAdapter acts as the first-level callback from the C/ETW side
+// for provider notifications. Because Go has trouble with callback arguments of
+// different size, it has only pointer-sized arguments, which are then cast to
+// the appropriate types when calling providerCallback.
+func providerCallbackAdapter(sourceID *windows.GUID, state uintptr, level uintptr, matchAnyKeyword uintptr, matchAllKeyword uintptr, filterData uintptr, i uintptr) uintptr {
+ providerCallback(sourceID, ProviderState(state), Level(level), uint64(matchAnyKeyword), uint64(matchAllKeyword), filterData, i)
+ return 0
+}
+
+// providerIDFromName generates a provider ID based on the provider name. It
+// uses the same algorithm as used by .NET's EventSource class, which is based
+// on RFC 4122. More information on the algorithm can be found here:
+// https://blogs.msdn.microsoft.com/dcook/2015/09/08/etw-provider-names-and-guids/
+// The algorithm is roughly:
+// Hash = Sha1(namespace + arg.ToUpper().ToUtf16be())
+// Guid = Hash[0..15], with Hash[7] tweaked according to RFC 4122
+func providerIDFromName(name string) *windows.GUID {
+ buffer := sha1.New()
+
+ namespace := []byte{0x48, 0x2C, 0x2D, 0xB2, 0xC3, 0x90, 0x47, 0xC8, 0x87, 0xF8, 0x1A, 0x15, 0xBF, 0xC1, 0x30, 0xFB}
+ buffer.Write(namespace)
+
+ binary.Write(buffer, binary.BigEndian, utf16.Encode([]rune(strings.ToUpper(name))))
+
+ sum := buffer.Sum(nil)
+ sum[7] = (sum[7] & 0xf) | 0x50
+
+ return &windows.GUID{
+ Data1: binary.LittleEndian.Uint32(sum[0:4]),
+ Data2: binary.LittleEndian.Uint16(sum[4:6]),
+ Data3: binary.LittleEndian.Uint16(sum[6:8]),
+ Data4: [8]byte{sum[8], sum[9], sum[10], sum[11], sum[12], sum[13], sum[14], sum[15]},
+ }
+}
+
+// NewProvider creates and registers a new ETW provider. The provider ID is
+// generated based on the provider name.
+func NewProvider(name string, callback EnableCallback) (provider *Provider, err error) {
+ return NewProviderWithID(name, providerIDFromName(name), callback)
+}
+
+// NewProviderWithID creates and registers a new ETW provider, allowing the
+// provider ID to be manually specified. This is most useful when there is an
+// existing provider ID that must be used to conform to existing diagnostic
+// infrastructure.
+func NewProviderWithID(name string, id *windows.GUID, callback EnableCallback) (provider *Provider, err error) {
+ providerCallbackOnce.Do(func() {
+ globalProviderCallback = windows.NewCallback(providerCallbackAdapter)
+ })
+
+ provider = providers.newProvider()
+ defer func() {
+ if err != nil {
+ providers.removeProvider(provider)
+ }
+ }()
+ provider.ID = id
+ provider.callback = callback
+
+ if err := eventRegister(provider.ID, globalProviderCallback, uintptr(provider.index), &provider.handle); err != nil {
+ return nil, err
+ }
+
+ metadata := &bytes.Buffer{}
+ 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()
+
+ if err := eventSetInformation(
+ provider.handle,
+ eventInfoClassProviderSetTraits,
+ uintptr(unsafe.Pointer(&provider.metadata[0])),
+ uint32(len(provider.metadata))); err != nil {
+
+ return nil, err
+ }
+
+ return provider, nil
+}
+
+// Close unregisters the provider.
+func (provider *Provider) Close() error {
+ providers.removeProvider(provider)
+ return eventUnregister(provider.handle)
+}
+
+// IsEnabled calls IsEnabledForLevelAndKeywords with LevelAlways and all
+// keywords set.
+func (provider *Provider) IsEnabled() bool {
+ return provider.IsEnabledForLevelAndKeywords(LevelAlways, ^uint64(0))
+}
+
+// IsEnabledForLevel calls IsEnabledForLevelAndKeywords with the specified level
+// and all keywords set.
+func (provider *Provider) IsEnabledForLevel(level Level) bool {
+ return provider.IsEnabledForLevelAndKeywords(level, ^uint64(0))
+}
+
+// IsEnabledForLevelAndKeywords allows event producer code to check if there are
+// any event sessions that are interested in an event, based on the event level
+// and keywords. Although this check happens automatically in the ETW
+// infrastructure, it can be useful to check if an event will actually be
+// consumed before doing expensive work to build the event data.
+func (provider *Provider) IsEnabledForLevelAndKeywords(level Level, keywords uint64) bool {
+ if !provider.enabled {
+ return false
+ }
+
+ // ETW automatically sets the level to 255 if it is specified as 0, so we
+ // don't need to worry about the level=0 (all events) case.
+ if level > provider.level {
+ return false
+ }
+
+ if keywords != 0 && (keywords&provider.keywordAny == 0 || keywords&provider.keywordAll != provider.keywordAll) {
+ return false
+ }
+
+ return true
+}
+
+// WriteEvent writes a single ETW event from the provider. The event is
+// constructed based on the EventOpt and FieldOpt values that are passed as
+// opts.
+func (provider *Provider) WriteEvent(name string, eventOpts []EventOpt, fieldOpts []FieldOpt) error {
+ options := eventOptions{descriptor: NewEventDescriptor()}
+ em := &EventMetadata{}
+ ed := &EventData{}
+
+ // We need to evaluate the EventOpts first since they might change tags, and
+ // we write out the tags before evaluating FieldOpts.
+ for _, opt := range eventOpts {
+ opt(&options)
+ }
+
+ if !provider.IsEnabledForLevelAndKeywords(options.descriptor.Level, options.descriptor.Keyword) {
+ return nil
+ }
+
+ em.WriteEventHeader(name, options.tags)
+
+ for _, opt := range fieldOpts {
+ opt(em, ed)
+ }
+
+ // Don't pass a data blob if there is no event data. There will always be
+ // event metadata (e.g. for the name) so we don't need to do this check for
+ // the metadata.
+ dataBlobs := [][]byte{}
+ if len(ed.Bytes()) > 0 {
+ dataBlobs = [][]byte{ed.Bytes()}
+ }
+
+ return provider.WriteEventRaw(options.descriptor, nil, nil, [][]byte{em.Bytes()}, dataBlobs)
+}
+
+// WriteEventRaw writes a single ETW event from the provider. This function is
+// less abstracted than WriteEvent, and presents a fairly direct interface to
+// the event writing functionality. It expects a series of event metadata and
+// event data blobs to be passed in, which must conform to the TraceLogging
+// schema. The functions on EventMetadata and EventData can help with creating
+// these blobs. The blobs of each type are effectively concatenated together by
+// the ETW infrastructure.
+func (provider *Provider) WriteEventRaw(
+ descriptor *EventDescriptor,
+ activityID *windows.GUID,
+ relatedActivityID *windows.GUID,
+ metadataBlobs [][]byte,
+ dataBlobs [][]byte) error {
+
+ dataDescriptorCount := uint32(1 + len(metadataBlobs) + len(dataBlobs))
+ dataDescriptors := make([]eventDataDescriptor, 0, dataDescriptorCount)
+
+ dataDescriptors = append(dataDescriptors, newEventDataDescriptor(eventDataDescriptorTypeProviderMetadata, provider.metadata))
+ for _, blob := range metadataBlobs {
+ dataDescriptors = append(dataDescriptors, newEventDataDescriptor(eventDataDescriptorTypeEventMetadata, blob))
+ }
+ for _, blob := range dataBlobs {
+ dataDescriptors = append(dataDescriptors, newEventDataDescriptor(eventDataDescriptorTypeUserData, blob))
+ }
+
+ return eventWriteTransfer(provider.handle, descriptor, activityID, relatedActivityID, dataDescriptorCount, &dataDescriptors[0])
+}
diff --git a/internal/etw/providerglobal.go b/internal/etw/providerglobal.go
index 28177a1f..6c7331d9 100644
--- a/internal/etw/providerglobal.go
+++ b/internal/etw/providerglobal.go
@@ -1,52 +1,52 @@
-package etw
-
-import (
- "sync"
-)
-
-// Because the provider callback function needs to be able to access the
-// provider data when it is invoked by ETW, we need to keep provider data stored
-// in a global map based on an index. The index is passed as the callback
-// context to ETW.
-type providerMap struct {
- m map[uint]*Provider
- i uint
- lock sync.Mutex
- once sync.Once
-}
-
-var providers = providerMap{
- m: make(map[uint]*Provider),
-}
-
-func (p *providerMap) newProvider() *Provider {
- p.lock.Lock()
- defer p.lock.Unlock()
-
- i := p.i
- p.i++
-
- provider := &Provider{
- index: i,
- }
-
- p.m[i] = provider
- return provider
-}
-
-func (p *providerMap) removeProvider(provider *Provider) {
- p.lock.Lock()
- defer p.lock.Unlock()
-
- delete(p.m, provider.index)
-}
-
-func (p *providerMap) getProvider(index uint) *Provider {
- p.lock.Lock()
- defer p.lock.Unlock()
-
- return p.m[index]
-}
-
-var providerCallbackOnce sync.Once
-var globalProviderCallback uintptr
+package etw
+
+import (
+ "sync"
+)
+
+// Because the provider callback function needs to be able to access the
+// provider data when it is invoked by ETW, we need to keep provider data stored
+// in a global map based on an index. The index is passed as the callback
+// context to ETW.
+type providerMap struct {
+ m map[uint]*Provider
+ i uint
+ lock sync.Mutex
+ once sync.Once
+}
+
+var providers = providerMap{
+ m: make(map[uint]*Provider),
+}
+
+func (p *providerMap) newProvider() *Provider {
+ p.lock.Lock()
+ defer p.lock.Unlock()
+
+ i := p.i
+ p.i++
+
+ provider := &Provider{
+ index: i,
+ }
+
+ p.m[i] = provider
+ return provider
+}
+
+func (p *providerMap) removeProvider(provider *Provider) {
+ p.lock.Lock()
+ defer p.lock.Unlock()
+
+ delete(p.m, provider.index)
+}
+
+func (p *providerMap) getProvider(index uint) *Provider {
+ p.lock.Lock()
+ defer p.lock.Unlock()
+
+ return p.m[index]
+}
+
+var providerCallbackOnce sync.Once
+var globalProviderCallback uintptr
diff --git a/internal/etw/ptr64_32.go b/internal/etw/ptr64_32.go
index 435ec3c0..d1a76125 100644
--- a/internal/etw/ptr64_32.go
+++ b/internal/etw/ptr64_32.go
@@ -1,16 +1,16 @@
-// +build 386 arm
-
-package etw
-
-import (
- "unsafe"
-)
-
-// byteptr64 defines a struct containing a pointer. The struct is guaranteed to
-// be 64 bits, regardless of the actual size of a pointer on the platform. This
-// is intended for use with certain Windows APIs that expect a pointer as a
-// ULONGLONG.
-type ptr64 struct {
- ptr unsafe.Pointer
- _ uint32
-}
+// +build 386 arm
+
+package etw
+
+import (
+ "unsafe"
+)
+
+// byteptr64 defines a struct containing a pointer. The struct is guaranteed to
+// be 64 bits, regardless of the actual size of a pointer on the platform. This
+// is intended for use with certain Windows APIs that expect a pointer as a
+// ULONGLONG.
+type ptr64 struct {
+ ptr unsafe.Pointer
+ _ uint32
+}
diff --git a/internal/etw/ptr64_64.go b/internal/etw/ptr64_64.go
index 903d3c0a..b86c8f2b 100644
--- a/internal/etw/ptr64_64.go
+++ b/internal/etw/ptr64_64.go
@@ -1,15 +1,15 @@
-// +build amd64 arm64
-
-package etw
-
-import (
- "unsafe"
-)
-
-// byteptr64 defines a struct containing a pointer. The struct is guaranteed to
-// be 64 bits, regardless of the actual size of a pointer on the platform. This
-// is intended for use with certain Windows APIs that expect a pointer as a
-// ULONGLONG.
-type ptr64 struct {
- ptr unsafe.Pointer
-}
+// +build amd64 arm64
+
+package etw
+
+import (
+ "unsafe"
+)
+
+// byteptr64 defines a struct containing a pointer. The struct is guaranteed to
+// be 64 bits, regardless of the actual size of a pointer on the platform. This
+// is intended for use with certain Windows APIs that expect a pointer as a
+// ULONGLONG.
+type ptr64 struct {
+ ptr unsafe.Pointer
+}
diff --git a/pkg/etwlogrus/hook.go b/pkg/etwlogrus/hook.go
index d3d5713b..fe0835b3 100644
--- a/pkg/etwlogrus/hook.go
+++ b/pkg/etwlogrus/hook.go
@@ -1,192 +1,192 @@
-package etwlogrus
-
-import (
- "fmt"
- "reflect"
-
- "github.com/Microsoft/go-winio/internal/etw"
- "github.com/sirupsen/logrus"
-)
-
-// Hook is a Logrus hook which logs received events to ETW.
-type Hook struct {
- provider *etw.Provider
-}
-
-// NewHook registers a new ETW provider and returns a hook to log from it.
-func NewHook(providerName string) (*Hook, error) {
- hook := Hook{}
-
- provider, err := etw.NewProvider(providerName, nil)
- if err != nil {
- return nil, err
- }
- hook.provider = provider
-
- return &hook, nil
-}
-
-// Levels returns the set of levels that this hook wants to receive log entries
-// for.
-func (h *Hook) Levels() []logrus.Level {
- return []logrus.Level{
- logrus.TraceLevel,
- logrus.DebugLevel,
- logrus.InfoLevel,
- logrus.WarnLevel,
- logrus.ErrorLevel,
- logrus.FatalLevel,
- logrus.PanicLevel,
- }
-}
-
-// Fire receives each Logrus entry as it is logged, and logs it to ETW.
-func (h *Hook) Fire(e *logrus.Entry) error {
- level := etw.Level(e.Level)
- if !h.provider.IsEnabledForLevel(level) {
- return nil
- }
-
- // Reserve extra space for the message field.
- fields := make([]etw.FieldOpt, 0, len(e.Data)+1)
-
- fields = append(fields, etw.StringField("Message", e.Message))
-
- for k, v := range e.Data {
- fields = append(fields, getFieldOpt(k, v))
- }
-
- // We could try to map Logrus levels to ETW levels, but we would lose some
- // fidelity as there are fewer ETW levels. So instead we use the level
- // directly.
- return h.provider.WriteEvent(
- "LogrusEntry",
- etw.WithEventOpts(etw.WithLevel(level)),
- fields)
-}
-
-// Currently, we support logging basic builtin types (int, string, etc), slices
-// of basic builtin types, error, types derived from the basic types (e.g. "type
-// foo int"), and structs (recursively logging their fields). We do not support
-// slices of derived types (e.g. "[]foo").
-//
-// For types that we don't support, the value is formatted via fmt.Sprint, and
-// we also log a message that the type is unsupported along with the formatted
-// type. The intent of this is to make it easier to see which types are not
-// supported in traces, so we can evaluate adding support for more types in the
-// future.
-func getFieldOpt(k string, v interface{}) etw.FieldOpt {
- switch v := v.(type) {
- case bool:
- return etw.BoolField(k, v)
- case []bool:
- return etw.BoolArray(k, v)
- case string:
- return etw.StringField(k, v)
- case []string:
- return etw.StringArray(k, v)
- case int:
- return etw.IntField(k, v)
- case []int:
- return etw.IntArray(k, v)
- case int8:
- return etw.Int8Field(k, v)
- case []int8:
- return etw.Int8Array(k, v)
- case int16:
- return etw.Int16Field(k, v)
- case []int16:
- return etw.Int16Array(k, v)
- case int32:
- return etw.Int32Field(k, v)
- case []int32:
- return etw.Int32Array(k, v)
- case int64:
- return etw.Int64Field(k, v)
- case []int64:
- return etw.Int64Array(k, v)
- case uint:
- return etw.UintField(k, v)
- case []uint:
- return etw.UintArray(k, v)
- case uint8:
- return etw.Uint8Field(k, v)
- case []uint8:
- return etw.Uint8Array(k, v)
- case uint16:
- return etw.Uint16Field(k, v)
- case []uint16:
- return etw.Uint16Array(k, v)
- case uint32:
- return etw.Uint32Field(k, v)
- case []uint32:
- return etw.Uint32Array(k, v)
- case uint64:
- return etw.Uint64Field(k, v)
- case []uint64:
- return etw.Uint64Array(k, v)
- case uintptr:
- return etw.UintptrField(k, v)
- case []uintptr:
- return etw.UintptrArray(k, v)
- case float32:
- return etw.Float32Field(k, v)
- case []float32:
- return etw.Float32Array(k, v)
- case float64:
- return etw.Float64Field(k, v)
- case []float64:
- return etw.Float64Array(k, v)
- case error:
- return etw.StringField(k, v.Error())
- default:
- switch rv := reflect.ValueOf(v); rv.Kind() {
- case reflect.Bool:
- return getFieldOpt(k, rv.Bool())
- case reflect.Int:
- return getFieldOpt(k, int(rv.Int()))
- case reflect.Int8:
- return getFieldOpt(k, int8(rv.Int()))
- case reflect.Int16:
- return getFieldOpt(k, int16(rv.Int()))
- case reflect.Int32:
- return getFieldOpt(k, int32(rv.Int()))
- case reflect.Int64:
- return getFieldOpt(k, int64(rv.Int()))
- case reflect.Uint:
- return getFieldOpt(k, uint(rv.Uint()))
- case reflect.Uint8:
- return getFieldOpt(k, uint8(rv.Uint()))
- case reflect.Uint16:
- return getFieldOpt(k, uint16(rv.Uint()))
- case reflect.Uint32:
- return getFieldOpt(k, uint32(rv.Uint()))
- case reflect.Uint64:
- return getFieldOpt(k, uint64(rv.Uint()))
- case reflect.Uintptr:
- return getFieldOpt(k, uintptr(rv.Uint()))
- case reflect.Float32:
- return getFieldOpt(k, float32(rv.Float()))
- case reflect.Float64:
- return getFieldOpt(k, float64(rv.Float()))
- case reflect.String:
- return getFieldOpt(k, rv.String())
- case reflect.Struct:
- fields := make([]etw.FieldOpt, 0, rv.NumField())
- for i := 0; i < rv.NumField(); i++ {
- field := rv.Field(i)
- if field.CanInterface() {
- fields = append(fields, getFieldOpt(k, field.Interface()))
- }
- }
- return etw.Struct(k, fields...)
- }
- }
-
- return etw.StringField(k, fmt.Sprintf("(Unsupported: %T) %v", v, v))
-}
-
-// Close cleans up the hook and closes the ETW provider.
-func (h *Hook) Close() error {
- return h.provider.Close()
-}
+package etwlogrus
+
+import (
+ "fmt"
+ "reflect"
+
+ "github.com/Microsoft/go-winio/internal/etw"
+ "github.com/sirupsen/logrus"
+)
+
+// Hook is a Logrus hook which logs received events to ETW.
+type Hook struct {
+ provider *etw.Provider
+}
+
+// NewHook registers a new ETW provider and returns a hook to log from it.
+func NewHook(providerName string) (*Hook, error) {
+ hook := Hook{}
+
+ provider, err := etw.NewProvider(providerName, nil)
+ if err != nil {
+ return nil, err
+ }
+ hook.provider = provider
+
+ return &hook, nil
+}
+
+// Levels returns the set of levels that this hook wants to receive log entries
+// for.
+func (h *Hook) Levels() []logrus.Level {
+ return []logrus.Level{
+ logrus.TraceLevel,
+ logrus.DebugLevel,
+ logrus.InfoLevel,
+ logrus.WarnLevel,
+ logrus.ErrorLevel,
+ logrus.FatalLevel,
+ logrus.PanicLevel,
+ }
+}
+
+// Fire receives each Logrus entry as it is logged, and logs it to ETW.
+func (h *Hook) Fire(e *logrus.Entry) error {
+ level := etw.Level(e.Level)
+ if !h.provider.IsEnabledForLevel(level) {
+ return nil
+ }
+
+ // Reserve extra space for the message field.
+ fields := make([]etw.FieldOpt, 0, len(e.Data)+1)
+
+ fields = append(fields, etw.StringField("Message", e.Message))
+
+ for k, v := range e.Data {
+ fields = append(fields, getFieldOpt(k, v))
+ }
+
+ // We could try to map Logrus levels to ETW levels, but we would lose some
+ // fidelity as there are fewer ETW levels. So instead we use the level
+ // directly.
+ return h.provider.WriteEvent(
+ "LogrusEntry",
+ etw.WithEventOpts(etw.WithLevel(level)),
+ fields)
+}
+
+// Currently, we support logging basic builtin types (int, string, etc), slices
+// of basic builtin types, error, types derived from the basic types (e.g. "type
+// foo int"), and structs (recursively logging their fields). We do not support
+// slices of derived types (e.g. "[]foo").
+//
+// For types that we don't support, the value is formatted via fmt.Sprint, and
+// we also log a message that the type is unsupported along with the formatted
+// type. The intent of this is to make it easier to see which types are not
+// supported in traces, so we can evaluate adding support for more types in the
+// future.
+func getFieldOpt(k string, v interface{}) etw.FieldOpt {
+ switch v := v.(type) {
+ case bool:
+ return etw.BoolField(k, v)
+ case []bool:
+ return etw.BoolArray(k, v)
+ case string:
+ return etw.StringField(k, v)
+ case []string:
+ return etw.StringArray(k, v)
+ case int:
+ return etw.IntField(k, v)
+ case []int:
+ return etw.IntArray(k, v)
+ case int8:
+ return etw.Int8Field(k, v)
+ case []int8:
+ return etw.Int8Array(k, v)
+ case int16:
+ return etw.Int16Field(k, v)
+ case []int16:
+ return etw.Int16Array(k, v)
+ case int32:
+ return etw.Int32Field(k, v)
+ case []int32:
+ return etw.Int32Array(k, v)
+ case int64:
+ return etw.Int64Field(k, v)
+ case []int64:
+ return etw.Int64Array(k, v)
+ case uint:
+ return etw.UintField(k, v)
+ case []uint:
+ return etw.UintArray(k, v)
+ case uint8:
+ return etw.Uint8Field(k, v)
+ case []uint8:
+ return etw.Uint8Array(k, v)
+ case uint16:
+ return etw.Uint16Field(k, v)
+ case []uint16:
+ return etw.Uint16Array(k, v)
+ case uint32:
+ return etw.Uint32Field(k, v)
+ case []uint32:
+ return etw.Uint32Array(k, v)
+ case uint64:
+ return etw.Uint64Field(k, v)
+ case []uint64:
+ return etw.Uint64Array(k, v)
+ case uintptr:
+ return etw.UintptrField(k, v)
+ case []uintptr:
+ return etw.UintptrArray(k, v)
+ case float32:
+ return etw.Float32Field(k, v)
+ case []float32:
+ return etw.Float32Array(k, v)
+ case float64:
+ return etw.Float64Field(k, v)
+ case []float64:
+ return etw.Float64Array(k, v)
+ case error:
+ return etw.StringField(k, v.Error())
+ default:
+ switch rv := reflect.ValueOf(v); rv.Kind() {
+ case reflect.Bool:
+ return getFieldOpt(k, rv.Bool())
+ case reflect.Int:
+ return getFieldOpt(k, int(rv.Int()))
+ case reflect.Int8:
+ return getFieldOpt(k, int8(rv.Int()))
+ case reflect.Int16:
+ return getFieldOpt(k, int16(rv.Int()))
+ case reflect.Int32:
+ return getFieldOpt(k, int32(rv.Int()))
+ case reflect.Int64:
+ return getFieldOpt(k, int64(rv.Int()))
+ case reflect.Uint:
+ return getFieldOpt(k, uint(rv.Uint()))
+ case reflect.Uint8:
+ return getFieldOpt(k, uint8(rv.Uint()))
+ case reflect.Uint16:
+ return getFieldOpt(k, uint16(rv.Uint()))
+ case reflect.Uint32:
+ return getFieldOpt(k, uint32(rv.Uint()))
+ case reflect.Uint64:
+ return getFieldOpt(k, uint64(rv.Uint()))
+ case reflect.Uintptr:
+ return getFieldOpt(k, uintptr(rv.Uint()))
+ case reflect.Float32:
+ return getFieldOpt(k, float32(rv.Float()))
+ case reflect.Float64:
+ return getFieldOpt(k, float64(rv.Float()))
+ case reflect.String:
+ return getFieldOpt(k, rv.String())
+ case reflect.Struct:
+ fields := make([]etw.FieldOpt, 0, rv.NumField())
+ for i := 0; i < rv.NumField(); i++ {
+ field := rv.Field(i)
+ if field.CanInterface() {
+ fields = append(fields, getFieldOpt(k, field.Interface()))
+ }
+ }
+ return etw.Struct(k, fields...)
+ }
+ }
+
+ return etw.StringField(k, fmt.Sprintf("(Unsupported: %T) %v", v, v))
+}
+
+// Close cleans up the hook and closes the ETW provider.
+func (h *Hook) Close() error {
+ return h.provider.Close()
+}
diff --git a/pkg/etwlogrus/hook_test.go b/pkg/etwlogrus/hook_test.go
index 5b1d07f8..3653cdc8 100644
--- a/pkg/etwlogrus/hook_test.go
+++ b/pkg/etwlogrus/hook_test.go
@@ -1,126 +1,126 @@
-package etwlogrus
-
-import (
- "github.com/Microsoft/go-winio/internal/etw"
- "testing"
-)
-
-func fireEvent(t *testing.T, p *etw.Provider, name string, value interface{}) {
- if err := p.WriteEvent(
- name,
- nil,
- etw.WithFields(getFieldOpt("Field", value))); err != nil {
-
- t.Fatal(err)
- }
-}
-
-// The purpose of this test is to log lots of different field types, to test the
-// logic that converts them to ETW. Because we don't have a way to
-// programatically validate the ETW events, this test has two main purposes: (1)
-// validate nothing causes a panic while logging (2) allow manual validation that
-// the data is logged correctly (through a tool like WPA).
-func TestFieldLogging(t *testing.T) {
- // Sample WPRP to collect this provider:
- //
- //
- //
- //
- //
- //
- //
- //
- //
- //
- //
- //
- //
- //
- //
- //
- //
- //
- //
- //
- //
- // Start collection:
- // wpr -start HookTest.wprp -filemode
- //
- // Stop collection:
- // wpr -stop HookTest.etl
- p, err := etw.NewProvider("HookTest", nil)
- if err != nil {
- t.Fatal(err)
- }
- defer func() {
- if err := p.Close(); err != nil {
- t.Fatal(err)
- }
- }()
-
- fireEvent(t, p, "Bool", true)
- fireEvent(t, p, "BoolSlice", []bool{true, false, true})
- fireEvent(t, p, "EmptyBoolSlice", []bool{})
- fireEvent(t, p, "String", "teststring")
- fireEvent(t, p, "StringSlice", []string{"sstr1", "sstr2", "sstr3"})
- fireEvent(t, p, "EmptyStringSlice", []string{})
- fireEvent(t, p, "Int", int(1))
- fireEvent(t, p, "IntSlice", []int{2, 3, 4})
- fireEvent(t, p, "EmptyIntSlice", []int{})
- fireEvent(t, p, "Int8", int8(5))
- fireEvent(t, p, "Int8Slice", []int8{6, 7, 8})
- fireEvent(t, p, "EmptyInt8Slice", []int8{})
- fireEvent(t, p, "Int16", int16(9))
- fireEvent(t, p, "Int16Slice", []int16{10, 11, 12})
- fireEvent(t, p, "EmptyInt16Slice", []int16{})
- fireEvent(t, p, "Int32", int32(13))
- fireEvent(t, p, "Int32Slice", []int32{14, 15, 16})
- fireEvent(t, p, "EmptyInt32Slice", []int32{})
- fireEvent(t, p, "Int64", int64(17))
- fireEvent(t, p, "Int64Slice", []int64{18, 19, 20})
- fireEvent(t, p, "EmptyInt64Slice", []int64{})
- fireEvent(t, p, "Uint", uint(21))
- fireEvent(t, p, "UintSlice", []uint{22, 23, 24})
- fireEvent(t, p, "EmptyUintSlice", []uint{})
- fireEvent(t, p, "Uint8", uint8(25))
- fireEvent(t, p, "Uint8Slice", []uint8{26, 27, 28})
- fireEvent(t, p, "EmptyUint8Slice", []uint8{})
- fireEvent(t, p, "Uint16", uint16(29))
- fireEvent(t, p, "Uint16Slice", []uint16{30, 31, 32})
- fireEvent(t, p, "EmptyUint16Slice", []uint16{})
- fireEvent(t, p, "Uint32", uint32(33))
- fireEvent(t, p, "Uint32Slice", []uint32{34, 35, 36})
- fireEvent(t, p, "EmptyUint32Slice", []uint32{})
- fireEvent(t, p, "Uint64", uint64(37))
- fireEvent(t, p, "Uint64Slice", []uint64{38, 39, 40})
- fireEvent(t, p, "EmptyUint64Slice", []uint64{})
- fireEvent(t, p, "Uintptr", uintptr(41))
- fireEvent(t, p, "UintptrSlice", []uintptr{42, 43, 44})
- fireEvent(t, p, "EmptyUintptrSlice", []uintptr{})
- fireEvent(t, p, "Float32", float32(45.46))
- fireEvent(t, p, "Float32Slice", []float32{47.48, 49.50, 51.52})
- fireEvent(t, p, "EmptyFloat32Slice", []float32{})
- fireEvent(t, p, "Float64", float64(53.54))
- fireEvent(t, p, "Float64Slice", []float64{55.56, 57.58, 59.60})
- fireEvent(t, p, "EmptyFloat64Slice", []float64{})
-
- type struct1 struct {
- A float32
- priv int
- B []uint
- }
- type struct2 struct {
- A int
- B int
- }
- type struct3 struct {
- struct2
- A int
- B string
- priv string
- C struct1
- D uint16
- }
- // Unexported fields, and fields in embedded structs, should not log.
- fireEvent(t, p, "Struct", struct3{struct2{-1, -2}, 1, "2s", "-3s", struct1{3.4, -4, []uint{5, 6, 7}}, 8})
-}
+package etwlogrus
+
+import (
+ "github.com/Microsoft/go-winio/internal/etw"
+ "testing"
+)
+
+func fireEvent(t *testing.T, p *etw.Provider, name string, value interface{}) {
+ if err := p.WriteEvent(
+ name,
+ nil,
+ etw.WithFields(getFieldOpt("Field", value))); err != nil {
+
+ t.Fatal(err)
+ }
+}
+
+// The purpose of this test is to log lots of different field types, to test the
+// logic that converts them to ETW. Because we don't have a way to
+// programatically validate the ETW events, this test has two main purposes: (1)
+// validate nothing causes a panic while logging (2) allow manual validation that
+// the data is logged correctly (through a tool like WPA).
+func TestFieldLogging(t *testing.T) {
+ // Sample WPRP to collect this provider:
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ // Start collection:
+ // wpr -start HookTest.wprp -filemode
+ //
+ // Stop collection:
+ // wpr -stop HookTest.etl
+ p, err := etw.NewProvider("HookTest", nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer func() {
+ if err := p.Close(); err != nil {
+ t.Fatal(err)
+ }
+ }()
+
+ fireEvent(t, p, "Bool", true)
+ fireEvent(t, p, "BoolSlice", []bool{true, false, true})
+ fireEvent(t, p, "EmptyBoolSlice", []bool{})
+ fireEvent(t, p, "String", "teststring")
+ fireEvent(t, p, "StringSlice", []string{"sstr1", "sstr2", "sstr3"})
+ fireEvent(t, p, "EmptyStringSlice", []string{})
+ fireEvent(t, p, "Int", int(1))
+ fireEvent(t, p, "IntSlice", []int{2, 3, 4})
+ fireEvent(t, p, "EmptyIntSlice", []int{})
+ fireEvent(t, p, "Int8", int8(5))
+ fireEvent(t, p, "Int8Slice", []int8{6, 7, 8})
+ fireEvent(t, p, "EmptyInt8Slice", []int8{})
+ fireEvent(t, p, "Int16", int16(9))
+ fireEvent(t, p, "Int16Slice", []int16{10, 11, 12})
+ fireEvent(t, p, "EmptyInt16Slice", []int16{})
+ fireEvent(t, p, "Int32", int32(13))
+ fireEvent(t, p, "Int32Slice", []int32{14, 15, 16})
+ fireEvent(t, p, "EmptyInt32Slice", []int32{})
+ fireEvent(t, p, "Int64", int64(17))
+ fireEvent(t, p, "Int64Slice", []int64{18, 19, 20})
+ fireEvent(t, p, "EmptyInt64Slice", []int64{})
+ fireEvent(t, p, "Uint", uint(21))
+ fireEvent(t, p, "UintSlice", []uint{22, 23, 24})
+ fireEvent(t, p, "EmptyUintSlice", []uint{})
+ fireEvent(t, p, "Uint8", uint8(25))
+ fireEvent(t, p, "Uint8Slice", []uint8{26, 27, 28})
+ fireEvent(t, p, "EmptyUint8Slice", []uint8{})
+ fireEvent(t, p, "Uint16", uint16(29))
+ fireEvent(t, p, "Uint16Slice", []uint16{30, 31, 32})
+ fireEvent(t, p, "EmptyUint16Slice", []uint16{})
+ fireEvent(t, p, "Uint32", uint32(33))
+ fireEvent(t, p, "Uint32Slice", []uint32{34, 35, 36})
+ fireEvent(t, p, "EmptyUint32Slice", []uint32{})
+ fireEvent(t, p, "Uint64", uint64(37))
+ fireEvent(t, p, "Uint64Slice", []uint64{38, 39, 40})
+ fireEvent(t, p, "EmptyUint64Slice", []uint64{})
+ fireEvent(t, p, "Uintptr", uintptr(41))
+ fireEvent(t, p, "UintptrSlice", []uintptr{42, 43, 44})
+ fireEvent(t, p, "EmptyUintptrSlice", []uintptr{})
+ fireEvent(t, p, "Float32", float32(45.46))
+ fireEvent(t, p, "Float32Slice", []float32{47.48, 49.50, 51.52})
+ fireEvent(t, p, "EmptyFloat32Slice", []float32{})
+ fireEvent(t, p, "Float64", float64(53.54))
+ fireEvent(t, p, "Float64Slice", []float64{55.56, 57.58, 59.60})
+ fireEvent(t, p, "EmptyFloat64Slice", []float64{})
+
+ type struct1 struct {
+ A float32
+ priv int
+ B []uint
+ }
+ type struct2 struct {
+ A int
+ B int
+ }
+ type struct3 struct {
+ struct2
+ A int
+ B string
+ priv string
+ C struct1
+ D uint16
+ }
+ // Unexported fields, and fields in embedded structs, should not log.
+ fireEvent(t, p, "Struct", struct3{struct2{-1, -2}, 1, "2s", "-3s", struct1{3.4, -4, []uint{5, 6, 7}}, 8})
+}
diff --git a/tools/etw-provider-gen/main.go b/tools/etw-provider-gen/main.go
index c6d95cf7..96b76aab 100644
--- a/tools/etw-provider-gen/main.go
+++ b/tools/etw-provider-gen/main.go
@@ -1,25 +1,25 @@
-package main
-
-import (
- "flag"
- "fmt"
- "os"
-
- "github.com/Microsoft/go-winio/internal/etw"
-)
-
-func main() {
- var pn = flag.String("provider-name", "", "The human readable ETW provider name to be converted into GUID format")
- flag.Parse()
- if pn == nil || *pn == "" {
- fmt.Fprint(os.Stderr, "--provider-name is required")
- os.Exit(1)
- }
- p, err := etw.NewProvider(*pn, nil)
- if err != nil {
- fmt.Fprintf(os.Stderr, "failed to convert provider-name: '%s' with err: '%s", *pn, err)
- os.Exit(1)
- }
- defer p.Close()
- fmt.Fprintf(os.Stdout, "%s", p)
-}
+package main
+
+import (
+ "flag"
+ "fmt"
+ "os"
+
+ "github.com/Microsoft/go-winio/internal/etw"
+)
+
+func main() {
+ var pn = flag.String("provider-name", "", "The human readable ETW provider name to be converted into GUID format")
+ flag.Parse()
+ if pn == nil || *pn == "" {
+ fmt.Fprint(os.Stderr, "--provider-name is required")
+ os.Exit(1)
+ }
+ p, err := etw.NewProvider(*pn, nil)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "failed to convert provider-name: '%s' with err: '%s", *pn, err)
+ os.Exit(1)
+ }
+ defer p.Close()
+ fmt.Fprintf(os.Stdout, "%s", p)
+}