github.com/Serizao/go-winio@v0.0.0-20230906082528-f02f7f4ad6e8/pkg/etw/eventmetadata.go (about) 1 //go:build windows 2 3 package etw 4 5 import ( 6 "bytes" 7 "encoding/binary" 8 ) 9 10 // inType indicates the type of data contained in the ETW event. 11 type inType byte 12 13 // Various inType definitions for TraceLogging. These must match the definitions 14 // found in TraceLoggingProvider.h in the Windows SDK. 15 // 16 //nolint:deadcode,varcheck // keep unused constants for potential future use 17 const ( 18 inTypeNull inType = iota 19 inTypeUnicodeString 20 inTypeANSIString 21 inTypeInt8 22 inTypeUint8 23 inTypeInt16 24 inTypeUint16 25 inTypeInt32 26 inTypeUint32 27 inTypeInt64 28 inTypeUint64 29 inTypeFloat 30 inTypeDouble 31 inTypeBool32 32 inTypeBinary 33 inTypeGUID 34 inTypePointerUnsupported 35 inTypeFileTime 36 inTypeSystemTime 37 inTypeSID 38 inTypeHexInt32 39 inTypeHexInt64 40 inTypeCountedString 41 inTypeCountedANSIString 42 inTypeStruct 43 inTypeCountedBinary 44 inTypeCountedArray inType = 32 45 inTypeArray inType = 64 46 ) 47 48 // outType specifies a hint to the event decoder for how the value should be 49 // formatted. 50 type outType byte 51 52 // Various outType definitions for TraceLogging. These must match the 53 // definitions found in TraceLoggingProvider.h in the Windows SDK. 54 // 55 //nolint:deadcode,varcheck // keep unused constants for potential future use 56 const ( 57 // outTypeDefault indicates that the default formatting for the inType will 58 // be used by the event decoder. 59 outTypeDefault outType = iota 60 outTypeNoPrint 61 outTypeString 62 outTypeBoolean 63 outTypeHex 64 outTypePID 65 outTypeTID 66 outTypePort 67 outTypeIPv4 68 outTypeIPv6 69 outTypeSocketAddress 70 outTypeXML 71 outTypeJSON 72 outTypeWin32Error 73 outTypeNTStatus 74 outTypeHResult 75 outTypeFileTime 76 outTypeSigned 77 outTypeUnsigned 78 outTypeUTF8 outType = 35 79 outTypePKCS7WithTypeInfo outType = 36 80 outTypeCodePointer outType = 37 81 outTypeDateTimeUTC outType = 38 82 ) 83 84 // eventMetadata maintains a buffer which builds up the metadata for an ETW 85 // event. It needs to be paired with EventData which describes the event. 86 type eventMetadata struct { 87 buffer bytes.Buffer 88 } 89 90 // toBytes returns the raw binary data containing the event metadata. Before being 91 // returned, the current size of the buffer is written to the start of the 92 // buffer. The returned value is not copied from the internal buffer, so it can 93 // be mutated by the eventMetadata object after it is returned. 94 func (em *eventMetadata) toBytes() []byte { 95 // Finalize the event metadata buffer by filling in the buffer length at the 96 // beginning. 97 binary.LittleEndian.PutUint16(em.buffer.Bytes(), uint16(em.buffer.Len())) 98 return em.buffer.Bytes() 99 } 100 101 // writeEventHeader writes the metadata for the start of an event to the buffer. 102 // This specifies the event name and tags. 103 func (em *eventMetadata) writeEventHeader(name string, tags uint32) { 104 _ = binary.Write(&em.buffer, binary.LittleEndian, uint16(0)) // Length placeholder 105 em.writeTags(tags) 106 em.buffer.WriteString(name) 107 em.buffer.WriteByte(0) // Null terminator for name 108 } 109 110 func (em *eventMetadata) writeFieldInner(name string, inType inType, outType outType, tags uint32, arrSize uint16) { 111 em.buffer.WriteString(name) 112 em.buffer.WriteByte(0) // Null terminator for name 113 114 if outType == outTypeDefault && tags == 0 { 115 em.buffer.WriteByte(byte(inType)) 116 } else { 117 em.buffer.WriteByte(byte(inType | 128)) 118 if tags == 0 { 119 em.buffer.WriteByte(byte(outType)) 120 } else { 121 em.buffer.WriteByte(byte(outType | 128)) 122 em.writeTags(tags) 123 } 124 } 125 126 if arrSize != 0 { 127 _ = binary.Write(&em.buffer, binary.LittleEndian, arrSize) 128 } 129 } 130 131 // writeTags writes out the tags value to the event metadata. Tags is a 28-bit 132 // value, interpreted as bit flags, which are only relevant to the event 133 // consumer. The event consumer may choose to attribute special meaning to tags 134 // (e.g. 0x4 could mean the field contains PII). Tags are written as a series of 135 // bytes, each containing 7 bits of tag value, with the high bit set if there is 136 // more tag data in the following byte. This allows for a more compact 137 // representation when not all of the tag bits are needed. 138 func (em *eventMetadata) writeTags(tags uint32) { 139 // Only use the top 28 bits of the tags value. 140 tags &= 0xfffffff 141 142 for { 143 // Tags are written with the most significant bits (e.g. 21-27) first. 144 val := tags >> 21 145 146 if tags&0x1fffff == 0 { 147 // If there is no more data to write after this, write this value 148 // without the high bit set, and return. 149 em.buffer.WriteByte(byte(val & 0x7f)) 150 return 151 } 152 153 em.buffer.WriteByte(byte(val | 0x80)) 154 155 tags <<= 7 156 } 157 } 158 159 // writeField writes the metadata for a simple field to the buffer. 160 // 161 //nolint:unparam // tags is currently always 0, may change in the future 162 func (em *eventMetadata) writeField(name string, inType inType, outType outType, tags uint32) { 163 em.writeFieldInner(name, inType, outType, tags, 0) 164 } 165 166 // writeArray writes the metadata for an array field to the buffer. The number 167 // of elements in the array must be written as a uint16 in the event data, 168 // immediately preceding the event data. 169 // 170 //nolint:unparam // tags is currently always 0, may change in the future 171 func (em *eventMetadata) writeArray(name string, inType inType, outType outType, tags uint32) { 172 em.writeFieldInner(name, inType|inTypeArray, outType, tags, 0) 173 } 174 175 // writeCountedArray writes the metadata for an array field to the buffer. The 176 // size of a counted array is fixed, and the size is written into the metadata 177 // directly. 178 // 179 //nolint:unused // keep for future use 180 func (em *eventMetadata) writeCountedArray(name string, count uint16, inType inType, outType outType, tags uint32) { 181 em.writeFieldInner(name, inType|inTypeCountedArray, outType, tags, count) 182 } 183 184 // writeStruct writes the metadata for a nested struct to the buffer. The struct 185 // contains the next N fields in the metadata, where N is specified by the 186 // fieldCount argument. 187 func (em *eventMetadata) writeStruct(name string, fieldCount uint8, tags uint32) { 188 em.writeFieldInner(name, inTypeStruct, outType(fieldCount), tags, 0) 189 }