Skip to content

Commit 208c0e1

Browse files
Chun-Tse Shaoacmel
authored andcommitted
perf record: Add 8-byte aligned event type PERF_RECORD_COMPRESSED2
The original PERF_RECORD_COMPRESS is not 8-byte aligned, which can cause asan runtime error: # Build with asan $ make -C tools/perf O=/tmp/perf DEBUG=1 EXTRA_CFLAGS="-O0 -g -fno-omit-frame-pointer -fsanitize=undefined" # Test success with many asan runtime errors: $ /tmp/perf/perf test "Zstd perf.data compression/decompression" -vv 83: Zstd perf.data compression/decompression: ... util/session.c:1959:13: runtime error: member access within misaligned address 0x7f69e3f99653 for type 'union perf_event', which requires 13 byte alignment 0x7f69e3f99653: note: pointer points here d0 3a 50 69 44 00 00 00 00 00 08 00 bb 07 00 00 00 00 00 00 44 00 00 00 00 00 00 00 ff 07 00 00 ^ util/session.c:2163:22: runtime error: member access within misaligned address 0x7f69e3f99653 for type 'union perf_event', which requires 8 byte alignment 0x7f69e3f99653: note: pointer points here d0 3a 50 69 44 00 00 00 00 00 08 00 bb 07 00 00 00 00 00 00 44 00 00 00 00 00 00 00 ff 07 00 00 ^ ... Since there is no way to align compressed data in zstd compression, this patch add a new event type `PERF_RECORD_COMPRESSED2`, which adds a field `data_size` to specify the actual compressed data size. The `header.size` contains the total record size, including the padding at the end to make it 8-byte aligned. Tested with `Zstd perf.data compression/decompression` Signed-off-by: Chun-Tse Shao <ctshao@google.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: Ben Gainey <ben.gainey@arm.com> Cc: Christophe Leroy <christophe.leroy@csgroup.eu> 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: Kan Liang <kan.liang@linux.intel.com> Cc: Leo Yan <leo.yan@arm.com> Cc: Mark Rutland <mark.rutland@arm.com> Cc: Namhyung Kim <namhyung@kernel.org> Cc: Nick Terrell <terrelln@fb.com> Cc: Peter Zijlstra <peterz@infradead.org> Link: https://lore.kernel.org/r/20250303183646.327510-1-ctshao@google.com Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
1 parent bcfab08 commit 208c0e1

7 files changed

Lines changed: 64 additions & 13 deletions

File tree

tools/lib/perf/Documentation/libperf.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ SYNOPSIS
210210
struct perf_record_time_conv;
211211
struct perf_record_header_feature;
212212
struct perf_record_compressed;
213+
struct perf_record_compressed2;
213214
--
214215

215216
DESCRIPTION

tools/lib/perf/include/perf/event.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,16 @@ struct perf_record_compressed {
457457
char data[];
458458
};
459459

