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

perf report: Implement perf.data record decompression

zstd_init(, comp_level = 0) initializes decompression part of API only
hat now consists of zstd_decompress_stream() function.

The perf.data PERF_RECORD_COMPRESSED records are decompressed using
zstd_decompress_stream() function into a linked list of mmaped memory
regions of mmap_comp_len size (struct decomp).

After decompression of one COMPRESSED record its content is iterated and
fetched for usual processing. The mmaped memory regions with
decompressed events are kept in the linked list till the tool process
termination.

When dumping raw records (e.g., perf report -D --header) file offsets of
events from compressed records are printed as zero.

Committer notes:

Since now we have support for processing PERF_RECORD_COMPRESSED, we see
none, in raw form, like we saw in the previous patch commiter notes,
they were decompressed into the usual PERF_RECORD_{FORK,MMAP,COMM,etc}
records, we only see the stats for those PERF_RECORD_COMPRESSED events,
and since I used the file generated in the commiter notes for the
previous patch, there they are, 2 compressed records:

$ perf report --header-only | grep cmdline
# cmdline : /home/acme/bin/perf record -z2 sleep 1
$ perf report -D | grep COMPRESS
COMPRESSED events: 2
COMPRESSED events: 0
$ perf report --stdio
# To display the perf.data header info, please use --header/--header-only options.
#
#
# Total Lost Samples: 0
#
# Samples: 15 of event 'cycles:u'
# Event count (approx.): 962227
#
# Overhead Command Shared Object Symbol
# ........ ....... ................ ...........................
#
46.99% sleep libc-2.28.so [.] _dl_addr
29.24% sleep [unknown] [k] 0xffffffffaea00a67
16.45% sleep libc-2.28.so [.] __GI__IO_un_link.part.1
5.92% sleep ld-2.28.so [.] _dl_setup_hash
1.40% sleep libc-2.28.so [.] __nanosleep
0.00% sleep [unknown] [k] 0xffffffffaea00163

#
# (Tip: To see callchains in a more compact form: perf report -g folded)
#
$

Signed-off-by: Alexey Budankov <alexey.budankov@linux.intel.com>
Reviewed-by: Jiri Olsa <jolsa@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: http://lkml.kernel.org/r/304b0a59-942c-3fe1-da02-aa749f87108b@linux.intel.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Alexey Budankov and committed by
Arnaldo Carvalho de Melo
cb62c6f1 504c1ad1

