Re: [PATCH] perf data: Allow filtering conversion by start and end time

From: Derek Foreman

Date: Fri Nov 28 2025 - 16:56:57 EST


On 11/27/25 3:34 PM, Namhyung Kim wrote:
Hello,

On Mon, Nov 24, 2025 at 03:23:00PM -0600, Derek Foreman wrote:
This adds a feature to allow restricting the range of converted samples
with start and end times specified in nanoseconds.

Use it like this:

$ perf data convert --to-json out.json -s 12345 -e 13340

Start, end, or both may be omitted.
Looks useful, thanks!
Thanks for the review!
Signed-off-by: Derek Foreman <derek.foreman@xxxxxxxxxxxxx>
---
tools/perf/Documentation/perf-data.txt | 6 ++++++
tools/perf/builtin-data.c | 4 ++++
tools/perf/util/data-convert-bt.c | 18 ++++++++++++++++++
tools/perf/util/data-convert-json.c | 18 ++++++++++++++++++
tools/perf/util/data-convert.h | 3 +++
5 files changed, 49 insertions(+)

diff --git a/tools/perf/Documentation/perf-data.txt b/tools/perf/Documentation/perf-data.txt
index 417bf17e265c..db0de1f354a3 100644
--- a/tools/perf/Documentation/perf-data.txt
+++ b/tools/perf/Documentation/perf-data.txt
@@ -40,6 +40,12 @@ OPTIONS for 'convert'
--force::
Don't complain, do it.
+-s::
Please add this as well.

--start=<TIME>::

+ Start time, in nanoseconds. Samples before this time will not be converted.
It'd be nice if you mention that users can see the timestamp from perf
script or so.

After looking at perf script, I realized I'd reinvented the wheel here, so in v2 I've used `--time` and the util functions that already exist for parsing and filtering time strings.

