def prepare(self): self.files = [os.path.join(self.args.output, 'data-%s.dtrace' % (self.args.cuts[0] if self.args.cuts else '0'))] assert not os.path.exists(self.files[0]) # TODO: remove if not asserts, or return back: was if ... os.remove(self.files[0]) dtrace_script = [DSCRIPT_HEADER] options = [ ('bufresize', 'auto'), ] if self.args.ring: # https://docs.oracle.com/cd/E19253-01/817-6223/chp-buf/index.html options += [ ('bufpolicy', 'ring'), ('bufsize', '64m') # 64 is maximum, system crashes on any bigger, even 65m ] else: options += [ ('switchrate', '10hz'), # print at 10Hz (instead of 1Hz) - brendangregg ('bufsize', '4g') ] dtrace_script.append(self.gen_options(options)) if is_domain_enabled('context_switches'): dtrace_script.append(dtrace_context_switch) if is_domain_enabled('fbt_hooks'): dtrace_script += fbt_dtrace_hooks for subcollector in self.subcollectors: hooks = subcollector.get_hooks(self.args) # support multi pid for subcollectors if hooks: dtrace_script += hooks subcollector.collect(self, True) self.script = dtrace_script
def gen_gpu_hooks( self, text ): # TODO: check /System/Library/Extensions/AppleIntelSKLGraphics.kext/Contents/Info.plist probes = '' driver = None for line in text.split('\n'): parts = line.split() signature = ' '.join(parts[4:-1]) arity = signature.count(',') + 1 name = signature.split('(')[0][1:] if 'dtHook' in name and parts[-1] == 'entry' and is_domain_enabled( 'dtHook'): probe = '%s::%s:entry{' % (parts[1], parts[3]) probe += r'printf("%x\t' + name + r'\t%x\t%x' probe += r'\t%x' * arity probe += r'\n", machtimestamp, pid, tid, ' probe += ', '.join(['arg' + str(i) for i in range(0, arity)]) stack = '' if not self.args.debug else ( STACKS['UMD'] + STACKS['KMD']).replace('{', '').replace('}', '') probe += '); %s}\n' % stack probes += probe if not driver: driver = parts[2] DTraceCollector.check_graphics_firmware(driver) if 'process_token_' in name and is_domain_enabled('process_token'): probe = '%s::%s:%s{' % (parts[1], parts[3], parts[-1]) probe += r'printf("%x\t' + parts[-1][ 0] + r'\t%x\t%x\tigfx\t' + name.replace( 'process_token_', '') probe += r'\n", machtimestamp, pid, tid' probe += ');}\n' probes += probe return probes
def prepare(self): self.files = [ os.path.join( self.args.output, 'data-%s.dtrace' % (self.args.cuts[0] if self.args.cuts else '0')) ] assert not os.path.exists( self.files[0] ) # TODO: remove if not asserts, or return back: was if ... os.remove(self.files[0]) dtrace_script = [DSCRIPT_HEADER] options = [ ('bufresize', 'auto'), ] if self.args.ring: # https://docs.oracle.com/cd/E19253-01/817-6223/chp-buf/index.html options += [ ('bufpolicy', 'ring'), ('bufsize', '64m' ) # 64 is maximum, system crashes on any bigger, even 65m ] else: options += [ ('switchrate', '10hz'), # print at 10Hz (instead of 1Hz) - brendangregg ('bufsize', '4g') ] dtrace_script.append(self.gen_options(options)) if self.args.debug: dtrace_script.append("#pragma D option debug") # XXX dtrace_script.append("#pragma D option strsize=512") # 256 is default if is_domain_enabled('context_switches'): dtrace_script.append(dtrace_context_switch) if is_domain_enabled('fbt_hooks'): dtrace_script += fbt_dtrace_hooks if self.args.filesystem: dtrace_script.append(IO_HOOKS) for subcollector in self.subcollectors: hooks = subcollector.get_hooks( self.args) # support multi pid for subcollectors if hooks: dtrace_script += hooks subcollector.collect(self, True) (probes, err) = self.execute( 'sudo -A dtrace -l -m *com.apple.driver.AppleIntel*Graphics*', log=False) # FIXME: sudo_execute if probes: dtrace_script.append(self.gen_gpu_hooks(probes)) self.script = dtrace_script
def prepare_per_pid(self): dtrace_script = [] if is_domain_enabled('pid_hooks'): dtrace_script += pid_dtrace_hooks # TODO: add %app_name% hotspots unconditionally if is_domain_enabled('instrument_target'): if self.args.victim: mod_name = os.path.basename(self.args.victim) elif self.args.target: (out, err) = DTraceCollector.execute('ps -p %d -o args' % self.args.target, log=False) if not err: lines = out.strip().split('\n') if len(lines) > 1: executable = lines[1].split()[0] mod_name = executable.split('/')[-1] print('Auto-instrumented module:', mod_name) dtrace_script += bracket_hook(mod_name.replace(' ', '*')) if is_domain_enabled('opencl'): dtrace_script.append(OPEN_CL) return dtrace_script
def start(self): if self.is_recording(): self.cancel() profile = os.path.normpath( os.path.join(self.args.bindir, '..', 'ETW', 'IntelSEAPI.wprp')) profiles = [ '-start %s' % option for option, _ in WPRCollector.get_options() if is_domain_enabled('wpa.' + option) ] cmd = '"%s" -start "%s" %s %s' % ( self.wpr, profile, ' '.join(profiles), ('' if self.args.ring else '-filemode')) (out, err) = self.execute(cmd) if err: return self.started = True return self
def run_dtrace(self, attach_by_name=False, victim=None, env=None): self.attach_by_pid = False # spawn dtrace tracers and exit, all means to stop it must be saved to self members: # launch command line with dtrace script and remember pid script = os.path.join(self.args.output, 'script.d') cmd = self.get_cmd(self.files[0], script) dtrace_script = self.script[:] hooks = [] dtrace_script += hooks # The target is known only when start is called, so doing part of preparation here if attach_by_name: dtrace_script += self.prepare_per_pid() cmd += " -W %s" % os.path.basename(self.args.victim) elif victim: dtrace_script += self.prepare_per_pid() cmd += ' -c "%s"' % ' '.join(victim) else: assert not any('$target' in item for item in dtrace_script) pids = self.args.target if isinstance(self.args.target, list) else [self.args.target] for pid in pids: cmd += " -p %s" % pid print("Attaching PIDs:", pids) items = self.prepare_per_pid() dtrace_script += self.patch_per_pid(pids, items) for pid in pids: if self.args.stacks: dtrace_script.append(OFF_CPU_STACKS.replace('$target', str(pid))) if is_domain_enabled('wakeups'): dtrace_script.append(dtrace_wakeup.replace('$target', str(pid))) for hook in hooks: dtrace_script.append(hook.replace('/*{CONDITIONS}*/', '/pid == %s/' % str(pid))) if self.args.stacks: dtrace_script = self.patch_stacks(pids, dtrace_script) # remove duplicates from the list: dtrace_script = [item for n, item in enumerate(dtrace_script) if item not in dtrace_script[:n]] dtrace_script = '\n'.join(dtrace_script) with open(script, 'w') as file: file.write(dtrace_script) return self.run_parallel(cmd, env)
def start(self): self.stop() cmd = None if self.args.cuts: self.files.append('%s\\etw-%s.etl' % (self.args.output, (self.args.cuts[0] if self.args.cuts else '0'))) self.files.append('%s\\kernel-%s.etl' % (self.args.output, (self.args.cuts[0] if self.args.cuts else '0'))) else: self.files.append('%s\\etw.etl' % self.args.output) self.files.append('%s\\kernel.etl' % self.args.output) if is_older_win7(): logman_pf = os.path.join(tempfile.gettempdir(), 'gpa_logman.pf') count = 0 with open(logman_pf, 'w') as file: if is_domain_enabled('Microsoft-Windows-DxgKrnl'): file.write( '"Microsoft-Windows-DxgKrnl" (Base,GPUScheduler,Profiler,Resource,References,0x4000000000000001)\n' ) count += 1 if is_domain_enabled('Microsoft-Windows-Dwm-Core'): file.write( '"Microsoft-Windows-Dwm-Core" (DetailedFrameInformation)\n' ) count += 1 if is_domain_enabled('Microsoft-Windows-DXGI'): file.write('"Microsoft-Windows-DXGI" (Events)\n') count += 1 if is_domain_enabled('SteamVR'): file.write('"{8C8F13B1-60EB-4B6A-A433-DE86104115AC}"\n') count += 1 if is_domain_enabled('OculusVR'): file.write('"{553787FC-D3D7-4F5E-ACB2-1597C7209B3C}"\n') count += 1 if count: cmd = 'logman start GPA_SEA -ct perf -bs 1024 -nb 120 480' cmd += ' -pf "%s" -o "%s" %s -ets' % ( logman_pf, self.files[0], (('-max %d -f bincirc' % (self.args.ring * 15)) if self.args.ring else '')) else: del self.files[0] else: if self.xperf: cmd = '"%s" -start GPA_SEA -on DX -f "%s" -ClockType PerfCounter -BufferSize 1024 -MinBuffers 120 -MaxBuffers 480' % ( self.xperf, self.files[0]) if self.args.ring: cmd += ' -MaxFile %d -FileMode Circular' % ( self.args.ring * 10 ) # turning seconds into megabytes... if cmd: (out, err) = self.execute(cmd) if err: return None if self.xperf: time_multiplier = 0 kernel_logger = [] # logman query providers "Windows Kernel Trace" complimentary = '' if is_domain_enabled('Kernel::ContextSwitches'): time_multiplier += 10 kernel_logger += ['PROC_THREAD', 'CSWITCH'] if is_domain_enabled('Kernel::Stacks', self.args.stacks): time_multiplier += 20 kernel_logger += ['LOADER', 'PROFILE'] complimentary += ' -stackwalk PROFILE+CSWITCH -SetProfInt 1000000' if is_domain_enabled('Kernel::IO'): time_multiplier += 5 kernel_logger += [ 'FILE_IO', 'FILE_IO_INIT', 'DISK_IO', 'DISK_IO_INIT', 'FILENAME', 'OPTICAL_IO', 'OPTICAL_IO_INIT' ] if is_domain_enabled('Kernel::Network', False): time_multiplier += 5 kernel_logger += ['NETWORKTRACE'] if is_domain_enabled('Kernel::Memory', False): time_multiplier += 5 kernel_logger += [ 'VIRT_ALLOC', 'MEMINFO', 'VAMAP', 'POOL', 'MEMINFO_WS' ] # 'FOOTPRINT', 'MEMORY' if is_domain_enabled('Kernel::PageFaults', False): time_multiplier += 5 kernel_logger += ['ALL_FAULTS', 'HARD_FAULTS'] if kernel_logger: cmd = '"%s" -on %s %s -f "%s" -ClockType PerfCounter -BufferSize 1024 -MinBuffers 120 -MaxBuffers 480' % ( self.xperf, '+'.join(kernel_logger), complimentary, self.files[-1]) if self.args.ring: cmd += ' -MaxFile %d -FileMode Circular' % ( self.args.ring * time_multiplier ) # turning seconds into megabytes... (out, err) = self.execute(cmd) if err or 'Error:' in out: del self.files[-1] return self else: del self.files[-1] else: time_multiplier = 0 kernel_logger = [] # logman query providers "Windows Kernel Trace" if is_domain_enabled('Kernel::ContextSwitches'): time_multiplier += 10 kernel_logger += ['process', 'thread', 'cswitch'] if is_domain_enabled('Kernel::Stacks', self.args.stacks): time_multiplier += 10 kernel_logger += ['img', 'profile'] if is_domain_enabled('Kernel::IO'): time_multiplier += 5 kernel_logger += ['fileio', 'disk'] if is_domain_enabled('Kernel::Network', False): time_multiplier += 5 kernel_logger += ['net'] if is_domain_enabled('Kernel::Memory', False): time_multiplier += 5 kernel_logger += ['virtalloc'] if is_domain_enabled('Kernel::PageFaults', False): time_multiplier += 5 kernel_logger += ['pf', 'hf'] if kernel_logger: cmd = 'logman start "NT Kernel Logger" -p "Windows Kernel Trace" (%s) -ct perf -bs 1024 -nb 120 480' % ','.join( kernel_logger) cmd += ' -o "%s" %s -ets' % (self.files[-1], ( ('-max %d -f bincirc' % (self.args.ring * time_multiplier)) if self.args.ring else '')) (out, err) = self.execute(cmd) if err or 'Error:' in out: del self.files[-1] return self else: del self.files[-1] return self
def run_dtrace(self, attach_by_name=False, victim=None, env=None): self.attach_by_pid = False # spawn dtrace tracers and exit, all means to stop it must be saved to self members: # launch command line with dtrace script and remember pid script = os.path.join(self.args.output, 'script.d') cmd = self.get_cmd(self.files[0], script) dtrace_script = self.script[:] hooks = [] for hook in self.args.hook_kmd: hooks += bracket_hook_kmd(*hook.split(':')) dtrace_script += hooks # The target is known only when start is called, so doing part of preparation here if attach_by_name: dtrace_script += self.prepare_per_pid() cmd += " -W %s" % os.path.basename(self.args.victim) elif victim: dtrace_script += self.prepare_per_pid() cmd += ' -c "%s"' % ' '.join(victim) else: assert not any('$target' in item for item in dtrace_script) if self.args.follow: self.fifo_reader.start() pids = self.args.target if isinstance( self.args.target, list) else [self.args.target] for pid in pids: cmd += " -p %s" % pid print("Attaching PIDs:", pids) items = self.prepare_per_pid() dtrace_script += self.patch_per_pid(pids, items) for pid in pids: if self.args.stacks: dtrace_script.append( OFF_CPU_STACKS.replace('$target', str(pid))) if is_domain_enabled('wakeups'): dtrace_script.append( dtrace_wakeup.replace('$target', str(pid))) for hook in hooks: dtrace_script.append( hook.replace('/*{CONDITIONS}*/', '/pid == %s/' % str(pid))) if self.args.stacks: dtrace_script = self.patch_stacks(pids, dtrace_script) if self.args.args: res = [] tmpl = r'printf("%x\targs\t%x\t%x'\ + (r'\t%d' * self.args.args)\ + r'\n", machtimestamp, pid, tid, '\ + r', '.join(['arg%d' % i for i in range(self.args.args)])\ + r');' for item in dtrace_script: res.append(item.replace('/*{ARGUMENTS}*/', tmpl)) dtrace_script = res # remove duplicates from the list: dtrace_script = [ item for n, item in enumerate(dtrace_script) if item not in dtrace_script[:n] ] dtrace_script = '\n'.join(dtrace_script) with open(script, 'w') as file: file.write(dtrace_script) return self.run_parallel(cmd, env)