[RFC PATCH] tools/perf: Integrate DAMON in perf
From: SeongJae Park
Date: Thu Jan 07 2021 - 07:11:46 EST
From: SeongJae Park <sjpark@xxxxxxxxx>
NOTE: This RFC has a dependancy on DAMON (Data Access MONitor)
patchset[1], which is not merged in the mainline yet. The aim of this
is to show how DAMON would be evolved once it is merged in and get some
comments early. So, if you have some interest in this, please consider
reviewing the DAMON patchset, either.
[1] https://lore.kernel.org/linux-mm/20201215115448.25633-1-sjpark@xxxxxxxxxx
---
Though there is a simple debugfs interface for DAMON and even a
dedicated user space tool, integrating the interface in perf will make
best user experiences. For the reason, this commit adds perf scripts
for DAMON. After this commit, users can record the data access
monitoring results and read in human readable form with:
$ sudo perf script record damon
$ sudo perf script report damon
or simply,
$ sudo perf script damon
Nevertheless, above commands do not start the monitoring by themselves,
so the user should turn it on in background. To make it easy, this
commit also adds a convenient version of 'perf script record damon',
which executes a command, turns on DAMON for the process, and records
DAMON trace events. For example, it can be used as:
$ sudo ~/libexec/perf-core/scripts/python/bin/damon-record.sh <cmd>
$ sudo perf script report damon
Currently, the report command supports only raw format.
Signed-off-by: SeongJae Park <sjpark@xxxxxxxxx>
---
tools/perf/scripts/python/bin/damon-record | 4 +
tools/perf/scripts/python/bin/damon-record.sh | 163 ++++++++++++++++++
tools/perf/scripts/python/bin/damon-report | 4 +
tools/perf/scripts/python/damon.py | 44 +++++
4 files changed, 215 insertions(+)
create mode 100644 tools/perf/scripts/python/bin/damon-record
create mode 100644 tools/perf/scripts/python/bin/damon-record.sh
create mode 100644 tools/perf/scripts/python/bin/damon-report
create mode 100644 tools/perf/scripts/python/damon.py
diff --git a/tools/perf/scripts/python/bin/damon-record b/tools/perf/scripts/python/bin/damon-record
new file mode 100644
index 000000000000..6e21802c8c0e
--- /dev/null
+++ b/tools/perf/scripts/python/bin/damon-record
@@ -0,0 +1,4 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+perf record -e damon:damon_aggregated $@
diff --git a/tools/perf/scripts/python/bin/damon-record.sh b/tools/perf/scripts/python/bin/damon-record.sh
new file mode 100644
index 000000000000..3e3e27343ccc
--- /dev/null
+++ b/tools/perf/scripts/python/bin/damon-record.sh
@@ -0,0 +1,163 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# This is more convenient version of 'perf script record damon <command>'.
+# While the command assumes DAMON will be turned on by the user, this do that
+# instead. That is, this command starts the command, turn DAMON on for the
+# process, and record the trace events.
+
+pr_usage()
+{
+ >&2 echo "Usage: $0 [OPTION]... <command>"
+ >&2 echo
+ >&2 echo "OPTION"
+ >&2 echo " --sampling <interval> Sampling interval (us)"
+ >&2 echo " --aggregate <interval> Aggregate interval (us)"
+ >&2 echo " --update <interval> Regions update interval (us)"
+ >&2 echo " --min-reg <nr> Minimum number of regions"
+ >&2 echo " --max-reg <nr> Maximum number of regions"
+}
+
+# Default values (intervals are in us)
+sampling_interval=5000
+aggregate_interval=100000
+regions_update_interval=1000000
+min_nr_regions=10
+max_nr_regions=1000
+cmd=""
+
+debugfs_dir=$(mount | grep -e "type debugfs" | awk '{print $3}')
+if [ -z "$debugfs_dir" ]
+then
+ >&2 echo "debugfs not found"
+ exit 1
+fi
+
+damon_dir="$debugfs_dir/damon"
+if [ ! -d "$damon_dir" ]
+then
+ >&2 echo "damon dir not found"
+ exit 1
+fi
+
+if [ $# -lt 1 ]
+then
+ pr_usage
+ exit 1
+fi
+
+while [ $# -ne 0 ]
+do
+ case $1 in
+ "--sampling")
+ if [ $# -lt 2 ]
+ then
+ >&2 echo "<interval> not given"
+ pr_usage
+ exit 1
+ fi
+ sampling_interval=$2
+ shift 2
+ continue
+ ;;
+ "--aggregate")
+ if [ $# -lt 2 ]
+ then
+ >&2 echo "<interval> not given"
+ pr_usage
+ exit 1
+ fi
+ aggregate_interval=$2
+ shift 2
+ continue
+ ;;
+ "--update")
+ if [ $# -lt 2 ]
+ then
+ >&2 echo "<interval> not given"
+ pr_usage
+ exit 1
+ fi
+ regions_update_interval=$2
+ shift 2
+ continue
+ ;;
+ "--min_reg")
+ if [ $# -lt 2 ]
+ then
+ >&2 echo "<nr> not given"
+ pr_usage
+ exit 1
+ fi
+ min_nr_regions=$2
+ shift 2
+ continue
+ ;;
+ "--max_reg")
+ if [ $# -lt 2 ]
+ then
+ >&2 echo "<nr> not given"
+ pr_usage
+ exit 1
+ fi
+ max_nr_regions=$2
+ shift 2
+ continue
+ ;;
+ *)
+ if [ $# -lt 1 ]
+ then
+ >&2 echo "<command> not given"
+ pr_usage
+ exit 1
+ fi
+ cmd="$*"
+ break
+ ;;
+ esac
+done
+
+if [ -z "$cmd" ]
+then
+ pr_usage
+ exit 1
+fi
+
+orig_attrs=$(cat "$damon_dir/attrs")
+attrs="$sampling_interval $aggregate_interval $regions_update_interval"
+attrs="$attrs $min_nr_regions $max_nr_regions"
+
+echo "$attrs" > "$damon_dir/attrs"
+
+$cmd &
+cmd_pid=$!
+
+echo "$cmd_pid" > "$damon_dir/target_ids"
+echo "on" > "$damon_dir/monitor_on"
+
+perf record -e damon:damon_aggregated &
+perf_pid=$!
+
+sigint_trap()
+{
+ kill 2 "$cmd_pid"
+ kill 2 "$perf_pid"
+ echo "$orig_attrs" > "$damon_dir/attrs"
+ exit
+}
+
+trap sigint_trap INT
+
+>&2 echo "Press Control+C to stop recording"
+
+while :;
+do
+ on_off=$(cat "$damon_dir/monitor_on")
+ if [ "$on_off" = "off" ]
+ then
+ kill 2 $perf_pid
+ echo "$orig_attrs" > "$damon_dir/attrs"
+ break
+ fi
+ sleep 1
+done
diff --git a/tools/perf/scripts/python/bin/damon-report b/tools/perf/scripts/python/bin/damon-report
new file mode 100644
index 000000000000..89ece171959e
--- /dev/null
+++ b/tools/perf/scripts/python/bin/damon-report
@@ -0,0 +1,4 @@
+#!/bin/bash
+# description: data access monitoring
+
+perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/damon.py
diff --git a/tools/perf/scripts/python/damon.py b/tools/perf/scripts/python/damon.py
new file mode 100644
index 000000000000..b71a9bdf00e7
--- /dev/null
+++ b/tools/perf/scripts/python/damon.py
@@ -0,0 +1,44 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Author: SeongJae Park <sjpark@xxxxxxxxx>
+
+from __future__ import print_function
+
+import os
+import sys
+
+sys.path.append(os.environ['PERF_EXEC_PATH'] + \
+ '/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
+
+from perf_trace_context import *
+from Core import *
+
+
+def trace_begin():
+ pass
+
+def trace_end():
+ pass
+
+start_time = None
+nr_printed = 0
+def damon__damon_aggregated(event_name, context, common_cpu,
+ common_secs, common_nsecs, common_pid, common_comm,
+ common_callchain, target_id, nr_regions, start, end,
+ nr_accesses, perf_sample_dict):
+ global start_time
+ global nr_printed
+ time = common_secs * 1000000000 + common_nsecs
+ if not start_time:
+ start_time = time
+ print('start_time: %d' % start_time)
+ if nr_printed == 0:
+ print('rel time: %d' % (time - start_time))
+ print('target_id: %d' % target_id)
+ print('nr_regions: %d' % nr_regions)
+ print('%x-%x (%d): %u' % (start, end, end - start, nr_accesses))
+
+ nr_printed += 1
+ if nr_printed == nr_regions:
+ nr_printed = 0
+ print()
--
2.17.1