Files
go-winio/etw/provider.go
T

88 lines
2.9 KiB
Go

package etw
import (
"bytes"
"encoding/binary"
"unsafe"
"golang.org/x/sys/windows"
)
type eventDataDescriptorType uint8
const (
eventDataDescriptorTypeUserData eventDataDescriptorType = iota
eventDataDescriptorTypeEventMetadata
eventDataDescriptorTypeProviderMetadata
)
type Provider struct {
handle providerHandle
metadata *bytes.Buffer
}
type providerHandle windows.Handle
type EnableCallback func(*windows.GUID, uint32, byte, uint64, uint64, uintptr)
type eventDataDescriptor struct {
ptr uint64
size uint32
dataType eventDataDescriptorType
reserved1 uint8
reserved2 uint16
}
func (descriptor *eventDataDescriptor) set(dataType eventDataDescriptorType, buffer *bytes.Buffer) {
// Passing a pointer to Go-managed memory as part of a block of memory is risky since the GC doesn't know about it.
// If we find a better way to do this we should use it instead.
descriptor.ptr = uint64(uintptr(unsafe.Pointer(&buffer.Bytes()[0])))
descriptor.size = uint32(buffer.Len())
descriptor.dataType = dataType
}
// NewProvider creates and registers a new provider.
func NewProvider(name string, id *windows.GUID, callback EnableCallback) (*Provider, error) {
innerCallback := func(sourceID *windows.GUID, isEnabled uint32, level byte, matchAnyKeyword uint64, matchAllKeyword uint64, filterData uintptr, _ uintptr) uintptr {
if callback != nil {
callback(sourceID, isEnabled, level, matchAnyKeyword, matchAllKeyword, filterData)
}
return 0
}
var providerHandle providerHandle
if err := eventRegister(id, windows.NewCallback(innerCallback), 0, &providerHandle); err != nil {
return nil, err
}
var metadataBuffer bytes.Buffer
binary.Write(&metadataBuffer, binary.LittleEndian, uint16(0))
binary.Write(&metadataBuffer, binary.LittleEndian, []byte(name))
binary.Write(&metadataBuffer, binary.LittleEndian, byte(0))
binary.LittleEndian.PutUint16(metadataBuffer.Bytes(), uint16(metadataBuffer.Len()))
return &Provider{
handle: providerHandle,
metadata: &metadataBuffer,
}, nil
}
// Close unregisters the provider.
func (provider *Provider) Close() error {
return eventUnregister(provider.handle)
}
// WriteEvent writes a single event to ETW, from this provider.
func (provider *Provider) WriteEvent(event *Event) error {
// Finalize the event metadata buffer by filling in the buffer length at the
// beginning.
binary.LittleEndian.PutUint16(event.Metadata.buffer.Bytes(), uint16(event.Metadata.buffer.Len()))
var dataDescriptors [3]eventDataDescriptor
dataDescriptors[0].set(eventDataDescriptorTypeProviderMetadata, provider.metadata)
dataDescriptors[1].set(eventDataDescriptorTypeEventMetadata, &event.Metadata.buffer)
dataDescriptors[2].set(eventDataDescriptorTypeUserData, &event.Data.buffer)
return eventWriteTransfer(provider.handle, event.Descriptor, nil, nil, 3, &dataDescriptors[0])
}