Received: by 2002:ac0:bc90:0:0:0:0:0 with SMTP id a16csp637092img; Mon, 18 Mar 2019 10:47:41 -0700 (PDT) X-Google-Smtp-Source: APXvYqyAo6vM5OvPVmYHSRF9k8jaK0NS8XwgPdIUCObtYLs3EME4hlvOQxeYuQpm6Svn5ovjT446 X-Received: by 2002:a17:902:8a8a:: with SMTP id p10mr20543461plo.92.1552931261211; Mon, 18 Mar 2019 10:47:41 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1552931261; cv=none; d=google.com; s=arc-20160816; b=pfTb5XuU5O8n/ha+n730d7uCdtZERG+OgDoddQoQ50czn08Osz8vQbKT8f016chw1N OR2F6Y4Y5qh9Pc4QoO+iwkfL0x0yO8R/Y28oFqDUn9eZBaKMu4GwNl8bOeO9zlpLPBDh 1QYJLygF7B3R5zoIEMxy1GMXYH8YLW5W6U9XnSQwXf/CuQOtakchn5NeuTqfjdC/U8Nf Xg2G3TnDEsPKu7xbUBzyFu3934NpEATafztv0FA+2EDPm5InJ/urkkeBdczjKRZ034PL naGnzjPsGwtYV5R8PbenaBox21q2B5SwWAGscry+ujkcq2PAv+Jltu4gT7PL5wDBkHdN thBg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding :content-language:in-reply-to:mime-version:user-agent:date :message-id:organization:references:cc:to:from:subject; bh=5DIlC6Jixz9OazYimmLaeXvuAc9HJ8g3R73Pm/2wh08=; b=Uhqcc/mrdlt3nve11rtQox4XoswSnTL9LAmqPugunNfi/EpCL/leUG7gqTE5UL551N OI/XZwMEl/AbSC1EiiDUm8kjgzcnnpBTI1WBa1msYx9P3dF1/IL1svN+zSITAGNA/Xk9 DJtci+8hz9dV115VUyP1HxGVXllY+U93c6dBtpVREFk63FAIYyqCU+nAIsA7nInLBvsb /rR4mXljrXe9AD/8u0skiaYdH1JOw8XEkBYF2C5e+2/MPKDGoO2hXD0RGcCAncb51xuG eym3zb15XFQQNy/MN0xWggMZnwTgOGcY1bj6pz12PJWQ8uEa67IFvJ8VyS3x6gl8X9XA leLg== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=intel.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id bi9si9473294plb.174.2019.03.18.10.47.26; Mon, 18 Mar 2019 10:47:41 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=intel.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726812AbfCRRpX (ORCPT + 99 others); Mon, 18 Mar 2019 13:45:23 -0400 Received: from mga05.intel.com ([192.55.52.43]:48089 "EHLO mga05.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726959AbfCRRpQ (ORCPT ); Mon, 18 Mar 2019 13:45:16 -0400 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga008.fm.intel.com ([10.253.24.58]) by fmsmga105.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 18 Mar 2019 10:45:15 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.58,494,1544515200"; d="scan'208";a="132632082" Received: from linux.intel.com ([10.54.29.200]) by fmsmga008.fm.intel.com with ESMTP; 18 Mar 2019 10:45:15 -0700 Received: from [10.251.91.19] (abudanko-mobl.ccr.corp.intel.com [10.251.91.19]) by linux.intel.com (Postfix) with ESMTP id 519D5580238; Mon, 18 Mar 2019 10:45:12 -0700 (PDT) Subject: [PATCH v10 10/12] perf report: implement record trace decompression From: Alexey Budankov To: Arnaldo Carvalho de Melo Cc: Jiri Olsa , Namhyung Kim , Alexander Shishkin , Peter Zijlstra , Ingo Molnar , Andi Kleen , linux-kernel References: <12cce142-6238-475b-b9aa-236531c12c2b@linux.intel.com> Organization: Intel Corp. Message-ID: <304b0a59-942c-3fe1-da02-aa749f87108b@linux.intel.com> Date: Mon, 18 Mar 2019 20:45:11 +0300 User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:60.0) Gecko/20100101 Thunderbird/60.5.1 MIME-Version: 1.0 In-Reply-To: <12cce142-6238-475b-b9aa-236531c12c2b@linux.intel.com> Content-Type: text/plain; charset=utf-8 Content-Language: en-US Content-Transfer-Encoding: 7bit Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org zstd_init(, comp_level = 0) initializes decompression part of API only that now consists of zstd_decompress_stream() function. Trace frames containing 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 trace (e.g., perf report -D --header) file offsets of events from compressed records are printed as zero. Signed-off-by: Alexey Budankov --- tools/perf/builtin-report.c | 5 +- tools/perf/util/compress.h | 11 +++ tools/perf/util/session.c | 129 +++++++++++++++++++++++++++++++++++- tools/perf/util/session.h | 10 +++ tools/perf/util/tool.h | 2 + tools/perf/util/zstd.c | 40 +++++++++++ 6 files changed, 195 insertions(+), 2 deletions(-) diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 1921aaa9cece..f8f899245289 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -1260,6 +1260,9 @@ int cmd_report(int argc, const char **argv) if (session == NULL) return -1; + if (zstd_init(&(session->zstd_data), 0) < 0) + pr_warning("Decompression initialization failed. Reported data may be incomplete.\n"); + if (report.queue_size) { ordered_events__set_alloc_size(&session->ordered_events, report.queue_size); @@ -1450,7 +1453,7 @@ int cmd_report(int argc, const char **argv) error: if (report.ptime_range) zfree(&report.ptime_range); - + zstd_fini(&(session->zstd_data)); perf_session__delete(session); return ret; } diff --git a/tools/perf/util/compress.h b/tools/perf/util/compress.h index d00d7cb095aa..46127f7e4563 100644 --- a/tools/perf/util/compress.h +++ b/tools/perf/util/compress.h @@ -20,6 +20,7 @@ bool lzma_is_compressed(const char *input); struct zstd_data { #ifdef HAVE_ZSTD_SUPPORT ZSTD_CStream *cstream; + ZSTD_DStream *dstream; #endif }; @@ -32,6 +33,9 @@ size_t zstd_compress_stream_to_records(struct zstd_data *data, void *dst, size_t dst_size, void *src, size_t src_size, size_t max_record_size, size_t process_header(void *record, size_t increment)); +size_t zstd_decompress_stream(struct zstd_data *data, + void *src, size_t src_size, void *dst, size_t dst_size); + #else /* !HAVE_ZSTD_SUPPORT */ static inline int zstd_init(struct zstd_data *data __maybe_unused, int level __maybe_unused) @@ -53,6 +57,13 @@ static inline size_t zstd_compress_stream_to_records(struct zstd_data *data __ma return 0; } +static inline size_t zstd_decompress_stream(struct zstd_data *data __maybe_unused, void *src __maybe_unused, + size_t src_size __maybe_unused, void *dst __maybe_unused, + size_t dst_size __maybe_unused) +{ + return 0; +} + #endif #endif /* PERF_COMPRESS_H */ diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 0ec34227bd60..81b7c09a97d1 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -29,6 +29,67 @@ #include "stat.h" #include "arch/common.h" +#ifdef HAVE_ZSTD_SUPPORT +static int perf_session__process_compressed_event(struct perf_session *session, + union perf_event *event, u64 file_offset) +{ + void *src; + size_t decomp_size, src_size; + u64 decomp_last_rem = 0; + size_t decomp_len = session->header.env.comp_mmap_len; + struct decomp *decomp, *decomp_last = session->decomp_last; + + decomp = mmap(NULL, sizeof(struct decomp) + decomp_len, PROT_READ|PROT_WRITE, + MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); + if (decomp == MAP_FAILED) { + pr_err("Couldn't allocate memory for decompression\n"); + return -1; + } + + decomp->file_pos = file_offset; + decomp->head = 0; + + if (decomp_last) { + decomp_last_rem = decomp_last->size - decomp_last->head; + memcpy(decomp->data, &(decomp_last->data[decomp_last->head]), decomp_last_rem); + decomp->size = decomp_last_rem; + } + + src = (void *)event + sizeof(struct compressed_event); + src_size = event->pack.header.size - sizeof(struct compressed_event); + + decomp_size = zstd_decompress_stream(&(session->zstd_data), src, src_size, + &(decomp->data[decomp_last_rem]), decomp_len - decomp_last_rem); + if (!decomp_size) { + munmap(decomp, sizeof(struct decomp) + decomp_len); + pr_err("Couldn't decompress data\n"); + return -1; + } + + decomp->size += decomp_size; + + if (session->decomp == NULL) { + session->decomp = decomp; + session->decomp_last = decomp; + } else { + session->decomp_last->next = decomp; + session->decomp_last = decomp; + } + + pr_debug("decomp (B): %ld to %ld\n", src_size, decomp_size); + + return 0; +} +#else /* !HAVE_ZSTD_SUPPORT */ +static int perf_session__process_compressed_event(struct perf_session *session __maybe_unused, + union perf_event *event __maybe_unused, + u64 file_offset __maybe_unused) +{ + dump_printf(": unhandled!\n"); + return 0; +} +#endif + static int perf_session__deliver_event(struct perf_session *session, union perf_event *event, struct perf_tool *tool, @@ -196,6 +257,21 @@ static void perf_session__delete_threads(struct perf_session *session) machine__delete_threads(&session->machines.host); } +static void perf_session__release_decomp_events(struct perf_session *session) +{ + struct decomp *next, *decomp; + size_t decomp_len; + next = session->decomp; + decomp_len = session->header.env.comp_mmap_len; + do { + decomp = next; + if (decomp == NULL) + break; + next = decomp->next; + munmap(decomp, decomp_len + sizeof(struct decomp)); + } while (1); +} + void perf_session__delete(struct perf_session *session) { if (session == NULL) @@ -204,6 +280,7 @@ void perf_session__delete(struct perf_session *session) auxtrace_index__free(&session->auxtrace_index); perf_session__destroy_kernel_maps(session); perf_session__delete_threads(session); + perf_session__release_decomp_events(session); perf_env__exit(&session->header.env); machines__exit(&session->machines); if (session->data) @@ -429,6 +506,8 @@ void perf_tool__fill_defaults(struct perf_tool *tool) tool->time_conv = process_event_op2_stub; if (tool->feature == NULL) tool->feature = process_event_op2_stub; + if (tool->compressed == NULL) + tool->compressed = perf_session__process_compressed_event; } static void swap_sample_id_all(union perf_event *event, void *data) @@ -1372,7 +1451,8 @@ static s64 perf_session__process_user_event(struct perf_session *session, int fd = perf_data__fd(session->data); int err; - dump_event(session->evlist, event, file_offset, &sample); + if (event->header.type != PERF_RECORD_COMPRESSED) + dump_event(session->evlist, event, file_offset, &sample); /* These events are processed right away */ switch (event->header.type) { @@ -1425,6 +1505,11 @@ static s64 perf_session__process_user_event(struct perf_session *session, return tool->time_conv(session, event); case PERF_RECORD_HEADER_FEATURE: return tool->feature(session, event); + case PERF_RECORD_COMPRESSED: + err = tool->compressed(session, event, file_offset); + if (err) + dump_event(session->evlist, event, file_offset, &sample); + return 0; default: return -EINVAL; } @@ -1707,6 +1792,8 @@ static int perf_session__flush_thread_stacks(struct perf_session *session) volatile int session_done; +static int __perf_session__process_decomp_events(struct perf_session *session); + static int __perf_session__process_pipe_events(struct perf_session *session) { struct ordered_events *oe = &session->ordered_events; @@ -1787,6 +1874,10 @@ static int __perf_session__process_pipe_events(struct perf_session *session) if (skip > 0) head += skip; + err = __perf_session__process_decomp_events(session); + if (err) + goto out_err; + if (!session_done()) goto more; done: @@ -1835,6 +1926,38 @@ fetch_mmaped_event(struct perf_session *session, return event; } +static int __perf_session__process_decomp_events(struct perf_session *session) +{ + s64 skip; + u64 size, file_pos = 0; + union perf_event *event; + struct decomp *decomp = session->decomp_last; + + if (!decomp) + return 0; + + while (decomp->head < decomp->size && !session_done()) { + event = fetch_mmaped_event(session, decomp->head, decomp->size, decomp->data); + if (!event) + break; + + size = event->header.size; + if (size < sizeof(struct perf_event_header) || + (skip = perf_session__process_event(session, event, file_pos)) < 0) { + pr_err("%#" PRIx64 " [%#x]: failed to process type: %d\n", + decomp->file_pos + decomp->head, event->header.size, event->header.type); + return -EINVAL; + } + + if (skip) + size += skip; + + decomp->head += size; + } + + return 0; +} + /* * On 64bit we can mmap the data file in one go. No need for tiny mmap * slices. On 32bit we use 32MB. @@ -1942,6 +2065,10 @@ reader__process_events(struct reader *rd, struct perf_session *session, head += size; file_pos += size; + err = __perf_session__process_decomp_events(session); + if (err) + goto out; + ui_progress__update(prog, size); 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 { u64 bytes_transferred; u64 bytes_compressed; struct zstd_data zstd_data; + struct decomp *decomp; + struct decomp *decomp_last; +}; + +struct decomp { + struct decomp *next; + u64 file_pos; + u64 head; + size_t size; + char data[]; }; struct perf_tool; diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h index 250391672f9f..9096a6e3de59 100644 --- a/tools/perf/util/tool.h +++ b/tools/perf/util/tool.h @@ -28,6 +28,7 @@ typedef int (*event_attr_op)(struct perf_tool *tool, typedef int (*event_op2)(struct perf_session *session, union perf_event *event); typedef s64 (*event_op3)(struct perf_session *session, union perf_event *event); +typedef int (*event_op4)(struct perf_session *session, union perf_event *event, u64 data); typedef int (*event_oe)(struct perf_tool *tool, union perf_event *event, struct ordered_events *oe); @@ -72,6 +73,7 @@ struct perf_tool { stat, stat_round, feature; + event_op4 compressed; event_op3 auxtrace; bool ordered_events; bool ordering_requires_timestamps; diff --git a/tools/perf/util/zstd.c b/tools/perf/util/zstd.c index 6d4f69d57567..15aa02c933ef 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) { size_t ret; + data->dstream = ZSTD_createDStream(); + if (data->dstream == NULL) { + pr_err("Couldn't create decompression stream.\n"); + return -1; + } + + ret = ZSTD_initDStream(data->dstream); + if (ZSTD_isError(ret)) { + pr_err("Failed to initialize decompression stream: %s\n", ZSTD_getErrorName(ret)); + return -1; + } + + if (!level) + return 0; + data->cstream = ZSTD_createCStream(); if (data->cstream == NULL) { pr_err("Couldn't create compression stream.\n"); @@ -26,6 +41,11 @@ int zstd_init(struct zstd_data *data, int level) int zstd_fini(struct zstd_data *data) { + if (data->dstream) { + ZSTD_freeDStream(data->dstream); + data->dstream = NULL; + } + if (data->cstream) { ZSTD_freeCStream(data->cstream); data->cstream = NULL; @@ -69,3 +89,23 @@ size_t zstd_compress_stream_to_records(struct zstd_data *data, return compressed; } +size_t zstd_decompress_stream(struct zstd_data *data, + void *src, size_t src_size, void *dst, size_t dst_size) +{ + size_t ret; + ZSTD_inBuffer input = { src, src_size, 0 }; + ZSTD_outBuffer output = { dst, dst_size, 0 }; + + while (input.pos < input.size) { + ret = ZSTD_decompressStream(data->dstream, &output, &input); + if (ZSTD_isError(ret)) { + pr_err("failed to decompress (B): %ld -> %ld : %s\n", + src_size, output.size, ZSTD_getErrorName(ret)); + break; + } + output.dst = dst + output.pos; + output.size = dst_size - output.pos; + } + + return output.pos; +} -- 2.20.1