* [RFC PATCH 0/2] damo: support perf event configuration
@ 2026-06-22 13:49 Akinobu Mita
2026-06-22 13:49 ` [RFC PATCH 1/2] perf python: Add access to various members of evsel Akinobu Mita
` (2 more replies)
0 siblings, 3 replies; 7+ messages in thread
From: Akinobu Mita @ 2026-06-22 13:49 UTC (permalink / raw)
To: damon; +Cc: linux-perf-users, sj, ravis.opensrc, akinobu.mita
This patch set aims to make it easier to specify the perf event settings
proposed for DAMON by Ravi's hardware-sampled access reports patch set [1]
from the damo tool.
It consists of patches for the perf tool and the damo tool.
[1] https://lore.kernel.org/damon/20260529165640.820-1-ravis.opensrc@gmail.com/
* perf
Akinobu Mita (1):
perf python: Add access to various members of evsel
tools/perf/util/python.c | 50 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 50 insertions(+)
* damo
Akinobu Mita (1):
damo: add --perf_event option
src/_damo_fmt_str.py | 5 ++
src/_damon.py | 106 +++++++++++++++++++++++++++++++++-
src/_damon_args.py | 79 +++++++++++++++++++++++--
src/_damon_sysfs.py | 133 ++++++++++++++++++++++++++++++++++++++++++-
4 files changed, 314 insertions(+), 9 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [RFC PATCH 1/2] perf python: Add access to various members of evsel
2026-06-22 13:49 [RFC PATCH 0/2] damo: support perf event configuration Akinobu Mita
@ 2026-06-22 13:49 ` Akinobu Mita
2026-06-23 18:10 ` Ian Rogers
2026-06-22 13:49 ` [RFC PATCH 2/2] damo: add --perf_event option Akinobu Mita
2026-06-22 15:24 ` [RFC PATCH 0/2] damo: support perf event configuration SeongJae Park
2 siblings, 1 reply; 7+ messages in thread
From: Akinobu Mita @ 2026-06-22 13:49 UTC (permalink / raw)
To: damon; +Cc: linux-perf-users, sj, ravis.opensrc, akinobu.mita
This change is necessary to specify the same PMU event selection as the
'perf record' -e option from DAMON's userspace tools.
For example, a Python script like the following will allow you to obtain
the values to be set in the type, config, config1, and config2 members of
perf_event_attr by providing a symbolic event name.
import perf
if __name__ == '__main__':
evlist = perf.parse_events("cpu/mem-loads,ldlat=30/P")
for evsel in evlist:
print(f"{evsel}: type={evsel.type} config={evsel.config}",
f"config1={evsel.config1} config2={evsel.config2}")
Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com>
---
tools/perf/util/python.c | 50 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 50 insertions(+)
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c
index cc1019d29a5d..3f108b405ee3 100644
--- a/tools/perf/util/python.c
+++ b/tools/perf/util/python.c
@@ -1241,11 +1241,60 @@ static PyMemberDef pyrf_evsel__members[] = {
evsel_attr_member_def(sample_type, T_ULONGLONG, "attribute sample_type."),
evsel_attr_member_def(read_format, T_ULONGLONG, "attribute read_format."),
evsel_attr_member_def(wakeup_events, T_UINT, "attribute wakeup_events."),
+ evsel_attr_member_def(config1, T_ULONGLONG, "attribute config1."),
+ evsel_attr_member_def(config2, T_ULONGLONG, "attribute config2."),
{ .name = NULL, },
};
static const char pyrf_evsel__doc[] = PyDoc_STR("perf event selector list object.");
+static PyObject *pyrf_evsel__get_freq(struct pyrf_evsel *pevsel, void *closure)
+{
+ return PyLong_FromLong(pevsel->evsel.core.attr.freq);
+}
+
+static PyObject *pyrf_evsel__get_exclude_kernel(struct pyrf_evsel *pevsel,
+ void *closure)
+{
+ return PyLong_FromLong(pevsel->evsel.core.attr.exclude_kernel);
+}
+
+static PyObject *pyrf_evsel__get_exclude_hv(struct pyrf_evsel *pevsel,
+ void *closure)
+{
+ return PyLong_FromLong(pevsel->evsel.core.attr.exclude_hv);
+}
+
+static PyObject *pyrf_evsel__get_precise_ip(struct pyrf_evsel *pevsel,
+ void *closure)
+{
+ return PyLong_FromLong(pevsel->evsel.core.attr.precise_ip);
+}
+
+static PyGetSetDef pyrf_evsel__getset[] = {
+ {
+ .name = "freq",
+ .get = (getter)pyrf_evsel__get_freq,
+ .doc = PyDoc_STR("freq"),
+ },
+ {
+ .name = "exclude_kernel",
+ .get = (getter)pyrf_evsel__get_exclude_kernel,
+ .doc = PyDoc_STR("exclude_kernel"),
+ },
+ {
+ .name = "exclude_hv",
+ .get = (getter)pyrf_evsel__get_exclude_hv,
+ .doc = PyDoc_STR("exclude_hv"),
+ },
+ {
+ .name = "precise_ip",
+ .get = (getter)pyrf_evsel__get_precise_ip,
+ .doc = PyDoc_STR("precise_ip"),
+ },
+ { .name = NULL, },
+};
+
static PyTypeObject pyrf_evsel__type = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "perf.evsel",
@@ -1255,6 +1304,7 @@ static PyTypeObject pyrf_evsel__type = {
.tp_doc = pyrf_evsel__doc,
.tp_members = pyrf_evsel__members,
.tp_methods = pyrf_evsel__methods,
+ .tp_getset = pyrf_evsel__getset,
.tp_init = (initproc)pyrf_evsel__init,
.tp_str = pyrf_evsel__str,
.tp_repr = pyrf_evsel__str,
--
2.43.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [RFC PATCH 2/2] damo: add --perf_event option
2026-06-22 13:49 [RFC PATCH 0/2] damo: support perf event configuration Akinobu Mita
2026-06-22 13:49 ` [RFC PATCH 1/2] perf python: Add access to various members of evsel Akinobu Mita
@ 2026-06-22 13:49 ` Akinobu Mita
2026-06-22 15:24 ` [RFC PATCH 0/2] damo: support perf event configuration SeongJae Park
2 siblings, 0 replies; 7+ messages in thread
From: Akinobu Mita @ 2026-06-22 13:49 UTC (permalink / raw)
To: damon; +Cc: linux-perf-users, sj, ravis.opensrc, akinobu.mita
The argument for the newly added --perf_event can be the same PMU event as
the 'perf record' -e option.
The appropriate PMU event can be found in the following way:
```
$ sudo perf mem record sleep 1
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.030 MB perf.data (12 samples) ]
$ sudo perf evlist
cpu/mem-loads,ldlat=30/P
cpu/mem-stores/P
```
In this case, you can set up perf events by starting damo as follows:
```
$ sudo damo start \
--perf_event "cpu/mem-loads,ldlat=30,freq=5000/P" \
--perf_event "cpu/mem-stores,freq=5000/P" \
...
```
In this example, the sampling frequency is also specified by the common
parameter 'freq'.
Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com>
---
src/_damo_fmt_str.py | 5 ++
src/_damon.py | 106 +++++++++++++++++++++++++++++++++-
src/_damon_args.py | 79 +++++++++++++++++++++++--
src/_damon_sysfs.py | 133 ++++++++++++++++++++++++++++++++++++++++++-
4 files changed, 314 insertions(+), 9 deletions(-)
diff --git a/src/_damo_fmt_str.py b/src/_damo_fmt_str.py
index 6c51e24a..d93d5ba8 100644
--- a/src/_damo_fmt_str.py
+++ b/src/_damo_fmt_str.py
@@ -257,6 +257,11 @@ def text_to_nr(txt):
pass
return float(new_txt)
+def text_to_hex(txt):
+ if type(txt) == int:
+ return txt
+ return int(txt, 16)
+
def try_common_input(txt, min_val=0, max_val=ulong_max):
'return success and number'
if txt == 'min':
diff --git a/src/_damon.py b/src/_damon.py
index 8cfc61e3..52653fc5 100644
--- a/src/_damon.py
+++ b/src/_damon.py
@@ -261,6 +261,93 @@ class DamonNrRegionsRange:
('max', _damo_fmt_str.format_nr(self.maximum, raw)),
])
+class DamonPerfEvent:
+ attr_type = None
+ config = None
+ config1 = None
+ config2 = None
+ freq = None
+ sample_period = None
+ sample_freq = None
+ sample_phys_addr = None
+ sample_weight_struct = None
+ exclude_kernel = None
+ exclude_hv = None
+ precise_ip = None
+ wakeup_events = None
+
+ def __init__(self, attr_type='0x0', config='0x0', config1='0x0',
+ config2='0x0', freq=1, sample_period=0, sample_freq=0,
+ sample_phys_addr=0, sample_weight_struct=0, exclude_kernel=0,
+ exclude_hv=0, precise_ip=2, wakeup_events=0):
+ self.attr_type = _damo_fmt_str.text_to_hex(attr_type)
+ self.config = _damo_fmt_str.text_to_hex(config)
+ self.config1 = _damo_fmt_str.text_to_hex(config1)
+ self.config2 = _damo_fmt_str.text_to_hex(config2)
+ self.freq = _damo_fmt_str.text_to_nr(freq)
+ self.sample_period = _damo_fmt_str.text_to_nr(sample_period)
+ self.sample_freq = _damo_fmt_str.text_to_nr(sample_freq)
+ self.sample_phys_addr = _damo_fmt_str.text_to_nr(sample_phys_addr)
+ self.sample_weight_struct = _damo_fmt_str.text_to_nr(sample_weight_struct)
+ self.exclude_kernel = _damo_fmt_str.text_to_nr(exclude_kernel)
+ self.exclude_hv = _damo_fmt_str.text_to_nr(exclude_hv)
+ self.precise_ip = _damo_fmt_str.text_to_nr(precise_ip)
+ self.wakeup_events = _damo_fmt_str.text_to_nr(wakeup_events)
+
+ def to_str(self, raw):
+ lines = []
+ lines.append('type: %#x' % self.attr_type)
+ lines.append('config: [%#x, %#x, %#x]' % self.config, self.config1, self.config2)
+ lines.append('freq: %s' % _damo_fmt_str.format_nr(self.freq, raw))
+ lines.append('sample_period: %s' % _damo_fmt_str.format_nr(self.sample_period, raw))
+ lines.append('sample_freq: %s' % _damo_fmt_str.format_nr(self.sample_freq, raw))
+ lines.append('sample_phys_addr: %s' % _damo_fmt_str.format_nr(self.sample_phys_addr, raw))
+ lines.append('sample_weight_struct: %s' % _damo_fmt_str.format_nr(self.sample_weight_struct, raw))
+ lines.append('exclude_kernel: %s' % _damo_fmt_str.format_nr(self.exclude_kernel, raw))
+ lines.append('exclude_hv: %s' % _damo_fmt_str.format_nr(self.exclude_hv, raw))
+ lines.append('precise_ip: %s' % _damo_fmt_str.format_nr(self.precise_ip, raw))
+ lines.append('wakeup_events: %s' % _damo_fmt_str.format_nr(self.wakeup_events, raw))
+ return '\n'.join(lines)
+
+ def __str__(self):
+ return self.to_str(False)
+
+ def __eq__(self, other):
+ return type(self) == type(other) and '%s' % self == '%s' % other
+
+ @classmethod
+ def from_kvpairs(cls, kvpairs):
+ return DamonPerfEvent(attr_type=kvpairs['type'],
+ config=kvpairs['config'],
+ config1=kvpairs['config1'],
+ config2=kvpairs['config2'],
+ freq=kvpairs['freq'],
+ sample_period=kvpairs['sample_period'],
+ sample_freq=kvpairs['sample_freq'],
+ sample_phys_addr=kvpairs['sample_phys_addr'],
+ sample_weight_struct=kvpairs['sample_weight_struct'],
+ exclude_kernel=kvpairs['exclude_kernel'],
+ exclude_hv=kvpairs['exclude_hv'],
+ precise_ip=kvpairs['precise_ip'],
+ wakeup_events=kvpairs['wakeup_events'])
+
+ def to_kvpairs(self, raw=False):
+ return collections.OrderedDict([
+ ('type', '%#x' % self.attr_type),
+ ('config', '%#x' % self.config),
+ ('config1', '%#x' % self.config1),
+ ('config2', '%#x' % self.config2),
+ ('freq', _damo_fmt_str.format_nr(self.freq, raw)),
+ ('sample_period', _damo_fmt_str.format_nr(self.sample_period, raw)),
+ ('sample_freq', _damo_fmt_str.format_nr(self.sample_freq, raw)),
+ ('sample_phys_addr', _damo_fmt_str.format_nr(self.sample_phys_addr, raw)),
+ ('sample_weight_struct', _damo_fmt_str.format_nr(self.sample_weight_struct, raw)),
+ ('exclude_kernel', _damo_fmt_str.format_nr(self.exclude_kernel, raw)),
+ ('exclude_hv', _damo_fmt_str.format_nr(self.exclude_hv, raw)),
+ ('precise_ip', _damo_fmt_str.format_nr(self.precise_ip, raw)),
+ ('wakeup_events', _damo_fmt_str.format_nr(self.wakeup_events, raw)),
+ ])
+
damon_filter_type_cpumask = 'cpumask'
damon_filter_type_threads = 'threads'
damon_filter_type_write = 'write'
@@ -359,14 +446,18 @@ class DamonPrimitivesEnabled:
class DamonSampleControl:
primitives_enabled = None
sample_filters = None
+ perf_events = None
- def __init__(self, primitives_enabled=None, sample_filters=None):
+ def __init__(self, primitives_enabled=None, sample_filters=None, perf_events=None):
if primitives_enabled is None:
primitives_enabled = DamonPrimitivesEnabled()
if sample_filters is None:
sample_filters = []
+ if perf_events is None:
+ perf_events = []
self.primitives_enabled = primitives_enabled
self.sample_filters = sample_filters
+ self.perf_events = perf_events
def to_str(self, raw):
lines = [
@@ -375,6 +466,10 @@ class DamonSampleControl:
lines.append('Filters')
for filter in self.sample_filters:
lines.append('- %s' % filter.to_str(raw))
+ if len(self.perf_events) > 0:
+ lines.append('PerfEvents')
+ for perf_event in self.perf_events:
+ lines.append('- %s' % perf_event.to_str(raw))
return '\n'.join(lines)
def __str__(self):
@@ -383,7 +478,8 @@ class DamonSampleControl:
def __eq__(self, other):
return type(self) == type(other) and \
self.primitives_enabled == other.primitives_enabled and \
- self.sample_filters == other.sample_filters
+ self.sample_filters == other.sample_filters and \
+ self.perf_events == other.perf_events
@classmethod
def from_kvpairs(cls, kv):
@@ -391,13 +487,17 @@ class DamonSampleControl:
primitives_enabled=DamonPrimitivesEnabled.from_kvpairs(
kv['primitives_enabled']),
sample_filters=[DamonSampleFilter.from_kvpairs(kvpairs)
- for kvpairs in kv['sample_filters']])
+ for kvpairs in kv['sample_filters']],
+ perf_events=[DamonPerfEvent.from_kvpairs(p)
+ for p in kv['perf_events']])
def to_kvpairs(self, raw=False):
return collections.OrderedDict([
('primitives_enabled', self.primitives_enabled.to_kvpairs(raw)),
('sample_filters', [
f.to_kvpairs(raw) for f in self.sample_filters]),
+ ('perf_events', [
+ p.to_kvpairs(raw) for p in self.perf_events])
])
unit_percent = 'percent'
diff --git a/src/_damon_args.py b/src/_damon_args.py
index c85e99be..8a652058 100644
--- a/src/_damon_args.py
+++ b/src/_damon_args.py
@@ -18,6 +18,12 @@ except ModuleNotFoundError:
# properly.
pass
+try:
+ import perf
+except:
+ print('Please install perf.cpython-*-linux-gnu.so', file=sys.stderr)
+ raise
+
import _damo_subproc
import _damo_sysinfo
import _damon
@@ -110,6 +116,33 @@ def damon_nr_regions_range_for(args_range, args_minr, args_maxr):
override_vals(nr_range, [args_minr, args_maxr])
return _damon.DamonNrRegionsRange(*nr_range)
+def damon_perf_events_for(args_perf_event, args_ops):
+ events = []
+ if args_perf_event:
+ for event in args_perf_event:
+ sample_phys_addr = 1 if args_ops == 'paddr' else 0
+ evlist = perf.parse_events(event[0])
+ evlist.config()
+ if len(evlist) != 1:
+ raise Exception('%s event is ambiguous' % event[0])
+ evsel = evlist[0]
+ sample_period = evsel.sample_period if evsel.freq == 0 else 0
+ sample_freq = evsel.sample_period if evsel.freq == 1 else 0
+ events.append(_damon.DamonPerfEvent(
+ attr_type=evsel.type,
+ config=evsel.config,
+ config1=evsel.config1,
+ config2=evsel.config2,
+ freq=evsel.freq,
+ sample_period=sample_period,
+ sample_freq=sample_freq,
+ sample_phys_addr=sample_phys_addr,
+ exclude_kernel=evsel.exclude_kernel,
+ exclude_hv=evsel.exclude_hv,
+ precise_ip=evsel.precise_ip,
+ ))
+ return events
+
def schemes_option_to_damos(schemes):
if os.path.isfile(schemes):
with open(schemes, 'r') as f:
@@ -540,10 +573,14 @@ def sample_control_to_ops_attrs_args(args, idx):
return '--sample_primitives of %s is not supported for now' % \
sample_primitives
-def build_sample_control_ops_attrs(args, idx):
+def build_sample_control_ops_attrs(args, idx, args_perf_event):
'''
Returns DamonSampleControl, OpsAttrs, and an error
'''
+ try:
+ perf_events = damon_perf_events_for(args_perf_event, args.ops[idx])
+ except Exception as e:
+ return None, None, 'invalid perf_event arguments (%s)' % e
err = sample_control_to_ops_attrs_args(args, idx)
if err is not None:
return None, None, err
@@ -595,10 +632,11 @@ def build_sample_control_ops_attrs(args, idx):
allow=True, tid_arr=tids))
sample_control = _damon.DamonSampleControl(
primitives_enabled=primitives_enabled,
- sample_filters=sample_filters)
+ sample_filters=sample_filters,
+ perf_events=perf_events)
return sample_control, None, None
-def damon_ctx_for(args, idx):
+def damon_ctx_for(args, idx, args_perf_event):
if args.ops[idx] is None:
if args.target_pid[idx] is None:
args.ops[idx] = 'paddr'
@@ -620,7 +658,7 @@ def damon_ctx_for(args, idx):
except Exception as e:
return None, 'invalid nr_regions arguments (%s)' % e
ops = args.ops[idx]
- sample_control, ops_attrs, err = build_sample_control_ops_attrs(args, idx)
+ sample_control, ops_attrs, err = build_sample_control_ops_attrs(args, idx, args_perf_event)
if err is not None:
return None, 'exp_ops_* handling fail (%s)' % err
pause = False
@@ -836,12 +874,35 @@ def gen_assign_probes(ctxs, args):
probe_idx += nr
return None
+def gen_assign_perf_events(args_perf_events, args):
+ nr_ctxs = get_nr_ctxs(args)
+ if args.nr_perf_events is None:
+ if nr_ctxs != 1 and len(args.perf_event) > 0:
+ return '--nr_perf_events is required'
+ args.nr_perf_events = [len(args.perf_event)]
+ args.nr_perf_events += [0] * (nr_ctxs - 1)
+ if sum(args.nr_perf_events) != len(args.perf_event):
+ return '--nr_perf_events mismatches number of perf_events (%d != %d)' % (
+ sum(args.nr_perf_events), len(args.perf_event))
+ if len(args.nr_perf_events) != nr_ctxs:
+ return '--nr_perf_events mismatches number of contexts (%d != %d)' % (
+ len(args.nr_perf_events), nr_ctxs)
+ perf_event_idx = 0
+ for nr in args.nr_perf_events:
+ args_perf_events.append(args.perf_event[perf_event_idx:perf_event_idx + nr])
+ perf_event_idx += nr
+ return None
+
def damon_ctxs_for(args):
fillup_none_ctx_args(args)
fillup_none_target_args(args)
+ args_perf_events = []
+ err = gen_assign_perf_events(args_perf_events, args)
+ if err is not None:
+ return None, err
ctxs = []
for idx in range(get_nr_ctxs(args)):
- ctx, err = damon_ctx_for(args, idx)
+ ctx, err = damon_ctx_for(args, idx, args_perf_events[idx])
if err is not None:
return None, err
ctxs.append(ctx)
@@ -1368,6 +1429,14 @@ def set_monitoring_attrs_argparser(parser, hide_help=False):
choices=['page_table', 'page_fault'], nargs='+',
help='access sampling primitives to use'
if not hide_help else argparse.SUPPRESS)
+ parser.add_argument('--perf_event', nargs='+',
+ metavar=('<event>'),
+ default=[], action='append',
+ help='monitoring perf_event'
+ if not hide_help else argparse.SUPPRESS)
+ parser.add_argument('--nr_perf_events', type=int, metavar='<number>', nargs='+',
+ help='number of perf events for each context (in order)'
+ if not hide_help else argparse.SUPPRESS)
def set_monitoring_damos_common_args(parser, hide_help=False):
parser.add_argument('--ops', choices=['vaddr', 'paddr', 'fvaddr'],
diff --git a/src/_damon_sysfs.py b/src/_damon_sysfs.py
index 387e6f96..ccff3e30 100644
--- a/src/_damon_sysfs.py
+++ b/src/_damon_sysfs.py
@@ -616,6 +616,102 @@ def write_probes_dir(dir_path, probes):
return err
return None
+def write_perf_event_dir(dir_path, perf_event):
+ err = _damo_fs.write_file(
+ os.path.join(dir_path, 'type'),
+ '%#x' % perf_event.attr_type)
+ if err is not None:
+ return err
+
+ err = _damo_fs.write_file(
+ os.path.join(dir_path, 'config'),
+ '%#x' % perf_event.config)
+ if err is not None:
+ return err
+
+ err = _damo_fs.write_file(
+ os.path.join(dir_path, 'config1'),
+ '%#x' % perf_event.config1)
+ if err is not None:
+ return err
+
+ err = _damo_fs.write_file(
+ os.path.join(dir_path, 'config2'),
+ '%#x' % perf_event.config2)
+ if err is not None:
+ return err
+
+ freq_path = os.path.join(dir_path, 'freq')
+ if os.path.isfile(freq_path):
+ err = _damo_fs.write_file(freq_path, '%d' % perf_event.freq)
+ if err is not None:
+ return err
+
+ sample_period_path = os.path.join(dir_path, 'sample_period')
+ if os.path.isfile(sample_period_path):
+ err = _damo_fs.write_file(sample_period_path,
+ '%d' % perf_event.sample_period)
+ if err is not None:
+ return err
+
+ err = _damo_fs.write_file(
+ os.path.join(dir_path, 'sample_freq'),
+ '%d' % perf_event.sample_freq)
+ if err is not None:
+ return err
+
+ err = _damo_fs.write_file(
+ os.path.join(dir_path, 'sample_phys_addr'),
+ '%d' % perf_event.sample_phys_addr)
+ if err is not None:
+ return err
+
+ sample_weight_struct_path = os.path.join(dir_path, 'sample_weight_struct')
+ if os.path.isfile(sample_weight_struct_path):
+ err = _damo_fs.write_file(sample_weight_struct_path,
+ '%d' % perf_event.sample_weight_struct)
+ if err is not None:
+ return err
+
+ exclude_kernel_path = os.path.join(dir_path, 'exclude_kernel')
+ if os.path.isfile(exclude_kernel_path):
+ err = _damo_fs.write_file(exclude_kernel_path,
+ '%d' % perf_event.exclude_kernel)
+ if err is not None:
+ return err
+
+ exclude_hv_path = os.path.join(dir_path, 'exclude_hv')
+ if os.path.isfile(exclude_hv_path):
+ err = _damo_fs.write_file(exclude_hv_path,
+ '%d' % perf_event.exclude_hv)
+ if err is not None:
+ return err
+
+ precise_ip_path = os.path.join(dir_path, 'precise_ip')
+ if os.path.isfile(precise_ip_path):
+ err = _damo_fs.write_file(precise_ip_path,
+ '%d' % perf_event.precise_ip)
+ if err is not None:
+ return err
+
+ wakeup_events_path = os.path.join(dir_path, 'wakeup_events')
+ if os.path.isfile(wakeup_events_path):
+ err = _damo_fs.write_file(wakeup_events_path,
+ '%d' % perf_event.wakeup_events)
+ if err is not None:
+ return err
+
+def write_perf_events_dir(dir_path, perf_events):
+ err = ensure_nr_file_for(os.path.join(dir_path, 'nr_perf_events'), perf_events)
+ if err is not None:
+ return err
+
+ for idx, perf_event in enumerate(perf_events):
+ err = write_perf_event_dir(os.path.join(dir_path, '%d' % idx), perf_event)
+ if err is not None:
+ return err
+ return None
+
def write_sample_filter_dir(dir_path, sample_filter):
err = _damo_fs.write_file(
os.path.join(dir_path, 'type'), sample_filter.filter_type)
@@ -677,6 +773,12 @@ def write_sample_control_dir(dir_path, sample_control):
'Y' if sample_control.primitives_enabled.page_fault else 'N')
if err is not None:
return err
+
+ err = write_perf_events_dir(
+ os.path.join(dir_path, 'perf_events'), sample_control.perf_events)
+ if err is not None:
+ return err
+
return write_sample_filters_dir(
os.path.join(dir_path, 'filters'), sample_control.sample_filters)
@@ -1094,9 +1196,15 @@ def files_content_to_sample_control(files_content):
primitives_enabled = _damon.DamonPrimitivesEnabled(
page_table=page_table, page_fault=page_fault)
sample_filters = files_content_to_sample_filters(files_content['filters'])
+
+ perf_events_content = files_content['perf_events']
+ perf_events = [files_content_to_perf_event(content)
+ for content in numbered_dirs_content(
+ perf_events_content, 'nr_perf_events')]
return _damon.DamonSampleControl(
primitives_enabled=primitives_enabled,
- sample_filters=sample_filters)
+ sample_filters=sample_filters,
+ perf_events=perf_events)
def files_content_to_ops_attrs(files_content):
use_reports = files_content['use_reports'].strip()
@@ -1106,6 +1214,29 @@ def files_content_to_ops_attrs(files_content):
return _damon.OpsAttrs(use_reports=use_reports, write_only=write_only,
cpus=cpus, tids=tids)
+def files_content_to_perf_event(files_content):
+ freq = int(files_content['freq']) if 'freq' in files_content else 1
+ sample_period = int(files_content['sample_period']) if 'sample_period' in files_content else 0
+ sample_weight_struct = int(files_content['sample_weight_struct']) if 'sample_weight_struct' in files_content else 0
+ exclude_kernel = int(files_content['exclude_kernel']) if 'exclude_kernel' in files_content else 0
+ exclude_hv = int(files_content['exclude_hv']) if 'exclude_hv' in files_content else 0
+ precise_ip = int(files_content['precise_ip']) if 'precise_ip' in files_content else 2
+ wakeup_events = int(files_content['wakeup_events']) if 'wakeup_events' in files_content else 0
+ return _damon.DamonPerfEvent(
+ attr_type=int(files_content['type'], 16),
+ config=int(files_content['config'], 16),
+ config1=int(files_content['config1'], 16),
+ config2=int(files_content['config2'], 16),
+ freq=freq,
+ sample_period=sample_period,
+ sample_freq=int(files_content['sample_freq']),
+ sample_phys_addr=int(files_content['sample_phys_addr']),
+ sample_weight_struct=sample_weight_struct,
+ exclude_kernel=exclude_kernel,
+ exclude_hv=exclude_hv,
+ precise_ip=precise_ip,
+ wakeup_events=wakeup_events)
+
def files_content_to_context(files_content):
mon_attrs_content = files_content['monitoring_attrs']
intervals_content = mon_attrs_content['intervals']
--
2.43.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [RFC PATCH 0/2] damo: support perf event configuration
2026-06-22 13:49 [RFC PATCH 0/2] damo: support perf event configuration Akinobu Mita
2026-06-22 13:49 ` [RFC PATCH 1/2] perf python: Add access to various members of evsel Akinobu Mita
2026-06-22 13:49 ` [RFC PATCH 2/2] damo: add --perf_event option Akinobu Mita
@ 2026-06-22 15:24 ` SeongJae Park
2026-06-23 13:29 ` Akinobu Mita
2 siblings, 1 reply; 7+ messages in thread
From: SeongJae Park @ 2026-06-22 15:24 UTC (permalink / raw)
To: Akinobu Mita; +Cc: SeongJae Park, damon, linux-perf-users, ravis.opensrc
Hello Akinobu,
On Mon, 22 Jun 2026 22:49:31 +0900 Akinobu Mita <akinobu.mita@gmail.com> wrote:
> This patch set aims to make it easier to specify the perf event settings
> proposed for DAMON by Ravi's hardware-sampled access reports patch set [1]
> from the damo tool.
Seems this patch is built on top of the per-CPU/thread/read/write monitoring
patch series [1]. We made a plan [2] to use another user interface, though.
The plan is, making the interface ready for page table accessed bits based
monitoring by LPC'26 (milestone 1), and implementing the perf event based
monitoring on top of it, by LSFMMBPF'27 (milestone 2).
So the eventual damo part change for supporting perf event based monitoring
will be quite different from this series. I guess you are sharing this series
not because you aiming to merge it as is right now, but just for sharing the
code with other stakeholders like Ravis, not me? If I'm not wrong, I will skip
this series for now, to focus on the milestone 1.
Please let me know if you want more of my feedback.
[1] https://lore.kernel.org/20251208062943.68824-1-sj@kernel.org
[2] https://lore.kernel.org/20260525225208.1179-1-sj@kernel.org/
Thanks,
SJ
[...]
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [RFC PATCH 0/2] damo: support perf event configuration
2026-06-22 15:24 ` [RFC PATCH 0/2] damo: support perf event configuration SeongJae Park
@ 2026-06-23 13:29 ` Akinobu Mita
2026-06-23 13:49 ` SeongJae Park
0 siblings, 1 reply; 7+ messages in thread
From: Akinobu Mita @ 2026-06-23 13:29 UTC (permalink / raw)
To: SeongJae Park; +Cc: damon, linux-perf-users, ravis.opensrc
2026年6月23日(火) 0:24 SeongJae Park <sj@kernel.org>:
>
> Hello Akinobu,
>
> On Mon, 22 Jun 2026 22:49:31 +0900 Akinobu Mita <akinobu.mita@gmail.com> wrote:
>
> > This patch set aims to make it easier to specify the perf event settings
> > proposed for DAMON by Ravi's hardware-sampled access reports patch set [1]
> > from the damo tool.
>
> Seems this patch is built on top of the per-CPU/thread/read/write monitoring
> patch series [1]. We made a plan [2] to use another user interface, though.
> The plan is, making the interface ready for page table accessed bits based
> monitoring by LPC'26 (milestone 1), and implementing the perf event based
> monitoring on top of it, by LSFMMBPF'27 (milestone 2).
>
> So the eventual damo part change for supporting perf event based monitoring
> will be quite different from this series. I guess you are sharing this series
> not because you aiming to merge it as is right now, but just for sharing the
> code with other stakeholders like Ravis, not me? If I'm not wrong, I will skip
> this series for now, to focus on the milestone 1.
Since I will align with the final user interface later, please focus on
the optimal interface for Milestone 1 without worrying about that.
> Please let me know if you want more of my feedback.
However, even if the interface ends up being completely different from the
current one, I believe that enabling configuration via `damo` using
symbolic event names like this would still require calling the perf
python interface (`import perf`) from damo and modifying the `perf` tool
itself; could you please check if that approach is acceptable?
Thank you for your continued excellent work on improving DAMON.
> [1] https://lore.kernel.org/20251208062943.68824-1-sj@kernel.org
> [2] https://lore.kernel.org/20260525225208.1179-1-sj@kernel.org/
>
>
> Thanks,
> SJ
>
> [...]
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [RFC PATCH 0/2] damo: support perf event configuration
2026-06-23 13:29 ` Akinobu Mita
@ 2026-06-23 13:49 ` SeongJae Park
0 siblings, 0 replies; 7+ messages in thread
From: SeongJae Park @ 2026-06-23 13:49 UTC (permalink / raw)
To: Akinobu Mita; +Cc: SeongJae Park, damon, linux-perf-users, ravis.opensrc
On Tue, 23 Jun 2026 22:29:42 +0900 Akinobu Mita <akinobu.mita@gmail.com> wrote:
> 2026年6月23日(火) 0:24 SeongJae Park <sj@kernel.org>:
> >
> > Hello Akinobu,
> >
> > On Mon, 22 Jun 2026 22:49:31 +0900 Akinobu Mita <akinobu.mita@gmail.com> wrote:
> >
> > > This patch set aims to make it easier to specify the perf event settings
> > > proposed for DAMON by Ravi's hardware-sampled access reports patch set [1]
> > > from the damo tool.
> >
> > Seems this patch is built on top of the per-CPU/thread/read/write monitoring
> > patch series [1]. We made a plan [2] to use another user interface, though.
> > The plan is, making the interface ready for page table accessed bits based
> > monitoring by LPC'26 (milestone 1), and implementing the perf event based
> > monitoring on top of it, by LSFMMBPF'27 (milestone 2).
> >
> > So the eventual damo part change for supporting perf event based monitoring
> > will be quite different from this series. I guess you are sharing this series
> > not because you aiming to merge it as is right now, but just for sharing the
> > code with other stakeholders like Ravis, not me? If I'm not wrong, I will skip
> > this series for now, to focus on the milestone 1.
>
> Since I will align with the final user interface later, please focus on
> the optimal interface for Milestone 1 without worrying about that.
Thank you, Akinobu.
>
> > Please let me know if you want more of my feedback.
>
> However, even if the interface ends up being completely different from the
> current one, I believe that enabling configuration via `damo` using
> symbolic event names like this would still require calling the perf
> python interface (`import perf`) from damo and modifying the `perf` tool
> itself; could you please check if that approach is acceptable?
I'm bit concerned at having such dependencies. I'd prefer making DAMON sysfs
interface uses a symbolic names and just use it from damo if possible. If that
is not possible, could you share more details about why such changes are
required? I'm still lazily delaying studying about perf events, so please bear
in mind with me.
>
> Thank you for your continued excellent work on improving DAMON.
Thank you for your grateful contributions, too!
Thanks,
SJ
[...]
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [RFC PATCH 1/2] perf python: Add access to various members of evsel
2026-06-22 13:49 ` [RFC PATCH 1/2] perf python: Add access to various members of evsel Akinobu Mita
@ 2026-06-23 18:10 ` Ian Rogers
0 siblings, 0 replies; 7+ messages in thread
From: Ian Rogers @ 2026-06-23 18:10 UTC (permalink / raw)
To: Akinobu Mita; +Cc: damon, linux-perf-users, sj, ravis.opensrc
On Mon, Jun 22, 2026 at 6:57 AM Akinobu Mita <akinobu.mita@gmail.com> wrote:
>
> This change is necessary to specify the same PMU event selection as the
> 'perf record' -e option from DAMON's userspace tools.
>
> For example, a Python script like the following will allow you to obtain
> the values to be set in the type, config, config1, and config2 members of
> perf_event_attr by providing a symbolic event name.
>
> import perf
>
> if __name__ == '__main__':
> evlist = perf.parse_events("cpu/mem-loads,ldlat=30/P")
> for evsel in evlist:
> print(f"{evsel}: type={evsel.type} config={evsel.config}",
> f"config1={evsel.config1} config2={evsel.config2}")
>
> Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com>
Thanks Akinobu! I agree we should expose more parts of the evsel
through the perf python API. I refactored this area recently so that
we could better support perf.data file usage (the session API).
There's a long series here that is merged enough to mean these changes
will need a refactor too:
https://lore.kernel.org/lkml/20260419235911.2186050-1-irogers@google.com/
https://web.git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next.git/tree/tools/perf/util/python.c?h=perf-tools-next#n2449
The main change I made was avoiding copying evsels in the python API,
as it adds many complications, and switching to reference counting.
This means the Python `evsel` can no longer directly access inner
variables and now requires an indirection. So in:
https://web.git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next.git/tree/tools/perf/util/python.c?h=perf-tools-next#n2290
```
static PyObject *pyrf_evsel__get_attr_config(PyObject *self, void
*closure __maybe_unused)
{
struct pyrf_evsel *pevsel = (void *)self;
CHECK_INITIALIZED(pevsel->evsel, "evsel");
return PyLong_FromUnsignedLongLong(pevsel->evsel->core.attr.config);
}
```
Note how it is `evsel->core` rather than `evsel.core` as it was before.
I believe you can copy-paste the code and tweak it for config1 and
config2, but I'm afraid the previous evsel_attr_member_def approach
won't work.
Thanks,
Ian
> ---
> tools/perf/util/python.c | 50 ++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 50 insertions(+)
>
> diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c
> index cc1019d29a5d..3f108b405ee3 100644
> --- a/tools/perf/util/python.c
> +++ b/tools/perf/util/python.c
> @@ -1241,11 +1241,60 @@ static PyMemberDef pyrf_evsel__members[] = {
> evsel_attr_member_def(sample_type, T_ULONGLONG, "attribute sample_type."),
> evsel_attr_member_def(read_format, T_ULONGLONG, "attribute read_format."),
> evsel_attr_member_def(wakeup_events, T_UINT, "attribute wakeup_events."),
> + evsel_attr_member_def(config1, T_ULONGLONG, "attribute config1."),
> + evsel_attr_member_def(config2, T_ULONGLONG, "attribute config2."),
> { .name = NULL, },
> };
>
> static const char pyrf_evsel__doc[] = PyDoc_STR("perf event selector list object.");
>
> +static PyObject *pyrf_evsel__get_freq(struct pyrf_evsel *pevsel, void *closure)
> +{
> + return PyLong_FromLong(pevsel->evsel.core.attr.freq);
> +}
> +
> +static PyObject *pyrf_evsel__get_exclude_kernel(struct pyrf_evsel *pevsel,
> + void *closure)
> +{
> + return PyLong_FromLong(pevsel->evsel.core.attr.exclude_kernel);
> +}
> +
> +static PyObject *pyrf_evsel__get_exclude_hv(struct pyrf_evsel *pevsel,
> + void *closure)
> +{
> + return PyLong_FromLong(pevsel->evsel.core.attr.exclude_hv);
> +}
> +
> +static PyObject *pyrf_evsel__get_precise_ip(struct pyrf_evsel *pevsel,
> + void *closure)
> +{
> + return PyLong_FromLong(pevsel->evsel.core.attr.precise_ip);
> +}
> +
> +static PyGetSetDef pyrf_evsel__getset[] = {
> + {
> + .name = "freq",
> + .get = (getter)pyrf_evsel__get_freq,
> + .doc = PyDoc_STR("freq"),
> + },
> + {
> + .name = "exclude_kernel",
> + .get = (getter)pyrf_evsel__get_exclude_kernel,
> + .doc = PyDoc_STR("exclude_kernel"),
> + },
> + {
> + .name = "exclude_hv",
> + .get = (getter)pyrf_evsel__get_exclude_hv,
> + .doc = PyDoc_STR("exclude_hv"),
> + },
> + {
> + .name = "precise_ip",
> + .get = (getter)pyrf_evsel__get_precise_ip,
> + .doc = PyDoc_STR("precise_ip"),
> + },
> + { .name = NULL, },
> +};
> +
> static PyTypeObject pyrf_evsel__type = {
> PyVarObject_HEAD_INIT(NULL, 0)
> .tp_name = "perf.evsel",
> @@ -1255,6 +1304,7 @@ static PyTypeObject pyrf_evsel__type = {
> .tp_doc = pyrf_evsel__doc,
> .tp_members = pyrf_evsel__members,
> .tp_methods = pyrf_evsel__methods,
> + .tp_getset = pyrf_evsel__getset,
> .tp_init = (initproc)pyrf_evsel__init,
> .tp_str = pyrf_evsel__str,
> .tp_repr = pyrf_evsel__str,
> --
> 2.43.0
>
>
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-06-23 18:10 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-22 13:49 [RFC PATCH 0/2] damo: support perf event configuration Akinobu Mita
2026-06-22 13:49 ` [RFC PATCH 1/2] perf python: Add access to various members of evsel Akinobu Mita
2026-06-23 18:10 ` Ian Rogers
2026-06-22 13:49 ` [RFC PATCH 2/2] damo: add --perf_event option Akinobu Mita
2026-06-22 15:24 ` [RFC PATCH 0/2] damo: support perf event configuration SeongJae Park
2026-06-23 13:29 ` Akinobu Mita
2026-06-23 13:49 ` SeongJae Park
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.