Skip to content
This repository was archived by the owner on Jan 10, 2023. It is now read-only.

Commit 523d6f7

Browse files
committed
Rad Telemetry support
1 parent 15e0dc6 commit 523d6f7

File tree

12 files changed

+409
-50
lines changed

12 files changed

+409
-50
lines changed

CMakeLists.txt

+4
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,10 @@ else()
100100
endif()
101101
endif()
102102

103+
if (EXISTS "${PROJECT_SOURCE_DIR}/RadTelemetry")
104+
MESSAGE("Found: Rad Telemetry")
105+
set (RAD_TELEMETRY_DIR ${PROJECT_SOURCE_DIR}/RadTelemetry)
106+
endif()
103107

104108

105109
if (WIN32)

buildall.py

+13-3
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ def read_registry(path, depth=0xFFFFFFFF, statics={}):
6767
hub = parts[0]
6868
path = '\\'.join(parts[1:])
6969
if not statics:
70-
statics['hubs'] = {'HKLM': _winreg.HKEY_LOCAL_MACHINE}
70+
statics['hubs'] = {'HKLM': _winreg.HKEY_LOCAL_MACHINE, 'HKCL': _winreg.HKEY_CLASSES_ROOT}
7171

7272
def enum_nodes(curpath, level):
7373
if level < 1:
@@ -149,18 +149,28 @@ def GetJDKPath():
149149
return os.path.split(matches[0])[0]
150150

151151

152-
def get_vs_versions():
152+
def get_vs_versions(): # https://www.mztools.com/articles/2008/MZ2008003.aspx
153153
if sys.platform != 'win32':
154154
return []
155+
versions = []
156+
"""
155157
bush = read_registry(r'HKLM\SOFTWARE\Microsoft\VisualStudio', 2)
158+
print(bush)
156159
157-
versions = []
158160
for key in bush:
159161
if '.' not in key:
160162
continue
161163
version = key.split('.')[0]
162164
if int(version) >= 12:
163165
versions.append(version)
166+
"""
167+
hkcl = read_registry(r'HKCL', 1)
168+
for key in hkcl:
169+
if 'VisualStudio.DTE.' in key:
170+
version = key.split('.')[2]
171+
if int(version) >= 12:
172+
versions.append(version)
173+
164174
if not versions:
165175
print("No Visual Studio version found")
166176
return sorted(versions)

runtool/collectors/win.py

+2
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,8 @@ def __init__(self, args):
232232
variants = self.detect_instances('xperf')
233233
if variants:
234234
self.xperf = variants[0] # TODO: select by higher version
235+
else:
236+
self.xperf = None
235237
self.files = []
236238
self.start()
237239

runtool/decoders/MSNT_SystemTrace.py

+125
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import sys
2+
import socket
3+
4+
def resolve_host(ip):
5+
try:
6+
return socket.gethostbyaddr(ip)[0]
7+
except:
8+
return None
9+
10+
def convert_numbers(obj):
11+
if isinstance(obj, dict):
12+
for k, v in obj.iteritems():
13+
obj[k] = convert_numbers(v)
14+
elif isinstance(obj, list):
15+
new = [convert_numbers(v) for v in obj]
16+
del obj[:]
17+
obj.extend(new)
18+
elif hasattr(obj, '__iter__'):
19+
for v in obj:
20+
convert_numbers(v)
21+
elif isinstance(obj, basestring):
22+
if obj.isdigit():
23+
return int(obj)
24+
return obj
25+
26+
27+
class MSNT_SystemTraceDecoder:
28+
def __init__(self, parser, args, callbacks):
29+
self.parser, self.args, self.callbacks = parser, args, callbacks
30+
self.tcpip = {}
31+
32+
@staticmethod
33+
def resolve_host(data, field):
34+
host = resolve_host(data[field])
35+
if host:
36+
data['%s_resolved' % field] = host
37+
38+
def handle_record(self, system, data, info):
39+
if info['EventName'] in ['TcpIp', 'UdpIp']:
40+
if info['Opcode'] in ['Fail']:
41+
return
42+
pid = data['PID']
43+
now = self.parser.convert_time(system['time'])
44+
if info['Opcode'] in ['ConnectIPV4', 'ReconnectIPV4', 'DisconnectIPV4', 'AcceptIPV4', 'TCPCopyIPV4']:
45+
target_thread = self.callbacks.process(pid).thread(0, 'TcpIp/UDP')
46+
self.resolve_host(data, 'daddr')
47+
self.resolve_host(data, 'saddr')
48+
target_thread.object(id(data), info['Opcode']).snapshot(now, args=data)
49+
return
50+
target = data['daddr'] + ':' + data['dport']
51+
source = data['saddr'] + ':' + data['sport']
52+
if info['Opcode'] in ['SendIPV4', 'SendIPV6']: # gather all sends by target
53+
receiver = self.tcpip.setdefault(target, {'packets':[], 'started': None})
54+
receiver['packets'].append({'pid': pid, 'size': data['size'], 'source': source, 'time': system['time'], 'type': info['EventName']})
55+
self.end_receiver(receiver)
56+
self.callbacks.process(pid).thread(0, 'TcpIp/UDP').marker('thread', 'SendIPV4').set(now, args=data)
57+
elif info['Opcode'] in ['RecvIPV4', 'RecvIPV6']: # on each receive take all ready packets for this 'source'
58+
self.callbacks.process(pid).thread(0, 'TcpIp/UDP').marker('thread', 'RecvIPV4').set(now, args=data)
59+
self.on_receive(data['size'], now, pid, source, target)
60+
elif info['EventName'] == 'SystemConfig':
61+
if info['Opcode'] in ['Video', 'NIC', 'PhyDisk', 'LogDisk', 'CPU', 'Platform']:
62+
self.callbacks.add_metadata(info['Opcode'], convert_numbers(data))
63+
elif sys.gettrace():
64+
print 'EventName:', info['EventName'], 'Opcode:', info['Opcode']
65+
66+
def on_receive(self, size, now, pid, source, target):
67+
receiver = self.tcpip.setdefault(source, {'packets': [], 'started': None})
68+
if receiver['packets']:
69+
if 'pid' in receiver:
70+
assert (receiver['pid'] == pid)
71+
else:
72+
receiver['pid'] = pid
73+
start_time = min(packet['time'] for packet in receiver['packets'])
74+
end_time = max(packet['time'] for packet in receiver['packets']) + 1000
75+
sent = [int(packet['size']) for packet in receiver['packets']]
76+
target_thread = self.callbacks.process(pid).thread(0, 'TcpIp/UDP')
77+
packet = receiver['packets'][0] # all packets to this target are considered to be from the same source
78+
target_task = target_thread.task('Recv').begin(now, args={'from': target, 'own': source})
79+
source_thread = self.callbacks.process(packet['pid']).thread(0, 'TcpIp/UDP')
80+
source_task = source_thread.task(packet['type']).begin(
81+
self.parser.convert_time(start_time),
82+
args={'sent': sent, 'to': packet['source'], 'own': target}
83+
)
84+
source_task.relate(target_task)
85+
source_task.end(end_time)
86+
assert (not receiver['started'])
87+
target_task.sizes = []
88+
receiver['started'] = target_task
89+
receiver['packets'] = []
90+
if receiver['started']:
91+
receiver['started'].sizes.append(int(size))
92+
receiver['started'].end_time = now + 1000
93+
94+
@classmethod
95+
def get_providers(cls):
96+
return ['MSNT_SYSTEMTRACE']
97+
98+
@staticmethod
99+
def end_receiver(receiver):
100+
if receiver['started']:
101+
target_task = receiver['started']
102+
target_task.add_args({'received': target_task.sizes})
103+
target_task.end(target_task.end_time)
104+
receiver['started'] = None
105+
106+
def finalize(self):
107+
for target, receiver in self.tcpip.iteritems():
108+
self.end_receiver(receiver)
109+
packets = receiver['packets']
110+
if packets:
111+
if 'pid' in receiver:
112+
pid = receiver['pid']
113+
now = self.parser.convert_time(max(packet['time'] for packet in packets)) + 1000
114+
packet = receiver['packets'][0] # all packets to this target are considered to be from the same source
115+
self.on_receive(0, now, pid, target, packet['source'])
116+
self.end_receiver(receiver)
117+
else:
118+
pass # not sure what to do with these remnants yet...
119+
120+
121+
DECODER_DESCRIPTORS = [{
122+
'format': 'etw',
123+
'available': True,
124+
'decoder': MSNT_SystemTraceDecoder
125+
}]

