Signed-off-by: John Howard <jhoward@microsoft.com>
| ... | ... |
@@ -1,7 +1,7 @@ |
| 1 | 1 |
# the following lines are in sorted order, FYI |
| 2 | 2 |
github.com/Azure/go-ansiterm d6e3b3328b783f23731bc4d058875b0371ff8109 |
| 3 | 3 |
github.com/Microsoft/hcsshim ada9cb39f715fb568e1030e7613732bb4f1e4aeb |
| 4 |
-github.com/Microsoft/go-winio 4de24ed3e8c509e6d1f609a8cb6b1c9fd9816e6d |
|
| 4 |
+github.com/Microsoft/go-winio c599b533b43b1363d7d7c6cfda5ede70ed73ff13 |
|
| 5 | 5 |
github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a |
| 6 | 6 |
github.com/go-check/check 4ed411733c5785b40214c70bce814c3a3a689609 https://github.com/cpuguy83/check.git |
| 7 | 7 |
github.com/golang/gddo 9b12a26f3fbd7397dee4e20939ddca719d840d2a |
| 8 | 8 |
deleted file mode 100644 |
| ... | ... |
@@ -1,15 +0,0 @@ |
| 1 |
-// Package etw provides support for TraceLogging-based ETW (Event Tracing |
|
| 2 |
-// for Windows). TraceLogging is a format of ETW events that are self-describing |
|
| 3 |
-// (the event contains information on its own schema). This allows them to be |
|
| 4 |
-// decoded without needing a separate manifest with event information. The |
|
| 5 |
-// implementation here is based on the information found in |
|
| 6 |
-// TraceLoggingProvider.h in the Windows SDK, which implements TraceLogging as a |
|
| 7 |
-// set of C macros. |
|
| 8 |
-package etw |
|
| 9 |
- |
|
| 10 |
-//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go etw.go |
|
| 11 |
- |
|
| 12 |
-//sys eventRegister(providerId *windows.GUID, callback uintptr, callbackContext uintptr, providerHandle *providerHandle) (win32err error) = advapi32.EventRegister |
|
| 13 |
-//sys eventUnregister(providerHandle providerHandle) (win32err error) = advapi32.EventUnregister |
|
| 14 |
-//sys eventWriteTransfer(providerHandle providerHandle, descriptor *EventDescriptor, activityID *windows.GUID, relatedActivityID *windows.GUID, dataDescriptorCount uint32, dataDescriptors *eventDataDescriptor) (win32err error) = advapi32.EventWriteTransfer |
|
| 15 |
-//sys eventSetInformation(providerHandle providerHandle, class eventInfoClass, information uintptr, length uint32) (win32err error) = advapi32.EventSetInformation |
| 16 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,65 +0,0 @@ |
| 1 |
-package etw |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "bytes" |
|
| 5 |
- "encoding/binary" |
|
| 6 |
-) |
|
| 7 |
- |
|
| 8 |
-// EventData maintains a buffer which builds up the data for an ETW event. It |
|
| 9 |
-// needs to be paired with EventMetadata which describes the event. |
|
| 10 |
-type EventData struct {
|
|
| 11 |
- buffer bytes.Buffer |
|
| 12 |
-} |
|
| 13 |
- |
|
| 14 |
-// Bytes returns the raw binary data containing the event data. The returned |
|
| 15 |
-// value is not copied from the internal buffer, so it can be mutated by the |
|
| 16 |
-// EventData object after it is returned. |
|
| 17 |
-func (ed *EventData) Bytes() []byte {
|
|
| 18 |
- return ed.buffer.Bytes() |
|
| 19 |
-} |
|
| 20 |
- |
|
| 21 |
-// WriteString appends a string, including the null terminator, to the buffer. |
|
| 22 |
-func (ed *EventData) WriteString(data string) {
|
|
| 23 |
- ed.buffer.WriteString(data) |
|
| 24 |
- ed.buffer.WriteByte(0) |
|
| 25 |
-} |
|
| 26 |
- |
|
| 27 |
-// WriteInt8 appends a int8 to the buffer. |
|
| 28 |
-func (ed *EventData) WriteInt8(value int8) {
|
|
| 29 |
- ed.buffer.WriteByte(uint8(value)) |
|
| 30 |
-} |
|
| 31 |
- |
|
| 32 |
-// WriteInt16 appends a int16 to the buffer. |
|
| 33 |
-func (ed *EventData) WriteInt16(value int16) {
|
|
| 34 |
- binary.Write(&ed.buffer, binary.LittleEndian, value) |
|
| 35 |
-} |
|
| 36 |
- |
|
| 37 |
-// WriteInt32 appends a int32 to the buffer. |
|
| 38 |
-func (ed *EventData) WriteInt32(value int32) {
|
|
| 39 |
- binary.Write(&ed.buffer, binary.LittleEndian, value) |
|
| 40 |
-} |
|
| 41 |
- |
|
| 42 |
-// WriteInt64 appends a int64 to the buffer. |
|
| 43 |
-func (ed *EventData) WriteInt64(value int64) {
|
|
| 44 |
- binary.Write(&ed.buffer, binary.LittleEndian, value) |
|
| 45 |
-} |
|
| 46 |
- |
|
| 47 |
-// WriteUint8 appends a uint8 to the buffer. |
|
| 48 |
-func (ed *EventData) WriteUint8(value uint8) {
|
|
| 49 |
- ed.buffer.WriteByte(value) |
|
| 50 |
-} |
|
| 51 |
- |
|
| 52 |
-// WriteUint16 appends a uint16 to the buffer. |
|
| 53 |
-func (ed *EventData) WriteUint16(value uint16) {
|
|
| 54 |
- binary.Write(&ed.buffer, binary.LittleEndian, value) |
|
| 55 |
-} |
|
| 56 |
- |
|
| 57 |
-// WriteUint32 appends a uint32 to the buffer. |
|
| 58 |
-func (ed *EventData) WriteUint32(value uint32) {
|
|
| 59 |
- binary.Write(&ed.buffer, binary.LittleEndian, value) |
|
| 60 |
-} |
|
| 61 |
- |
|
| 62 |
-// WriteUint64 appends a uint64 to the buffer. |
|
| 63 |
-func (ed *EventData) WriteUint64(value uint64) {
|
|
| 64 |
- binary.Write(&ed.buffer, binary.LittleEndian, value) |
|
| 65 |
-} |
| 66 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,29 +0,0 @@ |
| 1 |
-package etw |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "unsafe" |
|
| 5 |
-) |
|
| 6 |
- |
|
| 7 |
-type eventDataDescriptorType uint8 |
|
| 8 |
- |
|
| 9 |
-const ( |
|
| 10 |
- eventDataDescriptorTypeUserData eventDataDescriptorType = iota |
|
| 11 |
- eventDataDescriptorTypeEventMetadata |
|
| 12 |
- eventDataDescriptorTypeProviderMetadata |
|
| 13 |
-) |
|
| 14 |
- |
|
| 15 |
-type eventDataDescriptor struct {
|
|
| 16 |
- ptr ptr64 |
|
| 17 |
- size uint32 |
|
| 18 |
- dataType eventDataDescriptorType |
|
| 19 |
- reserved1 uint8 |
|
| 20 |
- reserved2 uint16 |
|
| 21 |
-} |
|
| 22 |
- |
|
| 23 |
-func newEventDataDescriptor(dataType eventDataDescriptorType, buffer []byte) eventDataDescriptor {
|
|
| 24 |
- return eventDataDescriptor{
|
|
| 25 |
- ptr: ptr64{ptr: unsafe.Pointer(&buffer[0])},
|
|
| 26 |
- size: uint32(len(buffer)), |
|
| 27 |
- dataType: dataType, |
|
| 28 |
- } |
|
| 29 |
-} |
| 30 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,67 +0,0 @@ |
| 1 |
-package etw |
|
| 2 |
- |
|
| 3 |
-// Channel represents the ETW logging channel that is used. It can be used by |
|
| 4 |
-// event consumers to give an event special treatment. |
|
| 5 |
-type Channel uint8 |
|
| 6 |
- |
|
| 7 |
-const ( |
|
| 8 |
- // ChannelTraceLogging is the default channel for TraceLogging events. It is |
|
| 9 |
- // not required to be used for TraceLogging, but will prevent decoding |
|
| 10 |
- // issues for these events on older operating systems. |
|
| 11 |
- ChannelTraceLogging Channel = 11 |
|
| 12 |
-) |
|
| 13 |
- |
|
| 14 |
-// Level represents the ETW logging level. There are several predefined levels |
|
| 15 |
-// that are commonly used, but technically anything from 0-255 is allowed. |
|
| 16 |
-// Lower levels indicate more important events, and 0 indicates an event that |
|
| 17 |
-// will always be collected. |
|
| 18 |
-type Level uint8 |
|
| 19 |
- |
|
| 20 |
-// Predefined ETW log levels. |
|
| 21 |
-const ( |
|
| 22 |
- LevelAlways Level = iota |
|
| 23 |
- LevelCritical |
|
| 24 |
- LevelError |
|
| 25 |
- LevelWarning |
|
| 26 |
- LevelInfo |
|
| 27 |
- LevelVerbose |
|
| 28 |
-) |
|
| 29 |
- |
|
| 30 |
-// EventDescriptor represents various metadata for an ETW event. |
|
| 31 |
-type EventDescriptor struct {
|
|
| 32 |
- id uint16 |
|
| 33 |
- version uint8 |
|
| 34 |
- Channel Channel |
|
| 35 |
- Level Level |
|
| 36 |
- Opcode uint8 |
|
| 37 |
- Task uint16 |
|
| 38 |
- Keyword uint64 |
|
| 39 |
-} |
|
| 40 |
- |
|
| 41 |
-// NewEventDescriptor returns an EventDescriptor initialized for use with |
|
| 42 |
-// TraceLogging. |
|
| 43 |
-func NewEventDescriptor() *EventDescriptor {
|
|
| 44 |
- // Standard TraceLogging events default to the TraceLogging channel, and |
|
| 45 |
- // verbose level. |
|
| 46 |
- return &EventDescriptor{
|
|
| 47 |
- Channel: ChannelTraceLogging, |
|
| 48 |
- Level: LevelVerbose, |
|
| 49 |
- } |
|
| 50 |
-} |
|
| 51 |
- |
|
| 52 |
-// Identity returns the identity of the event. If the identity is not 0, it |
|
| 53 |
-// should uniquely identify the other event metadata (contained in |
|
| 54 |
-// EventDescriptor, and field metadata). Only the lower 24 bits of this value |
|
| 55 |
-// are relevant. |
|
| 56 |
-func (ed *EventDescriptor) Identity() uint32 {
|
|
| 57 |
- return (uint32(ed.version) << 16) | uint32(ed.id) |
|
| 58 |
-} |
|
| 59 |
- |
|
| 60 |
-// SetIdentity sets the identity of the event. If the identity is not 0, it |
|
| 61 |
-// should uniquely identify the other event metadata (contained in |
|
| 62 |
-// EventDescriptor, and field metadata). Only the lower 24 bits of this value |
|
| 63 |
-// are relevant. |
|
| 64 |
-func (ed *EventDescriptor) SetIdentity(identity uint32) {
|
|
| 65 |
- ed.id = uint16(identity) |
|
| 66 |
- ed.version = uint8(identity >> 16) |
|
| 67 |
-} |
| 68 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,177 +0,0 @@ |
| 1 |
-package etw |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "bytes" |
|
| 5 |
- "encoding/binary" |
|
| 6 |
-) |
|
| 7 |
- |
|
| 8 |
-// InType indicates the type of data contained in the ETW event. |
|
| 9 |
-type InType byte |
|
| 10 |
- |
|
| 11 |
-// Various InType definitions for TraceLogging. These must match the definitions |
|
| 12 |
-// found in TraceLoggingProvider.h in the Windows SDK. |
|
| 13 |
-const ( |
|
| 14 |
- InTypeNull InType = iota |
|
| 15 |
- InTypeUnicodeString |
|
| 16 |
- InTypeANSIString |
|
| 17 |
- InTypeInt8 |
|
| 18 |
- InTypeUint8 |
|
| 19 |
- InTypeInt16 |
|
| 20 |
- InTypeUint16 |
|
| 21 |
- InTypeInt32 |
|
| 22 |
- InTypeUint32 |
|
| 23 |
- InTypeInt64 |
|
| 24 |
- InTypeUint64 |
|
| 25 |
- InTypeFloat |
|
| 26 |
- InTypeDouble |
|
| 27 |
- InTypeBool32 |
|
| 28 |
- InTypeBinary |
|
| 29 |
- InTypeGUID |
|
| 30 |
- InTypePointerUnsupported |
|
| 31 |
- InTypeFileTime |
|
| 32 |
- InTypeSystemTime |
|
| 33 |
- InTypeSID |
|
| 34 |
- InTypeHexInt32 |
|
| 35 |
- InTypeHexInt64 |
|
| 36 |
- InTypeCountedString |
|
| 37 |
- InTypeCountedANSIString |
|
| 38 |
- InTypeStruct |
|
| 39 |
- InTypeCountedBinary |
|
| 40 |
- InTypeCountedArray InType = 32 |
|
| 41 |
- InTypeArray InType = 64 |
|
| 42 |
-) |
|
| 43 |
- |
|
| 44 |
-// OutType specifies a hint to the event decoder for how the value should be |
|
| 45 |
-// formatted. |
|
| 46 |
-type OutType byte |
|
| 47 |
- |
|
| 48 |
-// Various OutType definitions for TraceLogging. These must match the |
|
| 49 |
-// definitions found in TraceLoggingProvider.h in the Windows SDK. |
|
| 50 |
-const ( |
|
| 51 |
- // OutTypeDefault indicates that the default formatting for the InType will |
|
| 52 |
- // be used by the event decoder. |
|
| 53 |
- OutTypeDefault OutType = iota |
|
| 54 |
- OutTypeNoPrint |
|
| 55 |
- OutTypeString |
|
| 56 |
- OutTypeBoolean |
|
| 57 |
- OutTypeHex |
|
| 58 |
- OutTypePID |
|
| 59 |
- OutTypeTID |
|
| 60 |
- OutTypePort |
|
| 61 |
- OutTypeIPv4 |
|
| 62 |
- OutTypeIPv6 |
|
| 63 |
- OutTypeSocketAddress |
|
| 64 |
- OutTypeXML |
|
| 65 |
- OutTypeJSON |
|
| 66 |
- OutTypeWin32Error |
|
| 67 |
- OutTypeNTStatus |
|
| 68 |
- OutTypeHResult |
|
| 69 |
- OutTypeFileTime |
|
| 70 |
- OutTypeSigned |
|
| 71 |
- OutTypeUnsigned |
|
| 72 |
- OutTypeUTF8 OutType = 35 |
|
| 73 |
- OutTypePKCS7WithTypeInfo OutType = 36 |
|
| 74 |
- OutTypeCodePointer OutType = 37 |
|
| 75 |
- OutTypeDateTimeUTC OutType = 38 |
|
| 76 |
-) |
|
| 77 |
- |
|
| 78 |
-// EventMetadata maintains a buffer which builds up the metadata for an ETW |
|
| 79 |
-// event. It needs to be paired with EventData which describes the event. |
|
| 80 |
-type EventMetadata struct {
|
|
| 81 |
- buffer bytes.Buffer |
|
| 82 |
-} |
|
| 83 |
- |
|
| 84 |
-// Bytes returns the raw binary data containing the event metadata. Before being |
|
| 85 |
-// returned, the current size of the buffer is written to the start of the |
|
| 86 |
-// buffer. The returned value is not copied from the internal buffer, so it can |
|
| 87 |
-// be mutated by the EventMetadata object after it is returned. |
|
| 88 |
-func (em *EventMetadata) Bytes() []byte {
|
|
| 89 |
- // Finalize the event metadata buffer by filling in the buffer length at the |
|
| 90 |
- // beginning. |
|
| 91 |
- binary.LittleEndian.PutUint16(em.buffer.Bytes(), uint16(em.buffer.Len())) |
|
| 92 |
- return em.buffer.Bytes() |
|
| 93 |
-} |
|
| 94 |
- |
|
| 95 |
-// WriteEventHeader writes the metadata for the start of an event to the buffer. |
|
| 96 |
-// This specifies the event name and tags. |
|
| 97 |
-func (em *EventMetadata) WriteEventHeader(name string, tags uint32) {
|
|
| 98 |
- binary.Write(&em.buffer, binary.LittleEndian, uint16(0)) // Length placeholder |
|
| 99 |
- em.writeTags(tags) |
|
| 100 |
- em.buffer.WriteString(name) |
|
| 101 |
- em.buffer.WriteByte(0) // Null terminator for name |
|
| 102 |
-} |
|
| 103 |
- |
|
| 104 |
-func (em *EventMetadata) writeField(name string, inType InType, outType OutType, tags uint32, arrSize uint16) {
|
|
| 105 |
- em.buffer.WriteString(name) |
|
| 106 |
- em.buffer.WriteByte(0) // Null terminator for name |
|
| 107 |
- |
|
| 108 |
- if outType == OutTypeDefault && tags == 0 {
|
|
| 109 |
- em.buffer.WriteByte(byte(inType)) |
|
| 110 |
- } else {
|
|
| 111 |
- em.buffer.WriteByte(byte(inType | 128)) |
|
| 112 |
- if tags == 0 {
|
|
| 113 |
- em.buffer.WriteByte(byte(outType)) |
|
| 114 |
- } else {
|
|
| 115 |
- em.buffer.WriteByte(byte(outType | 128)) |
|
| 116 |
- em.writeTags(tags) |
|
| 117 |
- } |
|
| 118 |
- } |
|
| 119 |
- |
|
| 120 |
- if arrSize != 0 {
|
|
| 121 |
- binary.Write(&em.buffer, binary.LittleEndian, arrSize) |
|
| 122 |
- } |
|
| 123 |
-} |
|
| 124 |
- |
|
| 125 |
-// writeTags writes out the tags value to the event metadata. Tags is a 28-bit |
|
| 126 |
-// value, interpreted as bit flags, which are only relevant to the event |
|
| 127 |
-// consumer. The event consumer may choose to attribute special meaning to tags |
|
| 128 |
-// (e.g. 0x4 could mean the field contains PII). Tags are written as a series of |
|
| 129 |
-// bytes, each containing 7 bits of tag value, with the high bit set if there is |
|
| 130 |
-// more tag data in the following byte. This allows for a more compact |
|
| 131 |
-// representation when not all of the tag bits are needed. |
|
| 132 |
-func (em *EventMetadata) writeTags(tags uint32) {
|
|
| 133 |
- // Only use the top 28 bits of the tags value. |
|
| 134 |
- tags &= 0xfffffff |
|
| 135 |
- |
|
| 136 |
- for {
|
|
| 137 |
- // Tags are written with the most significant bits (e.g. 21-27) first. |
|
| 138 |
- val := tags >> 21 |
|
| 139 |
- |
|
| 140 |
- if tags&0x1fffff == 0 {
|
|
| 141 |
- // If there is no more data to write after this, write this value |
|
| 142 |
- // without the high bit set, and return. |
|
| 143 |
- em.buffer.WriteByte(byte(val & 0x7f)) |
|
| 144 |
- return |
|
| 145 |
- } |
|
| 146 |
- |
|
| 147 |
- em.buffer.WriteByte(byte(val | 0x80)) |
|
| 148 |
- |
|
| 149 |
- tags <<= 7 |
|
| 150 |
- } |
|
| 151 |
-} |
|
| 152 |
- |
|
| 153 |
-// WriteField writes the metadata for a simple field to the buffer. |
|
| 154 |
-func (em *EventMetadata) WriteField(name string, inType InType, outType OutType, tags uint32) {
|
|
| 155 |
- em.writeField(name, inType, outType, tags, 0) |
|
| 156 |
-} |
|
| 157 |
- |
|
| 158 |
-// WriteArray writes the metadata for an array field to the buffer. The number |
|
| 159 |
-// of elements in the array must be written as a uint16 in the event data, |
|
| 160 |
-// immediately preceeding the event data. |
|
| 161 |
-func (em *EventMetadata) WriteArray(name string, inType InType, outType OutType, tags uint32) {
|
|
| 162 |
- em.writeField(name, inType|InTypeArray, outType, tags, 0) |
|
| 163 |
-} |
|
| 164 |
- |
|
| 165 |
-// WriteCountedArray writes the metadata for an array field to the buffer. The |
|
| 166 |
-// size of a counted array is fixed, and the size is written into the metadata |
|
| 167 |
-// directly. |
|
| 168 |
-func (em *EventMetadata) WriteCountedArray(name string, count uint16, inType InType, outType OutType, tags uint32) {
|
|
| 169 |
- em.writeField(name, inType|InTypeCountedArray, outType, tags, count) |
|
| 170 |
-} |
|
| 171 |
- |
|
| 172 |
-// WriteStruct writes the metadata for a nested struct to the buffer. The struct |
|
| 173 |
-// contains the next N fields in the metadata, where N is specified by the |
|
| 174 |
-// fieldCount argument. |
|
| 175 |
-func (em *EventMetadata) WriteStruct(name string, fieldCount uint8, tags uint32) {
|
|
| 176 |
- em.writeField(name, InTypeStruct, OutType(fieldCount), tags, 0) |
|
| 177 |
-} |
| 178 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,63 +0,0 @@ |
| 1 |
-package etw |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "golang.org/x/sys/windows" |
|
| 5 |
-) |
|
| 6 |
- |
|
| 7 |
-type eventOptions struct {
|
|
| 8 |
- descriptor *EventDescriptor |
|
| 9 |
- activityID *windows.GUID |
|
| 10 |
- relatedActivityID *windows.GUID |
|
| 11 |
- tags uint32 |
|
| 12 |
-} |
|
| 13 |
- |
|
| 14 |
-// EventOpt defines the option function type that can be passed to |
|
| 15 |
-// Provider.WriteEvent to specify general event options, such as level and |
|
| 16 |
-// keyword. |
|
| 17 |
-type EventOpt func(options *eventOptions) |
|
| 18 |
- |
|
| 19 |
-// WithEventOpts returns the variadic arguments as a single slice. |
|
| 20 |
-func WithEventOpts(opts ...EventOpt) []EventOpt {
|
|
| 21 |
- return opts |
|
| 22 |
-} |
|
| 23 |
- |
|
| 24 |
-// WithLevel specifies the level of the event to be written. |
|
| 25 |
-func WithLevel(level Level) EventOpt {
|
|
| 26 |
- return func(options *eventOptions) {
|
|
| 27 |
- options.descriptor.Level = level |
|
| 28 |
- } |
|
| 29 |
-} |
|
| 30 |
- |
|
| 31 |
-// WithKeyword specifies the keywords of the event to be written. Multiple uses |
|
| 32 |
-// of this option are OR'd together. |
|
| 33 |
-func WithKeyword(keyword uint64) EventOpt {
|
|
| 34 |
- return func(options *eventOptions) {
|
|
| 35 |
- options.descriptor.Keyword |= keyword |
|
| 36 |
- } |
|
| 37 |
-} |
|
| 38 |
- |
|
| 39 |
-func WithChannel(channel Channel) EventOpt {
|
|
| 40 |
- return func(options *eventOptions) {
|
|
| 41 |
- options.descriptor.Channel = channel |
|
| 42 |
- } |
|
| 43 |
-} |
|
| 44 |
- |
|
| 45 |
-// WithTags specifies the tags of the event to be written. Tags is a 28-bit |
|
| 46 |
-// value (top 4 bits are ignored) which are interpreted by the event consumer. |
|
| 47 |
-func WithTags(newTags uint32) EventOpt {
|
|
| 48 |
- return func(options *eventOptions) {
|
|
| 49 |
- options.tags |= newTags |
|
| 50 |
- } |
|
| 51 |
-} |
|
| 52 |
- |
|
| 53 |
-func WithActivityID(activityID *windows.GUID) EventOpt {
|
|
| 54 |
- return func(options *eventOptions) {
|
|
| 55 |
- options.activityID = activityID |
|
| 56 |
- } |
|
| 57 |
-} |
|
| 58 |
- |
|
| 59 |
-func WithRelatedActivityID(activityID *windows.GUID) EventOpt {
|
|
| 60 |
- return func(options *eventOptions) {
|
|
| 61 |
- options.relatedActivityID = activityID |
|
| 62 |
- } |
|
| 63 |
-} |
| 64 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,379 +0,0 @@ |
| 1 |
-package etw |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "math" |
|
| 5 |
- "unsafe" |
|
| 6 |
-) |
|
| 7 |
- |
|
| 8 |
-// FieldOpt defines the option function type that can be passed to |
|
| 9 |
-// Provider.WriteEvent to add fields to the event. |
|
| 10 |
-type FieldOpt func(em *EventMetadata, ed *EventData) |
|
| 11 |
- |
|
| 12 |
-// WithFields returns the variadic arguments as a single slice. |
|
| 13 |
-func WithFields(opts ...FieldOpt) []FieldOpt {
|
|
| 14 |
- return opts |
|
| 15 |
-} |
|
| 16 |
- |
|
| 17 |
-// BoolField adds a single bool field to the event. |
|
| 18 |
-func BoolField(name string, value bool) FieldOpt {
|
|
| 19 |
- return func(em *EventMetadata, ed *EventData) {
|
|
| 20 |
- em.WriteField(name, InTypeUint8, OutTypeBoolean, 0) |
|
| 21 |
- bool8 := uint8(0) |
|
| 22 |
- if value {
|
|
| 23 |
- bool8 = uint8(1) |
|
| 24 |
- } |
|
| 25 |
- ed.WriteUint8(bool8) |
|
| 26 |
- } |
|
| 27 |
-} |
|
| 28 |
- |
|
| 29 |
-// BoolArray adds an array of bool to the event. |
|
| 30 |
-func BoolArray(name string, values []bool) FieldOpt {
|
|
| 31 |
- return func(em *EventMetadata, ed *EventData) {
|
|
| 32 |
- em.WriteArray(name, InTypeUint8, OutTypeBoolean, 0) |
|
| 33 |
- ed.WriteUint16(uint16(len(values))) |
|
| 34 |
- for _, v := range values {
|
|
| 35 |
- bool8 := uint8(0) |
|
| 36 |
- if v {
|
|
| 37 |
- bool8 = uint8(1) |
|
| 38 |
- } |
|
| 39 |
- ed.WriteUint8(bool8) |
|
| 40 |
- } |
|
| 41 |
- } |
|
| 42 |
-} |
|
| 43 |
- |
|
| 44 |
-// StringField adds a single string field to the event. |
|
| 45 |
-func StringField(name string, value string) FieldOpt {
|
|
| 46 |
- return func(em *EventMetadata, ed *EventData) {
|
|
| 47 |
- em.WriteField(name, InTypeANSIString, OutTypeUTF8, 0) |
|
| 48 |
- ed.WriteString(value) |
|
| 49 |
- } |
|
| 50 |
-} |
|
| 51 |
- |
|
| 52 |
-// StringArray adds an array of string to the event. |
|
| 53 |
-func StringArray(name string, values []string) FieldOpt {
|
|
| 54 |
- return func(em *EventMetadata, ed *EventData) {
|
|
| 55 |
- em.WriteArray(name, InTypeANSIString, OutTypeUTF8, 0) |
|
| 56 |
- ed.WriteUint16(uint16(len(values))) |
|
| 57 |
- for _, v := range values {
|
|
| 58 |
- ed.WriteString(v) |
|
| 59 |
- } |
|
| 60 |
- } |
|
| 61 |
-} |
|
| 62 |
- |
|
| 63 |
-// IntField adds a single int field to the event. |
|
| 64 |
-func IntField(name string, value int) FieldOpt {
|
|
| 65 |
- switch unsafe.Sizeof(value) {
|
|
| 66 |
- case 4: |
|
| 67 |
- return Int32Field(name, int32(value)) |
|
| 68 |
- case 8: |
|
| 69 |
- return Int64Field(name, int64(value)) |
|
| 70 |
- default: |
|
| 71 |
- panic("Unsupported int size")
|
|
| 72 |
- } |
|
| 73 |
-} |
|
| 74 |
- |
|
| 75 |
-// IntArray adds an array of int to the event. |
|
| 76 |
-func IntArray(name string, values []int) FieldOpt {
|
|
| 77 |
- inType := InTypeNull |
|
| 78 |
- var writeItem func(*EventData, int) |
|
| 79 |
- switch unsafe.Sizeof(values[0]) {
|
|
| 80 |
- case 4: |
|
| 81 |
- inType = InTypeInt32 |
|
| 82 |
- writeItem = func(ed *EventData, item int) { ed.WriteInt32(int32(item)) }
|
|
| 83 |
- case 8: |
|
| 84 |
- inType = InTypeInt64 |
|
| 85 |
- writeItem = func(ed *EventData, item int) { ed.WriteInt64(int64(item)) }
|
|
| 86 |
- default: |
|
| 87 |
- panic("Unsupported int size")
|
|
| 88 |
- } |
|
| 89 |
- |
|
| 90 |
- return func(em *EventMetadata, ed *EventData) {
|
|
| 91 |
- em.WriteArray(name, inType, OutTypeDefault, 0) |
|
| 92 |
- ed.WriteUint16(uint16(len(values))) |
|
| 93 |
- for _, v := range values {
|
|
| 94 |
- writeItem(ed, v) |
|
| 95 |
- } |
|
| 96 |
- } |
|
| 97 |
-} |
|
| 98 |
- |
|
| 99 |
-// Int8Field adds a single int8 field to the event. |
|
| 100 |
-func Int8Field(name string, value int8) FieldOpt {
|
|
| 101 |
- return func(em *EventMetadata, ed *EventData) {
|
|
| 102 |
- em.WriteField(name, InTypeInt8, OutTypeDefault, 0) |
|
| 103 |
- ed.WriteInt8(value) |
|
| 104 |
- } |
|
| 105 |
-} |
|
| 106 |
- |
|
| 107 |
-// Int8Array adds an array of int8 to the event. |
|
| 108 |
-func Int8Array(name string, values []int8) FieldOpt {
|
|
| 109 |
- return func(em *EventMetadata, ed *EventData) {
|
|
| 110 |
- em.WriteArray(name, InTypeInt8, OutTypeDefault, 0) |
|
| 111 |
- ed.WriteUint16(uint16(len(values))) |
|
| 112 |
- for _, v := range values {
|
|
| 113 |
- ed.WriteInt8(v) |
|
| 114 |
- } |
|
| 115 |
- } |
|
| 116 |
-} |
|
| 117 |
- |
|
| 118 |
-// Int16Field adds a single int16 field to the event. |
|
| 119 |
-func Int16Field(name string, value int16) FieldOpt {
|
|
| 120 |
- return func(em *EventMetadata, ed *EventData) {
|
|
| 121 |
- em.WriteField(name, InTypeInt16, OutTypeDefault, 0) |
|
| 122 |
- ed.WriteInt16(value) |
|
| 123 |
- } |
|
| 124 |
-} |
|
| 125 |
- |
|
| 126 |
-// Int16Array adds an array of int16 to the event. |
|
| 127 |
-func Int16Array(name string, values []int16) FieldOpt {
|
|
| 128 |
- return func(em *EventMetadata, ed *EventData) {
|
|
| 129 |
- em.WriteArray(name, InTypeInt16, OutTypeDefault, 0) |
|
| 130 |
- ed.WriteUint16(uint16(len(values))) |
|
| 131 |
- for _, v := range values {
|
|
| 132 |
- ed.WriteInt16(v) |
|
| 133 |
- } |
|
| 134 |
- } |
|
| 135 |
-} |
|
| 136 |
- |
|
| 137 |
-// Int32Field adds a single int32 field to the event. |
|
| 138 |
-func Int32Field(name string, value int32) FieldOpt {
|
|
| 139 |
- return func(em *EventMetadata, ed *EventData) {
|
|
| 140 |
- em.WriteField(name, InTypeInt32, OutTypeDefault, 0) |
|
| 141 |
- ed.WriteInt32(value) |
|
| 142 |
- } |
|
| 143 |
-} |
|
| 144 |
- |
|
| 145 |
-// Int32Array adds an array of int32 to the event. |
|
| 146 |
-func Int32Array(name string, values []int32) FieldOpt {
|
|
| 147 |
- return func(em *EventMetadata, ed *EventData) {
|
|
| 148 |
- em.WriteArray(name, InTypeInt32, OutTypeDefault, 0) |
|
| 149 |
- ed.WriteUint16(uint16(len(values))) |
|
| 150 |
- for _, v := range values {
|
|
| 151 |
- ed.WriteInt32(v) |
|
| 152 |
- } |
|
| 153 |
- } |
|
| 154 |
-} |
|
| 155 |
- |
|
| 156 |
-// Int64Field adds a single int64 field to the event. |
|
| 157 |
-func Int64Field(name string, value int64) FieldOpt {
|
|
| 158 |
- return func(em *EventMetadata, ed *EventData) {
|
|
| 159 |
- em.WriteField(name, InTypeInt64, OutTypeDefault, 0) |
|
| 160 |
- ed.WriteInt64(value) |
|
| 161 |
- } |
|
| 162 |
-} |
|
| 163 |
- |
|
| 164 |
-// Int64Array adds an array of int64 to the event. |
|
| 165 |
-func Int64Array(name string, values []int64) FieldOpt {
|
|
| 166 |
- return func(em *EventMetadata, ed *EventData) {
|
|
| 167 |
- em.WriteArray(name, InTypeInt64, OutTypeDefault, 0) |
|
| 168 |
- ed.WriteUint16(uint16(len(values))) |
|
| 169 |
- for _, v := range values {
|
|
| 170 |
- ed.WriteInt64(v) |
|
| 171 |
- } |
|
| 172 |
- } |
|
| 173 |
-} |
|
| 174 |
- |
|
| 175 |
-// UintField adds a single uint field to the event. |
|
| 176 |
-func UintField(name string, value uint) FieldOpt {
|
|
| 177 |
- switch unsafe.Sizeof(value) {
|
|
| 178 |
- case 4: |
|
| 179 |
- return Uint32Field(name, uint32(value)) |
|
| 180 |
- case 8: |
|
| 181 |
- return Uint64Field(name, uint64(value)) |
|
| 182 |
- default: |
|
| 183 |
- panic("Unsupported uint size")
|
|
| 184 |
- } |
|
| 185 |
-} |
|
| 186 |
- |
|
| 187 |
-// UintArray adds an array of uint to the event. |
|
| 188 |
-func UintArray(name string, values []uint) FieldOpt {
|
|
| 189 |
- inType := InTypeNull |
|
| 190 |
- var writeItem func(*EventData, uint) |
|
| 191 |
- switch unsafe.Sizeof(values[0]) {
|
|
| 192 |
- case 4: |
|
| 193 |
- inType = InTypeUint32 |
|
| 194 |
- writeItem = func(ed *EventData, item uint) { ed.WriteUint32(uint32(item)) }
|
|
| 195 |
- case 8: |
|
| 196 |
- inType = InTypeUint64 |
|
| 197 |
- writeItem = func(ed *EventData, item uint) { ed.WriteUint64(uint64(item)) }
|
|
| 198 |
- default: |
|
| 199 |
- panic("Unsupported uint size")
|
|
| 200 |
- } |
|
| 201 |
- |
|
| 202 |
- return func(em *EventMetadata, ed *EventData) {
|
|
| 203 |
- em.WriteArray(name, inType, OutTypeDefault, 0) |
|
| 204 |
- ed.WriteUint16(uint16(len(values))) |
|
| 205 |
- for _, v := range values {
|
|
| 206 |
- writeItem(ed, v) |
|
| 207 |
- } |
|
| 208 |
- } |
|
| 209 |
-} |
|
| 210 |
- |
|
| 211 |
-// Uint8Field adds a single uint8 field to the event. |
|
| 212 |
-func Uint8Field(name string, value uint8) FieldOpt {
|
|
| 213 |
- return func(em *EventMetadata, ed *EventData) {
|
|
| 214 |
- em.WriteField(name, InTypeUint8, OutTypeDefault, 0) |
|
| 215 |
- ed.WriteUint8(value) |
|
| 216 |
- } |
|
| 217 |
-} |
|
| 218 |
- |
|
| 219 |
-// Uint8Array adds an array of uint8 to the event. |
|
| 220 |
-func Uint8Array(name string, values []uint8) FieldOpt {
|
|
| 221 |
- return func(em *EventMetadata, ed *EventData) {
|
|
| 222 |
- em.WriteArray(name, InTypeUint8, OutTypeDefault, 0) |
|
| 223 |
- ed.WriteUint16(uint16(len(values))) |
|
| 224 |
- for _, v := range values {
|
|
| 225 |
- ed.WriteUint8(v) |
|
| 226 |
- } |
|
| 227 |
- } |
|
| 228 |
-} |
|
| 229 |
- |
|
| 230 |
-// Uint16Field adds a single uint16 field to the event. |
|
| 231 |
-func Uint16Field(name string, value uint16) FieldOpt {
|
|
| 232 |
- return func(em *EventMetadata, ed *EventData) {
|
|
| 233 |
- em.WriteField(name, InTypeUint16, OutTypeDefault, 0) |
|
| 234 |
- ed.WriteUint16(value) |
|
| 235 |
- } |
|
| 236 |
-} |
|
| 237 |
- |
|
| 238 |
-// Uint16Array adds an array of uint16 to the event. |
|
| 239 |
-func Uint16Array(name string, values []uint16) FieldOpt {
|
|
| 240 |
- return func(em *EventMetadata, ed *EventData) {
|
|
| 241 |
- em.WriteArray(name, InTypeUint16, OutTypeDefault, 0) |
|
| 242 |
- ed.WriteUint16(uint16(len(values))) |
|
| 243 |
- for _, v := range values {
|
|
| 244 |
- ed.WriteUint16(v) |
|
| 245 |
- } |
|
| 246 |
- } |
|
| 247 |
-} |
|
| 248 |
- |
|
| 249 |
-// Uint32Field adds a single uint32 field to the event. |
|
| 250 |
-func Uint32Field(name string, value uint32) FieldOpt {
|
|
| 251 |
- return func(em *EventMetadata, ed *EventData) {
|
|
| 252 |
- em.WriteField(name, InTypeUint32, OutTypeDefault, 0) |
|
| 253 |
- ed.WriteUint32(value) |
|
| 254 |
- } |
|
| 255 |
-} |
|
| 256 |
- |
|
| 257 |
-// Uint32Array adds an array of uint32 to the event. |
|
| 258 |
-func Uint32Array(name string, values []uint32) FieldOpt {
|
|
| 259 |
- return func(em *EventMetadata, ed *EventData) {
|
|
| 260 |
- em.WriteArray(name, InTypeUint32, OutTypeDefault, 0) |
|
| 261 |
- ed.WriteUint16(uint16(len(values))) |
|
| 262 |
- for _, v := range values {
|
|
| 263 |
- ed.WriteUint32(v) |
|
| 264 |
- } |
|
| 265 |
- } |
|
| 266 |
-} |
|
| 267 |
- |
|
| 268 |
-// Uint64Field adds a single uint64 field to the event. |
|
| 269 |
-func Uint64Field(name string, value uint64) FieldOpt {
|
|
| 270 |
- return func(em *EventMetadata, ed *EventData) {
|
|
| 271 |
- em.WriteField(name, InTypeUint64, OutTypeDefault, 0) |
|
| 272 |
- ed.WriteUint64(value) |
|
| 273 |
- } |
|
| 274 |
-} |
|
| 275 |
- |
|
| 276 |
-// Uint64Array adds an array of uint64 to the event. |
|
| 277 |
-func Uint64Array(name string, values []uint64) FieldOpt {
|
|
| 278 |
- return func(em *EventMetadata, ed *EventData) {
|
|
| 279 |
- em.WriteArray(name, InTypeUint64, OutTypeDefault, 0) |
|
| 280 |
- ed.WriteUint16(uint16(len(values))) |
|
| 281 |
- for _, v := range values {
|
|
| 282 |
- ed.WriteUint64(v) |
|
| 283 |
- } |
|
| 284 |
- } |
|
| 285 |
-} |
|
| 286 |
- |
|
| 287 |
-// UintptrField adds a single uintptr field to the event. |
|
| 288 |
-func UintptrField(name string, value uintptr) FieldOpt {
|
|
| 289 |
- inType := InTypeNull |
|
| 290 |
- var writeItem func(*EventData, uintptr) |
|
| 291 |
- switch unsafe.Sizeof(value) {
|
|
| 292 |
- case 4: |
|
| 293 |
- inType = InTypeHexInt32 |
|
| 294 |
- writeItem = func(ed *EventData, item uintptr) { ed.WriteUint32(uint32(item)) }
|
|
| 295 |
- case 8: |
|
| 296 |
- inType = InTypeHexInt64 |
|
| 297 |
- writeItem = func(ed *EventData, item uintptr) { ed.WriteUint64(uint64(item)) }
|
|
| 298 |
- default: |
|
| 299 |
- panic("Unsupported uintptr size")
|
|
| 300 |
- } |
|
| 301 |
- |
|
| 302 |
- return func(em *EventMetadata, ed *EventData) {
|
|
| 303 |
- em.WriteField(name, inType, OutTypeDefault, 0) |
|
| 304 |
- writeItem(ed, value) |
|
| 305 |
- } |
|
| 306 |
-} |
|
| 307 |
- |
|
| 308 |
-// UintptrArray adds an array of uintptr to the event. |
|
| 309 |
-func UintptrArray(name string, values []uintptr) FieldOpt {
|
|
| 310 |
- inType := InTypeNull |
|
| 311 |
- var writeItem func(*EventData, uintptr) |
|
| 312 |
- switch unsafe.Sizeof(values[0]) {
|
|
| 313 |
- case 4: |
|
| 314 |
- inType = InTypeHexInt32 |
|
| 315 |
- writeItem = func(ed *EventData, item uintptr) { ed.WriteUint32(uint32(item)) }
|
|
| 316 |
- case 8: |
|
| 317 |
- inType = InTypeHexInt64 |
|
| 318 |
- writeItem = func(ed *EventData, item uintptr) { ed.WriteUint64(uint64(item)) }
|
|
| 319 |
- default: |
|
| 320 |
- panic("Unsupported uintptr size")
|
|
| 321 |
- } |
|
| 322 |
- |
|
| 323 |
- return func(em *EventMetadata, ed *EventData) {
|
|
| 324 |
- em.WriteArray(name, inType, OutTypeDefault, 0) |
|
| 325 |
- ed.WriteUint16(uint16(len(values))) |
|
| 326 |
- for _, v := range values {
|
|
| 327 |
- writeItem(ed, v) |
|
| 328 |
- } |
|
| 329 |
- } |
|
| 330 |
-} |
|
| 331 |
- |
|
| 332 |
-// Float32Field adds a single float32 field to the event. |
|
| 333 |
-func Float32Field(name string, value float32) FieldOpt {
|
|
| 334 |
- return func(em *EventMetadata, ed *EventData) {
|
|
| 335 |
- em.WriteField(name, InTypeFloat, OutTypeDefault, 0) |
|
| 336 |
- ed.WriteUint32(math.Float32bits(value)) |
|
| 337 |
- } |
|
| 338 |
-} |
|
| 339 |
- |
|
| 340 |
-// Float32Array adds an array of float32 to the event. |
|
| 341 |
-func Float32Array(name string, values []float32) FieldOpt {
|
|
| 342 |
- return func(em *EventMetadata, ed *EventData) {
|
|
| 343 |
- em.WriteArray(name, InTypeFloat, OutTypeDefault, 0) |
|
| 344 |
- ed.WriteUint16(uint16(len(values))) |
|
| 345 |
- for _, v := range values {
|
|
| 346 |
- ed.WriteUint32(math.Float32bits(v)) |
|
| 347 |
- } |
|
| 348 |
- } |
|
| 349 |
-} |
|
| 350 |
- |
|
| 351 |
-// Float64Field adds a single float64 field to the event. |
|
| 352 |
-func Float64Field(name string, value float64) FieldOpt {
|
|
| 353 |
- return func(em *EventMetadata, ed *EventData) {
|
|
| 354 |
- em.WriteField(name, InTypeDouble, OutTypeDefault, 0) |
|
| 355 |
- ed.WriteUint64(math.Float64bits(value)) |
|
| 356 |
- } |
|
| 357 |
-} |
|
| 358 |
- |
|
| 359 |
-// Float64Array adds an array of float64 to the event. |
|
| 360 |
-func Float64Array(name string, values []float64) FieldOpt {
|
|
| 361 |
- return func(em *EventMetadata, ed *EventData) {
|
|
| 362 |
- em.WriteArray(name, InTypeDouble, OutTypeDefault, 0) |
|
| 363 |
- ed.WriteUint16(uint16(len(values))) |
|
| 364 |
- for _, v := range values {
|
|
| 365 |
- ed.WriteUint64(math.Float64bits(v)) |
|
| 366 |
- } |
|
| 367 |
- } |
|
| 368 |
-} |
|
| 369 |
- |
|
| 370 |
-// Struct adds a nested struct to the event, the FieldOpts in the opts argument |
|
| 371 |
-// are used to specify the fields of the struct. |
|
| 372 |
-func Struct(name string, opts ...FieldOpt) FieldOpt {
|
|
| 373 |
- return func(em *EventMetadata, ed *EventData) {
|
|
| 374 |
- em.WriteStruct(name, uint8(len(opts)), 0) |
|
| 375 |
- for _, opt := range opts {
|
|
| 376 |
- opt(em, ed) |
|
| 377 |
- } |
|
| 378 |
- } |
|
| 379 |
-} |
| 380 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,279 +0,0 @@ |
| 1 |
-package etw |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "bytes" |
|
| 5 |
- "crypto/sha1" |
|
| 6 |
- "encoding/binary" |
|
| 7 |
- "encoding/hex" |
|
| 8 |
- "fmt" |
|
| 9 |
- "strings" |
|
| 10 |
- "unicode/utf16" |
|
| 11 |
- "unsafe" |
|
| 12 |
- |
|
| 13 |
- "golang.org/x/sys/windows" |
|
| 14 |
-) |
|
| 15 |
- |
|
| 16 |
-// Provider represents an ETW event provider. It is identified by a provider |
|
| 17 |
-// name and ID (GUID), which should always have a 1:1 mapping to each other |
|
| 18 |
-// (e.g. don't use multiple provider names with the same ID, or vice versa). |
|
| 19 |
-type Provider struct {
|
|
| 20 |
- ID *windows.GUID |
|
| 21 |
- handle providerHandle |
|
| 22 |
- metadata []byte |
|
| 23 |
- callback EnableCallback |
|
| 24 |
- index uint |
|
| 25 |
- enabled bool |
|
| 26 |
- level Level |
|
| 27 |
- keywordAny uint64 |
|
| 28 |
- keywordAll uint64 |
|
| 29 |
-} |
|
| 30 |
- |
|
| 31 |
-// String returns the `provider`.ID as a string |
|
| 32 |
-func (provider *Provider) String() string {
|
|
| 33 |
- data1 := make([]byte, 4) |
|
| 34 |
- binary.BigEndian.PutUint32(data1, provider.ID.Data1) |
|
| 35 |
- data2 := make([]byte, 2) |
|
| 36 |
- binary.BigEndian.PutUint16(data2, provider.ID.Data2) |
|
| 37 |
- data3 := make([]byte, 2) |
|
| 38 |
- binary.BigEndian.PutUint16(data3, provider.ID.Data3) |
|
| 39 |
- return fmt.Sprintf( |
|
| 40 |
- "%s-%s-%s-%s-%s", |
|
| 41 |
- hex.EncodeToString(data1), |
|
| 42 |
- hex.EncodeToString(data2), |
|
| 43 |
- hex.EncodeToString(data3), |
|
| 44 |
- hex.EncodeToString(provider.ID.Data4[:2]), |
|
| 45 |
- hex.EncodeToString(provider.ID.Data4[2:])) |
|
| 46 |
-} |
|
| 47 |
- |
|
| 48 |
-type providerHandle windows.Handle |
|
| 49 |
- |
|
| 50 |
-// ProviderState informs the provider EnableCallback what action is being |
|
| 51 |
-// performed. |
|
| 52 |
-type ProviderState uint32 |
|
| 53 |
- |
|
| 54 |
-const ( |
|
| 55 |
- // ProviderStateDisable indicates the provider is being disabled. |
|
| 56 |
- ProviderStateDisable ProviderState = iota |
|
| 57 |
- // ProviderStateEnable indicates the provider is being enabled. |
|
| 58 |
- ProviderStateEnable |
|
| 59 |
- // ProviderStateCaptureState indicates the provider is having its current |
|
| 60 |
- // state snap-shotted. |
|
| 61 |
- ProviderStateCaptureState |
|
| 62 |
-) |
|
| 63 |
- |
|
| 64 |
-type eventInfoClass uint32 |
|
| 65 |
- |
|
| 66 |
-const ( |
|
| 67 |
- eventInfoClassProviderBinaryTrackInfo eventInfoClass = iota |
|
| 68 |
- eventInfoClassProviderSetReserved1 |
|
| 69 |
- eventInfoClassProviderSetTraits |
|
| 70 |
- eventInfoClassProviderUseDescriptorType |
|
| 71 |
-) |
|
| 72 |
- |
|
| 73 |
-// EnableCallback is the form of the callback function that receives provider |
|
| 74 |
-// enable/disable notifications from ETW. |
|
| 75 |
-type EnableCallback func(*windows.GUID, ProviderState, Level, uint64, uint64, uintptr) |
|
| 76 |
- |
|
| 77 |
-func providerCallback(sourceID *windows.GUID, state ProviderState, level Level, matchAnyKeyword uint64, matchAllKeyword uint64, filterData uintptr, i uintptr) {
|
|
| 78 |
- provider := providers.getProvider(uint(i)) |
|
| 79 |
- |
|
| 80 |
- switch state {
|
|
| 81 |
- case ProviderStateDisable: |
|
| 82 |
- provider.enabled = false |
|
| 83 |
- case ProviderStateEnable: |
|
| 84 |
- provider.enabled = true |
|
| 85 |
- provider.level = level |
|
| 86 |
- provider.keywordAny = matchAnyKeyword |
|
| 87 |
- provider.keywordAll = matchAllKeyword |
|
| 88 |
- } |
|
| 89 |
- |
|
| 90 |
- if provider.callback != nil {
|
|
| 91 |
- provider.callback(sourceID, state, level, matchAnyKeyword, matchAllKeyword, filterData) |
|
| 92 |
- } |
|
| 93 |
-} |
|
| 94 |
- |
|
| 95 |
-// providerCallbackAdapter acts as the first-level callback from the C/ETW side |
|
| 96 |
-// for provider notifications. Because Go has trouble with callback arguments of |
|
| 97 |
-// different size, it has only pointer-sized arguments, which are then cast to |
|
| 98 |
-// the appropriate types when calling providerCallback. |
|
| 99 |
-func providerCallbackAdapter(sourceID *windows.GUID, state uintptr, level uintptr, matchAnyKeyword uintptr, matchAllKeyword uintptr, filterData uintptr, i uintptr) uintptr {
|
|
| 100 |
- providerCallback(sourceID, ProviderState(state), Level(level), uint64(matchAnyKeyword), uint64(matchAllKeyword), filterData, i) |
|
| 101 |
- return 0 |
|
| 102 |
-} |
|
| 103 |
- |
|
| 104 |
-// providerIDFromName generates a provider ID based on the provider name. It |
|
| 105 |
-// uses the same algorithm as used by .NET's EventSource class, which is based |
|
| 106 |
-// on RFC 4122. More information on the algorithm can be found here: |
|
| 107 |
-// https://blogs.msdn.microsoft.com/dcook/2015/09/08/etw-provider-names-and-guids/ |
|
| 108 |
-// The algorithm is roughly: |
|
| 109 |
-// Hash = Sha1(namespace + arg.ToUpper().ToUtf16be()) |
|
| 110 |
-// Guid = Hash[0..15], with Hash[7] tweaked according to RFC 4122 |
|
| 111 |
-func providerIDFromName(name string) *windows.GUID {
|
|
| 112 |
- buffer := sha1.New() |
|
| 113 |
- |
|
| 114 |
- namespace := []byte{0x48, 0x2C, 0x2D, 0xB2, 0xC3, 0x90, 0x47, 0xC8, 0x87, 0xF8, 0x1A, 0x15, 0xBF, 0xC1, 0x30, 0xFB}
|
|
| 115 |
- buffer.Write(namespace) |
|
| 116 |
- |
|
| 117 |
- binary.Write(buffer, binary.BigEndian, utf16.Encode([]rune(strings.ToUpper(name)))) |
|
| 118 |
- |
|
| 119 |
- sum := buffer.Sum(nil) |
|
| 120 |
- sum[7] = (sum[7] & 0xf) | 0x50 |
|
| 121 |
- |
|
| 122 |
- return &windows.GUID{
|
|
| 123 |
- Data1: binary.LittleEndian.Uint32(sum[0:4]), |
|
| 124 |
- Data2: binary.LittleEndian.Uint16(sum[4:6]), |
|
| 125 |
- Data3: binary.LittleEndian.Uint16(sum[6:8]), |
|
| 126 |
- Data4: [8]byte{sum[8], sum[9], sum[10], sum[11], sum[12], sum[13], sum[14], sum[15]},
|
|
| 127 |
- } |
|
| 128 |
-} |
|
| 129 |
- |
|
| 130 |
-// NewProvider creates and registers a new ETW provider. The provider ID is |
|
| 131 |
-// generated based on the provider name. |
|
| 132 |
-func NewProvider(name string, callback EnableCallback) (provider *Provider, err error) {
|
|
| 133 |
- return NewProviderWithID(name, providerIDFromName(name), callback) |
|
| 134 |
-} |
|
| 135 |
- |
|
| 136 |
-// NewProviderWithID creates and registers a new ETW provider, allowing the |
|
| 137 |
-// provider ID to be manually specified. This is most useful when there is an |
|
| 138 |
-// existing provider ID that must be used to conform to existing diagnostic |
|
| 139 |
-// infrastructure. |
|
| 140 |
-func NewProviderWithID(name string, id *windows.GUID, callback EnableCallback) (provider *Provider, err error) {
|
|
| 141 |
- providerCallbackOnce.Do(func() {
|
|
| 142 |
- globalProviderCallback = windows.NewCallback(providerCallbackAdapter) |
|
| 143 |
- }) |
|
| 144 |
- |
|
| 145 |
- provider = providers.newProvider() |
|
| 146 |
- defer func() {
|
|
| 147 |
- if err != nil {
|
|
| 148 |
- providers.removeProvider(provider) |
|
| 149 |
- } |
|
| 150 |
- }() |
|
| 151 |
- provider.ID = id |
|
| 152 |
- provider.callback = callback |
|
| 153 |
- |
|
| 154 |
- if err := eventRegister(provider.ID, globalProviderCallback, uintptr(provider.index), &provider.handle); err != nil {
|
|
| 155 |
- return nil, err |
|
| 156 |
- } |
|
| 157 |
- |
|
| 158 |
- metadata := &bytes.Buffer{}
|
|
| 159 |
- binary.Write(metadata, binary.LittleEndian, uint16(0)) // Write empty size for buffer (to update later) |
|
| 160 |
- metadata.WriteString(name) |
|
| 161 |
- metadata.WriteByte(0) // Null terminator for name |
|
| 162 |
- binary.LittleEndian.PutUint16(metadata.Bytes(), uint16(metadata.Len())) // Update the size at the beginning of the buffer |
|
| 163 |
- provider.metadata = metadata.Bytes() |
|
| 164 |
- |
|
| 165 |
- if err := eventSetInformation( |
|
| 166 |
- provider.handle, |
|
| 167 |
- eventInfoClassProviderSetTraits, |
|
| 168 |
- uintptr(unsafe.Pointer(&provider.metadata[0])), |
|
| 169 |
- uint32(len(provider.metadata))); err != nil {
|
|
| 170 |
- |
|
| 171 |
- return nil, err |
|
| 172 |
- } |
|
| 173 |
- |
|
| 174 |
- return provider, nil |
|
| 175 |
-} |
|
| 176 |
- |
|
| 177 |
-// Close unregisters the provider. |
|
| 178 |
-func (provider *Provider) Close() error {
|
|
| 179 |
- providers.removeProvider(provider) |
|
| 180 |
- return eventUnregister(provider.handle) |
|
| 181 |
-} |
|
| 182 |
- |
|
| 183 |
-// IsEnabled calls IsEnabledForLevelAndKeywords with LevelAlways and all |
|
| 184 |
-// keywords set. |
|
| 185 |
-func (provider *Provider) IsEnabled() bool {
|
|
| 186 |
- return provider.IsEnabledForLevelAndKeywords(LevelAlways, ^uint64(0)) |
|
| 187 |
-} |
|
| 188 |
- |
|
| 189 |
-// IsEnabledForLevel calls IsEnabledForLevelAndKeywords with the specified level |
|
| 190 |
-// and all keywords set. |
|
| 191 |
-func (provider *Provider) IsEnabledForLevel(level Level) bool {
|
|
| 192 |
- return provider.IsEnabledForLevelAndKeywords(level, ^uint64(0)) |
|
| 193 |
-} |
|
| 194 |
- |
|
| 195 |
-// IsEnabledForLevelAndKeywords allows event producer code to check if there are |
|
| 196 |
-// any event sessions that are interested in an event, based on the event level |
|
| 197 |
-// and keywords. Although this check happens automatically in the ETW |
|
| 198 |
-// infrastructure, it can be useful to check if an event will actually be |
|
| 199 |
-// consumed before doing expensive work to build the event data. |
|
| 200 |
-func (provider *Provider) IsEnabledForLevelAndKeywords(level Level, keywords uint64) bool {
|
|
| 201 |
- if !provider.enabled {
|
|
| 202 |
- return false |
|
| 203 |
- } |
|
| 204 |
- |
|
| 205 |
- // ETW automatically sets the level to 255 if it is specified as 0, so we |
|
| 206 |
- // don't need to worry about the level=0 (all events) case. |
|
| 207 |
- if level > provider.level {
|
|
| 208 |
- return false |
|
| 209 |
- } |
|
| 210 |
- |
|
| 211 |
- if keywords != 0 && (keywords&provider.keywordAny == 0 || keywords&provider.keywordAll != provider.keywordAll) {
|
|
| 212 |
- return false |
|
| 213 |
- } |
|
| 214 |
- |
|
| 215 |
- return true |
|
| 216 |
-} |
|
| 217 |
- |
|
| 218 |
-// WriteEvent writes a single ETW event from the provider. The event is |
|
| 219 |
-// constructed based on the EventOpt and FieldOpt values that are passed as |
|
| 220 |
-// opts. |
|
| 221 |
-func (provider *Provider) WriteEvent(name string, eventOpts []EventOpt, fieldOpts []FieldOpt) error {
|
|
| 222 |
- options := eventOptions{descriptor: NewEventDescriptor()}
|
|
| 223 |
- em := &EventMetadata{}
|
|
| 224 |
- ed := &EventData{}
|
|
| 225 |
- |
|
| 226 |
- // We need to evaluate the EventOpts first since they might change tags, and |
|
| 227 |
- // we write out the tags before evaluating FieldOpts. |
|
| 228 |
- for _, opt := range eventOpts {
|
|
| 229 |
- opt(&options) |
|
| 230 |
- } |
|
| 231 |
- |
|
| 232 |
- if !provider.IsEnabledForLevelAndKeywords(options.descriptor.Level, options.descriptor.Keyword) {
|
|
| 233 |
- return nil |
|
| 234 |
- } |
|
| 235 |
- |
|
| 236 |
- em.WriteEventHeader(name, options.tags) |
|
| 237 |
- |
|
| 238 |
- for _, opt := range fieldOpts {
|
|
| 239 |
- opt(em, ed) |
|
| 240 |
- } |
|
| 241 |
- |
|
| 242 |
- // Don't pass a data blob if there is no event data. There will always be |
|
| 243 |
- // event metadata (e.g. for the name) so we don't need to do this check for |
|
| 244 |
- // the metadata. |
|
| 245 |
- dataBlobs := [][]byte{}
|
|
| 246 |
- if len(ed.Bytes()) > 0 {
|
|
| 247 |
- dataBlobs = [][]byte{ed.Bytes()}
|
|
| 248 |
- } |
|
| 249 |
- |
|
| 250 |
- return provider.WriteEventRaw(options.descriptor, nil, nil, [][]byte{em.Bytes()}, dataBlobs)
|
|
| 251 |
-} |
|
| 252 |
- |
|
| 253 |
-// WriteEventRaw writes a single ETW event from the provider. This function is |
|
| 254 |
-// less abstracted than WriteEvent, and presents a fairly direct interface to |
|
| 255 |
-// the event writing functionality. It expects a series of event metadata and |
|
| 256 |
-// event data blobs to be passed in, which must conform to the TraceLogging |
|
| 257 |
-// schema. The functions on EventMetadata and EventData can help with creating |
|
| 258 |
-// these blobs. The blobs of each type are effectively concatenated together by |
|
| 259 |
-// the ETW infrastructure. |
|
| 260 |
-func (provider *Provider) WriteEventRaw( |
|
| 261 |
- descriptor *EventDescriptor, |
|
| 262 |
- activityID *windows.GUID, |
|
| 263 |
- relatedActivityID *windows.GUID, |
|
| 264 |
- metadataBlobs [][]byte, |
|
| 265 |
- dataBlobs [][]byte) error {
|
|
| 266 |
- |
|
| 267 |
- dataDescriptorCount := uint32(1 + len(metadataBlobs) + len(dataBlobs)) |
|
| 268 |
- dataDescriptors := make([]eventDataDescriptor, 0, dataDescriptorCount) |
|
| 269 |
- |
|
| 270 |
- dataDescriptors = append(dataDescriptors, newEventDataDescriptor(eventDataDescriptorTypeProviderMetadata, provider.metadata)) |
|
| 271 |
- for _, blob := range metadataBlobs {
|
|
| 272 |
- dataDescriptors = append(dataDescriptors, newEventDataDescriptor(eventDataDescriptorTypeEventMetadata, blob)) |
|
| 273 |
- } |
|
| 274 |
- for _, blob := range dataBlobs {
|
|
| 275 |
- dataDescriptors = append(dataDescriptors, newEventDataDescriptor(eventDataDescriptorTypeUserData, blob)) |
|
| 276 |
- } |
|
| 277 |
- |
|
| 278 |
- return eventWriteTransfer(provider.handle, descriptor, activityID, relatedActivityID, dataDescriptorCount, &dataDescriptors[0]) |
|
| 279 |
-} |
| 280 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,52 +0,0 @@ |
| 1 |
-package etw |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "sync" |
|
| 5 |
-) |
|
| 6 |
- |
|
| 7 |
-// Because the provider callback function needs to be able to access the |
|
| 8 |
-// provider data when it is invoked by ETW, we need to keep provider data stored |
|
| 9 |
-// in a global map based on an index. The index is passed as the callback |
|
| 10 |
-// context to ETW. |
|
| 11 |
-type providerMap struct {
|
|
| 12 |
- m map[uint]*Provider |
|
| 13 |
- i uint |
|
| 14 |
- lock sync.Mutex |
|
| 15 |
- once sync.Once |
|
| 16 |
-} |
|
| 17 |
- |
|
| 18 |
-var providers = providerMap{
|
|
| 19 |
- m: make(map[uint]*Provider), |
|
| 20 |
-} |
|
| 21 |
- |
|
| 22 |
-func (p *providerMap) newProvider() *Provider {
|
|
| 23 |
- p.lock.Lock() |
|
| 24 |
- defer p.lock.Unlock() |
|
| 25 |
- |
|
| 26 |
- i := p.i |
|
| 27 |
- p.i++ |
|
| 28 |
- |
|
| 29 |
- provider := &Provider{
|
|
| 30 |
- index: i, |
|
| 31 |
- } |
|
| 32 |
- |
|
| 33 |
- p.m[i] = provider |
|
| 34 |
- return provider |
|
| 35 |
-} |
|
| 36 |
- |
|
| 37 |
-func (p *providerMap) removeProvider(provider *Provider) {
|
|
| 38 |
- p.lock.Lock() |
|
| 39 |
- defer p.lock.Unlock() |
|
| 40 |
- |
|
| 41 |
- delete(p.m, provider.index) |
|
| 42 |
-} |
|
| 43 |
- |
|
| 44 |
-func (p *providerMap) getProvider(index uint) *Provider {
|
|
| 45 |
- p.lock.Lock() |
|
| 46 |
- defer p.lock.Unlock() |
|
| 47 |
- |
|
| 48 |
- return p.m[index] |
|
| 49 |
-} |
|
| 50 |
- |
|
| 51 |
-var providerCallbackOnce sync.Once |
|
| 52 |
-var globalProviderCallback uintptr |
| 53 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,16 +0,0 @@ |
| 1 |
-// +build 386 arm |
|
| 2 |
- |
|
| 3 |
-package etw |
|
| 4 |
- |
|
| 5 |
-import ( |
|
| 6 |
- "unsafe" |
|
| 7 |
-) |
|
| 8 |
- |
|
| 9 |
-// byteptr64 defines a struct containing a pointer. The struct is guaranteed to |
|
| 10 |
-// be 64 bits, regardless of the actual size of a pointer on the platform. This |
|
| 11 |
-// is intended for use with certain Windows APIs that expect a pointer as a |
|
| 12 |
-// ULONGLONG. |
|
| 13 |
-type ptr64 struct {
|
|
| 14 |
- ptr unsafe.Pointer |
|
| 15 |
- _ uint32 |
|
| 16 |
-} |
| 17 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,15 +0,0 @@ |
| 1 |
-// +build amd64 arm64 |
|
| 2 |
- |
|
| 3 |
-package etw |
|
| 4 |
- |
|
| 5 |
-import ( |
|
| 6 |
- "unsafe" |
|
| 7 |
-) |
|
| 8 |
- |
|
| 9 |
-// byteptr64 defines a struct containing a pointer. The struct is guaranteed to |
|
| 10 |
-// be 64 bits, regardless of the actual size of a pointer on the platform. This |
|
| 11 |
-// is intended for use with certain Windows APIs that expect a pointer as a |
|
| 12 |
-// ULONGLONG. |
|
| 13 |
-type ptr64 struct {
|
|
| 14 |
- ptr unsafe.Pointer |
|
| 15 |
-} |
| 16 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,78 +0,0 @@ |
| 1 |
-// Code generated by 'go generate'; DO NOT EDIT. |
|
| 2 |
- |
|
| 3 |
-package etw |
|
| 4 |
- |
|
| 5 |
-import ( |
|
| 6 |
- "syscall" |
|
| 7 |
- "unsafe" |
|
| 8 |
- |
|
| 9 |
- "golang.org/x/sys/windows" |
|
| 10 |
-) |
|
| 11 |
- |
|
| 12 |
-var _ unsafe.Pointer |
|
| 13 |
- |
|
| 14 |
-// Do the interface allocations only once for common |
|
| 15 |
-// Errno values. |
|
| 16 |
-const ( |
|
| 17 |
- errnoERROR_IO_PENDING = 997 |
|
| 18 |
-) |
|
| 19 |
- |
|
| 20 |
-var ( |
|
| 21 |
- errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) |
|
| 22 |
-) |
|
| 23 |
- |
|
| 24 |
-// errnoErr returns common boxed Errno values, to prevent |
|
| 25 |
-// allocations at runtime. |
|
| 26 |
-func errnoErr(e syscall.Errno) error {
|
|
| 27 |
- switch e {
|
|
| 28 |
- case 0: |
|
| 29 |
- return nil |
|
| 30 |
- case errnoERROR_IO_PENDING: |
|
| 31 |
- return errERROR_IO_PENDING |
|
| 32 |
- } |
|
| 33 |
- // TODO: add more here, after collecting data on the common |
|
| 34 |
- // error values see on Windows. (perhaps when running |
|
| 35 |
- // all.bat?) |
|
| 36 |
- return e |
|
| 37 |
-} |
|
| 38 |
- |
|
| 39 |
-var ( |
|
| 40 |
- modadvapi32 = windows.NewLazySystemDLL("advapi32.dll")
|
|
| 41 |
- |
|
| 42 |
- procEventRegister = modadvapi32.NewProc("EventRegister")
|
|
| 43 |
- procEventUnregister = modadvapi32.NewProc("EventUnregister")
|
|
| 44 |
- procEventWriteTransfer = modadvapi32.NewProc("EventWriteTransfer")
|
|
| 45 |
- procEventSetInformation = modadvapi32.NewProc("EventSetInformation")
|
|
| 46 |
-) |
|
| 47 |
- |
|
| 48 |
-func eventRegister(providerId *windows.GUID, callback uintptr, callbackContext uintptr, providerHandle *providerHandle) (win32err error) {
|
|
| 49 |
- r0, _, _ := syscall.Syscall6(procEventRegister.Addr(), 4, uintptr(unsafe.Pointer(providerId)), uintptr(callback), uintptr(callbackContext), uintptr(unsafe.Pointer(providerHandle)), 0, 0) |
|
| 50 |
- if r0 != 0 {
|
|
| 51 |
- win32err = syscall.Errno(r0) |
|
| 52 |
- } |
|
| 53 |
- return |
|
| 54 |
-} |
|
| 55 |
- |
|
| 56 |
-func eventUnregister(providerHandle providerHandle) (win32err error) {
|
|
| 57 |
- r0, _, _ := syscall.Syscall(procEventUnregister.Addr(), 1, uintptr(providerHandle), 0, 0) |
|
| 58 |
- if r0 != 0 {
|
|
| 59 |
- win32err = syscall.Errno(r0) |
|
| 60 |
- } |
|
| 61 |
- return |
|
| 62 |
-} |
|
| 63 |
- |
|
| 64 |
-func eventWriteTransfer(providerHandle providerHandle, descriptor *EventDescriptor, activityID *windows.GUID, relatedActivityID *windows.GUID, dataDescriptorCount uint32, dataDescriptors *eventDataDescriptor) (win32err error) {
|
|
| 65 |
- r0, _, _ := syscall.Syscall6(procEventWriteTransfer.Addr(), 6, uintptr(providerHandle), uintptr(unsafe.Pointer(descriptor)), uintptr(unsafe.Pointer(activityID)), uintptr(unsafe.Pointer(relatedActivityID)), uintptr(dataDescriptorCount), uintptr(unsafe.Pointer(dataDescriptors))) |
|
| 66 |
- if r0 != 0 {
|
|
| 67 |
- win32err = syscall.Errno(r0) |
|
| 68 |
- } |
|
| 69 |
- return |
|
| 70 |
-} |
|
| 71 |
- |
|
| 72 |
-func eventSetInformation(providerHandle providerHandle, class eventInfoClass, information uintptr, length uint32) (win32err error) {
|
|
| 73 |
- r0, _, _ := syscall.Syscall6(procEventSetInformation.Addr(), 4, uintptr(providerHandle), uintptr(class), uintptr(information), uintptr(length), 0, 0) |
|
| 74 |
- if r0 != 0 {
|
|
| 75 |
- win32err = syscall.Errno(r0) |
|
| 76 |
- } |
|
| 77 |
- return |
|
| 78 |
-} |
| 79 | 1 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,15 @@ |
| 0 |
+// Package etw provides support for TraceLogging-based ETW (Event Tracing |
|
| 1 |
+// for Windows). TraceLogging is a format of ETW events that are self-describing |
|
| 2 |
+// (the event contains information on its own schema). This allows them to be |
|
| 3 |
+// decoded without needing a separate manifest with event information. The |
|
| 4 |
+// implementation here is based on the information found in |
|
| 5 |
+// TraceLoggingProvider.h in the Windows SDK, which implements TraceLogging as a |
|
| 6 |
+// set of C macros. |
|
| 7 |
+package etw |
|
| 8 |
+ |
|
| 9 |
+//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go etw.go |
|
| 10 |
+ |
|
| 11 |
+//sys eventRegister(providerId *windows.GUID, callback uintptr, callbackContext uintptr, providerHandle *providerHandle) (win32err error) = advapi32.EventRegister |
|
| 12 |
+//sys eventUnregister(providerHandle providerHandle) (win32err error) = advapi32.EventUnregister |
|
| 13 |
+//sys eventWriteTransfer(providerHandle providerHandle, descriptor *eventDescriptor, activityID *windows.GUID, relatedActivityID *windows.GUID, dataDescriptorCount uint32, dataDescriptors *eventDataDescriptor) (win32err error) = advapi32.EventWriteTransfer |
|
| 14 |
+//sys eventSetInformation(providerHandle providerHandle, class eventInfoClass, information uintptr, length uint32) (win32err error) = advapi32.EventSetInformation |
| 0 | 15 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,65 @@ |
| 0 |
+package etw |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "bytes" |
|
| 4 |
+ "encoding/binary" |
|
| 5 |
+) |
|
| 6 |
+ |
|
| 7 |
+// eventData maintains a buffer which builds up the data for an ETW event. It |
|
| 8 |
+// needs to be paired with EventMetadata which describes the event. |
|
| 9 |
+type eventData struct {
|
|
| 10 |
+ buffer bytes.Buffer |
|
| 11 |
+} |
|
| 12 |
+ |
|
| 13 |
+// bytes returns the raw binary data containing the event data. The returned |
|
| 14 |
+// value is not copied from the internal buffer, so it can be mutated by the |
|
| 15 |
+// eventData object after it is returned. |
|
| 16 |
+func (ed *eventData) bytes() []byte {
|
|
| 17 |
+ return ed.buffer.Bytes() |
|
| 18 |
+} |
|
| 19 |
+ |
|
| 20 |
+// writeString appends a string, including the null terminator, to the buffer. |
|
| 21 |
+func (ed *eventData) writeString(data string) {
|
|
| 22 |
+ ed.buffer.WriteString(data) |
|
| 23 |
+ ed.buffer.WriteByte(0) |
|
| 24 |
+} |
|
| 25 |
+ |
|
| 26 |
+// writeInt8 appends a int8 to the buffer. |
|
| 27 |
+func (ed *eventData) writeInt8(value int8) {
|
|
| 28 |
+ ed.buffer.WriteByte(uint8(value)) |
|
| 29 |
+} |
|
| 30 |
+ |
|
| 31 |
+// writeInt16 appends a int16 to the buffer. |
|
| 32 |
+func (ed *eventData) writeInt16(value int16) {
|
|
| 33 |
+ binary.Write(&ed.buffer, binary.LittleEndian, value) |
|
| 34 |
+} |
|
| 35 |
+ |
|
| 36 |
+// writeInt32 appends a int32 to the buffer. |
|
| 37 |
+func (ed *eventData) writeInt32(value int32) {
|
|
| 38 |
+ binary.Write(&ed.buffer, binary.LittleEndian, value) |
|
| 39 |
+} |
|
| 40 |
+ |
|
| 41 |
+// writeInt64 appends a int64 to the buffer. |
|
| 42 |
+func (ed *eventData) writeInt64(value int64) {
|
|
| 43 |
+ binary.Write(&ed.buffer, binary.LittleEndian, value) |
|
| 44 |
+} |
|
| 45 |
+ |
|
| 46 |
+// writeUint8 appends a uint8 to the buffer. |
|
| 47 |
+func (ed *eventData) writeUint8(value uint8) {
|
|
| 48 |
+ ed.buffer.WriteByte(value) |
|
| 49 |
+} |
|
| 50 |
+ |
|
| 51 |
+// writeUint16 appends a uint16 to the buffer. |
|
| 52 |
+func (ed *eventData) writeUint16(value uint16) {
|
|
| 53 |
+ binary.Write(&ed.buffer, binary.LittleEndian, value) |
|
| 54 |
+} |
|
| 55 |
+ |
|
| 56 |
+// writeUint32 appends a uint32 to the buffer. |
|
| 57 |
+func (ed *eventData) writeUint32(value uint32) {
|
|
| 58 |
+ binary.Write(&ed.buffer, binary.LittleEndian, value) |
|
| 59 |
+} |
|
| 60 |
+ |
|
| 61 |
+// writeUint64 appends a uint64 to the buffer. |
|
| 62 |
+func (ed *eventData) writeUint64(value uint64) {
|
|
| 63 |
+ binary.Write(&ed.buffer, binary.LittleEndian, value) |
|
| 64 |
+} |
| 0 | 65 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,29 @@ |
| 0 |
+package etw |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "unsafe" |
|
| 4 |
+) |
|
| 5 |
+ |
|
| 6 |
+type eventDataDescriptorType uint8 |
|
| 7 |
+ |
|
| 8 |
+const ( |
|
| 9 |
+ eventDataDescriptorTypeUserData eventDataDescriptorType = iota |
|
| 10 |
+ eventDataDescriptorTypeEventMetadata |
|
| 11 |
+ eventDataDescriptorTypeProviderMetadata |
|
| 12 |
+) |
|
| 13 |
+ |
|
| 14 |
+type eventDataDescriptor struct {
|
|
| 15 |
+ ptr ptr64 |
|
| 16 |
+ size uint32 |
|
| 17 |
+ dataType eventDataDescriptorType |
|
| 18 |
+ reserved1 uint8 |
|
| 19 |
+ reserved2 uint16 |
|
| 20 |
+} |
|
| 21 |
+ |
|
| 22 |
+func newEventDataDescriptor(dataType eventDataDescriptorType, buffer []byte) eventDataDescriptor {
|
|
| 23 |
+ return eventDataDescriptor{
|
|
| 24 |
+ ptr: ptr64{ptr: unsafe.Pointer(&buffer[0])},
|
|
| 25 |
+ size: uint32(len(buffer)), |
|
| 26 |
+ dataType: dataType, |
|
| 27 |
+ } |
|
| 28 |
+} |
| 0 | 29 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,67 @@ |
| 0 |
+package etw |
|
| 1 |
+ |
|
| 2 |
+// Channel represents the ETW logging channel that is used. It can be used by |
|
| 3 |
+// event consumers to give an event special treatment. |
|
| 4 |
+type Channel uint8 |
|
| 5 |
+ |
|
| 6 |
+const ( |
|
| 7 |
+ // ChannelTraceLogging is the default channel for TraceLogging events. It is |
|
| 8 |
+ // not required to be used for TraceLogging, but will prevent decoding |
|
| 9 |
+ // issues for these events on older operating systems. |
|
| 10 |
+ ChannelTraceLogging Channel = 11 |
|
| 11 |
+) |
|
| 12 |
+ |
|
| 13 |
+// Level represents the ETW logging level. There are several predefined levels |
|
| 14 |
+// that are commonly used, but technically anything from 0-255 is allowed. |
|
| 15 |
+// Lower levels indicate more important events, and 0 indicates an event that |
|
| 16 |
+// will always be collected. |
|
| 17 |
+type Level uint8 |
|
| 18 |
+ |
|
| 19 |
+// Predefined ETW log levels. |
|
| 20 |
+const ( |
|
| 21 |
+ LevelAlways Level = iota |
|
| 22 |
+ LevelCritical |
|
| 23 |
+ LevelError |
|
| 24 |
+ LevelWarning |
|
| 25 |
+ LevelInfo |
|
| 26 |
+ LevelVerbose |
|
| 27 |
+) |
|
| 28 |
+ |
|
| 29 |
+// EventDescriptor represents various metadata for an ETW event. |
|
| 30 |
+type eventDescriptor struct {
|
|
| 31 |
+ id uint16 |
|
| 32 |
+ version uint8 |
|
| 33 |
+ channel Channel |
|
| 34 |
+ level Level |
|
| 35 |
+ opcode uint8 |
|
| 36 |
+ task uint16 |
|
| 37 |
+ keyword uint64 |
|
| 38 |
+} |
|
| 39 |
+ |
|
| 40 |
+// NewEventDescriptor returns an EventDescriptor initialized for use with |
|
| 41 |
+// TraceLogging. |
|
| 42 |
+func newEventDescriptor() *eventDescriptor {
|
|
| 43 |
+ // Standard TraceLogging events default to the TraceLogging channel, and |
|
| 44 |
+ // verbose level. |
|
| 45 |
+ return &eventDescriptor{
|
|
| 46 |
+ channel: ChannelTraceLogging, |
|
| 47 |
+ level: LevelVerbose, |
|
| 48 |
+ } |
|
| 49 |
+} |
|
| 50 |
+ |
|
| 51 |
+// Identity returns the identity of the event. If the identity is not 0, it |
|
| 52 |
+// should uniquely identify the other event metadata (contained in |
|
| 53 |
+// EventDescriptor, and field metadata). Only the lower 24 bits of this value |
|
| 54 |
+// are relevant. |
|
| 55 |
+func (ed *eventDescriptor) identity() uint32 {
|
|
| 56 |
+ return (uint32(ed.version) << 16) | uint32(ed.id) |
|
| 57 |
+} |
|
| 58 |
+ |
|
| 59 |
+// SetIdentity sets the identity of the event. If the identity is not 0, it |
|
| 60 |
+// should uniquely identify the other event metadata (contained in |
|
| 61 |
+// EventDescriptor, and field metadata). Only the lower 24 bits of this value |
|
| 62 |
+// are relevant. |
|
| 63 |
+func (ed *eventDescriptor) setIdentity(identity uint32) {
|
|
| 64 |
+ ed.id = uint16(identity) |
|
| 65 |
+ ed.version = uint8(identity >> 16) |
|
| 66 |
+} |
| 0 | 67 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,177 @@ |
| 0 |
+package etw |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "bytes" |
|
| 4 |
+ "encoding/binary" |
|
| 5 |
+) |
|
| 6 |
+ |
|
| 7 |
+// inType indicates the type of data contained in the ETW event. |
|
| 8 |
+type inType byte |
|
| 9 |
+ |
|
| 10 |
+// Various inType definitions for TraceLogging. These must match the definitions |
|
| 11 |
+// found in TraceLoggingProvider.h in the Windows SDK. |
|
| 12 |
+const ( |
|
| 13 |
+ inTypeNull inType = iota |
|
| 14 |
+ inTypeUnicodeString |
|
| 15 |
+ inTypeANSIString |
|
| 16 |
+ inTypeInt8 |
|
| 17 |
+ inTypeUint8 |
|
| 18 |
+ inTypeInt16 |
|
| 19 |
+ inTypeUint16 |
|
| 20 |
+ inTypeInt32 |
|
| 21 |
+ inTypeUint32 |
|
| 22 |
+ inTypeInt64 |
|
| 23 |
+ inTypeUint64 |
|
| 24 |
+ inTypeFloat |
|
| 25 |
+ inTypeDouble |
|
| 26 |
+ inTypeBool32 |
|
| 27 |
+ inTypeBinary |
|
| 28 |
+ inTypeGUID |
|
| 29 |
+ inTypePointerUnsupported |
|
| 30 |
+ inTypeFileTime |
|
| 31 |
+ inTypeSystemTime |
|
| 32 |
+ inTypeSID |
|
| 33 |
+ inTypeHexInt32 |
|
| 34 |
+ inTypeHexInt64 |
|
| 35 |
+ inTypeCountedString |
|
| 36 |
+ inTypeCountedANSIString |
|
| 37 |
+ inTypeStruct |
|
| 38 |
+ inTypeCountedBinary |
|
| 39 |
+ inTypeCountedArray inType = 32 |
|
| 40 |
+ inTypeArray inType = 64 |
|
| 41 |
+) |
|
| 42 |
+ |
|
| 43 |
+// outType specifies a hint to the event decoder for how the value should be |
|
| 44 |
+// formatted. |
|
| 45 |
+type outType byte |
|
| 46 |
+ |
|
| 47 |
+// Various outType definitions for TraceLogging. These must match the |
|
| 48 |
+// definitions found in TraceLoggingProvider.h in the Windows SDK. |
|
| 49 |
+const ( |
|
| 50 |
+ // outTypeDefault indicates that the default formatting for the inType will |
|
| 51 |
+ // be used by the event decoder. |
|
| 52 |
+ outTypeDefault outType = iota |
|
| 53 |
+ outTypeNoPrint |
|
| 54 |
+ outTypeString |
|
| 55 |
+ outTypeBoolean |
|
| 56 |
+ outTypeHex |
|
| 57 |
+ outTypePID |
|
| 58 |
+ outTypeTID |
|
| 59 |
+ outTypePort |
|
| 60 |
+ outTypeIPv4 |
|
| 61 |
+ outTypeIPv6 |
|
| 62 |
+ outTypeSocketAddress |
|
| 63 |
+ outTypeXML |
|
| 64 |
+ outTypeJSON |
|
| 65 |
+ outTypeWin32Error |
|
| 66 |
+ outTypeNTStatus |
|
| 67 |
+ outTypeHResult |
|
| 68 |
+ outTypeFileTime |
|
| 69 |
+ outTypeSigned |
|
| 70 |
+ outTypeUnsigned |
|
| 71 |
+ outTypeUTF8 outType = 35 |
|
| 72 |
+ outTypePKCS7WithTypeInfo outType = 36 |
|
| 73 |
+ outTypeCodePointer outType = 37 |
|
| 74 |
+ outTypeDateTimeUTC outType = 38 |
|
| 75 |
+) |
|
| 76 |
+ |
|
| 77 |
+// eventMetadata maintains a buffer which builds up the metadata for an ETW |
|
| 78 |
+// event. It needs to be paired with EventData which describes the event. |
|
| 79 |
+type eventMetadata struct {
|
|
| 80 |
+ buffer bytes.Buffer |
|
| 81 |
+} |
|
| 82 |
+ |
|
| 83 |
+// bytes returns the raw binary data containing the event metadata. Before being |
|
| 84 |
+// returned, the current size of the buffer is written to the start of the |
|
| 85 |
+// buffer. The returned value is not copied from the internal buffer, so it can |
|
| 86 |
+// be mutated by the eventMetadata object after it is returned. |
|
| 87 |
+func (em *eventMetadata) bytes() []byte {
|
|
| 88 |
+ // Finalize the event metadata buffer by filling in the buffer length at the |
|
| 89 |
+ // beginning. |
|
| 90 |
+ binary.LittleEndian.PutUint16(em.buffer.Bytes(), uint16(em.buffer.Len())) |
|
| 91 |
+ return em.buffer.Bytes() |
|
| 92 |
+} |
|
| 93 |
+ |
|
| 94 |
+// writeEventHeader writes the metadata for the start of an event to the buffer. |
|
| 95 |
+// This specifies the event name and tags. |
|
| 96 |
+func (em *eventMetadata) writeEventHeader(name string, tags uint32) {
|
|
| 97 |
+ binary.Write(&em.buffer, binary.LittleEndian, uint16(0)) // Length placeholder |
|
| 98 |
+ em.writeTags(tags) |
|
| 99 |
+ em.buffer.WriteString(name) |
|
| 100 |
+ em.buffer.WriteByte(0) // Null terminator for name |
|
| 101 |
+} |
|
| 102 |
+ |
|
| 103 |
+func (em *eventMetadata) writeFieldInner(name string, inType inType, outType outType, tags uint32, arrSize uint16) {
|
|
| 104 |
+ em.buffer.WriteString(name) |
|
| 105 |
+ em.buffer.WriteByte(0) // Null terminator for name |
|
| 106 |
+ |
|
| 107 |
+ if outType == outTypeDefault && tags == 0 {
|
|
| 108 |
+ em.buffer.WriteByte(byte(inType)) |
|
| 109 |
+ } else {
|
|
| 110 |
+ em.buffer.WriteByte(byte(inType | 128)) |
|
| 111 |
+ if tags == 0 {
|
|
| 112 |
+ em.buffer.WriteByte(byte(outType)) |
|
| 113 |
+ } else {
|
|
| 114 |
+ em.buffer.WriteByte(byte(outType | 128)) |
|
| 115 |
+ em.writeTags(tags) |
|
| 116 |
+ } |
|
| 117 |
+ } |
|
| 118 |
+ |
|
| 119 |
+ if arrSize != 0 {
|
|
| 120 |
+ binary.Write(&em.buffer, binary.LittleEndian, arrSize) |
|
| 121 |
+ } |
|
| 122 |
+} |
|
| 123 |
+ |
|
| 124 |
+// writeTags writes out the tags value to the event metadata. Tags is a 28-bit |
|
| 125 |
+// value, interpreted as bit flags, which are only relevant to the event |
|
| 126 |
+// consumer. The event consumer may choose to attribute special meaning to tags |
|
| 127 |
+// (e.g. 0x4 could mean the field contains PII). Tags are written as a series of |
|
| 128 |
+// bytes, each containing 7 bits of tag value, with the high bit set if there is |
|
| 129 |
+// more tag data in the following byte. This allows for a more compact |
|
| 130 |
+// representation when not all of the tag bits are needed. |
|
| 131 |
+func (em *eventMetadata) writeTags(tags uint32) {
|
|
| 132 |
+ // Only use the top 28 bits of the tags value. |
|
| 133 |
+ tags &= 0xfffffff |
|
| 134 |
+ |
|
| 135 |
+ for {
|
|
| 136 |
+ // Tags are written with the most significant bits (e.g. 21-27) first. |
|
| 137 |
+ val := tags >> 21 |
|
| 138 |
+ |
|
| 139 |
+ if tags&0x1fffff == 0 {
|
|
| 140 |
+ // If there is no more data to write after this, write this value |
|
| 141 |
+ // without the high bit set, and return. |
|
| 142 |
+ em.buffer.WriteByte(byte(val & 0x7f)) |
|
| 143 |
+ return |
|
| 144 |
+ } |
|
| 145 |
+ |
|
| 146 |
+ em.buffer.WriteByte(byte(val | 0x80)) |
|
| 147 |
+ |
|
| 148 |
+ tags <<= 7 |
|
| 149 |
+ } |
|
| 150 |
+} |
|
| 151 |
+ |
|
| 152 |
+// writeField writes the metadata for a simple field to the buffer. |
|
| 153 |
+func (em *eventMetadata) writeField(name string, inType inType, outType outType, tags uint32) {
|
|
| 154 |
+ em.writeFieldInner(name, inType, outType, tags, 0) |
|
| 155 |
+} |
|
| 156 |
+ |
|
| 157 |
+// writeArray writes the metadata for an array field to the buffer. The number |
|
| 158 |
+// of elements in the array must be written as a uint16 in the event data, |
|
| 159 |
+// immediately preceeding the event data. |
|
| 160 |
+func (em *eventMetadata) writeArray(name string, inType inType, outType outType, tags uint32) {
|
|
| 161 |
+ em.writeFieldInner(name, inType|inTypeArray, outType, tags, 0) |
|
| 162 |
+} |
|
| 163 |
+ |
|
| 164 |
+// writeCountedArray writes the metadata for an array field to the buffer. The |
|
| 165 |
+// size of a counted array is fixed, and the size is written into the metadata |
|
| 166 |
+// directly. |
|
| 167 |
+func (em *eventMetadata) writeCountedArray(name string, count uint16, inType inType, outType outType, tags uint32) {
|
|
| 168 |
+ em.writeFieldInner(name, inType|inTypeCountedArray, outType, tags, count) |
|
| 169 |
+} |
|
| 170 |
+ |
|
| 171 |
+// writeStruct writes the metadata for a nested struct to the buffer. The struct |
|
| 172 |
+// contains the next N fields in the metadata, where N is specified by the |
|
| 173 |
+// fieldCount argument. |
|
| 174 |
+func (em *eventMetadata) writeStruct(name string, fieldCount uint8, tags uint32) {
|
|
| 175 |
+ em.writeFieldInner(name, inTypeStruct, outType(fieldCount), tags, 0) |
|
| 176 |
+} |
| 0 | 177 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,63 @@ |
| 0 |
+package etw |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "golang.org/x/sys/windows" |
|
| 4 |
+) |
|
| 5 |
+ |
|
| 6 |
+type eventOptions struct {
|
|
| 7 |
+ descriptor *eventDescriptor |
|
| 8 |
+ activityID *windows.GUID |
|
| 9 |
+ relatedActivityID *windows.GUID |
|
| 10 |
+ tags uint32 |
|
| 11 |
+} |
|
| 12 |
+ |
|
| 13 |
+// EventOpt defines the option function type that can be passed to |
|
| 14 |
+// Provider.WriteEvent to specify general event options, such as level and |
|
| 15 |
+// keyword. |
|
| 16 |
+type EventOpt func(options *eventOptions) |
|
| 17 |
+ |
|
| 18 |
+// WithEventOpts returns the variadic arguments as a single slice. |
|
| 19 |
+func WithEventOpts(opts ...EventOpt) []EventOpt {
|
|
| 20 |
+ return opts |
|
| 21 |
+} |
|
| 22 |
+ |
|
| 23 |
+// WithLevel specifies the level of the event to be written. |
|
| 24 |
+func WithLevel(level Level) EventOpt {
|
|
| 25 |
+ return func(options *eventOptions) {
|
|
| 26 |
+ options.descriptor.level = level |
|
| 27 |
+ } |
|
| 28 |
+} |
|
| 29 |
+ |
|
| 30 |
+// WithKeyword specifies the keywords of the event to be written. Multiple uses |
|
| 31 |
+// of this option are OR'd together. |
|
| 32 |
+func WithKeyword(keyword uint64) EventOpt {
|
|
| 33 |
+ return func(options *eventOptions) {
|
|
| 34 |
+ options.descriptor.keyword |= keyword |
|
| 35 |
+ } |
|
| 36 |
+} |
|
| 37 |
+ |
|
| 38 |
+func WithChannel(channel Channel) EventOpt {
|
|
| 39 |
+ return func(options *eventOptions) {
|
|
| 40 |
+ options.descriptor.channel = channel |
|
| 41 |
+ } |
|
| 42 |
+} |
|
| 43 |
+ |
|
| 44 |
+// WithTags specifies the tags of the event to be written. Tags is a 28-bit |
|
| 45 |
+// value (top 4 bits are ignored) which are interpreted by the event consumer. |
|
| 46 |
+func WithTags(newTags uint32) EventOpt {
|
|
| 47 |
+ return func(options *eventOptions) {
|
|
| 48 |
+ options.tags |= newTags |
|
| 49 |
+ } |
|
| 50 |
+} |
|
| 51 |
+ |
|
| 52 |
+func WithActivityID(activityID *windows.GUID) EventOpt {
|
|
| 53 |
+ return func(options *eventOptions) {
|
|
| 54 |
+ options.activityID = activityID |
|
| 55 |
+ } |
|
| 56 |
+} |
|
| 57 |
+ |
|
| 58 |
+func WithRelatedActivityID(activityID *windows.GUID) EventOpt {
|
|
| 59 |
+ return func(options *eventOptions) {
|
|
| 60 |
+ options.relatedActivityID = activityID |
|
| 61 |
+ } |
|
| 62 |
+} |
| 0 | 63 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,379 @@ |
| 0 |
+package etw |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "math" |
|
| 4 |
+ "unsafe" |
|
| 5 |
+) |
|
| 6 |
+ |
|
| 7 |
+// FieldOpt defines the option function type that can be passed to |
|
| 8 |
+// Provider.WriteEvent to add fields to the event. |
|
| 9 |
+type FieldOpt func(em *eventMetadata, ed *eventData) |
|
| 10 |
+ |
|
| 11 |
+// WithFields returns the variadic arguments as a single slice. |
|
| 12 |
+func WithFields(opts ...FieldOpt) []FieldOpt {
|
|
| 13 |
+ return opts |
|
| 14 |
+} |
|
| 15 |
+ |
|
| 16 |
+// BoolField adds a single bool field to the event. |
|
| 17 |
+func BoolField(name string, value bool) FieldOpt {
|
|
| 18 |
+ return func(em *eventMetadata, ed *eventData) {
|
|
| 19 |
+ em.writeField(name, inTypeUint8, outTypeBoolean, 0) |
|
| 20 |
+ bool8 := uint8(0) |
|
| 21 |
+ if value {
|
|
| 22 |
+ bool8 = uint8(1) |
|
| 23 |
+ } |
|
| 24 |
+ ed.writeUint8(bool8) |
|
| 25 |
+ } |
|
| 26 |
+} |
|
| 27 |
+ |
|
| 28 |
+// BoolArray adds an array of bool to the event. |
|
| 29 |
+func BoolArray(name string, values []bool) FieldOpt {
|
|
| 30 |
+ return func(em *eventMetadata, ed *eventData) {
|
|
| 31 |
+ em.writeArray(name, inTypeUint8, outTypeBoolean, 0) |
|
| 32 |
+ ed.writeUint16(uint16(len(values))) |
|
| 33 |
+ for _, v := range values {
|
|
| 34 |
+ bool8 := uint8(0) |
|
| 35 |
+ if v {
|
|
| 36 |
+ bool8 = uint8(1) |
|
| 37 |
+ } |
|
| 38 |
+ ed.writeUint8(bool8) |
|
| 39 |
+ } |
|
| 40 |
+ } |
|
| 41 |
+} |
|
| 42 |
+ |
|
| 43 |
+// StringField adds a single string field to the event. |
|
| 44 |
+func StringField(name string, value string) FieldOpt {
|
|
| 45 |
+ return func(em *eventMetadata, ed *eventData) {
|
|
| 46 |
+ em.writeField(name, inTypeANSIString, outTypeUTF8, 0) |
|
| 47 |
+ ed.writeString(value) |
|
| 48 |
+ } |
|
| 49 |
+} |
|
| 50 |
+ |
|
| 51 |
+// StringArray adds an array of string to the event. |
|
| 52 |
+func StringArray(name string, values []string) FieldOpt {
|
|
| 53 |
+ return func(em *eventMetadata, ed *eventData) {
|
|
| 54 |
+ em.writeArray(name, inTypeANSIString, outTypeUTF8, 0) |
|
| 55 |
+ ed.writeUint16(uint16(len(values))) |
|
| 56 |
+ for _, v := range values {
|
|
| 57 |
+ ed.writeString(v) |
|
| 58 |
+ } |
|
| 59 |
+ } |
|
| 60 |
+} |
|
| 61 |
+ |
|
| 62 |
+// IntField adds a single int field to the event. |
|
| 63 |
+func IntField(name string, value int) FieldOpt {
|
|
| 64 |
+ switch unsafe.Sizeof(value) {
|
|
| 65 |
+ case 4: |
|
| 66 |
+ return Int32Field(name, int32(value)) |
|
| 67 |
+ case 8: |
|
| 68 |
+ return Int64Field(name, int64(value)) |
|
| 69 |
+ default: |
|
| 70 |
+ panic("Unsupported int size")
|
|
| 71 |
+ } |
|
| 72 |
+} |
|
| 73 |
+ |
|
| 74 |
+// IntArray adds an array of int to the event. |
|
| 75 |
+func IntArray(name string, values []int) FieldOpt {
|
|
| 76 |
+ inType := inTypeNull |
|
| 77 |
+ var writeItem func(*eventData, int) |
|
| 78 |
+ switch unsafe.Sizeof(values[0]) {
|
|
| 79 |
+ case 4: |
|
| 80 |
+ inType = inTypeInt32 |
|
| 81 |
+ writeItem = func(ed *eventData, item int) { ed.writeInt32(int32(item)) }
|
|
| 82 |
+ case 8: |
|
| 83 |
+ inType = inTypeInt64 |
|
| 84 |
+ writeItem = func(ed *eventData, item int) { ed.writeInt64(int64(item)) }
|
|
| 85 |
+ default: |
|
| 86 |
+ panic("Unsupported int size")
|
|
| 87 |
+ } |
|
| 88 |
+ |
|
| 89 |
+ return func(em *eventMetadata, ed *eventData) {
|
|
| 90 |
+ em.writeArray(name, inType, outTypeDefault, 0) |
|
| 91 |
+ ed.writeUint16(uint16(len(values))) |
|
| 92 |
+ for _, v := range values {
|
|
| 93 |
+ writeItem(ed, v) |
|
| 94 |
+ } |
|
| 95 |
+ } |
|
| 96 |
+} |
|
| 97 |
+ |
|
| 98 |
+// Int8Field adds a single int8 field to the event. |
|
| 99 |
+func Int8Field(name string, value int8) FieldOpt {
|
|
| 100 |
+ return func(em *eventMetadata, ed *eventData) {
|
|
| 101 |
+ em.writeField(name, inTypeInt8, outTypeDefault, 0) |
|
| 102 |
+ ed.writeInt8(value) |
|
| 103 |
+ } |
|
| 104 |
+} |
|
| 105 |
+ |
|
| 106 |
+// Int8Array adds an array of int8 to the event. |
|
| 107 |
+func Int8Array(name string, values []int8) FieldOpt {
|
|
| 108 |
+ return func(em *eventMetadata, ed *eventData) {
|
|
| 109 |
+ em.writeArray(name, inTypeInt8, outTypeDefault, 0) |
|
| 110 |
+ ed.writeUint16(uint16(len(values))) |
|
| 111 |
+ for _, v := range values {
|
|
| 112 |
+ ed.writeInt8(v) |
|
| 113 |
+ } |
|
| 114 |
+ } |
|
| 115 |
+} |
|
| 116 |
+ |
|
| 117 |
+// Int16Field adds a single int16 field to the event. |
|
| 118 |
+func Int16Field(name string, value int16) FieldOpt {
|
|
| 119 |
+ return func(em *eventMetadata, ed *eventData) {
|
|
| 120 |
+ em.writeField(name, inTypeInt16, outTypeDefault, 0) |
|
| 121 |
+ ed.writeInt16(value) |
|
| 122 |
+ } |
|
| 123 |
+} |
|
| 124 |
+ |
|
| 125 |
+// Int16Array adds an array of int16 to the event. |
|
| 126 |
+func Int16Array(name string, values []int16) FieldOpt {
|
|
| 127 |
+ return func(em *eventMetadata, ed *eventData) {
|
|
| 128 |
+ em.writeArray(name, inTypeInt16, outTypeDefault, 0) |
|
| 129 |
+ ed.writeUint16(uint16(len(values))) |
|
| 130 |
+ for _, v := range values {
|
|
| 131 |
+ ed.writeInt16(v) |
|
| 132 |
+ } |
|
| 133 |
+ } |
|
| 134 |
+} |
|
| 135 |
+ |
|
| 136 |
+// Int32Field adds a single int32 field to the event. |
|
| 137 |
+func Int32Field(name string, value int32) FieldOpt {
|
|
| 138 |
+ return func(em *eventMetadata, ed *eventData) {
|
|
| 139 |
+ em.writeField(name, inTypeInt32, outTypeDefault, 0) |
|
| 140 |
+ ed.writeInt32(value) |
|
| 141 |
+ } |
|
| 142 |
+} |
|
| 143 |
+ |
|
| 144 |
+// Int32Array adds an array of int32 to the event. |
|
| 145 |
+func Int32Array(name string, values []int32) FieldOpt {
|
|
| 146 |
+ return func(em *eventMetadata, ed *eventData) {
|
|
| 147 |
+ em.writeArray(name, inTypeInt32, outTypeDefault, 0) |
|
| 148 |
+ ed.writeUint16(uint16(len(values))) |
|
| 149 |
+ for _, v := range values {
|
|
| 150 |
+ ed.writeInt32(v) |
|
| 151 |
+ } |
|
| 152 |
+ } |
|
| 153 |
+} |
|
| 154 |
+ |
|
| 155 |
+// Int64Field adds a single int64 field to the event. |
|
| 156 |
+func Int64Field(name string, value int64) FieldOpt {
|
|
| 157 |
+ return func(em *eventMetadata, ed *eventData) {
|
|
| 158 |
+ em.writeField(name, inTypeInt64, outTypeDefault, 0) |
|
| 159 |
+ ed.writeInt64(value) |
|
| 160 |
+ } |
|
| 161 |
+} |
|
| 162 |
+ |
|
| 163 |
+// Int64Array adds an array of int64 to the event. |
|
| 164 |
+func Int64Array(name string, values []int64) FieldOpt {
|
|
| 165 |
+ return func(em *eventMetadata, ed *eventData) {
|
|
| 166 |
+ em.writeArray(name, inTypeInt64, outTypeDefault, 0) |
|
| 167 |
+ ed.writeUint16(uint16(len(values))) |
|
| 168 |
+ for _, v := range values {
|
|
| 169 |
+ ed.writeInt64(v) |
|
| 170 |
+ } |
|
| 171 |
+ } |
|
| 172 |
+} |
|
| 173 |
+ |
|
| 174 |
+// UintField adds a single uint field to the event. |
|
| 175 |
+func UintField(name string, value uint) FieldOpt {
|
|
| 176 |
+ switch unsafe.Sizeof(value) {
|
|
| 177 |
+ case 4: |
|
| 178 |
+ return Uint32Field(name, uint32(value)) |
|
| 179 |
+ case 8: |
|
| 180 |
+ return Uint64Field(name, uint64(value)) |
|
| 181 |
+ default: |
|
| 182 |
+ panic("Unsupported uint size")
|
|
| 183 |
+ } |
|
| 184 |
+} |
|
| 185 |
+ |
|
| 186 |
+// UintArray adds an array of uint to the event. |
|
| 187 |
+func UintArray(name string, values []uint) FieldOpt {
|
|
| 188 |
+ inType := inTypeNull |
|
| 189 |
+ var writeItem func(*eventData, uint) |
|
| 190 |
+ switch unsafe.Sizeof(values[0]) {
|
|
| 191 |
+ case 4: |
|
| 192 |
+ inType = inTypeUint32 |
|
| 193 |
+ writeItem = func(ed *eventData, item uint) { ed.writeUint32(uint32(item)) }
|
|
| 194 |
+ case 8: |
|
| 195 |
+ inType = inTypeUint64 |
|
| 196 |
+ writeItem = func(ed *eventData, item uint) { ed.writeUint64(uint64(item)) }
|
|
| 197 |
+ default: |
|
| 198 |
+ panic("Unsupported uint size")
|
|
| 199 |
+ } |
|
| 200 |
+ |
|
| 201 |
+ return func(em *eventMetadata, ed *eventData) {
|
|
| 202 |
+ em.writeArray(name, inType, outTypeDefault, 0) |
|
| 203 |
+ ed.writeUint16(uint16(len(values))) |
|
| 204 |
+ for _, v := range values {
|
|
| 205 |
+ writeItem(ed, v) |
|
| 206 |
+ } |
|
| 207 |
+ } |
|
| 208 |
+} |
|
| 209 |
+ |
|
| 210 |
+// Uint8Field adds a single uint8 field to the event. |
|
| 211 |
+func Uint8Field(name string, value uint8) FieldOpt {
|
|
| 212 |
+ return func(em *eventMetadata, ed *eventData) {
|
|
| 213 |
+ em.writeField(name, inTypeUint8, outTypeDefault, 0) |
|
| 214 |
+ ed.writeUint8(value) |
|
| 215 |
+ } |
|
| 216 |
+} |
|
| 217 |
+ |
|
| 218 |
+// Uint8Array adds an array of uint8 to the event. |
|
| 219 |
+func Uint8Array(name string, values []uint8) FieldOpt {
|
|
| 220 |
+ return func(em *eventMetadata, ed *eventData) {
|
|
| 221 |
+ em.writeArray(name, inTypeUint8, outTypeDefault, 0) |
|
| 222 |
+ ed.writeUint16(uint16(len(values))) |
|
| 223 |
+ for _, v := range values {
|
|
| 224 |
+ ed.writeUint8(v) |
|
| 225 |
+ } |
|
| 226 |
+ } |
|
| 227 |
+} |
|
| 228 |
+ |
|
| 229 |
+// Uint16Field adds a single uint16 field to the event. |
|
| 230 |
+func Uint16Field(name string, value uint16) FieldOpt {
|
|
| 231 |
+ return func(em *eventMetadata, ed *eventData) {
|
|
| 232 |
+ em.writeField(name, inTypeUint16, outTypeDefault, 0) |
|
| 233 |
+ ed.writeUint16(value) |
|
| 234 |
+ } |
|
| 235 |
+} |
|
| 236 |
+ |
|
| 237 |
+// Uint16Array adds an array of uint16 to the event. |
|
| 238 |
+func Uint16Array(name string, values []uint16) FieldOpt {
|
|
| 239 |
+ return func(em *eventMetadata, ed *eventData) {
|
|
| 240 |
+ em.writeArray(name, inTypeUint16, outTypeDefault, 0) |
|
| 241 |
+ ed.writeUint16(uint16(len(values))) |
|
| 242 |
+ for _, v := range values {
|
|
| 243 |
+ ed.writeUint16(v) |
|
| 244 |
+ } |
|
| 245 |
+ } |
|
| 246 |
+} |
|
| 247 |
+ |
|
| 248 |
+// Uint32Field adds a single uint32 field to the event. |
|
| 249 |
+func Uint32Field(name string, value uint32) FieldOpt {
|
|
| 250 |
+ return func(em *eventMetadata, ed *eventData) {
|
|
| 251 |
+ em.writeField(name, inTypeUint32, outTypeDefault, 0) |
|
| 252 |
+ ed.writeUint32(value) |
|
| 253 |
+ } |
|
| 254 |
+} |
|
| 255 |
+ |
|
| 256 |
+// Uint32Array adds an array of uint32 to the event. |
|
| 257 |
+func Uint32Array(name string, values []uint32) FieldOpt {
|
|
| 258 |
+ return func(em *eventMetadata, ed *eventData) {
|
|
| 259 |
+ em.writeArray(name, inTypeUint32, outTypeDefault, 0) |
|
| 260 |
+ ed.writeUint16(uint16(len(values))) |
|
| 261 |
+ for _, v := range values {
|
|
| 262 |
+ ed.writeUint32(v) |
|
| 263 |
+ } |
|
| 264 |
+ } |
|
| 265 |
+} |
|
| 266 |
+ |
|
| 267 |
+// Uint64Field adds a single uint64 field to the event. |
|
| 268 |
+func Uint64Field(name string, value uint64) FieldOpt {
|
|
| 269 |
+ return func(em *eventMetadata, ed *eventData) {
|
|
| 270 |
+ em.writeField(name, inTypeUint64, outTypeDefault, 0) |
|
| 271 |
+ ed.writeUint64(value) |
|
| 272 |
+ } |
|
| 273 |
+} |
|
| 274 |
+ |
|
| 275 |
+// Uint64Array adds an array of uint64 to the event. |
|
| 276 |
+func Uint64Array(name string, values []uint64) FieldOpt {
|
|
| 277 |
+ return func(em *eventMetadata, ed *eventData) {
|
|
| 278 |
+ em.writeArray(name, inTypeUint64, outTypeDefault, 0) |
|
| 279 |
+ ed.writeUint16(uint16(len(values))) |
|
| 280 |
+ for _, v := range values {
|
|
| 281 |
+ ed.writeUint64(v) |
|
| 282 |
+ } |
|
| 283 |
+ } |
|
| 284 |
+} |
|
| 285 |
+ |
|
| 286 |
+// UintptrField adds a single uintptr field to the event. |
|
| 287 |
+func UintptrField(name string, value uintptr) FieldOpt {
|
|
| 288 |
+ inType := inTypeNull |
|
| 289 |
+ var writeItem func(*eventData, uintptr) |
|
| 290 |
+ switch unsafe.Sizeof(value) {
|
|
| 291 |
+ case 4: |
|
| 292 |
+ inType = inTypeHexInt32 |
|
| 293 |
+ writeItem = func(ed *eventData, item uintptr) { ed.writeUint32(uint32(item)) }
|
|
| 294 |
+ case 8: |
|
| 295 |
+ inType = inTypeHexInt64 |
|
| 296 |
+ writeItem = func(ed *eventData, item uintptr) { ed.writeUint64(uint64(item)) }
|
|
| 297 |
+ default: |
|
| 298 |
+ panic("Unsupported uintptr size")
|
|
| 299 |
+ } |
|
| 300 |
+ |
|
| 301 |
+ return func(em *eventMetadata, ed *eventData) {
|
|
| 302 |
+ em.writeField(name, inType, outTypeDefault, 0) |
|
| 303 |
+ writeItem(ed, value) |
|
| 304 |
+ } |
|
| 305 |
+} |
|
| 306 |
+ |
|
| 307 |
+// UintptrArray adds an array of uintptr to the event. |
|
| 308 |
+func UintptrArray(name string, values []uintptr) FieldOpt {
|
|
| 309 |
+ inType := inTypeNull |
|
| 310 |
+ var writeItem func(*eventData, uintptr) |
|
| 311 |
+ switch unsafe.Sizeof(values[0]) {
|
|
| 312 |
+ case 4: |
|
| 313 |
+ inType = inTypeHexInt32 |
|
| 314 |
+ writeItem = func(ed *eventData, item uintptr) { ed.writeUint32(uint32(item)) }
|
|
| 315 |
+ case 8: |
|
| 316 |
+ inType = inTypeHexInt64 |
|
| 317 |
+ writeItem = func(ed *eventData, item uintptr) { ed.writeUint64(uint64(item)) }
|
|
| 318 |
+ default: |
|
| 319 |
+ panic("Unsupported uintptr size")
|
|
| 320 |
+ } |
|
| 321 |
+ |
|
| 322 |
+ return func(em *eventMetadata, ed *eventData) {
|
|
| 323 |
+ em.writeArray(name, inType, outTypeDefault, 0) |
|
| 324 |
+ ed.writeUint16(uint16(len(values))) |
|
| 325 |
+ for _, v := range values {
|
|
| 326 |
+ writeItem(ed, v) |
|
| 327 |
+ } |
|
| 328 |
+ } |
|
| 329 |
+} |
|
| 330 |
+ |
|
| 331 |
+// Float32Field adds a single float32 field to the event. |
|
| 332 |
+func Float32Field(name string, value float32) FieldOpt {
|
|
| 333 |
+ return func(em *eventMetadata, ed *eventData) {
|
|
| 334 |
+ em.writeField(name, inTypeFloat, outTypeDefault, 0) |
|
| 335 |
+ ed.writeUint32(math.Float32bits(value)) |
|
| 336 |
+ } |
|
| 337 |
+} |
|
| 338 |
+ |
|
| 339 |
+// Float32Array adds an array of float32 to the event. |
|
| 340 |
+func Float32Array(name string, values []float32) FieldOpt {
|
|
| 341 |
+ return func(em *eventMetadata, ed *eventData) {
|
|
| 342 |
+ em.writeArray(name, inTypeFloat, outTypeDefault, 0) |
|
| 343 |
+ ed.writeUint16(uint16(len(values))) |
|
| 344 |
+ for _, v := range values {
|
|
| 345 |
+ ed.writeUint32(math.Float32bits(v)) |
|
| 346 |
+ } |
|
| 347 |
+ } |
|
| 348 |
+} |
|
| 349 |
+ |
|
| 350 |
+// Float64Field adds a single float64 field to the event. |
|
| 351 |
+func Float64Field(name string, value float64) FieldOpt {
|
|
| 352 |
+ return func(em *eventMetadata, ed *eventData) {
|
|
| 353 |
+ em.writeField(name, inTypeDouble, outTypeDefault, 0) |
|
| 354 |
+ ed.writeUint64(math.Float64bits(value)) |
|
| 355 |
+ } |
|
| 356 |
+} |
|
| 357 |
+ |
|
| 358 |
+// Float64Array adds an array of float64 to the event. |
|
| 359 |
+func Float64Array(name string, values []float64) FieldOpt {
|
|
| 360 |
+ return func(em *eventMetadata, ed *eventData) {
|
|
| 361 |
+ em.writeArray(name, inTypeDouble, outTypeDefault, 0) |
|
| 362 |
+ ed.writeUint16(uint16(len(values))) |
|
| 363 |
+ for _, v := range values {
|
|
| 364 |
+ ed.writeUint64(math.Float64bits(v)) |
|
| 365 |
+ } |
|
| 366 |
+ } |
|
| 367 |
+} |
|
| 368 |
+ |
|
| 369 |
+// Struct adds a nested struct to the event, the FieldOpts in the opts argument |
|
| 370 |
+// are used to specify the fields of the struct. |
|
| 371 |
+func Struct(name string, opts ...FieldOpt) FieldOpt {
|
|
| 372 |
+ return func(em *eventMetadata, ed *eventData) {
|
|
| 373 |
+ em.writeStruct(name, uint8(len(opts)), 0) |
|
| 374 |
+ for _, opt := range opts {
|
|
| 375 |
+ opt(em, ed) |
|
| 376 |
+ } |
|
| 377 |
+ } |
|
| 378 |
+} |
| 0 | 379 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,279 @@ |
| 0 |
+package etw |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "bytes" |
|
| 4 |
+ "crypto/sha1" |
|
| 5 |
+ "encoding/binary" |
|
| 6 |
+ "encoding/hex" |
|
| 7 |
+ "fmt" |
|
| 8 |
+ "strings" |
|
| 9 |
+ "unicode/utf16" |
|
| 10 |
+ "unsafe" |
|
| 11 |
+ |
|
| 12 |
+ "golang.org/x/sys/windows" |
|
| 13 |
+) |
|
| 14 |
+ |
|
| 15 |
+// Provider represents an ETW event provider. It is identified by a provider |
|
| 16 |
+// name and ID (GUID), which should always have a 1:1 mapping to each other |
|
| 17 |
+// (e.g. don't use multiple provider names with the same ID, or vice versa). |
|
| 18 |
+type Provider struct {
|
|
| 19 |
+ ID *windows.GUID |
|
| 20 |
+ handle providerHandle |
|
| 21 |
+ metadata []byte |
|
| 22 |
+ callback EnableCallback |
|
| 23 |
+ index uint |
|
| 24 |
+ enabled bool |
|
| 25 |
+ level Level |
|
| 26 |
+ keywordAny uint64 |
|
| 27 |
+ keywordAll uint64 |
|
| 28 |
+} |
|
| 29 |
+ |
|
| 30 |
+// String returns the `provider`.ID as a string |
|
| 31 |
+func (provider *Provider) String() string {
|
|
| 32 |
+ data1 := make([]byte, 4) |
|
| 33 |
+ binary.BigEndian.PutUint32(data1, provider.ID.Data1) |
|
| 34 |
+ data2 := make([]byte, 2) |
|
| 35 |
+ binary.BigEndian.PutUint16(data2, provider.ID.Data2) |
|
| 36 |
+ data3 := make([]byte, 2) |
|
| 37 |
+ binary.BigEndian.PutUint16(data3, provider.ID.Data3) |
|
| 38 |
+ return fmt.Sprintf( |
|
| 39 |
+ "%s-%s-%s-%s-%s", |
|
| 40 |
+ hex.EncodeToString(data1), |
|
| 41 |
+ hex.EncodeToString(data2), |
|
| 42 |
+ hex.EncodeToString(data3), |
|
| 43 |
+ hex.EncodeToString(provider.ID.Data4[:2]), |
|
| 44 |
+ hex.EncodeToString(provider.ID.Data4[2:])) |
|
| 45 |
+} |
|
| 46 |
+ |
|
| 47 |
+type providerHandle windows.Handle |
|
| 48 |
+ |
|
| 49 |
+// ProviderState informs the provider EnableCallback what action is being |
|
| 50 |
+// performed. |
|
| 51 |
+type ProviderState uint32 |
|
| 52 |
+ |
|
| 53 |
+const ( |
|
| 54 |
+ // ProviderStateDisable indicates the provider is being disabled. |
|
| 55 |
+ ProviderStateDisable ProviderState = iota |
|
| 56 |
+ // ProviderStateEnable indicates the provider is being enabled. |
|
| 57 |
+ ProviderStateEnable |
|
| 58 |
+ // ProviderStateCaptureState indicates the provider is having its current |
|
| 59 |
+ // state snap-shotted. |
|
| 60 |
+ ProviderStateCaptureState |
|
| 61 |
+) |
|
| 62 |
+ |
|
| 63 |
+type eventInfoClass uint32 |
|
| 64 |
+ |
|
| 65 |
+const ( |
|
| 66 |
+ eventInfoClassProviderBinaryTrackInfo eventInfoClass = iota |
|
| 67 |
+ eventInfoClassProviderSetReserved1 |
|
| 68 |
+ eventInfoClassProviderSetTraits |
|
| 69 |
+ eventInfoClassProviderUseDescriptorType |
|
| 70 |
+) |
|
| 71 |
+ |
|
| 72 |
+// EnableCallback is the form of the callback function that receives provider |
|
| 73 |
+// enable/disable notifications from ETW. |
|
| 74 |
+type EnableCallback func(*windows.GUID, ProviderState, Level, uint64, uint64, uintptr) |
|
| 75 |
+ |
|
| 76 |
+func providerCallback(sourceID *windows.GUID, state ProviderState, level Level, matchAnyKeyword uint64, matchAllKeyword uint64, filterData uintptr, i uintptr) {
|
|
| 77 |
+ provider := providers.getProvider(uint(i)) |
|
| 78 |
+ |
|
| 79 |
+ switch state {
|
|
| 80 |
+ case ProviderStateDisable: |
|
| 81 |
+ provider.enabled = false |
|
| 82 |
+ case ProviderStateEnable: |
|
| 83 |
+ provider.enabled = true |
|
| 84 |
+ provider.level = level |
|
| 85 |
+ provider.keywordAny = matchAnyKeyword |
|
| 86 |
+ provider.keywordAll = matchAllKeyword |
|
| 87 |
+ } |
|
| 88 |
+ |
|
| 89 |
+ if provider.callback != nil {
|
|
| 90 |
+ provider.callback(sourceID, state, level, matchAnyKeyword, matchAllKeyword, filterData) |
|
| 91 |
+ } |
|
| 92 |
+} |
|
| 93 |
+ |
|
| 94 |
+// providerCallbackAdapter acts as the first-level callback from the C/ETW side |
|
| 95 |
+// for provider notifications. Because Go has trouble with callback arguments of |
|
| 96 |
+// different size, it has only pointer-sized arguments, which are then cast to |
|
| 97 |
+// the appropriate types when calling providerCallback. |
|
| 98 |
+func providerCallbackAdapter(sourceID *windows.GUID, state uintptr, level uintptr, matchAnyKeyword uintptr, matchAllKeyword uintptr, filterData uintptr, i uintptr) uintptr {
|
|
| 99 |
+ providerCallback(sourceID, ProviderState(state), Level(level), uint64(matchAnyKeyword), uint64(matchAllKeyword), filterData, i) |
|
| 100 |
+ return 0 |
|
| 101 |
+} |
|
| 102 |
+ |
|
| 103 |
+// providerIDFromName generates a provider ID based on the provider name. It |
|
| 104 |
+// uses the same algorithm as used by .NET's EventSource class, which is based |
|
| 105 |
+// on RFC 4122. More information on the algorithm can be found here: |
|
| 106 |
+// https://blogs.msdn.microsoft.com/dcook/2015/09/08/etw-provider-names-and-guids/ |
|
| 107 |
+// The algorithm is roughly: |
|
| 108 |
+// Hash = Sha1(namespace + arg.ToUpper().ToUtf16be()) |
|
| 109 |
+// Guid = Hash[0..15], with Hash[7] tweaked according to RFC 4122 |
|
| 110 |
+func providerIDFromName(name string) *windows.GUID {
|
|
| 111 |
+ buffer := sha1.New() |
|
| 112 |
+ |
|
| 113 |
+ namespace := []byte{0x48, 0x2C, 0x2D, 0xB2, 0xC3, 0x90, 0x47, 0xC8, 0x87, 0xF8, 0x1A, 0x15, 0xBF, 0xC1, 0x30, 0xFB}
|
|
| 114 |
+ buffer.Write(namespace) |
|
| 115 |
+ |
|
| 116 |
+ binary.Write(buffer, binary.BigEndian, utf16.Encode([]rune(strings.ToUpper(name)))) |
|
| 117 |
+ |
|
| 118 |
+ sum := buffer.Sum(nil) |
|
| 119 |
+ sum[7] = (sum[7] & 0xf) | 0x50 |
|
| 120 |
+ |
|
| 121 |
+ return &windows.GUID{
|
|
| 122 |
+ Data1: binary.LittleEndian.Uint32(sum[0:4]), |
|
| 123 |
+ Data2: binary.LittleEndian.Uint16(sum[4:6]), |
|
| 124 |
+ Data3: binary.LittleEndian.Uint16(sum[6:8]), |
|
| 125 |
+ Data4: [8]byte{sum[8], sum[9], sum[10], sum[11], sum[12], sum[13], sum[14], sum[15]},
|
|
| 126 |
+ } |
|
| 127 |
+} |
|
| 128 |
+ |
|
| 129 |
+// NewProvider creates and registers a new ETW provider. The provider ID is |
|
| 130 |
+// generated based on the provider name. |
|
| 131 |
+func NewProvider(name string, callback EnableCallback) (provider *Provider, err error) {
|
|
| 132 |
+ return NewProviderWithID(name, providerIDFromName(name), callback) |
|
| 133 |
+} |
|
| 134 |
+ |
|
| 135 |
+// NewProviderWithID creates and registers a new ETW provider, allowing the |
|
| 136 |
+// provider ID to be manually specified. This is most useful when there is an |
|
| 137 |
+// existing provider ID that must be used to conform to existing diagnostic |
|
| 138 |
+// infrastructure. |
|
| 139 |
+func NewProviderWithID(name string, id *windows.GUID, callback EnableCallback) (provider *Provider, err error) {
|
|
| 140 |
+ providerCallbackOnce.Do(func() {
|
|
| 141 |
+ globalProviderCallback = windows.NewCallback(providerCallbackAdapter) |
|
| 142 |
+ }) |
|
| 143 |
+ |
|
| 144 |
+ provider = providers.newProvider() |
|
| 145 |
+ defer func() {
|
|
| 146 |
+ if err != nil {
|
|
| 147 |
+ providers.removeProvider(provider) |
|
| 148 |
+ } |
|
| 149 |
+ }() |
|
| 150 |
+ provider.ID = id |
|
| 151 |
+ provider.callback = callback |
|
| 152 |
+ |
|
| 153 |
+ if err := eventRegister(provider.ID, globalProviderCallback, uintptr(provider.index), &provider.handle); err != nil {
|
|
| 154 |
+ return nil, err |
|
| 155 |
+ } |
|
| 156 |
+ |
|
| 157 |
+ metadata := &bytes.Buffer{}
|
|
| 158 |
+ binary.Write(metadata, binary.LittleEndian, uint16(0)) // Write empty size for buffer (to update later) |
|
| 159 |
+ metadata.WriteString(name) |
|
| 160 |
+ metadata.WriteByte(0) // Null terminator for name |
|
| 161 |
+ binary.LittleEndian.PutUint16(metadata.Bytes(), uint16(metadata.Len())) // Update the size at the beginning of the buffer |
|
| 162 |
+ provider.metadata = metadata.Bytes() |
|
| 163 |
+ |
|
| 164 |
+ if err := eventSetInformation( |
|
| 165 |
+ provider.handle, |
|
| 166 |
+ eventInfoClassProviderSetTraits, |
|
| 167 |
+ uintptr(unsafe.Pointer(&provider.metadata[0])), |
|
| 168 |
+ uint32(len(provider.metadata))); err != nil {
|
|
| 169 |
+ |
|
| 170 |
+ return nil, err |
|
| 171 |
+ } |
|
| 172 |
+ |
|
| 173 |
+ return provider, nil |
|
| 174 |
+} |
|
| 175 |
+ |
|
| 176 |
+// Close unregisters the provider. |
|
| 177 |
+func (provider *Provider) Close() error {
|
|
| 178 |
+ providers.removeProvider(provider) |
|
| 179 |
+ return eventUnregister(provider.handle) |
|
| 180 |
+} |
|
| 181 |
+ |
|
| 182 |
+// IsEnabled calls IsEnabledForLevelAndKeywords with LevelAlways and all |
|
| 183 |
+// keywords set. |
|
| 184 |
+func (provider *Provider) IsEnabled() bool {
|
|
| 185 |
+ return provider.IsEnabledForLevelAndKeywords(LevelAlways, ^uint64(0)) |
|
| 186 |
+} |
|
| 187 |
+ |
|
| 188 |
+// IsEnabledForLevel calls IsEnabledForLevelAndKeywords with the specified level |
|
| 189 |
+// and all keywords set. |
|
| 190 |
+func (provider *Provider) IsEnabledForLevel(level Level) bool {
|
|
| 191 |
+ return provider.IsEnabledForLevelAndKeywords(level, ^uint64(0)) |
|
| 192 |
+} |
|
| 193 |
+ |
|
| 194 |
+// IsEnabledForLevelAndKeywords allows event producer code to check if there are |
|
| 195 |
+// any event sessions that are interested in an event, based on the event level |
|
| 196 |
+// and keywords. Although this check happens automatically in the ETW |
|
| 197 |
+// infrastructure, it can be useful to check if an event will actually be |
|
| 198 |
+// consumed before doing expensive work to build the event data. |
|
| 199 |
+func (provider *Provider) IsEnabledForLevelAndKeywords(level Level, keywords uint64) bool {
|
|
| 200 |
+ if !provider.enabled {
|
|
| 201 |
+ return false |
|
| 202 |
+ } |
|
| 203 |
+ |
|
| 204 |
+ // ETW automatically sets the level to 255 if it is specified as 0, so we |
|
| 205 |
+ // don't need to worry about the level=0 (all events) case. |
|
| 206 |
+ if level > provider.level {
|
|
| 207 |
+ return false |
|
| 208 |
+ } |
|
| 209 |
+ |
|
| 210 |
+ if keywords != 0 && (keywords&provider.keywordAny == 0 || keywords&provider.keywordAll != provider.keywordAll) {
|
|
| 211 |
+ return false |
|
| 212 |
+ } |
|
| 213 |
+ |
|
| 214 |
+ return true |
|
| 215 |
+} |
|
| 216 |
+ |
|
| 217 |
+// WriteEvent writes a single ETW event from the provider. The event is |
|
| 218 |
+// constructed based on the EventOpt and FieldOpt values that are passed as |
|
| 219 |
+// opts. |
|
| 220 |
+func (provider *Provider) WriteEvent(name string, eventOpts []EventOpt, fieldOpts []FieldOpt) error {
|
|
| 221 |
+ options := eventOptions{descriptor: newEventDescriptor()}
|
|
| 222 |
+ em := &eventMetadata{}
|
|
| 223 |
+ ed := &eventData{}
|
|
| 224 |
+ |
|
| 225 |
+ // We need to evaluate the EventOpts first since they might change tags, and |
|
| 226 |
+ // we write out the tags before evaluating FieldOpts. |
|
| 227 |
+ for _, opt := range eventOpts {
|
|
| 228 |
+ opt(&options) |
|
| 229 |
+ } |
|
| 230 |
+ |
|
| 231 |
+ if !provider.IsEnabledForLevelAndKeywords(options.descriptor.level, options.descriptor.keyword) {
|
|
| 232 |
+ return nil |
|
| 233 |
+ } |
|
| 234 |
+ |
|
| 235 |
+ em.writeEventHeader(name, options.tags) |
|
| 236 |
+ |
|
| 237 |
+ for _, opt := range fieldOpts {
|
|
| 238 |
+ opt(em, ed) |
|
| 239 |
+ } |
|
| 240 |
+ |
|
| 241 |
+ // Don't pass a data blob if there is no event data. There will always be |
|
| 242 |
+ // event metadata (e.g. for the name) so we don't need to do this check for |
|
| 243 |
+ // the metadata. |
|
| 244 |
+ dataBlobs := [][]byte{}
|
|
| 245 |
+ if len(ed.bytes()) > 0 {
|
|
| 246 |
+ dataBlobs = [][]byte{ed.bytes()}
|
|
| 247 |
+ } |
|
| 248 |
+ |
|
| 249 |
+ return provider.writeEventRaw(options.descriptor, nil, nil, [][]byte{em.bytes()}, dataBlobs)
|
|
| 250 |
+} |
|
| 251 |
+ |
|
| 252 |
+// writeEventRaw writes a single ETW event from the provider. This function is |
|
| 253 |
+// less abstracted than WriteEvent, and presents a fairly direct interface to |
|
| 254 |
+// the event writing functionality. It expects a series of event metadata and |
|
| 255 |
+// event data blobs to be passed in, which must conform to the TraceLogging |
|
| 256 |
+// schema. The functions on EventMetadata and EventData can help with creating |
|
| 257 |
+// these blobs. The blobs of each type are effectively concatenated together by |
|
| 258 |
+// the ETW infrastructure. |
|
| 259 |
+func (provider *Provider) writeEventRaw( |
|
| 260 |
+ descriptor *eventDescriptor, |
|
| 261 |
+ activityID *windows.GUID, |
|
| 262 |
+ relatedActivityID *windows.GUID, |
|
| 263 |
+ metadataBlobs [][]byte, |
|
| 264 |
+ dataBlobs [][]byte) error {
|
|
| 265 |
+ |
|
| 266 |
+ dataDescriptorCount := uint32(1 + len(metadataBlobs) + len(dataBlobs)) |
|
| 267 |
+ dataDescriptors := make([]eventDataDescriptor, 0, dataDescriptorCount) |
|
| 268 |
+ |
|
| 269 |
+ dataDescriptors = append(dataDescriptors, newEventDataDescriptor(eventDataDescriptorTypeProviderMetadata, provider.metadata)) |
|
| 270 |
+ for _, blob := range metadataBlobs {
|
|
| 271 |
+ dataDescriptors = append(dataDescriptors, newEventDataDescriptor(eventDataDescriptorTypeEventMetadata, blob)) |
|
| 272 |
+ } |
|
| 273 |
+ for _, blob := range dataBlobs {
|
|
| 274 |
+ dataDescriptors = append(dataDescriptors, newEventDataDescriptor(eventDataDescriptorTypeUserData, blob)) |
|
| 275 |
+ } |
|
| 276 |
+ |
|
| 277 |
+ return eventWriteTransfer(provider.handle, descriptor, activityID, relatedActivityID, dataDescriptorCount, &dataDescriptors[0]) |
|
| 278 |
+} |
| 0 | 279 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,52 @@ |
| 0 |
+package etw |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "sync" |
|
| 4 |
+) |
|
| 5 |
+ |
|
| 6 |
+// Because the provider callback function needs to be able to access the |
|
| 7 |
+// provider data when it is invoked by ETW, we need to keep provider data stored |
|
| 8 |
+// in a global map based on an index. The index is passed as the callback |
|
| 9 |
+// context to ETW. |
|
| 10 |
+type providerMap struct {
|
|
| 11 |
+ m map[uint]*Provider |
|
| 12 |
+ i uint |
|
| 13 |
+ lock sync.Mutex |
|
| 14 |
+ once sync.Once |
|
| 15 |
+} |
|
| 16 |
+ |
|
| 17 |
+var providers = providerMap{
|
|
| 18 |
+ m: make(map[uint]*Provider), |
|
| 19 |
+} |
|
| 20 |
+ |
|
| 21 |
+func (p *providerMap) newProvider() *Provider {
|
|
| 22 |
+ p.lock.Lock() |
|
| 23 |
+ defer p.lock.Unlock() |
|
| 24 |
+ |
|
| 25 |
+ i := p.i |
|
| 26 |
+ p.i++ |
|
| 27 |
+ |
|
| 28 |
+ provider := &Provider{
|
|
| 29 |
+ index: i, |
|
| 30 |
+ } |
|
| 31 |
+ |
|
| 32 |
+ p.m[i] = provider |
|
| 33 |
+ return provider |
|
| 34 |
+} |
|
| 35 |
+ |
|
| 36 |
+func (p *providerMap) removeProvider(provider *Provider) {
|
|
| 37 |
+ p.lock.Lock() |
|
| 38 |
+ defer p.lock.Unlock() |
|
| 39 |
+ |
|
| 40 |
+ delete(p.m, provider.index) |
|
| 41 |
+} |
|
| 42 |
+ |
|
| 43 |
+func (p *providerMap) getProvider(index uint) *Provider {
|
|
| 44 |
+ p.lock.Lock() |
|
| 45 |
+ defer p.lock.Unlock() |
|
| 46 |
+ |
|
| 47 |
+ return p.m[index] |
|
| 48 |
+} |
|
| 49 |
+ |
|
| 50 |
+var providerCallbackOnce sync.Once |
|
| 51 |
+var globalProviderCallback uintptr |
| 0 | 52 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,16 @@ |
| 0 |
+// +build 386 arm |
|
| 1 |
+ |
|
| 2 |
+package etw |
|
| 3 |
+ |
|
| 4 |
+import ( |
|
| 5 |
+ "unsafe" |
|
| 6 |
+) |
|
| 7 |
+ |
|
| 8 |
+// byteptr64 defines a struct containing a pointer. The struct is guaranteed to |
|
| 9 |
+// be 64 bits, regardless of the actual size of a pointer on the platform. This |
|
| 10 |
+// is intended for use with certain Windows APIs that expect a pointer as a |
|
| 11 |
+// ULONGLONG. |
|
| 12 |
+type ptr64 struct {
|
|
| 13 |
+ ptr unsafe.Pointer |
|
| 14 |
+ _ uint32 |
|
| 15 |
+} |
| 0 | 16 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,15 @@ |
| 0 |
+// +build amd64 arm64 |
|
| 1 |
+ |
|
| 2 |
+package etw |
|
| 3 |
+ |
|
| 4 |
+import ( |
|
| 5 |
+ "unsafe" |
|
| 6 |
+) |
|
| 7 |
+ |
|
| 8 |
+// byteptr64 defines a struct containing a pointer. The struct is guaranteed to |
|
| 9 |
+// be 64 bits, regardless of the actual size of a pointer on the platform. This |
|
| 10 |
+// is intended for use with certain Windows APIs that expect a pointer as a |
|
| 11 |
+// ULONGLONG. |
|
| 12 |
+type ptr64 struct {
|
|
| 13 |
+ ptr unsafe.Pointer |
|
| 14 |
+} |
| 0 | 15 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,78 @@ |
| 0 |
+// Code generated by 'go generate'; DO NOT EDIT. |
|
| 1 |
+ |
|
| 2 |
+package etw |
|
| 3 |
+ |
|
| 4 |
+import ( |
|
| 5 |
+ "syscall" |
|
| 6 |
+ "unsafe" |
|
| 7 |
+ |
|
| 8 |
+ "golang.org/x/sys/windows" |
|
| 9 |
+) |
|
| 10 |
+ |
|
| 11 |
+var _ unsafe.Pointer |
|
| 12 |
+ |
|
| 13 |
+// Do the interface allocations only once for common |
|
| 14 |
+// Errno values. |
|
| 15 |
+const ( |
|
| 16 |
+ errnoERROR_IO_PENDING = 997 |
|
| 17 |
+) |
|
| 18 |
+ |
|
| 19 |
+var ( |
|
| 20 |
+ errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) |
|
| 21 |
+) |
|
| 22 |
+ |
|
| 23 |
+// errnoErr returns common boxed Errno values, to prevent |
|
| 24 |
+// allocations at runtime. |
|
| 25 |
+func errnoErr(e syscall.Errno) error {
|
|
| 26 |
+ switch e {
|
|
| 27 |
+ case 0: |
|
| 28 |
+ return nil |
|
| 29 |
+ case errnoERROR_IO_PENDING: |
|
| 30 |
+ return errERROR_IO_PENDING |
|
| 31 |
+ } |
|
| 32 |
+ // TODO: add more here, after collecting data on the common |
|
| 33 |
+ // error values see on Windows. (perhaps when running |
|
| 34 |
+ // all.bat?) |
|
| 35 |
+ return e |
|
| 36 |
+} |
|
| 37 |
+ |
|
| 38 |
+var ( |
|
| 39 |
+ modadvapi32 = windows.NewLazySystemDLL("advapi32.dll")
|
|
| 40 |
+ |
|
| 41 |
+ procEventRegister = modadvapi32.NewProc("EventRegister")
|
|
| 42 |
+ procEventUnregister = modadvapi32.NewProc("EventUnregister")
|
|
| 43 |
+ procEventWriteTransfer = modadvapi32.NewProc("EventWriteTransfer")
|
|
| 44 |
+ procEventSetInformation = modadvapi32.NewProc("EventSetInformation")
|
|
| 45 |
+) |
|
| 46 |
+ |
|
| 47 |
+func eventRegister(providerId *windows.GUID, callback uintptr, callbackContext uintptr, providerHandle *providerHandle) (win32err error) {
|
|
| 48 |
+ r0, _, _ := syscall.Syscall6(procEventRegister.Addr(), 4, uintptr(unsafe.Pointer(providerId)), uintptr(callback), uintptr(callbackContext), uintptr(unsafe.Pointer(providerHandle)), 0, 0) |
|
| 49 |
+ if r0 != 0 {
|
|
| 50 |
+ win32err = syscall.Errno(r0) |
|
| 51 |
+ } |
|
| 52 |
+ return |
|
| 53 |
+} |
|
| 54 |
+ |
|
| 55 |
+func eventUnregister(providerHandle providerHandle) (win32err error) {
|
|
| 56 |
+ r0, _, _ := syscall.Syscall(procEventUnregister.Addr(), 1, uintptr(providerHandle), 0, 0) |
|
| 57 |
+ if r0 != 0 {
|
|
| 58 |
+ win32err = syscall.Errno(r0) |
|
| 59 |
+ } |
|
| 60 |
+ return |
|
| 61 |
+} |
|
| 62 |
+ |
|
| 63 |
+func eventWriteTransfer(providerHandle providerHandle, descriptor *eventDescriptor, activityID *windows.GUID, relatedActivityID *windows.GUID, dataDescriptorCount uint32, dataDescriptors *eventDataDescriptor) (win32err error) {
|
|
| 64 |
+ r0, _, _ := syscall.Syscall6(procEventWriteTransfer.Addr(), 6, uintptr(providerHandle), uintptr(unsafe.Pointer(descriptor)), uintptr(unsafe.Pointer(activityID)), uintptr(unsafe.Pointer(relatedActivityID)), uintptr(dataDescriptorCount), uintptr(unsafe.Pointer(dataDescriptors))) |
|
| 65 |
+ if r0 != 0 {
|
|
| 66 |
+ win32err = syscall.Errno(r0) |
|
| 67 |
+ } |
|
| 68 |
+ return |
|
| 69 |
+} |
|
| 70 |
+ |
|
| 71 |
+func eventSetInformation(providerHandle providerHandle, class eventInfoClass, information uintptr, length uint32) (win32err error) {
|
|
| 72 |
+ r0, _, _ := syscall.Syscall6(procEventSetInformation.Addr(), 4, uintptr(providerHandle), uintptr(class), uintptr(information), uintptr(length), 0, 0) |
|
| 73 |
+ if r0 != 0 {
|
|
| 74 |
+ win32err = syscall.Errno(r0) |
|
| 75 |
+ } |
|
| 76 |
+ return |
|
| 77 |
+} |
| ... | ... |
@@ -4,26 +4,31 @@ import ( |
| 4 | 4 |
"fmt" |
| 5 | 5 |
"reflect" |
| 6 | 6 |
|
| 7 |
- "github.com/Microsoft/go-winio/internal/etw" |
|
| 7 |
+ "github.com/Microsoft/go-winio/pkg/etw" |
|
| 8 | 8 |
"github.com/sirupsen/logrus" |
| 9 | 9 |
) |
| 10 | 10 |
|
| 11 | 11 |
// Hook is a Logrus hook which logs received events to ETW. |
| 12 | 12 |
type Hook struct {
|
| 13 |
- provider *etw.Provider |
|
| 13 |
+ provider *etw.Provider |
|
| 14 |
+ closeProvider bool |
|
| 14 | 15 |
} |
| 15 | 16 |
|
| 16 |
-// NewHook registers a new ETW provider and returns a hook to log from it. |
|
| 17 |
+// NewHook registers a new ETW provider and returns a hook to log from it. The |
|
| 18 |
+// provider will be closed when the hook is closed. |
|
| 17 | 19 |
func NewHook(providerName string) (*Hook, error) {
|
| 18 |
- hook := Hook{}
|
|
| 19 |
- |
|
| 20 | 20 |
provider, err := etw.NewProvider(providerName, nil) |
| 21 | 21 |
if err != nil {
|
| 22 | 22 |
return nil, err |
| 23 | 23 |
} |
| 24 |
- hook.provider = provider |
|
| 25 | 24 |
|
| 26 |
- return &hook, nil |
|
| 25 |
+ return &Hook{provider, true}, nil
|
|
| 26 |
+} |
|
| 27 |
+ |
|
| 28 |
+// NewHookFromProvider creates a new hook based on an existing ETW provider. The |
|
| 29 |
+// provider will not be closed when the hook is closed. |
|
| 30 |
+func NewHookFromProvider(provider *etw.Provider) (*Hook, error) {
|
|
| 31 |
+ return &Hook{provider, false}, nil
|
|
| 27 | 32 |
} |
| 28 | 33 |
|
| 29 | 34 |
// Levels returns the set of levels that this hook wants to receive log entries |
| ... | ... |
@@ -40,9 +45,22 @@ func (h *Hook) Levels() []logrus.Level {
|
| 40 | 40 |
} |
| 41 | 41 |
} |
| 42 | 42 |
|
| 43 |
+var logrusToETWLevelMap = map[logrus.Level]etw.Level{
|
|
| 44 |
+ logrus.PanicLevel: etw.LevelAlways, |
|
| 45 |
+ logrus.FatalLevel: etw.LevelCritical, |
|
| 46 |
+ logrus.ErrorLevel: etw.LevelError, |
|
| 47 |
+ logrus.WarnLevel: etw.LevelWarning, |
|
| 48 |
+ logrus.InfoLevel: etw.LevelInfo, |
|
| 49 |
+ logrus.DebugLevel: etw.LevelVerbose, |
|
| 50 |
+ logrus.TraceLevel: etw.LevelVerbose, |
|
| 51 |
+} |
|
| 52 |
+ |
|
| 43 | 53 |
// Fire receives each Logrus entry as it is logged, and logs it to ETW. |
| 44 | 54 |
func (h *Hook) Fire(e *logrus.Entry) error {
|
| 45 |
- level := etw.Level(e.Level) |
|
| 55 |
+ // Logrus defines more levels than ETW typically uses, but analysis is |
|
| 56 |
+ // easiest when using a consistent set of levels across ETW providers, so we |
|
| 57 |
+ // map the Logrus levels to ETW levels. |
|
| 58 |
+ level := logrusToETWLevelMap[e.Level] |
|
| 46 | 59 |
if !h.provider.IsEnabledForLevel(level) {
|
| 47 | 60 |
return nil |
| 48 | 61 |
} |
| ... | ... |
@@ -56,9 +74,6 @@ func (h *Hook) Fire(e *logrus.Entry) error {
|
| 56 | 56 |
fields = append(fields, getFieldOpt(k, v)) |
| 57 | 57 |
} |
| 58 | 58 |
|
| 59 |
- // We could try to map Logrus levels to ETW levels, but we would lose some |
|
| 60 |
- // fidelity as there are fewer ETW levels. So instead we use the level |
|
| 61 |
- // directly. |
|
| 62 | 59 |
return h.provider.WriteEvent( |
| 63 | 60 |
"LogrusEntry", |
| 64 | 61 |
etw.WithEventOpts(etw.WithLevel(level)), |
| ... | ... |
@@ -186,7 +201,12 @@ func getFieldOpt(k string, v interface{}) etw.FieldOpt {
|
| 186 | 186 |
return etw.StringField(k, fmt.Sprintf("(Unsupported: %T) %v", v, v))
|
| 187 | 187 |
} |
| 188 | 188 |
|
| 189 |
-// Close cleans up the hook and closes the ETW provider. |
|
| 189 |
+// Close cleans up the hook and closes the ETW provider. If the provder was |
|
| 190 |
+// registered by etwlogrus, it will be closed as part of `Close`. If the |
|
| 191 |
+// provider was passed in, it will not be closed. |
|
| 190 | 192 |
func (h *Hook) Close() error {
|
| 191 |
- return h.provider.Close() |
|
| 193 |
+ if h.closeProvider {
|
|
| 194 |
+ return h.provider.Close() |
|
| 195 |
+ } |
|
| 196 |
+ return nil |
|
| 192 | 197 |
} |
| 193 | 198 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,159 @@ |
| 0 |
+package security |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "os" |
|
| 4 |
+ "syscall" |
|
| 5 |
+ "unsafe" |
|
| 6 |
+ |
|
| 7 |
+ "github.com/pkg/errors" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+type ( |
|
| 11 |
+ accessMask uint32 |
|
| 12 |
+ accessMode uint32 |
|
| 13 |
+ desiredAccess uint32 |
|
| 14 |
+ inheritMode uint32 |
|
| 15 |
+ objectType uint32 |
|
| 16 |
+ shareMode uint32 |
|
| 17 |
+ securityInformation uint32 |
|
| 18 |
+ trusteeForm uint32 |
|
| 19 |
+ trusteeType uint32 |
|
| 20 |
+ |
|
| 21 |
+ explicitAccess struct {
|
|
| 22 |
+ accessPermissions accessMask |
|
| 23 |
+ accessMode accessMode |
|
| 24 |
+ inheritance inheritMode |
|
| 25 |
+ trustee trustee |
|
| 26 |
+ } |
|
| 27 |
+ |
|
| 28 |
+ trustee struct {
|
|
| 29 |
+ multipleTrustee *trustee |
|
| 30 |
+ multipleTrusteeOperation int32 |
|
| 31 |
+ trusteeForm trusteeForm |
|
| 32 |
+ trusteeType trusteeType |
|
| 33 |
+ name uintptr |
|
| 34 |
+ } |
|
| 35 |
+) |
|
| 36 |
+ |
|
| 37 |
+const ( |
|
| 38 |
+ accessMaskDesiredPermission accessMask = 1 << 31 // GENERIC_READ |
|
| 39 |
+ |
|
| 40 |
+ accessModeGrant accessMode = 1 |
|
| 41 |
+ |
|
| 42 |
+ desiredAccessReadControl desiredAccess = 0x20000 |
|
| 43 |
+ desiredAccessWriteDac desiredAccess = 0x40000 |
|
| 44 |
+ |
|
| 45 |
+ gvmga = "GrantVmGroupAccess:" |
|
| 46 |
+ |
|
| 47 |
+ inheritModeNoInheritance inheritMode = 0x0 |
|
| 48 |
+ inheritModeSubContainersAndObjectsInherit inheritMode = 0x3 |
|
| 49 |
+ |
|
| 50 |
+ objectTypeFileObject objectType = 0x1 |
|
| 51 |
+ |
|
| 52 |
+ securityInformationDACL securityInformation = 0x4 |
|
| 53 |
+ |
|
| 54 |
+ shareModeRead shareMode = 0x1 |
|
| 55 |
+ shareModeWrite shareMode = 0x2 |
|
| 56 |
+ |
|
| 57 |
+ sidVmGroup = "S-1-5-83-0" |
|
| 58 |
+ |
|
| 59 |
+ trusteeFormIsSid trusteeForm = 0 |
|
| 60 |
+ |
|
| 61 |
+ trusteeTypeWellKnownGroup trusteeType = 5 |
|
| 62 |
+) |
|
| 63 |
+ |
|
| 64 |
+// GrantVMGroupAccess sets the DACL for a specified file or directory to |
|
| 65 |
+// include Grant ACE entries for the VM Group SID. This is a golang re- |
|
| 66 |
+// implementation of the same function in vmcompute, just not exported in |
|
| 67 |
+// RS5. Which kind of sucks. Sucks a lot :/ |
|
| 68 |
+func GrantVmGroupAccess(name string) error {
|
|
| 69 |
+ // Stat (to determine if `name` is a directory). |
|
| 70 |
+ s, err := os.Stat(name) |
|
| 71 |
+ if err != nil {
|
|
| 72 |
+ return errors.Wrapf(err, "%s os.Stat %s", gvmga, name) |
|
| 73 |
+ } |
|
| 74 |
+ |
|
| 75 |
+ // Get a handle to the file/directory. Must defer Close on success. |
|
| 76 |
+ fd, err := createFile(name, s.IsDir()) |
|
| 77 |
+ if err != nil {
|
|
| 78 |
+ return err // Already wrapped |
|
| 79 |
+ } |
|
| 80 |
+ defer syscall.CloseHandle(fd) |
|
| 81 |
+ |
|
| 82 |
+ // Get the current DACL and Security Descriptor. Must defer LocalFree on success. |
|
| 83 |
+ ot := objectTypeFileObject |
|
| 84 |
+ si := securityInformationDACL |
|
| 85 |
+ sd := uintptr(0) |
|
| 86 |
+ origDACL := uintptr(0) |
|
| 87 |
+ if err := getSecurityInfo(fd, uint32(ot), uint32(si), nil, nil, &origDACL, nil, &sd); err != nil {
|
|
| 88 |
+ return errors.Wrapf(err, "%s GetSecurityInfo %s", gvmga, name) |
|
| 89 |
+ } |
|
| 90 |
+ defer syscall.LocalFree((syscall.Handle)(unsafe.Pointer(sd))) |
|
| 91 |
+ |
|
| 92 |
+ // Generate a new DACL which is the current DACL with the required ACEs added. |
|
| 93 |
+ // Must defer LocalFree on success. |
|
| 94 |
+ newDACL, err := generateDACLWithAcesAdded(name, s.IsDir(), origDACL) |
|
| 95 |
+ if err != nil {
|
|
| 96 |
+ return err // Already wrapped |
|
| 97 |
+ } |
|
| 98 |
+ defer syscall.LocalFree((syscall.Handle)(unsafe.Pointer(newDACL))) |
|
| 99 |
+ |
|
| 100 |
+ // And finally use SetSecurityInfo to apply the updated DACL. |
|
| 101 |
+ if err := setSecurityInfo(fd, uint32(ot), uint32(si), uintptr(0), uintptr(0), newDACL, uintptr(0)); err != nil {
|
|
| 102 |
+ return errors.Wrapf(err, "%s SetSecurityInfo %s", gvmga, name) |
|
| 103 |
+ } |
|
| 104 |
+ |
|
| 105 |
+ return nil |
|
| 106 |
+} |
|
| 107 |
+ |
|
| 108 |
+// createFile is a helper function to call [Nt]CreateFile to get a handle to |
|
| 109 |
+// the file or directory. |
|
| 110 |
+func createFile(name string, isDir bool) (syscall.Handle, error) {
|
|
| 111 |
+ namep := syscall.StringToUTF16(name) |
|
| 112 |
+ da := uint32(desiredAccessReadControl | desiredAccessWriteDac) |
|
| 113 |
+ sm := uint32(shareModeRead | shareModeWrite) |
|
| 114 |
+ fa := uint32(syscall.FILE_ATTRIBUTE_NORMAL) |
|
| 115 |
+ if isDir {
|
|
| 116 |
+ fa = uint32(fa | syscall.FILE_FLAG_BACKUP_SEMANTICS) |
|
| 117 |
+ } |
|
| 118 |
+ fd, err := syscall.CreateFile(&namep[0], da, sm, nil, syscall.OPEN_EXISTING, fa, 0) |
|
| 119 |
+ if err != nil {
|
|
| 120 |
+ return 0, errors.Wrapf(err, "%s syscall.CreateFile %s", gvmga, name) |
|
| 121 |
+ } |
|
| 122 |
+ return fd, nil |
|
| 123 |
+} |
|
| 124 |
+ |
|
| 125 |
+// generateDACLWithAcesAdded generates a new DACL with the two needed ACEs added. |
|
| 126 |
+// The caller is responsible for LocalFree of the returned DACL on success. |
|
| 127 |
+func generateDACLWithAcesAdded(name string, isDir bool, origDACL uintptr) (uintptr, error) {
|
|
| 128 |
+ // Generate pointers to the SIDs based on the string SIDs |
|
| 129 |
+ sid, err := syscall.StringToSid(sidVmGroup) |
|
| 130 |
+ if err != nil {
|
|
| 131 |
+ return 0, errors.Wrapf(err, "%s syscall.StringToSid %s %s", gvmga, name, sidVmGroup) |
|
| 132 |
+ } |
|
| 133 |
+ |
|
| 134 |
+ inheritance := inheritModeNoInheritance |
|
| 135 |
+ if isDir {
|
|
| 136 |
+ inheritance = inheritModeSubContainersAndObjectsInherit |
|
| 137 |
+ } |
|
| 138 |
+ |
|
| 139 |
+ eaArray := []explicitAccess{
|
|
| 140 |
+ explicitAccess{
|
|
| 141 |
+ accessPermissions: accessMaskDesiredPermission, |
|
| 142 |
+ accessMode: accessModeGrant, |
|
| 143 |
+ inheritance: inheritance, |
|
| 144 |
+ trustee: trustee{
|
|
| 145 |
+ trusteeForm: trusteeFormIsSid, |
|
| 146 |
+ trusteeType: trusteeTypeWellKnownGroup, |
|
| 147 |
+ name: uintptr(unsafe.Pointer(sid)), |
|
| 148 |
+ }, |
|
| 149 |
+ }, |
|
| 150 |
+ } |
|
| 151 |
+ |
|
| 152 |
+ modifiedDACL := uintptr(0) |
|
| 153 |
+ if err := setEntriesInAcl(uintptr(uint32(1)), uintptr(unsafe.Pointer(&eaArray[0])), origDACL, &modifiedDACL); err != nil {
|
|
| 154 |
+ return 0, errors.Wrapf(err, "%s SetEntriesInAcl %s", gvmga, name) |
|
| 155 |
+ } |
|
| 156 |
+ |
|
| 157 |
+ return modifiedDACL, nil |
|
| 158 |
+} |
| 0 | 159 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,7 @@ |
| 0 |
+package security |
|
| 1 |
+ |
|
| 2 |
+//go:generate go run mksyscall_windows.go -output zsyscall_windows.go syscall_windows.go |
|
| 3 |
+ |
|
| 4 |
+//sys getSecurityInfo(handle syscall.Handle, objectType uint32, si uint32, ppsidOwner **uintptr, ppsidGroup **uintptr, ppDacl *uintptr, ppSacl *uintptr, ppSecurityDescriptor *uintptr) (err error) [failretval!=0] = advapi32.GetSecurityInfo |
|
| 5 |
+//sys setSecurityInfo(handle syscall.Handle, objectType uint32, si uint32, psidOwner uintptr, psidGroup uintptr, pDacl uintptr, pSacl uintptr) (err error) [failretval!=0] = advapi32.SetSecurityInfo |
|
| 6 |
+//sys setEntriesInAcl(count uintptr, pListOfEEs uintptr, oldAcl uintptr, newAcl *uintptr) (err error) [failretval!=0] = advapi32.SetEntriesInAclW |
| 0 | 7 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,81 @@ |
| 0 |
+// Code generated mksyscall_windows.exe DO NOT EDIT |
|
| 1 |
+ |
|
| 2 |
+package security |
|
| 3 |
+ |
|
| 4 |
+import ( |
|
| 5 |
+ "syscall" |
|
| 6 |
+ "unsafe" |
|
| 7 |
+ |
|
| 8 |
+ "golang.org/x/sys/windows" |
|
| 9 |
+) |
|
| 10 |
+ |
|
| 11 |
+var _ unsafe.Pointer |
|
| 12 |
+ |
|
| 13 |
+// Do the interface allocations only once for common |
|
| 14 |
+// Errno values. |
|
| 15 |
+const ( |
|
| 16 |
+ errnoERROR_IO_PENDING = 997 |
|
| 17 |
+) |
|
| 18 |
+ |
|
| 19 |
+var ( |
|
| 20 |
+ errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) |
|
| 21 |
+) |
|
| 22 |
+ |
|
| 23 |
+// errnoErr returns common boxed Errno values, to prevent |
|
| 24 |
+// allocations at runtime. |
|
| 25 |
+func errnoErr(e syscall.Errno) error {
|
|
| 26 |
+ switch e {
|
|
| 27 |
+ case 0: |
|
| 28 |
+ return nil |
|
| 29 |
+ case errnoERROR_IO_PENDING: |
|
| 30 |
+ return errERROR_IO_PENDING |
|
| 31 |
+ } |
|
| 32 |
+ // TODO: add more here, after collecting data on the common |
|
| 33 |
+ // error values see on Windows. (perhaps when running |
|
| 34 |
+ // all.bat?) |
|
| 35 |
+ return e |
|
| 36 |
+} |
|
| 37 |
+ |
|
| 38 |
+var ( |
|
| 39 |
+ modadvapi32 = windows.NewLazySystemDLL("advapi32.dll")
|
|
| 40 |
+ |
|
| 41 |
+ procGetSecurityInfo = modadvapi32.NewProc("GetSecurityInfo")
|
|
| 42 |
+ procSetSecurityInfo = modadvapi32.NewProc("SetSecurityInfo")
|
|
| 43 |
+ procSetEntriesInAclW = modadvapi32.NewProc("SetEntriesInAclW")
|
|
| 44 |
+) |
|
| 45 |
+ |
|
| 46 |
+func getSecurityInfo(handle syscall.Handle, objectType uint32, si uint32, ppsidOwner **uintptr, ppsidGroup **uintptr, ppDacl *uintptr, ppSacl *uintptr, ppSecurityDescriptor *uintptr) (err error) {
|
|
| 47 |
+ r1, _, e1 := syscall.Syscall9(procGetSecurityInfo.Addr(), 8, uintptr(handle), uintptr(objectType), uintptr(si), uintptr(unsafe.Pointer(ppsidOwner)), uintptr(unsafe.Pointer(ppsidGroup)), uintptr(unsafe.Pointer(ppDacl)), uintptr(unsafe.Pointer(ppSacl)), uintptr(unsafe.Pointer(ppSecurityDescriptor)), 0) |
|
| 48 |
+ if r1 != 0 {
|
|
| 49 |
+ if e1 != 0 {
|
|
| 50 |
+ err = errnoErr(e1) |
|
| 51 |
+ } else {
|
|
| 52 |
+ err = syscall.EINVAL |
|
| 53 |
+ } |
|
| 54 |
+ } |
|
| 55 |
+ return |
|
| 56 |
+} |
|
| 57 |
+ |
|
| 58 |
+func setSecurityInfo(handle syscall.Handle, objectType uint32, si uint32, psidOwner uintptr, psidGroup uintptr, pDacl uintptr, pSacl uintptr) (err error) {
|
|
| 59 |
+ r1, _, e1 := syscall.Syscall9(procSetSecurityInfo.Addr(), 7, uintptr(handle), uintptr(objectType), uintptr(si), uintptr(psidOwner), uintptr(psidGroup), uintptr(pDacl), uintptr(pSacl), 0, 0) |
|
| 60 |
+ if r1 != 0 {
|
|
| 61 |
+ if e1 != 0 {
|
|
| 62 |
+ err = errnoErr(e1) |
|
| 63 |
+ } else {
|
|
| 64 |
+ err = syscall.EINVAL |
|
| 65 |
+ } |
|
| 66 |
+ } |
|
| 67 |
+ return |
|
| 68 |
+} |
|
| 69 |
+ |
|
| 70 |
+func setEntriesInAcl(count uintptr, pListOfEEs uintptr, oldAcl uintptr, newAcl *uintptr) (err error) {
|
|
| 71 |
+ r1, _, e1 := syscall.Syscall6(procSetEntriesInAclW.Addr(), 4, uintptr(count), uintptr(pListOfEEs), uintptr(oldAcl), uintptr(unsafe.Pointer(newAcl)), 0, 0) |
|
| 72 |
+ if r1 != 0 {
|
|
| 73 |
+ if e1 != 0 {
|
|
| 74 |
+ err = errnoErr(e1) |
|
| 75 |
+ } else {
|
|
| 76 |
+ err = syscall.EINVAL |
|
| 77 |
+ } |
|
| 78 |
+ } |
|
| 79 |
+ return |
|
| 80 |
+} |