+181 -2
+4 -1
tools/perf/builtin-report.c
··· 1258 1258 if (session == NULL) 1259 1259 return -1; 1260 1260 1261 + if (zstd_init(&(session->zstd_data), 0) < 0) 1262 + pr_warning("Decompression initialization failed. Reported data may be incomplete.\n"); 1263 + 1261 1264 if (report.queue_size) { 1262 1265 ordered_events__set_alloc_size(&session->ordered_events, 1263 1266 report.queue_size); ··· 1451 1448 error: 1452 1449 if (report.ptime_range) 1453 1450 zfree(&report.ptime_range); 1454 - 1451 + zstd_fini(&(session->zstd_data)); 1455 1452 perf_session__delete(session); 1456 1453 return ret; 1457 1454 }
+11
tools/perf/util/compress.h
··· 20 20 struct zstd_data { 21 21 #ifdef HAVE_ZSTD_SUPPORT 22 22 ZSTD_CStream *cstream; 23 + ZSTD_DStream *dstream; 23 24 #endif 24 25 }; 25 26 ··· 32 31 size_t zstd_compress_stream_to_records(struct zstd_data *data, void *dst, size_t dst_size, 33 32 void *src, size_t src_size, size_t max_record_size, 34 33 size_t process_header(void *record, size_t increment)); 34 + 35 + size_t zstd_decompress_stream(struct zstd_data *data, void *src, size_t src_size, 36 + void *dst, size_t dst_size); 35 37 #else /* !HAVE_ZSTD_SUPPORT */ 36 38 37 39 static inline int zstd_init(struct zstd_data *data __maybe_unused, int level __maybe_unused) ··· 53 49 void *src __maybe_unused, size_t src_size __maybe_unused, 54 50 size_t max_record_size __maybe_unused, 55 51 size_t process_header(void *record, size_t increment) __maybe_unused) 52 + { 53 + return 0; 54 + } 55 + 56 + static inline size_t zstd_decompress_stream(struct zstd_data *data __maybe_unused, void *src __maybe_unused, 57 + size_t src_size __maybe_unused, void *dst __maybe_unused, 58 + size_t dst_size __maybe_unused) 56 59 { 57 60 return 0; 58 61 }
+115 -1
tools/perf/util/session.c
··· 29 29 #include "stat.h" 30 30 #include "arch/common.h" 31 31 32 + #ifdef HAVE_ZSTD_SUPPORT 33 + static int perf_session__process_compressed_event(struct perf_session *session, 34 + union perf_event *event, u64 file_offset) 35 + { 36 + void *src; 37 + size_t decomp_size, src_size; 38 + u64 decomp_last_rem = 0; 39 + size_t decomp_len = session->header.env.comp_mmap_len; 40 + struct decomp *decomp, *decomp_last = session->decomp_last; 41 + 42 + decomp = mmap(NULL, sizeof(struct decomp) + decomp_len, PROT_READ|PROT_WRITE, 43 + MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); 44 + if (decomp == MAP_FAILED) { 45 + pr_err("Couldn't allocate memory for decompression\n"); 46 + return -1; 47 + } 48 + 49 + decomp->file_pos = file_offset; 50 + decomp->head = 0; 51 + 52 + if (decomp_last) { 53 + decomp_last_rem = decomp_last->size - decomp_last->head; 54 + memcpy(decomp->data, &(decomp_last->data[decomp_last->head]), decomp_last_rem); 55 + decomp->size = decomp_last_rem; 56 + } 57 + 58 + src = (void *)event + sizeof(struct compressed_event); 59 + src_size = event->pack.header.size - sizeof(struct compressed_event); 60 + 61 + decomp_size = zstd_decompress_stream(&(session->zstd_data), src, src_size, 62 + &(decomp->data[decomp_last_rem]), decomp_len - decomp_last_rem); 63 + if (!decomp_size) { 64 + munmap(decomp, sizeof(struct decomp) + decomp_len); 65 + pr_err("Couldn't decompress data\n"); 66 + return -1; 67 + } 68 + 69 + decomp->size += decomp_size; 70 + 71 + if (session->decomp == NULL) { 72 + session->decomp = decomp; 73 + session->decomp_last = decomp; 74 + } else { 75 + session->decomp_last->next = decomp; 76 + session->decomp_last = decomp; 77 + } 78 + 79 + pr_debug("decomp (B): %ld to %ld\n", src_size, decomp_size); 80 + 81 + return 0; 82 + } 83 + #else /* !HAVE_ZSTD_SUPPORT */ 84 + #define perf_session__process_compressed_event perf_session__process_compressed_event_stub 85 + #endif 86 + 32 87 static int perf_session__deliver_event(struct perf_session *session, 33 88 union perf_event *event, 34 89 struct perf_tool *tool, ··· 252 197 machine__delete_threads(&session->machines.host); 253 198 } 254 199 200 + static void perf_session__release_decomp_events(struct perf_session *session) 201 + { 202 + struct decomp *next, *decomp; 203 + size_t decomp_len; 204 + next = session->decomp; 205 + decomp_len = session->header.env.comp_mmap_len; 206 + do { 207 + decomp = next; 208 + if (decomp == NULL) 209 + break; 210 + next = decomp->next; 211 + munmap(decomp, decomp_len + sizeof(struct decomp)); 212 + } while (1); 213 + } 214 + 255 215 void perf_session__delete(struct perf_session *session) 256 216 { 257 217 if (session == NULL) ··· 275 205 auxtrace_index__free(&session->auxtrace_index); 276 206 perf_session__destroy_kernel_maps(session); 277 207 perf_session__delete_threads(session); 208 + perf_session__release_decomp_events(session); 278 209 perf_env__exit(&session->header.env); 279 210 machines__exit(&session->machines); 280 211 if (session->data) ··· 510 439 if (tool->feature == NULL) 511 440 tool->feature = process_event_op2_stub; 512 441 if (tool->compressed == NULL) 513 - tool->compressed = perf_session__process_compressed_event_stub; 442 + tool->compressed = perf_session__process_compressed_event; 514 443 } 515 444 516 445 static void swap_sample_id_all(union perf_event *event, void *data) ··· 1796 1725 1797 1726 volatile int session_done; 1798 1727 1728 + static int __perf_session__process_decomp_events(struct perf_session *session); 1729 + 1799 1730 static int __perf_session__process_pipe_events(struct perf_session *session) 1800 1731 { 1801 1732 struct ordered_events *oe = &session->ordered_events; ··· 1878 1805 if (skip > 0) 1879 1806 head += skip; 1880 1807 1808 + err = __perf_session__process_decomp_events(session); 1809 + if (err) 1810 + goto out_err; 1811 + 1881 1812 if (!session_done()) 1882 1813 goto more; 1883 1814 done: ··· 1928 1851 } 1929 1852 1930 1853 return event; 1854 + } 1855 + 1856 + static int __perf_session__process_decomp_events(struct perf_session *session) 1857 + { 1858 + s64 skip; 1859 + u64 size, file_pos = 0; 1860 + struct decomp *decomp = session->decomp_last; 1861 + 1862 + if (!decomp) 1863 + return 0; 1864 + 1865 + while (decomp->head < decomp->size && !session_done()) { 1866 + union perf_event *event = fetch_mmaped_event(session, decomp->head, decomp->size, decomp->data); 1867 + 1868 + if (!event) 1869 + break; 1870 + 1871 + size = event->header.size; 1872 + 1873 + if (size < sizeof(struct perf_event_header) || 1874 + (skip = perf_session__process_event(session, event, file_pos)) < 0) { 1875 + pr_err("%#" PRIx64 " [%#x]: failed to process type: %d\n", 1876 + decomp->file_pos + decomp->head, event->header.size, event->header.type); 1877 + return -EINVAL; 1878 + } 1879 + 1880 + if (skip) 1881 + size += skip; 1882 + 1883 + decomp->head += size; 1884 + } 1885 + 1886 + return 0; 1931 1887 } 1932 1888 1933 1889 /* ··· 2071 1961 2072 1962 head += size; 2073 1963 file_pos += size; 1964 + 1965 + err = __perf_session__process_decomp_events(session); 1966 + if (err) 1967 + goto out; 2074 1968 2075 1969 ui_progress__update(prog, size); 2076 1970
+10
tools/perf/util/session.h
··· 39 39 u64 bytes_transferred; 40 40 u64 bytes_compressed; 41 41 struct zstd_data zstd_data; 42 + struct decomp *decomp; 43 + struct decomp *decomp_last; 44 + }; 45 + 46 + struct decomp { 47 + struct decomp *next; 48 + u64 file_pos; 49 + u64 head; 50 + size_t size; 51 + char data[]; 42 52 }; 43 53 44 54 struct perf_tool;
+41
tools/perf/util/zstd.c
··· 9 9 { 10 10 size_t ret; 11 11 12 + data->dstream = ZSTD_createDStream(); 13 + if (data->dstream == NULL) { 14 + pr_err("Couldn't create decompression stream.\n"); 15 + return -1; 16 + } 17 + 18 + ret = ZSTD_initDStream(data->dstream); 19 + if (ZSTD_isError(ret)) { 20 + pr_err("Failed to initialize decompression stream: %s\n", ZSTD_getErrorName(ret)); 21 + return -1; 22 + } 23 + 24 + if (!level) 25 + return 0; 26 + 12 27 data->cstream = ZSTD_createCStream(); 13 28 if (data->cstream == NULL) { 14 29 pr_err("Couldn't create compression stream.\n"); ··· 41 26 42 27 int zstd_fini(struct zstd_data *data) 43 28 { 29 + if (data->dstream) { 30 + ZSTD_freeDStream(data->dstream); 31 + data->dstream = NULL; 32 + } 33 + 44 34 if (data->cstream) { 45 35 ZSTD_freeCStream(data->cstream); 46 36 data->cstream = NULL; ··· 87 67 } 88 68 89 69 return compressed; 70 + } 71 + 72 + size_t zstd_decompress_stream(struct zstd_data *data, void *src, size_t src_size, 73 + void *dst, size_t dst_size) 74 + { 75 + size_t ret; 76 + ZSTD_inBuffer input = { src, src_size, 0 }; 77 + ZSTD_outBuffer output = { dst, dst_size, 0 }; 78 + 79 + while (input.pos < input.size) { 80 + ret = ZSTD_decompressStream(data->dstream, &output, &input); 81 + if (ZSTD_isError(ret)) { 82 + pr_err("failed to decompress (B): %ld -> %ld : %s\n", 83 + src_size, output.size, ZSTD_getErrorName(ret)); 84 + break; 85 + } 86 + output.dst = dst + output.pos; 87 + output.size = dst_size - output.pos; 88 + } 89 + 90 + return output.pos; 90 91 }