runtool/exporters/ChromeTracing.py

+2
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,8 @@ def complete_task(self, type, begin, end):
280280
res += [',\n']
281281
end_begin = begin.copy()
282282
end_begin['time'] = end['time'] - 1000
283+
if 'args' in end:
284+
end_begin['args'] = end['args']
283285
res += self.format_task('e', 'frame', end_begin, {})
284286
else:
285287
res = self.format_task(GoogleTrace.Phase[type], type, begin, end)

runtool/importers/osx.py

+23-14
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ def handle_record(self, time, cmd, args):
5858
elif cmd in ['e', 'r']:
5959
pid, tid = args[0:2]
6060
self.task(time, int(pid, 16), int(tid, 16), cmd == 'e', args[2], args[3], args[4:])
61+
elif cmd == 'arg':
62+
pid, tid = args[0:2]
63+
self.arg(time, int(pid, 16), int(tid, 16), args[2], '\t'.join(args[3:]))
6164
else:
6265
print "unsupported cmd:", cmd, args
6366

@@ -82,12 +85,28 @@ def task(self, time, pid, tid, starts, domain, name, args):
8285
self.gpu_frame['catch'][0 if starts else 1] = time
8386
if name == 'CGLFlushDrawable':
8487
return
88+
""" OLD WAY
8589
data = {
8690
'domain': domain, 'type': 0 if starts else 1,
8791
'time': time, 'tid': tid, 'pid': pid, 'str': name,
8892
'args': dict((idx, val) for idx, val in enumerate(args))
8993
}
9094
self.callbacks.on_event('task_begin' if starts else 'task_end', data)
95+
"""
96+
thread = self.callbacks.process(pid).thread(tid)
97+
if starts:
98+
item = thread.task(name, domain) if not args or args[0] == '0' else thread.frame(name, domain)
99+
thread.task_stack.append(item.begin(time))
100+
elif thread.task_stack: # it's fine, circular buffer can eat some begins
101+
task = thread.task_stack.pop()
102+
task.end(time)
103+
104+
def arg(self, time, pid, tid, name, value):
105+
thread = self.callbacks.process(pid).thread(tid)
106+
if thread.task_stack:
107+
thread.task_stack[-1].add_args({name: value})
108+
else:
109+
print "Orphan arg:", name, value
91110

