Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf data: Allow filtering conversion by time range

This adds a feature to allow restricting the range of converted samples
with a range string like perf-script and perf-report --time.

Committer testing:

Put a probe on the ICMP receive path handling broadcast packets:

# perf probe icmp_rcv:64
Added new event:
probe:icmp_rcv_L64 (on icmp_rcv:64)

You can now use it in all perf tools, such as:

perf record -e probe:icmp_rcv_L64 -aR sleep 1

# perf record -e probe:icmp_rcv_L64 ping -c 10 -b 127.255.255.255
WARNING: pinging broadcast address
PING 127.255.255.255 (127.255.255.255) 56(84) bytes of data.
^C
--- 127.255.255.255 ping statistics ---
10 packets transmitted, 0 received, 100% packet loss, time 9217ms

[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.034 MB perf.data (10 samples) ]

# perf script
ping 52785 [009] 5847.300394: probe:icmp_rcv_L64: (ffffffffaadb337e)
ping 52785 [009] 5848.325018: probe:icmp_rcv_L64: (ffffffffaadb337e)
ping 52785 [009] 5849.349007: probe:icmp_rcv_L64: (ffffffffaadb337e)
ping 52785 [009] 5850.372979: probe:icmp_rcv_L64: (ffffffffaadb337e)
ping 52785 [009] 5851.396988: probe:icmp_rcv_L64: (ffffffffaadb337e)
ping 52785 [009] 5852.420954: probe:icmp_rcv_L64: (ffffffffaadb337e)
ping 52785 [009] 5853.444934: probe:icmp_rcv_L64: (ffffffffaadb337e)
ping 52785 [009] 5854.468926: probe:icmp_rcv_L64: (ffffffffaadb337e)
ping 52785 [009] 5855.492914: probe:icmp_rcv_L64: (ffffffffaadb337e)
ping 52785 [009] 5856.516883: probe:icmp_rcv_L64: (ffffffffaadb337e)
#

Now get some slices using perf script:

# perf script --time 40%
ping 52785 [009] 5847.300394: probe:icmp_rcv_L64: (ffffffffaadb337e)
ping 52785 [009] 5848.325018: probe:icmp_rcv_L64: (ffffffffaadb337e)
ping 52785 [009] 5849.349007: probe:icmp_rcv_L64: (ffffffffaadb337e)
ping 52785 [009] 5850.372979: probe:icmp_rcv_L64: (ffffffffaadb337e)
# perf script --time 40%-60%
ping 52785 [009] 5851.396988: probe:icmp_rcv_L64: (ffffffffaadb337e)
ping 52785 [009] 5852.420954: probe:icmp_rcv_L64: (ffffffffaadb337e)
#

And finally use this new feature:

# perf data convert --to-json out.json --time 0%-10%
[ perf data convert: Converted 'perf.data' into JSON data 'out.json' ]
[ perf data convert: Converted and wrote 0.001 MB (1 samples) ]
[ perf data convert: Skipped 9 samples ]
# cat out.json
{
"linux-perf-json-version": 1,
"headers": {
"header-version": 1,
"captured-on": "2026-01-06T22:26:40Z",
"data-offset": 520,
"data-size": 34648,
"feat-offset": 35168,
"hostname": "number",
"os-release": "6.17.12-300.fc43.x86_64",
"arch": "x86_64",
"cpu-desc": "AMD Ryzen 9 9950X3D 16-Core Processor",
"cpuid": "AuthenticAMD,26,68,0",
"nrcpus-online": 32,
"nrcpus-avail": 32,
"perf-version": "6.19.rc4.gf4c270685d3d",
"cmdline": [
"/home/acme/bin/perf"
]
},
"samples": [
{
"timestamp": 5847300394661,
"pid": 52785,
"tid": 52785,
"cpu": 9,
"comm": "ping",
"callchain": [
{
"ip": "0xffffffffaadb337f",
"symbol": "icmp_rcv",
"dso": "[kernel.kallsyms]"
}
],
"__probe_ip": "ffffffffaadb337e"
}
]
}
#

