From 89c1d0e19d91c7556dbba2d42b9d52c0460cc995 Mon Sep 17 00:00:00 2001 From: John Starks Date: Sat, 4 May 2019 16:50:01 -0700 Subject: [PATCH] pkg/etw: Add support for time.Time This change adds support for encoding time.Time values as FILETIME values in tracelogging events. FILETIME has slightly less precision than time.Time (100ns vs 1ns), but it is the closest availble match. --- pkg/etw/eventdata.go | 6 ++++++ pkg/etw/fieldopt.go | 12 ++++++++++++ 2 files changed, 18 insertions(+) diff --git a/pkg/etw/eventdata.go b/pkg/etw/eventdata.go index 79dc10a..a524d89 100644 --- a/pkg/etw/eventdata.go +++ b/pkg/etw/eventdata.go @@ -3,6 +3,7 @@ package etw import ( "bytes" "encoding/binary" + "syscall" ) // eventData maintains a buffer which builds up the data for an ETW event. It @@ -63,3 +64,8 @@ func (ed *eventData) writeUint32(value uint32) { func (ed *eventData) writeUint64(value uint64) { binary.Write(&ed.buffer, binary.LittleEndian, value) } + +// writeFiletime appends a FILETIME to the buffer. +func (ed *eventData) writeFiletime(value syscall.Filetime) { + binary.Write(&ed.buffer, binary.LittleEndian, value) +} diff --git a/pkg/etw/fieldopt.go b/pkg/etw/fieldopt.go index c29c6bd..d544a29 100644 --- a/pkg/etw/fieldopt.go +++ b/pkg/etw/fieldopt.go @@ -4,6 +4,8 @@ import ( "fmt" "math" "reflect" + "syscall" + "time" "unsafe" ) @@ -380,6 +382,14 @@ func Struct(name string, opts ...FieldOpt) FieldOpt { } } +// Time adds a time to the event. +func Time(name string, value time.Time) FieldOpt { + return func(em *eventMetadata, ed *eventData) { + em.writeField(name, inTypeFileTime, outTypeDateTimeUTC, 0) + ed.writeFiletime(syscall.NsecToFiletime(value.UTC().UnixNano())) + } +} + // Currently, we support logging basic builtin types (int, string, etc), slices // of basic builtin types, error, types derived from the basic types (e.g. "type // foo int"), and structs (recursively logging their fields). We do not support @@ -454,6 +464,8 @@ func SmartField(name string, v interface{}) FieldOpt { return Float64Array(name, v) case error: return StringField(name, v.Error()) + case time.Time: + return Time(name, v) default: switch rv := reflect.ValueOf(v); rv.Kind() { case reflect.Bool: