@@ -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,59 @@ 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+ }
151+
152+ // Option represents the option for the trace.
153+ type Option func (o * opts )
154+
155+ // WithStackExts sets the stack extensions.
156+ func WithStackExts (stackexts * StackExtensions ) Option {
157+ return func (o * opts ) {
158+ o .stackexts = stackexts
159+ }
160+ }
161+
162+ // NewKernelTrace creates a new NT Kernel Logger trace.
163+ func NewKernelTrace (config * config.Config ) * Trace {
164+ t := & Trace {Name : etw .KernelLoggerSession , GUID : etw .KernelTraceControlGUID , stackExtensions : NewStackExtensions (config .EventSource ), config : config }
126165 t .enableCallstacks ()
127166 return t
128167}
129168
169+ // NewTrace creates a new trace that can host various ETW provider sessions.
170+ // The providers to be run inside the session can be given in the last argument
171+ // or added by the AddProvider method.
172+ func NewTrace (name string , config * config.Config , providers ... ProviderInfo ) * Trace {
173+ t := & Trace {Name : name , config : config , Providers : make ([]ProviderInfo , 0 )}
174+ t .Providers = providers
175+ return t
176+ }
177+
178+ // AddProvider adds a new provider to the multi trace session
179+ // with optional parameters that influence the provider.
180+ func (t * Trace ) AddProvider (guid windows.GUID , keywords uint64 , enableStacks bool , options ... Option ) {
181+ var opts opts
182+
183+ for _ , opt := range options {
184+ opt (& opts )
185+ }
186+
187+ t .Providers = append (t .Providers , ProviderInfo {GUID : guid , Keywords : keywords , EnableStacks : enableStacks , stackExtensions : opts .stackexts })
188+ }
189+
190+ // HasProviders determines if this trace contains providers.
191+ func (t * Trace ) HasProviders () bool { return len (t .Providers ) > 0 }
192+
193+ // IsGUIDEmpty determines if the provider GUID is empty.
194+ func (t * Trace ) IsGUIDEmpty () bool {
195+ return t .GUID .Data1 == 0 &&
196+ t .GUID .Data2 == 0 &&
197+ t .GUID .Data3 == 0 &&
198+ t .GUID .Data4 == [8 ]byte {}
199+ }
200+
130201func (t * Trace ) enableCallstacks () {
131202 if t .IsKernelTrace () {
132203 t .stackExtensions .EnableProcessCallstack ()
@@ -137,10 +208,6 @@ func (t *Trace) enableCallstacks() {
137208
138209 t .stackExtensions .EnableMemoryCallstack ()
139210 }
140-
141- if t .IsThreadpoolTrace () {
142- t .stackExtensions .EnableThreadpoolCallstack ()
143- }
144211}
145212
146213// Start registers and starts an event tracing session.
@@ -151,6 +218,11 @@ func (t *Trace) Start() error {
151218 if len (t .Name ) > maxLoggerNameSize {
152219 return fmt .Errorf ("trace name [%s] is too long" , t .Name )
153220 }
221+
222+ if ! t .IsGUIDEmpty () && t .HasProviders () {
223+ return fmt .Errorf ("%s trace has the root GUID set but providers are not empty" , t .Name )
224+ }
225+
154226 cfg := t .config .EventSource
155227 props := initEventTraceProps (cfg )
156228 flags := t .enableFlagsDynamically (cfg )
@@ -212,21 +284,30 @@ func (t *Trace) Start() error {
212284 return etw .SetTraceSystemFlags (handle , sysTraceFlags )
213285 }
214286
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 )
287+ // For each provider in multi trace, the call to etw.EnableTrace is
288+ // needed to configure how an ETW provider publishes events to the
289+ // trace session.
290+ // For instance, if stack enrichment is enabled, it is necessary to
291+ // instruct the provider to emit stack addresses in the extended
292+ // data item section when writing events to the session buffers
293+ for _ , provider := range t .Providers {
294+ switch {
295+ case provider .EnableStacks && provider .HasStackExtensions ():
296+ if err := etw .EnableStackTracing (t .startHandle , provider .stackExtensions .EventIds ()); err != nil {
297+ return fmt .Errorf ("fail to enable provider callstack tracing: %v" , err )
298+ }
299+ if err := etw .EnableTrace (provider .GUID , t .startHandle , provider .Keywords ); err != nil {
300+ return err
301+ }
302+ case provider .EnableStacks :
303+ opts := etw.EnableTraceOpts {WithStacktrace : true }
304+ if err := etw .EnableTraceWithOpts (provider .GUID , t .startHandle , provider .Keywords , opts ); err != nil {
305+ return err
306+ }
226307 }
227308 }
228309
229- return etw . EnableTrace ( t . GUID , t . startHandle , t . Keywords )
310+ return nil
230311}
231312
232313// IsStarted indicates if the trace is started successfully.
@@ -317,9 +398,6 @@ func (t *Trace) Close() error {
317398// IsKernelTrace determines if this is the system logger trace.
318399func (t * Trace ) IsKernelTrace () bool { return t .GUID == etw .KernelTraceControlGUID }
319400
320- // IsThreadpoolTrace determines if this is the thread pool logger trace.
321- func (t * Trace ) IsThreadpoolTrace () bool { return t .GUID == etw .ThreadpoolGUID }
322-
323401// enableFlagsDynamically crafts the system logger event mask
324402// depending on the compiled rules result or the config state.
325403// System logger flags is a bitmask that indicates which kernel events
0 commit comments