Remove sea collectors (#4191)
* Removed redundant collectors * Remove isea filter * Remove the code from IntelSEAPI * Removed shell=True * remove asserts * Refactored command line arguments
This commit is contained in:
@@ -1,226 +0,0 @@
|
||||
# Intel® Single Event API
|
||||
#
|
||||
# This file is provided under the BSD 3-Clause license.
|
||||
# Copyright (c) 2021, Intel Corporation
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
# Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
# Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
# Neither the name of the Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
import os
|
||||
import sys
|
||||
import glob
|
||||
import shutil
|
||||
import traceback
|
||||
import subprocess
|
||||
|
||||
# http://www.brendangregg.com/perf.html
|
||||
# sudo perf probe --funcs
|
||||
|
||||
sys.path.append(os.path.realpath(os.path.join(os.path.dirname(__file__), '..')))
|
||||
import sea
|
||||
|
||||
from sea_runtool import Collector, Progress, format_bytes
|
||||
|
||||
|
||||
def time_sync():
|
||||
sea.ITT('lin').time_sync()
|
||||
|
||||
supported_events = [
|
||||
"binder_locked",
|
||||
"binder_unlock",
|
||||
"binder_lock",
|
||||
"binder_transaction",
|
||||
"binder_transaction_received",
|
||||
"memory_bus_usage",
|
||||
"clock_set_rate",
|
||||
"cpufreq_interactive_up",
|
||||
"cpufreq_interactive_down",
|
||||
"cpufreq_interactive_already",
|
||||
"cpufreq_interactive_notyet",
|
||||
"cpufreq_interactive_setspeed",
|
||||
"cpufreq_interactive_target",
|
||||
"cpufreq_interactive_boost",
|
||||
"cpufreq_interactive_unboost",
|
||||
"f2fs_write_begin",
|
||||
"f2fs_write_end",
|
||||
"f2fs_sync_file_enter",
|
||||
"f2fs_sync_file_exit",
|
||||
"ext4_sync_file_enter",
|
||||
"ext4_sync_file_exit",
|
||||
"ext4_da_write_begin",
|
||||
"ext4_da_write_end",
|
||||
"block_rq_issue",
|
||||
"block_rq_complete",
|
||||
"drm_vblank_event",
|
||||
"exynos_busfreq_target_int",
|
||||
"exynos_busfreq_target_mif",
|
||||
"exynos_page_flip_state",
|
||||
"i915_gem_object_create",
|
||||
"i915_gem_object_bind",
|
||||
"i915_gem_object_unbind",
|
||||
"i915_gem_object_change_domain",
|
||||
"i915_gem_object_pread",
|
||||
"i915_gem_object_pwrite",
|
||||
"i915_gem_object_fault",
|
||||
"i915_gem_object_clflush",
|
||||
"i915_gem_object_destroy",
|
||||
"i915_gem_ring_dispatch",
|
||||
"i915_gem_ring_flush",
|
||||
"i915_gem_request",
|
||||
"i915_gem_request_add",
|
||||
"i915_gem_request_complete",
|
||||
"i915_gem_request_retire",
|
||||
"i915_gem_request_wait_begin",
|
||||
"i915_gem_request_wait_end",
|
||||
"i915_gem_ring_wait_begin",
|
||||
"i915_gem_ring_wait_end",
|
||||
"i915_mvp_read_req",
|
||||
"i915_reg_rw",
|
||||
"i915_flip_request",
|
||||
"i915_flip_complete",
|
||||
"intel_gpu_freq_change",
|
||||
"irq_handler_entry",
|
||||
"irq_handler_exit",
|
||||
"softirq_raise",
|
||||
"softirq_entry",
|
||||
"softirq_exit",
|
||||
"ipi_entry",
|
||||
"ipi_exit",
|
||||
"graph_ent",
|
||||
"graph_ret",
|
||||
"mali_dvfs_event",
|
||||
"mali_dvfs_set_clock",
|
||||
"mali_dvfs_set_voltage",
|
||||
"tracing_mark_write:mali_driver",
|
||||
"mm_vmscan_kswapd_wake",
|
||||
"mm_vmscan_kswapd_sleep",
|
||||
"mm_vmscan_direct_reclaim_begin",
|
||||
"mm_vmscan_direct_reclaim_end",
|
||||
"workqueue_execute_start",
|
||||
"workqueue_execute_end",
|
||||
"power_start",
|
||||
"power_frequency",
|
||||
"cpu_frequency",
|
||||
"cpu_idle",
|
||||
"regulator_enable",
|
||||
"regulator_enable_delay",
|
||||
"regulator_enable_complete",
|
||||
"regulator_disable",
|
||||
"regulator_disable_complete",
|
||||
"regulator_set_voltage",
|
||||
"regulator_set_voltage_complete",
|
||||
"sched_switch",
|
||||
"sched_wakeup",
|
||||
"workqueue_execute_start",
|
||||
"workqueue_execute_end",
|
||||
"workqueue_queue_work",
|
||||
"workqueue_activate_work",
|
||||
]
|
||||
|
||||
|
||||
class FTrace(Collector):
|
||||
def __init__(self, args, remote=False):
|
||||
Collector.__init__(self, args)
|
||||
self.remote = remote
|
||||
self.event_list = []
|
||||
self.file = None
|
||||
self.perf_file = None
|
||||
self.perf_proc = None
|
||||
for event in supported_events:
|
||||
for path in glob.glob('/sys/kernel/debug/tracing/events/*/%s/enable' % event):
|
||||
self.event_list.append(path)
|
||||
|
||||
def echo(self, what, where):
|
||||
self.log("echo %s > %s" % (what, where))
|
||||
try:
|
||||
if self.remote:
|
||||
self.remote.execute('echo %s > %s' % (what, where))
|
||||
else:
|
||||
with open(where, "w") as file:
|
||||
file.write(what)
|
||||
except:
|
||||
self.log("Failed: " + traceback.format_exc())
|
||||
return False
|
||||
return True
|
||||
|
||||
def start(self):
|
||||
if not self.echo("nop", "/sys/kernel/debug/tracing/current_tracer"):
|
||||
self.log("Warning: failed to access ftrace subsystem")
|
||||
return
|
||||
self.file = os.path.join(self.args.output, 'nop-%s.ftrace' % (self.args.cuts[0] if self.args.cuts else '0'))
|
||||
|
||||
self.echo("0", "/sys/kernel/debug/tracing/tracing_on")
|
||||
self.echo("nop", "/sys/kernel/debug/tracing/current_tracer") # google chrome understands this format
|
||||
self.echo("", "/sys/kernel/debug/tracing/set_event") # disabling all events
|
||||
self.echo("", "/sys/kernel/debug/tracing/trace") # cleansing ring buffer (we need it's header only)
|
||||
if self.args.ring:
|
||||
self.echo("%d" % (self.args.ring * 1024), "/sys/kernel/debug/tracing/buffer_size_kb")
|
||||
|
||||
# best is to write sync markers here
|
||||
self.echo("1", "/sys/kernel/debug/tracing/tracing_on") # activate tracing
|
||||
time_sync()
|
||||
self.echo("0", "/sys/kernel/debug/tracing/tracing_on") # deactivate tracing
|
||||
# saving first part of synchronization as it will be wiped out in ring
|
||||
self.copy_from_target("/sys/kernel/debug/tracing/trace", self.file)
|
||||
self.echo("", "/sys/kernel/debug/tracing/trace") # cleansing ring buffer again
|
||||
|
||||
for event in self.event_list: # enabling only supported
|
||||
self.echo("1", event)
|
||||
|
||||
for path in glob.glob('/sys/kernel/debug/dri/*/i915_mvp_enable'): # special case for Intel GPU events
|
||||
self.echo("1", path)
|
||||
self.echo("1", "/sys/kernel/debug/tracing/tracing_on")
|
||||
if self.args.stacks and self.args.target:
|
||||
self.perf_file = os.path.join(self.args.output, 'perf-%s.data' % (self.args.cuts[0] if self.args.cuts else '0'))
|
||||
if os.path.exists(self.perf_file):
|
||||
os.remove(self.perf_file)
|
||||
cmd = 'perf record -a -g -o "%s" --pid=%s' % (self.perf_file, self.args.target)
|
||||
self.log(cmd)
|
||||
self.perf_proc = subprocess.Popen(cmd, shell=True, stdout=self.get_output(), stderr=self.get_output(), preexec_fn=os.setpgrp)
|
||||
|
||||
def copy_from_target(self, what, where):
|
||||
self.log("copy %s > %s" % (what, where))
|
||||
if self.remote:
|
||||
self.remote.copy('%s:%s' % (self.args.ssh, what), where)
|
||||
else:
|
||||
shutil.copy(what, where)
|
||||
|
||||
def stop(self, wait=True):
|
||||
results = []
|
||||
if self.perf_proc:
|
||||
self.perf_proc.wait()
|
||||
if os.path.exists(self.perf_file):
|
||||
results.append(self.perf_file + '.perf')
|
||||
with open(results[-1], 'wb') as file:
|
||||
self.execute('perf script -F comm,tid,pid,time,ip,sym,dso,symoff --show-kernel-path --demangle-kernel --full-source-path -i "%s"' % self.perf_file, stdout=file)
|
||||
os.remove(self.perf_file)
|
||||
|
||||
if not self.file:
|
||||
return results
|
||||
time_sync()
|
||||
self.echo("0", "/sys/kernel/debug/tracing/tracing_on")
|
||||
for path in glob.glob('/sys/kernel/debug/dri/*/i915_mvp_enable'): # special case for Intel GPU events
|
||||
self.echo("0", path)
|
||||
file_name = os.path.join(self.args.output, "tmp.ftrace")
|
||||
self.copy_from_target("/sys/kernel/debug/tracing/trace", file_name)
|
||||
self.log("append %s > %s" % (file_name, self.file))
|
||||
with open(file_name) as file_from, open(self.file, 'a') as file_to:
|
||||
shutil.copyfileobj(file_from, file_to)
|
||||
os.remove(file_name)
|
||||
results.append(self.file)
|
||||
self.execute('chmod -R a+rwX "%s"' % self.args.output)
|
||||
return results
|
||||
|
||||
|
||||
COLLECTOR_DESCRIPTORS = [{
|
||||
'format': 'ftrace',
|
||||
'available': True,
|
||||
'collector': FTrace
|
||||
}]
|
||||
827
thirdparty/itt_collector/runtool/collectors/osx.py
vendored
827
thirdparty/itt_collector/runtool/collectors/osx.py
vendored
@@ -1,827 +0,0 @@
|
||||
# Intel® Single Event API
|
||||
#
|
||||
# This file is provided under the BSD 3-Clause license.
|
||||
# Copyright (c) 2021, Intel Corporation
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
# Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
# Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
# Neither the name of the Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
from __future__ import print_function
|
||||
import re
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from datetime import datetime, timedelta
|
||||
import shutil
|
||||
import subprocess
|
||||
import threading
|
||||
sys.path.append(os.path.realpath(os.path.join(os.path.dirname(__file__), '..')))
|
||||
from sea_runtool import Collector, is_domain_enabled, message, get_original_env
|
||||
|
||||
"""
|
||||
sudo dtrace -l | perl -pe 's/^.*?\S+\s+(\S+?)([0-9]|\s).*/\1/' | sort | uniq > /tmp/dtrace_providers.txt
|
||||
sudo dtrace -l > /tmp/dtrace_list.txt
|
||||
dtrace -n 'fbt:::entry { @[probefunc] = count(); }' -c 'ping host'
|
||||
http://www.brendangregg.com/DTrace/DTrace-cheatsheet.pdf
|
||||
|
||||
https://docs.oracle.com/cd/E19253-01/817-6223/chp-variables-5/index.html
|
||||
TODO: printf("%s: called from %a\n", probefunc, caller);
|
||||
objc_runtime$target::: { ustack(); } /*objc_exception_throw*/
|
||||
pid$target::objc_msgSend:entry
|
||||
sudo dtrace -qn 'fbt::*vent13k*:entry/arg3/{printf("%d\n",arg2)}' # keylogger
|
||||
"""
|
||||
|
||||
DSCRIPT_HEADER = r"""
|
||||
#pragma D option nolibs
|
||||
#define GREEDY_ON ++self->greedy_enabled
|
||||
#define GREEDY_OFF self->greedy_enabled = (self->greedy_enabled > 0) ? (self->greedy_enabled - 1) : self->greedy_enabled
|
||||
|
||||
BEGIN
|
||||
{
|
||||
self->greedy_enabled = 0;
|
||||
}
|
||||
|
||||
|
||||
"""
|
||||
|
||||
dtrace_context_switch = r"""
|
||||
|
||||
/*
|
||||
off-cpu
|
||||
|
||||
Probe that fires when the current CPU is about to end execution of a thread.
|
||||
The curcpu variable indicates the current CPU.
|
||||
The curlwpsinfo variable indicates the thread that is ending execution.
|
||||
The curpsinfo variable describes the process containing the current thread.
|
||||
The lwpsinfo_t structure of the thread that the current CPU will next execute is pointed to by args[0].
|
||||
The psinfo_t of the process containing the next thread is pointed to by args[1].
|
||||
*/
|
||||
|
||||
sched:::off-cpu
|
||||
{
|
||||
printf(
|
||||
"%x\toff\t%x\t%x\t%x\t%s\t%x\t%x\t%s\n", machtimestamp, curcpu->cpu_id,
|
||||
curlwpsinfo->pr_lwpid, curlwpsinfo->pr_pri, curpsinfo->pr_fname,
|
||||
args[0]->pr_lwpid, args[0]->pr_pri, args[1]->pr_fname
|
||||
);
|
||||
}
|
||||
"""
|
||||
|
||||
OFF_CPU_STACKS = r"""
|
||||
sched:::off-cpu
|
||||
/pid == $target/
|
||||
{
|
||||
printf("%x\tkstack\t%x\t%x:", machtimestamp, pid, tid);
|
||||
stack();
|
||||
printf("\n%x\tustack\t%x\t%x:", machtimestamp, pid, tid);
|
||||
ustack();
|
||||
/*
|
||||
printf("\n%x\tjstack\t%x\t%x:", machtimestamp, pid, tid);
|
||||
jstack(); //TODO: enable better support for jstack-s
|
||||
*/
|
||||
printf("\n");
|
||||
}
|
||||
"""
|
||||
|
||||
dtrace_wakeup = r"""
|
||||
sched:::wakeup
|
||||
/curpsinfo->pr_pid == $target || args[1]->pr_pid == $target/
|
||||
{
|
||||
printf("%x\twkp\t%x\t%x\t%s\t%x\t%s\t%x\t%x\t%x\t%x\n", machtimestamp,
|
||||
curpsinfo->pr_pid, curlwpsinfo->pr_lwpid,
|
||||
execname, cpu,
|
||||
stringof(args[1]->pr_fname),
|
||||
args[1]->pr_pid, args[0]->pr_lwpid,
|
||||
args[0]->pr_stype, args[0]->pr_wchan
|
||||
);
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
|
||||
osxaskpass = r"""#!/bin/bash
|
||||
osascript -e 'Tell application "System Events" to display dialog "Password:" default answer "" with hidden answer with title "DTrace requires root priveledges"' -e 'text returned of result' 2>/dev/null
|
||||
"""
|
||||
|
||||
|
||||
pid_dtrace_hooks = [r"""
|
||||
pid$target::*dtSEAHookScope*:entry /*{UMD_STACKS}*/
|
||||
{
|
||||
printf(
|
||||
"%x\te\t%x\t%x\t%s\t%s\n", machtimestamp, pid, tid, copyinstr(arg1), copyinstr(arg2)
|
||||
);
|
||||
}
|
||||
""", r"""
|
||||
pid$target::*dtSEAHookEndScope*:entry
|
||||
{
|
||||
printf(
|
||||
"%x\tr\t%x\t%x\t%s\t%s\n", machtimestamp, pid, tid, copyinstr(arg0), copyinstr(arg1)
|
||||
);
|
||||
}
|
||||
""", r"""
|
||||
/*
|
||||
pid$target::*dtSEAHookArgStr*:entry
|
||||
{
|
||||
printf(
|
||||
"%x\targ\t%x\t%x\t%s\t%s\n",
|
||||
machtimestamp, pid, tid, copyinstr(arg0), copyinstr(arg1)
|
||||
);
|
||||
}
|
||||
|
||||
pid$target::*dtSEAHookArgInt*:entry
|
||||
{
|
||||
printf(
|
||||
"%x\targ\t%x\t%x\t%s\t%d\n",
|
||||
machtimestamp, pid, tid, copyinstr(arg0), arg1
|
||||
);
|
||||
}
|
||||
*/
|
||||
|
||||
"""
|
||||
|
||||
]
|
||||
|
||||
pid_dtrace_hooks += [ # XXX
|
||||
r"""
|
||||
objc$target:::entry
|
||||
/*{CONDITIONS}*/
|
||||
{
|
||||
printf(
|
||||
"%x\te\t%x\t%x\tobjc\t%s%s\n", machtimestamp, pid, tid, probemod, probefunc
|
||||
);
|
||||
/*{ARGUMENTS}*/
|
||||
}
|
||||
""", r"""
|
||||
objc$target:::return
|
||||
/*{CONDITIONS}*/
|
||||
{
|
||||
printf(
|
||||
"%x\tr\t%x\t%x\tobjc\t%s%s\n", machtimestamp, pid, tid, probemod, probefunc
|
||||
);
|
||||
}
|
||||
"""
|
||||
] if 0 else []
|
||||
|
||||
|
||||
FOLLOW_CHILD = r"""
|
||||
//https://www.synack.com/2015/11/17/monitoring-process-creation-via-the-kernel-part-i/
|
||||
proc:::exec-success /$target == curpsinfo->pr_ppid/{
|
||||
printf("%x\tfollowchild\t%x\t%x\t%s\t%x\t%x\t%s\n", machtimestamp, pid, tid, probename, curpsinfo->pr_ppid, curpsinfo->pr_pid, curpsinfo->pr_psargs);
|
||||
system("printf \"%d\n\" >> /*{FOLLOW_CHILD}*/", curpsinfo->pr_pid);
|
||||
}
|
||||
|
||||
proc:::exec /$target == curpsinfo->pr_ppid/{
|
||||
printf("%x\tfollowchild\t%x\t%x\t%s\t%x\t%x\t%s\n", machtimestamp, pid, tid, probename, curpsinfo->pr_ppid, curpsinfo->pr_pid, curpsinfo->pr_psargs);
|
||||
system("printf \"%d\n\" >> /*{FOLLOW_CHILD}*/", curpsinfo->pr_pid);
|
||||
}
|
||||
|
||||
syscall::exec*:return /$target == curpsinfo->pr_ppid/
|
||||
{
|
||||
printf(
|
||||
"%x\tfollowchild\t%x\t%x\t%s\t%s\t%s\n", machtimestamp, pid, tid, probemod, probefunc, probename
|
||||
);
|
||||
}
|
||||
|
||||
syscall::fork:return /$target == curpsinfo->pr_ppid/
|
||||
{
|
||||
printf(
|
||||
"%x\tfollowchild\t%x\t%x\t%s\t%s\t%s\n", machtimestamp, pid, tid, probemod, probefunc, probename
|
||||
);
|
||||
system("printf \"%d\n\" >> /*{FOLLOW_CHILD}*/", curpsinfo->pr_pid);
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
BRACKET_FUNC = r"""
|
||||
pid$target:/*{M:F}*/:entry /*{UMD_STACKS}*/
|
||||
/*{CONDITIONS}*/
|
||||
{
|
||||
printf(
|
||||
"%x\te\t%x\t%x\t%s\t%s\n", machtimestamp, pid, tid, probemod, probefunc
|
||||
);
|
||||
/*{ARGUMENTS}*/
|
||||
GREEDY_ON;
|
||||
}
|
||||
""", r"""
|
||||
pid$target:/*{M:F}*/:return
|
||||
/*{CONDITIONS}*/
|
||||
{
|
||||
GREEDY_OFF;
|
||||
|
||||
printf(
|
||||
"%x\tr\t%x\t%x\t%s\t%s\n", machtimestamp, pid, tid, probemod, probefunc
|
||||
);
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
def bracket_hook(module='', function=''):
|
||||
res = []
|
||||
for item in BRACKET_FUNC:
|
||||
res.append(item.replace('/*{M:F}*/', '%s:%s' % (module, function)))
|
||||
return res
|
||||
|
||||
|
||||
for mask in ['JavaScriptCore', '*GL*:*gl*', '*GL*:*GL*', 'Metal', '*MTLDriver', '*GLDriver']: # ,'mdtest', 'libigdmd.dylib'
|
||||
pid_dtrace_hooks += bracket_hook(*mask.split(':'))
|
||||
|
||||
|
||||
# TODO: add opencl_api & opencl_cpu providers
|
||||
|
||||
IO_HOOKS = r"""
|
||||
|
||||
fsinfo:::open,fsinfo:::close
|
||||
{
|
||||
printf(
|
||||
"%x\tio\t%x\t%x\t%s\t%s\n", machtimestamp, pid, tid, probename, stringof(args[0]->fi_pathname)
|
||||
);
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
|
||||
OPEN_CL = r"""
|
||||
opencl_api$target:::, opencl_cpu$target:::
|
||||
{
|
||||
printf(
|
||||
"%x\tocl\t%x\t%x\t%s\t%s\t%s\n", machtimestamp, pid, tid, probemod, probefunc, probename
|
||||
);
|
||||
}
|
||||
"""
|
||||
|
||||
# FIXME: extract Interrupt handling and do conditional
|
||||
fbt_dtrace_hooks = [r"""
|
||||
|
||||
//void kernel_debug_enter(uint32_t coreid, uint32_t debugid, uint64_t timestamp, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t arg4, uintptr_t threadid);
|
||||
fbt::kernel_debug_enter:entry
|
||||
{
|
||||
printf(
|
||||
"%x\tkd\t%x\t%x\t%s\t%x\t%x\t%x\t%x\t%x\t%x\t%x\t%x\n", machtimestamp, pid, tid, probefunc, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7
|
||||
);
|
||||
}
|
||||
|
||||
""", r"""
|
||||
|
||||
fbt::*debugid*enabled*:entry
|
||||
{
|
||||
printf(
|
||||
"%x\tkd\t%x\t%x\t%s\t%x\t%x\t%x\t%x\t%x\t%x\n", machtimestamp, pid, tid, probefunc, arg0, arg1, arg2, arg3, arg4, arg5
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
""" if not "DISABLED XXX" else '', r"""
|
||||
fbt::*dtSEAHookScope*:entry /*{KMD_STACKS}*/
|
||||
{
|
||||
printf(
|
||||
"%x\te\t%x\t%x\t%s_%s\t%s\t%d\n",
|
||||
machtimestamp, pid, tid, stringof(probemod), stringof(arg1), stringof(arg2), arg0
|
||||
);
|
||||
}
|
||||
""", r"""
|
||||
fbt::*dtSEAHookEndScope*:entry
|
||||
{
|
||||
printf(
|
||||
"%x\tr\t%x\t%x\t%s_%s\t%s\n",
|
||||
machtimestamp, pid, tid, stringof(probemod), stringof(arg0), stringof(arg1)
|
||||
);
|
||||
}
|
||||
|
||||
fbt::*dtSEAHookArgStr*:entry
|
||||
{
|
||||
printf(
|
||||
"%x\targ\t%x\t%x\t%s\t%s\n",
|
||||
machtimestamp, pid, tid, stringof(arg0), stringof(arg1)
|
||||
);
|
||||
}
|
||||
|
||||
fbt::*dtSEAHookArgInt*:entry
|
||||
{
|
||||
printf(
|
||||
"%x\targ\t%x\t%x\t%s\t%d\n",
|
||||
machtimestamp, pid, tid, stringof(arg0), arg1
|
||||
);
|
||||
}
|
||||
|
||||
fbt::*dtSEAHookArgBlobStart*:entry
|
||||
{
|
||||
printf(
|
||||
"%x\tbs\t%x\t%x\t%x\t%s\t%d\n",
|
||||
machtimestamp, pid, tid, arg1, stringof(arg1), arg0
|
||||
);
|
||||
trace(stringof(arg0));
|
||||
tracemem(arg0, 10);
|
||||
}
|
||||
|
||||
fbt::*dtSEAHookArgBlob1024*:entry
|
||||
{
|
||||
printf(
|
||||
"%x\tblb\t%x\t%x\n",
|
||||
machtimestamp, pid, tid
|
||||
);
|
||||
tracemem(arg0, 1024);
|
||||
}
|
||||
|
||||
fbt::*dtSEAHookArgBlobEnd*:entry
|
||||
{
|
||||
printf(
|
||||
"%x\tbe\t%x\t%x\n",
|
||||
machtimestamp, pid, tid
|
||||
);
|
||||
}
|
||||
|
||||
""" if not "DISABLED XXX" else '', r"""
|
||||
|
||||
/*
|
||||
Interrupt handling.
|
||||
The list of interrupts was obtained by running 'dtrace -l | grep handleInterrupt'
|
||||
*/
|
||||
fbt:com.apple.driver.AppleAPIC:_ZN28AppleAPICInterruptController15handleInterruptEPvP9IOServicei:entry,
|
||||
fbt:com.apple.iokit.IOPCIFamily:_ZN8AppleVTD15handleInterruptEP22IOInterruptEventSourcei:entry,
|
||||
fbt:com.apple.iokit.IOPCIFamily:_ZN32IOPCIMessagedInterruptController15handleInterruptEPvP9IOServicei:entry,
|
||||
fbt:com.apple.driver.AppleSMBusController:_ZN23AppleSMBusControllerMCP15handleInterruptEP22IOInterruptEventSourcei:entry,
|
||||
fbt:com.apple.driver.AppleThunderboltNHI:_ZN19AppleThunderboltNHI15handleInterruptEv:entry
|
||||
{
|
||||
printf("%x\tie\t%x\t%x\t%s\t%x\t%s\t%s\n", machtimestamp,
|
||||
curpsinfo->pr_pid, curlwpsinfo->pr_lwpid,
|
||||
execname, cpu, probemod, probefunc
|
||||
);
|
||||
}
|
||||
|
||||
fbt:com.apple.driver.AppleAPIC:_ZN28AppleAPICInterruptController15handleInterruptEPvP9IOServicei:return,
|
||||
fbt:com.apple.iokit.IOPCIFamily:_ZN8AppleVTD15handleInterruptEP22IOInterruptEventSourcei:return,
|
||||
fbt:com.apple.iokit.IOPCIFamily:_ZN32IOPCIMessagedInterruptController15handleInterruptEPvP9IOServicei:return,
|
||||
fbt:com.apple.driver.AppleSMBusController:_ZN23AppleSMBusControllerMCP15handleInterruptEP22IOInterruptEventSourcei:return,
|
||||
fbt:com.apple.driver.AppleThunderboltNHI:_ZN19AppleThunderboltNHI15handleInterruptEv:return
|
||||
{
|
||||
printf("%x\tir\t%x\t%x\t%s\t%x\t%s\t%s\n", machtimestamp,
|
||||
curpsinfo->pr_pid, curlwpsinfo->pr_lwpid,
|
||||
execname, cpu, probemod, probefunc
|
||||
);
|
||||
}
|
||||
|
||||
"""]
|
||||
|
||||
|
||||
STACKS = {
|
||||
|
||||
'UMD': r"""
|
||||
{
|
||||
printf("%x\tustack\t%x\t%x:", machtimestamp, pid, tid);
|
||||
ustack();
|
||||
printf("\n");
|
||||
}
|
||||
""",
|
||||
|
||||
'KMD': r"""
|
||||
{
|
||||
printf("%x\tkstack\t%x\t%x:", machtimestamp, pid, tid);
|
||||
stack();
|
||||
printf("\n");
|
||||
}
|
||||
"""
|
||||
}
|
||||
|
||||
|
||||
def mach_absolute_time(static={}):
|
||||
if not static:
|
||||
import ctypes
|
||||
libc = ctypes.CDLL('libc.dylib', use_errno=True)
|
||||
static['mach_absolute_time'] = libc.mach_absolute_time
|
||||
static['mach_absolute_time'].restype = ctypes.c_uint64
|
||||
return static['mach_absolute_time']()
|
||||
|
||||
|
||||
class FifoReader(threading.Thread):
|
||||
def __init__(self, collector, path):
|
||||
threading.Thread.__init__(self)
|
||||
self.collector = collector
|
||||
self.pipe = path
|
||||
if os.path.exists(self.pipe):
|
||||
os.remove(self.pipe)
|
||||
os.mkfifo(self.pipe)
|
||||
self.file = os.open(self.pipe, os.O_RDWR)
|
||||
|
||||
def run(self):
|
||||
print('Started reading', self.pipe)
|
||||
while self.file:
|
||||
chunks = os.read(self.file, 1024).strip()
|
||||
for chunk in chunks.split('\n'):
|
||||
if chunk != 'close':
|
||||
self.collector.attach(int(chunk))
|
||||
print('Read:', chunk)
|
||||
print('Stopped reading', self.pipe)
|
||||
|
||||
def stop(self):
|
||||
os.write(self.file, 'close\n')
|
||||
os.close(self.file)
|
||||
self.file = None
|
||||
os.unlink(self.pipe)
|
||||
|
||||
|
||||
class DTraceCollector(Collector):
|
||||
class Subcollector:
|
||||
@staticmethod
|
||||
def get_hooks(args):
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def collect(collector, on):
|
||||
pass
|
||||
|
||||
def __init__(self, args):
|
||||
Collector.__init__(self, args)
|
||||
|
||||
self.processes = {}
|
||||
|
||||
self.files = []
|
||||
self.subcollectors = set()
|
||||
|
||||
self.attached = set()
|
||||
|
||||
if 'SUDO_ASKPASS' not in os.environ:
|
||||
get_original_env()['SUDO_ASKPASS'] = self.create_ask_pass()
|
||||
assert 'DYLD_INSERT_LIBRARIES' not in os.environ
|
||||
|
||||
self.sudo_execute('pkill dtrace')
|
||||
self.script = None
|
||||
self.prepare()
|
||||
self.times = []
|
||||
self.attach_by_pid = True
|
||||
|
||||
@staticmethod
|
||||
def create_ask_pass():
|
||||
path = '/tmp/osxaskpass.sh'
|
||||
if os.path.exists(path):
|
||||
return path
|
||||
with open(path, 'w') as file:
|
||||
file.write(osxaskpass)
|
||||
os.chmod(path, 0o700)
|
||||
return path
|
||||
|
||||
@staticmethod
|
||||
def gen_options(options):
|
||||
return '\n'.join('#pragma D option %s=%s' % (key, str(value)) for key, value in options) + '\n'
|
||||
|
||||
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 sudo_execute(self, cmd):
|
||||
return self.execute('sudo -E -A ' + cmd)
|
||||
|
||||
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 patch_per_pid(self, pids, items):
|
||||
result = []
|
||||
for item in items:
|
||||
if '$target:' in item:
|
||||
for pid in pids:
|
||||
result.append(item.replace('$target:', '%s:' % str(pid)))
|
||||
else:
|
||||
result.append(item)
|
||||
|
||||
return result
|
||||
|
||||
def get_cmd(self, out, script, pids=[]):
|
||||
# -C Run the C preprocessor
|
||||
# -Z Permit probe descriptions that match zero probes
|
||||
# -w Permit destructive actions in D programs
|
||||
cmd = 'sudo -E -A dtrace -C -Z -w -o "%s" -s "%s"' % (out, script) # FIXME: sudo_execute
|
||||
if self.args.verbose != 'info':
|
||||
cmd += ' -q' # Set quiet mode
|
||||
else:
|
||||
# -S Show D compiler intermediate code
|
||||
# -v Print an interface stability report
|
||||
# -V Report the highest D programming interface version
|
||||
# -e Exit after compiling
|
||||
# -l List probes instead of enabling them
|
||||
cmd += ' '
|
||||
|
||||
for pid in pids:
|
||||
cmd += " -p %s" % pid
|
||||
return cmd
|
||||
|
||||
def launch_victim(self, victim, env):
|
||||
proc = self.run_dtrace(victim=victim, env=env)
|
||||
if not proc:
|
||||
return None
|
||||
|
||||
class PopenWrapper:
|
||||
def __init__(self, parent, victim):
|
||||
self.parent = parent
|
||||
cmd = 'pgrep -n "%s"' % os.path.basename(victim[0])
|
||||
while True:
|
||||
data, err = parent.execute(cmd)
|
||||
if data:
|
||||
self.pid = int(data)
|
||||
break
|
||||
time.sleep(1)
|
||||
|
||||
def send_signal(self, sig):
|
||||
self.parent.sudo_execute('kill -%d %d' % (sig, self.pid))
|
||||
|
||||
def wait(self, sec=10):
|
||||
proc['proc'].wait()
|
||||
""" XXX
|
||||
for x in range(0, sec):
|
||||
proc['proc'].poll()
|
||||
time.sleep(1)
|
||||
"""
|
||||
return proc['proc'].returncode
|
||||
|
||||
def communicate(self):
|
||||
self.wait()
|
||||
return None, None
|
||||
|
||||
return PopenWrapper(self, victim)
|
||||
|
||||
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): # FIXME: see man dtrace -W option for proper attach
|
||||
self.times.append(datetime.now())
|
||||
if self.attach_by_pid:
|
||||
self.run_dtrace()
|
||||
|
||||
def run_parallel(self, cmd, env=None):
|
||||
self.log(cmd)
|
||||
proc = self.processes.setdefault(cmd, {})
|
||||
proc['proc'] = subprocess.Popen(cmd, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env = env or os.environ)
|
||||
proc['pid'] = proc['proc'].pid
|
||||
self.log("%s -> pid: %d" % (cmd, proc['proc'].pid))
|
||||
return proc
|
||||
|
||||
@staticmethod
|
||||
def patch_stacks(pids, items):
|
||||
reg_exp = re.compile(r"""(.*)\/\*\{(.*)_STACKS\}\*\/.*""", re.IGNORECASE | re.DOTALL)
|
||||
result = []
|
||||
for item in items:
|
||||
result.append(item)
|
||||
if '_STACKS}*/' in item:
|
||||
lines = item.strip().split('\n')
|
||||
assert lines[-1].strip().endswith('}')
|
||||
res = reg_exp.search(lines[0])
|
||||
what, where = res.groups()
|
||||
condition = '/$target == pid/' if pids else ''
|
||||
code = '\n%s %s %s' % (what, condition, STACKS[where])
|
||||
if pids:
|
||||
for pid in pids:
|
||||
result.append(code.replace('$target', str(pid)))
|
||||
else:
|
||||
result.append(code)
|
||||
return result
|
||||
|
||||
def attach(self, pid):
|
||||
if pid in self.attached:
|
||||
return
|
||||
pids = [pid] + list(self.get_pid_children(pid))
|
||||
self.attached |= set(pids)
|
||||
items = self.prepare_per_pid()
|
||||
dtrace_script = [DSCRIPT_HEADER] + [item for item in self.script if '$target' in item]
|
||||
dtrace_script += self.patch_per_pid(pids, items)
|
||||
script = os.path.join(self.args.output, 'script_%d.d' % pid)
|
||||
dtrace_script = '\n'.join(dtrace_script)
|
||||
self.files.append(os.path.join(self.args.output, 'data-%d-%s.dtrace' % (pid, self.args.cuts[0] if self.args.cuts else '0')))
|
||||
cmd = self.get_cmd(self.files[-1], script, pids)
|
||||
with open(script, 'w') as file:
|
||||
file.write(dtrace_script)
|
||||
self.run_parallel(cmd)
|
||||
|
||||
@staticmethod
|
||||
def get_pid_children(parent):
|
||||
(out, err) = DTraceCollector.execute('ps -o pid,ppid -ax', log=False)
|
||||
if err:
|
||||
print(err)
|
||||
return
|
||||
for line in out.split('\n'):
|
||||
if not line:
|
||||
continue
|
||||
parts = line.split()
|
||||
if len(parts) != 2:
|
||||
continue
|
||||
pid, ppid = line.split()
|
||||
if str(parent) == ppid:
|
||||
yield int(pid)
|
||||
|
||||
@staticmethod
|
||||
def locate(what, statics={}):
|
||||
try:
|
||||
if not statics:
|
||||
res = subprocess.check_output(['locate', '-S']).decode("utf-8")
|
||||
statics['locate'] = 'WARNING' not in res
|
||||
if not statics['locate']:
|
||||
print(res)
|
||||
if statics['locate']:
|
||||
return subprocess.check_output(['locate', what]).decode("utf-8").split('\n')
|
||||
except Exception:
|
||||
pass
|
||||
return []
|
||||
|
||||
def collect_codes(self):
|
||||
items = self.locate("*.codes")
|
||||
files = [item.strip() for item in items if not item.startswith('/Volumes')]
|
||||
|
||||
items = self.locate("kdebug.h")
|
||||
files += [item.strip() for item in items if not item.startswith('/Volumes')]
|
||||
|
||||
filtered = {}
|
||||
for file in files:
|
||||
if not file or not os.path.exists(file):
|
||||
continue
|
||||
name = os.path.basename(file)
|
||||
size = os.path.getsize(file)
|
||||
if size and (name not in filtered or os.path.getsize(filtered[name]) < size):
|
||||
filtered[name] = file
|
||||
for file in filtered.values():
|
||||
shutil.copy(file, self.args.output)
|
||||
|
||||
items = self.locate("IntelGPUSignposts.plist")
|
||||
plists = []
|
||||
for line in items:
|
||||
line = line.strip()
|
||||
if line:
|
||||
|
||||
plists.append((os.path.getmtime(line), line)) # finding newest
|
||||
if plists:
|
||||
plist = sorted(plists)[-1][1]
|
||||
shutil.copy(plist, self.args.output)
|
||||
|
||||
def collect_system_info(self):
|
||||
with open(os.path.join(self.args.output, 'sysinfo.txt'), 'w') as file:
|
||||
(probes, err) = self.execute('sysctl -a', stdout=file)
|
||||
|
||||
def stop(self, wait=True):
|
||||
for name, data in self.processes.items():
|
||||
print('Stopping:', name)
|
||||
pids = [data['pid']] + list(self.get_pid_children(data['pid']))
|
||||
for pid in pids:
|
||||
self.sudo_execute("kill -2 %d" % pid)
|
||||
for pid in pids:
|
||||
try:
|
||||
os.waitpid(pid, 0)
|
||||
except:
|
||||
pass
|
||||
|
||||
if not data['proc']:
|
||||
continue
|
||||
out, err = data['proc'].communicate()
|
||||
message(None, "\n\n -= Target %s output =- {\n" % name)
|
||||
if out:
|
||||
self.log("Trace %s out:\n%s" % (name, out.decode()))
|
||||
message(None, out.strip())
|
||||
message(None, "-" * 50)
|
||||
if err:
|
||||
self.log("Trace %s err:\n%s" % (name, err.decode()), True)
|
||||
message(None, err.strip())
|
||||
message(None, "}\n\n")
|
||||
|
||||
if data['proc'].returncode != 0:
|
||||
message('error', '%s(%d) has exited with error code %d check logs for details' % (name, data['pid'], data['proc'].returncode))
|
||||
|
||||
for subcollector in self.subcollectors:
|
||||
print('Stopping:', subcollector)
|
||||
subcollector.collect(self, False)
|
||||
|
||||
self.times.append(datetime.now())
|
||||
|
||||
sys_log = os.path.join(self.args.output, 'sys_log.json')
|
||||
with open(sys_log, 'w') as file:
|
||||
cmd = 'log show --source --style json --debug --signpost' # --last 1m --start, --end 'YYYY-MM-DD HH:MM:SS'
|
||||
cmd += self.times[1].strftime(" --end '%Y-%m-%d %H:%M:%S'")
|
||||
if self.args.ring:
|
||||
cmd += (self.times[1] - timedelta(seconds=self.args.ring)).strftime(" --start '%Y-%m-%d %H:%M:%S'")
|
||||
else:
|
||||
cmd += self.times[0].strftime(" --start '%Y-%m-%d %H:%M:%S'")
|
||||
self.execute(cmd, stdout=file) # FIXME: get time of collection or ring size
|
||||
|
||||
self.collect_system_info()
|
||||
self.collect_codes()
|
||||
|
||||
res = self.files + [sys_log]
|
||||
|
||||
return res
|
||||
|
||||
@classmethod
|
||||
def available(cls):
|
||||
if 'darwin' not in sys.platform:
|
||||
return False
|
||||
(out, err) = cls.execute('csrutil status')
|
||||
if 'disabled' not in out:
|
||||
print('Please do: "csrutil disable" from Recovery OS terminal to be able using dtrace...')
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
COLLECTOR_DESCRIPTORS = [{
|
||||
'format': 'dtrace',
|
||||
'available': DTraceCollector.available(),
|
||||
'collector': DTraceCollector
|
||||
}]
|
||||
|
||||
if __name__ == "__main__":
|
||||
print(mach_absolute_time())
|
||||
DTraceCollector.check_graphics_firmware('com.apple.driver.AppleIntelSKLGraphics')
|
||||
|
||||
313
thirdparty/itt_collector/runtool/collectors/win.py
vendored
313
thirdparty/itt_collector/runtool/collectors/win.py
vendored
@@ -1,313 +0,0 @@
|
||||
# Intel® Single Event API
|
||||
#
|
||||
# This file is provided under the BSD 3-Clause license.
|
||||
# Copyright (c) 2021, Intel Corporation
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
# Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
# Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
# Neither the name of the Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
from __future__ import print_function
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from datetime import datetime
|
||||
import shutil
|
||||
import tempfile
|
||||
import platform
|
||||
import traceback
|
||||
import subprocess
|
||||
sys.path.append(os.path.realpath(os.path.join(os.path.dirname(__file__), '..')))
|
||||
from sea_runtool import Collector, is_domain_enabled
|
||||
import sea
|
||||
|
||||
|
||||
def async_exec(cmd, title=None, env=None):
|
||||
cmd = 'start "%s" /MIN /LOW %s' % (title if title else cmd, cmd)
|
||||
subprocess.Popen(cmd, shell=True, stdin=None, stdout=None, stderr=None, creationflags=0x00000008, env=env) # DETACHED_PROCESS
|
||||
|
||||
|
||||
class WPRCollector(Collector):
|
||||
def __init__(self, args):
|
||||
Collector.__init__(self, args)
|
||||
self.wpr = self.detect()
|
||||
self.started = False
|
||||
if self.args.cuts:
|
||||
self.file = os.path.join(args.output, "wpa-%s.etl" % (self.args.cuts[0] if self.args.cuts else '0'))
|
||||
else:
|
||||
self.file = os.path.join(args.output, "wpa.etl")
|
||||
|
||||
@classmethod
|
||||
def detect(cls, statics={}):
|
||||
if 'res' in statics:
|
||||
return statics['res']
|
||||
wprs = cls.detect_instances('wpr')
|
||||
res = []
|
||||
for wpr in wprs:
|
||||
proc = subprocess.Popen('"%s" /?' % wpr, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
(out, err) = proc.communicate()
|
||||
out = out.decode()
|
||||
if err:
|
||||
return None
|
||||
for line in out.split('\n'):
|
||||
pos = line.find('Version')
|
||||
if -1 != pos:
|
||||
version = line[pos + len('Version '):].strip()
|
||||
if int(version.split('.')[0]) >= 10:
|
||||
res.append((wpr, version.split()[0]))
|
||||
break
|
||||
if not res:
|
||||
return None
|
||||
statics['res'] = sorted(res, key=lambda __ver: [int(item) for item in __ver[1].split('.')], reverse=True)[0][0]
|
||||
return statics['res']
|
||||
|
||||
@staticmethod
|
||||
def get_options():
|
||||
wpr = WPRCollector.detect()
|
||||
if not wpr:
|
||||
return
|
||||
proc = subprocess.Popen('"%s" -profiles' % wpr, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
(out, err) = proc.communicate()
|
||||
if err:
|
||||
return
|
||||
for line in out.split('\n'):
|
||||
if not line.startswith('\t'):
|
||||
continue
|
||||
parts = line.strip().split()
|
||||
yield parts[0], parts[0] in ['DiskIO', 'FileIO', 'GPU', 'GeneralProfile', 'Handle', 'Heap', 'Network', 'Power', 'Video', 'VirtualAllocation']
|
||||
|
||||
def start(self):
|
||||
if not self.wpr:
|
||||
print("Failed to start without WPR...")
|
||||
return
|
||||
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 cancel(self):
|
||||
return self.execute('"%s" -cancel' % self.wpr)
|
||||
|
||||
@classmethod
|
||||
def is_recording(cls, statics={}):
|
||||
if not statics:
|
||||
statics['wpr'] = cls.detect()
|
||||
statics['xperf'] = os.path.normpath(os.path.join(os.path.dirname(statics['wpr']), 'xperf.exe'))
|
||||
if os.path.exists(statics['xperf']):
|
||||
(out, err) = cls.execute('"%s" -Loggers | find "WPR_"' % statics['xperf'])
|
||||
return any('WPR_' in line for line in out.split('\n'))
|
||||
else:
|
||||
(out, err) = cls.execute('"%s" -status' % statics['wpr'])
|
||||
return err or not any('WPR is not recording' in line for line in out.split('\n'))
|
||||
|
||||
def stop(self, wait=True):
|
||||
if not self.started:
|
||||
return []
|
||||
|
||||
self.log("Stop wait=%s" % str(wait))
|
||||
if not wait:
|
||||
cmd = 'start "WPR stop" /MIN /LOW "%s" "%s" wpa "%s" "%s"' % (sys.executable, os.path.realpath(__file__), self.file, self.args.output)
|
||||
self.log(cmd)
|
||||
subprocess.Popen(cmd, shell=False, stdin=None, stdout=None, stderr=None, creationflags=0x00000008, env=sea.prepare_environ(self.args)) # DETACHED_PROCESS
|
||||
while self.is_recording():
|
||||
self.log("is_recording")
|
||||
time.sleep(1)
|
||||
return [self.file]
|
||||
else:
|
||||
env = sea.prepare_environ(self.args)
|
||||
self.stop_wpr(self.wpr, self.file, self.args.output, env)
|
||||
return [self.file]
|
||||
|
||||
@classmethod
|
||||
def stop_wpr(cls, wpr, file, output, env=None):
|
||||
(out, err) = cls.execute('"%s" -stop "%s"' % (wpr, file), env=env)
|
||||
if err:
|
||||
return []
|
||||
assert(file in out)
|
||||
|
||||
@classmethod
|
||||
def launch(cls, args):
|
||||
cls.stop_wpr(cls.detect(), args[0], args[1])
|
||||
|
||||
|
||||
class ETWTrace(Collector):
|
||||
def __init__(self, args):
|
||||
Collector.__init__(self, args)
|
||||
wpr = WPRCollector.detect()
|
||||
self.xperf = os.path.normpath(os.path.join(os.path.dirname(wpr), 'xperf')) if wpr else None
|
||||
if not self.xperf or not os.path.exists(self.xperf):
|
||||
variants = self.detect_instances('xperf')
|
||||
if variants:
|
||||
self.xperf = variants[0] # TODO: select by higher version
|
||||
else:
|
||||
self.xperf = None
|
||||
self.files = []
|
||||
self.start()
|
||||
|
||||
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)
|
||||
|
||||
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 is_domain_enabled('Intel_Graphics_D3D10'):
|
||||
file.write('"{AD367E62-97EF-4B20-8235-E8AB49DB0C23}"\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]
|
||||
|
||||
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]
|
||||
|
||||
self.files.append('%s/etw_profilers.logman' % self.args.output)
|
||||
cmd = 'cmd /c logman query providers ^> "%s"' % self.files[-1]
|
||||
async_exec(cmd, 'Collecting ETW providers')
|
||||
|
||||
return self
|
||||
|
||||
def stop(self, wait=True): # TODO: stop without waits
|
||||
if self.xperf:
|
||||
proc = subprocess.Popen('xperf -stop', shell=False)
|
||||
if wait:
|
||||
proc.wait()
|
||||
else:
|
||||
proc = subprocess.Popen('logman stop "NT Kernel Logger" -ets', shell=False)
|
||||
if wait:
|
||||
proc.wait()
|
||||
proc = subprocess.Popen('logman stop "GPA_SEA" -ets', shell=False)
|
||||
if wait:
|
||||
proc.wait()
|
||||
|
||||
return self.files
|
||||
|
||||
COLLECTOR_DESCRIPTORS = [
|
||||
{
|
||||
'available': sys.platform == 'win32' and WPRCollector.detect(),
|
||||
'collector': WPRCollector,
|
||||
'format': 'wpa'
|
||||
},
|
||||
{
|
||||
'available': sys.platform == 'win32',
|
||||
'collector': ETWTrace,
|
||||
'format': 'etw'
|
||||
}
|
||||
]
|
||||
|
||||
if __name__ == "__main__":
|
||||
with open(os.path.join(tempfile.gettempdir(), datetime.now().strftime('sea_%H_%M_%S__%d_%m_%Y.log')), 'a') as log:
|
||||
log.write(str(sys.argv) + '\n')
|
||||
try:
|
||||
name = sys.argv[1]
|
||||
for desc in COLLECTOR_DESCRIPTORS:
|
||||
if desc['format'] == name:
|
||||
cls = desc['collector']
|
||||
cls.set_output(log)
|
||||
cls.launch(sys.argv[2:])
|
||||
break
|
||||
except:
|
||||
log.write(traceback.format_exc())
|
||||
2
thirdparty/itt_collector/runtool/sea.py
vendored
2
thirdparty/itt_collector/runtool/sea.py
vendored
@@ -45,7 +45,6 @@ class Task:
|
||||
return self
|
||||
|
||||
def arg(self, name, value):
|
||||
assert self.id
|
||||
try:
|
||||
value = float(value)
|
||||
self.itt.lib.itt_metadata_add(self.itt.domain, self.id, self.itt.get_string_id(name), value)
|
||||
@@ -54,7 +53,6 @@ class Task:
|
||||
return self
|
||||
|
||||
def blob(self, name, pointer, size):
|
||||
assert self.id
|
||||
self.itt.lib.itt_metadata_add_blob(self.itt.domain, self.id, self.itt.get_string_id(name), pointer, size)
|
||||
return self
|
||||
|
||||
|
||||
243
thirdparty/itt_collector/runtool/sea_runtool.py
vendored
243
thirdparty/itt_collector/runtool/sea_runtool.py
vendored
@@ -83,22 +83,6 @@ class DummyWith(): # for conditional with statements
|
||||
return False
|
||||
|
||||
|
||||
class Profiler():
|
||||
def __enter__(self):
|
||||
try:
|
||||
import cProfile as profile
|
||||
except:
|
||||
import profile
|
||||
self.profiler = profile.Profile()
|
||||
self.profiler.enable()
|
||||
return self
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
self.profiler.disable()
|
||||
self.profiler.print_stats('time')
|
||||
return False
|
||||
|
||||
|
||||
def get_extensions(name, multiple=False):
|
||||
big_name = (name + 's').upper()
|
||||
this_module = sys.modules[__name__]
|
||||
@@ -128,10 +112,6 @@ def get_exporters():
|
||||
return get_extensions('exporter')
|
||||
|
||||
|
||||
def get_collectors():
|
||||
return get_extensions('collector')
|
||||
|
||||
|
||||
verbose_choices = ['fatal', 'error', 'warning', 'info']
|
||||
|
||||
|
||||
@@ -145,7 +125,6 @@ def parse_args(args):
|
||||
format_choices.append("xcode")
|
||||
elif sys.platform == 'linux':
|
||||
format_choices.append("kernelshark")
|
||||
parser.add_argument("-f", "--format", choices=format_choices, nargs='*', default=[], help='One or many output formats.')
|
||||
parser.add_argument("-o", "--output", help='Output folder pattern -<pid> will be added to it')
|
||||
parser.add_argument("-b", "--bindir", help='If you run script not from its location')
|
||||
parser.add_argument("-i", "--input", help='Provide input folder for transformation (<the one you passed to -o>-<pid>)')
|
||||
@@ -155,9 +134,7 @@ def parse_args(args):
|
||||
parser.add_argument("-c", "--cuts", nargs='*', help='Set "all" to merge all cuts in one trace')
|
||||
parser.add_argument("-r", "--ring", type=int, const='5', default=None, action='store', nargs='?', help='Makes trace to cycle inside ring buffer of given length in seconds')
|
||||
parser.add_argument("--target", help='Pid of target')
|
||||
parser.add_argument("--stacks", action="store_true", help='Collect stacks')
|
||||
parser.add_argument("--profile", action="store_true", help='Internal: profile runtool execution')
|
||||
parser.add_argument("--collector", choices=list(get_collectors().keys()) + ['default'])
|
||||
parser.add_argument("-s", "--app_status", action="store_true", help='Script returns the application status')
|
||||
|
||||
separators = ['!', '?', '%']
|
||||
separator = None
|
||||
@@ -174,14 +151,6 @@ def parse_args(args):
|
||||
sys.exit(-1)
|
||||
victim = args[separator + 1:]
|
||||
victim[-1] = victim[-1].strip() # removal of trailing '\r' - when launched from .sh
|
||||
if not parsed_args.output:
|
||||
if sys.platform != 'win32':
|
||||
parsed_args.output = '/tmp/isea_collection'
|
||||
print('Collection will be written into:' , parsed_args.output)
|
||||
else:
|
||||
parser.print_help()
|
||||
print("Error: No output (-o) given in launch mode")
|
||||
sys.exit(-1)
|
||||
handle_args(parsed_args)
|
||||
return parsed_args, victim
|
||||
else: # nothing to launch, transformation mode
|
||||
@@ -189,20 +158,6 @@ def parse_args(args):
|
||||
args[-1] = args[-1].strip() # removal of trailing '\r' - when launched from .sh
|
||||
parsed_args = parser.parse_args(args)
|
||||
handle_args(parsed_args)
|
||||
if not parsed_args.input:
|
||||
if sys.platform != 'win32':
|
||||
parsed_args.input = '/tmp/isea_collection'
|
||||
if os.path.exists(parsed_args.input):
|
||||
print('Collection will be read from:', parsed_args.input)
|
||||
else:
|
||||
parser.print_help()
|
||||
sys.exit(-1)
|
||||
else:
|
||||
print("--input argument is required for transformation mode.")
|
||||
parser.print_help()
|
||||
sys.exit(-1)
|
||||
if not parsed_args.format:
|
||||
parsed_args.format = ['gt']
|
||||
setattr(parsed_args, 'user_input', parsed_args.input)
|
||||
if not parsed_args.output:
|
||||
parsed_args.output = parsed_args.input
|
||||
@@ -239,7 +194,8 @@ def verbose_level(level=None, statics={}):
|
||||
|
||||
|
||||
def message(level, txt, statics={}):
|
||||
assert isinstance(statics, dict)
|
||||
if not isinstance(statics, dict):
|
||||
return False
|
||||
if level and verbose_level(level) > verbose_level(): # see default in "parse_args"
|
||||
return False
|
||||
|
||||
@@ -265,14 +221,13 @@ def main():
|
||||
(args, victim) = parse_args(sys.argv[1:]) # skipping the script name
|
||||
reset_global('arguments', args)
|
||||
|
||||
if victim:
|
||||
if args.output:
|
||||
ensure_dir(args.output, clean=True)
|
||||
launch(args, victim)
|
||||
else:
|
||||
ext = os.path.splitext(args.input)[1] if not os.path.isdir(args.input) else None
|
||||
transform_all(args)
|
||||
ret_code = launch(args, victim)
|
||||
Collector.log('Started with arguments: %s' % str(sys.argv))
|
||||
save_domains()
|
||||
if ret_code != 0 and not args.app_status:
|
||||
ret_code = 0
|
||||
return ret_code
|
||||
|
||||
|
||||
def os_lib_ext():
|
||||
@@ -282,32 +237,11 @@ def os_lib_ext():
|
||||
return '.dylib'
|
||||
elif 'linux' in sys.platform:
|
||||
return '.so'
|
||||
assert (not "Unsupported platform")
|
||||
|
||||
|
||||
def get_pids(victim, tracer):
|
||||
assert len(victim) == 1 # one wildcard is supported yet
|
||||
assert sys.platform != 'win32' # no Windows support yet
|
||||
out, err = tracer.execute('ps -o pid,ppid,command -ax', log=False)
|
||||
if err:
|
||||
tracer.log(err)
|
||||
return []
|
||||
|
||||
parsed = {}
|
||||
for line in out.split('\n'):
|
||||
if not line:
|
||||
continue
|
||||
parts = line.split()
|
||||
if len(parts) < 3:
|
||||
continue
|
||||
cmd = ' '.join(parts[2:])
|
||||
if fnmatch.fnmatch(cmd.lower(), victim[0].lower()) and __file__ not in cmd: # get matching cmd
|
||||
parsed[parts[0]] = cmd
|
||||
print("Matching cmd:\t", parts[0], cmd)
|
||||
return set(parsed.keys())
|
||||
raise "Unsupported platform"
|
||||
|
||||
|
||||
def launch(args, victim):
|
||||
ret_code = 0
|
||||
sea.prepare_environ(args)
|
||||
sea_itf = sea.ITT('tools')
|
||||
|
||||
@@ -340,8 +274,7 @@ def launch(args, victim):
|
||||
env["INTEL_JIT_PROFILER64"] = paths['64']
|
||||
|
||||
env["INTEL_SEA_FEATURES"] = os.environ['INTEL_SEA_FEATURES'] if 'INTEL_SEA_FEATURES' in os.environ else ""
|
||||
env["INTEL_SEA_FEATURES"] += (" " + str(args.format)) if args.format else ""
|
||||
env["INTEL_SEA_FEATURES"] += " stacks" if args.stacks else ""
|
||||
env["INTEL_SEA_FEATURES"] += (" stat")
|
||||
|
||||
if args.verbose == 'info':
|
||||
env['INTEL_SEA_VERBOSE'] = '1'
|
||||
@@ -365,27 +298,12 @@ def launch(args, victim):
|
||||
environ = global_storage('sea_env')
|
||||
for key, val in env.items():
|
||||
if key in environ and val != environ[key]:
|
||||
assert key in ['LD_PRELOAD', 'DYLD_INSERT_LIBRARIES']
|
||||
if key not in ['LD_PRELOAD', 'DYLD_INSERT_LIBRARIES']:
|
||||
raise key + ' wasn\'t found!'
|
||||
environ[key] += ':' + val
|
||||
else:
|
||||
environ[key] = val
|
||||
|
||||
if 'kernelshark' in args.format:
|
||||
victim = 'trace-cmd record -e IntelSEAPI/* ' + victim
|
||||
|
||||
tracer = None
|
||||
|
||||
if args.collector:
|
||||
tracer = get_collectors()[args.collector]
|
||||
elif not tracer: # using default collector per system
|
||||
if 'linux' in sys.platform:
|
||||
tracer = get_collectors()['ftrace']
|
||||
elif 'win32' == sys.platform:
|
||||
tracer = get_collectors()['etw']
|
||||
elif 'darwin' in sys.platform:
|
||||
tracer = get_collectors()['dtrace']
|
||||
run_suspended = False
|
||||
|
||||
if args.dir:
|
||||
full_victim = os.path.join(args.dir, victim[0])
|
||||
if os.path.exists(full_victim):
|
||||
@@ -393,28 +311,15 @@ def launch(args, victim):
|
||||
|
||||
setattr(args, 'victim', victim[0])
|
||||
|
||||
tracer = tracer(args) if tracer else None # turning class into instance
|
||||
if '!' in sys.argv[1:]:
|
||||
assert tracer
|
||||
proc = subprocess.Popen(victim, env=environ, shell=False, cwd=args.dir, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
|
||||
if hasattr(tracer, 'launch_victim'):
|
||||
victim[0] = victim[0].replace(' ', r'\ ')
|
||||
proc = tracer.launch_victim(victim, env=environ)
|
||||
else:
|
||||
if run_suspended: # might consider using preload of SEA lib and do the suspend there. Or allow tracers to run it.
|
||||
suspended = '(cd "%s"; kill -STOP $$; exec %s )' % (args.dir or '.', ' '.join(victim))
|
||||
proc = subprocess.Popen(suspended, env=environ, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
else:
|
||||
proc = subprocess.Popen(victim, env=environ, shell=False, cwd=args.dir, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
|
||||
if sys.platform != 'win32' and not run_suspended: # FIXME: implement suspended start on Windows!
|
||||
if not args.ring:
|
||||
proc.send_signal(signal.SIGSTOP)
|
||||
if sys.platform != 'win32': # FIXME: implement suspended start on Windows!
|
||||
if not args.ring:
|
||||
proc.send_signal(signal.SIGSTOP)
|
||||
|
||||
args.target = proc.pid
|
||||
|
||||
tracer.start()
|
||||
|
||||
if sys.platform != 'win32': # collector start may be long, so we freeze victim during this time
|
||||
print("PID:", proc.pid)
|
||||
if not args.ring:
|
||||
@@ -429,6 +334,7 @@ def launch(args, victim):
|
||||
except KeyboardInterrupt:
|
||||
print("Stopping all...")
|
||||
proc.send_signal(signal.SIGABRT)
|
||||
ret_code = -1
|
||||
out, err = proc.communicate()
|
||||
if out or err:
|
||||
print("\n\n -= Target output =- {\n")
|
||||
@@ -436,42 +342,16 @@ def launch(args, victim):
|
||||
print("\n", "-" * 50, "\n")
|
||||
print(err.decode().strip())
|
||||
print("\n}\n\n")
|
||||
elif '?' in sys.argv[1:]:
|
||||
print("Attach to:", victim)
|
||||
pids = get_pids(victim, tracer)
|
||||
if not pids:
|
||||
print("Error: nothing found...")
|
||||
return
|
||||
if tracer:
|
||||
args.target = list(pids)
|
||||
tracer.start()
|
||||
|
||||
print("Waiting for CTRL+C...")
|
||||
global_storage('collection')['time']['before'] = time.time()
|
||||
|
||||
def is_running(pid):
|
||||
try:
|
||||
os.kill(int(pid), 0)
|
||||
return True
|
||||
except OSError:
|
||||
return False
|
||||
|
||||
try:
|
||||
while any(is_running(pid) for pid in pids):
|
||||
time.sleep(0.5)
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
ret_code = proc.returncode
|
||||
else:
|
||||
message('error', 'unsupported separator')
|
||||
return -1
|
||||
|
||||
global_storage('collection')['time']['after'] = time.time()
|
||||
print("Stopping collectors...")
|
||||
if tracer:
|
||||
args.trace = tracer.stop()
|
||||
|
||||
if not args.output:
|
||||
return []
|
||||
return ret_code
|
||||
|
||||
args.input = args.output
|
||||
|
||||
@@ -486,8 +366,9 @@ def launch(args, victim):
|
||||
allowed_pids = [args.target]
|
||||
global_storage('collection').setdefault('targets', allowed_pids)
|
||||
|
||||
if args.format:
|
||||
transform_all(args)
|
||||
transform_all(args)
|
||||
|
||||
return ret_code
|
||||
|
||||
|
||||
def subst_env_vars(path):
|
||||
@@ -500,7 +381,6 @@ PermanentCache = os.path.join(UserProfile, '.isea_cache.dict')
|
||||
|
||||
def ensure_dir(path, clean, statics={}):
|
||||
if path in statics:
|
||||
assert(statics[path] or not clean)
|
||||
return
|
||||
statics[path] = clean
|
||||
if os.path.exists(path):
|
||||
@@ -837,7 +717,6 @@ class TaskCombinerCommon:
|
||||
else:
|
||||
message('warning', 'Negative length task: %s => %s' % (str(item), str(data)))
|
||||
else:
|
||||
assert (self.tree["ring_buffer"] or self.tree['cuts'])
|
||||
if 'str' in data: # nothing to show without name
|
||||
self.no_begin.append(data)
|
||||
elif fn == "frame_begin":
|
||||
@@ -848,8 +727,6 @@ class TaskCombinerCommon:
|
||||
if index is not None:
|
||||
item = frames.pop(index)
|
||||
self.complete_task("frame", item, data)
|
||||
else:
|
||||
assert (self.tree["ring_buffer"] or self.tree['cuts'])
|
||||
elif fn == "metadata_add":
|
||||
if 'id' in data:
|
||||
task = get_task(data['id'])
|
||||
@@ -896,7 +773,7 @@ class TaskCombinerCommon:
|
||||
get_task(data['parent']) or find_task(data['parent'])
|
||||
)
|
||||
else:
|
||||
assert (not "Unsupported type:" + fn)
|
||||
raise "Unsupported type:" + fn
|
||||
|
||||
def compress_counter(self, cache, data):
|
||||
values = cache['values']
|
||||
@@ -962,8 +839,7 @@ class Callbacks(TaskCombinerCommon):
|
||||
self.tid_map.update(tid_map)
|
||||
self.allowed_pids |= set(tid_map.values())
|
||||
|
||||
for fmt in args.format:
|
||||
self.callbacks.append(get_exporters()[fmt](args, tree))
|
||||
self.callbacks.append(get_exporters()['stat'](args, tree))
|
||||
|
||||
if args.target:
|
||||
if isinstance(args.target, list):
|
||||
@@ -1033,10 +909,7 @@ class Callbacks(TaskCombinerCommon):
|
||||
if not type:
|
||||
return False
|
||||
|
||||
if not is_domain_enabled(data['domain']):
|
||||
return False
|
||||
|
||||
if data.get('internal_name', None) and not is_domain_enabled('%s.%s' % (data['domain'], data['internal_name'])):
|
||||
if data.get('internal_name', None):
|
||||
return False
|
||||
|
||||
self.__call__(type, data)
|
||||
@@ -1050,9 +923,6 @@ class Callbacks(TaskCombinerCommon):
|
||||
if self.handle_special(type, begin, end): # returns True if event is consumed and doesn't require processing
|
||||
return True
|
||||
|
||||
if not is_domain_enabled(begin['domain']):
|
||||
return False
|
||||
|
||||
if end:
|
||||
# copy here as handler can change the data for own good - this shall not affect other handlers
|
||||
[callback.complete_task(type, begin.copy(), end.copy() if end else end) for callback in self.callbacks]
|
||||
@@ -1278,7 +1148,8 @@ class Callbacks(TaskCombinerCommon):
|
||||
return args
|
||||
|
||||
def end(self, time_stamp):
|
||||
assert self.data # expected to be initialized in self.begin call
|
||||
if not self.data:
|
||||
return
|
||||
if time_stamp:
|
||||
end_data = self.data.copy()
|
||||
end_data.update({'time': time_stamp, 'type': self.event_type + 1})
|
||||
@@ -1674,7 +1545,8 @@ class FileWrapper:
|
||||
return None
|
||||
call["time"] = tuple[0]
|
||||
|
||||
assert (tuple[1] < len(TaskTypes)) # sanity check
|
||||
if tuple[1] >= len(TaskTypes):
|
||||
return None
|
||||
call["type"] = tuple[1]
|
||||
|
||||
flags = tuple[2]
|
||||
@@ -1843,14 +1715,14 @@ def resolve_cmd(args, path, load_addr, ptr, cache={}):
|
||||
elif 'linux' in sys.platform:
|
||||
cmd = 'addr2line %s -e "%s" -i -p -f -C' % (to_hex(ptr), path)
|
||||
else:
|
||||
assert (not "Unsupported platform!")
|
||||
raise "Unsupported platform!"
|
||||
|
||||
env = dict(os.environ)
|
||||
if "INTEL_SEA_VERBOSE" in env:
|
||||
del env["INTEL_SEA_VERBOSE"]
|
||||
|
||||
try:
|
||||
proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
|
||||
proc = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
|
||||
(symbol, err) = proc.communicate()
|
||||
except IOError:
|
||||
err = traceback.format_exc()
|
||||
@@ -2044,45 +1916,6 @@ def get_name(begin):
|
||||
return "<unknown>"
|
||||
|
||||
|
||||
def get_filter_path():
|
||||
filter = os.environ.get('INTEL_SEA_FILTER')
|
||||
if filter:
|
||||
filter = subst_env_vars(filter)
|
||||
else:
|
||||
filter = os.path.join(UserProfile, '.isea_domains.fltr')
|
||||
return filter
|
||||
|
||||
|
||||
def is_domain_enabled(domain, default=True):
|
||||
domains = global_storage('sea.is_domain_enabled', {})
|
||||
if not domains:
|
||||
filter = get_filter_path()
|
||||
try:
|
||||
with open(filter) as file:
|
||||
for line in file:
|
||||
enabled = not line.startswith('#')
|
||||
name = line.strip(' #\n\r')
|
||||
domains[name] = enabled
|
||||
if not enabled:
|
||||
message('warning', 'The domain "%s" is disabled in %s' % (name, filter))
|
||||
except IOError:
|
||||
pass
|
||||
if domain not in domains:
|
||||
domains[domain] = default
|
||||
return domains[domain]
|
||||
|
||||
|
||||
def save_domains():
|
||||
domains = global_storage('sea.is_domain_enabled', {})
|
||||
|
||||
filter = get_filter_path()
|
||||
print("Saving domains:", filter)
|
||||
|
||||
with open(filter, 'w') as file:
|
||||
for key, value in domains.items():
|
||||
file.write('%s%s\n' % ('#' if not value else '', key))
|
||||
|
||||
|
||||
class GraphCombiner(TaskCombiner):
|
||||
def __init__(self, args, tree):
|
||||
TaskCombiner.__init__(self, args, tree)
|
||||
@@ -2240,7 +2073,8 @@ class Collector:
|
||||
|
||||
@classmethod
|
||||
def log(cls, msg, stack=False):
|
||||
assert type(stack) is bool # to avoid "log" function being misused as "print" where comma allows more args
|
||||
if not type(stack) is bool:
|
||||
stack = False
|
||||
msg = msg.strip()
|
||||
cut = '\n' + '-' * 100 + '\n'
|
||||
msg = cut + msg + '\n\n' + (''.join(traceback.format_stack()[:-1]) if stack else '') + cut
|
||||
@@ -2260,7 +2094,7 @@ class Collector:
|
||||
if sys.version[0] == '3':
|
||||
kwargs['encoding'] = 'utf8'
|
||||
|
||||
(out, err) = subprocess.Popen(cmd, shell=True, **kwargs).communicate()
|
||||
(out, err) = subprocess.Popen(cmd, shell=False, **kwargs).communicate()
|
||||
if log:
|
||||
cls.log("\ncmd:\t%s:\nout:\t%s\nerr:\t%s\ntime: %s" % (cmd, str(out).strip(), str(err).strip(), str(timedelta(seconds=(time.time() - start_time)))), stack=True if err else False)
|
||||
if verbose_level() == verbose_level('info'):
|
||||
@@ -2281,9 +2115,9 @@ class Collector:
|
||||
info = subprocess.STARTUPINFO()
|
||||
info.dwFlags = subprocess.STARTF_USESHOWWINDOW
|
||||
info.wShowWindow = 0 # SW_HIDE
|
||||
subprocess.Popen(cmd, shell=True, startupinfo=info, stdin=None, stdout=None, stderr=None, creationflags=(CREATE_NO_WINDOW | CREATE_NEW_PROCESS_GROUP), **kwargs)
|
||||
subprocess.Popen(cmd, shell=False, startupinfo=info, stdin=None, stdout=None, stderr=None, creationflags=(CREATE_NO_WINDOW | CREATE_NEW_PROCESS_GROUP), **kwargs)
|
||||
else:
|
||||
subprocess.Popen(cmd, shell=True, stdin=None, stdout=None, stderr=None, **kwargs)
|
||||
subprocess.Popen(cmd, shell=False, stdin=None, stdout=None, stderr=None, **kwargs)
|
||||
|
||||
def start(self):
|
||||
raise NotImplementedError('Collector.start is not implemented!')
|
||||
@@ -2308,6 +2142,7 @@ class Collector:
|
||||
|
||||
if __name__ == "__main__":
|
||||
start_time = time.time()
|
||||
main()
|
||||
ret_code = main()
|
||||
elapsed = time.time() - start_time
|
||||
print("Time Elapsed:", str(timedelta(seconds=elapsed)).split('.')[0])
|
||||
exit(ret_code)
|
||||
|
||||
@@ -19,21 +19,11 @@ set(TARGET_NAME sea_itt_lib)
|
||||
set(CMAKE_DEBUG_POSTFIX "")
|
||||
set(CMAKE_RELEASE_POSTFIX "")
|
||||
|
||||
if (WIN32)
|
||||
add_custom_command(OUTPUT "${PROJECT_BINARY_DIR}/IntelSEAPI.rc" "${PROJECT_BINARY_DIR}/IntelSEAPI.h"
|
||||
PRE_BUILD
|
||||
COMMAND mc -um ${CMAKE_CURRENT_SOURCE_DIR}/IntelSEAPI.man -h ${PROJECT_BINARY_DIR} -r ${PROJECT_BINARY_DIR}
|
||||
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/IntelSEAPI.man
|
||||
MAIN_DEPENDENCY IttNotifyStdSrc.h
|
||||
COMMENT "Generating ${PROJECT_BINARY_DIR}/IntelSEAPI.rc ${PROJECT_BINARY_DIR}/IntelSEAPI.h"
|
||||
)
|
||||
endif()
|
||||
file(GLOB_RECURSE SOURCES "*.cpp" "*.h")
|
||||
|
||||
add_library(${TARGET_NAME} SHARED ${SOURCES})
|
||||
|
||||
target_include_directories(${TARGET_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${PROJECT_BINARY_DIR})
|
||||
target_include_directories(${TARGET_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES OUTPUT_NAME IntelSEAPI)
|
||||
|
||||
target_link_libraries(${TARGET_NAME} PRIVATE ittnotify)
|
||||
|
||||
BIN
thirdparty/itt_collector/sea_itt_lib/IntelSEAPI.man
vendored
BIN
thirdparty/itt_collector/sea_itt_lib/IntelSEAPI.man
vendored
Binary file not shown.
@@ -1333,7 +1333,7 @@ __itt_event UNICODE_AGNOSTIC(event_create)(const char *name, int namelen) {
|
||||
ITT_FUNCTION_STAT();
|
||||
__itt_domain* pEvents = get_events_domain();
|
||||
__itt_string_handle* pStr = UNICODE_AGNOSTIC(string_handle_create)(name);
|
||||
return intptr_t(pStr) - intptr_t(pEvents);
|
||||
return __itt_event(intptr_t(pStr) - intptr_t(pEvents));
|
||||
}
|
||||
|
||||
int event_start(__itt_event event) {
|
||||
@@ -1682,34 +1682,6 @@ void SetRing(uint64_t nanoseconds) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
typedef ULONG(__stdcall* TEtwNotificationRegister)(
|
||||
LPCGUID Guid,
|
||||
ULONG Type,
|
||||
PVOID Callback,
|
||||
PVOID Context,
|
||||
REGHANDLE* RegHandle);
|
||||
|
||||
TEtwNotificationRegister g_fnOrigEtwNotificationRegister = nullptr;
|
||||
|
||||
ULONG _stdcall MyEtwNotificationRegister(
|
||||
LPCGUID Guid,
|
||||
ULONG Type,
|
||||
PVOID Callback,
|
||||
PVOID Context,
|
||||
REGHANDLE* RegHandle) {
|
||||
WCHAR strGuid[100] = {};
|
||||
StringFromGUID2(*Guid, strGuid, sizeof(strGuid) - 1);
|
||||
char str[100] = {};
|
||||
sprintf_s(str, "%ls", strGuid);
|
||||
VerbosePrint("\nEventRegister, provider: %s\n", str);
|
||||
static __itt_string_handle* pKey = UNICODE_AGNOSTIC(string_handle_create)("EventRegister::Provider");
|
||||
WriteMeta(GetRegularFields(), pKey, str);
|
||||
return g_fnOrigEtwNotificationRegister(Guid, Type, Callback, Context, RegHandle);
|
||||
}
|
||||
#endif
|
||||
|
||||
void InitSEA() {
|
||||
for (size_t i = 0; (i < MAX_HANDLERS) && g_handlers[i]; ++i) {
|
||||
g_handlers[i]->Init(g_rfMainThread);
|
||||
|
||||
@@ -102,7 +102,6 @@ struct ___itt_counter : public __itt_counter_info_t{};
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "windows.h"
|
||||
#include "IntelSEAPI.h"
|
||||
#elif defined(__linux__)
|
||||
#ifndef USE_PROBES
|
||||
__thread FILE* stdsrc_trace_info_t::pFile = nullptr;
|
||||
@@ -125,11 +124,6 @@ struct ___itt_counter : public __itt_counter_info_t{};
|
||||
__itt_id from; //d3 is not used, so we fit d1 and d2 into 16 bytes
|
||||
GUID to;
|
||||
};
|
||||
|
||||
//http://www.geoffchappell.com/studies/windows/win32/ntdll/api/etw/eventwritefull.htm
|
||||
//http://helpdoc-online.com/Microsoft_Platform_SDK_August_2001_Performance_Monitoring_en/Tracing_Event_Instances.php
|
||||
typedef NTSYSAPI NTSTATUS (NTAPI * FZwTraceEvent)(IN ULONG TraceHandle, IN ULONG Flags, IN ULONG TraceHeaderLength, IN PEVENT_TRACE_HEADER TraceHeader);
|
||||
|
||||
#else
|
||||
#include <cstdio>
|
||||
#define _strdup strdup
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
#ifdef _WIN32
|
||||
#define setenv _putenv
|
||||
#include <windows.h>
|
||||
#include "IntelSEAPI.h"
|
||||
#undef API_VERSION
|
||||
#include <Dbghelp.h>
|
||||
#pragma comment(lib, "dbghelp")
|
||||
@@ -187,9 +186,6 @@ extern "C" {
|
||||
if (!bInitialized) {
|
||||
bInitialized = true;
|
||||
sea::InitSEA();
|
||||
#ifdef _WIN32
|
||||
EventRegisterIntelSEAPI();
|
||||
#endif
|
||||
atexit(AtExit);
|
||||
}
|
||||
}
|
||||
@@ -204,9 +200,6 @@ extern "C" {
|
||||
g_bInitialized = false;
|
||||
|
||||
sea::FinitaLaComedia();
|
||||
#ifdef _WIN32
|
||||
EventUnregisterIntelSEAPI();
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user