mirror of
https://github.com/infiniteCable2/openpilot.git
synced 2026-08-05 16:26:10 +08:00
merge selfdrive/debug/ into tools/scripts/ (#38195)
* merge selfdrive/debug/ into tools/scripts/ * rm unused * single timings script * lil more * profiling is scripts quality
This commit is contained in:
@@ -12,9 +12,6 @@ collect_ignore = [
|
||||
"selfdrive/test/process_replay/test_processes.py",
|
||||
"selfdrive/test/process_replay/test_regen.py",
|
||||
]
|
||||
collect_ignore_glob = [
|
||||
"selfdrive/debug/*.py",
|
||||
]
|
||||
|
||||
|
||||
def pytest_sessionstart(session):
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import os
|
||||
|
||||
from openpilot.common.basedir import BASEDIR
|
||||
from opendbc.car.docs import generate_cars_md, get_all_car_docs
|
||||
from openpilot.selfdrive.debug.dump_car_docs import dump_car_docs
|
||||
from openpilot.selfdrive.debug.print_docs_diff import print_car_docs_diff
|
||||
from openpilot.selfdrive.car.docs import CARS_MD_TEMPLATE
|
||||
|
||||
|
||||
@@ -14,9 +10,3 @@ class TestCarDocs:
|
||||
|
||||
def test_generator(self):
|
||||
generate_cars_md(self.all_cars, CARS_MD_TEMPLATE)
|
||||
|
||||
def test_docs_diff(self):
|
||||
dump_path = os.path.join(BASEDIR, "selfdrive", "car", "tests", "cars_dump")
|
||||
dump_car_docs(dump_path)
|
||||
print_car_docs_diff(dump_path)
|
||||
os.remove(dump_path)
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
# debug scripts
|
||||
|
||||
## [can_printer.py](can_printer.py)
|
||||
|
||||
```
|
||||
usage: can_printer.py [-h] [--bus BUS] [--max_msg MAX_MSG] [--addr ADDR]
|
||||
|
||||
simple CAN data viewer
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
--bus BUS CAN bus to print out (default: 0)
|
||||
--max_msg MAX_MSG max addr (default: None)
|
||||
--addr ADDR
|
||||
```
|
||||
|
||||
## [dump.py](dump.py)
|
||||
|
||||
```
|
||||
usage: dump.py [-h] [--pipe] [--raw] [--json] [--dump-json] [--no-print] [--addr ADDR] [--values VALUES] [socket [socket ...]]
|
||||
|
||||
Dump communication sockets. See cereal/services.py for a complete list of available sockets.
|
||||
|
||||
positional arguments:
|
||||
socket socket names to dump. defaults to all services defined in cereal
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
--pipe
|
||||
--raw
|
||||
--json
|
||||
--dump-json
|
||||
--no-print
|
||||
--addr ADDR
|
||||
--values VALUES values to monitor (instead of entire event)
|
||||
```
|
||||
|
||||
## [vw_mqb_config.py](vw_mqb_config.py)
|
||||
|
||||
```
|
||||
usage: vw_mqb_config.py [-h] [--debug] {enable,show,disable}
|
||||
|
||||
Shows Volkswagen EPS software and coding info, and enables or disables Heading Control
|
||||
Assist (Lane Assist). Useful for enabling HCA on cars without factory Lane Assist that want
|
||||
to use openpilot integrated at the CAN gateway (J533).
|
||||
|
||||
positional arguments:
|
||||
{enable,show,disable}
|
||||
show or modify current EPS HCA config
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
--debug enable ISO-TP/UDS stack debugging output
|
||||
|
||||
This tool is meant to run directly on a vehicle-installed comma three, with
|
||||
the openpilot/tmux processes stopped. It should also work on a separate PC with a USB-
|
||||
attached comma panda. Vehicle ignition must be on. Recommend engine not be running when
|
||||
making changes. Must turn ignition off and on again for any changes to take effect.
|
||||
```
|
||||
@@ -1,82 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
from tqdm import tqdm
|
||||
|
||||
from cereal.services import SERVICE_LIST, QueueSize
|
||||
from openpilot.tools.lib.logreader import LogReader
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="Analyze message sizes from a log route")
|
||||
parser.add_argument("route", nargs="?", default="98395b7c5b27882e/000000a8--f87e7cd255",
|
||||
help="Log route to analyze (default: 98395b7c5b27882e/000000a8--f87e7cd255)")
|
||||
args = parser.parse_args()
|
||||
|
||||
lr = LogReader(args.route)
|
||||
|
||||
szs = {}
|
||||
for msg in tqdm(lr):
|
||||
sz = len(msg.as_builder().to_bytes())
|
||||
msg_type = msg.which()
|
||||
if msg_type not in szs:
|
||||
szs[msg_type] = {'min': sz, 'max': sz, 'sum': sz, 'count': 1}
|
||||
else:
|
||||
szs[msg_type]['min'] = min(szs[msg_type]['min'], sz)
|
||||
szs[msg_type]['max'] = max(szs[msg_type]['max'], sz)
|
||||
szs[msg_type]['sum'] += sz
|
||||
szs[msg_type]['count'] += 1
|
||||
|
||||
print()
|
||||
print(f"{'Service':<36} {'Min (KB)':>12} {'Max (KB)':>12} {'Avg (KB)':>12} {'KB/min':>12} {'KB/sec':>12} {'Minutes in 10MB':>18} {'Seconds in Queue':>18}")
|
||||
print("-" * 132)
|
||||
def sort_key(x):
|
||||
k, v = x
|
||||
avg = v['sum'] / v['count']
|
||||
freq = SERVICE_LIST.get(k, None)
|
||||
freq_val = freq.frequency if freq else 0.0
|
||||
kb_per_min = (avg * freq_val * 60) / 1024 if freq_val > 0 else 0.0
|
||||
return kb_per_min
|
||||
total_kb_per_min = 0.0
|
||||
RINGBUFFER_SIZE_KB = 10 * 1024 # 10MB old default
|
||||
for k, v in sorted(szs.items(), key=sort_key, reverse=True):
|
||||
avg = v['sum'] / v['count']
|
||||
service = SERVICE_LIST.get(k, None)
|
||||
freq_val = service.frequency if service else 0.0
|
||||
queue_size_kb = (service.queue_size / 1024) if service else 250 # default to SMALL
|
||||
kb_per_min = (avg * freq_val * 60) / 1024 if freq_val > 0 else 0.0
|
||||
kb_per_sec = kb_per_min / 60
|
||||
minutes_in_buffer = RINGBUFFER_SIZE_KB / kb_per_min if kb_per_min > 0 else float('inf')
|
||||
seconds_in_queue = (queue_size_kb / kb_per_sec) if kb_per_sec > 0 else float('inf')
|
||||
total_kb_per_min += kb_per_min
|
||||
min_str = f"{minutes_in_buffer:.2f}" if minutes_in_buffer != float('inf') else "inf"
|
||||
sec_queue_str = f"{seconds_in_queue:.2f}" if seconds_in_queue != float('inf') else "inf"
|
||||
print(f"{k:<36} {v['min']/1024:>12.2f} {v['max']/1024:>12.2f} {avg/1024:>12.2f} {kb_per_min:>12.2f} {kb_per_sec:>12.2f} {min_str:>18} {sec_queue_str:>18}")
|
||||
|
||||
# Summary section
|
||||
print()
|
||||
print(f"Total usage: {total_kb_per_min / 1024:.2f} MB/min")
|
||||
|
||||
# Calculate memory usage: old (10MB for all) vs new (from services.py)
|
||||
OLD_SIZE = 10 * 1024 * 1024 # 10MB was the old default
|
||||
old_total = len(SERVICE_LIST) * OLD_SIZE
|
||||
|
||||
new_total = sum(s.queue_size for s in SERVICE_LIST.values())
|
||||
|
||||
# Count by queue size
|
||||
size_counts = {QueueSize.BIG: 0, QueueSize.MEDIUM: 0, QueueSize.SMALL: 0}
|
||||
for s in SERVICE_LIST.values():
|
||||
size_counts[s.queue_size] += 1
|
||||
|
||||
savings_pct = (1 - new_total / old_total) * 100
|
||||
|
||||
print()
|
||||
print(f"{'Queue Size Comparison':<40}")
|
||||
print("-" * 60)
|
||||
print(f"{'Old (10MB default):':<30} {old_total / 1024 / 1024:>10.2f} MB")
|
||||
print(f"{'New (from services.py):':<30} {new_total / 1024 / 1024:>10.2f} MB")
|
||||
print(f"{'Savings:':<30} {savings_pct:>10.1f}%")
|
||||
print()
|
||||
print(f"{'Breakdown:':<30}")
|
||||
print(f" BIG (10MB): {size_counts[QueueSize.BIG]:>3} services")
|
||||
print(f" MEDIUM (2MB): {size_counts[QueueSize.MEDIUM]:>3} services")
|
||||
print(f" SMALL (250KB): {size_counts[QueueSize.SMALL]:>3} services")
|
||||
@@ -1,36 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
import numpy as np
|
||||
import time
|
||||
from tqdm import tqdm
|
||||
|
||||
from cereal import car
|
||||
from opendbc.car.tests.routes import CarTestRoute
|
||||
from openpilot.selfdrive.car.tests.test_models import TestCarModelBase
|
||||
from openpilot.tools.plotjuggler.juggle import DEMO_ROUTE
|
||||
|
||||
N_RUNS = 10
|
||||
|
||||
|
||||
class CarModelTestCase(TestCarModelBase):
|
||||
test_route = CarTestRoute(DEMO_ROUTE, None)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Get CAN messages and parsers
|
||||
tm = CarModelTestCase()
|
||||
tm.setUpClass()
|
||||
tm.setUp()
|
||||
|
||||
CC = car.CarControl.new_message()
|
||||
ets = []
|
||||
for _ in tqdm(range(N_RUNS)):
|
||||
start_t = time.process_time_ns()
|
||||
for msg in tm.can_msgs:
|
||||
for cp in tm.CI.can_parsers.values():
|
||||
if cp is not None:
|
||||
cp.update_strings(msg)
|
||||
ets.append((time.process_time_ns() - start_t) * 1e-6)
|
||||
|
||||
print(f'{len(tm.can_msgs)} CAN packets, {N_RUNS} runs')
|
||||
print(f'{np.mean(ets):.2f} mean ms, {max(ets):.2f} max ms, {min(ets):.2f} min ms, {np.std(ets):.2f} std ms')
|
||||
print(f'{np.mean(ets) / len(tm.can_msgs):.4f} mean ms / CAN packet')
|
||||
@@ -1,50 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import numpy as np
|
||||
import time
|
||||
from collections import defaultdict, deque
|
||||
from collections.abc import MutableSequence
|
||||
|
||||
import cereal.messaging as messaging
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
context = messaging.Context()
|
||||
poller = messaging.Poller()
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("socket", type=str, nargs='*', help="socket name")
|
||||
args = parser.parse_args()
|
||||
|
||||
socket_names = args.socket
|
||||
sockets = {}
|
||||
|
||||
rcv_times: defaultdict[str, MutableSequence[float]] = defaultdict(lambda: deque(maxlen=100))
|
||||
valids: defaultdict[str, deque[bool]] = defaultdict(lambda: deque(maxlen=100))
|
||||
|
||||
t = time.monotonic()
|
||||
for name in socket_names:
|
||||
sock = messaging.sub_sock(name, poller=poller)
|
||||
sockets[sock] = name
|
||||
|
||||
prev_print = t
|
||||
while True:
|
||||
for socket in poller.poll(100):
|
||||
msg = messaging.recv_one(socket)
|
||||
if msg is None:
|
||||
continue
|
||||
|
||||
name = msg.which()
|
||||
|
||||
t = time.monotonic()
|
||||
rcv_times[name].append(msg.logMonoTime / 1e9)
|
||||
valids[name].append(msg.valid)
|
||||
|
||||
if t - prev_print > 1:
|
||||
print()
|
||||
for name in socket_names:
|
||||
dts = np.diff(rcv_times[name])
|
||||
mean = np.mean(dts)
|
||||
print(f"{name}: Freq {1.0 / mean:.2f} Hz, Min {np.min(dts) / mean * 100:.2f}%, Max {np.max(dts) / mean * 100:.2f}%, valid ", all(valids[name]))
|
||||
|
||||
prev_print = t
|
||||
@@ -1,27 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import cereal.messaging as messaging
|
||||
from cereal.services import SERVICE_LIST
|
||||
|
||||
TO_CHECK = ['carState']
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sm = messaging.SubMaster(TO_CHECK)
|
||||
|
||||
prev_t: dict[str, float] = {}
|
||||
|
||||
while True:
|
||||
sm.update()
|
||||
|
||||
for s in TO_CHECK:
|
||||
if sm.updated[s]:
|
||||
t = sm.logMonoTime[s] / 1e9
|
||||
|
||||
if s in prev_t:
|
||||
expected = 1.0 / (SERVICE_LIST[s].frequency)
|
||||
dt = t - prev_t[s]
|
||||
if dt > 10 * expected:
|
||||
print(t, s, dt)
|
||||
|
||||
prev_t[s] = t
|
||||
@@ -1,36 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
import time
|
||||
import numpy as np
|
||||
import datetime
|
||||
from collections.abc import MutableSequence
|
||||
from collections import defaultdict
|
||||
|
||||
import cereal.messaging as messaging
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
ts: defaultdict[str, MutableSequence[float]] = defaultdict(list)
|
||||
socks = {s: messaging.sub_sock(s, conflate=False) for s in sys.argv[1:]}
|
||||
try:
|
||||
st = time.monotonic()
|
||||
while True:
|
||||
print()
|
||||
for s, sock in socks.items():
|
||||
msgs = messaging.drain_sock(sock)
|
||||
for m in msgs:
|
||||
ts[s].append(m.logMonoTime / 1e6)
|
||||
|
||||
if len(ts[s]) > 2:
|
||||
d = np.diff(ts[s])[-100:]
|
||||
print(f"{s:25} {np.mean(d):7.2f} {np.std(d):7.2f} {np.max(d):7.2f} {np.min(d):7.2f}")
|
||||
time.sleep(1)
|
||||
except KeyboardInterrupt:
|
||||
print("\n")
|
||||
print("="*5, "timing summary", "="*5)
|
||||
for s, sock in socks.items():
|
||||
msgs = messaging.drain_sock(sock)
|
||||
if len(ts[s]) > 2:
|
||||
d = np.diff(ts[s])
|
||||
print(f"{s:25} {np.mean(d):7.2f} {np.std(d):7.2f} {np.max(d):7.2f} {np.min(d):7.2f}")
|
||||
print("="*5, datetime.timedelta(seconds=time.monotonic()-st), "="*5)
|
||||
@@ -1,18 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import pickle
|
||||
|
||||
from opendbc.car.docs import get_all_car_docs
|
||||
|
||||
|
||||
def dump_car_docs(path):
|
||||
with open(path, 'wb') as f:
|
||||
pickle.dump(get_all_car_docs(), f)
|
||||
print(f'Dumping car info to {path}')
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--path", required=True)
|
||||
args = parser.parse_args()
|
||||
dump_car_docs(args.path)
|
||||
@@ -1,120 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
from collections import defaultdict
|
||||
import difflib
|
||||
import pickle
|
||||
|
||||
from opendbc.car.docs import get_all_car_docs
|
||||
from opendbc.car.docs_definitions import Column
|
||||
|
||||
FOOTNOTE_TAG = "<sup>{}</sup>"
|
||||
STAR_ICON = '<a href="##"><img valign="top" ' + \
|
||||
'src="https://media.githubusercontent.com/media/commaai/openpilot/master/docs/assets/icon-star-{}.svg" width="22" /></a>'
|
||||
VIDEO_ICON = '<a href="{}" target="_blank">' + \
|
||||
'<img height="18px" src="https://media.githubusercontent.com/media/commaai/openpilot/master/docs/assets/icon-youtube.svg" /></a>'
|
||||
COLUMNS = "|" + "|".join([column.value for column in Column]) + "|"
|
||||
COLUMN_HEADER = "|---|---|---|{}|".format("|".join([":---:"] * (len(Column) - 3)))
|
||||
ARROW_SYMBOL = "➡️"
|
||||
|
||||
|
||||
def load_base_car_docs(path):
|
||||
with open(path, "rb") as f:
|
||||
return pickle.load(f)
|
||||
|
||||
|
||||
def match_cars(base_cars, new_cars):
|
||||
changes = []
|
||||
additions = []
|
||||
for new in new_cars:
|
||||
# Addition if no close matches or close match already used
|
||||
# Change if close match and not already used
|
||||
matches = difflib.get_close_matches(new.name, [b.name for b in base_cars], cutoff=0.)
|
||||
if not len(matches) or matches[0] in [c[1].name for c in changes]:
|
||||
additions.append(new)
|
||||
else:
|
||||
changes.append((new, next(car for car in base_cars if car.name == matches[0])))
|
||||
|
||||
# Removal if base car not in changes
|
||||
removals = [b for b in base_cars if b.name not in [c[1].name for c in changes]]
|
||||
return changes, additions, removals
|
||||
|
||||
|
||||
def build_column_diff(base_car, new_car):
|
||||
row_builder = []
|
||||
for column in Column:
|
||||
base_column = base_car.get_column(column, STAR_ICON, VIDEO_ICON, FOOTNOTE_TAG)
|
||||
new_column = new_car.get_column(column, STAR_ICON, VIDEO_ICON, FOOTNOTE_TAG)
|
||||
|
||||
if base_column != new_column:
|
||||
row_builder.append(f"{base_column} {ARROW_SYMBOL} {new_column}")
|
||||
else:
|
||||
row_builder.append(new_column)
|
||||
|
||||
return format_row(row_builder)
|
||||
|
||||
|
||||
def format_row(builder):
|
||||
return "|" + "|".join(builder) + "|"
|
||||
|
||||
|
||||
def print_car_docs_diff(path):
|
||||
base_car_docs = defaultdict(list)
|
||||
new_car_docs = defaultdict(list)
|
||||
|
||||
for car in load_base_car_docs(path):
|
||||
base_car_docs[car.car_fingerprint].append(car)
|
||||
for car in get_all_car_docs():
|
||||
new_car_docs[car.car_fingerprint].append(car)
|
||||
|
||||
# Add new platforms to base cars so we can detect additions and removals in one pass
|
||||
base_car_docs.update({car: [] for car in new_car_docs if car not in base_car_docs})
|
||||
|
||||
changes = defaultdict(list)
|
||||
for base_car_model, base_cars in base_car_docs.items():
|
||||
# Match car info changes, and get additions and removals
|
||||
new_cars = new_car_docs[base_car_model]
|
||||
car_changes, car_additions, car_removals = match_cars(base_cars, new_cars)
|
||||
|
||||
# Removals
|
||||
for car_docs in car_removals:
|
||||
changes["removals"].append(format_row([car_docs.get_column(column, STAR_ICON, VIDEO_ICON, FOOTNOTE_TAG) for column in Column]))
|
||||
|
||||
# Additions
|
||||
for car_docs in car_additions:
|
||||
changes["additions"].append(format_row([car_docs.get_column(column, STAR_ICON, VIDEO_ICON, FOOTNOTE_TAG) for column in Column]))
|
||||
|
||||
for new_car, base_car in car_changes:
|
||||
# Column changes
|
||||
row_diff = build_column_diff(base_car, new_car)
|
||||
if ARROW_SYMBOL in row_diff:
|
||||
changes["column"].append(row_diff)
|
||||
|
||||
# Detail sentence changes
|
||||
if base_car.detail_sentence != new_car.detail_sentence:
|
||||
changes["detail"].append(f"- Sentence for {base_car.name} changed!\n" +
|
||||
" ```diff\n" +
|
||||
f" - {base_car.detail_sentence}\n" +
|
||||
f" + {new_car.detail_sentence}\n" +
|
||||
" ```")
|
||||
|
||||
# Print diff
|
||||
if any(len(c) for c in changes.values()):
|
||||
markdown_builder = ["### ⚠️ This PR makes changes to [CARS.md](../blob/master/docs/CARS.md) ⚠️"]
|
||||
|
||||
for title, category in (("## 🔀 Column Changes", "column"), ("## ❌ Removed", "removals"),
|
||||
("## ➕ Added", "additions"), ("## 📖 Detail Sentence Changes", "detail")):
|
||||
if len(changes[category]):
|
||||
markdown_builder.append(title)
|
||||
if category not in ("detail",):
|
||||
markdown_builder.append(COLUMNS)
|
||||
markdown_builder.append(COLUMN_HEADER)
|
||||
markdown_builder.extend(changes[category])
|
||||
|
||||
print("\n".join(markdown_builder))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--path", required=True)
|
||||
args = parser.parse_args()
|
||||
print_car_docs_diff(args.path)
|
||||
@@ -1,54 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from openpilot.tools.lib.logreader import LogReader
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--width', default=2160, type=int)
|
||||
parser.add_argument('--height', default=1080, type=int)
|
||||
parser.add_argument('--route', default='rlog', type=str)
|
||||
args = parser.parse_args()
|
||||
|
||||
w = args.width
|
||||
h = args.height
|
||||
route = args.route
|
||||
|
||||
fingers = [[-1, -1]] * 5
|
||||
touch_points = []
|
||||
current_slot = 0
|
||||
|
||||
lr = list(LogReader(route))
|
||||
for msg in lr:
|
||||
if msg.which() == 'touch':
|
||||
for event in msg.touch:
|
||||
if event.type == 3 and event.code == 47:
|
||||
current_slot = event.value
|
||||
elif event.type == 3 and event.code == 57 and event.value == -1:
|
||||
fingers[current_slot] = [-1, -1]
|
||||
elif event.type == 3 and event.code == 53:
|
||||
fingers[current_slot][1] = event.value
|
||||
if fingers[current_slot][0] != -1:
|
||||
touch_points.append(fingers[current_slot].copy())
|
||||
elif event.type == 3 and event.code == 54:
|
||||
fingers[current_slot][0] = w - event.value
|
||||
if fingers[current_slot][1] != -1:
|
||||
touch_points.append(fingers[current_slot].copy())
|
||||
|
||||
if not touch_points:
|
||||
print(f'No touch events found for {route}')
|
||||
quit()
|
||||
|
||||
unique_points, counts = np.unique(touch_points, axis=0, return_counts=True)
|
||||
|
||||
plt.figure(figsize=(10, 3))
|
||||
plt.scatter(unique_points[:, 0], unique_points[:, 1], c=counts, s=counts * 20, edgecolors='red')
|
||||
plt.colorbar()
|
||||
plt.title(f'Touches for {route}')
|
||||
plt.xlim(0, w)
|
||||
plt.ylim(0, h)
|
||||
plt.grid(True)
|
||||
plt.show()
|
||||
@@ -284,7 +284,7 @@ class TestOnroad:
|
||||
print("--------------- Memory Usage -------------------")
|
||||
print("------------------------------------------------")
|
||||
|
||||
from openpilot.selfdrive.debug.mem_usage import print_report
|
||||
from openpilot.tools.scripts.mem_usage import print_report
|
||||
print_report(self.msgs['procLog'], self.msgs['deviceState'])
|
||||
|
||||
offset = int(SERVICE_LIST['deviceState'].frequency * LOG_OFFSET)
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ Welcome to the first part of the comma CTF!
|
||||
* everything you'll need to find the flags is in the openpilot repo
|
||||
* grep is also your friend
|
||||
* first, [setup](https://github.com/commaai/openpilot/tree/master/tools#setup-your-pc) your PC
|
||||
* read the docs & checkout out the tools in tools/ and selfdrive/debug/
|
||||
* read the docs & checkout out the tools in tools/
|
||||
* tip: once you get the replay and UI up, start by familiarizing yourself with seeking in replay
|
||||
|
||||
getting started
|
||||
|
||||
@@ -5,7 +5,7 @@ import time
|
||||
from collections import defaultdict
|
||||
|
||||
import cereal.messaging as messaging
|
||||
from openpilot.selfdrive.debug.can_table import can_table
|
||||
from openpilot.tools.scripts.can_table import can_table
|
||||
from openpilot.tools.lib.logreader import LogIterable, LogReader
|
||||
|
||||
RED = '\033[91m'
|
||||
@@ -8,7 +8,7 @@ System tools like top/htop can only show current cpu usage values, so I write th
|
||||
Calculate minumium/maximum/accumulated_average cpu usage as long term inspections.
|
||||
Monitor multiple processes simuteneously.
|
||||
Sample usage:
|
||||
root@localhost:/data/openpilot$ python selfdrive/debug/cpu_usage_stat.py pandad,ubloxd
|
||||
root@localhost:/data/openpilot$ python tools/scripts/cpu_usage_stat.py pandad,ubloxd
|
||||
('Add monitored proc:', './pandad')
|
||||
('Add monitored proc:', 'python locationd/ubloxd.py')
|
||||
pandad: 1.96%, min: 1.96%, max: 1.96%, acc: 1.96%
|
||||
@@ -1,43 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
|
||||
if len(sys.argv) < 4:
|
||||
print(f"{sys.argv[0]} <route> <segment> <frame number> [front|wide|driver]")
|
||||
print('example: ./fetch_image_from_route.py "02c45f73a2e5c6e9|2020-06-01--18-03-08" 3 500 driver')
|
||||
exit(0)
|
||||
|
||||
cameras = {
|
||||
"front": "cameras",
|
||||
"wide": "ecameras",
|
||||
"driver": "dcameras"
|
||||
}
|
||||
|
||||
import requests
|
||||
from PIL import Image
|
||||
from openpilot.tools.lib.auth_config import get_token
|
||||
from openpilot.tools.lib.framereader import FrameReader
|
||||
|
||||
jwt = get_token()
|
||||
|
||||
route = sys.argv[1]
|
||||
segment = int(sys.argv[2])
|
||||
frame = int(sys.argv[3])
|
||||
camera = cameras[sys.argv[4]] if len(sys.argv) > 4 and sys.argv[4] in cameras else "cameras"
|
||||
|
||||
url = f'https://api.commadotai.com/v1/route/{route}/files'
|
||||
r = requests.get(url, headers={"Authorization": f"JWT {jwt}"}, timeout=10)
|
||||
assert r.status_code == 200
|
||||
print("got api response")
|
||||
|
||||
segments = r.json()[camera]
|
||||
if segment >= len(segments):
|
||||
raise Exception(f"segment {segment} not found, got {len(segments)} segments")
|
||||
|
||||
fr = FrameReader(segments[segment])
|
||||
if frame >= fr.frame_count:
|
||||
raise Exception(f"frame {frame} not found, got {fr.frame_count} frames")
|
||||
|
||||
im = Image.fromarray(fr.get(frame))
|
||||
fn = f"uxxx_{route.replace('|', '_')}_{segment}_{frame}.png"
|
||||
im.save(fn)
|
||||
print(f"saved {fn}")
|
||||
@@ -1,47 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
from openpilot.common.basedir import BASEDIR
|
||||
from openpilot.tools.lib.logreader import LogReader
|
||||
|
||||
os.environ['BASEDIR'] = BASEDIR
|
||||
|
||||
|
||||
def get_arg_parser():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Unlogging and save to file",
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
||||
|
||||
parser.add_argument("route", type=(lambda x: x.replace("#", "|")), nargs="?",
|
||||
help="The route whose messages will be published.")
|
||||
parser.add_argument("--out_path", nargs='?', default='/data/ubloxRaw.stream',
|
||||
help="Output pickle file path")
|
||||
return parser
|
||||
|
||||
|
||||
def main():
|
||||
args = get_arg_parser().parse_args(sys.argv[1:])
|
||||
|
||||
lr = LogReader(args.route)
|
||||
|
||||
with open(args.out_path, 'wb') as f:
|
||||
try:
|
||||
done = False
|
||||
i = 0
|
||||
while not done:
|
||||
msg = next(lr)
|
||||
if not msg:
|
||||
break
|
||||
smsg = msg.as_builder()
|
||||
typ = smsg.which()
|
||||
if typ == 'ubloxRaw':
|
||||
f.write(smsg.to_bytes())
|
||||
i += 1
|
||||
except StopIteration:
|
||||
print('All done')
|
||||
print(f'Writed {i} msgs')
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Executable
+102
@@ -0,0 +1,102 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import datetime
|
||||
import time
|
||||
from collections import deque
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
import numpy as np
|
||||
|
||||
import cereal.messaging as messaging
|
||||
from cereal.services import SERVICE_LIST
|
||||
|
||||
|
||||
@dataclass
|
||||
class ServiceTiming:
|
||||
times: list[float] = field(default_factory=list)
|
||||
window: deque[float] = field(default_factory=lambda: deque(maxlen=100))
|
||||
valids: deque[bool] = field(default_factory=lambda: deque(maxlen=100))
|
||||
lag_events: list[tuple[float, float]] = field(default_factory=list)
|
||||
|
||||
def add(self, mono_time: float, valid: bool, expected_interval: float | None, lag_threshold: float) -> None:
|
||||
if self.times:
|
||||
dt = mono_time - self.times[-1]
|
||||
self.window.append(dt)
|
||||
if expected_interval is not None and dt > lag_threshold * expected_interval:
|
||||
self.lag_events.append((mono_time, dt))
|
||||
|
||||
self.times.append(mono_time)
|
||||
self.valids.append(valid)
|
||||
|
||||
def intervals(self, latest_only: bool) -> np.ndarray:
|
||||
if latest_only:
|
||||
return np.array(self.window)
|
||||
return np.diff(self.times)
|
||||
|
||||
|
||||
def format_row(name: str, timing: ServiceTiming, latest_only: bool) -> str:
|
||||
dts = timing.intervals(latest_only)
|
||||
if len(dts) == 0:
|
||||
return f"{name:25} waiting for messages"
|
||||
|
||||
mean = np.mean(dts)
|
||||
hz = 1.0 / mean if mean > 0 else 0.0
|
||||
valid = all(timing.valids) if timing.valids else False
|
||||
return f"{name:25} {hz:8.2f}Hz {mean * 1e3:8.2f}ms {np.std(dts) * 1e3:8.2f}ms {np.max(dts) * 1e3:8.2f}ms {np.min(dts) * 1e3:8.2f}ms valid={valid}"
|
||||
|
||||
|
||||
def print_lag_events(name: str, timing: ServiceTiming, printed_lags: dict[str, int]) -> None:
|
||||
start = printed_lags.get(name, 0)
|
||||
for mono_time, dt in timing.lag_events[start:]:
|
||||
print(f"{mono_time:.3f} {name} lag {dt:.3f}s", flush=True)
|
||||
printed_lags[name] = len(timing.lag_events)
|
||||
|
||||
|
||||
def monitor_services(socket_names: list[str], print_interval: float, lag_threshold: float, lag_only: bool) -> None:
|
||||
sockets = {name: messaging.sub_sock(name, conflate=False) for name in socket_names}
|
||||
timings = {name: ServiceTiming() for name in socket_names}
|
||||
printed_lags: dict[str, int] = {}
|
||||
|
||||
start_time = time.monotonic()
|
||||
last_print = start_time
|
||||
|
||||
try:
|
||||
while True:
|
||||
for name, sock in sockets.items():
|
||||
for msg in messaging.drain_sock(sock):
|
||||
expected_interval = 1.0 / SERVICE_LIST[name].frequency if name in SERVICE_LIST else None
|
||||
timings[name].add(msg.logMonoTime / 1e9, msg.valid, expected_interval, lag_threshold)
|
||||
|
||||
now = time.monotonic()
|
||||
if now - last_print < print_interval:
|
||||
time.sleep(0.01)
|
||||
continue
|
||||
|
||||
if not lag_only:
|
||||
print(flush=True)
|
||||
print(f"{'service':25} {'freq':>10} {'mean':>10} {'std':>10} {'max':>10} {'min':>10} valid", flush=True)
|
||||
for name in socket_names:
|
||||
print(format_row(name, timings[name], latest_only=True), flush=True)
|
||||
|
||||
for name in socket_names:
|
||||
print_lag_events(name, timings[name], printed_lags)
|
||||
|
||||
last_print = now
|
||||
except KeyboardInterrupt:
|
||||
print("\n", flush=True)
|
||||
print("=" * 5, "timing summary", "=" * 5, flush=True)
|
||||
print(f"{'service':25} {'freq':>10} {'mean':>10} {'std':>10} {'max':>10} {'min':>10} valid", flush=True)
|
||||
for name in socket_names:
|
||||
print(format_row(name, timings[name], latest_only=False), flush=True)
|
||||
print("=" * 5, datetime.timedelta(seconds=time.monotonic() - start_time), "=" * 5, flush=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="Check live service timing, frequency, validity, and lag")
|
||||
parser.add_argument("socket", nargs="*", default=["carState"], help="service/socket name")
|
||||
parser.add_argument("--lag-threshold", type=float, default=10.0, help="report intervals above this multiple of the expected service interval")
|
||||
parser.add_argument("--lag-only", action="store_true", help="only print lag events")
|
||||
parser.add_argument("--print-interval", type=float, default=1.0, help="seconds between table updates")
|
||||
args = parser.parse_args()
|
||||
|
||||
monitor_services(args.socket, args.print_interval, args.lag_threshold, args.lag_only)
|
||||
Reference in New Issue
Block a user