#!/usr/bin/env python3 import argparse import collections import dpkt import json import operator import os import socket import subprocess import struct import tabulate SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) PROJECT_DIR = os.path.normpath(os.path.join(SCRIPT_DIR, "..")) # helpers def format_endpoint(addr, port): return "{}:{}".format(socket.inet_ntoa(addr), port) def parse_capnp_header(fname): ret = {} last_struct = "" with open(fname) as f: for row in f: row = row.strip() if row.startswith("struct") and \ not row.startswith("struct _capnpPrivate"): last_struct = row.split()[1] if row.startswith("CAPNP_DECLARE_STRUCT_HEADER"): bytes_val = bytes.fromhex(row.split("(")[1].split(",")[0]) val = struct.unpack(">Q", bytes_val)[0] ret[val] = last_struct return ret def parse_all_capnp_headers(dirname): ids = {} ret = subprocess.run(["find", dirname, "-name", "*.capnp.h"], stdout=subprocess.PIPE) ret.check_returncode() headers = list(filter(None, ret.stdout.decode("utf-8").split("\n"))) for header in headers: ids.update(parse_capnp_header(header)) return ids MESSAGES = parse_all_capnp_headers(os.path.join(PROJECT_DIR, "src")) class Connection: # uint32_t message_size SIZE_FORMAT = "I" SIZE_LEN = struct.calcsize(SIZE_FORMAT) def __init__(self): self._data = bytes() self._message = bytes() self._ts = [] self._last = None self._stats = collections.defaultdict(lambda: {"duration": [], "size": []}) self._requests = [] def _extract_message(self): if len(self._data) < self.SIZE_LEN: return False msg_len = struct.unpack_from(self.SIZE_FORMAT, self._data)[0] if len(self._data) < self.SIZE_LEN + msg_len: return False self._message = self._data[self.SIZE_LEN:] self._data = bytes() return True def add_data(self, data, direction, ts): self._data += data self._ts.append(ts) if not self._extract_message(): return message_id = struct.unpack("