@@ -51,6 +51,8 @@ def level(self):
5151
5252 @property
5353 def context (self ):
54+ if self .UserContext is None :
55+ return None
5456 return ctypes .py_object .from_address (self .UserContext ).value
5557
5658 @property
@@ -74,12 +76,17 @@ def __repr__(self):
7476PEventRecord = ctypes .POINTER (EventRecord )
7577
7678class EventTraceProperties (gdef .EVENT_TRACE_PROPERTIES ):
79+ """Represent an Event Trace session that may exist or now. (https://docs.microsoft.com/en-us/windows/win32/api/evntrace/ns-evntrace-event_trace_properties)
80+
81+ This class is widly used by :class:`EtwTrace`
82+ """
7783 # Test: ascii / Use Wchar ?
7884 FULL_SIZE = ctypes .sizeof (gdef .EVENT_TRACE_PROPERTIES ) + MAX_SESSION_NAME_LEN_W + MAX_LOGFILE_PATH_LEN_W
7985
8086 # def alloc(cls, size) ?
8187 @classmethod
8288 def create (cls ):
89+ """Initialize a new :class:`EventTraceProperties`"""
8390 buff = windows .utils .BUFFER (cls )(size = cls .FULL_SIZE )
8491 # ctypes.memset(buff, "\x00", cls.FULL_SIZE)
8592 self = buff [0 ]
@@ -92,14 +99,13 @@ def get_logfilename(self):
9299 assert self .LogFileNameOffset
93100 return windows .current_process .read_string (ctypes .addressof (self ) + self .LogFileNameOffset )
94101
95-
96102 def set_logfilename (self , filename ):
97103 assert self .LogFileNameOffset
98104 if not filename .endswith ("\x00 " ):
99105 filename += "\x00 "
100106 return windows .current_process .write_memory (ctypes .addressof (self ) + self .LogFileNameOffset , filename )
101107
102- logfile = property (get_logfilename , set_logfilename )
108+ logfile = property (get_logfilename , set_logfilename ) #: The logfile associated with the session
103109
104110 def get_logger_name (self ):
105111 assert self .LoggerNameOffset
@@ -112,17 +118,23 @@ def set_logfilename(self, filename):
112118 filename += "\x00 "
113119 return windows .current_process .write_memory (ctypes .addressof (self ) + self .LoggerNameOffset , filename )
114120
115- name = property (get_logger_name , set_logfilename )
121+ name = property (get_logger_name , set_logfilename ) #: The name of the session
116122
117123 @property
118124 def guid (self ):
125+ """The GUID of the Event Trace session (see ``Wnode.Guid``)"""
119126 return self .Wnode .Guid
120127
121128 @property
122129 def id (self ):
123- """LoggerId"""
130+ """The LoggerId if the session (see ``Wnode.HistoricalContext``) """
124131 return self .Wnode .HistoricalContext
125132
133+
134+ def __repr__ (self ):
135+ return """<{0} name="{1}" guid={2}>""" .format (type (self ).__name__ , self .name , self .guid )
136+
137+
126138 # GUID setter ?
127139
128140class CtxProcess (object ):
@@ -154,15 +166,17 @@ def __exit__(self, exc_type, exc_value, traceback):
154166
155167
156168class EtwTrace (object ):
169+ """Represent an ETW Trace for tracing/processing events"""
157170 def __init__ (self , name , logfile = None , guid = None ):
158- self .name = windows .pycompat .raw_encode (name )
159- self .logfile = logfile
171+ self .name = windows .pycompat .raw_encode (name ) #: The name of the trace
172+ self .logfile = logfile #: The logging file of the trace (``None`` means real time trace)
160173 if guid and isinstance (guid , basestring ):
161174 guid = gdef .GUID .from_string (guid )
162- self .guid = guid
175+ self .guid = guid #: The guid of the trace
163176 self .handle = 0
164177
165178 def exists (self ):
179+ """Return ``True`` if the trace already exist (based on its name)"""
166180 prop = EventTraceProperties .create ()
167181 try :
168182 windows .winproxy .ControlTraceA (self .handle , self .name , prop , gdef .EVENT_TRACE_CONTROL_QUERY )
@@ -173,6 +187,7 @@ def exists(self):
173187 return True
174188
175189 def start (self , flags = 0 ):
190+ """Start the tracing"""
176191 prop = EventTraceProperties .create ()
177192 prop .NumberOfBuffers = 42
178193 prop .EnableFlags = flags
@@ -189,6 +204,11 @@ def start(self, flags=0):
189204 self .handle = handle
190205
191206 def stop (self , soft = False ): # Change name
207+ """stop the tracing.
208+
209+ ``soft`` will allow to stop a non-existing trace that do not exists/run.
210+ This allow for simpler script that stop/start some EtwTrace.
211+ """
192212 prop = EventTraceProperties .create ()
193213 try :
194214 windows .winproxy .ControlTraceA (0 , self .name , prop , gdef .EVENT_TRACE_CONTROL_STOP )
@@ -199,16 +219,19 @@ def stop(self, soft=False): # Change name
199219 return True
200220
201221 def flush (self ):
222+ """Flush the trace"""
202223 prop = EventTraceProperties .create ()
203224 windows .winproxy .ControlTraceA (0 , self .name , prop , gdef .EVENT_TRACE_CONTROL_FLUSH )
204225
205226
206227 def enable (self , guid , flags = 0xff , level = 0xff ):
228+ """Enable the specified event trace provider."""
207229 if isinstance (guid , basestring ):
208230 guid = gdef .GUID .from_string (guid )
209231 return windows .winproxy .EnableTrace (1 , flags , level , guid , self .handle ) # EnableTraceEx ?
210232
211233 def enable_ex (self , guid , flags = 0xff , level = 0xff , any_keyword = 0xffffffff , all_keyword = 0x00 ):
234+ """Enable the specified event trace provider."""
212235 if isinstance (guid , basestring ):
213236 guid = gdef .GUID .from_string (guid )
214237
@@ -223,6 +246,17 @@ def enable_ex(self, guid, flags=0xff, level=0xff, any_keyword = 0xffffffff, all_
223246
224247
225248 def process (self , callback , begin = None , end = None , context = None ):
249+ """Process the event retrieved by the trace.
250+ This function will call ``callback`` with any :class:`EventRecord` in the trace.
251+ ``begin/end`` allow to filter and only process events in a given timeframe.
252+
253+ .. warning::
254+
255+ If the trace if ``REALTIME`` (no logfile) this function will hang/process new event until the trace is stopped.
256+
257+ Using ``logman -ets stop TRACE_NAME`` for exemple.
258+
259+ """
226260 if end == "now" :
227261 end = gdef .FILETIME ()
228262 windows .winproxy .GetSystemTimeAsFileTime (end )
@@ -272,11 +306,19 @@ def __repr__(self):
272306 return """<{0} name={1!r} logfile={2!r}>""" .format (type (self ).__name__ , self .name , self .logfile )
273307
274308class TraceProvider (object ):
309+ """Represent a ETW provider, which is just a GUID.
310+ Corresponding name for a provider may be available trhought WMI.
311+ """
275312 def __init__ (self , guid ):
276313 self .guid = guid
277314
278315 @property
279316 def infos (self ):
317+ """The :class:`TraceGuidInfo` associated with the provider.
318+ Main use is to retrieve the instances of the provider (directly available with ``instances``)
319+
320+ :type: :class:`TraceGuidInfo`
321+ """
280322 size = gdef .DWORD ()
281323 info_buffer = ctypes .c_buffer (0x1000 )
282324 try :
@@ -293,13 +335,21 @@ def infos(self):
293335 # Our trace providers should be able to directly returns its instances
294336 @property
295337 def instances (self ):
338+ """The instances of the provider.
339+
340+ :type: [:class:`TraceProviderInstanceInfo`] -- A list of :class:`TraceProviderInstanceInfo`
341+ """
296342 return self .infos .instances
297343
298344 def __repr__ (self ):
299345 return """<{0} for "{1}">""" .format (type (self ).__name__ , self .guid )
300346
301347
302348class TraceGuidInfo (gdef .TRACE_GUID_INFO ):
349+ """Defines the header to the list of sessions that enabled the provider
350+ (see https://docs.microsoft.com/en-us/windows/win32/api/evntrace/ns-evntrace-trace_guid_info)
351+ """
352+
303353 @classmethod
304354 def from_raw_buffer (cls , buffer ):
305355 self = cls .from_buffer (buffer )
@@ -317,13 +367,20 @@ def _instance_generator(self):
317367
318368 @property
319369 def instances (self ):
370+ """The instances of the provider.
371+
372+ :type: [:class:`TraceProviderInstanceInfo`] -- A list of :class:`TraceProviderInstanceInfo`
373+ """
320374 return [x for x in self ._instance_generator ()]
321375
322376 def __repr__ (self ):
323377 return "<{0} InstanceCount={1} Reserved={2}>" .format (type (self ).__name__ , self .InstanceCount , self .Reserved )
324378
325379
326380class TraceProviderInstanceInfo (gdef .TRACE_PROVIDER_INSTANCE_INFO ):
381+ """Defines an instance of the provider
382+ (see https://docs.microsoft.com/en-us/windows/win32/api/evntrace/ns-evntrace-trace_provider_instance_info)
383+ """
327384 @classmethod
328385 def from_raw_buffer (cls , buffer , offset ):
329386 self = cls .from_buffer (buffer , offset )
@@ -340,15 +397,25 @@ def _instance_generator(self):
340397
341398 @property
342399 def sessions (self ):
400+ """The sessions for the instance
401+
402+ :type: [:class:`~windows.generated_def.winstructs.TRACE_ENABLE_INFO`] -- A list of session
403+ """
343404 return [x for x in self ._instance_generator ()]
344405
345406 def __repr__ (self ):
346407 return "<{0} Pid={1} EnableCount={2}>" .format (type (self ).__name__ , self .Pid , self .EnableCount )
347408
348409
349410class EtwManager (object ):
411+ """An object to query ETW session/providers and open new trace"""
412+
350413 @property
351414 def sessions (self ):
415+ """The list of currently active ETW session.
416+
417+ :type: [:class:`EventTraceProperties`] -- A list of :class:`EventTraceProperties`
418+ """
352419 # Create a tuple of MAX_ETW_SESSIONS EventTraceProperties ptr
353420 t = [EventTraceProperties .create () for _ in range (MAX_ETW_SESSIONS )]
354421 # Put this in a ctypes array
@@ -362,12 +429,20 @@ def sessions(self):
362429
363430 @property
364431 def providers (self ):
432+ """The list of currently existing ETW providers.
433+
434+ :type: [:class:`TraceProvider`] -- A list of ETW providers
435+ """
365436 buffer = windows .utils .BUFFER (gdef .GUID , 0x1000 )()
366437 size = gdef .DWORD ()
367438 windows .winproxy .EnumerateTraceGuidsEx (gdef .TraceGuidQueryList , None , 0 , buffer , buffer .real_size , size )
368- return [TraceProvider (g ) for g in buffer [:size .value / ctypes .sizeof (gdef .GUID )]]
439+ return [TraceProvider (g ) for g in buffer [:size .value // ctypes .sizeof (gdef .GUID )]]
369440
370441
371442 # Temp name / API ?
372443 def open_trace (self , name = None , logfile = None , guid = None ):
444+ """Open a new ETW Trace
445+
446+ :return: :class:`EtwTrace`
447+ """
373448 return EtwTrace (name , logfile , guid )
0 commit comments