Signed-off-by: Derek Foreman <derek.foreman@collabora.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Ian Rogers <irogers@google.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Clark <james.clark@linaro.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Derek Foreman and committed by
Arnaldo Carvalho de Melo
8e746e95 523471c5

+96
+28
tools/perf/Documentation/perf-data.txt
··· 40 40 --force:: 41 41 Don't complain, do it. 42 42 43 + --time:: 44 + Only convert samples within given time window: <start>,<stop>. Times 45 + have the format seconds.nanoseconds. If start is not given (i.e. time 46 + string is ',x.y') then analysis starts at the beginning of the file. If 47 + stop time is not given (i.e. time string is 'x.y,') then analysis goes 48 + to end of file. Multiple ranges can be separated by spaces, which 49 + requires the argument to be quoted e.g. --time "1234.567,1234.789 1235," 50 + 51 + Also support time percent with multiple time ranges. Time string is 52 + 'a%/n,b%/m,...' or 'a%-b%,c%-%d,...'. 53 + 54 + For example: 55 + Select the second 10% time slice: 56 + 57 + perf data convert --to-json out.json --time 10%/2 58 + 59 + Select from 0% to 10% time slice: 60 + 61 + perf data convert --to-json out.json --time 0%-10% 62 + 63 + Select the first and second 10% time slices: 64 + 65 + perf data convert --to-json out.json --time 10%/1,10%/2 66 + 67 + Select from 0% to 10% and 30% to 40% slices: 68 + 69 + perf data convert --to-json out.json --time 0%-10%,30%-40% 70 + 43 71 -v:: 44 72 --verbose:: 45 73 Be more verbose (show counter open errors, etc).
+3
tools/perf/builtin-data.c
··· 33 33 struct perf_data_convert_opts opts = { 34 34 .force = false, 35 35 .all = false, 36 + .time_str = NULL, 36 37 }; 37 38 38 39 const struct option data_options[] = { ··· 46 45 #endif 47 46 OPT_BOOLEAN('f', "force", &opts.force, "don't complain, do it"), 48 47 OPT_BOOLEAN(0, "all", &opts.all, "Convert all events"), 48 + OPT_STRING(0, "time", &opts.time_str, "str", 49 + "Time span of interest (start,stop)"), 49 50 OPT_END() 50 51 }; 51 52
+31
tools/perf/util/data-convert-bt.c
··· 34 34 #include "util.h" 35 35 #include "clockid.h" 36 36 #include "util/sample.h" 37 + #include "util/time-utils.h" 37 38 38 39 #ifdef HAVE_LIBTRACEEVENT 39 40 #include <event-parse.h> ··· 92 91 struct perf_tool tool; 93 92 struct ctf_writer writer; 94 93 94 + struct perf_time_interval *ptime_range; 95 + int range_size; 96 + int range_num; 97 + 95 98 u64 events_size; 96 99 u64 events_count; 97 100 u64 non_sample_count; 101 + u64 skipped; 98 102 99 103 /* Ordered events configured queue size. */ 100 104 u64 queue_size; ··· 816 810 817 811 if (WARN_ONCE(!priv, "Failed to setup all events.\n")) 818 812 return 0; 813 + 814 + if (perf_time__ranges_skip_sample(c->ptime_range, c->range_num, sample->time)) { 815 + ++c->skipped; 816 + return 0; 817 + } 819 818 820 819 event_class = priv->event_class; 821 820 ··· 1655 1644 if (IS_ERR(session)) 1656 1645 return PTR_ERR(session); 1657 1646 1647 + if (opts->time_str) { 1648 + err = perf_time__parse_for_ranges(opts->time_str, session, 1649 + &c.ptime_range, 1650 + &c.range_size, 1651 + &c.range_num); 1652 + if (err < 0) 1653 + goto free_session; 1654 + } 1655 + 1658 1656 /* CTF writer */ 1659 1657 if (ctf_writer__init(cw, path, session, opts->tod)) 1660 1658 goto free_session; ··· 1707 1687 else 1708 1688 fprintf(stderr, ", %" PRIu64 " non-samples) ]\n", c.non_sample_count); 1709 1689 1690 + if (c.skipped) { 1691 + fprintf(stderr, "[ perf data convert: Skipped %" PRIu64 " samples ]\n", 1692 + c.skipped); 1693 + } 1694 + 1695 + if (c.ptime_range) 1696 + zfree(&c.ptime_range); 1697 + 1710 1698 cleanup_events(session); 1711 1699 perf_session__delete(session); 1712 1700 ctf_writer__cleanup(cw); ··· 1724 1696 free_writer: 1725 1697 ctf_writer__cleanup(cw); 1726 1698 free_session: 1699 + if (c.ptime_range) 1700 + zfree(&c.ptime_range); 1701 + 1727 1702 perf_session__delete(session); 1728 1703 pr_err("Error during conversion setup.\n"); 1729 1704 return err;
+33
tools/perf/util/data-convert-json.c
··· 25 25 #include "util/session.h" 26 26 #include "util/symbol.h" 27 27 #include "util/thread.h" 28 + #include "util/time-utils.h" 28 29 #include "util/tool.h" 29 30 30 31 #ifdef HAVE_LIBTRACEEVENT ··· 36 35 struct perf_tool tool; 37 36 FILE *out; 38 37 bool first; 38 + struct perf_time_interval *ptime_range; 39 + int range_size; 40 + int range_num; 41 + 39 42 u64 events_count; 43 + u64 skipped; 40 44 }; 41 45 42 46 // Outputs a JSON-encoded string surrounded by quotes with characters escaped. ··· 169 163 pr_err("Sample resolution failed!\n"); 170 164 addr_location__exit(&al); 171 165 return -1; 166 + } 167 + 168 + if (perf_time__ranges_skip_sample(c->ptime_range, c->range_num, sample->time)) { 169 + ++c->skipped; 170 + return 0; 172 171 } 173 172 174 173 ++c->events_count; ··· 331 320 struct convert_json c = { 332 321 .first = true, 333 322 .events_count = 0, 323 + .ptime_range = NULL, 324 + .range_size = 0, 325 + .range_num = 0, 326 + .skipped = 0, 334 327 }; 335 328 struct perf_data data = { 336 329 .mode = PERF_DATA_MODE_READ, ··· 397 382 goto err_session_delete; 398 383 } 399 384 385 + if (opts->time_str) { 386 + ret = perf_time__parse_for_ranges(opts->time_str, session, 387 + &c.ptime_range, 388 + &c.range_size, 389 + &c.range_num); 390 + if (ret < 0) 391 + goto err_session_delete; 392 + } 393 + 400 394 // The opening brace is printed manually because it isn't delimited from a 401 395 // previous value (i.e. we don't want a leading newline) 402 396 fputc('{', c.out); ··· 435 411 "[ perf data convert: Converted and wrote %.3f MB (%" PRIu64 " samples) ]\n", 436 412 (ftell(c.out)) / 1024.0 / 1024.0, c.events_count); 437 413 414 + if (c.skipped) { 415 + fprintf(stderr, "[ perf data convert: Skipped %" PRIu64 " samples ]\n", 416 + c.skipped); 417 + } 418 + 438 419 ret = 0; 420 + 421 + if (c.ptime_range) 422 + zfree(&c.ptime_range); 423 + 439 424 err_session_delete: 440 425 perf_session__delete(session); 441 426 err_fclose:
+1
tools/perf/util/data-convert.h
··· 8 8 bool force; 9 9 bool all; 10 10 bool tod; 11 + const char *time_str; 11 12 }; 12 13 13 14 #ifdef HAVE_LIBBABELTRACE_SUPPORT