I mostly copy and pasted the --time help from another file. I think asciidoc has a way to templatize this with an include file (it's in at least 3 files now), but I wasn't sure if I should try to do that.


+
+-e::
--end=<TIME>::

+ End time, in nanoseconds. Samples after this time will not be converted.
+
-v::
--verbose::
Be more verbose (show counter open errors, etc).
diff --git a/tools/perf/builtin-data.c b/tools/perf/builtin-data.c
index ce51cbf6dc97..da813c62e284 100644
--- a/tools/perf/builtin-data.c
+++ b/tools/perf/builtin-data.c
@@ -33,6 +33,8 @@ const char *to_ctf;
struct perf_data_convert_opts opts = {
.force = false,
.all = false,
+ .range_start = 0,
+ .range_end = 0,
};
const struct option data_options[] = {
@@ -45,6 +47,8 @@ const struct option data_options[] = {
#endif
OPT_BOOLEAN('f', "force", &opts.force, "don't complain, do it"),
OPT_BOOLEAN(0, "all", &opts.all, "Convert all events"),
+ OPT_U64('s', "start", &opts.range_start, "Earliest timestamp to convert"),
+ OPT_U64('e', "end", &opts.range_end, "Latest timestamp to convert"),
OPT_END()
};
diff --git a/tools/perf/util/data-convert-bt.c b/tools/perf/util/data-convert-bt.c
index 3d2e437e1354..6819f11fe900 100644
--- a/tools/perf/util/data-convert-bt.c
+++ b/tools/perf/util/data-convert-bt.c
@@ -91,9 +91,13 @@ struct convert {
struct perf_tool tool;
struct ctf_writer writer;
+ u64 start;
+ u64 end;
+
u64 events_size;
u64 events_count;
u64 non_sample_count;
+ u64 skipped;
/* Ordered events configured queue size. */
u64 queue_size;
@@ -811,6 +815,12 @@ static int process_sample_event(const struct perf_tool *tool,
if (WARN_ONCE(!priv, "Failed to setup all events.\n"))
return 0;
+ if (sample->time < c->start ||
+ (c->end && sample->time > c->end)) {
+ ++c->skipped;
+ return 0;
+ }
+
event_class = priv->event_class;
/* update stats */
@@ -1626,6 +1636,9 @@ int bt_convert__perf2ctf(const char *input, const char *path,
c.tool.namespaces = perf_event__process_namespaces;
c.tool.ordering_requires_timestamps = true;
+ c.start = opts->range_start;
+ c.end = opts->range_end;
+
if (opts->all) {
c.tool.comm = process_comm_event;
c.tool.exit = process_exit_event;
@@ -1677,6 +1690,11 @@ int bt_convert__perf2ctf(const char *input, const char *path,
"[ perf data convert: Converted '%s' into CTF data '%s' ]\n",
data.path, path);
+ if (c.skipped)
+ fprintf(stderr,
+ "[ perf data convert: Skipped %" PRIu64 " samples",
+ c.skipped);
This is a strange style to have the literal in a different line. It'd
be nice if you can fix others too - probably in a separate commit.

And this line could come after the next, but it's up to you.
I think it's better your way, yes.
Also it'd be recommended to have a pair of parentheses for multi-line
bodies even it's a single statement.

I've added a second patch for v2. One of the lines exceeded 100 chars, which bothered checkpatch.pl, so I left the literal on the second line for now.

Thanks,
Derek

+
fprintf(stderr,
"[ perf data convert: Converted and wrote %.3f MB (%" PRIu64 " samples",
(double) c.events_size / 1024.0 / 1024.0,
diff --git a/tools/perf/util/data-convert-json.c b/tools/perf/util/data-convert-json.c
index 9dc1e184cf3c..dcbba34d6b08 100644
--- a/tools/perf/util/data-convert-json.c
+++ b/tools/perf/util/data-convert-json.c
@@ -35,7 +35,11 @@ struct convert_json {
struct perf_tool tool;
FILE *out;
bool first;
+ u64 start;
+ u64 end;
+
u64 events_count;
+ u64 skipped;
};
// Outputs a JSON-encoded string surrounded by quotes with characters escaped.
@@ -165,6 +169,12 @@ static int process_sample_event(const struct perf_tool *tool,
return -1;
}
+ if (sample->time < c->start ||
+ (c->end && sample->time > c->end)) {
+ ++c->skipped;
+ return 0;
+ }
+
++c->events_count;
if (c->first)
@@ -320,6 +330,9 @@ int bt_convert__perf2json(const char *input_name, const char *output_name,
struct convert_json c = {
.first = true,
.events_count = 0,
+ .start = opts->range_start,
+ .end = opts->range_end,
+ .skipped = 0,
};
struct perf_data data = {
.mode = PERF_DATA_MODE_READ,
@@ -407,6 +420,11 @@ int bt_convert__perf2json(const char *input_name, const char *output_name,
"[ perf data convert: Converted '%s' into JSON data '%s' ]\n",
data.path, output_name);
+ if (c.skipped)
+ fprintf(stderr,
+ "[ perf data convert: Skipped %" PRIu64 " samples.\n",
+ c.skipped);
Ditto.

Thanks,
Namhyung

+
fprintf(stderr,
"[ perf data convert: Converted and wrote %.3f MB (%" PRIu64 " samples) ]\n",
(ftell(c.out)) / 1024.0 / 1024.0, c.events_count);
diff --git a/tools/perf/util/data-convert.h b/tools/perf/util/data-convert.h
index 1b4c5f598415..5d055e1c31c8 100644
--- a/tools/perf/util/data-convert.h
+++ b/tools/perf/util/data-convert.h
@@ -3,11 +3,14 @@
#define __DATA_CONVERT_H
#include <stdbool.h>
+#include <linux/types.h>
struct perf_data_convert_opts {
bool force;
bool all;
bool tod;
+ u64 range_start;
+ u64 range_end;
};
#ifdef HAVE_LIBBABELTRACE_SUPPORT
--
2.47.3