92111
def submit_prepare(self, time, id, pid, tid, args):
93112
if id not in self.prepares:
@@ -117,10 +136,6 @@ def gpu_call(self, time, cmd, pid, tid, args):
117136
'time': time, 'tid': tid, 'pid': pid, 'str': 'PrepareQueueKMD', 'id': int(id, 16),
118137
'args': dict((idx, val) for idx, val in enumerate(args))
119138
}
120-
elif 'SwCtxCreation' == cmd:
121-
pass
122-
elif 'SubmitExecList' == cmd:
123-
pass
124139
elif 'SubmitQueueKMD' == cmd:
125140
id = args[-3] if len(args) == 7 else args[-4]
126141
self.submit_prepare(time, id, pid, tid, args)
@@ -166,8 +181,6 @@ def gpu_call(self, time, cmd, pid, tid, args):
166181
id = args[1]
167182
if id in self.cpu_packets:
168183
self.cpu_packets[id]['name'] = '3DBlt:' + id
169-
elif 'CompleteExecList' == cmd:
170-
pass
171184
elif 'CompleteExecute' == cmd:
172185
id = args[-1]
173186
gpu_task_id = id
@@ -190,13 +203,7 @@ def gpu_call(self, time, cmd, pid, tid, args):
190203
if id == self.gpu_frame['task']:
191204
self.on_gpu_frame(time, end_data['pid'], end_data['tid'])
192205
del self.gpu_packets[gpu_task_id]
193-
elif 'RemoveQueueKMD' == cmd:
194-
pass
195-
elif 'SwCtxDestroy' == cmd:
196-
pass
197-
elif 'WriteStamp' == cmd:
198-
pass
199-
elif 'DidFlip' == cmd:
206+
elif cmd in ['RemoveQueueKMD', 'DidFlip', 'WriteStamp', 'SwCtxDestroy', 'SwCtxCreation', 'CompleteExecList', 'SubmitExecList']:
200207
pass
201208
else:
202209
print "Unhandled gpu_call:", cmd
@@ -231,7 +238,9 @@ def transform_dtrace(args):
231238
for line in file:
232239
count += 1
233240
ends_with_vt = (11 == ord(line[-1])) if len(line) else False
234-
line = line.strip()
241+
#old_line = line
242+
line = line.strip('\r\n')
243+
#print "%d\t%s" % (count, line)
235244
if not line:
236245
if reading_stack:
237246
dtrace.handle_stack(*(reading_stack + [stack]))

runtool/sea_runtool.py

+19-14
Original file line numberDiff line numberDiff line change
@@ -202,14 +202,15 @@ def main():
202202
transform_all(args)
203203
else:
204204
try:
205-
output = get_importers()[ext.lstrip('.')](args)
205+
importer = get_importers()[ext.lstrip('.')]
206206
except KeyError:
207207
print("Error! Format %s is unavailable or unsupported" % ext)
208-
else:
209-
output = join_gt_output(args, output)
210-
replacement = ('/', '\\') if sys.platform == 'win32' else ('\\', '/')
211-
for path in output:
212-
print os.path.abspath(path).replace(*replacement)
208+
return
209+
output = importer(args)
210+
output = join_gt_output(args, output)
211+
replacement = ('/', '\\') if sys.platform == 'win32' else ('\\', '/')
212+
for path in output:
213+
print os.path.abspath(path).replace(*replacement)
213214

214215

215216

@@ -441,7 +442,8 @@ def launch(args, victim):
441442
def transform_all(args):
442443
if not args.trace: # no itt trace
443444
args.trace = []
444-
for ext in ['etl', 'ftrace', 'dtrace', 'perf']:
445+
importers = get_importers()
446+
for ext in importers.iterkeys():
445447
for file in glob(os.path.join(args.input, '*.' + ext)):
446448
if not any(sub in file for sub in ['.etl.', '.dtrace.', 'merged.']):
447449
args.trace.append(file)
@@ -2005,13 +2007,16 @@ def complete_task(self, type, begin, end):
20052007
task['time'].append(end['time'] - begin['time'])
20062008
if '__file__' in begin:
20072009
task['src'] = begin['__file__'] + ":" + begin['__line__']
2008-
tasks = self.domains[begin['domain']]['tasks']
2009-
stack = tasks[tid]['stack'] if tid in tasks else []
2010-
if len(stack):
2011-
parent = stack[-1]
2012-
self.add_relation({'label': 'calls', 'from': self.make_id(parent['domain'], self.get_name_ex(parent)), 'to': self.make_id(begin['domain'], self.get_name_ex(begin))})
2013-
else:
2014-
self.add_relation({'label': 'executes', 'from': self.make_id("threads", str(tid)), 'to': self.make_id(begin['domain'], self.get_name_ex(begin)), 'color': 'gray'})
2010+
if begin['domain'] in self.domains: # assumption that task was managed with ITT way
2011+
tasks = self.domains[begin['domain']]['tasks']
2012+
stack = tasks[tid]['stack'] if tid in tasks else []
2013+
if len(stack):
2014+
parent = stack[-1]
2015+
self.add_relation({'label': 'calls', 'from': self.make_id(parent['domain'], self.get_name_ex(parent)), 'to': self.make_id(begin['domain'], self.get_name_ex(begin))})
2016+
else:
2017+
self.add_relation({'label': 'executes', 'from': self.make_id("threads", str(tid)), 'to': self.make_id(begin['domain'], self.get_name_ex(begin)), 'color': 'gray'})
2018+
else: # FIXME: real complete_tasks formed by modern API are not tracked
2019+
pass
20152020
elif type == 'marker':
20162021
domain['markers'].setdefault(begin['str'], [])
20172022
elif type == 'frame':

