@@ -72,16 +72,10 @@ func initEventTraceProps(c config.EventSourceConfig) etw.EventTraceProperties {
7272 }
7373}
7474
75- // Trace is the essential building block for controlling
76- // trace sessions and configuring event consumers. Such
77- // operations include starting, stopping, and flushing
78- // trace sessions, and opening the trace for processing
79- // and event consumption.
80- type Trace struct {
81- // Name represents the unique tracing session name.
82- Name string
75+ // ProviderInfo describes ETW provider metadata.
76+ type ProviderInfo struct {
8377 // GUID is the globally unique identifier for the
84- // ETW provider.
78+ // ETW provider for which the session is started .
8579 GUID windows.GUID
8680 // Keywords is the bitmask of keywords that determine
8781 // the categories of events for the provider to emit.
@@ -91,11 +85,42 @@ type Trace struct {
9185 // for providers that are enabled via etw.EnableProvider
9286 // API.
9387 Keywords uint64
94-
88+ // EnableStacks indicates if callstacks are enabled for
89+ // this provider.
90+ EnableStacks bool
9591 // stackExtensions manager stack tracing enablement.
9692 // For each event present in the stack identifiers,
9793 // the StackWalk event is published by the provider.
9894 stackExtensions * StackExtensions
95+ }
96+
97+ func (p * ProviderInfo ) HasStackExtensions () bool {
98+ return p .stackExtensions != nil && ! p .stackExtensions .Empty ()
99+ }
100+
101+ // Trace is the essential building block for controlling
102+ // trace sessions and configuring event consumers. Such
103+ // operations include starting, stopping, and flushing
104+ // trace sessions, and opening the trace for processing
105+ // and event consumption. Trace can be configured to
106+ // operate a single ETW provider, or it can act as a
107+ // container for multiple provider sessions.
108+ type Trace struct {
109+ // Name represents the unique tracing session name.
110+ Name string
111+ // GUID is the globally unique identifier for the
112+ // ETW provider for which the session is started.
113+ GUID windows.GUID
114+
115+ // Providers is the list of providers to be run inside
116+ // the tracing session. For each provider, the GUID,
117+ // keywords and other parameters can be specified.
118+ Providers []ProviderInfo
119+
120+ // stackExtensions manages stack tracing enablement.
121+ // For each event present in the stack identifiers,
122+ // the StackWalk event is published by the provider.
123+ stackExtensions * StackExtensions
99124
100125 // startHandle is the session handle returned by the
101126 // etw.StartTrace function. This handle is
@@ -120,13 +145,68 @@ type Trace struct {
120145 errs chan error
121146}
122147
123- // NewTrace creates a new trace with specified name, provider GUID, and keywords.
124- func NewTrace (name string , guid windows.GUID , keywords uint64 , config * config.Config ) * Trace {
125- t := & Trace {Name : name , GUID : guid , Keywords : keywords , stackExtensions : NewStackExtensions (config .EventSource ), config : config }
148+ type opts struct {
149+ stackexts * StackExtensions
150+ keywords uint64
151+ }
152+
153+ // Option represents the option for the trace.
154+ type Option func (o * opts )
155+
156+ // WithStackExts sets the stack extensions.
157+ func WithStackExts (stackexts * StackExtensions ) Option {
158+ return func (o * opts ) {
159+ o .stackexts = stackexts
160+ }
161+ }
162+
163+ // WithKeywords sets the bitmask of keywords that determine
164+ // the categories of events for the provider to emit.
165+ func WithKeywords (keywords uint64 ) Option {
166+ return func (o * opts ) {
167+ o .keywords = keywords
168+ }
169+ }
170+
171+ // NewKernelTrace creates a new NT Kernel Logger trace.
172+ func NewKernelTrace (config * config.Config ) * Trace {
173+ t := & Trace {Name : etw .KernelLoggerSession , GUID : etw .KernelTraceControlGUID , stackExtensions : NewStackExtensions (config .EventSource ), config : config }
126174 t .enableCallstacks ()
127175 return t
128176}
129177
178+ // NewTrace creates a new trace that can host various ETW provider sessions.
179+ // The providers to be run inside the session can be given in the last argument
180+ // or added by the AddProvider method.
181+ func NewTrace (name string , config * config.Config , providers ... ProviderInfo ) * Trace {
182+ t := & Trace {Name : name , config : config , Providers : make ([]ProviderInfo , 0 )}
183+ t .Providers = providers
184+ return t
185+ }
186+
187+ // AddProvider adds a new provider to the multi trace session
188+ // with optional parameters that influence the provider.
189+ func (t * Trace ) AddProvider (guid windows.GUID , enableStacks bool , options ... Option ) {
190+ var opts opts
191+
192+ for _ , opt := range options {
193+ opt (& opts )
194+ }
195+
196+ t .Providers = append (t .Providers , ProviderInfo {GUID : guid , Keywords : opts .keywords , EnableStacks : enableStacks , stackExtensions : opts .stackexts })
197+ }
198+
199+ // HasProviders determines if this trace contains providers.
200+ func (t * Trace ) HasProviders () bool { return len (t .Providers ) > 0 }
201+
202+ // IsGUIDEmpty determines if the provider GUID is empty.
203+ func (t * Trace ) IsGUIDEmpty () bool {
204+ return t .GUID .Data1 == 0 &&
205+ t .GUID .Data2 == 0 &&
206+ t .GUID .Data3 == 0 &&
207+ t .GUID .Data4 == [8 ]byte {}
208+ }
209+
130210func (t * Trace ) enableCallstacks () {
131211 if t .IsKernelTrace () {
132212 t .stackExtensions .EnableProcessCallstack ()
@@ -137,10 +217,6 @@ func (t *Trace) enableCallstacks() {
137217
138218 t .stackExtensions .EnableMemoryCallstack ()
139219 }
140-
141- if t .IsThreadpoolTrace () {
142- t .stackExtensions .EnableThreadpoolCallstack ()
143- }
144220}
145221
146222// Start registers and starts an event tracing session.
@@ -151,6 +227,11 @@ func (t *Trace) Start() error {
151227 if len (t .Name ) > maxLoggerNameSize {
152228 return fmt .Errorf ("trace name [%s] is too long" , t .Name )
153229 }
230+
231+ if ! t .IsGUIDEmpty () && t .HasProviders () {
232+ return fmt .Errorf ("%s trace has the root GUID set but providers are not empty" , t .Name )
233+ }
234+
154235 cfg := t .config .EventSource
155236 props := initEventTraceProps (cfg )
156237 flags := t .enableFlagsDynamically (cfg )
@@ -212,21 +293,34 @@ func (t *Trace) Start() error {
212293 return etw .SetTraceSystemFlags (handle , sysTraceFlags )
213294 }
214295
215- // if we're starting a trace for non-system logger, the call
216- // to etw.EnableTrace is needed to configure how an ETW provider
217- // publishes events to the trace session. For instance, if stack
218- // enrichment is enabled, it is necessary to instruct the provider
219- // to emit stack addresses in the extended data item section when
220- // writing events to the session buffers
221- if cfg .StackEnrichment && ! t .IsThreadpoolTrace () {
222- return etw .EnableTraceWithOpts (t .GUID , t .startHandle , t .Keywords , etw.EnableTraceOpts {WithStacktrace : true })
223- } else if cfg .StackEnrichment && len (t .stackExtensions .EventIds ()) > 0 {
224- if err := etw .EnableStackTracing (t .startHandle , t .stackExtensions .EventIds ()); err != nil {
225- return fmt .Errorf ("fail to enable system events callstack tracing: %v" , err )
296+ // For each provider in multi trace, the call to etw.EnableTrace is
297+ // needed to configure how an ETW provider publishes events to the
298+ // trace session.
299+ // For instance, if stack enrichment is enabled, it is necessary to
300+ // instruct the provider to emit stack addresses in the extended
301+ // data item section when writing events to the session buffers
302+ for _ , provider := range t .Providers {
303+ switch {
304+ case provider .EnableStacks && provider .HasStackExtensions ():
305+ if err := etw .EnableStackTracing (t .startHandle , provider .stackExtensions .EventIds ()); err != nil {
306+ return fmt .Errorf ("fail to enable provider callstack tracing: %v" , err )
307+ }
308+ if err := etw .EnableTrace (provider .GUID , t .startHandle , provider .Keywords ); err != nil {
309+ return err
310+ }
311+ case provider .EnableStacks :
312+ opts := etw.EnableTraceOpts {WithStacktrace : true }
313+ if err := etw .EnableTraceWithOpts (provider .GUID , t .startHandle , provider .Keywords , opts ); err != nil {
314+ return err
315+ }
316+ default :
317+ if err := etw .EnableTrace (provider .GUID , t .startHandle , provider .Keywords ); err != nil {
318+ return err
319+ }
226320 }
227321 }
228322
229- return etw . EnableTrace ( t . GUID , t . startHandle , t . Keywords )
323+ return nil
230324}
231325
232326// IsStarted indicates if the trace is started successfully.
@@ -317,9 +411,6 @@ func (t *Trace) Close() error {
317411// IsKernelTrace determines if this is the system logger trace.
318412func (t * Trace ) IsKernelTrace () bool { return t .GUID == etw .KernelTraceControlGUID }
319413
320- // IsThreadpoolTrace determines if this is the thread pool logger trace.
321- func (t * Trace ) IsThreadpoolTrace () bool { return t .GUID == etw .ThreadpoolGUID }
322-
323414// enableFlagsDynamically crafts the system logger event mask
324415// depending on the compiled rules result or the config state.
325416// System logger flags is a bitmask that indicates which kernel events
0 commit comments