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

perf inject: Fix output from a file to a pipe

When the input is a regular file but the output is a pipe, it should
write a pipe header. But just repiping would write a portion of the
existing header which is different in 'size' value. So we need to
prevent it and write a new pipe header along with other information
like event attributes and features.

This can handle something like this:

# perf record -a -B sleep 1

# perf inject -b -i perf.data | perf report -i -

Factor out perf_event__synthesize_for_pipe() to be shared between perf
record and inject.

Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Acked-by: Jiri Olsa <jolsa@redhat.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Ian Rogers <irogers@google.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: http://lore.kernel.org/lkml/20210719223153.1618812-5-namhyung@kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Namhyung Kim and committed by
Arnaldo Carvalho de Melo
c3a057dc fea20d66

+88 -37
+26 -2
tools/perf/builtin-inject.c
··· 919 919 .use_stdio = true, 920 920 }; 921 921 int ret; 922 + bool repipe = true; 922 923 923 924 struct option options[] = { 924 925 OPT_BOOLEAN('b', "build-ids", &inject.build_ids, ··· 994 993 } 995 994 996 995 data.path = inject.input_name; 997 - if (!strcmp(inject.input_name, "-") || inject.output.is_pipe) 996 + if (!strcmp(inject.input_name, "-") || inject.output.is_pipe) { 998 997 inject.is_pipe = true; 998 + /* 999 + * Do not repipe header when input is a regular file 1000 + * since either it can rewrite the header at the end 1001 + * or write a new pipe header. 1002 + */ 1003 + if (strcmp(inject.input_name, "-")) 1004 + repipe = false; 1005 + } 999 1006 1000 - inject.session = __perf_session__new(&data, inject.is_pipe, 1007 + inject.session = __perf_session__new(&data, repipe, 1001 1008 perf_data__fd(&inject.output), 1002 1009 &inject.tool); 1003 1010 if (IS_ERR(inject.session)) { ··· 1015 1006 1016 1007 if (zstd_init(&(inject.session->zstd_data), 0) < 0) 1017 1008 pr_warning("Decompression initialization failed.\n"); 1009 + 1010 + if (!data.is_pipe && inject.output.is_pipe) { 1011 + ret = perf_header__write_pipe(perf_data__fd(&inject.output)); 1012 + if (ret < 0) { 1013 + pr_err("Couldn't write a new pipe header.\n"); 1014 + goto out_delete; 1015 + } 1016 + 1017 + ret = perf_event__synthesize_for_pipe(&inject.tool, 1018 + inject.session, 1019 + &inject.output, 1020 + perf_event__repipe); 1021 + if (ret < 0) 1022 + goto out_delete; 1023 + } 1018 1024 1019 1025 if (inject.build_ids && !inject.build_id_all) { 1020 1026 /*
+4 -34
tools/perf/builtin-record.c
··· 1387 1387 struct perf_data *data = &rec->data; 1388 1388 struct record_opts *opts = &rec->opts; 1389 1389 struct perf_tool *tool = &rec->tool; 1390 - int fd = perf_data__fd(data); 1391 1390 int err = 0; 1392 1391 event_op f = process_synthesized_event; 1393 1392 ··· 1394 1395 return 0; 1395 1396 1396 1397 if (data->is_pipe) { 1397 - /* 1398 - * We need to synthesize events first, because some 1399 - * features works on top of them (on report side). 1400 - */ 1401 - err = perf_event__synthesize_attrs(tool, rec->evlist, 1402 - process_synthesized_event); 1403 - if (err < 0) { 1404 - pr_err("Couldn't synthesize attrs.\n"); 1405 - goto out; 1406 - } 1407 - 1408 - err = perf_event__synthesize_features(tool, session, rec->evlist, 1398 + err = perf_event__synthesize_for_pipe(tool, session, data, 1409 1399 process_synthesized_event); 1410 - if (err < 0) { 1411 - pr_err("Couldn't synthesize features.\n"); 1412 - return err; 1413 - } 1400 + if (err < 0) 1401 + goto out; 1414 1402 1415 - if (have_tracepoints(&rec->evlist->core.entries)) { 1416 - /* 1417 - * FIXME err <= 0 here actually means that 1418 - * there were no tracepoints so its not really 1419 - * an error, just that we don't need to 1420 - * synthesize anything. We really have to 1421 - * return this more properly and also 1422 - * propagate errors that now are calling die() 1423 - */ 1424 - err = perf_event__synthesize_tracing_data(tool, fd, rec->evlist, 1425 - process_synthesized_event); 1426 - if (err <= 0) { 1427 - pr_err("Couldn't record tracing data.\n"); 1428 - goto out; 1429 - } 1430 - rec->bytes_written += err; 1431 - } 1403 + rec->bytes_written += err; 1432 1404 } 1433 1405 1434 1406 err = perf_event__synth_time_conv(record__pick_pc(rec), tool,
+52 -1
tools/perf/util/synthetic-events.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0-only 2 2 3 + #include "util/cgroup.h" 4 + #include "util/data.h" 3 5 #include "util/debug.h" 4 6 #include "util/dso.h" 5 7 #include "util/event.h" ··· 18 16 #include "util/synthetic-events.h" 19 17 #include "util/target.h" 20 18 #include "util/time-utils.h" 21 - #include "util/cgroup.h" 22 19 #include <linux/bitops.h> 23 20 #include <linux/kernel.h> 24 21 #include <linux/string.h> ··· 2178 2177 ret = process(tool, ff.buf, NULL, NULL); 2179 2178 2180 2179 free(ff.buf); 2180 + return ret; 2181 + } 2182 + 2183 + int perf_event__synthesize_for_pipe(struct perf_tool *tool, 2184 + struct perf_session *session, 2185 + struct perf_data *data, 2186 + perf_event__handler_t process) 2187 + { 2188 + int err; 2189 + int ret = 0; 2190 + struct evlist *evlist = session->evlist; 2191 + 2192 + /* 2193 + * We need to synthesize events first, because some 2194 + * features works on top of them (on report side). 2195 + */ 2196 + err = perf_event__synthesize_attrs(tool, evlist, process); 2197 + if (err < 0) { 2198 + pr_err("Couldn't synthesize attrs.\n"); 2199 + return err; 2200 + } 2201 + ret += err; 2202 + 2203 + err = perf_event__synthesize_features(tool, session, evlist, process); 2204 + if (err < 0) { 2205 + pr_err("Couldn't synthesize features.\n"); 2206 + return err; 2207 + } 2208 + ret += err; 2209 + 2210 + if (have_tracepoints(&evlist->core.entries)) { 2211 + int fd = perf_data__fd(data); 2212 + 2213 + /* 2214 + * FIXME err <= 0 here actually means that 2215 + * there were no tracepoints so its not really 2216 + * an error, just that we don't need to 2217 + * synthesize anything. We really have to 2218 + * return this more properly and also 2219 + * propagate errors that now are calling die() 2220 + */ 2221 + err = perf_event__synthesize_tracing_data(tool, fd, evlist, 2222 + process); 2223 + if (err <= 0) { 2224 + pr_err("Couldn't record tracing data.\n"); 2225 + return err; 2226 + } 2227 + ret += err; 2228 + } 2229 + 2181 2230 return ret; 2182 2231 }
+6
tools/perf/util/synthetic-events.h
··· 14 14 struct machine; 15 15 struct perf_counts_values; 16 16 struct perf_cpu_map; 17 + struct perf_data; 17 18 struct perf_event_attr; 18 19 struct perf_event_mmap_page; 19 20 struct perf_sample; ··· 101 100 return 0; 102 101 } 103 102 #endif // HAVE_LIBBPF_SUPPORT 103 + 104 + int perf_event__synthesize_for_pipe(struct perf_tool *tool, 105 + struct perf_session *session, 106 + struct perf_data *data, 107 + perf_event__handler_t process); 104 108 105 109 #endif // __PERF_SYNTHETIC_EVENTS_H