460+
/*
461+
* `header.size` includes the padding we are going to add while writing the record.
462+
* `data_size` only includes the size of `data[]` itself.
463+
*/
464+
struct perf_record_compressed2 {
465+
struct perf_event_header header;
466+
__u64 data_size;
467+
char data[];
468+
};
469+
460470
enum perf_user_event_type { /* above any possible kernel type */
461471
PERF_RECORD_USER_TYPE_START = 64,
462472
PERF_RECORD_HEADER_ATTR = 64,
@@ -478,6 +488,7 @@ enum perf_user_event_type { /* above any possible kernel type */
478488
PERF_RECORD_HEADER_FEATURE = 80,
479489
PERF_RECORD_COMPRESSED = 81,
480490
PERF_RECORD_FINISHED_INIT = 82,
491+
PERF_RECORD_COMPRESSED2 = 83,
481492
PERF_RECORD_HEADER_MAX
482493
};
483494

@@ -518,6 +529,7 @@ union perf_event {
518529
struct perf_record_time_conv time_conv;
519530
struct perf_record_header_feature feat;
520531
struct perf_record_compressed pack;
532+
struct perf_record_compressed2 pack2;
521533
};
522534

523535
#endif /* __LIBPERF_EVENT_H */

tools/perf/Documentation/perf.data-file-format.txt

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,7 @@ struct {
370370
u32 mmap_len;
371371
};
372372

373-
Indicates that trace contains records of PERF_RECORD_COMPRESSED type
373+
Indicates that trace contains records of PERF_RECORD_COMPRESSED2 type
374374
that have perf_events records in compressed form.
375375

376376
HEADER_CPU_PMU_CAPS = 28,
@@ -602,7 +602,14 @@ struct auxtrace_error_event {
602602
Describes a header feature. These are records used in pipe-mode that
603603
contain information that otherwise would be in perf.data file's header.
604604

605-
PERF_RECORD_COMPRESSED = 81,
605+
PERF_RECORD_COMPRESSED = 81, /* deprecated */
606+
607+
The header is followed by compressed data frame that can be decompressed
608+
into array of perf trace records. The size of the entire compressed event
609+
record including the header is limited by the max value of header.size.
610+
611+
It is deprecated and new files should use PERF_RECORD_COMPRESSED2 to gurantee
612+
8-byte alignment.
606613

607614
struct compressed_event {
608615
struct perf_event_header header;
@@ -618,10 +625,17 @@ This is used, for instance, to 'perf inject' events after init and before
618625
regular events, those emitted by the kernel, to support combining guest and
619626
host records.
620627

628+
PERF_RECORD_COMPRESSED2 = 83,
621629

622-
The header is followed by compressed data frame that can be decompressed
623-
into array of perf trace records. The size of the entire compressed event
624-
record including the header is limited by the max value of header.size.
630+
8-byte aligned version of `PERF_RECORD_COMPRESSED`. `header.size` indicates the
631+
total record size, including padding for 8-byte alignment, and `data_size`
632+
specifies the actual size of the compressed data.
633+
634+
struct perf_record_compressed2 {
635+
struct perf_event_header header;
636+
__u64 data_size;
637+
char data[];
638+
};
625639

626640
Event types
627641

tools/perf/builtin-record.c

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -650,14 +650,27 @@ static int record__pushfn(struct mmap *map, void *to, void *bf, size_t size)
650650
struct record *rec = to;
651651

652652
if (record__comp_enabled(rec)) {
653+
struct perf_record_compressed2 *event = map->data;
654+
size_t padding = 0;
655+
u8 pad[8] = {0};
653656
ssize_t compressed = zstd_compress(rec->session, map, map->data,
654657
mmap__mmap_len(map), bf, size);
655658

656659
if (compressed < 0)
657660
return (int)compressed;
658661

659-
size = compressed;
660-
bf = map->data;
662+
bf = event;
663+
thread->samples++;
664+
665+
/*
666+
* The record from `zstd_compress` is not 8 bytes aligned, which would cause asan
667+
* error. We make it aligned here.
668+
*/
669+
event->data_size = compressed - sizeof(struct perf_record_compressed2);
670+
event->header.size = PERF_ALIGN(compressed, sizeof(u64));
671+
padding = event->header.size - compressed;
672+
return record__write(rec, map, bf, compressed) ||
673+
record__write(rec, map, &pad, padding);
661674
}
662675

663676
thread->samples++;
@@ -1536,15 +1549,15 @@ static void record__adjust_affinity(struct record *rec, struct mmap *map)
15361549

15371550
static size_t process_comp_header(void *record, size_t increment)
15381551
{
1539-
struct perf_record_compressed *event = record;
1552+
struct perf_record_compressed2 *event = record;
15401553
size_t size = sizeof(*event);
15411554

15421555
if (increment) {
15431556
event->header.size += increment;
15441557
return increment;
15451558
}
15461559

1547-
event->header.type = PERF_RECORD_COMPRESSED;
1560+
event->header.type = PERF_RECORD_COMPRESSED2;
15481561
event->header.size = size;
15491562

15501563
return size;
@@ -1554,7 +1567,7 @@ static ssize_t zstd_compress(struct perf_session *session, struct mmap *map,
15541567
void *dst, size_t dst_size, void *src, size_t src_size)
15551568
{
15561569
ssize_t compressed;
1557-
size_t max_record_size = PERF_SAMPLE_MAX_SIZE - sizeof(struct perf_record_compressed) - 1;
1570+
size_t max_record_size = PERF_SAMPLE_MAX_SIZE - sizeof(struct perf_record_compressed2) - 1;
15581571
struct zstd_data *zstd_data = &session->zstd_data;
15591572

15601573
if (map && map->file)

tools/perf/util/event.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ static const char *perf_event__names[] = {
7777
[PERF_RECORD_HEADER_FEATURE] = "FEATURE",
7878
[PERF_RECORD_COMPRESSED] = "COMPRESSED",
7979
[PERF_RECORD_FINISHED_INIT] = "FINISHED_INIT",
80+
[PERF_RECORD_COMPRESSED2] = "COMPRESSED2",
8081
};
8182

8283
const char *perf_event__name(unsigned int id)

tools/perf/util/session.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1400,7 +1400,9 @@ static s64 perf_session__process_user_event(struct perf_session *session,
14001400
int err;
14011401

14021402
perf_sample__init(&sample, /*all=*/true);
1403-
if (event->header.type != PERF_RECORD_COMPRESSED || perf_tool__compressed_is_stub(tool))
1403+
if ((event->header.type != PERF_RECORD_COMPRESSED &&
1404+
event->header.type != PERF_RECORD_COMPRESSED2) ||
1405+
perf_tool__compressed_is_stub(tool))
14041406
dump_event(session->evlist, event, file_offset, &sample, file_path);
14051407

14061408
/* These events are processed right away */
@@ -1481,6 +1483,7 @@ static s64 perf_session__process_user_event(struct perf_session *session,
14811483
err = tool->feature(session, event);
14821484
break;
14831485
case PERF_RECORD_COMPRESSED:
1486+
case PERF_RECORD_COMPRESSED2:
14841487
err = tool->compressed(session, event, file_offset, file_path);
14851488
if (err)
14861489
dump_event(session->evlist, event, file_offset, &sample, file_path);

tools/perf/util/tool.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,15 @@ static int perf_session__process_compressed_event(struct perf_session *session,
4343
decomp->size = decomp_last_rem;
4444
}
4545

46-
src = (void *)event + sizeof(struct perf_record_compressed);
47-
src_size = event->pack.header.size - sizeof(struct perf_record_compressed);
46+
if (event->header.type == PERF_RECORD_COMPRESSED) {
47+
src = (void *)event + sizeof(struct perf_record_compressed);
48+
src_size = event->pack.header.size - sizeof(struct perf_record_compressed);
49+
} else if (event->header.type == PERF_RECORD_COMPRESSED2) {
50+
src = (void *)event + sizeof(struct perf_record_compressed2);
51+
src_size = event->pack2.data_size;
52+
} else {
53+
return -1;
54+
}
4855

4956
decomp_size = zstd_decompress_stream(session->active_decomp->zstd_decomp, src, src_size,
5057
&(decomp->data[decomp_last_rem]), decomp_len - decomp_last_rem);

0 commit comments

Comments
 (0)