Skip to content

Instantly share code, notes, and snippets.

Last active Feb 24, 2021
What would you like to do?
IOC connection time analysis

IOC connection time analysis

We will use caproto-shark to analyze CA network traffic. Note that the actual servers and clients involve may or may not be using caproto themselves; it does not matter.

  1. Install caproto and pandas if they are not already installed.

    pip install caproto[standard] pandas
  2. Start tcpdump to capture network traffic and write it to a file.

    sudo tcpdump -i <INTERFACE> -w some_network_traffic.pcap

    Use ifconfig to list the available interfaces. For example, if the servers in question are running on the localhost:

    sudo tcpdump -i lo -w some_network_traffic.pcap
  3. Run some EPICS client code that will create CA connections with servers (e.g. run bsui).

  4. Stop tcpdump with Ctrl+C.

  5. Analyze the results using the script in this gist.

    python some_network_traffic.pcap

Example output:

0 channel creation requests were unanswered during this capture.

Raw data:
          PV  server_address             t        dt
0   simple:A  1.614185e+09  0.014851
1   simple:B  1.614185e+09  0.014851
2   simple:C  1.614185e+09  0.014851
3  rpi:color  1.614185e+09  0.150308

Aggregated stats by server address:
                count      mean  std       min       25%       50%       75%       max
server_address                                                                    3.0  0.014851  0.0  0.014851  0.014851  0.014851  0.014851  0.014851    1.0  0.150308  NaN  0.150308  0.150308  0.150308  0.150308  0.150308
Analyze IOCs response times for channel creation (connection).
python some_network_traffic.pcap
from collections import namedtuple
from caproto import CreateChanRequest, CreateChanResponse
from caproto.sync.shark import shark
import pandas
Pair = namedtuple("Pair", ["request", "response"])
Record = namedtuple("Record", ["PV", "server_address", "t", "dt"])
def match_request_and_response(parsed):
"From a stream of parsed CA traffic, extract channel creation request/response pairs."
unanswered_requests = {}
pairs = []
for item in parsed:
command = item.command
if isinstance(command, CreateChanRequest):
unanswered_requests[command.cid] = item
elif isinstance(command, CreateChanResponse):
request_item = unanswered_requests.pop(command.cid, None)
pairs.append(Pair(request_item, item))
return pairs, list(unanswered_requests.values())
def build_record_from_request_and_response_pair(pair):
"Extract PV name, server address, absolute time t, and request/response dt."
request, response = pair
record = Record(,
dt=response.timestamp - request.timestamp
return record
def main(filepath):
with open(filepath, "rb") as file:
parsed = shark(file)
pairs, unanswered = match_request_and_response(parsed)
print(f"{len(unanswered)} channel creation requests were unanswered during this capture.")
# TODO Tally the unanswered requests by server_address and print that.
# The collections.Counter object would be useful there.
records = [build_record_from_request_and_response_pair(pair) for pair in pairs]
df = pandas.DataFrame.from_records(records, columns=Record._fields)
server_stats = df.groupby("server_address")["dt"].describe()
print("\n\nRaw data:")
print("\n\nAggregated stats by server address:")
if __name__ == "__main__":
import sys
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment