Skip to content

Commit

Permalink
[windows] Update ETW with new functions; add ability to get ETW stats
Browse files Browse the repository at this point in the history
intermediate checking. Have standalone commit to allow changing number of ETW buffers

add ability to get etw stats

clean up cherry-pick
  • Loading branch information
derekwbrown committed May 10, 2024
1 parent 6d305d8 commit 66cd601
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 11 deletions.
32 changes: 30 additions & 2 deletions comp/etw/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,31 @@ type ProviderConfiguration struct {
// ProviderConfigurationFunc is a function used to configure a provider
type ProviderConfigurationFunc func(cfg *ProviderConfiguration)

// SessionConfiguration is a structure containing all the configuration options for an ETW session
type SessionConfiguration struct {
//MaxBuffers is the maximum number of buffers for ETW to allocate. The default is 0
MaxBuffers uint32
}

// SessionStatistics contains statistics about the session
type SessionStatistics struct {
// NumberOfBuffers is the number of buffers allocated for the session
NumberOfBuffers uint32
// FreeBuffers is the number of buffers that are free
FreeBuffers uint32
// EventsLost is the number of events not recorded
EventsLost uint32
// BuffersWritten is the number of buffers written
BuffersWritten uint32
// LogBuffersLost is the number of log buffers lost
LogBuffersLost uint32
// RealTimeBuffersLost is the number of real-time buffers lost
RealTimeBuffersLost uint32
}

// SessionConfigurationFunc is a function used to configure a session
type SessionConfigurationFunc func(cfg *SessionConfiguration)

// Session represents an ETW session. A session can have multiple tracing providers enabled.
type Session interface {
// ConfigureProvider configures a particular ETW provider identified by its GUID for this session.
Expand All @@ -180,12 +205,15 @@ type Session interface {
// StopTracing stops all tracing activities.
// It's not possible to use the session anymore after a call to StopTracing.
StopTracing() error

// GetSessionStatistics returns statistics about the session
GetSessionStatistics() (SessionStatistics, error)
}

// Component offers a way to create ETW tracing sessions with a given name.
type Component interface {
NewSession(sessionName string) (Session, error)
NewWellKnownSession(sessionName string) (Session, error)
NewSession(sessionName string, f SessionConfigurationFunc) (Session, error)
NewWellKnownSession(sessionName string, f SessionConfigurationFunc) (Session, error)
}

// UserData offers a wrapper around the UserData field of an ETW event.
Expand Down
8 changes: 4 additions & 4 deletions comp/etw/impl/etwImpl.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,16 @@ func NewEtw() (etw.Component, error) {
type etwImpl struct {
}

func (s *etwImpl) NewSession(sessionName string) (etw.Session, error) {
session, err := createEtwSession(sessionName)
func (s *etwImpl) NewSession(sessionName string, f etw.SessionConfigurationFunc) (etw.Session, error) {
session, err := createEtwSession(sessionName, f)
if err != nil {
return nil, err
}
return session, nil
}

func (s *etwImpl) NewWellKnownSession(sessionName string) (etw.Session, error) {
session, err := createWellKnownEtwSession(sessionName)
func (s *etwImpl) NewWellKnownSession(sessionName string, f etw.SessionConfigurationFunc) (etw.Session, error) {
session, err := createWellKnownEtwSession(sessionName, f)
if err != nil {
return nil, err
}
Expand Down
53 changes: 51 additions & 2 deletions comp/etw/impl/etwSession.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,35 @@ func (e *etwSession) StopTracing() error {
return globalError
}

func (e *etwSession) GetSessionStatistics() (etw.SessionStatistics, error) {
var stats etw.SessionStatistics
// it is not clear if we can safely reuse the properties buffer here
// so we allocate a new one
tmpBuf := make([]byte, len(e.propertiesBuf))

sessionptp := (C.PEVENT_TRACE_PROPERTIES)(unsafe.Pointer(&e.propertiesBuf[0]))
ptp := (C.PEVENT_TRACE_PROPERTIES)(unsafe.Pointer(&tmpBuf[0]))

*ptp = *sessionptp

ret := windows.Errno(C.ControlTraceW(
e.hSession,
nil,
ptp,
C.EVENT_TRACE_CONTROL_QUERY))
if ret != windows.ERROR_SUCCESS {
return stats, ret
}

stats.NumberOfBuffers = uint32(ptp.NumberOfBuffers)
stats.FreeBuffers = uint32(ptp.FreeBuffers)
stats.EventsLost = uint32(ptp.EventsLost)
stats.BuffersWritten = uint32(ptp.BuffersWritten)
stats.LogBuffersLost = uint32(ptp.LogBuffersLost)
stats.RealTimeBuffersLost = uint32(ptp.RealTimeBuffersLost)
return stats, nil
}

// deleteEtwSession deletes an ETW session by name, typically after a crash since we don't have access to the session
// handle anymore.
func deleteEtwSession(name string) error {
Expand Down Expand Up @@ -179,7 +208,7 @@ func deleteEtwSession(name string) error {
return ret
}

func createEtwSession(name string) (*etwSession, error) {
func createEtwSession(name string, f etw.SessionConfigurationFunc) (*etwSession, error) {
_ = deleteEtwSession(name)

utf16SessionName, err := windows.UTF16FromString(name)
Expand All @@ -193,6 +222,13 @@ func createEtwSession(name string) (*etwSession, error) {
if err != nil {
return nil, fmt.Errorf("incorrect session name; %w", err)
}

// get any caller supplied configuration
cfg := &etw.SessionConfiguration{}
if f != nil {
f(cfg)
}

sessionNameSize := (len(utf16SessionName) * int(unsafe.Sizeof(utf16SessionName[0])))
bufSize := int(unsafe.Sizeof(C.EVENT_TRACE_PROPERTIES{})) + sessionNameSize
propertiesBuf := make([]byte, bufSize)
Expand All @@ -204,6 +240,9 @@ func createEtwSession(name string) (*etwSession, error) {

pProperties.LogFileMode = C.EVENT_TRACE_REAL_TIME_MODE

if cfg.MaxBuffers > 0 {
pProperties.MaximumBuffers = C.ulong(cfg.MaxBuffers)
}
ret := windows.Errno(C.StartTraceW(
&s.hSession,
C.LPWSTR(unsafe.Pointer(&s.utf16name[0])),
Expand All @@ -223,7 +262,7 @@ func createEtwSession(name string) (*etwSession, error) {
return nil, fmt.Errorf("StartTraceW failed; %w", err)
}

func createWellKnownEtwSession(name string) (*etwSession, error) {
func createWellKnownEtwSession(name string, f etw.SessionConfigurationFunc) (*etwSession, error) {
utf16SessionName, err := windows.UTF16FromString(name)
if err != nil {
return nil, fmt.Errorf("incorrect session name; %w", err)
Expand All @@ -234,6 +273,12 @@ func createWellKnownEtwSession(name string) (*etwSession, error) {
utf16name: utf16SessionName,
wellKnown: true,
}

// get any caller supplied configuration
cfg := &etw.SessionConfiguration{}
if f != nil {
f(cfg)
}
sessionNameSize := (len(utf16SessionName) * int(unsafe.Sizeof(utf16SessionName[0])))
bufSize := int(unsafe.Sizeof(C.EVENT_TRACE_PROPERTIES{})) + sessionNameSize
propertiesBuf := make([]byte, bufSize)
Expand All @@ -244,6 +289,10 @@ func createWellKnownEtwSession(name string) (*etwSession, error) {
pProperties.Wnode.Flags = C.WNODE_FLAG_TRACED_GUID

pProperties.LogFileMode = C.EVENT_TRACE_REAL_TIME_MODE
if cfg.MaxBuffers > 0 {
pProperties.MaximumBuffers = C.ulong(cfg.MaxBuffers)
}

s.propertiesBuf = propertiesBuf
return s, nil
}
2 changes: 1 addition & 1 deletion comp/trace/etwtracer/etwtracerimpl/etwtracerimpl.go
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ func (a *etwtracerimpl) start(_ context.Context) error {
a.log.Infof("Starting Datadog APM ETW tracer component")
var err error
etwSessionName := "Datadog APM ETW tracer"
a.session, err = a.etw.NewSession(etwSessionName)
a.session, err = a.etw.NewSession(etwSessionName, func(cfg *etw.SessionConfiguration) {})
if err != nil {
a.log.Errorf("Failed to create the ETW session '%s': %v", etwSessionName, err)
// Don't fail the Agent startup
Expand Down
2 changes: 1 addition & 1 deletion pkg/network/protocols/http/etw_interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func NewEtwInterface(c *config.Config) (*EtwInterface, error) {
return nil, err
}

ei.session, err = etwcomp.NewSession(etwSessionName)
ei.session, err = etwcomp.NewSession(etwSessionName, func(cfg *etw.SessionConfiguration) {} )
if err != nil {
return nil, err
}
Expand Down
4 changes: 3 additions & 1 deletion pkg/security/probe/probe_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,9 @@ func (p *WindowsProbe) initEtwFIM() error {
if err != nil {
return err
}
p.fimSession, err = etwcomp.NewSession(etwSessionName)
p.fimSession, err = etwcomp.NewSession(etwSessionName, func(cfg *etw.SessionConfiguration) {
})

if err != nil {
return err
}
Expand Down

0 comments on commit 66cd601

Please sign in to comment.