sea_itt_lib/CMakeLists.txt

+20
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,26 @@ else()
6666
endif()
6767

6868

69+
if(RAD_TELEMETRY_DIR)
70+
set(INCLUDE_DIRS ${INCLUDE_DIRS} ${RAD_TELEMETRY_DIR}/include)
71+
if(APPLE)
72+
set(LINK_LIBS ${LINK_LIBS} ${RAD_TELEMETRY_DIR}/lib/librad_tm_mac_x64_link.a)
73+
set(LINK_LIBS ${LINK_LIBS} ${RAD_TELEMETRY_DIR}/lib/librad_tm_mac_x86_link.a)
74+
set(EXTRA_SOURCE ${EXTRA_SOURCE} RadTelemetry.cpp)
75+
elseif (WIN32)
76+
set(RAD_LIB_NAME "rad_tm_win")
77+
if (ARCH_64)
78+
set(RAD_LIB_NAME "${RAD_LIB_NAME}64")
79+
else()
80+
set(RAD_LIB_NAME "${RAD_LIB_NAME}32")
81+
endif()
82+
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
83+
set(RAD_LIB_NAME "${RAD_LIB_NAME}_d")
84+
endif()
85+
set(LINK_LIBS ${LINK_LIBS} ${RAD_TELEMETRY_DIR}/lib/${RAD_LIB_NAME}.lib)
86+
set(EXTRA_SOURCE ${EXTRA_SOURCE} RadTelemetry.cpp)
87+
endif()
88+
endif()
6989

7090
if(JDK)
7191
if (${CMAKE_HOST_SYSTEM_NAME} MATCHES "Linux")

sea_itt_lib/ETWHandler.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -32,19 +32,19 @@ class CETW: public IHandler
3232
if (!oTask.pName)
3333
return;
3434
__itt_id id = (bOverlapped || oTask.id.d1 || oTask.id.d2) ? oTask.id : __itt_id{ uint64_t(&oTask), uint64_t(&oTask) };
35-
uint64_t data[3] = { rf.pid, rf.tid, rf.nanoseconds };
35+
uint64_t data[3] = { uint64_t(rf.pid), uint64_t(rf.tid), rf.nanoseconds };
3636
EventWriteTASK_COMPLETE(oTask.pDomain->nameA, oTask.pName->strA, &IdCaster{ id }.to, &IdCaster{ oTask.parent }.to, Cookie<CTraceEventFormat::CArgs>(oTask).Str().c_str(), rf.nanoseconds - oTask.rf.nanoseconds, data);
3737
}
3838

3939
void Marker(const CTraceEventFormat::SRegularFields& rf, const __itt_domain *pDomain, __itt_id id, __itt_string_handle *pName, __itt_scope scope) override
4040
{
41-
uint64_t data[3] = { rf.pid, rf.tid, rf.nanoseconds };
41+
uint64_t data[3] = { uint64_t(rf.pid), uint64_t(rf.tid), rf.nanoseconds };
4242
EventWriteMARKER(pDomain->nameA, pName->strA, id.d1, GetScope(scope), data);
4343
}
4444

4545
void Counter(const CTraceEventFormat::SRegularFields& rf, const __itt_domain *pDomain, const __itt_string_handle *pName, double value) override
4646
{
47-
uint64_t data[3] = { rf.pid, rf.tid, rf.nanoseconds };
47+
uint64_t data[3] = { uint64_t(rf.pid), uint64_t(rf.tid), rf.nanoseconds };
4848
EventWriteCOUNTER(pDomain->nameA, pName->strA, value, data);
4949
}
5050

0 commit comments

Comments
 (0)