├── MANIFEST.in ├── README.txt ├── etw.gyp ├── etw ├── __init__.py ├── consumer.py ├── controller.py ├── descriptors │ ├── __init__.py │ ├── binary_buffer.py │ ├── event.py │ ├── field.py │ ├── fileio.py │ ├── image.py │ ├── pagefault.py │ ├── pagefault_xp.py │ ├── process.py │ ├── registry.py │ └── thread.py ├── evntcons.py ├── evntrace.py ├── guiddef.py ├── provider.py └── util.py ├── ez_setup.py ├── generate_descriptor.py ├── generate_descriptors.bat ├── setup.cfg ├── setup.py ├── test ├── __init__.py ├── descriptors │ ├── __init__.py │ ├── test_binary_buffer.py │ └── test_event.py ├── test_consumer.py ├── test_controller.py └── test_provider.py └── test_generate_descriptor.py /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.txt 2 | include ez_setup.py 3 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | This directory contains the sources for the python module "etw", which uses 2 | ctypes to provide a pure python implementation of a set of classes that make 3 | it relatively easy use Event Tracing for Windows from python. 4 | The classes allow controlling and consuming event trace sessions, as well 5 | as generating ETW events. 6 | 7 | -------------------------------------------------------------------------------- /etw.gyp: -------------------------------------------------------------------------------- 1 | # Copyright 2011 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | { 16 | 'variables': { 17 | 'chromium_code': 1, 18 | 'etw_sources': [ 19 | 'etw/__init__.py', 20 | 'etw/consumer.py', 21 | 'etw/controller.py', 22 | 'etw/evntcons.py', 23 | 'etw/evntrace.py', 24 | 'etw/guiddef.py', 25 | 'etw/provider.py', 26 | 'etw/util.py', 27 | 'etw/descriptors/__init__.py', 28 | 'etw/descriptors/binary_buffer.py', 29 | 'etw/descriptors/event.py', 30 | 'etw/descriptors/field.py', 31 | 'etw/descriptors/fileio.py', 32 | 'etw/descriptors/image.py', 33 | 'etw/descriptors/pagefault.py', 34 | 'etw/descriptors/pagefault_xp.py', 35 | 'etw/descriptors/process.py', 36 | 'etw/descriptors/registry.py', 37 | 'etw/descriptors/thread.py', 38 | ], 39 | }, 40 | 'targets': [ 41 | { 42 | 'target_name': 'etw', 43 | 'type': 'none', 44 | 'sources': [ 45 | '<@(etw_sources)', 46 | ], 47 | 'actions': [ 48 | { 49 | 'action_name': 'build_etw', 50 | 'msvs_cygwin_shell': 0, 51 | 'inputs': [ 52 | '<@(etw_sources)', 53 | ], 54 | 'outputs': [ 55 | '<(PRODUCT_DIR)/ETW-0.6.5.0-py2.6.egg', 56 | ], 57 | 'action': [ 58 | '"<(DEPTH)/third_party/setuptools/setup_env.bat" &&' 59 | '"<(DEPTH)/third_party/python_26/python"', 60 | 'setup.py', 61 | 'bdist_egg', 62 | '--dist-dir=<(PRODUCT_DIR)', 63 | '--bdist-dir=<(PRODUCT_DIR)/temp/etw/', 64 | ], 65 | }, 66 | ], 67 | }, 68 | ], 69 | } 70 | -------------------------------------------------------------------------------- /etw/__init__.py: -------------------------------------------------------------------------------- 1 | #!python 2 | # Copyright 2010 Google Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | """This module provides utility classes that make it easier to deal with 16 | Event Tracing for Windows. The classes implement an ETW controller, consumer 17 | and provider. 18 | """ 19 | from etw.consumer import TraceEventSource, EventConsumer, EventHandler 20 | from etw.controller import TraceController, TraceProperties 21 | from etw.provider import TraceProvider, MofEvent 22 | from etw.guiddef import GUID 23 | 24 | __all__ = ['GUID', 25 | 'TraceProvider', 26 | 'MofEvent', 27 | 'EventConsumer', 28 | 'EventHandler', 29 | 'TraceEventSource', 30 | 'TraceController', 31 | 'TraceProperties'] 32 | -------------------------------------------------------------------------------- /etw/consumer.py: -------------------------------------------------------------------------------- 1 | # Copyright 2010 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """Implements a trace consumer utility class.""" 15 | from collections import defaultdict 16 | from ctypes import byref, cast, POINTER 17 | from etw import evntcons 18 | from etw import evntrace 19 | from etw import util 20 | from etw.descriptors import event 21 | import logging 22 | 23 | 24 | def _BindHandler(handler_func, handler_instance): 25 | def BoundHandler(event): 26 | handler_func(handler_instance, event) 27 | return BoundHandler 28 | 29 | 30 | def EventHandler(*event_infos): 31 | """EventHandler decorator factory. 32 | 33 | This decorator factory assigns the event_infos value passed in as a property 34 | of the decorated function. This is used by the Consumer class to 35 | populate an event handler map with the decorated functions. 36 | """ 37 | def wrapper(func): 38 | func.event_infos = event_infos[:] 39 | return func 40 | return wrapper 41 | 42 | 43 | class MetaEventConsumer(type): 44 | """Meta class for TraceConsumer. 45 | 46 | The purpose of this metaclass is to populate an event handler map for a 47 | TraceConsumer subclass. It iterates through the class' dict searching 48 | for functions that have an event_info property and assigns them to a map. 49 | The map is then assigned to the subclass type. It also handles a hierarchy 50 | of consumers and will register event handlers defined in parent classes 51 | for the given sub class. 52 | """ 53 | def __new__(cls, name, bases, dict): 54 | """Create a new TraceConsumer class type.""" 55 | event_handler_map = defaultdict(list) 56 | for base in bases: 57 | base_map = getattr(base, 'event_handler_map', None) 58 | if base_map: 59 | event_handler_map.update(base_map) 60 | for v in dict.values(): 61 | event_infos = getattr(v, 'event_infos', []) 62 | for event_info in event_infos: 63 | event_handler_map[event_info].append(v) 64 | new_type = type.__new__(cls, name, bases, dict) 65 | new_type.event_handler_map = event_handler_map 66 | return new_type 67 | 68 | 69 | class EventConsumer(object): 70 | """An Event Tracing for Windows event handler base class. 71 | 72 | Derive your handlers from this class, and define event handlers like so: 73 | 74 | @EventHandler(module.Event.EventName) 75 | def OnEventName(self, event): 76 | pass 77 | 78 | to handle events. One or more event handler instances can then be passed to a 79 | LogConsumer, which will dispatch log events to them during log consumption. 80 | 81 | Note that if any handler raises an exception, the exception will be logged, 82 | and log parsing will be terminated as soon as possible. 83 | """ 84 | __metaclass__ = MetaEventConsumer 85 | 86 | 87 | class _TraceLogSession(object): 88 | """An internal implementation class that wraps an open event trace session. 89 | 90 | The purpose of this class is to maintain per-session state, such as 91 | the ETW time to wall-clock conversion state, the event handle, etc. 92 | """ 93 | def __init__(self, event_source, raw_time): 94 | self._event_source = event_source 95 | self._raw_time = raw_time 96 | # Assume FILETIME conversion until we get other data. 97 | self._time_epoch_delta = util.FILETIME_EPOCH_DELTA_S 98 | self._time_multiplier = util.FILETIME_TO_SECONDS_MULTIPLIER 99 | 100 | self._buffer_callback = evntrace.EVENT_TRACE_BUFFER_CALLBACK( 101 | self._ProcessBufferCallback) 102 | self._event_callback = evntrace.EVENT_CALLBACK( 103 | self._ProcessEventCallback) 104 | self._handle = None 105 | self._start_time = None 106 | self._processed_first_event = False 107 | self.is_64_bit_log = False 108 | 109 | def SessionTimeToTime(self, session_time): 110 | """Convert a raw time value from this session to a python time value. 111 | 112 | Args: 113 | session_time: a time value read from a event header or event field 114 | in this session. 115 | 116 | Returns: a floating point time value in seconds, with zero at 1.1.1970. 117 | """ 118 | return session_time * self._time_multiplier - self._time_epoch_delta 119 | 120 | def Close(self): 121 | """Close this session.""" 122 | try: 123 | evntrace.CloseTrace(self._handle) 124 | except: 125 | logging.exception("Exception closing session.") 126 | 127 | def OpenRealtimeSession(self, name): 128 | """Open a real time trace session named "name". 129 | 130 | Args: 131 | name: name of the session to open. 132 | """ 133 | logfile = evntrace.EVENT_TRACE_LOGFILE() 134 | logfile.LoggerName = name 135 | logfile.ProcessTraceMode = evntcons.PROCESS_TRACE_MODE_REAL_TIME 136 | if self._raw_time: 137 | logfile.ProcessTraceMode = evntcons.PROCESS_TRACE_MODE_RAW_TIMESTAMP 138 | logfile.BufferCallback = self._buffer_callback 139 | logfile.EventCallback = self._event_callback 140 | self._handle = evntrace.OpenTrace(byref(logfile)) 141 | self._ProcessHeader(logfile.LogfileHeader) 142 | 143 | def OpenFileSession(self, path): 144 | """Open a file session for the file at "path". 145 | 146 | Args: 147 | path: relative or absolute path to the file to open. 148 | """ 149 | logfile = evntrace.EVENT_TRACE_LOGFILE() 150 | logfile.LogFileName = path 151 | if self._raw_time: 152 | logfile.ProcessTraceMode = evntcons.PROCESS_TRACE_MODE_RAW_TIMESTAMP 153 | logfile.BufferCallback = self._buffer_callback 154 | logfile.EventCallback = self._event_callback 155 | self._handle = evntrace.OpenTrace(byref(logfile)) 156 | self._ProcessHeader(logfile.LogfileHeader) 157 | 158 | def _ProcessHeader(self, logfile_header): 159 | if logfile_header.PointerSize == 8: 160 | self.is_64_bit_log = True 161 | 162 | if self._raw_time: 163 | mode = logfile_header.ReservedFlags 164 | self._start_time = util.FileTimeToTime(logfile_header.StartTime) 165 | ticks_sec = None 166 | if mode == 1: # QPC timer resolution 167 | ticks_sec = logfile_header.PerfFreq 168 | elif mode == 2: # System time 169 | ticks_sec = 1000 # TODO(siggi): verify this is milliseconds 170 | elif mode == 3: # CPU cycle counter 171 | ticks_sec = logfile_header.CpuSpeedInMHz * 1000000.0 172 | 173 | self._time_multiplier = 1.0 / ticks_sec 174 | 175 | def _ProcessFirstEvent(self, event): 176 | if self._raw_time: 177 | self._time_epoch_delta = ( 178 | event.contents.Header.TimeStamp * self._time_multiplier - 179 | self._start_time) 180 | 181 | def _ProcessBufferCallback(self, buffer): 182 | return self._event_source._ProcessBufferCallback(self, buffer) 183 | 184 | def _ProcessEventCallback(self, event_trace): 185 | try: 186 | # When in raw time mode, we need special processing 187 | # for the first event to calibrate the session start time. 188 | if not self._processed_first_event: 189 | self._ProcessFirstEvent(event_trace) 190 | self._processed_first_event = True 191 | 192 | self._event_source._ProcessEventCallback(self, event_trace) 193 | except: 194 | logging.exception('Exception in _ProcessEventCallback') 195 | 196 | class TraceEventSource(object): 197 | """An Event Tracing for Windows consumer class. 198 | 199 | To consume one or more logs, derive one or more handler classes from 200 | EventConsumer and declare a set of event handlers per the documentation 201 | of that class. Then instantiate one or more consumers and pass them into the 202 | TraceEventSource constructor, or add them to the list of handlers with 203 | AddHandler. 204 | Then proceed to open one or more sessions with OpenRealtimeSession and/or 205 | OpenFileSession, and lastly call Consume to consume the open sessions. 206 | This will fire events at the EventConsumers as the log is consumed. 207 | 208 | Note that each TraceEventSource can at most consume a single real time 209 | session, and no more than 63 sessions overall. 210 | """ 211 | 212 | def __init__(self, handlers=[], raw_time=False): 213 | """Creates an idle consumer. 214 | 215 | Args: 216 | handlers: an optional list of handlers to consume the log(s). 217 | Each handler should be an object derived from EventConsumer. 218 | raw_time: if True, consume logs with the raw time option. This allows 219 | converting stamps recorded in events to wall-clock time. 220 | """ 221 | self._stop = False 222 | self._handlers = handlers[:] 223 | self._raw_time = raw_time 224 | self._trace_sessions = [] 225 | self._handler_cache = dict() 226 | 227 | def __del__(self): 228 | """Clean up any trace sessions we have open.""" 229 | self.Close() 230 | 231 | def AddHandler(self, handler): 232 | """Add a new handler to this consumer. 233 | 234 | Args: 235 | handler: the handler to add. 236 | """ 237 | self._handlers.append(handler) 238 | # Clear our handler cache. 239 | self._handler_cache.clear() 240 | 241 | def OpenRealtimeSession(self, name): 242 | """Open a trace session named "name". 243 | 244 | Args: 245 | name: name of the session to open. 246 | """ 247 | session = _TraceLogSession(self, self._raw_time) 248 | session.OpenRealtimeSession(name) 249 | self._trace_sessions.append(session) 250 | 251 | def OpenFileSession(self, path): 252 | """Open a file session for the file at "path". 253 | 254 | Args: 255 | path: relative or absolute path to the file to open. 256 | """ 257 | session = _TraceLogSession(self, self._raw_time) 258 | session.OpenFileSession(path) 259 | self._trace_sessions.append(session) 260 | 261 | def Consume(self): 262 | """Consume all open sessions. 263 | 264 | Note: if any of the open sessions are realtime sessions, this function 265 | will not return until Close() is called to close the realtime session. 266 | """ 267 | handles = (evntrace.TRACEHANDLE * 268 | len(self._trace_sessions))() 269 | 270 | for i in range(len(self._trace_sessions)): 271 | handles[i] = self._trace_sessions[i]._handle 272 | 273 | evntrace.ProcessTrace(cast(handles, POINTER(evntrace.TRACEHANDLE)), 274 | len(handles), 275 | None, 276 | None) 277 | 278 | def Close(self): 279 | """Close all open trace sessions.""" 280 | while len(self._trace_sessions): 281 | session = self._trace_sessions.pop() 282 | session.Close() 283 | 284 | def ProcessEvent(self, session, event_trace): 285 | """Process a single event. 286 | 287 | Retrieve the guid, version and type from the event and try to find a handler 288 | for the event and event class that can parse the event data. If both exist, 289 | dispatch the event object to the handler. 290 | 291 | Args: 292 | session: the _TraceLogSession on which this event occurred. 293 | event_trace: a POINTER(EVENT_TRACE) for the current event. 294 | """ 295 | header = event_trace.contents.Header 296 | guid = str(header.Guid) 297 | version = header.Class.Version 298 | kind = header.Class.Type 299 | 300 | # Look for a handler and EventClass for the event. 301 | event_class = event.EventClass.Get(guid, version, kind) 302 | if event_class: 303 | handlers = self._GetHandlers(guid, kind) 304 | if handlers: 305 | event_obj = event_class(session, event_trace) 306 | for handler in handlers: 307 | handler(event_obj) 308 | 309 | def ProcessBuffer(self, session, buffer): 310 | """Process a buffer. 311 | 312 | Args: 313 | session: the _TraceLogSession on which this event occurred. 314 | event: a POINTER(TRACE_EVENT) for the current event. 315 | """ 316 | pass 317 | 318 | def _ProcessBufferCallback(self, session, buffer): 319 | try: 320 | self.ProcessBuffer(session, buffer) 321 | except: 322 | # Terminate parsing on exception. 323 | logging.exception("Exception in ProcessBuffer, terminating parsing") 324 | self._stop = True 325 | 326 | if self._stop: 327 | return 0 328 | else: 329 | return 1 330 | 331 | def _ProcessEventCallback(self, session, event): 332 | # Don't process the event if we're stopping. Note that we can only 333 | # terminate the processing once a whole buffer has been processed. 334 | if self._stop: 335 | return 336 | 337 | try: 338 | self.ProcessEvent(session, event) 339 | except: 340 | # Terminate parsing on exception. 341 | logging.exception("Exception in ProcessEvent, terminating parsing") 342 | self._stop = True 343 | 344 | def _GetHandlers(self, guid, kind): 345 | key = (guid, kind) 346 | handler_list = self._handler_cache.get(key, None) 347 | if handler_list != None: 348 | return handler_list 349 | 350 | # We didn't cache this already. 351 | handler_list = [] 352 | for handler_instance in self._handlers: 353 | for handler_func in handler_instance.event_handler_map.get(key, []): 354 | handler_list.append(_BindHandler(handler_func, handler_instance)) 355 | 356 | return handler_list 357 | -------------------------------------------------------------------------------- /etw/controller.py: -------------------------------------------------------------------------------- 1 | #!python 2 | # Copyright 2010 Google Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | """A Windows Event Trace controller class.""" 16 | 17 | from ctypes import addressof, byref, cast, memmove, sizeof 18 | from ctypes import c_char, c_wchar, c_wchar_p 19 | from ctypes import ArgumentError, pointer, POINTER 20 | import evntrace 21 | 22 | class TraceProperties(object): 23 | """A utility class to wrap usage of the EVENT_TRACE_PROPERTIES structure.""" 24 | max_string_len = 1024; 25 | buf_size = (sizeof(evntrace.EVENT_TRACE_PROPERTIES) + 26 | 2 * sizeof(c_wchar) * (max_string_len)) 27 | 28 | def __init__(self): 29 | self._buf = (c_char * self.buf_size)() 30 | self._props = cast(pointer(self._buf), 31 | POINTER(evntrace.EVENT_TRACE_PROPERTIES)) 32 | prop = self.get() 33 | prop.contents.Wnode.BufferSize = self.buf_size 34 | prop.contents.Wnode.Flags = evntrace.WNODE_FLAG_TRACED_GUID 35 | prop.contents.LoggerNameOffset = sizeof(evntrace.EVENT_TRACE_PROPERTIES) 36 | prop.contents.LogFileNameOffset = (sizeof(evntrace.EVENT_TRACE_PROPERTIES) + 37 | sizeof(c_wchar) * self.max_string_len) 38 | 39 | def get(self): 40 | return self._props 41 | 42 | def GetLoggerName(self): 43 | """Retrieves the current logger name from the buffer.""" 44 | props = self._props 45 | return c_wchar_p(addressof(props.contents) + 46 | props.contents.LoggerNameOffset) 47 | 48 | def GetLogFileName(self): 49 | """Retrieves the current log file name from the buffer.""" 50 | props = self._props 51 | return c_wchar_p(addressof(props.contents) + 52 | props.contents.LogFileNameOffset) 53 | 54 | def SetLogFileName(self, logger_name): 55 | """Set the current log file name stored in the buffer.""" 56 | name_len = len(logger_name) + 1 57 | if self.max_string_len < name_len : 58 | raise ArgumentError("Name too long") 59 | 60 | memmove(self.GetLogFileName(), 61 | c_wchar_p(logger_name), 62 | sizeof(c_wchar) * name_len) 63 | 64 | 65 | class TraceController(object): 66 | """Creates and manages a trace session, enables and disables providers.""" 67 | def __init__(self): 68 | """Create an idle controller.""" 69 | self._session = evntrace.TRACEHANDLE() 70 | self._session_name = "" 71 | 72 | def __del__(self): 73 | self._Cleanup() 74 | 75 | def _GetSession(self): 76 | return self._session 77 | 78 | def _GetSessionName(self): 79 | return self._session 80 | 81 | session = property(_GetSession, 82 | doc='The current session, if one in progress') 83 | session_name = property(_GetSessionName, 84 | doc='The current session name, if one in progress') 85 | 86 | def Start(self, name, properties): 87 | """Start a new trace session. 88 | 89 | Args: 90 | name: the name of the trace session. 91 | properties: a TraceProperties instance with session properties. 92 | """ 93 | session = evntrace.TRACEHANDLE() 94 | evntrace.StartTrace(byref(session), name, properties.get()) 95 | 96 | self._Cleanup() 97 | self._session_name = name 98 | self._session = session 99 | 100 | def Stop(self, properties = None): 101 | """Stop the current trace session. 102 | 103 | Args: 104 | properties: if provided, on success contains the stopped sessions 105 | properties. Use this to e.g. see whether any buffers were lost. 106 | """ 107 | if properties == None: 108 | properties = TraceProperties() 109 | 110 | session = self.session 111 | self._session = evntrace.TRACEHANDLE() 112 | self._session_name = "" 113 | evntrace.ControlTrace(session, 114 | None, 115 | properties.get(), 116 | evntrace.EVENT_TRACE_CONTROL_STOP) 117 | 118 | def EnableProvider(self, provider, level, flags = None): 119 | """Enable provider at level with flags. 120 | 121 | Args: 122 | provider: a GUID naming a provider. 123 | level: a trace level, e.g. etw.envtrace.TRACE_LEVEL_INFORMATION. 124 | flags: a set of enable flags to set for the provider. 125 | """ 126 | if flags == None: 127 | flags = 0 128 | evntrace.EnableTrace(True, flags, level, byref(provider), self.session) 129 | 130 | def DisableProvider(self, provider): 131 | """Disable provider. 132 | 133 | Args: 134 | provider: a GUID naming a provider. 135 | """ 136 | evntrace.EnableTrace(False, 137 | 0, 138 | evntrace.TRACE_LEVEL_NONE, 139 | byref(provider), 140 | self.session) 141 | 142 | def _Cleanup(self): 143 | if self.session: 144 | self.Stop() 145 | -------------------------------------------------------------------------------- /etw/descriptors/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebmarchand/pyetw/302431b0eecc7698f1b7641ed8cf39f8769beb4b/etw/descriptors/__init__.py -------------------------------------------------------------------------------- /etw/descriptors/binary_buffer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2.6 2 | # Copyright 2010 Google Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | """Helper classes for reading binary buffers.""" 16 | import ctypes 17 | import pywintypes 18 | 19 | ctypes.windll.advapi32.IsValidSid.argtypes = [ctypes.c_void_p] 20 | ctypes.windll.advapi32.GetLengthSid.argtypes = [ctypes.c_void_p] 21 | 22 | 23 | class BufferOverflowError(RuntimeError): 24 | """A custom error to throw when a buffer overflow occurs.""" 25 | 26 | 27 | class BufferDataError(RuntimeError): 28 | """A custom error to throw when the buffer contains invalid data.""" 29 | 30 | 31 | class BinaryBuffer(object): 32 | """A utility class to wrap a binary buffer. 33 | 34 | This class wraps a buffer of data and provides accessor methods for 35 | reading types from the buffer while checking to make sure accesses 36 | do not overflow the buffer. 37 | """ 38 | 39 | def __init__(self, start, length): 40 | """Create a new binary buffer. 41 | 42 | Args: 43 | start: Integer start address of the buffer. 44 | length: Length of the buffer. 45 | """ 46 | self._start = start 47 | self._length = length 48 | 49 | def Contains(self, offset, length): 50 | """Tests whether the current buffer contains the specified segment. 51 | 52 | Args: 53 | offset: Offset of the segment from the start of the buffer. 54 | length: Length of the segment. 55 | 56 | Returns: 57 | Whether the buffer contains the segment. 58 | """ 59 | if offset < 0: 60 | return False 61 | if length < 0: 62 | return False 63 | return offset + length <= self._length 64 | 65 | def GetAt(self, offset, length): 66 | """Gets the address of a segment in the buffer checking for overflow. 67 | 68 | Args: 69 | offset: Offset of the segment from the start of the buffer. 70 | length: Length of the segment. 71 | 72 | Returns: 73 | The address of the segment in the buffer. 74 | 75 | Raises: 76 | BufferOverflowError: The position and length specifies a segment that 77 | overflows the buffer. 78 | """ 79 | if not self.Contains(offset, length): 80 | raise BufferOverflowError() 81 | return self._start + offset 82 | 83 | def GetTypeAt(self, offset, data_type): 84 | """Gets a data type from an offset in the buffer. 85 | 86 | Args: 87 | offset: Offset of the data type from the start of the buffer. 88 | data_type: A ctypes type that specifies the data type to get from 89 | the buffer. 90 | 91 | Returns: 92 | The value of the data type at the offset within the buffer. 93 | """ 94 | return ctypes.cast(self.GetAt(offset, ctypes.sizeof(data_type)), 95 | ctypes.POINTER(data_type)).contents.value 96 | 97 | def GetStringAt(self, offset): 98 | """Gets a string from an offset in the buffer. 99 | 100 | Args: 101 | offset: Offset of the string from the start of the buffer. 102 | 103 | Returns: 104 | The string starting at position offset within the buffer. 105 | """ 106 | return ctypes.string_at(self.GetAt(offset, ctypes.sizeof(ctypes.c_char))) 107 | 108 | def GetWStringAt(self, offset): 109 | """Gets a string from an offset in the buffer. 110 | 111 | Args: 112 | offset: Offset of the string from the start of the buffer. 113 | 114 | Returns: 115 | The string starting at position offset within the buffer. 116 | """ 117 | return ctypes.wstring_at(self.GetAt(offset, ctypes.sizeof(ctypes.c_wchar))) 118 | 119 | 120 | class BinaryBufferReader(object): 121 | """A utility class to help read values from a buffer. 122 | 123 | This class wraps a binary buffer and maintains a current position so that 124 | consecutive values can be read out of the buffer. It provides methods 125 | for reading specific types and consuming data while checking for overflow. 126 | """ 127 | 128 | def __init__(self, start, length): 129 | """Creates a new binary buffer reader. 130 | 131 | Args: 132 | start: Integer start address of the buffer. 133 | length: Length of the buffer. 134 | """ 135 | self._buffer = BinaryBuffer(start, length) 136 | self._offset = 0 137 | 138 | def Consume(self, length): 139 | """Advances the current offset in the buffer by length. 140 | 141 | Args: 142 | length: The length to consume. 143 | 144 | Raises: 145 | BufferOverflowError: Consuming the specified length will overflow 146 | the buffer. 147 | """ 148 | if not self._buffer.Contains(self._offset, length): 149 | raise BufferOverflowError() 150 | self._offset += length 151 | 152 | def Read(self, data_type): 153 | """Reads the value of the data type from the current offset in the buffer. 154 | 155 | Args: 156 | data_type: A ctypes type that specifies the data type to get from 157 | the buffer. 158 | 159 | Returns: 160 | The value of the data type at the current offset in the buffer. 161 | """ 162 | val = self._buffer.GetTypeAt(self._offset, data_type) 163 | self.Consume(ctypes.sizeof(data_type)) 164 | return val 165 | 166 | def ReadBoolean(self): 167 | return self.Read(ctypes.c_byte) != 0 168 | 169 | def ReadInt8(self): 170 | return self.Read(ctypes.c_byte) 171 | 172 | def ReadUInt8(self): 173 | return self.Read(ctypes.c_ubyte) 174 | 175 | def ReadInt16(self): 176 | return self.Read(ctypes.c_short) 177 | 178 | def ReadUInt16(self): 179 | return self.Read(ctypes.c_ushort) 180 | 181 | def ReadInt32(self): 182 | return self.Read(ctypes.c_int) 183 | 184 | def ReadUInt32(self): 185 | return self.Read(ctypes.c_uint) 186 | 187 | def ReadInt64(self): 188 | return self.Read(ctypes.c_longlong) 189 | 190 | def ReadUInt64(self): 191 | return self.Read(ctypes.c_ulonglong) 192 | 193 | def ReadString(self): 194 | val = self._buffer.GetStringAt(self._offset) 195 | self.Consume(len(val) + ctypes.sizeof(ctypes.c_char)) 196 | return val 197 | 198 | def ReadWString(self): 199 | val = self._buffer.GetWStringAt(self._offset) 200 | self.Consume(len(val) + ctypes.sizeof(ctypes.c_wchar)) 201 | return val 202 | 203 | _MINIMUM_SID_SIZE = 8 204 | 205 | def ReadSid(self, is_64_bit_ptrs): 206 | """Reads a SID from the current offset in the buffer. 207 | 208 | Args: 209 | is_64_bit_ptrs: Whether the current buffer contains 64 bit pointers. 210 | 211 | Returns: 212 | The SID at the current offset in the buffer. 213 | 214 | Raises: 215 | BufferDataError: Raised if the buffer does not contain a valid SID at 216 | this offset. 217 | """ 218 | # Two pointers are included before the SID. If the first one is zero, 219 | # there is no more data, so read that one first. 220 | if is_64_bit_ptrs: 221 | ReadPtr = self.ReadUInt64 222 | else: 223 | ReadPtr = self.ReadUInt32 224 | 225 | has_sid = ReadPtr() 226 | if not has_sid: 227 | return None 228 | 229 | # Ignore the second pointer. 230 | ignore = ReadPtr() 231 | 232 | data = self._buffer.GetAt(self._offset, self._MINIMUM_SID_SIZE) 233 | if not ctypes.windll.advapi32.IsValidSid(data): 234 | raise BufferDataError('Invalid SID.') 235 | sid_len = ctypes.windll.advapi32.GetLengthSid(data) 236 | self.Consume(sid_len) 237 | 238 | sid_buffer = ctypes.string_at(data, sid_len) 239 | return pywintypes.SID(sid_buffer) 240 | -------------------------------------------------------------------------------- /etw/descriptors/event.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2.6 2 | # Copyright 2010 Google Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | """EventClass and EventCategory base classes for Event descriptors.""" 16 | import inspect 17 | from etw.descriptors import binary_buffer 18 | 19 | 20 | class EventClass(object): 21 | """Base class for event classes. 22 | 23 | The EventClass class is used to transform an event data buffer into a set of 24 | named attributes on the object. Classes that want to parse event data should 25 | derive from this class and define the _fields_ property: 26 | 27 | class MyEventClass(EventClass): 28 | _fields_ = [('IntField', field.Int32), 29 | ('StringField', field.String)] 30 | 31 | The constructor iterates over the _fields_ property and invokes the function 32 | defined in the second half of the tuple. The return value is assigned as a 33 | named attribute of this class, the name being the first half of the tuple. The 34 | function in the second half of the tuple should take a TraceLogSession and a 35 | BinaryBufferReader as parameters and should return a mixed value. 36 | 37 | Subclasses must also define the _event_types_ list. This will cause the 38 | subclass to be registered in the the EventClass's subclass map for each event 39 | listed in _event_types_. This is used by the log consumer to identify the 40 | proper event class to create when handling a particular log event. 41 | 42 | Attributes: 43 | process_id: The ID of the process that generated the event. 44 | thread_id: The ID of the thread that generated the event. 45 | raw_time_stamp: The raw time stamp of the ETW event. 46 | time_stamp: The timestamp of the event (in seconds since 01-01-1970). 47 | """ 48 | # A map of all classes that derive from this class. The keys are 49 | # (string guid, number version, number event_type) tuples and the values are 50 | # the derived classes. 51 | _subclass_map = {} 52 | 53 | def __init__(self, log_session, event_trace): 54 | """Initialize by extracting event trace header and MOF data. 55 | 56 | Args: 57 | event_trace: a POINTER(EVENT_TRACE) for the current event. 58 | is_64_bit_log: whether the log is from a 64 bit system. 59 | """ 60 | header = event_trace.contents.Header 61 | self.process_id = header.ProcessId 62 | self.thread_id = header.ThreadId 63 | 64 | self.raw_time_stamp = header.TimeStamp 65 | self.time_stamp = log_session.SessionTimeToTime(header.TimeStamp) 66 | reader = binary_buffer.BinaryBufferReader(event_trace.contents.MofData, 67 | event_trace.contents.MofLength) 68 | for name, field in self._fields_: 69 | setattr(self, name, field(log_session, reader)) 70 | 71 | @staticmethod 72 | def Get(guid, version, event_type): 73 | """Returns the subclass for the given guid, version and event_type. 74 | 75 | Args: 76 | guid: The event category guid as a string. 77 | version: The version of the event as a number. 78 | event_type: The type of the event as a number. 79 | 80 | Returns: 81 | The type of the EventClass subclass that matches the 82 | guid/version/event_type tuple. 83 | """ 84 | key = guid, version, event_type 85 | return EventClass._subclass_map.get(key, None) 86 | 87 | @staticmethod 88 | def Set(guid, version, event_type, subclass): 89 | """Sets the subclass for the given guid, version and event_type. 90 | 91 | Args: 92 | guid: The event category guid as a string. 93 | version: The version of the event as a number. 94 | event_type: The type of the event as a number. 95 | subclass: The EventClass subclass to add to the map. 96 | """ 97 | key = guid, version, event_type 98 | EventClass._subclass_map[key] = subclass 99 | 100 | @classmethod 101 | def GetEventTypes(cls): 102 | return cls._event_types_ 103 | 104 | class MetaEventCategory(type): 105 | """Meta class for EventCategory. 106 | 107 | The purpose of this metaclass is to populate a map of EventClass classes 108 | that are defined as subclasses of EventCategory classes. When an EventCategory 109 | class is defined, the __new__ method is called, and the nested EventClass 110 | classes are saved into map using a tuple of the EventCategory's GUID and 111 | version along with the nested class' event types as a key. The populated map 112 | is accessed statically through the EventClass.Get method to retrieve a defined 113 | EventClass. 114 | """ 115 | 116 | def __new__(cls, name, bases, attrs): 117 | """Create a new EventCategory class. 118 | 119 | Args: 120 | name: The name of the class to create. 121 | bases: The base classes of the class to create. 122 | attrs: The attributes of the class to create. 123 | 124 | Returns: 125 | A new class with the specified name, base classes and attributes. 126 | """ 127 | for value in attrs.values(): 128 | if inspect.isclass(value) and issubclass(value, EventClass): 129 | for event_type in value.GetEventTypes(): 130 | EventClass.Set(attrs['GUID'], attrs['VERSION'], event_type[1], value) 131 | return type.__new__(cls, name, bases, attrs) 132 | 133 | 134 | class EventCategory(object): 135 | """Base class for event categories. 136 | 137 | The EventCategory class provides subclasses with a common metaclass to enable 138 | the popuplation of its event class map. Subclasses must define GUID and 139 | VERSION constants as they are used as keys to the map. 140 | """ 141 | __metaclass__ = MetaEventCategory 142 | -------------------------------------------------------------------------------- /etw/descriptors/field.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2.6 2 | # Copyright 2010 Google Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | """Function definitions for callable field types. 17 | 18 | The functions defined here are meant to be used as callable field types 19 | used with event descriptor field definitions. EventClass subclasses are 20 | defined like: 21 | 22 | class MyEventClass(EventClass): 23 | _fields_ = [('IntField', field.Int32), 24 | ('StringField', field.String)] 25 | 26 | When a log event is parsed, the EventClass is created, and each callable 27 | field type is invoked. The return value is assigned to the EventClass using 28 | the name provided in the _fields_ list. 29 | 30 | To add a new field type, define a function that takes these arguments: 31 | session: The _TraceLogSession instance the event arrived on. 32 | This has a session-related properties and functionality, such as 33 | the is_64_bit_log property and the SessionTimeToTime member that 34 | will convert a time stamp in the session's units to a python time. 35 | reader: An instance of the BinaryBufferReader class to read from. 36 | and returns a mixed value. If the BinaryBufferReader doesn't already have 37 | a function to read a certain type, it will need to be added as well. 38 | """ 39 | 40 | 41 | def Boolean(unused_session, reader): 42 | return reader.ReadBoolean() 43 | 44 | 45 | def Int8(unused_session, reader): 46 | return reader.ReadInt8() 47 | 48 | 49 | def UInt8(unused_session, reader): 50 | return reader.ReadUInt8() 51 | 52 | 53 | def Int16(unused_session, reader): 54 | return reader.ReadInt16() 55 | 56 | 57 | def UInt16(unused_session, reader): 58 | return reader.ReadUInt16() 59 | 60 | 61 | def Int32(unused_session, reader): 62 | return reader.ReadInt32() 63 | 64 | 65 | def UInt32(unused_session, reader): 66 | return reader.ReadUInt32() 67 | 68 | 69 | def Int64(unused_session, reader): 70 | return reader.ReadInt64() 71 | 72 | 73 | def UInt64(unused_session, reader): 74 | return reader.ReadUInt64() 75 | 76 | 77 | def Pointer(session, reader): 78 | if session.is_64_bit_log: 79 | return reader.ReadUInt64() 80 | else: 81 | return reader.ReadUInt32() 82 | 83 | 84 | def String(unused_session, reader): 85 | return reader.ReadString() 86 | 87 | 88 | def WString(unused_session, reader): 89 | return reader.ReadWString() 90 | 91 | 92 | def Sid(session, reader): 93 | return reader.ReadSid(session.is_64_bit_log) 94 | 95 | 96 | def WmiTime(session, reader): 97 | return session.SessionTimeToTime(reader.ReadUInt64()) 98 | -------------------------------------------------------------------------------- /etw/descriptors/fileio.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2.6 2 | # Copyright 2010 Google Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | """Generated event descriptor file for a MOF event class. 16 | 17 | DO NOT EDIT. This is an ETW event descriptor file generated by 18 | sawbuck/py/etw/generate_descriptor.py. It contains event descriptions for 19 | MOF GUID {90cbdc39-4a3e-11d1-84f4-0000f80464e3}. 20 | """ 21 | 22 | 23 | from etw.descriptors import event 24 | from etw.descriptors import field 25 | 26 | 27 | class Event(object): 28 | GUID = '{90cbdc39-4a3e-11d1-84f4-0000f80464e3}' 29 | Name = (GUID, 0) 30 | FileCreate = (GUID, 32) 31 | FileDelete = (GUID, 35) 32 | FileRundown = (GUID, 36) 33 | Create = (GUID, 64) 34 | Cleanup = (GUID, 65) 35 | Close = (GUID, 66) 36 | Read = (GUID, 67) 37 | Write = (GUID, 68) 38 | SetInfo = (GUID, 69) 39 | Delete = (GUID, 70) 40 | Rename = (GUID, 71) 41 | DirEnum = (GUID, 72) 42 | Flush = (GUID, 73) 43 | QueryInfo = (GUID, 74) 44 | FSControl = (GUID, 75) 45 | OperationEnd = (GUID, 76) 46 | DirNotify = (GUID, 77) 47 | 48 | 49 | class FileIo(event.EventCategory): 50 | GUID = Event.GUID 51 | VERSION = 2 52 | 53 | class ReadWrite(event.EventClass): 54 | _event_types_ = [Event.Read, 55 | Event.Write] 56 | _fields_ = [('Offset', field.UInt64), 57 | ('IrpPtr', field.Pointer), 58 | ('TTID', field.Pointer), 59 | ('FileObject', field.Pointer), 60 | ('FileKey', field.Pointer), 61 | ('IoSize', field.UInt32), 62 | ('IoFlags', field.UInt32)] 63 | 64 | class Create(event.EventClass): 65 | _event_types_ = [Event.Create] 66 | _fields_ = [('IrpPtr', field.Pointer), 67 | ('TTID', field.Pointer), 68 | ('FileObject', field.Pointer), 69 | ('CreateOptions', field.UInt32), 70 | ('FileAttributes', field.UInt32), 71 | ('ShareAccess', field.UInt32), 72 | ('OpenPath', field.WString)] 73 | 74 | class Name(event.EventClass): 75 | _event_types_ = [Event.FileCreate, 76 | Event.FileDelete, 77 | Event.FileRundown, 78 | Event.Name] 79 | _fields_ = [('FileObject', field.Pointer), 80 | ('FileName', field.WString)] 81 | 82 | class Info(event.EventClass): 83 | _event_types_ = [Event.Delete, 84 | Event.FSControl, 85 | Event.QueryInfo, 86 | Event.Rename, 87 | Event.SetInfo] 88 | _fields_ = [('IrpPtr', field.Pointer), 89 | ('TTID', field.Pointer), 90 | ('FileObject', field.Pointer), 91 | ('FileKey', field.Pointer), 92 | ('ExtraInfo', field.Pointer), 93 | ('InfoClass', field.UInt32)] 94 | 95 | class SimpleOp(event.EventClass): 96 | _event_types_ = [Event.Cleanup, 97 | Event.Close, 98 | Event.Flush] 99 | _fields_ = [('IrpPtr', field.Pointer), 100 | ('TTID', field.Pointer), 101 | ('FileObject', field.Pointer), 102 | ('FileKey', field.Pointer)] 103 | 104 | class DirEnum(event.EventClass): 105 | _event_types_ = [Event.DirEnum, 106 | Event.DirNotify] 107 | _fields_ = [('IrpPtr', field.Pointer), 108 | ('TTID', field.Pointer), 109 | ('FileObject', field.Pointer), 110 | ('FileKey', field.Pointer), 111 | ('Length', field.UInt32), 112 | ('InfoClass', field.UInt32), 113 | ('FileIndex', field.UInt32), 114 | ('FileName', field.WString)] 115 | 116 | class OpEnd(event.EventClass): 117 | _event_types_ = [Event.OperationEnd] 118 | _fields_ = [('IrpPtr', field.Pointer), 119 | ('ExtraInfo', field.Pointer), 120 | ('NtStatus', field.UInt32)] 121 | 122 | 123 | class FileIo_V0(event.EventCategory): 124 | GUID = Event.GUID 125 | VERSION = 0 126 | 127 | class Name(event.EventClass): 128 | _event_types_ = [Event.Name] 129 | _fields_ = [('FileObject', field.Pointer), 130 | ('FileName', field.WString)] 131 | 132 | 133 | class FileIo_V1(event.EventCategory): 134 | GUID = Event.GUID 135 | VERSION = 1 136 | 137 | class Name(event.EventClass): 138 | _event_types_ = [Event.FileCreate, 139 | Event.Name] 140 | _fields_ = [('FileObject', field.Pointer), 141 | ('FileName', field.WString)] 142 | -------------------------------------------------------------------------------- /etw/descriptors/image.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2.6 2 | # Copyright 2010 Google Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | """Generated event descriptor file for a MOF event class. 16 | 17 | DO NOT EDIT. This is an ETW event descriptor file generated by 18 | sawbuck/py/etw/generate_descriptor.py. It contains event descriptions for 19 | MOF GUID {2cb15d1d-5fc1-11d2-abe1-00a0c911f518}. 20 | """ 21 | 22 | 23 | from etw.descriptors import event 24 | from etw.descriptors import field 25 | 26 | 27 | class Event(object): 28 | GUID = '{2cb15d1d-5fc1-11d2-abe1-00a0c911f518}' 29 | UnLoad = (GUID, 2) 30 | DCStart = (GUID, 3) 31 | DCEnd = (GUID, 4) 32 | Load = (GUID, 10) 33 | KernelBase = (GUID, 33) 34 | 35 | 36 | class Image_V0(event.EventCategory): 37 | GUID = Event.GUID 38 | VERSION = 0 39 | 40 | class Load(event.EventClass): 41 | _event_types_ = [Event.Load] 42 | _fields_ = [('BaseAddress', field.Pointer), 43 | ('ModuleSize', field.UInt32), 44 | ('ImageFileName', field.WString)] 45 | 46 | 47 | class Image_V1(event.EventCategory): 48 | GUID = Event.GUID 49 | VERSION = 1 50 | 51 | class Load(event.EventClass): 52 | _event_types_ = [Event.Load] 53 | _fields_ = [('ImageBase', field.Pointer), 54 | ('ImageSize', field.Pointer), 55 | ('ProcessId', field.UInt32), 56 | ('FileName', field.WString)] 57 | 58 | 59 | class Image(event.EventCategory): 60 | GUID = Event.GUID 61 | VERSION = 2 62 | 63 | class Load(event.EventClass): 64 | _event_types_ = [Event.DCEnd, 65 | Event.DCStart, 66 | Event.Load, 67 | Event.UnLoad] 68 | _fields_ = [('ImageBase', field.Pointer), 69 | ('ImageSize', field.Pointer), 70 | ('ProcessId', field.UInt32), 71 | ('ImageChecksum', field.UInt32), 72 | ('TimeDateStamp', field.UInt32), 73 | ('Reserved0', field.UInt32), 74 | ('DefaultBase', field.Pointer), 75 | ('Reserved1', field.UInt32), 76 | ('Reserved2', field.UInt32), 77 | ('Reserved3', field.UInt32), 78 | ('Reserved4', field.UInt32), 79 | ('FileName', field.WString)] 80 | 81 | class KernelImageBase(event.EventClass): 82 | _event_types_ = [Event.KernelBase] 83 | _fields_ = [('ImageBase', field.Pointer)] 84 | -------------------------------------------------------------------------------- /etw/descriptors/pagefault.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2.6 2 | # Copyright 2010 Google Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | """Generated event descriptor file for a MOF event class. 16 | 17 | DO NOT EDIT. This is an ETW event descriptor file generated by 18 | sawbuck/py/etw/generate_descriptor.py. It contains event descriptions for 19 | MOF GUID {3d6fa8d3-fe05-11d0-9dda-00c04fd7ba7c}. 20 | """ 21 | 22 | 23 | from etw.descriptors import event 24 | from etw.descriptors import field 25 | 26 | 27 | class Event(object): 28 | GUID = '{3d6fa8d3-fe05-11d0-9dda-00c04fd7ba7c}' 29 | TransitionFault = (GUID, 10) 30 | DemandZeroFault = (GUID, 11) 31 | CopyOnWrite = (GUID, 12) 32 | GuardPageFault = (GUID, 13) 33 | HardPageFault = (GUID, 14) 34 | AccessViolation = (GUID, 15) 35 | HardFault = (GUID, 32) 36 | VirtualAlloc = (GUID, 98) 37 | VirtualFree = (GUID, 99) 38 | HRRundown = (GUID, 100) 39 | HRCreate = (GUID, 101) 40 | HRReserve = (GUID, 102) 41 | HRRelease = (GUID, 103) 42 | HRDestroy = (GUID, 104) 43 | ImageLoadBacked = (GUID, 105) 44 | 45 | 46 | class PageFault_V2(event.EventCategory): 47 | GUID = Event.GUID 48 | VERSION = 2 49 | 50 | class PageFault_HardFault(event.EventClass): 51 | _event_types_ = [Event.HardFault] 52 | _fields_ = [('InitialTime', field.WmiTime), 53 | ('ReadOffset', field.UInt64), 54 | ('VirtualAddress', field.Pointer), 55 | ('FileObject', field.Pointer), 56 | ('TThreadId', field.UInt32), 57 | ('ByteCount', field.UInt32)] 58 | 59 | class PageFault_VirtualAlloc(event.EventClass): 60 | _event_types_ = [Event.VirtualAlloc, 61 | Event.VirtualFree] 62 | _fields_ = [('BaseAddress', field.Pointer), 63 | ('RegionSize', field.Int32), 64 | ('ProcessId', field.UInt32), 65 | ('Flags', field.UInt32)] 66 | 67 | class PageFault_HeapRangeRundown_V2(event.EventClass): 68 | _event_types_ = [Event.HRRundown] 69 | _fields_ = [('HeapHandle', field.Pointer), 70 | ('HRFlags', field.UInt32), 71 | ('HRPid', field.UInt32), 72 | ('HRRangeCount', field.UInt32)] 73 | 74 | class PageFault_HeapRangeDestroy(event.EventClass): 75 | _event_types_ = [Event.HRDestroy] 76 | _fields_ = [('HeapHandle', field.Pointer)] 77 | 78 | class PageFault_TypeGroup1(event.EventClass): 79 | _event_types_ = [Event.AccessViolation, 80 | Event.CopyOnWrite, 81 | Event.DemandZeroFault, 82 | Event.GuardPageFault, 83 | Event.HardPageFault, 84 | Event.TransitionFault] 85 | _fields_ = [('VirtualAddress', field.Pointer), 86 | ('ProgramCounter', field.Pointer)] 87 | 88 | class PageFault_ImageLoadBacked(event.EventClass): 89 | _event_types_ = [Event.ImageLoadBacked] 90 | _fields_ = [('FileObject', field.Pointer), 91 | ('DeviceChar', field.UInt32), 92 | ('FileChar', field.UInt16), 93 | ('LoadFlags', field.UInt16)] 94 | 95 | class PageFault_HeapRangeTypeGroup(event.EventClass): 96 | _event_types_ = [Event.HRRelease, 97 | Event.HRReserve] 98 | _fields_ = [('HeapHandle', field.Pointer), 99 | ('HRAddress', field.Pointer), 100 | ('HRSize', field.Int32)] 101 | 102 | class PageFault_HeapRangeCreate(event.EventClass): 103 | _event_types_ = [Event.HRCreate] 104 | _fields_ = [('HeapHandle', field.Pointer), 105 | ('FirstRangeSize', field.Int32), 106 | ('HRCreateFlags', field.UInt32)] 107 | 108 | 109 | class PageFault(event.EventCategory): 110 | GUID = Event.GUID 111 | VERSION = 3 112 | 113 | class HeapRangeRundown(event.EventClass): 114 | _event_types_ = [Event.HRRundown] 115 | _fields_ = [('HeapHandle', field.Pointer), 116 | ('HRFlags', field.UInt32), 117 | ('HRPid', field.UInt32), 118 | ('HRRangeCount', field.UInt32), 119 | ('Reserved', field.UInt32)] 120 | -------------------------------------------------------------------------------- /etw/descriptors/pagefault_xp.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2.6 2 | # Copyright 2010 Google Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | """Hand-crafted event descriptor file for XP page faults. 16 | 17 | These descriptions are missing from the MOF data on Win7, so the gaps 18 | are plugged by hand here. 19 | """ 20 | 21 | from etw.descriptors.pagefault import Event 22 | from etw.descriptors import event 23 | from etw.descriptors import field 24 | 25 | 26 | class PageFault_V1(event.EventCategory): 27 | GUID = Event.GUID 28 | VERSION = 1 29 | 30 | class PageFault_HardFault(event.EventClass): 31 | _event_types_ = [Event.HardFault] 32 | _fields_ = [('InitialTime', field.WmiTime), 33 | ('ReadOffset', field.UInt64), 34 | ('VirtualAddress', field.Pointer), 35 | ('FileObject', field.Pointer), 36 | ('TThreadId', field.UInt32), 37 | ('ByteCount', field.UInt32)] 38 | 39 | class PageFault_TypeGroup1(event.EventClass): 40 | _event_types_ = [Event.AccessViolation, 41 | Event.CopyOnWrite, 42 | Event.DemandZeroFault, 43 | Event.GuardPageFault, 44 | Event.HardPageFault, 45 | Event.TransitionFault] 46 | _fields_ = [('VirtualAddress', field.Pointer), 47 | ('ProgramCounter', field.Pointer)] 48 | -------------------------------------------------------------------------------- /etw/descriptors/process.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2.6 2 | # Copyright 2010 Google Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | """Generated event descriptor file for a MOF event class. 16 | 17 | DO NOT EDIT. This is an ETW event descriptor file generated by 18 | sawbuck/py/etw/generate_descriptor.py. It contains event descriptions for 19 | MOF GUID {3d6fa8d0-fe05-11d0-9dda-00c04fd7ba7c}. 20 | """ 21 | 22 | 23 | from etw.descriptors import event 24 | from etw.descriptors import field 25 | 26 | 27 | class Event(object): 28 | GUID = '{3d6fa8d0-fe05-11d0-9dda-00c04fd7ba7c}' 29 | Start = (GUID, 1) 30 | End = (GUID, 2) 31 | DCStart = (GUID, 3) 32 | DCEnd = (GUID, 4) 33 | PerfCtr = (GUID, 32) 34 | PerfCtrRundown = (GUID, 33) 35 | InSwap = (GUID, 35) 36 | Defunct = (GUID, 39) 37 | 38 | 39 | class Process_V1(event.EventCategory): 40 | GUID = Event.GUID 41 | VERSION = 1 42 | 43 | class TypeGroup1(event.EventClass): 44 | _event_types_ = [Event.DCEnd, 45 | Event.DCStart, 46 | Event.End, 47 | Event.Start] 48 | _fields_ = [('PageDirectoryBase', field.Pointer), 49 | ('ProcessId', field.UInt32), 50 | ('ParentId', field.UInt32), 51 | ('SessionId', field.UInt32), 52 | ('ExitStatus', field.Int32), 53 | ('UserSID', field.Sid), 54 | ('ImageFileName', field.String)] 55 | 56 | 57 | class Process_V0(event.EventCategory): 58 | GUID = Event.GUID 59 | VERSION = 0 60 | 61 | class TypeGroup1(event.EventClass): 62 | _event_types_ = [Event.DCEnd, 63 | Event.DCStart, 64 | Event.End, 65 | Event.Start] 66 | _fields_ = [('ProcessId', field.Pointer), 67 | ('ParentId', field.Pointer), 68 | ('UserSID', field.Sid), 69 | ('ImageFileName', field.String)] 70 | 71 | 72 | class Process(event.EventCategory): 73 | GUID = Event.GUID 74 | VERSION = 3 75 | 76 | class TypeGroup1(event.EventClass): 77 | _event_types_ = [Event.DCEnd, 78 | Event.DCStart, 79 | Event.Defunct, 80 | Event.End, 81 | Event.Start] 82 | _fields_ = [('UniqueProcessKey', field.Pointer), 83 | ('ProcessId', field.UInt32), 84 | ('ParentId', field.UInt32), 85 | ('SessionId', field.UInt32), 86 | ('ExitStatus', field.Int32), 87 | ('DirectoryTableBase', field.Pointer), 88 | ('UserSID', field.Sid), 89 | ('ImageFileName', field.String), 90 | ('CommandLine', field.WString)] 91 | 92 | 93 | class Process_V2(event.EventCategory): 94 | GUID = Event.GUID 95 | VERSION = 2 96 | 97 | class TypeGroup2(event.EventClass): 98 | _event_types_ = [Event.PerfCtr, 99 | Event.PerfCtrRundown] 100 | _fields_ = [('ProcessId', field.UInt32), 101 | ('PageFaultCount', field.UInt32), 102 | ('HandleCount', field.UInt32), 103 | ('Reserved', field.UInt32), 104 | ('PeakVirtualSize', field.Int32), 105 | ('PeakWorkingSetSize', field.Int32), 106 | ('PeakPagefileUsage', field.Int32), 107 | ('QuotaPeakPagedPoolUsage', field.Int32), 108 | ('QuotaPeakNonPagedPoolUsage', field.Int32), 109 | ('VirtualSize', field.Int32), 110 | ('WorkingSetSize', field.Int32), 111 | ('PagefileUsage', field.Int32), 112 | ('QuotaPagedPoolUsage', field.Int32), 113 | ('QuotaNonPagedPoolUsage', field.Int32), 114 | ('PrivatePageCount', field.Int32)] 115 | 116 | class TypeGroup1(event.EventClass): 117 | _event_types_ = [Event.DCEnd, 118 | Event.DCStart, 119 | Event.Defunct, 120 | Event.End, 121 | Event.Start] 122 | _fields_ = [('UniqueProcessKey', field.Pointer), 123 | ('ProcessId', field.UInt32), 124 | ('ParentId', field.UInt32), 125 | ('SessionId', field.UInt32), 126 | ('ExitStatus', field.Int32), 127 | ('UserSID', field.Sid), 128 | ('ImageFileName', field.String), 129 | ('CommandLine', field.WString)] 130 | 131 | class TypeGroup3(event.EventClass): 132 | _event_types_ = [Event.InSwap] 133 | _fields_ = [('DirectoryTableBase', field.Pointer), 134 | ('ProcessId', field.UInt32)] 135 | -------------------------------------------------------------------------------- /etw/descriptors/registry.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2.6 2 | # Copyright 2010 Google Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | """Generated event descriptor file for a MOF event class. 16 | 17 | DO NOT EDIT. This is an ETW event descriptor file generated by 18 | sawbuck/py/etw/generate_descriptor.py. It contains event descriptions for 19 | MOF GUID {ae53722e-c863-11d2-8659-00c04fa321a1}. 20 | """ 21 | 22 | 23 | from etw.descriptors import event 24 | from etw.descriptors import field 25 | 26 | 27 | class Event(object): 28 | GUID = '{ae53722e-c863-11d2-8659-00c04fa321a1}' 29 | Create = (GUID, 10) 30 | Open = (GUID, 11) 31 | Delete = (GUID, 12) 32 | Query = (GUID, 13) 33 | SetValue = (GUID, 14) 34 | DeleteValue = (GUID, 15) 35 | QueryValue = (GUID, 16) 36 | EnumerateKey = (GUID, 17) 37 | EnumerateValueKey = (GUID, 18) 38 | QueryMultipleValue = (GUID, 19) 39 | SetInformation = (GUID, 20) 40 | Flush = (GUID, 21) 41 | RunDown = (GUID, 22) 42 | KCBDelete = (GUID, 23) 43 | KCBRundownBegin = (GUID, 24) 44 | KCBRundownEnd = (GUID, 25) 45 | Virtualize = (GUID, 26) 46 | Close = (GUID, 27) 47 | SetSecurity = (GUID, 28) 48 | QuerySecurity = (GUID, 29) 49 | TxRCommit = (GUID, 30) 50 | TxRPrepare = (GUID, 31) 51 | TxRRollback = (GUID, 32) 52 | Counters = (GUID, 34) 53 | Config = (GUID, 35) 54 | 55 | 56 | class Registry(event.EventCategory): 57 | GUID = Event.GUID 58 | VERSION = 2 59 | 60 | class Config(event.EventClass): 61 | _event_types_ = [Event.Config] 62 | _fields_ = [('CurrentControlSet', field.UInt32)] 63 | 64 | class Counters(event.EventClass): 65 | _event_types_ = [Event.Counters] 66 | _fields_ = [('Counter1', field.UInt64), 67 | ('Counter2', field.UInt64), 68 | ('Counter3', field.UInt64), 69 | ('Counter4', field.UInt64), 70 | ('Counter5', field.UInt64), 71 | ('Counter6', field.UInt64), 72 | ('Counter7', field.UInt64), 73 | ('Counter8', field.UInt64), 74 | ('Counter9', field.UInt64), 75 | ('Counter10', field.UInt64), 76 | ('Counter11', field.UInt64)] 77 | 78 | class TypeGroup1(event.EventClass): 79 | _event_types_ = [Event.Close, 80 | Event.Create, 81 | Event.Delete, 82 | Event.DeleteValue, 83 | Event.EnumerateKey, 84 | Event.EnumerateValueKey, 85 | Event.Flush, 86 | Event.KCBCreate, 87 | Event.KCBDelete, 88 | Event.KCBRundownBegin, 89 | Event.KCBRundownEnd, 90 | Event.Open, 91 | Event.Query, 92 | Event.QueryMultipleValue, 93 | Event.QuerySecurity, 94 | Event.QueryValue, 95 | Event.SetInformation, 96 | Event.SetSecurity, 97 | Event.SetValue, 98 | Event.Virtualize] 99 | _fields_ = [('InitialTime', field.Int64), 100 | ('Status', field.UInt32), 101 | ('Index', field.UInt32), 102 | ('KeyHandle', field.Pointer), 103 | ('KeyName', field.WString)] 104 | 105 | class TxR(event.EventClass): 106 | _event_types_ = [Event.TxRCommit, 107 | Event.TxRPrepare, 108 | Event.TxRRollback] 109 | _fields_ = [('TxrGUID', field.UInt8), 110 | ('Status', field.UInt32), 111 | ('UowCount', field.UInt32), 112 | ('OperationTime', field.UInt64), 113 | ('Hive', field.WString)] 114 | 115 | 116 | class Registry_V0(event.EventCategory): 117 | GUID = Event.GUID 118 | VERSION = 0 119 | 120 | class TypeGroup1(event.EventClass): 121 | _event_types_ = [Event.Create, 122 | Event.Delete, 123 | Event.DeleteValue, 124 | Event.EnumerateKey, 125 | Event.EnumerateValueKey, 126 | Event.Flush, 127 | Event.Open, 128 | Event.Query, 129 | Event.QueryMultipleValue, 130 | Event.QueryValue, 131 | Event.SetInformation, 132 | Event.SetValue] 133 | _fields_ = [('Status', field.Pointer), 134 | ('KeyHandle', field.Pointer), 135 | ('ElapsedTime', field.Int64), 136 | ('KeyName', field.WString)] 137 | 138 | 139 | class Registry_V1(event.EventCategory): 140 | GUID = Event.GUID 141 | VERSION = 1 142 | 143 | class TypeGroup1(event.EventClass): 144 | _event_types_ = [Event.Create, 145 | Event.Delete, 146 | Event.DeleteValue, 147 | Event.EnumerateKey, 148 | Event.EnumerateValueKey, 149 | Event.Flush, 150 | Event.Open, 151 | Event.Query, 152 | Event.QueryMultipleValue, 153 | Event.QueryValue, 154 | Event.RunDown, 155 | Event.SetInformation, 156 | Event.SetValue] 157 | _fields_ = [('Status', field.Pointer), 158 | ('KeyHandle', field.Pointer), 159 | ('ElapsedTime', field.Int64), 160 | ('Index', field.UInt32), 161 | ('KeyName', field.WString)] 162 | -------------------------------------------------------------------------------- /etw/descriptors/thread.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2.6 2 | # Copyright 2010 Google Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | """Generated event descriptor file for a MOF event class. 16 | 17 | DO NOT EDIT. This is an ETW event descriptor file generated by 18 | sawbuck/py/etw/generate_descriptor.py. It contains event descriptions for 19 | MOF GUID {3d6fa8d1-fe05-11d0-9dda-00c04fd7ba7c}. 20 | """ 21 | 22 | 23 | from etw.descriptors import event 24 | from etw.descriptors import field 25 | 26 | 27 | class Event(object): 28 | GUID = '{3d6fa8d1-fe05-11d0-9dda-00c04fd7ba7c}' 29 | Start = (GUID, 1) 30 | End = (GUID, 2) 31 | DCStart = (GUID, 3) 32 | DCEnd = (GUID, 4) 33 | CSwitch = (GUID, 36) 34 | CompCS = (GUID, 37) 35 | SetPriority = (GUID, 48) 36 | SetBasePriority = (GUID, 49) 37 | ReadyThread = (GUID, 50) 38 | SetPagePriority = (GUID, 51) 39 | SetIoPriority = (GUID, 52) 40 | ThreadAffinity = (GUID, 53) 41 | WorkerThread = (GUID, 57) 42 | 43 | 44 | class Thread_V2(event.EventCategory): 45 | GUID = Event.GUID 46 | VERSION = 2 47 | 48 | class WorkerThread(event.EventClass): 49 | _event_types_ = [Event.WorkerThread] 50 | _fields_ = [('TThreadId', field.UInt32), 51 | ('StartTime', field.UInt64), 52 | ('ThreadRoutine', field.Pointer)] 53 | 54 | class CSwitch(event.EventClass): 55 | _event_types_ = [Event.CSwitch] 56 | _fields_ = [('NewThreadId', field.UInt32), 57 | ('OldThreadId', field.UInt32), 58 | ('NewThreadPriority', field.Int8), 59 | ('OldThreadPriority', field.Int8), 60 | ('PreviousCState', field.UInt8), 61 | ('SpareByte', field.Int8), 62 | ('OldThreadWaitReason', field.Int8), 63 | ('OldThreadWaitMode', field.Int8), 64 | ('OldThreadState', field.Int8), 65 | ('OldThreadWaitIdealProcessor', field.Int8), 66 | ('NewThreadWaitTime', field.UInt32), 67 | ('Reserved', field.UInt32)] 68 | 69 | class ThreadAffinity(event.EventClass): 70 | _event_types_ = [Event.ThreadAffinity] 71 | _fields_ = [('Affinity', field.Pointer), 72 | ('ThreadId', field.UInt32), 73 | ('Group', field.UInt16), 74 | ('Reserved', field.UInt16)] 75 | 76 | class CompCS(event.EventClass): 77 | _event_types_ = [Event.CompCS] 78 | _fields_ = [] 79 | 80 | class ReadyThread(event.EventClass): 81 | _event_types_ = [Event.ReadyThread] 82 | _fields_ = [('TThreadId', field.UInt32), 83 | ('AdjustReason', field.Int8), 84 | ('AdjustIncrement', field.Int8), 85 | ('Flag', field.Int8), 86 | ('Reserved', field.Int8)] 87 | 88 | class TypeGroup1(event.EventClass): 89 | _event_types_ = [Event.DCEnd, 90 | Event.DCStart, 91 | Event.End, 92 | Event.Start] 93 | _fields_ = [('ProcessId', field.UInt32), 94 | ('TThreadId', field.UInt32), 95 | ('StackBase', field.Pointer), 96 | ('StackLimit', field.Pointer), 97 | ('UserStackBase', field.Pointer), 98 | ('UserStackLimit', field.Pointer), 99 | ('StartAddr', field.Pointer), 100 | ('Win32StartAddr', field.Pointer), 101 | ('TebBase', field.Pointer), 102 | ('SubProcessTag', field.UInt32)] 103 | 104 | 105 | class Thread(event.EventCategory): 106 | GUID = Event.GUID 107 | VERSION = 3 108 | 109 | class TypeGroup1(event.EventClass): 110 | _event_types_ = [Event.DCEnd, 111 | Event.DCStart, 112 | Event.End, 113 | Event.Start] 114 | _fields_ = [('ProcessId', field.UInt32), 115 | ('TThreadId', field.UInt32), 116 | ('StackBase', field.Pointer), 117 | ('StackLimit', field.Pointer), 118 | ('UserStackBase', field.Pointer), 119 | ('UserStackLimit', field.Pointer), 120 | ('Affinity', field.Pointer), 121 | ('Win32StartAddr', field.Pointer), 122 | ('TebBase', field.Pointer), 123 | ('SubProcessTag', field.UInt32), 124 | ('BasePriority', field.UInt8), 125 | ('PagePriority', field.UInt8), 126 | ('IoPriority', field.UInt8), 127 | ('ThreadFlags', field.UInt8)] 128 | 129 | class ThreadPriority(event.EventClass): 130 | _event_types_ = [Event.SetBasePriority, 131 | Event.SetIoPriority, 132 | Event.SetPagePriority, 133 | Event.SetPriority] 134 | _fields_ = [('ThreadId', field.UInt32), 135 | ('OldPriority', field.UInt8), 136 | ('NewPriority', field.UInt8), 137 | ('Reserved', field.UInt16)] 138 | 139 | 140 | class Thread_V0(event.EventCategory): 141 | GUID = Event.GUID 142 | VERSION = 0 143 | 144 | class TypeGroup1(event.EventClass): 145 | _event_types_ = [Event.DCEnd, 146 | Event.DCStart, 147 | Event.End, 148 | Event.Start] 149 | _fields_ = [('TThreadId', field.UInt32), 150 | ('ProcessId', field.UInt32)] 151 | 152 | 153 | class Thread_V1(event.EventCategory): 154 | GUID = Event.GUID 155 | VERSION = 1 156 | 157 | class TypeGroup2(event.EventClass): 158 | _event_types_ = [Event.DCEnd, 159 | Event.End] 160 | _fields_ = [('ProcessId', field.UInt32), 161 | ('TThreadId', field.UInt32)] 162 | 163 | class TypeGroup1(event.EventClass): 164 | _event_types_ = [Event.DCStart, 165 | Event.Start] 166 | _fields_ = [('ProcessId', field.UInt32), 167 | ('TThreadId', field.UInt32), 168 | ('StackBase', field.Pointer), 169 | ('StackLimit', field.Pointer), 170 | ('UserStackBase', field.Pointer), 171 | ('UserStackLimit', field.Pointer), 172 | ('StartAddr', field.Pointer), 173 | ('Win32StartAddr', field.Pointer), 174 | ('WaitMode', field.Int8)] 175 | 176 | class CSwitch_V1(event.EventClass): 177 | _event_types_ = [Event.CSwitch] 178 | _fields_ = [('NewThreadId', field.UInt32), 179 | ('OldThreadId', field.UInt32), 180 | ('NewThreadPriority', field.Int8), 181 | ('OldThreadPriority', field.Int8), 182 | ('NewThreadQuantum', field.Int8), 183 | ('OldThreadQuantum', field.Int8), 184 | ('OldThreadWaitReason', field.Int8), 185 | ('OldThreadWaitMode', field.Int8), 186 | ('OldThreadState', field.Int8), 187 | ('OldThreadWaitIdealProcessor', field.Int8), 188 | ('NewThreadWaitTime', field.UInt32)] 189 | 190 | class WorkerThread_V1(event.EventClass): 191 | _event_types_ = [Event.WorkerThread] 192 | _fields_ = [('TThreadId', field.UInt32), 193 | ('StartTime', field.UInt64), 194 | ('ThreadRoutine', field.Pointer)] 195 | -------------------------------------------------------------------------------- /etw/evntcons.py: -------------------------------------------------------------------------------- 1 | #!python 2 | # Copyright 2009 Google Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | """This file contains declarations gleaned from the evntcons.h file from 16 | the Platform SDK. 17 | """ 18 | 19 | PROCESS_TRACE_MODE_REAL_TIME = 0x00000100 20 | PROCESS_TRACE_MODE_RAW_TIMESTAMP = 0x00001000 21 | PROCESS_TRACE_MODE_EVENT_RECORD = 0x10000000 22 | -------------------------------------------------------------------------------- /etw/evntrace.py: -------------------------------------------------------------------------------- 1 | #!python 2 | # Copyright 2009 Google Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | """This file contains declarations gleaned from the evntrace.h file from 16 | the Platform SDK. Many of the ct.Structures here are simplified from their 17 | Platform SDK version by not in-lining one of the options of unions they 18 | contain. 19 | """ 20 | 21 | import ctypes as ct 22 | import ctypes.wintypes as wt 23 | import exceptions 24 | from guiddef import GUID 25 | import winerror 26 | 27 | 28 | TRACEHANDLE = ct.c_uint64 29 | 30 | # from wmistr.h 31 | 32 | # WNODE_HEADER flags are defined as follows 33 | WNODE_FLAG_ALL_DATA = 0x00000001 # set for WNODE_ALL_DATA 34 | WNODE_FLAG_SINGLE_INSTANCE = 0x00000002 # set for WNODE_SINGLE_INSTANCE 35 | WNODE_FLAG_SINGLE_ITEM = 0x00000004 # set for WNODE_SINGLE_ITEM 36 | WNODE_FLAG_EVENT_ITEM = 0x00000008 # set for WNODE_EVENT_ITEM 37 | 38 | # Set if data block size is 39 | # identical for all instances 40 | # (used with WNODE_ALL_DATA 41 | # only) 42 | WNODE_FLAG_FIXED_INSTANCE_SIZE = 0x00000010 43 | WNODE_FLAG_TOO_SMALL = 0x00000020 # set for WNODE_TOO_SMALL 44 | 45 | # Set when a data provider returns a 46 | # WNODE_ALL_DATA in which the number of 47 | # instances and their names returned 48 | # are identical to those returned from the 49 | # previous WNODE_ALL_DATA query. Only data 50 | # blocks registered with dynamic instance 51 | # names should use this flag. 52 | WNODE_FLAG_INSTANCES_SAME = 0x00000040 53 | 54 | # Instance names are not specified in 55 | # WNODE_ALL_DATA; values specified at 56 | # registration are used instead. Always 57 | # set for guids registered with static 58 | # instance names 59 | WNODE_FLAG_STATIC_INSTANCE_NAMES = 0x00000080 60 | WNODE_FLAG_INTERNAL = 0x00000100 # Used internally by WMI 61 | 62 | # timestamp should not be modified by 63 | # a historical logger 64 | WNODE_FLAG_USE_TIMESTAMP = 0x00000200 65 | WNODE_FLAG_PERSIST_EVENT = 0x00000400 66 | WNODE_FLAG_EVENT_REFERENCE = 0x00002000 67 | 68 | # Set if Instance names are ansi. Only set when returning from 69 | # WMIQuerySingleInstanceA and WMIQueryAllDataA 70 | WNODE_FLAG_ANSI_INSTANCENAMES = 0x00004000 71 | 72 | # Set if WNODE is a method call 73 | WNODE_FLAG_METHOD_ITEM = 0x00008000 74 | 75 | # Set if instance names originated from a PDO 76 | WNODE_FLAG_PDO_INSTANCE_NAMES = 0x00010000 77 | 78 | # The second byte, except the first bit is used exclusively for tracing 79 | WNODE_FLAG_TRACED_GUID = 0x00020000 # denotes a trace 80 | WNODE_FLAG_LOG_WNODE = 0x00040000 # request to log Wnode 81 | WNODE_FLAG_USE_GUID_PTR = 0x00080000 # Guid is actually a pointer 82 | WNODE_FLAG_USE_MOF_PTR = 0x00100000 # MOF data are dereferenced 83 | 84 | WNODE_FLAG_NO_HEADER = 0x00200000 # Trace without header 85 | WNODE_FLAG_SEND_DATA_BLOCK = 0x00400000 # Data Block delivery 86 | 87 | # Set for events that are WNODE_EVENT_REFERENCE 88 | # Mask for event severity level. Level 0xff is the most severe type of event 89 | WNODE_FLAG_SEVERITY_MASK = 0xff000000 90 | 91 | 92 | # From evntrace.h 93 | WMI_GET_ALL_DATA = 0 94 | WMI_GET_SINGLE_INSTANCE = 1 95 | WMI_SET_SINGLE_INSTANCE = 2 96 | WMI_SET_SINGLE_ITEM = 3 97 | WMI_ENABLE_EVENTS = 4 98 | WMI_DISABLE_EVENTS = 5 99 | WMI_ENABLE_COLLECTION = 6 100 | WMI_DISABLE_COLLECTION = 7 101 | WMI_REGINFO = 8 102 | WMI_EXECUTE_METHOD = 9 103 | WMI_CAPTURE_STATE = 10 104 | 105 | 106 | # 107 | # EventTraceGuid is used to identify a event tracing session 108 | # 109 | EventTraceGuid = GUID('{68fdd900-4a3e-11d1-84f4-0000f80464e3}') 110 | 111 | # 112 | # SystemTraceControlGuid. Used to specify event tracing for kernel 113 | # 114 | SystemTraceControlGuid = GUID('{9e814aad-3204-11d2-9a82-006008a86939}') 115 | 116 | # 117 | # EventTraceConfigGuid. Used to report system configuration records 118 | # 119 | EventTraceConfigGuid = GUID('{01853a65-418f-4f36-aefc-dc0f1d2fd235}') 120 | 121 | # 122 | # DefaultTraceSecurityGuid. Specifies the default event tracing security 123 | # 124 | DefaultTraceSecurityGuid = GUID('{0811c1af-7a07-4a06-82ed-869455cdf713}') 125 | 126 | KERNEL_LOGGER_NAME = "NT Kernel Logger" 127 | GLOBAL_LOGGER_NAME = "GlobalLogger" 128 | EVENT_LOGGER_NAME =" EventLog" 129 | DIAG_LOGGER_NAME = "DiagLog" 130 | 131 | MAX_MOF_FIELDS = 16 # Limit of USE_MOF_PTR fields 132 | 133 | 134 | # types for event data going to System Event Logger 135 | SYSTEM_EVENT_TYPE = 1 136 | 137 | # 138 | # predefined generic event types (0x00 to 0x09 reserved). 139 | # 140 | 141 | EVENT_TRACE_TYPE_INFO = 0x00 # Info or point event 142 | EVENT_TRACE_TYPE_START = 0x01 # Start event 143 | EVENT_TRACE_TYPE_END = 0x02 # End event 144 | EVENT_TRACE_TYPE_STOP = 0x02 # Stop event (WinEvent compatible) 145 | EVENT_TRACE_TYPE_DC_START = 0x03 # Collection start marker 146 | EVENT_TRACE_TYPE_DC_END = 0x04 # Collection end marker 147 | EVENT_TRACE_TYPE_EXTENSION = 0x05 # Extension/continuation 148 | EVENT_TRACE_TYPE_REPLY = 0x06 # Reply event 149 | EVENT_TRACE_TYPE_DEQUEUE = 0x07 # De-queue event 150 | EVENT_TRACE_TYPE_RESUME = 0x07 # Resume event (WinEvent compatible) 151 | EVENT_TRACE_TYPE_CHECKPOINT = 0x08 # Generic checkpoint event 152 | EVENT_TRACE_TYPE_SUSPEND = 0x08 # Suspend event (WinEvent compatible) 153 | EVENT_TRACE_TYPE_WINEVT_SEND = 0x09 # Send Event (WinEvent compatible) 154 | EVENT_TRACE_TYPE_WINEVT_RECEIVE = 0XF0 # Receive Event (WinEvent compatible) 155 | 156 | # 157 | # Predefined Event Tracing Levels for Software/Debug Tracing 158 | # 159 | # 160 | # Trace Level is UCHAR and passed in through the EnableLevel parameter 161 | # in EnableTrace API. It is retrieved by the provider using the 162 | # GetTraceEnableLevel macro.It should be interpreted as an integer value 163 | # to mean everything at or below that level will be traced. 164 | # 165 | # Here are the possible Levels. 166 | # 167 | 168 | TRACE_LEVEL_NONE = 0 # Tracing is not on 169 | TRACE_LEVEL_CRITICAL = 1 # Abnormal exit or termination 170 | TRACE_LEVEL_FATAL = 1 # Deprecated name for Abnormal exit or termination 171 | TRACE_LEVEL_ERROR = 2 # Severe errors that need logging 172 | TRACE_LEVEL_WARNING = 3 # Warnings such as allocation failure 173 | TRACE_LEVEL_INFORMATION = 4 # Includes non-error cases(e.g.,Entry-Exit) 174 | TRACE_LEVEL_VERBOSE = 5 # Detailed traces from intermediate steps 175 | TRACE_LEVEL_RESERVED6 = 6 176 | TRACE_LEVEL_RESERVED7 = 7 177 | TRACE_LEVEL_RESERVED8 = 8 178 | TRACE_LEVEL_RESERVED9 = 9 179 | 180 | 181 | # 182 | # Event types for Process & Threads 183 | # 184 | 185 | EVENT_TRACE_TYPE_LOAD = 0x0A # Load image 186 | 187 | # 188 | # Event types for IO subsystem 189 | # 190 | 191 | EVENT_TRACE_TYPE_IO_READ = 0x0A 192 | EVENT_TRACE_TYPE_IO_WRITE = 0x0B 193 | EVENT_TRACE_TYPE_IO_READ_INIT = 0x0C 194 | EVENT_TRACE_TYPE_IO_WRITE_INIT = 0x0D 195 | EVENT_TRACE_TYPE_IO_FLUSH = 0x0E 196 | EVENT_TRACE_TYPE_IO_FLUSH_INIT =0x0F 197 | 198 | 199 | # 200 | # Event types for Memory subsystem 201 | # 202 | 203 | EVENT_TRACE_TYPE_MM_TF = 0x0A # Transition fault 204 | EVENT_TRACE_TYPE_MM_DZF = 0x0B # Demand Zero fault 205 | EVENT_TRACE_TYPE_MM_COW = 0x0C # Copy on Write 206 | EVENT_TRACE_TYPE_MM_GPF = 0x0D # Guard Page fault 207 | EVENT_TRACE_TYPE_MM_HPF = 0x0E # Hard page fault 208 | EVENT_TRACE_TYPE_MM_AV = 0x0F # Access violation 209 | 210 | # 211 | # Event types for Network subsystem, all protocols 212 | # 213 | 214 | EVENT_TRACE_TYPE_SEND = 0x0A # Send 215 | EVENT_TRACE_TYPE_RECEIVE = 0x0B # Receive 216 | EVENT_TRACE_TYPE_CONNECT = 0x0C # Connect 217 | EVENT_TRACE_TYPE_DISCONNECT = 0x0D # Disconnect 218 | EVENT_TRACE_TYPE_RETRANSMIT = 0x0E # ReTransmit 219 | EVENT_TRACE_TYPE_ACCEPT = 0x0F # Accept 220 | EVENT_TRACE_TYPE_RECONNECT = 0x10 # ReConnect 221 | EVENT_TRACE_TYPE_CONNFAIL = 0x11 # Fail 222 | EVENT_TRACE_TYPE_COPY_TCP = 0x12 # Copy in PendData 223 | EVENT_TRACE_TYPE_COPY_ARP = 0x13 # NDIS_STATUS_RESOURCES Copy 224 | EVENT_TRACE_TYPE_ACKFULL = 0x14 # A full data ACK 225 | EVENT_TRACE_TYPE_ACKPART = 0x15 # A Partial data ACK 226 | EVENT_TRACE_TYPE_ACKDUP = 0x16 # A Duplicate data ACK 227 | 228 | 229 | # 230 | # Event Types for the Header (to handle internal event headers) 231 | # 232 | 233 | EVENT_TRACE_TYPE_GUIDMAP = 0x0A 234 | EVENT_TRACE_TYPE_CONFIG = 0x0B 235 | EVENT_TRACE_TYPE_SIDINFO = 0x0C 236 | EVENT_TRACE_TYPE_SECURITY = 0x0D 237 | 238 | # 239 | # Event Types for Registry subsystem 240 | # 241 | 242 | EVENT_TRACE_TYPE_REGCREATE = 0x0A # NtCreateKey 243 | EVENT_TRACE_TYPE_REGOPEN = 0x0B # NtOpenKey 244 | EVENT_TRACE_TYPE_REGDELETE = 0x0C # NtDeleteKey 245 | EVENT_TRACE_TYPE_REGQUERY = 0x0D # NtQueryKey 246 | EVENT_TRACE_TYPE_REGSETVALUE = 0x0E # NtSetValueKey 247 | EVENT_TRACE_TYPE_REGDELETEVALUE = 0x0F # NtDeleteValueKey 248 | EVENT_TRACE_TYPE_REGQUERYVALUE = 0x10 # NtQueryValueKey 249 | EVENT_TRACE_TYPE_REGENUMERATEKEY = 0x11 # NtEnumerateKey 250 | EVENT_TRACE_TYPE_REGENUMERATEVALUEKEY = 0x12 # NtEnumerateValueKey 251 | EVENT_TRACE_TYPE_REGQUERYMULTIPLEVALUE = 0x13 # NtQueryMultipleValueKey 252 | EVENT_TRACE_TYPE_REGSETINFORMATION = 0x14 # NtSetInformationKey 253 | EVENT_TRACE_TYPE_REGFLUSH = 0x15 # NtFlushKey 254 | EVENT_TRACE_TYPE_REGKCBCREATE = 0x16 # KcbCreate 255 | EVENT_TRACE_TYPE_REGKCBDELETE = 0x17 # KcbDelete 256 | EVENT_TRACE_TYPE_REGKCBRUNDOWNBEGIN = 0x18 # KcbRundownBegin 257 | EVENT_TRACE_TYPE_REGKCBRUNDOWNEND = 0x19 # KcbRundownEnd 258 | EVENT_TRACE_TYPE_REGVIRTUALIZE = 0x1A # VirtualizeKey 259 | EVENT_TRACE_TYPE_REGCLOSE = 0x1B # NtClose (KeyObject) 260 | EVENT_TRACE_TYPE_REGSETSECURITY = 0x1C # SetSecurityDescriptor (KeyObject) 261 | EVENT_TRACE_TYPE_REGQUERYSECURITY = 0x1D # QuerySecurityDescriptor (KeyObject) 262 | # CmKtmNotification (TRANSACTION_NOTIFY_COMMIT) 263 | EVENT_TRACE_TYPE_REGCOMMIT = 0x1E 264 | # CmKtmNotification (TRANSACTION_NOTIFY_PREPARE) 265 | EVENT_TRACE_TYPE_REGPREPARE = 0x1F 266 | # CmKtmNotification (TRANSACTION_NOTIFY_ROLLBACK) 267 | EVENT_TRACE_TYPE_REGROLLBACK = 0x20 268 | EVENT_TRACE_TYPE_REGMOUNTHIVE = 0x21 # NtLoadKey variations + system hives 269 | 270 | # 271 | # Event types for system configuration records 272 | # 273 | EVENT_TRACE_TYPE_CONFIG_CPU = 0x0A # CPU Configuration 274 | EVENT_TRACE_TYPE_CONFIG_PHYSICALDISK = 0x0B # Physical Disk Configuration 275 | EVENT_TRACE_TYPE_CONFIG_LOGICALDISK = 0x0C # Logical Disk Configuration 276 | EVENT_TRACE_TYPE_CONFIG_NIC = 0x0D # NIC Configuration 277 | EVENT_TRACE_TYPE_CONFIG_VIDEO = 0x0E # Video Adapter Configuration 278 | EVENT_TRACE_TYPE_CONFIG_SERVICES = 0x0F # Active Services 279 | EVENT_TRACE_TYPE_CONFIG_POWER = 0x10 # ACPI Configuration 280 | EVENT_TRACE_TYPE_CONFIG_NETINFO = 0x11 # Networking Configuration 281 | 282 | EVENT_TRACE_TYPE_CONFIG_IRQ = 0x15 # IRQ assigned to devices 283 | EVENT_TRACE_TYPE_CONFIG_PNP = 0x16 # PnP device info 284 | # Primary/Secondary IDE channel Configuration 285 | EVENT_TRACE_TYPE_CONFIG_IDECHANNEL = 0x17 286 | EVENT_TRACE_TYPE_CONFIG_PLATFORM = 0x19 # Platform Configuration 287 | 288 | # 289 | # Enable flags for Kernel Events 290 | # 291 | EVENT_TRACE_FLAG_PROCESS = 0x00000001 # process start & end 292 | EVENT_TRACE_FLAG_THREAD = 0x00000002 # thread start & end 293 | EVENT_TRACE_FLAG_IMAGE_LOAD = 0x00000004 # image load 294 | 295 | EVENT_TRACE_FLAG_DISK_IO = 0x00000100 # physical disk IO 296 | EVENT_TRACE_FLAG_DISK_FILE_IO = 0x00000200 # requires disk IO 297 | 298 | EVENT_TRACE_FLAG_MEMORY_PAGE_FAULTS = 0x00001000 # all page faults 299 | EVENT_TRACE_FLAG_MEMORY_HARD_FAULTS = 0x00002000 # hard faults only 300 | 301 | EVENT_TRACE_FLAG_NETWORK_TCPIP = 0x00010000 # tcpip send & receive 302 | 303 | EVENT_TRACE_FLAG_REGISTRY = 0x00020000 # registry calls 304 | EVENT_TRACE_FLAG_DBGPRINT = 0x00040000 # DbgPrint(ex) Calls 305 | 306 | # 307 | # Enable flags for Kernel Events on Vista and above 308 | # 309 | EVENT_TRACE_FLAG_PROCESS_COUNTERS = 0x00000008 # process perf counters 310 | EVENT_TRACE_FLAG_CSWITCH = 0x00000010 # context switches 311 | EVENT_TRACE_FLAG_DPC = 0x00000020 # deffered procedure calls 312 | EVENT_TRACE_FLAG_INTERRUPT = 0x00000040 # interrupts 313 | EVENT_TRACE_FLAG_SYSTEMCALL = 0x00000080 # system calls 314 | 315 | EVENT_TRACE_FLAG_DISK_IO_INIT = 0x00000400 # physical disk IO initiation 316 | 317 | EVENT_TRACE_FLAG_ALPC = 0x00100000 # ALPC traces 318 | EVENT_TRACE_FLAG_SPLIT_IO = 0x00200000 # split io traces (VolumeManager) 319 | 320 | EVENT_TRACE_FLAG_DRIVER = 0x00800000 # driver delays 321 | EVENT_TRACE_FLAG_PROFILE = 0x01000000 # sample based profiling 322 | EVENT_TRACE_FLAG_FILE_IO = 0x02000000 # file IO 323 | EVENT_TRACE_FLAG_FILE_IO_INIT = 0x04000000 # file IO initiation 324 | 325 | # 326 | # Enable flags for Kernel Events on Win7 and above 327 | # 328 | EVENT_TRACE_FLAG_DISPATCHER = 0x00000800 # scheduler (ReadyThread) 329 | EVENT_TRACE_FLAG_VIRTUAL_ALLOC = 0x00004000 # VM operations 330 | 331 | # 332 | # Pre-defined Enable flags for everybody else 333 | # 334 | EVENT_TRACE_FLAG_EXTENSION = 0x80000000 # Indicates more flags 335 | EVENT_TRACE_FLAG_FORWARD_WMI = 0x40000000 # Can forward to WMI 336 | EVENT_TRACE_FLAG_ENABLE_RESERVE = 0x20000000 # Reserved 337 | 338 | # 339 | # Logger Mode flags 340 | # 341 | EVENT_TRACE_FILE_MODE_NONE = 0x00000000 # Logfile is off 342 | EVENT_TRACE_FILE_MODE_SEQUENTIAL = 0x00000001 # Log sequentially 343 | EVENT_TRACE_FILE_MODE_CIRCULAR = 0x00000002 # Log in circular manner 344 | EVENT_TRACE_FILE_MODE_APPEND = 0x00000004 # Append sequential log 345 | 346 | EVENT_TRACE_REAL_TIME_MODE = 0x00000100 # Real time mode on 347 | EVENT_TRACE_DELAY_OPEN_FILE_MODE = 0x00000200 # Delay opening file 348 | EVENT_TRACE_BUFFERING_MODE = 0x00000400 # Buffering mode only 349 | EVENT_TRACE_PRIVATE_LOGGER_MODE = 0x00000800 # Process Private Logger 350 | EVENT_TRACE_ADD_HEADER_MODE = 0x00001000 # Add a logfile header 351 | 352 | EVENT_TRACE_USE_GLOBAL_SEQUENCE = 0x00004000 # Use global sequence no. 353 | EVENT_TRACE_USE_LOCAL_SEQUENCE = 0x00008000 # Use local sequence no. 354 | 355 | EVENT_TRACE_RELOG_MODE = 0x00010000 # Relogger 356 | 357 | EVENT_TRACE_USE_PAGED_MEMORY = 0x01000000 # Use pageable buffers 358 | 359 | # 360 | # Logger Mode flags on XP and above 361 | # 362 | EVENT_TRACE_FILE_MODE_NEWFILE = 0x00000008 # Auto-switch log file 363 | EVENT_TRACE_FILE_MODE_PREALLOCATE = 0x00000020 # Pre-allocate mode 364 | 365 | # 366 | # Logger Mode flags on Vista and above 367 | # 368 | # Session cannot be stopped (Autologger only) 369 | EVENT_TRACE_NONSTOPPABLE_MODE = 0x00000040 370 | EVENT_TRACE_SECURE_MODE = 0x00000080 # Secure session 371 | EVENT_TRACE_USE_KBYTES_FOR_SIZE = 0x00002000 # Use KBytes as file size unit 372 | EVENT_TRACE_PRIVATE_IN_PROC = 0x00020000 # In process private logger 373 | # Reserved bit, used to signal Heap/Critsec tracing 374 | EVENT_TRACE_MODE_RESERVED = 0x00100000 375 | 376 | # 377 | # Logger Mode flags on Win7 and above 378 | # 379 | # Use this for low frequency sessions. 380 | EVENT_TRACE_NO_PER_PROCESSOR_BUFFERING = 0x10000000 381 | 382 | # 383 | # ControlTrace Codes 384 | # 385 | EVENT_TRACE_CONTROL_QUERY = 0 386 | EVENT_TRACE_CONTROL_STOP = 1 387 | EVENT_TRACE_CONTROL_UPDATE = 2 388 | 389 | # 390 | # Flush ControlTrace Codes for XP and above 391 | # 392 | EVENT_TRACE_CONTROL_FLUSH = 3 # Flushes all the buffers 393 | 394 | # 395 | # Flags used by WMI Trace Message 396 | # Note that the order or value of these flags should NOT be changed 397 | # as they are processed 398 | # in this order. 399 | # 400 | TRACE_MESSAGE_SEQUENCE = 1 # Message should include a sequence number 401 | TRACE_MESSAGE_GUID = 2 # Message includes a GUID 402 | TRACE_MESSAGE_COMPONENTID = 4 # Message has no GUID, Component ID instead 403 | TRACE_MESSAGE_TIMESTAMP = 8 # Message includes a timestamp 404 | # *Obsolete* Clock type is controlled by the logger 405 | TRACE_MESSAGE_PERFORMANCE_TIMESTAMP = 16 406 | TRACE_MESSAGE_SYSTEMINFO = 32 # Message includes system information TID,PID 407 | 408 | # 409 | # Vista flags set by system to indicate provider pointer size. 410 | # 411 | 412 | TRACE_MESSAGE_POINTER32 = 0x0040 # Message logged by 32 bit provider 413 | TRACE_MESSAGE_POINTER64 = 0x0080 # Message logged by 64 bit provider 414 | 415 | # Only the lower 16 bits of flags are placed in the message 416 | # those above 16 bits are reserved for local processing 417 | TRACE_MESSAGE_FLAG_MASK = 0xFFFF 418 | 419 | # the maximum size allowed for a single trace message 420 | # longer messages will return ERROR_BUFFER_OVERFLOW 421 | TRACE_MESSAGE_MAXIMUM_SIZE = 8*1024 422 | 423 | # 424 | # Flags to indicate to consumer which fields 425 | # in the EVENT_TRACE_HEADER are valid 426 | # 427 | 428 | EVENT_TRACE_USE_PROCTIME = 0x0001 # ProcessorTime field is valid 429 | EVENT_TRACE_USE_NOCPUTIME = 0x0002 # No Kernel/User/Processor Times 430 | 431 | # 432 | # TRACE_HEADER_FLAG values are used in the Flags field of EVENT_TRACE_HEADER 433 | # ct.Structure while calling into TraceEvent API 434 | # 435 | 436 | TRACE_HEADER_FLAG_USE_TIMESTAMP = 0x00000200 437 | TRACE_HEADER_FLAG_TRACED_GUID = 0x00020000 # denotes a trace 438 | TRACE_HEADER_FLAG_LOG_WNODE = 0x00040000 # request to log Wnode 439 | TRACE_HEADER_FLAG_USE_GUID_PTR = 0x00080000 # Guid is actually a pointer 440 | TRACE_HEADER_FLAG_USE_MOF_PTR = 0x00100000 # MOF data are dereferenced 441 | 442 | 443 | class WNODE_HEADER(ct.Structure): 444 | _fields_ = [('BufferSize', ct.c_ulong), 445 | ('ProviderId', ct.c_ulong), 446 | ('HistoricalContext', ct.c_uint64), 447 | ('TimeStamp', wt.LARGE_INTEGER), 448 | ('Guid', GUID), 449 | ('ClientContext', ct.c_ulong), 450 | ('Flags', ct.c_ulong)] 451 | 452 | 453 | class EVENT_TRACE_PROPERTIES(ct.Structure): 454 | _fields_ = [('Wnode', WNODE_HEADER), 455 | ('BufferSize', ct.c_ulong), 456 | ('MinimumBuffers', ct.c_ulong), 457 | ('MaximumBuffers', ct.c_ulong), 458 | ('MaximumFileSize', ct.c_ulong), 459 | ('LogFileMode', ct.c_ulong), 460 | ('FlushTimer', ct.c_ulong), 461 | ('EnableFlags', ct.c_ulong), 462 | ('AgeLimit', ct.c_long), 463 | ('NumberOfBuffers', ct.c_ulong), 464 | ('FreeBuffers', ct.c_ulong), 465 | ('EventsLost', ct.c_ulong), 466 | ('BuffersWritten', ct.c_ulong), 467 | ('LogBuffersLost', ct.c_ulong), 468 | ('RealTimeBuffersLost', ct.c_ulong), 469 | ('LoggerThreadId', wt.HANDLE), 470 | ('LogFileNameOffset', ct.c_ulong), 471 | ('LoggerNameOffset', ct.c_ulong)] 472 | 473 | 474 | class TRACE_GUID_REGISTRATION(ct.Structure): 475 | _fields_ = [('Guid', ct.POINTER(GUID)), 476 | ('RegHandle', wt.HANDLE)] 477 | 478 | 479 | class EVENT_TRACE_HEADER_CLASS(ct.Structure): 480 | _fields_ = [('Type', ct.c_ubyte), 481 | ('Level', ct.c_ubyte), 482 | ('Version', ct.c_uint16)] 483 | 484 | 485 | class EVENT_TRACE_HEADER(ct.Structure): 486 | _fields_ = [('Size', ct.c_ushort), 487 | ('HeaderType', ct.c_ubyte), 488 | ('MarkerFlags', ct.c_ubyte), 489 | ('Class', EVENT_TRACE_HEADER_CLASS), 490 | ('ThreadId', ct.c_ulong), 491 | ('ProcessId', ct.c_ulong), 492 | ('TimeStamp', wt.LARGE_INTEGER), 493 | ('Guid', GUID), 494 | ('ClientContext', ct.c_ulong), 495 | ('Flags', ct.c_ulong)] 496 | 497 | 498 | class MOF_FIELD(ct.Structure): 499 | _fields_ = [('DataPtr', ct.c_ulonglong), 500 | ('Length', ct.c_ulong), 501 | ('DataType', ct.c_ulong)] 502 | 503 | 504 | class EVENT_TRACE(ct.Structure): 505 | _fields_ = [('Header', EVENT_TRACE_HEADER), 506 | ('InstanceId', ct.c_ulong), 507 | ('ParentInstanceId', ct.c_ulong), 508 | ('ParentGuid', GUID), 509 | ('MofData', ct.c_void_p), 510 | ('MofLength', ct.c_ulong), 511 | ('ClientContext', ct.c_ulong)] 512 | 513 | 514 | class SYSTEMTIME(ct.Structure): 515 | _fields_ = [('wYear', wt.WORD), 516 | ('wMonth', wt.WORD), 517 | ('wDayOfWeek', wt.WORD), 518 | ('wDay', wt.WORD), 519 | ('wHour', wt.WORD), 520 | ('wMinute', wt.WORD), 521 | ('wSecond', wt.WORD), 522 | ('wMilliseconds', wt.WORD)] 523 | 524 | 525 | class TIME_ZONE_INFORMATION(ct.Structure): 526 | _fields_ = [('Bias', ct.c_long), 527 | ('StandardName', ct.c_wchar * 32), 528 | ('StandardDate', SYSTEMTIME), 529 | ('StandardBias', ct.c_long), 530 | ('DaylightName', ct.c_wchar * 32), 531 | ('DaylightDate', SYSTEMTIME), 532 | ('DaylightBias', ct.c_long)] 533 | 534 | 535 | class TRACE_LOGFILE_HEADER(ct.Structure): 536 | _fields_ = [('BufferSize', ct.c_ulong), 537 | ('MajorVersion', ct.c_byte), 538 | ('MinorVersion', ct.c_byte), 539 | ('SubVersion', ct.c_byte), 540 | ('SubMinorVersion', ct.c_byte), 541 | ('ProviderVersion', ct.c_ulong), 542 | ('NumberOfProcessors', ct.c_ulong), 543 | ('EndTime', wt.LARGE_INTEGER), 544 | ('TimerResolution', ct.c_ulong), 545 | ('MaximumFileSize', ct.c_ulong), 546 | ('LogFileMode', ct.c_ulong), 547 | ('BuffersWritten', ct.c_ulong), 548 | ('StartBuffers', ct.c_ulong), 549 | ('PointerSize', ct.c_ulong), 550 | ('EventsLost', ct.c_ulong), 551 | ('CpuSpeedInMHz', ct.c_ulong), 552 | ('LoggerName', ct.c_wchar_p), 553 | ('LogFileName', ct.c_wchar_p), 554 | ('TimeZone', TIME_ZONE_INFORMATION), 555 | ('BootTime', wt.LARGE_INTEGER), 556 | ('PerfFreq', wt.LARGE_INTEGER), 557 | ('StartTime', wt.LARGE_INTEGER), 558 | ('ReservedFlags', ct.c_ulong), 559 | ('BuffersLost', ct.c_ulong)] 560 | 561 | 562 | # This must be "forward declared", because of the callback type below, 563 | # which is contained in the ct.Structure. 564 | class EVENT_TRACE_LOGFILE(ct.Structure): 565 | pass 566 | 567 | 568 | # The type for event trace callbacks. 569 | EVENT_CALLBACK = ct.WINFUNCTYPE(None, ct.POINTER(EVENT_TRACE)) 570 | EVENT_TRACE_BUFFER_CALLBACK = ct.WINFUNCTYPE(ct.c_ulong, 571 | ct.POINTER(EVENT_TRACE_LOGFILE)) 572 | 573 | 574 | EVENT_TRACE_LOGFILE._fields_ = [ 575 | ('LogFileName', ct.c_wchar_p), 576 | ('LoggerName', ct.c_wchar_p), 577 | ('CurrentTime', ct.c_longlong), 578 | ('BuffersRead', ct.c_ulong), 579 | ('ProcessTraceMode', ct.c_ulong), 580 | ('CurrentEvent', EVENT_TRACE), 581 | ('LogfileHeader', TRACE_LOGFILE_HEADER), 582 | ('BufferCallback', EVENT_TRACE_BUFFER_CALLBACK), 583 | ('BufferSize', ct.c_ulong), 584 | ('Filled', ct.c_ulong), 585 | ('EventsLost', ct.c_ulong), 586 | ('EventCallback', EVENT_CALLBACK), 587 | ('IsKernelTrace', ct.c_ulong), 588 | ('Context', ct.c_void_p)] 589 | 590 | 591 | def CheckWinError(result, func, arguments): 592 | if result != winerror.ERROR_SUCCESS: 593 | raise exceptions.WindowsError(result) 594 | 595 | 596 | StartTrace = ct.windll.advapi32.StartTraceW 597 | StartTrace.argtypes = [ct.POINTER(TRACEHANDLE), 598 | ct.c_wchar_p, 599 | ct.POINTER(EVENT_TRACE_PROPERTIES)] 600 | StartTrace.restype = ct.c_ulong 601 | StartTrace.errcheck = CheckWinError 602 | 603 | 604 | ControlTrace = ct.windll.advapi32.ControlTraceW 605 | ControlTrace.argtypes = [TRACEHANDLE, 606 | ct.c_wchar_p, 607 | ct.POINTER(EVENT_TRACE_PROPERTIES), 608 | ct.c_ulong] 609 | ControlTrace.restype = ct.c_ulong 610 | ControlTrace.errcheck = CheckWinError 611 | 612 | 613 | EnableTrace = ct.windll.advapi32.EnableTrace 614 | EnableTrace.argtypes = [ct.c_ulong, 615 | ct.c_ulong, 616 | ct.c_ulong, 617 | ct.POINTER(GUID), 618 | TRACEHANDLE] 619 | EnableTrace.restype = ct.c_ulong 620 | EnableTrace.errcheck = CheckWinError 621 | 622 | 623 | WMIDPREQUEST = ct.WINFUNCTYPE(ct.c_ulong, 624 | ct.c_ulong, 625 | ct.c_void_p, 626 | ct.c_ulong, 627 | ct.c_void_p) 628 | 629 | 630 | RegisterTraceGuids = ct.windll.advapi32.RegisterTraceGuidsW 631 | RegisterTraceGuids.argtypes = [WMIDPREQUEST, # RequestAddress 632 | ct.c_void_p, # RequestContext 633 | ct.POINTER(GUID), # ControlGuid 634 | ct.c_ulong, # GuidCount 635 | # TraceGuidReg 636 | ct.POINTER(TRACE_GUID_REGISTRATION), 637 | ct.c_wchar_p, # MofImagePath 638 | ct.c_wchar_p, # MofResourceName 639 | ct.POINTER(TRACEHANDLE)] # RegistrationHandle 640 | RegisterTraceGuids.restype = ct.c_ulong 641 | RegisterTraceGuids.errcheck = CheckWinError 642 | 643 | 644 | UnregisterTraceGuids = ct.windll.advapi32.UnregisterTraceGuids 645 | UnregisterTraceGuids.argtypes = [TRACEHANDLE] 646 | UnregisterTraceGuids.restype = ct.c_ulong 647 | UnregisterTraceGuids.errcheck = CheckWinError 648 | 649 | 650 | GetTraceLoggerHandle = ct.windll.advapi32.GetTraceLoggerHandle 651 | GetTraceLoggerHandle.argtypes = [ct.c_void_p] 652 | GetTraceLoggerHandle.restype = TRACEHANDLE 653 | 654 | 655 | GetTraceEnableFlags = ct.windll.advapi32.GetTraceEnableFlags 656 | GetTraceEnableFlags.argtypes = [TRACEHANDLE] 657 | GetTraceEnableFlags.restype = ct.c_ulong 658 | 659 | 660 | GetTraceEnableLevel = ct.windll.advapi32.GetTraceEnableLevel 661 | GetTraceEnableLevel.argtypes = [TRACEHANDLE] 662 | GetTraceEnableLevel.restype = ct.c_ubyte 663 | 664 | 665 | TraceEvent = ct.windll.advapi32.TraceEvent 666 | TraceEvent.argtypes = [TRACEHANDLE, ct.POINTER(EVENT_TRACE_HEADER)] 667 | TraceEvent.restype = ct.c_ulong 668 | TraceEvent.errcheck = CheckWinError 669 | 670 | 671 | def CheckTraceHandle(result, func, arguments): 672 | if result == ct.c_ulong(-1).value: 673 | raise exceptions.WindowsError(ct.GetLastError()) 674 | 675 | return result 676 | 677 | 678 | OpenTrace = ct.windll.advapi32.OpenTraceW 679 | OpenTrace.argtypes = [ct.POINTER(EVENT_TRACE_LOGFILE)] 680 | OpenTrace.restype = TRACEHANDLE 681 | OpenTrace.errcheck = CheckTraceHandle 682 | 683 | 684 | ProcessTrace = ct.windll.advapi32.ProcessTrace 685 | ProcessTrace.argtypes = [ct.POINTER(TRACEHANDLE), 686 | ct.c_ulong, 687 | ct.POINTER(wt.FILETIME), 688 | ct.POINTER(wt.FILETIME)] 689 | ProcessTrace.restype = ct.c_ulong 690 | ProcessTrace.errcheck = CheckWinError 691 | 692 | 693 | CloseTrace = ct.windll.advapi32.CloseTrace 694 | CloseTrace.argtypes = [TRACEHANDLE] 695 | CloseTrace.restype = ct.c_ulong 696 | CloseTrace.errcheck = CheckWinError 697 | -------------------------------------------------------------------------------- /etw/guiddef.py: -------------------------------------------------------------------------------- 1 | #!python 2 | # Copyright 2010 Google Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | """A GUID declaration in ctypes.""" 16 | import ctypes 17 | import re 18 | 19 | class GUID(ctypes.Structure): 20 | _DECOMPOSE_RE = re.compile( 21 | '{([0-9A-F]{8})-([0-9A-F]{4})-([0-9A-F]{4})-([0-9A-F]{2})([0-9A-F]{2})-' 22 | '([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})' 23 | '([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})}', re.I) 24 | 25 | def __init__(self, str = None): 26 | if str: 27 | m = self._DECOMPOSE_RE.match(str) 28 | g = [int(i, 16) for i in m.groups()] 29 | self.Data1 = g[0] 30 | self.Data2 = g[1] 31 | self.Data3 = g[2] 32 | for i in range(8): 33 | self.Data4[i] = g[3 + i] 34 | 35 | _fields_ = [("Data1", ctypes.c_ulong), 36 | ("Data2", ctypes.c_ushort), 37 | ("Data3", ctypes.c_ushort), 38 | ("Data4", ctypes.c_ubyte * 8)] 39 | 40 | def __str__(self): 41 | return "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}" % \ 42 | (self.Data1, self.Data2, self.Data3, 43 | self.Data4[0], self.Data4[1], 44 | self.Data4[2], self.Data4[3], self.Data4[4], 45 | self.Data4[5], self.Data4[6], self.Data4[7]) 46 | -------------------------------------------------------------------------------- /etw/provider.py: -------------------------------------------------------------------------------- 1 | #!python 2 | # Copyright 2009 Google Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | """An Event Tracing for Windows event provider.""" 16 | from ctypes import addressof, byref, cast, pointer, sizeof 17 | from ctypes import POINTER, Structure 18 | import evntrace 19 | import winerror 20 | 21 | class MofEvent(object): 22 | """A utility class to wrap trace event structures""" 23 | def __init__(self, num_fields, event_class, type, level): 24 | """Create and initialize an event with a given number of MOF fields. 25 | 26 | Args: 27 | num_fields: the number of MOF fields to allocate. Each MOF field 28 | can be set with a buffer and length, which will cause the buffer 29 | contents to be logged with the event. 30 | event_class: the event class GUID. 31 | type: the integer event type. 32 | level: the trace level of this event. 33 | """ 34 | class MofEventType(Structure): 35 | _fields_ = [('header', evntrace.EVENT_TRACE_HEADER), 36 | ('fields', evntrace.MOF_FIELD * num_fields)] 37 | 38 | event = MofEventType() 39 | event.header.Size = sizeof(event) 40 | event.header.Guid = event_class 41 | event.header.Class.Type = type 42 | event.header.Class.Level = level 43 | event.header.Flags = (evntrace.WNODE_FLAG_TRACED_GUID | 44 | evntrace.WNODE_FLAG_USE_MOF_PTR) 45 | self._event = event 46 | 47 | def _getEvent(self): 48 | return self._event 49 | 50 | event = property(_getEvent, 51 | doc='Retrieve the event structure.') 52 | 53 | def SetField(self, index, data_len, data): 54 | """Set a MOF field with a length and a buffer. 55 | 56 | Args: 57 | index: index of the field to set. 58 | data_len: length of the buffer backing "data". 59 | data: a ctypes pointer to a buffer of length "data_len" or better. 60 | 61 | Note: 62 | The buffer "data" points to must remain valid for the lifetime of 63 | the event. If the event is logged after the e.g. the string or object 64 | backing "data" goes out of scope, the event will log garbage, or 65 | possibly cause a crash on logging. 66 | """ 67 | self._event.fields[index].DataPtr = addressof(data.contents) 68 | self._event.fields[index].Length = data_len 69 | 70 | 71 | class TraceProvider(object): 72 | """A trace provider for Event Tracing for Windows. 73 | 74 | To use select a provider GUID, then instantiate a TraceProvider with the GUID. 75 | The TraceProvider takes care of registering with ETW, handling ETW callbacks 76 | to update the tracing level and enable mask. 77 | 78 | To issue an event, first check the enable_level and the enable_mask. 79 | If an event should be issued, create a MofEvent, set its fields to point 80 | to the event data, and pass it to Log(). 81 | """ 82 | def __init__(self, control_guid, trace_guids = None): 83 | """Create a TraceProvider with a given control_guid name. 84 | 85 | Args: 86 | control_guid: the GUID that names this provider. 87 | trace_guids: optionally a list of GUIDs that name the event classes 88 | this provider will log. 89 | """ 90 | self._guid = control_guid 91 | if trace_guids == None: 92 | trace_guids = [evntrace.GUID()] 93 | 94 | # Copy the trace guids, and allocate an array of guid 95 | # registrations to point to them. 96 | self._trace_guids = trace_guids[:] 97 | self._guid_registrations = (evntrace.TRACE_GUID_REGISTRATION * 98 | len(trace_guids))() 99 | for i in range(len(trace_guids)): 100 | self._guid_registrations[i].Guid = pointer(self._trace_guids[i]) 101 | 102 | self._registration_handle = evntrace.TRACEHANDLE() 103 | self._callback = evntrace.WMIDPREQUEST(self._ControlCallback) 104 | self._session_handle = None 105 | self._enable_level = 0 106 | self._enable_flags = 0; 107 | 108 | # If the provider is enabled, the control callback will fire from 109 | # within the call to RegisterTraceGuids, so don't touch anything 110 | # that the callback uses past this point. 111 | evntrace.RegisterTraceGuids(self._callback, 112 | None, 113 | self._guid, 114 | len(self._trace_guids), 115 | cast(self._guid_registrations, POINTER( 116 | evntrace.TRACE_GUID_REGISTRATION)), 117 | None, 118 | None, 119 | byref(self._registration_handle)) 120 | 121 | def ShouldLog(self, level, enable_flag): 122 | """Test whether an event should be logged at this time. 123 | 124 | Args: 125 | level: the trace level at which the event would be logged. 126 | enable_flags: a mask of enable bits that should trigger the event. 127 | 128 | Returns: 129 | true iff the current logging level is greater or equal to level, and 130 | any of the bits in enable_flags are currently enabled. 131 | """ 132 | return (self.enable_level >= level and 133 | (self.enable_flags & enable_flag) != 0) 134 | 135 | def Log(self, mof_event): 136 | """Outputs mof_event to any listening trace session(s). 137 | 138 | Args: 139 | mof_event: a MofEvent instance initialized with the data to log. 140 | """ 141 | return evntrace.TraceEvent(self._session_handle, 142 | byref(mof_event.event.header)) 143 | 144 | def _GetEnableLevel(self): 145 | return self._enable_level 146 | 147 | def _GetEnableFlags(self): 148 | return self._enable_flags 149 | 150 | enable_level = property(_GetEnableLevel, 151 | doc='Retrieves the current enable level') 152 | 153 | enable_flags = property(_GetEnableFlags, 154 | doc='Retrieves the current enable flags') 155 | 156 | def OnEventsEnabled(self): 157 | """An event hook for overriding in subclasses. 158 | 159 | Called when events have been enabled, or when the log level or enable mask 160 | has changed. The new enable_level and enable_mask are available from 161 | the properties in this call. 162 | """ 163 | pass 164 | 165 | def OnEventsDisabled(self): 166 | """An event hook for overriding in subclasses. 167 | 168 | Called just before events are disabled, the old enable_level and 169 | enable_mask are still in effect. 170 | """ 171 | pass 172 | 173 | def __del__(self): 174 | """Cleanup our registration if one is still in effect.""" 175 | if self._registration_handle.value != 0: 176 | evntrace.UnregisterTraceGuids(self._registration_handle) 177 | 178 | def _ControlCallback(self, request, unused_context, unused_reserved, buffer): 179 | if request == evntrace.WMI_ENABLE_EVENTS: 180 | return self._EnableEvents(buffer) 181 | elif request == evntrace.WMI_DISABLE_EVENTS: 182 | return self._DisableEvents() 183 | 184 | return winerror.ERROR_INVALID_PARAMETER 185 | 186 | def _EnableEvents(self, buffer): 187 | # We're in a control callback and events were just enabled 188 | # or changed, retrieve our session properties. 189 | self._session_handle = evntrace.GetTraceLoggerHandle(buffer) 190 | self._enable_level = evntrace.GetTraceEnableLevel(self._session_handle) 191 | self._enable_flags = evntrace.GetTraceEnableFlags(self._session_handle) 192 | self.OnEventsEnabled() 193 | return winerror.ERROR_SUCCESS 194 | 195 | def _DisableEvents(self): 196 | # We're in a control callback and events were just disabled. 197 | # Clear our session properties. 198 | self._enable_level = 0 199 | self._enable_flags = 0 200 | self._session_handle = None 201 | return winerror.ERROR_SUCCESS 202 | -------------------------------------------------------------------------------- /etw/util.py: -------------------------------------------------------------------------------- 1 | # Copyright 2010 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | """General utility functions.""" 15 | 16 | 17 | # The number of seconds between 01-01-1601 and 01-01-1970 18 | FILETIME_EPOCH_DELTA_S = 11644473600 19 | 20 | 21 | # Multiplier to to convert from units of 100ns to seconds. 22 | FILETIME_TO_SECONDS_MULTIPLIER = 1.0/10000000.0 23 | 24 | 25 | def FileTimeToTime(file_time): 26 | """Converts a Win32 FILETIME to python-compatible time.""" 27 | # A file time is a 64 bit integer that represents the number of 100 28 | # nanosecond lapses since 01-01-1601. We convert this value to seconds 29 | # and then change the epoch to be relative to 01-01-1970 which makes it 30 | # compatible with time.time(). 31 | time_stamp_100ns = file_time 32 | time_stamp_s = float(time_stamp_100ns) * FILETIME_TO_SECONDS_MULTIPLIER 33 | return time_stamp_s - FILETIME_EPOCH_DELTA_S 34 | -------------------------------------------------------------------------------- /ez_setup.py: -------------------------------------------------------------------------------- 1 | #!python 2 | """Bootstrap setuptools installation 3 | 4 | If you want to use setuptools in your package's setup.py, just include this 5 | file in the same directory with it, and add this to the top of your setup.py:: 6 | 7 | from ez_setup import use_setuptools 8 | use_setuptools() 9 | 10 | If you want to require a specific version of setuptools, set a download 11 | mirror, or use an alternate download directory, you can do so by supplying 12 | the appropriate options to ``use_setuptools()``. 13 | 14 | This file can also be run as a script to install or upgrade setuptools. 15 | """ 16 | import sys 17 | DEFAULT_VERSION = "0.6c11" 18 | DEFAULT_URL = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3] 19 | 20 | md5_data = { 21 | 'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca', 22 | 'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb', 23 | 'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b', 24 | 'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a', 25 | 'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618', 26 | 'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac', 27 | 'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5', 28 | 'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4', 29 | 'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c', 30 | 'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b', 31 | 'setuptools-0.6c10-py2.3.egg': 'ce1e2ab5d3a0256456d9fc13800a7090', 32 | 'setuptools-0.6c10-py2.4.egg': '57d6d9d6e9b80772c59a53a8433a5dd4', 33 | 'setuptools-0.6c10-py2.5.egg': 'de46ac8b1c97c895572e5e8596aeb8c7', 34 | 'setuptools-0.6c10-py2.6.egg': '58ea40aef06da02ce641495523a0b7f5', 35 | 'setuptools-0.6c11-py2.3.egg': '2baeac6e13d414a9d28e7ba5b5a596de', 36 | 'setuptools-0.6c11-py2.4.egg': 'bd639f9b0eac4c42497034dec2ec0c2b', 37 | 'setuptools-0.6c11-py2.5.egg': '64c94f3bf7a72a13ec83e0b24f2749b2', 38 | 'setuptools-0.6c11-py2.6.egg': 'bfa92100bd772d5a213eedd356d64086', 39 | 'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27', 40 | 'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277', 41 | 'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa', 42 | 'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e', 43 | 'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e', 44 | 'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f', 45 | 'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2', 46 | 'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc', 47 | 'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167', 48 | 'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64', 49 | 'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d', 50 | 'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20', 51 | 'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab', 52 | 'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53', 53 | 'setuptools-0.6c7-py2.3.egg': '209fdf9adc3a615e5115b725658e13e2', 54 | 'setuptools-0.6c7-py2.4.egg': '5a8f954807d46a0fb67cf1f26c55a82e', 55 | 'setuptools-0.6c7-py2.5.egg': '45d2ad28f9750e7434111fde831e8372', 56 | 'setuptools-0.6c8-py2.3.egg': '50759d29b349db8cfd807ba8303f1902', 57 | 'setuptools-0.6c8-py2.4.egg': 'cba38d74f7d483c06e9daa6070cce6de', 58 | 'setuptools-0.6c8-py2.5.egg': '1721747ee329dc150590a58b3e1ac95b', 59 | 'setuptools-0.6c9-py2.3.egg': 'a83c4020414807b496e4cfbe08507c03', 60 | 'setuptools-0.6c9-py2.4.egg': '260a2be2e5388d66bdaee06abec6342a', 61 | 'setuptools-0.6c9-py2.5.egg': 'fe67c3e5a17b12c0e7c541b7ea43a8e6', 62 | 'setuptools-0.6c9-py2.6.egg': 'ca37b1ff16fa2ede6e19383e7b59245a', 63 | } 64 | 65 | import sys, os 66 | try: from hashlib import md5 67 | except ImportError: from md5 import md5 68 | 69 | def _validate_md5(egg_name, data): 70 | if egg_name in md5_data: 71 | digest = md5(data).hexdigest() 72 | if digest != md5_data[egg_name]: 73 | print >>sys.stderr, ( 74 | "md5 validation of %s failed! (Possible download problem?)" 75 | % egg_name 76 | ) 77 | sys.exit(2) 78 | return data 79 | 80 | def use_setuptools( 81 | version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, 82 | download_delay=15 83 | ): 84 | """Automatically find/download setuptools and make it available on sys.path 85 | 86 | `version` should be a valid setuptools version number that is available 87 | as an egg for download under the `download_base` URL (which should end with 88 | a '/'). `to_dir` is the directory where setuptools will be downloaded, if 89 | it is not already available. If `download_delay` is specified, it should 90 | be the number of seconds that will be paused before initiating a download, 91 | should one be required. If an older version of setuptools is installed, 92 | this routine will print a message to ``sys.stderr`` and raise SystemExit in 93 | an attempt to abort the calling script. 94 | """ 95 | was_imported = 'pkg_resources' in sys.modules or 'setuptools' in sys.modules 96 | def do_download(): 97 | egg = download_setuptools(version, download_base, to_dir, download_delay) 98 | sys.path.insert(0, egg) 99 | import setuptools; setuptools.bootstrap_install_from = egg 100 | try: 101 | import pkg_resources 102 | except ImportError: 103 | return do_download() 104 | try: 105 | pkg_resources.require("setuptools>="+version); return 106 | except pkg_resources.VersionConflict, e: 107 | if was_imported: 108 | print >>sys.stderr, ( 109 | "The required version of setuptools (>=%s) is not available, and\n" 110 | "can't be installed while this script is running. Please install\n" 111 | " a more recent version first, using 'easy_install -U setuptools'." 112 | "\n\n(Currently using %r)" 113 | ) % (version, e.args[0]) 114 | sys.exit(2) 115 | else: 116 | del pkg_resources, sys.modules['pkg_resources'] # reload ok 117 | return do_download() 118 | except pkg_resources.DistributionNotFound: 119 | return do_download() 120 | 121 | def download_setuptools( 122 | version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, 123 | delay = 15 124 | ): 125 | """Download setuptools from a specified location and return its filename 126 | 127 | `version` should be a valid setuptools version number that is available 128 | as an egg for download under the `download_base` URL (which should end 129 | with a '/'). `to_dir` is the directory where the egg will be downloaded. 130 | `delay` is the number of seconds to pause before an actual download attempt. 131 | """ 132 | import urllib2, shutil 133 | egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3]) 134 | url = download_base + egg_name 135 | saveto = os.path.join(to_dir, egg_name) 136 | src = dst = None 137 | if not os.path.exists(saveto): # Avoid repeated downloads 138 | try: 139 | from distutils import log 140 | if delay: 141 | log.warn(""" 142 | --------------------------------------------------------------------------- 143 | This script requires setuptools version %s to run (even to display 144 | help). I will attempt to download it for you (from 145 | %s), but 146 | you may need to enable firewall access for this script first. 147 | I will start the download in %d seconds. 148 | 149 | (Note: if this machine does not have network access, please obtain the file 150 | 151 | %s 152 | 153 | and place it in this directory before rerunning this script.) 154 | ---------------------------------------------------------------------------""", 155 | version, download_base, delay, url 156 | ); from time import sleep; sleep(delay) 157 | log.warn("Downloading %s", url) 158 | src = urllib2.urlopen(url) 159 | # Read/write all in one block, so we don't create a corrupt file 160 | # if the download is interrupted. 161 | data = _validate_md5(egg_name, src.read()) 162 | dst = open(saveto,"wb"); dst.write(data) 163 | finally: 164 | if src: src.close() 165 | if dst: dst.close() 166 | return os.path.realpath(saveto) 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | def main(argv, version=DEFAULT_VERSION): 204 | """Install or upgrade setuptools and EasyInstall""" 205 | try: 206 | import setuptools 207 | except ImportError: 208 | egg = None 209 | try: 210 | egg = download_setuptools(version, delay=0) 211 | sys.path.insert(0,egg) 212 | from setuptools.command.easy_install import main 213 | return main(list(argv)+[egg]) # we're done here 214 | finally: 215 | if egg and os.path.exists(egg): 216 | os.unlink(egg) 217 | else: 218 | if setuptools.__version__ == '0.0.1': 219 | print >>sys.stderr, ( 220 | "You have an obsolete version of setuptools installed. Please\n" 221 | "remove it from your system entirely before rerunning this script." 222 | ) 223 | sys.exit(2) 224 | 225 | req = "setuptools>="+version 226 | import pkg_resources 227 | try: 228 | pkg_resources.require(req) 229 | except pkg_resources.VersionConflict: 230 | try: 231 | from setuptools.command.easy_install import main 232 | except ImportError: 233 | from easy_install import main 234 | main(list(argv)+[download_setuptools(delay=0)]) 235 | sys.exit(0) # try to force an exit 236 | else: 237 | if argv: 238 | from setuptools.command.easy_install import main 239 | main(argv) 240 | else: 241 | print "Setuptools version",version,"or greater has been installed." 242 | print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)' 243 | 244 | def update_md5(filenames): 245 | """Update our built-in md5 registry""" 246 | 247 | import re 248 | 249 | for name in filenames: 250 | base = os.path.basename(name) 251 | f = open(name,'rb') 252 | md5_data[base] = md5(f.read()).hexdigest() 253 | f.close() 254 | 255 | data = [" %r: %r,\n" % it for it in md5_data.items()] 256 | data.sort() 257 | repl = "".join(data) 258 | 259 | import inspect 260 | srcfile = inspect.getsourcefile(sys.modules[__name__]) 261 | f = open(srcfile, 'rb'); src = f.read(); f.close() 262 | 263 | match = re.search("\nmd5_data = {\n([^}]+)}", src) 264 | if not match: 265 | print >>sys.stderr, "Internal error!" 266 | sys.exit(2) 267 | 268 | src = src[:match.start(1)] + repl + src[match.end(1):] 269 | f = open(srcfile,'w') 270 | f.write(src) 271 | f.close() 272 | 273 | 274 | if __name__=='__main__': 275 | if len(sys.argv)>2 and sys.argv[1]=='--md5update': 276 | update_md5(sys.argv[2:]) 277 | else: 278 | main(sys.argv[1:]) 279 | -------------------------------------------------------------------------------- /generate_descriptor.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2.6 2 | # Copyright 2010 Google Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | """A script to generate ETW event descriptors for ETW MOF events.""" 16 | import optparse 17 | import os 18 | import pywintypes 19 | import win32com.client 20 | 21 | LICENSE_HEADER = """\ 22 | #!/usr/bin/python2.6 23 | # Copyright 2010 Google Inc. 24 | # 25 | # Licensed under the Apache License, Version 2.0 (the "License"); 26 | # you may not use this file except in compliance with the License. 27 | # You may obtain a copy of the License at 28 | # 29 | # http://www.apache.org/licenses/LICENSE-2.0 30 | # 31 | # Unless required by applicable law or agreed to in writing, software 32 | # distributed under the License is distributed on an "AS IS" BASIS, 33 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 34 | # See the License for the specific language governing permissions and 35 | # limitations under the License.""" 36 | 37 | DO_NOT_EDIT_HEADER = """\ 38 | \"\"\"Generated event descriptor file for a MOF event class. 39 | 40 | DO NOT EDIT. This is an ETW event descriptor file generated by 41 | sawbuck/py/etw/generate_descriptor.py. It contains event descriptions for 42 | MOF GUID %s. 43 | \"\"\" 44 | """ 45 | 46 | # Generate symbols for the WbemScripting module so that we can have symbols 47 | # for debugging and use constants throughout the file. 48 | win32com.client.gencache.EnsureModule('{565783C6-CB41-11D1-8B02-00600806D9B6}', 49 | 0, 1, 1) 50 | 51 | 52 | class DescriptorGenerator(object): 53 | """A class used to generate an ETW event descriptor.""" 54 | 55 | def __init__(self): 56 | self._locator = win32com.client.Dispatch('WbemScripting.SWbemLocator') 57 | self._services = self._locator.ConnectServer('localhost', r'root\wmi') 58 | 59 | def _GetCategories(self, guid): 60 | """Returns the event classes (categories) that match the given GUID. 61 | 62 | Args: 63 | guid: The guid string of the categories to retrieve. 64 | 65 | Returns: 66 | A list of SWbemObject category classes. 67 | """ 68 | categories = self._services.SubclassesOf( 69 | 'EventTrace', win32com.client.constants.wbemFlagUseAmendedQualifiers) 70 | 71 | ret = [] 72 | for category in categories: 73 | if self._GetQualifier(category, 'Guid') == guid: 74 | ret.append(category) 75 | 76 | return ret 77 | 78 | def _GetEvents(self, category): 79 | """Returns the event subclasses of the given category class. 80 | 81 | Args: 82 | category: The SWbemObject category class to get event subclasses for. 83 | 84 | Returns: 85 | A list of SWbemObject event classes. 86 | """ 87 | return self._services.SubclassesOf( 88 | category.Path_.Class, 89 | win32com.client.constants.wbemFlagUseAmendedQualifiers) 90 | 91 | def _GetQualifier(self, obj, name): 92 | """Helper function to get a qualifer that may not exist. 93 | 94 | Args: 95 | obj: The SWbemObject class on which to query for a qualifier. 96 | name: The name of the qualifier. 97 | 98 | Returns: 99 | The value (mixed type) of the named qualifier or None if the named 100 | qualifier doesn't exist. 101 | """ 102 | try: 103 | return obj.Qualifiers_.Item(name).Value 104 | except pywintypes.com_error: 105 | return None 106 | 107 | def Generate(self, guid, output_dir): 108 | """Generates a descriptor file for guid and stores it in output_dir. 109 | 110 | Args: 111 | guid: The string guid of the MOF event category class. 112 | output_dir: The output directory to save the generated descriptors in. 113 | """ 114 | guid = guid.lower() 115 | 116 | lines = [] 117 | lines.append(LICENSE_HEADER) 118 | lines.append(DO_NOT_EDIT_HEADER % guid) 119 | lines.append('') 120 | lines.append('from etw.descriptors import event') 121 | lines.append('from etw.descriptors import field') 122 | 123 | categories = self._GetCategories(guid) 124 | lines.append('') 125 | lines.append('') 126 | lines.append(self._GenerateEventTypeClass(guid, categories)) 127 | 128 | for category in categories: 129 | lines.append('') 130 | lines.append('') 131 | lines.append(self._GenerateCategoryClass(category)) 132 | lines.append('') 133 | 134 | # All categories of the same GUID have the same display name. 135 | name = categories[0].Qualifiers_.Item('DisplayName').Value.lower() 136 | f = open(os.path.join(output_dir, '%s.py' % name), 'wb') 137 | f.write('\n'.join(lines)) 138 | f.close() 139 | 140 | def _GenerateEventTypeClass(self, guid, categories): 141 | """Generates the event type class definition for the given categories. 142 | 143 | Args: 144 | guid: The string guid of the event category. 145 | categories: A list of SWbemObject category classes that match the guid. 146 | 147 | Returns: 148 | A string of the generated code for the event type class. 149 | """ 150 | lines = [] 151 | lines.append('class Event(object):') 152 | lines.append(' GUID = \'%s\'' % guid) 153 | 154 | event_types = dict() 155 | for category in categories: 156 | for event in self._GetEvents(category): 157 | event_types.update(self._GetEventTypes(event)) 158 | 159 | keys = event_types.keys() 160 | keys.sort() 161 | for key in keys: 162 | lines.append(' %s = (GUID, %d)' % (event_types[key], key)) 163 | 164 | return '\n'.join(lines) 165 | 166 | def _GetEventTypes(self, event): 167 | """Gets a dict of event type to name for the given event. 168 | 169 | Args: 170 | event: The SWbemObject event class to get event types for. 171 | 172 | Returns: 173 | A dict of event type (number) to event type name (string). 174 | """ 175 | event_types = event.Qualifiers_.Item('EventType').Value 176 | event_type_names = event.Qualifiers_.Item('EventTypeName').Value 177 | 178 | # The above qualifiers can be int or list, so we convert ints to list. 179 | if type(event_types) is int: 180 | event_types = [event_types] 181 | event_type_names = [event_type_names] 182 | 183 | return dict(zip(event_types, event_type_names)) 184 | 185 | def _GenerateCategoryClass(self, category): 186 | """Generates the category class definition for the given category. 187 | 188 | Args: 189 | category: The SWbemObject category class to generate a definition for. 190 | 191 | Returns: 192 | A string of the generated event category class definition. 193 | """ 194 | class_name = category.Path_.Class 195 | lines = [] 196 | lines.append('class %s(event.EventCategory):' % class_name) 197 | lines.append(' GUID = Event.GUID') 198 | lines.append(' VERSION = %d' % 199 | self._GetQualifier(category, 'EventVersion') or 0) 200 | 201 | for event in self._GetEvents(category): 202 | lines.append('') 203 | lines.append(self._GenerateEventClass(class_name, event)) 204 | 205 | return '\n'.join(lines) 206 | 207 | def _GenerateEventClass(self, category_name, event): 208 | """Generates the event class definition for the given event. 209 | 210 | Args: 211 | category_name: The string name of the parent category class. 212 | event: The SWbemObject event class to generate a definition for. 213 | 214 | Returns: 215 | A string of the generated event class definition. 216 | """ 217 | # Class may be named "EventCategoryName_EventClassName" or "EventClassName". 218 | # We just want the event class name, so we strip off the category name if 219 | # it's there. 220 | class_name = event.Path_.Class 221 | index = class_name.find(category_name + '_') 222 | if index == 0: 223 | class_name = class_name[len(category_name) + 1:] 224 | 225 | lines = [] 226 | lines.append(' class %s(event.EventClass):' % class_name) 227 | lines.append(' _event_types_ = [%s]' % self._GenerateEventTypes(event)) 228 | lines.append(' _fields_ = [%s]' % self._GenerateFields(event)) 229 | 230 | return '\n'.join(lines) 231 | 232 | def _GenerateEventTypes(self, event): 233 | """Generates the event types as a comma delimited string for event. 234 | 235 | Args: 236 | event: The SWbemObject event class to generate an event types definition 237 | for. 238 | 239 | Returns: 240 | A string of the generated event types definition. 241 | """ 242 | event_types = self._GetEventTypes(event).values() 243 | event_types.sort() 244 | event_types = ['Event.%s' % str(event_type) for event_type in event_types] 245 | return ',\n '.join(event_types) 246 | 247 | def _GenerateFields(self, event): 248 | """Generates the fields as comma delimited tuples for event. 249 | 250 | Args: 251 | event: The SWbemObject event class to generate a fields definition for. 252 | 253 | Returns: 254 | A string of the generated fields definition. 255 | """ 256 | lines = [] 257 | fields = self._GetFields(event) 258 | keys = fields.keys() 259 | keys.sort() 260 | for key in keys: 261 | field = fields[key] 262 | lines.append('(\'%s\', field.%s)' % 263 | (field.Name, self._GetFieldType(field))) 264 | 265 | return ',\n '.join(lines) 266 | 267 | def _GetFields(self, event): 268 | """Gets the fields of the given event. 269 | 270 | Args: 271 | event: The SWbemObject event class to get the fields for. 272 | 273 | Returns: 274 | A dict of numerical index to SWbemProperty field object. 275 | """ 276 | # Normally we could just return a list here, but the properties don't come 277 | # out in the right order. We return a dict with the WmiDataId property 278 | # as the key instead. 279 | fields = dict() 280 | for prop in event.Properties_: 281 | index = self._GetQualifier(prop, 'WmiDataId') 282 | if index: 283 | fields[index] = prop 284 | return fields 285 | 286 | def _GetFieldType(self, field): 287 | """Gets the buffer reader function to call for the given property. 288 | 289 | Args: 290 | field: The SWbemProperty field object to get the field type for. 291 | 292 | Returns: 293 | The type of the field as a string. 294 | 295 | Raises: 296 | ValueError: Raised if the field's CIMType is not handled. 297 | """ 298 | const = win32com.client.constants 299 | if field.CIMType == const.wbemCimtypeBoolean: 300 | return 'Boolean' 301 | elif field.CIMType == const.wbemCimtypeSint8: 302 | return 'Int8' 303 | elif field.CIMType == const.wbemCimtypeUint8: 304 | return 'UInt8' 305 | elif field.CIMType == const.wbemCimtypeSint16: 306 | return 'Int16' 307 | elif field.CIMType == const.wbemCimtypeUint16: 308 | return 'UInt16' 309 | elif field.CIMType == const.wbemCimtypeSint32: 310 | return 'Int32' 311 | elif field.CIMType == const.wbemCimtypeUint32: 312 | pointer = self._GetQualifier(field, 'pointer') 313 | if pointer: 314 | return 'Pointer' 315 | else: 316 | return 'UInt32' 317 | elif field.CIMType == const.wbemCimtypeSint64: 318 | return 'Int64' 319 | elif field.CIMType == const.wbemCimtypeUint64: 320 | return 'UInt64' 321 | elif field.CIMType == const.wbemCimtypeString: 322 | str_format = self._GetQualifier(field, 'format') 323 | if str_format == 'w': 324 | return 'WString' 325 | else: 326 | return 'String' 327 | elif field.CIMType == const.wbemCimtypeObject: 328 | ext = field.Qualifiers_.Item('extension').Value 329 | if ext == 'Sid': 330 | return 'Sid' 331 | elif ext == 'SizeT': 332 | return 'Int32' 333 | elif ext == 'WmiTime': 334 | return 'WmiTime' 335 | raise ValueError('Field %s is of unhandled object type: %s' % 336 | (field.Name, ext)) 337 | 338 | raise ValueError('Field %s is of unhandled type: %s' % 339 | (field.Name, field.CIMType)) 340 | 341 | 342 | def main(): 343 | parser = optparse.OptionParser() 344 | parser.add_option('-g', '--guid', dest='guid', 345 | help='GUID of the event category to generate a paser for.') 346 | parser.add_option('-o', '--output_dir', dest='output_dir', default='.', 347 | help='Output directory for the generated file.') 348 | options, unused_args = parser.parse_args() 349 | 350 | if not options.guid: 351 | print 'No guid specified.' 352 | return 353 | 354 | generator = DescriptorGenerator() 355 | generator.Generate(options.guid, options.output_dir) 356 | 357 | 358 | if __name__ == '__main__': 359 | main() 360 | -------------------------------------------------------------------------------- /generate_descriptors.bat: -------------------------------------------------------------------------------- 1 | :: Copyright 2010 Google Inc. 2 | :: 3 | :: Licensed under the Apache License, Version 2.0 (the "License"); 4 | :: you may not use this file except in compliance with the License. 5 | :: You may obtain a copy of the License at 6 | :: 7 | :: http://www.apache.org/licenses/LICENSE-2.0 8 | :: 9 | :: Unless required by applicable law or agreed to in writing, software 10 | :: distributed under the License is distributed on an "AS IS" BASIS, 11 | :: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | :: See the License for the specific language governing permissions and 13 | :: limitations under the License. 14 | @echo off 15 | 16 | :: Image load events 17 | python generate_descriptor.py -g {2cb15d1d-5fc1-11d2-abe1-00a0c911f518} -o etw\descriptors 18 | 19 | :: Page fault events 20 | python generate_descriptor.py -g {3d6fa8d3-fe05-11d0-9dda-00c04fd7ba7c} -o etw\descriptors 21 | 22 | :: Process events 23 | python generate_descriptor.py -g {3d6fa8d0-fe05-11d0-9dda-00c04fd7ba7c} -o etw\descriptors 24 | 25 | :: Thread events 26 | python generate_descriptor.py -g {3d6fa8d1-fe05-11d0-9dda-00c04fd7ba7c} -o etw\descriptors 27 | 28 | :: Registry events 29 | python generate_descriptor.py -g {ae53722e-c863-11d2-8659-00c04fa321a1} -o etw\descriptors 30 | 31 | :: File Io events 32 | python generate_descriptor.py -g {90cbdc39-4a3e-11d1-84f4-0000f80464e3} -o etw\descriptors 33 | 34 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [egg_info] 2 | tag_build = 3 | tag_date = 0 4 | tag_svn_revision = 0 5 | 6 | [nosetests] 7 | verbosity = 2 8 | 9 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!python 2 | # Copyright 2010 Google Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | """Setup script for etw module.""" 16 | import sys 17 | print sys.version 18 | 19 | from ez_setup import use_setuptools 20 | use_setuptools() 21 | 22 | from setuptools import setup 23 | 24 | 25 | setup(name = 'ETW', 26 | version = '0.6.5.0', 27 | description = 'Utility classes for Event Tracing for Windows', 28 | author = 'Sigurdur Asgeirsson', 29 | author_email = 'siggi@chromium.org', 30 | url = 'http://code.google.com/p/sawbuck', 31 | packages = ['etw', 'etw.descriptors'], 32 | tests_require = ["nose>=0.9.2"], 33 | test_suite = 'nose.collector', 34 | license = 'Apache 2.0') 35 | -------------------------------------------------------------------------------- /test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebmarchand/pyetw/302431b0eecc7698f1b7641ed8cf39f8769beb4b/test/__init__.py -------------------------------------------------------------------------------- /test/descriptors/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebmarchand/pyetw/302431b0eecc7698f1b7641ed8cf39f8769beb4b/test/descriptors/__init__.py -------------------------------------------------------------------------------- /test/descriptors/test_binary_buffer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2.6 2 | # Copyright 2010 Google Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | """Unit test for the etw.descriptors.binary_buffer module.""" 16 | import ctypes 17 | import unittest 18 | from etw.descriptors import binary_buffer 19 | 20 | 21 | class BinaryBufferTest(unittest.TestCase): 22 | def testContains(self): 23 | """Test buffer Contains.""" 24 | test_buffer = binary_buffer.BinaryBuffer(0, 10) 25 | 26 | self.assertFalse(test_buffer.Contains(-1, 5)) # pos < 0 27 | self.assertFalse(test_buffer.Contains(11, 5)) # pos > buffer length 28 | self.assertFalse(test_buffer.Contains(5, -1)) # len < 0 29 | self.assertFalse(test_buffer.Contains(5, 11)) # len > buffer length 30 | 31 | self.assertTrue(test_buffer.Contains(0, 0)) 32 | self.assertTrue(test_buffer.Contains(0, 5)) 33 | self.assertTrue(test_buffer.Contains(0, 10)) 34 | self.assertFalse(test_buffer.Contains(0, 11)) 35 | 36 | self.assertTrue(test_buffer.Contains(5, 0)) 37 | self.assertTrue(test_buffer.Contains(5, 2)) 38 | self.assertTrue(test_buffer.Contains(5, 5)) 39 | self.assertFalse(test_buffer.Contains(5, 6)) 40 | 41 | self.assertTrue(test_buffer.Contains(10, 0)) 42 | self.assertFalse(test_buffer.Contains(10, 1)) 43 | 44 | def testGetAt(self): 45 | """Test buffer GetAt.""" 46 | test_buffer = binary_buffer.BinaryBuffer(20, 10) 47 | 48 | self.assertEqual(20, test_buffer.GetAt(0, 0)) 49 | self.assertEqual(20, test_buffer.GetAt(0, 5)) 50 | self.assertEqual(22, test_buffer.GetAt(2, 5)) 51 | self.assertEqual(28, test_buffer.GetAt(8, 2)) 52 | 53 | self.assertRaises(binary_buffer.BufferOverflowError, 54 | test_buffer.GetAt, 0, 11) 55 | self.assertRaises(binary_buffer.BufferOverflowError, 56 | test_buffer.GetAt, 5, 10) 57 | self.assertRaises(binary_buffer.BufferOverflowError, 58 | test_buffer.GetAt, 7, 15) 59 | 60 | def testGetTypeAt(self): 61 | """Test buffer GetTypeAt.""" 62 | data = ctypes.c_buffer(8) 63 | ptr = ctypes.cast(data, ctypes.c_void_p) 64 | test_buffer = binary_buffer.BinaryBuffer(ptr.value, ctypes.sizeof(data)) 65 | 66 | # char 67 | data.value = 'hello' 68 | self.assertEqual('h', test_buffer.GetTypeAt(0, ctypes.c_char)) 69 | self.assertEqual('o', test_buffer.GetTypeAt(4, ctypes.c_char)) 70 | 71 | # uint 72 | uint_ptr = ctypes.cast(ptr, ctypes.POINTER(ctypes.c_uint)) 73 | uint_ptr.contents.value = 1234 74 | self.assertEqual(1234, test_buffer.GetTypeAt(0, ctypes.c_uint)) 75 | 76 | # int 77 | int_ptr = ctypes.cast(ptr.value + 4, ctypes.POINTER(ctypes.c_int)) 78 | int_ptr.contents.value = -4321 79 | self.assertEqual(-4321, test_buffer.GetTypeAt(4, ctypes.c_int)) 80 | 81 | # ulonglong 82 | long_ptr = ctypes.cast(ptr, ctypes.POINTER(ctypes.c_ulonglong)) 83 | long_ptr.contents.value = 123456789 84 | self.assertEqual(123456789, test_buffer.GetTypeAt(0, ctypes.c_ulonglong)) 85 | 86 | # longlong 87 | long_ptr = ctypes.cast(ptr, ctypes.POINTER(ctypes.c_longlong)) 88 | long_ptr.contents.value = -123456789 89 | self.assertEqual(-123456789, test_buffer.GetTypeAt(0, ctypes.c_longlong)) 90 | 91 | # error 92 | self.assertRaises(binary_buffer.BufferOverflowError, 93 | test_buffer.GetTypeAt, 4, ctypes.c_longlong) 94 | 95 | def testGetStringAt(self): 96 | """Test buffer GetStringAt.""" 97 | # non-wide 98 | data = ctypes.create_string_buffer('Hello!') 99 | test_buffer = binary_buffer.BinaryBuffer( 100 | ctypes.cast(data, ctypes.c_void_p).value, ctypes.sizeof(data)) 101 | self.assertEqual('Hello!', test_buffer.GetStringAt(0)) 102 | self.assertEqual('lo!', test_buffer.GetStringAt(3)) 103 | 104 | # wide 105 | data = ctypes.create_unicode_buffer('Hello!') 106 | test_buffer = binary_buffer.BinaryBuffer( 107 | ctypes.cast(data, ctypes.c_void_p).value, ctypes.sizeof(data)) 108 | self.assertEqual(u'Hello!', test_buffer.GetWStringAt(0)) 109 | self.assertEqual(u'lo!', test_buffer.GetWStringAt(6)) 110 | 111 | # error 112 | self.assertRaises(binary_buffer.BufferOverflowError, 113 | test_buffer.GetStringAt, 20) 114 | self.assertRaises(binary_buffer.BufferOverflowError, 115 | test_buffer.GetWStringAt, 20) 116 | 117 | 118 | class BinaryBufferReaderTest(unittest.TestCase): 119 | def testConsume(self): 120 | """Test buffer reader Consume.""" 121 | reader = binary_buffer.BinaryBufferReader(0, 10) 122 | reader.Consume(5) 123 | self.assertEquals(5, reader._offset) 124 | self.assertRaises(binary_buffer.BufferOverflowError, 125 | reader.Consume, 6) 126 | 127 | def testRead(self): 128 | """Test buffer reader Read.""" 129 | data = ctypes.c_buffer(4) 130 | reader = binary_buffer.BinaryBufferReader( 131 | ctypes.cast(data, ctypes.c_void_p).value, ctypes.sizeof(data)) 132 | 133 | int_ptr = ctypes.cast(data, ctypes.POINTER(ctypes.c_int)) 134 | int_ptr.contents.value = -4321 135 | self.assertEqual(-4321, reader.Read(ctypes.c_int)) 136 | self.assertEqual(ctypes.sizeof(ctypes.c_int), reader._offset) 137 | 138 | def testReadString(self): 139 | """Test buffer reader ReaderString.""" 140 | data = ctypes.create_string_buffer('Hello!') 141 | reader = binary_buffer.BinaryBufferReader( 142 | ctypes.cast(data, ctypes.c_void_p).value, ctypes.sizeof(data)) 143 | self.assertEqual('Hello!', reader.ReadString()) 144 | 145 | data = ctypes.create_unicode_buffer('Hello!') 146 | reader = binary_buffer.BinaryBufferReader( 147 | ctypes.cast(data, ctypes.c_void_p).value, ctypes.sizeof(data)) 148 | self.assertEqual(u'Hello!', reader.ReadWString()) 149 | 150 | POINTER_SIZE_32 = 4 151 | MAX_SID_SIZE = 68 152 | WIN_WORLD_SID = 1 153 | 154 | def testReadSid(self): 155 | """Test buffer reader ReadSid.""" 156 | data = ctypes.c_buffer(2 * self.POINTER_SIZE_32 + self.MAX_SID_SIZE) 157 | # The first pointer preceding a Sid must be non-NULL. 158 | data[0] = "1" 159 | ptr = ctypes.cast(data, ctypes.c_void_p) 160 | sid_ptr = ctypes.cast(ptr.value + 2 * self.POINTER_SIZE_32, ctypes.c_void_p) 161 | 162 | size = ctypes.c_int() 163 | size.value = self.MAX_SID_SIZE 164 | self.assertNotEqual(0, ctypes.windll.advapi32.CreateWellKnownSid( 165 | self.WIN_WORLD_SID, None, sid_ptr, ctypes.byref(size))) 166 | 167 | reader = binary_buffer.BinaryBufferReader(ptr.value, ctypes.sizeof(data)) 168 | sid = reader.ReadSid(False) 169 | self.assertTrue(sid.IsValid()) 170 | 171 | # Now try and read a non-Sid, which is preceded by a NULL pointer. 172 | data = ctypes.c_buffer(self.POINTER_SIZE_32) 173 | ptr = ctypes.cast(data, ctypes.c_void_p) 174 | reader = binary_buffer.BinaryBufferReader(ptr.value, ctypes.sizeof(data)) 175 | self.assertEqual(None, reader.ReadSid(False)) 176 | 177 | 178 | if __name__ == '__main__': 179 | unittest.main() 180 | -------------------------------------------------------------------------------- /test/descriptors/test_event.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2.6 2 | # Copyright 2010 Google Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | """Unit test for the etw.descriptors.event module.""" 16 | import ctypes 17 | import datetime 18 | import time 19 | import unittest 20 | from etw import evntrace 21 | from etw import util 22 | from etw.descriptors import binary_buffer 23 | from etw.descriptors import event 24 | from etw.descriptors import field 25 | 26 | 27 | class MockEventTrace(object): 28 | """This class mocks the EVENT_TRACE structure.""" 29 | 30 | class MockContents(object): 31 | pass 32 | 33 | class MockHeader(object): 34 | def __init__(self, header_dict): 35 | for k, v in header_dict.items(): 36 | setattr(self, k, v) 37 | 38 | def __init__(self, header_dict, mof_data, mof_length): 39 | self.contents = MockEventTrace.MockContents() 40 | self.contents.Header = MockEventTrace.MockHeader(header_dict) 41 | self.contents.MofData = mof_data 42 | self.contents.MofLength = mof_length 43 | 44 | 45 | class MockSession(object): 46 | """This class mocks a _TraceLogSession object.""" 47 | def __init__(self): 48 | self.is_64_bit_log = False 49 | 50 | def SessionTimeToTime(self, session_time): 51 | return util.FileTimeToTime(session_time) 52 | 53 | 54 | class EventClassTest(unittest.TestCase): 55 | def testCreation(self): 56 | """Test creating a subclass of EventClass.""" 57 | class TestEventClass(event.EventClass): 58 | _fields_ = [('TestString', field.String), 59 | ('TestInt32', field.Int32), 60 | ('TestInt64', field.Int64)] 61 | 62 | time_s = int(time.time()) 63 | date_time = datetime.datetime.utcfromtimestamp(time_s) 64 | sys_time = evntrace.SYSTEMTIME( 65 | date_time.year, date_time.month, 0, date_time.day, 66 | date_time.hour, date_time.minute, date_time.second, 67 | date_time.microsecond / 1000) 68 | file_time = ctypes.wintypes.FILETIME() 69 | ctypes.windll.kernel32.SystemTimeToFileTime(ctypes.byref(sys_time), 70 | ctypes.byref(file_time)) 71 | time_stamp = file_time.dwHighDateTime 72 | time_stamp <<= 32 73 | time_stamp |= file_time.dwLowDateTime 74 | 75 | header_dict = {'ProcessId': 5678, 'ThreadId': 8765, 'TimeStamp': time_stamp} 76 | 77 | data = ctypes.c_buffer(18) 78 | data.value = 'Hello' 79 | 80 | ptr = ctypes.cast(data, ctypes.c_void_p) 81 | 82 | int32 = ctypes.cast(ptr.value + 6, ctypes.POINTER(ctypes.c_int)) 83 | int32.contents.value = 1234 84 | 85 | int64 = ctypes.cast(ptr.value + 10, ctypes.POINTER(ctypes.c_longlong)) 86 | int64.contents.value = 4321 87 | 88 | mock_event_trace = MockEventTrace(header_dict, ptr.value, 89 | ctypes.sizeof(data)) 90 | mock_session = MockSession() 91 | obj = TestEventClass(mock_session, mock_event_trace) 92 | self.assertEqual(obj.process_id, 5678) 93 | self.assertEqual(obj.thread_id, 8765) 94 | self.assertEqual(obj.time_stamp, time_s) 95 | self.assertEqual(obj.TestString, 'Hello') 96 | self.assertEqual(obj.TestInt32, 1234) 97 | self.assertEqual(obj.TestInt64, 4321) 98 | 99 | def testBadBuffer(self): 100 | """Test using an invalid buffer.""" 101 | class TestEventClass(event.EventClass): 102 | _fields_ = [('FieldA', field.Int32), 103 | ('FieldB', field.Int32)] 104 | 105 | header_dict = {'ProcessId': 5678, 'ThreadId': 8765, 'TimeStamp': 123456789} 106 | 107 | data = ctypes.c_buffer(4) 108 | ptr = ctypes.cast(data, ctypes.c_void_p) 109 | int32 = ctypes.cast(ptr.value, ctypes.POINTER(ctypes.c_int)) 110 | int32.contents.value = 1234 111 | 112 | mock_event_trace = MockEventTrace(header_dict, ptr.value, 113 | ctypes.sizeof(data)) 114 | mock_session = MockSession() 115 | self.assertRaises(binary_buffer.BufferOverflowError, TestEventClass, 116 | mock_session, mock_event_trace) 117 | 118 | 119 | class EventCategoryTest(unittest.TestCase): 120 | def testCreation(self): 121 | """Test creation of a subclass of EventCategory.""" 122 | class TestEventCategory(event.EventCategory): 123 | # Although it may look like this class isn't used, it is actually 124 | # being registered in the EventClass map by the EventCategory 125 | # meta class. 126 | GUID = 'a' 127 | VERSION = 1 128 | 129 | class TestEventClass1(event.EventClass): 130 | _event_types_ = [('a', 1), 131 | ('a', 3), 132 | ('a', 5)] 133 | 134 | class TestEventClass2(event.EventClass): 135 | _event_types_ = [('a', 2), 136 | ('a', 4), 137 | ('a', 6)] 138 | 139 | # Passing cases. 140 | self.assertNotEqual(event.EventClass.Get('a', 1, 1), None) 141 | self.assertNotEqual(event.EventClass.Get('a', 1, 2), None) 142 | self.assertNotEqual(event.EventClass.Get('a', 1, 3), None) 143 | self.assertNotEqual(event.EventClass.Get('a', 1, 4), None) 144 | self.assertNotEqual(event.EventClass.Get('a', 1, 5), None) 145 | self.assertNotEqual(event.EventClass.Get('a', 1, 6), None) 146 | 147 | # Failing cases. 148 | self.assertEqual(event.EventClass.Get('b', 1, 1), None) 149 | self.assertEqual(event.EventClass.Get('a', 2, 1), None) 150 | self.assertEqual(event.EventClass.Get('a', 1, 7), None) 151 | 152 | 153 | if __name__ == '__main__': 154 | unittest.main() 155 | -------------------------------------------------------------------------------- /test/test_consumer.py: -------------------------------------------------------------------------------- 1 | #!python 2 | # Copyright 2010 Google Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | from etw import TraceEventSource, EventConsumer, EventHandler 16 | from etw.descriptors import image 17 | import exceptions 18 | import os 19 | import unittest 20 | 21 | 22 | _SRC_DIR = os.path.abspath(os.path.join(__file__, '../../../../..')) 23 | 24 | class TraceConsumerTest(unittest.TestCase): 25 | _TEST_LOG = os.path.normpath( 26 | os.path.join(_SRC_DIR, 27 | 'sawbuck/log_lib/test_data/image_data_32_v2.etl')) 28 | 29 | def _Consume(self, log_file, handlers, raw_time=False): 30 | log_consumer = TraceEventSource(handlers, raw_time) 31 | log_consumer.OpenFileSession(log_file) 32 | log_consumer.Consume() 33 | 34 | def testCreation(self): 35 | """Test creating a consumer.""" 36 | consumer = TraceEventSource() 37 | 38 | def testOpenFileSession(self): 39 | """Test opening a file session.""" 40 | consumer = TraceEventSource() 41 | consumer.OpenFileSession(self._TEST_LOG) 42 | 43 | def testConsume(self): 44 | """Test consuming a test log.""" 45 | class TestConsumer(EventConsumer): 46 | def __init__(self): 47 | super(TestConsumer, self).__init__() 48 | self._image_load_events = 0 49 | 50 | @EventHandler(image.Event.Load) 51 | def OnImageLoad(self, event_data): 52 | self._image_load_events += 1 53 | 54 | consumer = TestConsumer() 55 | self._Consume(self._TEST_LOG, [consumer]) 56 | self.assertNotEqual(consumer._image_load_events, 0) 57 | 58 | def testMultipleConsumers(self): 59 | """Test two consumers instances.""" 60 | class TestConsumer(EventConsumer): 61 | def __init__(self): 62 | super(TestConsumer, self).__init__() 63 | self._image_load_events = 0 64 | 65 | @EventHandler(image.Event.Load) 66 | def OnImageLoad(self, event_data): 67 | self._image_load_events += 1 68 | 69 | consumer1 = TestConsumer() 70 | consumer2 = TestConsumer() 71 | 72 | self._Consume(self._TEST_LOG, [consumer1, consumer2]) 73 | 74 | self.assertNotEqual(consumer1._image_load_events, 0) 75 | self.assertNotEqual(consumer2._image_load_events, 0) 76 | self.assertEqual(consumer1._image_load_events, consumer2._image_load_events) 77 | 78 | def testSubConsumers(self): 79 | """Test multi-level consumer hierarchy.""" 80 | class TestConsumer(EventConsumer): 81 | def __init__(self): 82 | super(TestConsumer, self).__init__() 83 | self._image_load_events = 0 84 | 85 | @EventHandler(image.Event.Load) 86 | def OnImageLoad(self, event_data): 87 | self._image_load_events += 1 88 | 89 | class SubTestConsumer(TestConsumer): 90 | pass 91 | 92 | consumer = SubTestConsumer() 93 | self._Consume(self._TEST_LOG, [consumer]) 94 | self.assertNotEqual(consumer._image_load_events, 0) 95 | 96 | def testMultipleEvents(self): 97 | """Test multiple event handlers.""" 98 | class TestConsumer(EventConsumer): 99 | def __init__(self): 100 | super(TestConsumer, self).__init__() 101 | self._image_start_load_events = 0 102 | self._image_start_events = 0 103 | self._image_load_events = 0 104 | 105 | @EventHandler(image.Event.Load, image.Event.DCStart) 106 | def OnImageStartLoad(self, event_data): 107 | self._image_start_load_events += 1 108 | 109 | @EventHandler(image.Event.Load) 110 | def OnImageLoad(self, event_data): 111 | self._image_load_events += 1 112 | 113 | @EventHandler(image.Event.DCStart) 114 | def OnImageStart(self, event_data): 115 | self._image_start_events += 1 116 | 117 | consumer = TestConsumer() 118 | self._Consume(self._TEST_LOG, [consumer]) 119 | self.assertTrue(consumer._image_start_load_events > 10) 120 | self.assertEquals(consumer._image_load_events + 121 | consumer._image_start_events, 122 | consumer._image_start_load_events) 123 | 124 | def testThrowFromHandler(self): 125 | """Test that throwing from a handler terminates processing.""" 126 | class TestConsumer(EventConsumer): 127 | @EventHandler(image.Event.Load) 128 | def OnImageStartLoad(self, event_data): 129 | raise exceptions.RuntimeError("Intentionally throwing") 130 | 131 | consumer = TestConsumer() 132 | self.assertRaises(exceptions.WindowsError, 133 | self._Consume, 134 | self._TEST_LOG, 135 | [consumer]) 136 | 137 | def testRawTimeConsuming(self): 138 | """Test that consuming events with raw times produces the same results.""" 139 | class TestConsumer(EventConsumer): 140 | def __init__(self, times): 141 | self._times = times 142 | 143 | @EventHandler(image.Event.Load) 144 | def OnImageStartLoad(self, event_data): 145 | self._times.append(event_data.time_stamp) 146 | 147 | cooked_times = [] 148 | consumer = TestConsumer(cooked_times) 149 | self._Consume(self._TEST_LOG, [consumer]) 150 | 151 | raw_times = [] 152 | consumer = TestConsumer(raw_times) 153 | self._Consume(self._TEST_LOG, [consumer], raw_time=True) 154 | 155 | self.assertEqual(cooked_times, raw_times) 156 | 157 | if __name__ == '__main__': 158 | unittest.main() 159 | -------------------------------------------------------------------------------- /test/test_controller.py: -------------------------------------------------------------------------------- 1 | #!python 2 | # Copyright 2010 Google Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | from etw import TraceController, TraceProperties, TraceProvider 16 | import exceptions 17 | import etw.evntrace as evn 18 | import os 19 | import tempfile 20 | import unittest 21 | 22 | 23 | class TracePropertiesTest(unittest.TestCase): 24 | def setUp(self): 25 | self._props = TraceProperties() 26 | 27 | def testInit(self): 28 | """Test property initialization.""" 29 | p = self._props 30 | self.assertEquals("", p.GetLogFileName().value) 31 | self.assertEquals("", p.GetLoggerName().value) 32 | 33 | def testSetLogFileName(self): 34 | """Test SetLogFileName.""" 35 | p = self._props 36 | p.SetLogFileName(r'c:\foo.etl') 37 | self.assertEquals(r'c:\foo.etl', p.GetLogFileName().value) 38 | 39 | 40 | class ControllerTest(unittest.TestCase): 41 | _TEST_SESSION_NAME = 'Test Session' 42 | _TEST_PROVIDER = evn.GUID('{55EC8EBB-A25D-4e0a-958D-D6E9ECB4EC5A}') 43 | 44 | def setUp(self): 45 | self._is_xp = os.sys.getwindowsversion()[0] == 5 46 | self._controller = TraceController() 47 | (file, path) = tempfile.mkstemp('.etl') 48 | os.close(file) 49 | self._temp_file = path 50 | 51 | def tearDown(self): 52 | try: 53 | self._controller.Stop() 54 | except: 55 | pass 56 | os.unlink(self._temp_file) 57 | 58 | def StartPrivateSession(self): 59 | controller = self._controller 60 | props = TraceProperties() 61 | props.SetLogFileName(self._temp_file) 62 | p = props.get() 63 | p.contents.Wnode.ClientContext = 1 # QPC timer accuracy. 64 | p.contents.LogFileMode = evn.EVENT_TRACE_FILE_MODE_SEQUENTIAL 65 | 66 | # On Vista and later, we create a private in-process log session, because 67 | # otherwise we'd need administrator privileges. Unfortunately we can't 68 | # do the same on XP and better, because the semantics of a private 69 | # logger session are different, and the IN_PROC flag is not supported. 70 | if not self._is_xp: 71 | # In-proc, process private log for non-admin use on Vista. 72 | p.contents.LogFileMode |= (evn.EVENT_TRACE_PRIVATE_IN_PROC | 73 | evn.EVENT_TRACE_PRIVATE_LOGGER_MODE) 74 | 75 | p.contents.MaximumFileSize = 100 # 100M file size. 76 | p.contents.FlushTimer = 1 # 1 second flush lag. 77 | controller.Start(self._TEST_SESSION_NAME, props) 78 | 79 | def testStart(self): 80 | """Test starting sessions.""" 81 | self.StartPrivateSession() 82 | 83 | # Should raise when trying to re-open session 84 | self.assertRaises(exceptions.WindowsError, self.StartPrivateSession) 85 | 86 | def testStop(self): 87 | """Test stopping sessions.""" 88 | # Should raise with no session going. 89 | self.assertRaises(exceptions.WindowsError, self._controller.Stop) 90 | 91 | # This starts the 'Test Session' 92 | self.testStart() 93 | 94 | # Should succeed. 95 | self._controller.Stop() 96 | 97 | def testEnableDisablePrivateSession(self): 98 | """Test enabling and disabling providers.""" 99 | self.StartPrivateSession() 100 | controller = self._controller 101 | # For a private session we can only enable and 102 | # disable providers registered in our process, so 103 | # instantiate the test provider here. 104 | provider = TraceProvider(self._TEST_PROVIDER) 105 | self.assertEquals(evn.TRACE_LEVEL_NONE, provider.enable_level) 106 | self.assertEquals(0, provider.enable_flags) 107 | 108 | controller.EnableProvider(self._TEST_PROVIDER, 109 | evn.TRACE_LEVEL_INFORMATION, 110 | 0xCAFEBABE) 111 | 112 | self.assertEquals(evn.TRACE_LEVEL_INFORMATION, provider.enable_level) 113 | self.assertEquals(0xCAFEBABE, provider.enable_flags) 114 | 115 | controller.DisableProvider(self._TEST_PROVIDER) 116 | self.assertEquals(evn.TRACE_LEVEL_NONE, provider.enable_level) 117 | self.assertEquals(0, provider.enable_flags) 118 | 119 | 120 | if __name__ == '__main__': 121 | unittest.main() 122 | -------------------------------------------------------------------------------- /test/test_provider.py: -------------------------------------------------------------------------------- 1 | #!python 2 | # Copyright 2010 Google Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | from etw import MofEvent, TraceEventSource 16 | from etw import TraceController, TraceProperties, TraceProvider 17 | import ctypes 18 | import etw.evntrace as evn 19 | import exceptions 20 | import os 21 | import tempfile 22 | import unittest 23 | 24 | 25 | class TraceProviderTest(unittest.TestCase): 26 | _TEST_PROVIDER = evn.GUID('{67644E0D-1B90-4923-9678-91102029E876}') 27 | _TEST_SESSION_NAME = 'TraceProviderTest' 28 | 29 | _LOG_EVENT_ID = evn.GUID('{7fe69228-633e-4f06-80c1-527fea23e3a7}') 30 | _LOG_MESSAGE = 10 31 | _LOG_MESSAGE_WITH_STACKTRACE = 11 32 | 33 | def setUp(self): 34 | properties = TraceProperties() 35 | try: 36 | # Shut down any session dangling from a previous run. 37 | evn.ControlTrace(evn.TRACEHANDLE(), 38 | self._TEST_SESSION_NAME, 39 | properties.get(), 40 | evn.EVENT_TRACE_CONTROL_STOP) 41 | except exceptions.WindowsError: 42 | pass 43 | 44 | (fd, name) = self._tempfile = tempfile.mkstemp('.etl', 'TraceProviderTest') 45 | os.close(fd) 46 | self._tempfile = name 47 | 48 | # Start a trace session 49 | controller = TraceController() 50 | prop = TraceProperties() 51 | prop.SetLogFileName(self._tempfile) 52 | controller.Start(self._TEST_SESSION_NAME, prop) 53 | self._controller = controller 54 | controller.EnableProvider(self._TEST_PROVIDER, 55 | evn.TRACE_LEVEL_INFORMATION, 56 | 0xFFFFFFFF) 57 | 58 | def tearDown(self): 59 | if self._controller._session: 60 | self._controller.Stop() 61 | os.unlink(self._tempfile) 62 | 63 | def testCreateProvider(self): 64 | """Test provider creation.""" 65 | provider = TraceProvider(self._TEST_PROVIDER) 66 | 67 | def testLog(self): 68 | """Log a text message to an enabled provider""" 69 | provider = TraceProvider(self._TEST_PROVIDER) 70 | mof_event = MofEvent(1, self._LOG_EVENT_ID, 71 | self._LOG_MESSAGE, 72 | evn.TRACE_LEVEL_INFORMATION) 73 | str = 'This is an event, goddamn it' 74 | string_ptr = ctypes.cast(ctypes.c_char_p(str), 75 | ctypes.POINTER(ctypes.c_char)) 76 | mof_event.SetField(0, len(str) + 1, string_ptr) 77 | provider.Log(mof_event) 78 | 79 | self._controller.Stop() 80 | 81 | class TestConsumer(TraceEventSource): 82 | def ProcessEvent(self, session, event): 83 | print event 84 | 85 | consumer = TestConsumer() 86 | consumer.OpenFileSession(self._tempfile) 87 | consumer.Consume() 88 | 89 | 90 | if __name__ == '__main__': 91 | unittest.main() 92 | -------------------------------------------------------------------------------- /test_generate_descriptor.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2.6 2 | # Copyright 2010 Google Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | """Unit test for the generate_descriptors module.""" 16 | 17 | import generate_descriptor 18 | import pywintypes 19 | import unittest 20 | import win32com.client 21 | 22 | 23 | class MockSWbemObject(object): 24 | def __init__(self, className, qualifiers=None, properties=None): 25 | self.Path_ = MockSWbemObjectPath(className) 26 | self.Qualifiers_ = MockSWbemSet(qualifiers) 27 | self.Properties_ = MockSWbemSet(properties) 28 | 29 | 30 | class MockSWbemObjectPath(object): 31 | def __init__(self, className): 32 | self.Class = className 33 | 34 | 35 | class MockSWbemProperty(object): 36 | def __init__(self, name, cimtype, qualifiers): 37 | self.Name = name 38 | self.CIMType = cimtype 39 | self.Qualifiers_ = MockSWbemSet(qualifiers) 40 | 41 | 42 | class MockSWbemQualifier(object): 43 | def __init__(self, name, value): 44 | self.Name = name 45 | self.Value = value 46 | 47 | 48 | class MockSWbemSet(object): 49 | def __init__(self, items): 50 | if not items: 51 | items = [] 52 | self._items = items 53 | 54 | def Item(self, name): 55 | for item in self._items: 56 | if item.Name == name: 57 | return item 58 | raise pywintypes.com_error 59 | 60 | def __iter__(self): 61 | return iter(self._items) 62 | 63 | 64 | class DescriptorGeneratorTest(unittest.TestCase): 65 | _TEST_GUID = '{84c2c8bc-02c7-4de1-88da-40312efbd84d}' 66 | 67 | _EXPECTED_GENERATE_EVENT_TYPE_CLASS_OUTPUT = ( 68 | 'class Event(object):\n' 69 | ' GUID = \'%s\'\n' 70 | ' START = (GUID, 0)\n' 71 | ' STOP = (GUID, 1)\n' 72 | ' ERROR = (GUID, 2)\n' 73 | ' STARTED = (GUID, 3)\n' 74 | ' STOPPED = (GUID, 4)') 75 | 76 | def testGenerateEventTypeClass(self): 77 | """Test generating an event type class.""" 78 | categories = [ 79 | MockSWbemObject('Category1'), 80 | MockSWbemObject('Category2') 81 | ] 82 | 83 | qualifiers = [ 84 | MockSWbemQualifier('EventType', [0, 1, 2]), 85 | MockSWbemQualifier('EventTypeName', ['START', 'STOP', 'ERROR']), 86 | ] 87 | event1 = MockSWbemObject('Event1', qualifiers) 88 | 89 | qualifiers = [ 90 | MockSWbemQualifier('EventType', [3, 4]), 91 | MockSWbemQualifier('EventTypeName', ['STARTED', 'STOPPED']) 92 | ] 93 | event2 = MockSWbemObject('Event2', qualifiers) 94 | 95 | def _GetEvents(category): 96 | if category.Path_.Class == 'Category1': 97 | return [event1] 98 | elif category.Path_.Class == 'Category2': 99 | return [event1, event2] 100 | 101 | generator = generate_descriptor.DescriptorGenerator() 102 | generator._GetEvents = _GetEvents 103 | output = generator._GenerateEventTypeClass(self._TEST_GUID, categories) 104 | self.assertEquals( 105 | self._EXPECTED_GENERATE_EVENT_TYPE_CLASS_OUTPUT % self._TEST_GUID, 106 | output) 107 | 108 | _EXPECTED_GENERATE_CATEGORY_CLASS_OUTPUT = ( 109 | 'class Category(event.EventCategory):\n' 110 | ' GUID = Event.GUID\n' 111 | ' VERSION = 2\n' 112 | '\n' 113 | ' class Event1(event.EventClass):\n' 114 | ' _event_types_ = [Event.Test]\n' 115 | ' _fields_ = [(\'Int32\', field.Int32)]\n' 116 | '\n' 117 | ' class Event2(event.EventClass):\n' 118 | ' _event_types_ = [Event.Test]\n' 119 | ' _fields_ = [(\'Int32\', field.Int32)]') 120 | 121 | def testGenerateEventCategory(self): 122 | """Test generating an event category.""" 123 | qualifiers = [MockSWbemQualifier('EventVersion', 2)] 124 | category = MockSWbemObject('Category', qualifiers) 125 | 126 | qualifiers = [ 127 | MockSWbemQualifier('EventType', 0), 128 | MockSWbemQualifier('EventTypeName', 'Test') 129 | ] 130 | properties = [ 131 | MockSWbemProperty('Int32', win32com.client.constants.wbemCimtypeSint32, 132 | [MockSWbemQualifier('WmiDataId', '0')]) 133 | ] 134 | 135 | def _GetEvents(unused_category): 136 | return [ 137 | MockSWbemObject('Event1', qualifiers, properties), 138 | MockSWbemObject('Event2', qualifiers, properties) 139 | ] 140 | 141 | generator = generate_descriptor.DescriptorGenerator() 142 | generator._GetEvents = _GetEvents 143 | output = generator._GenerateCategoryClass(category) 144 | self.assertEquals(self._EXPECTED_GENERATE_CATEGORY_CLASS_OUTPUT, output) 145 | 146 | _EXPECTED_GENERATE_EVENT_CLASS_OUTPUT = ( 147 | ' class Event(event.EventClass):\n' 148 | ' _event_types_ = [Event.Error,\n' 149 | ' Event.Start,\n' 150 | ' Event.Stop]\n' 151 | ' _fields_ = [(\'Int32\', field.Int32),\n' 152 | ' (\'Pointer\', field.Pointer),\n' 153 | ' (\'String\', field.String),\n' 154 | ' (\'SizeT\', field.Int32)]') 155 | 156 | def testGenerateEventClass(self): 157 | """Test generating an event class.""" 158 | # Event type names are purposely out of order alphabetically as 159 | # they should get sorted. 160 | qualifiers = [ 161 | MockSWbemQualifier('EventType', [0, 1, 2]), 162 | MockSWbemQualifier('EventTypeName', ['Start', 'Stop', 'Error']) 163 | ] 164 | # Properties are out of order by WmiDataId as they should get sorted. 165 | properties = [ 166 | MockSWbemProperty('String', win32com.client.constants.wbemCimtypeString, 167 | [MockSWbemQualifier('WmiDataId', '2'), 168 | MockSWbemQualifier('format', '')]), 169 | MockSWbemProperty('Int32', win32com.client.constants.wbemCimtypeSint32, 170 | [MockSWbemQualifier('WmiDataId', '0')]), 171 | MockSWbemProperty('SizeT', win32com.client.constants.wbemCimtypeObject, 172 | [MockSWbemQualifier('WmiDataId', '3'), 173 | MockSWbemQualifier('extension', 'SizeT')]), 174 | MockSWbemProperty('Pointer', 175 | win32com.client.constants.wbemCimtypeUint32, 176 | [MockSWbemQualifier('WmiDataId', '1'), 177 | MockSWbemQualifier('pointer', True)]) 178 | ] 179 | event = MockSWbemObject('Event', qualifiers, properties) 180 | 181 | generator = generate_descriptor.DescriptorGenerator() 182 | output = generator._GenerateEventClass('Category', event) 183 | self.assertEquals(self._EXPECTED_GENERATE_EVENT_CLASS_OUTPUT, output) 184 | 185 | 186 | if __name__ == '__main__': 187 | unittest.main() 188 | --------------------------------------------------------------------------------