Convert to UNix line endings

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