trace: add audio tracing and plotting script

Change-Id: Ie5c8fe1d9cc4e5a38a623a1cd4755c00ad4f4904
This commit is contained in:
Tobias Hildebrandt
2022-08-08 15:32:47 -04:00
committed by Sébastien Blin
parent cb3d3c3c85
commit 53f60a1aa4
9 changed files with 330 additions and 5 deletions

1
tools/trace/audio/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
out.png

152
tools/trace/audio/audio.py Normal file
View File

@ -0,0 +1,152 @@
#!/usr/bin/env python3
"""
Quick-and-dirty script to plot lttng trace data of the jami audio system
Depends on `python3-bt2` package:
https://babeltrace.org/docs/v2.0/python/bt2/
If using a virtual environment, you need to link the system babeltrace2 python3
package to your virtual environment, something like:
`ln -s /usr/lib/python3/dist-packages/bt2 "$VIRTUAL_ENV"/lib/python3*/site-packages/`
"""
import re
import dataclasses
from typing import Dict, List
import sys
import bt2
import matplotlib.pyplot as plt
@dataclasses.dataclass
class Intervals:
"""Keep track of timestamps and the interval intervals between them"""
intervals: List[int] = dataclasses.field(default_factory=list)
times: List[int] = dataclasses.field(default_factory=list)
last_timestamp: int = 0
def add_event(self, timestamp: int):
"""Add a new timestamp and calculate the interval"""
if self.last_timestamp == 0:
self.intervals.append(0)
else:
self.intervals.append(timestamp - self.last_timestamp)
self.times.append(timestamp)
self.last_timestamp = timestamp
def filter(self, earliest: int, latest: int):
"""Filter out entries that are not in between `earliest` and `latest` timestamps"""
new_intervals = []
new_times = []
for i, val in enumerate(self.times):
if val > earliest and val < latest:
new_times.append(val)
new_intervals.append(self.intervals[i])
self.times = new_times
self.intervals = new_intervals
def analyze(filename: str):
"""Process trace file"""
# running_averages: Dict[str, RunningAverage] = {}
intervals: Dict[str, Intervals] = {}
desired_event_regex = re.compile("jami:(call|audio|conference)")
for message in bt2.TraceCollectionMessageIterator(filename):
# pylint: disable=protected-access
if not isinstance(message, bt2._EventMessageConst):
continue
ns_from_origin = message.default_clock_snapshot.ns_from_origin
name = message.event.name
if not desired_event_regex.match(name):
continue
if "id" in message.event.payload_field and name != "jami:conference_add_participant":
name = f"{name}({str(message.event.payload_field['id'])})"
if not name in intervals:
# running_averages[name] = RunningAverage()
intervals[name] = Intervals()
# running_averages[name].add_val(ns_from_origin)
intervals[name].add_event(ns_from_origin)
# print(f"{ns_from_origin / 10e9:.9f}: {name}")
for key, val in intervals.items():
print(
f"event: {key}, average: {(sum(val.intervals) / len(val.intervals)) / 1e9:.9f}"
)
earliest_recorded = intervals["jami:audio_layer_put_recorded_end"].times[0]
latest_recorded = intervals["jami:audio_layer_put_recorded_end"].times[-1]
earliest_recorded += int(1e9) # start graph 1 second later
print(
f"earliest: {earliest_recorded / 1e9 :.9f}, latest: {latest_recorded / 1e9:.9f}"
)
for _, interval_val in intervals.items():
interval_val.filter(earliest_recorded, latest_recorded)
# convert from unix timestamp to seconds since start
for _, interval_val in intervals.items():
for i, _ in enumerate(interval_val.times):
interval_val.times[i] -= earliest_recorded
plot(intervals, filename)
def plot(intervals: Dict[str, Intervals], filename: str):
"""Plot audio event data"""
add_part = intervals["jami:conference_add_participant"]
fig, axis = plt.subplots(figsize=(20, 9))
axis.set_xlabel("seconds since start")
axis.set_ylabel("interval between events (seconds)")
axis.set_title("jami:audio*")
# only plot audio events
events_to_plot_regex = re.compile("jami:audio")
# plot each desired interval
for interval_key, interval_val in intervals.items():
if not events_to_plot_regex.match(interval_key):
continue
axis.plot(
[i / 1e9 for i in interval_val.times],
[i / 1e9 for i in interval_val.intervals],
label=interval_key,
)
# generate vertical line for each time a new participant is added to conference
for time in add_part.times:
axis.axvline(
time / 1e9,
color="black",
ls="--",
)
axis.legend()
# save it as out.png
filename = "out.png"
fig.savefig(filename, dpi=100)
print(f"png saved as {filename}")
if __name__ == "__main__":
if len(sys.argv) < 2:
print("usage: python3 audio.py <lttng trace directory>", file=sys.stderr)
sys.exit(-1)
analyze(sys.argv[1])

View File

@ -0,0 +1,65 @@
#!/bin/sh
# this script sets up an lttng session and enables triggers for call start and conference end
# ideally, this script would be more configurable, right now it's hardcoded and hacky
if lttng status | grep -q "Recording session"; then
echo "lttng session already active, exiting"
exit 1
fi
SESSION="jami"
# Prepare a capture session
lttng create "$SESSION"
# Enable kernel and user channels
# lttng enable-channel -k --subbuf-size 4096K kernel
lttng enable-channel -u --subbuf-size 1024K user
# Enable all kernel syscalls tracing
# lttng enable-event --kernel --syscall --all --channel=kernel
# Enable sched_* (scheduler) functions tracing
# lttng enable-event --kernel --channel=kernel "$(lttng list --kernel | grep sched_ | awk '{ print $1 }' | xargs | sed -e 's/ /,/g')"
# Enable net_* functions tracing
# lttng enable-event --kernel --channel=kernel "$(lttng list --kernel | grep net_ | awk '{ print $1 }' | xargs | sed -e 's/ /,/g')"
# Userspace tracing
# Add support for tracef() traces
# You need to link application with @-llttng-ust@
# lttng enable-event --userspace 'lttng_ust_tracef:*' --channel=user
# Add context for libc (including malloc, etc)
export LD_PRELOAD="$LD_PRELOAD":liblttng-ust-libc-wrapper.so
lttng enable-event --userspace 'lttng_ust_libc:*' --channel=user
lttng add-context --userspace -t vtid -t procname
# Add context for pthread (including mutexes)
export LD_PRELOAD="$LD_PRELOAD":liblttng-ust-pthread-wrapper.so
lttng enable-event --userspace 'lttng_ust_pthread:*' --channel=user
# Add context for function profiling
export LD_PRELOAD="$LD_PRELOAD":liblttng-ust-cyg-profile.so
lttng enable-event --userspace --all --channel=user
lttng add-context --userspace -t vpid -t vtid -t procname
# loop over lines in our for-loop, since triggers might have spaces in their names
OLDIFS="$IFS"
IFS='
'
# remove all triggers
for trigger in $(lttng list-triggers | sed -nr 's/- name: (.*)$/\1/p'); do
echo "removing trigger: $trigger"
lttng remove-trigger "$trigger"
done
IFS=$OLDIFS
# add start and end trigger
lttng add-trigger --name "jami call start" --condition=event-rule-matches --type=user --name='jami:call_start' --action=start-session $SESSION
lttng add-trigger --name "jami conference end" --condition=event-rule-matches --type=user --name="jami:conference_end" --action=stop-session $SESSION