* [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; 4+ 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] 4+ 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-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, 0 replies; 4+ 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] 4+ 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; 4+ 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] 4+ 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
2 siblings, 0 replies; 4+ 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] 4+ messages in thread
end of thread, other threads:[~2026-06-22 15:24 UTC | newest]
Thread overview: 4+ 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-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
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox