Example #1
0
    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
Example #2
0
 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
Example #3
0
    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
Example #4
0
    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
Example #5
0
 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
Example #6
0
    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)
Example #7
0
    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
Example #8
0
    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)