You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
146 lines
5.1 KiB
146 lines
5.1 KiB
#!/usr/bin/env python3
|
|
import argparse
|
|
import json
|
|
import matplotlib.patches as mpatches
|
|
import matplotlib.pyplot as plt
|
|
import sys
|
|
from collections import defaultdict
|
|
|
|
from openpilot.tools.lib.logreader import LogReader
|
|
|
|
DEMO_ROUTE = "9f583b1d93915c31|2022-05-18--10-49-51--0"
|
|
|
|
COLORS = ['blue', 'green', 'red', 'yellow', 'orange', 'purple']
|
|
PLOT_SERVICES = ['card', 'controlsd'] # , 'boardd']
|
|
|
|
|
|
def plot(lr):
|
|
seen = set()
|
|
aligned = False
|
|
|
|
start_time = None
|
|
# dict of services to events per inferred frame
|
|
times = {s: [[]] for s in PLOT_SERVICES}
|
|
|
|
first_event = None
|
|
# temp_times = {s: [] for s in PLOT_SERVICES} # holds only current frame of services
|
|
|
|
timestamps = [json.loads(msg.logMessage) for msg in lr if msg.which() == 'logMessage' and 'timestamp' in msg.logMessage]
|
|
# print(timestamps)
|
|
timestamps = sorted(timestamps, key=lambda m: float(m['msg']['timestamp']['time']))
|
|
|
|
# closely matches timestamp time
|
|
start_time = next(msg.logMonoTime for msg in lr)
|
|
|
|
for jmsg in timestamps:
|
|
if len(times[PLOT_SERVICES[0]]) > 400:
|
|
continue
|
|
|
|
# print()
|
|
# print(msg.logMonoTime)
|
|
time = int(jmsg['msg']['timestamp']['time'])
|
|
service = jmsg['ctx']['daemon']
|
|
event = jmsg['msg']['timestamp']['event']
|
|
# print(jmsg)
|
|
# print(seen)
|
|
|
|
if service in PLOT_SERVICES and first_event is None:
|
|
first_event = event
|
|
|
|
# Align the best we can; wait for all to be seen and this is the first event
|
|
# TODO: detect first logMessage correctly by keeping track of events before aligned
|
|
aligned = aligned or (all(s in seen for s in PLOT_SERVICES) and event == first_event)
|
|
if not aligned:
|
|
seen.add(service)
|
|
continue
|
|
|
|
if service in PLOT_SERVICES:
|
|
|
|
# new frame when we've seen this event before
|
|
new_frame = event in {e[1] for e in times[service][-1]}
|
|
if new_frame:
|
|
times[service].append([])
|
|
|
|
# print(msg.logMonoTime, jmsg)
|
|
print('new_frame', new_frame)
|
|
times[service][-1].append(((time - start_time) * 1e-6, event))
|
|
|
|
points = {"x": [], "y": [], "labels": []}
|
|
colors = COLORS[:len(PLOT_SERVICES)]
|
|
offset_services = True
|
|
height = 0.9 if offset_services else 0.9
|
|
# offsets = [[0, -10 * j] for j in range(len(PLOT_SERVICES))] if offset_services else None
|
|
|
|
fig, ax = plt.subplots()
|
|
|
|
for idx, service_times in enumerate(zip(*times.values())):
|
|
print()
|
|
print('idx', idx)
|
|
service_bars = []
|
|
for j, (service, frame_times) in enumerate(zip(times.keys(), service_times)):
|
|
if idx + 1 == len(times[service]):
|
|
break
|
|
print(service, frame_times)
|
|
start = frame_times[0][0]
|
|
# use the first event time from next frame
|
|
end = times[service][idx + 1][0][0] # frame_times[-1][0]
|
|
print('start, end', start, end)
|
|
service_bars.append((start, end - start))
|
|
for event in frame_times:
|
|
points['x'].append(event[0])
|
|
points['y'].append(idx - j * 1)
|
|
points['labels'].append(event[1])
|
|
print(service_bars)
|
|
|
|
# offset = offset_services
|
|
# offset each service
|
|
for j, sb in enumerate(service_bars):
|
|
ax.broken_barh([sb], (idx - height / 2 - j * 1, height), facecolors=[colors[j]], alpha=0.5) # , offsets=offsets)
|
|
# ax.broken_barh(service_bars, [(idx - height / 2 - j * 5, height - j * 5) for j in range(len(service_bars))], facecolors=(colors), alpha=0.5)#, offsets=offsets)
|
|
|
|
scatter = ax.scatter(points['x'], points['y'], marker='d', edgecolor='black')
|
|
# for lbl, x, y in zip(points['labels'], points['x'], points['y']):
|
|
# ax.annotate(lbl, (x, y))
|
|
|
|
plt.legend(handles=[mpatches.Patch(color=colors[i], label=PLOT_SERVICES[i]) for i in range(len(PLOT_SERVICES))])
|
|
|
|
# plt.scatter([t[0] for t in times], [t[1] for t in times], marker='d', edgecolor='black')
|
|
ax.set_xlabel('milliseconds')
|
|
|
|
txt = ax.text(0, 0, '', ha='center', fontsize=8, color='red')
|
|
|
|
def hover(event):
|
|
txt.set_text("")
|
|
status, pts = scatter.contains(event)
|
|
txt.set_visible(status)
|
|
if status:
|
|
lbl = points['labels'][pts['ind'][0]]
|
|
txt.set_text(lbl)
|
|
txt.set_position((event.xdata, event.ydata + 1))
|
|
event.canvas.draw()
|
|
|
|
fig.canvas.mpl_connect("motion_notify_event", hover)
|
|
|
|
plt.show()
|
|
# plt.pause(1000)
|
|
return times, points
|
|
|
|
|
|
if __name__ == "__main__":
|
|
# parser = argparse.ArgumentParser(description="A tool for analyzing openpilot's end-to-end latency",
|
|
# formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
|
# parser.add_argument("--demo", action="store_true", help="Use the demo route instead of providing one")
|
|
# parser.add_argument("route_or_segment_name", nargs='?', help="The route to print")
|
|
#
|
|
# if len(sys.argv) == 1:
|
|
# parser.print_help()
|
|
# sys.exit()
|
|
# args = parser.parse_args()
|
|
|
|
# r = DEMO_ROUTE if args.demo else args.route_or_segment_name.strip()
|
|
# lr = LogReader(r, sort_by_time=True)
|
|
lr = LogReader('08e4c2a99df165b1/00000017--e2d24ab118/0', sort_by_time=True) # polls on carControl
|
|
lr = LogReader('08e4c2a99df165b1/00000018--cf65e47c24/0', sort_by_time=True) # polls on carControl, sends it earlier
|
|
lr = LogReader('08e4c2a99df165b1/00000019--e73e3ab4df/0', sort_by_time=True) # polls on carControl, more logging
|
|
|
|
times, points = plot(lr)
|
|
|