diff options
| -rw-r--r-- | tools/perf/builtin-report.c | 5 | ||||
| -rw-r--r-- | tools/perf/util/compress.h | 11 | ||||
| -rw-r--r-- | tools/perf/util/session.c | 116 | ||||
| -rw-r--r-- | tools/perf/util/session.h | 10 | ||||
| -rw-r--r-- | tools/perf/util/zstd.c | 41 |
5 files changed, 181 insertions, 2 deletions
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 91e27ac297c2..1ca533f06a4c 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
| @@ -1258,6 +1258,9 @@ repeat: | |||
| 1258 | if (session == NULL) | 1258 | if (session == NULL) |
| 1259 | return -1; | 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 | if (report.queue_size) { | 1264 | if (report.queue_size) { |
| 1262 | ordered_events__set_alloc_size(&session->ordered_events, | 1265 | ordered_events__set_alloc_size(&session->ordered_events, |
| 1263 | report.queue_size); | 1266 | report.queue_size); |
| @@ -1448,7 +1451,7 @@ repeat: | |||
| 1448 | error: | 1451 | error: |
| 1449 | if (report.ptime_range) | 1452 | if (report.ptime_range) |
| 1450 | zfree(&report.ptime_range); | 1453 | zfree(&report.ptime_range); |
| 1451 | 1454 | zstd_fini(&(session->zstd_data)); | |
| 1452 | perf_session__delete(session); | 1455 | perf_session__delete(session); |
| 1453 | return ret; | 1456 | return ret; |
| 1454 | } | 1457 | } |
diff --git a/tools/perf/util/compress.h b/tools/perf/util/compress.h index 1041a4fd81e2..0cd3369af2a4 100644 --- a/tools/perf/util/compress.h +++ b/tools/perf/util/compress.h | |||
| @@ -20,6 +20,7 @@ bool lzma_is_compressed(const char *input); | |||
| 20 | struct zstd_data { | 20 | struct zstd_data { |
| 21 | #ifdef HAVE_ZSTD_SUPPORT | 21 | #ifdef HAVE_ZSTD_SUPPORT |
| 22 | ZSTD_CStream *cstream; | 22 | ZSTD_CStream *cstream; |
| 23 | ZSTD_DStream *dstream; | ||
| 23 | #endif | 24 | #endif |
| 24 | }; | 25 | }; |
| 25 | 26 | ||
| @@ -31,6 +32,9 @@ int zstd_fini(struct zstd_data *data); | |||
| 31 | size_t zstd_compress_stream_to_records(struct zstd_data *data, void *dst, size_t dst_size, | 32 | size_t zstd_compress_stream_to_records(struct zstd_data *data, void *dst, size_t dst_size, |
| 32 | void *src, size_t src_size, size_t max_record_size, | 33 | void *src, size_t src_size, size_t max_record_size, |
| 33 | size_t process_header(void *record, size_t increment)); | 34 | size_t process_header(void *record, size_t increment)); |
| 35 | |||
| 36 | size_t zstd_decompress_stream(struct zstd_data *data, void *src, size_t src_size, | ||
| 37 | void *dst, size_t dst_size); | ||
| 34 | #else /* !HAVE_ZSTD_SUPPORT */ | 38 | #else /* !HAVE_ZSTD_SUPPORT */ |
| 35 | 39 | ||
| 36 | static inline int zstd_init(struct zstd_data *data __maybe_unused, int level __maybe_unused) | 40 | static inline int zstd_init(struct zstd_data *data __maybe_unused, int level __maybe_unused) |
| @@ -52,6 +56,13 @@ size_t zstd_compress_stream_to_records(struct zstd_data *data __maybe_unused, | |||
| 52 | { | 56 | { |
| 53 | return 0; | 57 | return 0; |
| 54 | } | 58 | } |
| 59 | |||
| 60 | static inline size_t zstd_decompress_stream(struct zstd_data *data __maybe_unused, void *src __maybe_unused, | ||
| 61 | size_t src_size __maybe_unused, void *dst __maybe_unused, | ||
| 62 | size_t dst_size __maybe_unused) | ||
| 63 | { | ||
| 64 | return 0; | ||
| 65 | } | ||
| 55 | #endif | 66 | #endif |
| 56 | 67 | ||
| 57 | #endif /* PERF_COMPRESS_H */ | 68 | #endif /* PERF_COMPRESS_H */ |
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index ec1dec86d0e1..2310a1752983 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
| @@ -29,6 +29,61 @@ | |||
| 29 | #include "stat.h" | 29 | #include "stat.h" |
| 30 | #include "arch/common.h" | 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 | static int perf_session__deliver_event(struct perf_session *session, | 87 | static int perf_session__deliver_event(struct perf_session *session, |
| 33 | union perf_event *event, | 88 | union perf_event *event, |
| 34 | struct perf_tool *tool, | 89 | struct perf_tool *tool, |
| @@ -197,6 +252,21 @@ static void perf_session__delete_threads(struct perf_session *session) | |||
| 197 | machine__delete_threads(&session->machines.host); | 252 | machine__delete_threads(&session->machines.host); |
| 198 | } | 253 | } |
| 199 | 254 | ||
| 255 | static void perf_session__release_decomp_events(struct perf_session *session) | ||
| 256 | { | ||
| 257 | struct decomp *next, *decomp; | ||
| 258 | size_t decomp_len; | ||
| 259 | next = session->decomp; | ||
| 260 | decomp_len = session->header.env.comp_mmap_len; | ||
| 261 | do { | ||
| 262 | decomp = next; | ||
| 263 | if (decomp == NULL) | ||
| 264 | break; | ||
| 265 | next = decomp->next; | ||
| 266 | munmap(decomp, decomp_len + sizeof(struct decomp)); | ||
| 267 | } while (1); | ||
| 268 | } | ||
| 269 | |||
| 200 | void perf_session__delete(struct perf_session *session) | 270 | void perf_session__delete(struct perf_session *session) |
| 201 | { | 271 | { |
| 202 | if (session == NULL) | 272 | if (session == NULL) |
| @@ -205,6 +275,7 @@ void perf_session__delete(struct perf_session *session) | |||
| 205 | auxtrace_index__free(&session->auxtrace_index); | 275 | auxtrace_index__free(&session->auxtrace_index); |
| 206 | perf_session__destroy_kernel_maps(session); | 276 | perf_session__destroy_kernel_maps(session); |
| 207 | perf_session__delete_threads(session); | 277 | perf_session__delete_threads(session); |
| 278 | perf_session__release_decomp_events(session); | ||
| 208 | perf_env__exit(&session->header.env); | 279 | perf_env__exit(&session->header.env); |
| 209 | machines__exit(&session->machines); | 280 | machines__exit(&session->machines); |
| 210 | if (session->data) | 281 | if (session->data) |
| @@ -439,7 +510,7 @@ void perf_tool__fill_defaults(struct perf_tool *tool) | |||
| 439 | if (tool->feature == NULL) | 510 | if (tool->feature == NULL) |
| 440 | tool->feature = process_event_op2_stub; | 511 | tool->feature = process_event_op2_stub; |
| 441 | if (tool->compressed == NULL) | 512 | if (tool->compressed == NULL) |
| 442 | tool->compressed = perf_session__process_compressed_event_stub; | 513 | tool->compressed = perf_session__process_compressed_event; |
| 443 | } | 514 | } |
| 444 | 515 | ||
| 445 | static void swap_sample_id_all(union perf_event *event, void *data) | 516 | static void swap_sample_id_all(union perf_event *event, void *data) |
| @@ -1725,6 +1796,8 @@ static int perf_session__flush_thread_stacks(struct perf_session *session) | |||
| 1725 | 1796 | ||
| 1726 | volatile int session_done; | 1797 | volatile int session_done; |
| 1727 | 1798 | ||
| 1799 | static int __perf_session__process_decomp_events(struct perf_session *session); | ||
| 1800 | |||
| 1728 | static int __perf_session__process_pipe_events(struct perf_session *session) | 1801 | static int __perf_session__process_pipe_events(struct perf_session *session) |
| 1729 | { | 1802 | { |
| 1730 | struct ordered_events *oe = &session->ordered_events; | 1803 | struct ordered_events *oe = &session->ordered_events; |
| @@ -1805,6 +1878,10 @@ more: | |||
| 1805 | if (skip > 0) | 1878 | if (skip > 0) |
| 1806 | head += skip; | 1879 | head += skip; |
| 1807 | 1880 | ||
| 1881 | err = __perf_session__process_decomp_events(session); | ||
| 1882 | if (err) | ||
| 1883 | goto out_err; | ||
| 1884 | |||
| 1808 | if (!session_done()) | 1885 | if (!session_done()) |
| 1809 | goto more; | 1886 | goto more; |
| 1810 | done: | 1887 | done: |
| @@ -1853,6 +1930,39 @@ fetch_mmaped_event(struct perf_session *session, | |||
| 1853 | return event; | 1930 | return event; |
| 1854 | } | 1931 | } |
| 1855 | 1932 | ||
| 1933 | static int __perf_session__process_decomp_events(struct perf_session *session) | ||
| 1934 | { | ||
| 1935 | s64 skip; | ||
| 1936 | u64 size, file_pos = 0; | ||
| 1937 | struct decomp *decomp = session->decomp_last; | ||
| 1938 | |||
| 1939 | if (!decomp) | ||
| 1940 | return 0; | ||
| 1941 | |||
| 1942 | while (decomp->head < decomp->size && !session_done()) { | ||
| 1943 | union perf_event *event = fetch_mmaped_event(session, decomp->head, decomp->size, decomp->data); | ||
| 1944 | |||
| 1945 | if (!event) | ||
| 1946 | break; | ||
| 1947 | |||
| 1948 | size = event->header.size; | ||
| 1949 | |||
| 1950 | if (size < sizeof(struct perf_event_header) || | ||
| 1951 | (skip = perf_session__process_event(session, event, file_pos)) < 0) { | ||
| 1952 | pr_err("%#" PRIx64 " [%#x]: failed to process type: %d\n", | ||
| 1953 | decomp->file_pos + decomp->head, event->header.size, event->header.type); | ||
| 1954 | return -EINVAL; | ||
| 1955 | } | ||
| 1956 | |||
| 1957 | if (skip) | ||
| 1958 | size += skip; | ||
| 1959 | |||
| 1960 | decomp->head += size; | ||
| 1961 | } | ||
| 1962 | |||
| 1963 | return 0; | ||
| 1964 | } | ||
| 1965 | |||
| 1856 | /* | 1966 | /* |
| 1857 | * On 64bit we can mmap the data file in one go. No need for tiny mmap | 1967 | * On 64bit we can mmap the data file in one go. No need for tiny mmap |
| 1858 | * slices. On 32bit we use 32MB. | 1968 | * slices. On 32bit we use 32MB. |
| @@ -1962,6 +2072,10 @@ more: | |||
| 1962 | head += size; | 2072 | head += size; |
| 1963 | file_pos += size; | 2073 | file_pos += size; |
| 1964 | 2074 | ||
| 2075 | err = __perf_session__process_decomp_events(session); | ||
| 2076 | if (err) | ||
| 2077 | goto out; | ||
| 2078 | |||
| 1965 | ui_progress__update(prog, size); | 2079 | ui_progress__update(prog, size); |
| 1966 | 2080 | ||
| 1967 | if (session_done()) | 2081 | if (session_done()) |
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 6c984c895924..dd8920b745bc 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h | |||
| @@ -39,6 +39,16 @@ struct perf_session { | |||
| 39 | u64 bytes_transferred; | 39 | u64 bytes_transferred; |
| 40 | u64 bytes_compressed; | 40 | u64 bytes_compressed; |
| 41 | struct zstd_data zstd_data; | 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 | struct perf_tool; | 54 | struct perf_tool; |
diff --git a/tools/perf/util/zstd.c b/tools/perf/util/zstd.c index 359ec9a9d306..23bdb9884576 100644 --- a/tools/perf/util/zstd.c +++ b/tools/perf/util/zstd.c | |||
| @@ -9,6 +9,21 @@ int zstd_init(struct zstd_data *data, int level) | |||
| 9 | { | 9 | { |
| 10 | size_t ret; | 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 | data->cstream = ZSTD_createCStream(); | 27 | data->cstream = ZSTD_createCStream(); |
| 13 | if (data->cstream == NULL) { | 28 | if (data->cstream == NULL) { |
| 14 | pr_err("Couldn't create compression stream.\n"); | 29 | pr_err("Couldn't create compression stream.\n"); |
| @@ -26,6 +41,11 @@ int zstd_init(struct zstd_data *data, int level) | |||
| 26 | 41 | ||
| 27 | int zstd_fini(struct zstd_data *data) | 42 | int zstd_fini(struct zstd_data *data) |
| 28 | { | 43 | { |
| 44 | if (data->dstream) { | ||
| 45 | ZSTD_freeDStream(data->dstream); | ||
| 46 | data->dstream = NULL; | ||
| 47 | } | ||
| 48 | |||
| 29 | if (data->cstream) { | 49 | if (data->cstream) { |
| 30 | ZSTD_freeCStream(data->cstream); | 50 | ZSTD_freeCStream(data->cstream); |
| 31 | data->cstream = NULL; | 51 | data->cstream = NULL; |
| @@ -68,3 +88,24 @@ size_t zstd_compress_stream_to_records(struct zstd_data *data, void *dst, size_t | |||
| 68 | 88 | ||
| 69 | return compressed; | 89 | return compressed; |
| 70 | } | 90 | } |
| 91 | |||
| 92 | size_t zstd_decompress_stream(struct zstd_data *data, void *src, size_t src_size, | ||
| 93 | void *dst, size_t dst_size) | ||
| 94 | { | ||
| 95 | size_t ret; | ||
| 96 | ZSTD_inBuffer input = { src, src_size, 0 }; | ||
| 97 | ZSTD_outBuffer output = { dst, dst_size, 0 }; | ||
| 98 | |||
| 99 | while (input.pos < input.size) { | ||
| 100 | ret = ZSTD_decompressStream(data->dstream, &output, &input); | ||
| 101 | if (ZSTD_isError(ret)) { | ||
| 102 | pr_err("failed to decompress (B): %ld -> %ld : %s\n", | ||
| 103 | src_size, output.size, ZSTD_getErrorName(ret)); | ||
| 104 | break; | ||
| 105 | } | ||
| 106 | output.dst = dst + output.pos; | ||
| 107 | output.size = dst_size - output.pos; | ||
| 108 | } | ||
| 109 | |||
| 110 | return output.pos; | ||
| 111 | } | ||
