From patchwork Mon Jan 4 17:46:41 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11997135 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 77FACC433E0 for ; Mon, 4 Jan 2021 17:48:19 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 53928206CB for ; Mon, 4 Jan 2021 17:48:19 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726019AbhADRsT (ORCPT ); Mon, 4 Jan 2021 12:48:19 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34248 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725889AbhADRsS (ORCPT ); Mon, 4 Jan 2021 12:48:18 -0500 Received: from mail-ed1-x52e.google.com (mail-ed1-x52e.google.com [IPv6:2a00:1450:4864:20::52e]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7B640C061793 for ; Mon, 4 Jan 2021 09:47:38 -0800 (PST) Received: by mail-ed1-x52e.google.com with SMTP id r5so28191902eda.12 for ; Mon, 04 Jan 2021 09:47:38 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=Ctl/1hc1CjgpqETA+YlX+MS6l+shLWrhVEkHHBmBygY=; b=dTrLrVAuOEe1Y3zT+63siD5MWNgksQ++tBgn2buEuWMxMqKKVzFVrjKe38iT4PVfqf RkuFvQD0XeUeTa1vKc701bs9//gdR5qRcPDif8UjcrSLDhUEpOpod9uBtyfbJhjA9fqa UvJ+sOHaNOrA0Bz4vML2Tyfqaj6US9gN5eP4U1O/3tCe4VvpZd8jJLjfRL1/bhPJ67Kr aEztcGDCvpeqI7DH4OIwA1qshX5faHmcHPfGuIsLdN8N83rl6CsrbPQPyKQTvgxfGXHv jQM/Ry1juI/i/etetCqkuE7MsjP672/FZtAbWnshQW9eficKk68SNknEBMRD5itl0mIS N1ew== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=Ctl/1hc1CjgpqETA+YlX+MS6l+shLWrhVEkHHBmBygY=; b=NWLhhxIcP4sUQT4pv/u4MxGmbzzTZr+KqiItQhD0r26RxhLUNA86VFkhl3qUawZ73p GjKyS77PkGro+3fZGv5AM/eto+FQIM0Q8sT9efSDRvdUFf5OqVQe3GpD3oUjaGRQ9psz Pb5MmOfonAW+11pGmaxVEWkj0mf7a7f0yphrjL4Imk93cU80IXXFeoFZugc3W/4jIrSb E7rMEnGB98jX/0GPzFaaCGb4l5k0B4LCLvfUDUxmTnZ14qHzCyzkbkOrf/mu1cDg0Lya xSGNS4bh9Swy4ddUxenSSQVgxkPcMuB1GP670eOxlPVb2af0ZziqCj2Hc/RWicbbw7z5 bOYA== X-Gm-Message-State: AOAM531GWb8yeSkojtjfprx7QSDwL5tYtFORR/ymBIb6FBGyw1CLewGx qEtVsscoZxD5bCUpaxYMqYfnE0QDRpWADQ== X-Google-Smtp-Source: ABdhPJyIoBlDV/4siZj6gKtCGSkihg09BvyjRtGmtmoNVqheRNMXqQatwDiIMxEDL292lvXqM8+u2Q== X-Received: by 2002:aa7:cb49:: with SMTP id w9mr74215709edt.357.1609782457226; Mon, 04 Jan 2021 09:47:37 -0800 (PST) Received: from localhost.localdomain ([95.87.199.238]) by smtp.gmail.com with ESMTPSA id l14sm44107750edq.35.2021.01.04.09.47.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 04 Jan 2021 09:47:36 -0800 (PST) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v8 01/44] kernel-shark: Adopt trace-cmd API changes Date: Mon, 4 Jan 2021 19:46:41 +0200 Message-Id: <20210104174724.70404-2-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210104174724.70404-1-y.karadz@gmail.com> References: <20210104174724.70404-1-y.karadz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org Use the new names of the methods free_record() -> tracecmd_free_record() tracecmd_get_pevent() -> tracecmd_get_tep() follow the tracecmd library naming convention. Signed-off-by: Yordan Karadzhov (VMware) --- src/libkshark.c | 20 ++++++++++---------- src/plugins/sched_events.c | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/libkshark.c b/src/libkshark.c index 52aacd3..42dc08c 100644 --- a/src/libkshark.c +++ b/src/libkshark.c @@ -151,7 +151,7 @@ bool kshark_open(struct kshark_context *kshark_ctx, const char *file) } kshark_ctx->handle = handle; - kshark_ctx->pevent = tracecmd_get_pevent(handle); + kshark_ctx->pevent = tracecmd_get_tep(handle); kshark_ctx->advanced_event_filter = tep_filter_alloc(kshark_ctx->pevent); @@ -674,7 +674,7 @@ static void free_rec_list(struct rec_list **rec_list, int n_cpus, temp_rec = rec_list[cpu]; rec_list[cpu] = temp_rec->next; if (type == REC_RECORD) - free_record(temp_rec->rec); + tracecmd_free_record(temp_rec->rec); free(temp_rec); } } @@ -774,14 +774,14 @@ static ssize_t get_records(struct kshark_context *kshark_ctx, if (!kshark_show_task(kshark_ctx, entry->pid)) { entry->visible &= ~kshark_ctx->filter_mask; } - free_record(rec); + tracecmd_free_record(rec); break; } /* REC_ENTRY */ } task = kshark_add_task(kshark_ctx, pid); if (!task) { - free_record(rec); + tracecmd_free_record(rec); goto fail; } @@ -902,7 +902,7 @@ ssize_t kshark_load_data_entries(struct kshark_context *kshark_ctx, * to all fields of the record. * * @param kshark_ctx: Input location for the session context pointer. - * @param data_rows: Output location for the trace data. Use free_record() + * @param data_rows: Output location for the trace data. Use tracecmd_free_record() * to free the elements of the outputted array. * * @returns The size of the outputted data in the case of success, or a @@ -1167,7 +1167,7 @@ int kshark_get_pid_easy(struct kshark_entry *entry) data = tracecmd_read_at(kshark_ctx->handle, entry->offset, NULL); pid = tep_data_pid(kshark_ctx->pevent, data); - free_record(data); + tracecmd_free_record(data); pthread_mutex_unlock(&kshark_ctx->input_mutex); } @@ -1234,7 +1234,7 @@ const char *kshark_get_latency_easy(struct kshark_entry *entry) data = tracecmd_read_at(kshark_ctx->handle, entry->offset, NULL); lat = kshark_get_latency(kshark_ctx->pevent, data); - free_record(data); + tracecmd_free_record(data); pthread_mutex_unlock(&kshark_ctx->input_mutex); @@ -1279,7 +1279,7 @@ int kshark_get_event_id_easy(struct kshark_entry *entry) data = tracecmd_read_at(kshark_ctx->handle, entry->offset, NULL); event_id = tep_data_type(kshark_ctx->pevent, data); - free_record(data); + tracecmd_free_record(data); pthread_mutex_unlock(&kshark_ctx->input_mutex); } @@ -1378,7 +1378,7 @@ const char *kshark_get_info_easy(struct kshark_entry *entry) if (event) info = kshark_get_info(kshark_ctx->pevent, data, event); - free_record(data); + tracecmd_free_record(data); pthread_mutex_unlock(&kshark_ctx->input_mutex); @@ -1492,7 +1492,7 @@ char* kshark_dump_entry(const struct kshark_entry *entry) free(temp_str); } - free_record(data); + tracecmd_free_record(data); if (size < 1) entry_str = NULL; } else { diff --git a/src/plugins/sched_events.c b/src/plugins/sched_events.c index d0fd15e..4b671f1 100644 --- a/src/plugins/sched_events.c +++ b/src/plugins/sched_events.c @@ -160,7 +160,7 @@ static int find_wakeup_pid(struct kshark_context *kshark_ctx, struct kshark_entr record = tracecmd_read_at(kshark_ctx->handle, e->offset, NULL); ret = tep_read_number_field(pid_field, record->data, &val); - free_record(record); + tracecmd_free_record(record); if (ret) return -1; @@ -252,7 +252,7 @@ bool plugin_switch_match_rec_pid(struct kshark_context *kshark_ctx, if (ret == 0 && !(val & 0x7f)) switch_pid = tep_data_pid(plugin_ctx->pevent, record); - free_record(record); + tracecmd_free_record(record); } if (switch_pid >= 0 && switch_pid == pid) From patchwork Mon Jan 4 17:46:42 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11997137 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id B604FC433DB for ; Mon, 4 Jan 2021 17:48:22 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 7CE2C206E4 for ; Mon, 4 Jan 2021 17:48:22 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726072AbhADRsW (ORCPT ); Mon, 4 Jan 2021 12:48:22 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34254 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725889AbhADRsU (ORCPT ); Mon, 4 Jan 2021 12:48:20 -0500 Received: from mail-ed1-x52f.google.com (mail-ed1-x52f.google.com [IPv6:2a00:1450:4864:20::52f]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 9B31EC061794 for ; Mon, 4 Jan 2021 09:47:39 -0800 (PST) Received: by mail-ed1-x52f.google.com with SMTP id h16so28202448edt.7 for ; Mon, 04 Jan 2021 09:47:39 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=/MssCVWttlOSkk+O7dcpV0ElxXwUugI/eWzTY461130=; b=dp0dXJ++WkHZ97smGi7rR5HlexmVad5AEXYQfa9G8v0UH0cgBMQi30WBJGyLvgTmd7 QsydfEauCe4DuhtYp/e7H9jLG0e/BJ3mSaNX4r7XwadWnARWMS3pF/y3i8q6FSr2Lig+ i1QpBv0hJ8sd3Nt+eKH2uokMoAFB+8GSzXrpqZ5ZGYPNu2+KcVSEFYFftRUjJL/tzQjA jQ8/7OopPHT9nEEyxQ7Il867hZH1O1xIG922FIv8tG6RjgrwR3l3f+rLj3SXasoazvnx HwK+NNMd9kywAHArsYC2C54MzVYnAIDvjIAbKCGVqQhUn4thazmvm5C465yjjW62XoW4 jebA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=/MssCVWttlOSkk+O7dcpV0ElxXwUugI/eWzTY461130=; b=D2SDjQCKAmhoGWpQrPR6LQ6IQfkKJpORiSzYe7CwjBhZ6PUXQQKV5D9MBfuF9U76UA FWWw+erh2bRs0Ur10mtrtHXh82Ba2L98WNPqp0fhNnTwLlS5h+++5fKYxbofeFbwSt50 vB62q2dbQGXftXUbLdtWoe+atu2TY+OvxxyZWCRXSkTYaraHQBTuCaUjfsxJbdy1qBo6 DJmN30TkHzgEOqKjVD2G62zUSBz6gyxvudL2HfJtgfhbo1L1kacs9CSAIjUHHddVK/vS S56Kg+bJDAy6DGK6FXTccA6mVD52WU2hT3EPXSDeBz6Od8v+4mINwpIshkZuHzrIvj/U JHBA== X-Gm-Message-State: AOAM533UCO9lHOlbw51NO1B8ZZJteGbg3+x+GB8YH12X/u0TeHJvuHU8 sUpXmJ/CET+4yo45aFBtmEE= X-Google-Smtp-Source: ABdhPJx8kLA/ad9NjS9WNkbAWPHpF97xd9RGChhWlJg6TOsWw8YK3N7+RdPqwmILbrJgfTC9hIyJlg== X-Received: by 2002:a05:6402:b9a:: with SMTP id cf26mr70866588edb.372.1609782458286; Mon, 04 Jan 2021 09:47:38 -0800 (PST) Received: from localhost.localdomain ([95.87.199.238]) by smtp.gmail.com with ESMTPSA id l14sm44107750edq.35.2021.01.04.09.47.37 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 04 Jan 2021 09:47:37 -0800 (PST) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v8 02/44] kernel-shark: Use libtraceevent and libtracefs Date: Mon, 4 Jan 2021 19:46:42 +0200 Message-Id: <20210104174724.70404-3-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210104174724.70404-1-y.karadz@gmail.com> References: <20210104174724.70404-1-y.karadz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org libtraceevent and libtracefs are now stand-alone libraries, independent form trace-cmd. Start building and linking KernelShark against those independent libraries. All 3 libraries (libtraceevent, libtracefs and libtracecmd) are being detected by using pkg-config. Since trace-filter-hash.h is no longer distributed as a public header by the libtracecmd, we are adding a copy of it. This is a temporary solution. In the folowing patches we will introduce a new implementation of the hash table provided by trace-filter-hash.h and the header itself will be removed once it is no longer needed. Signed-off-by: Yordan Karadzhov (VMware) --- CMakeLists.txt | 9 ++- build/FindTraceCmd.cmake | 138 +++++++++++++++---------------------- build/FindTraceEvent.cmake | 76 ++++++++++++++++++++ build/FindTraceFS.cmake | 76 ++++++++++++++++++++ src/CMakeLists.txt | 6 +- src/libkshark.c | 10 +-- src/libkshark.h | 2 +- src/trace-filter-hash.h | 64 +++++++++++++++++ 8 files changed, 286 insertions(+), 95 deletions(-) create mode 100644 build/FindTraceEvent.cmake create mode 100644 build/FindTraceFS.cmake create mode 100644 src/trace-filter-hash.h diff --git a/CMakeLists.txt b/CMakeLists.txt index c95249e..3ea40f0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,7 +21,11 @@ if (NOT _LIBDIR) set(_LIBDIR "${_INSTALL_PREFIX}/lib") endif (NOT _LIBDIR) -include(${KS_DIR}/build/FindTraceCmd.cmake) +set(CMAKE_MODULE_PATH "${KS_DIR}/build") +find_package(TraceEvent REQUIRED) +find_package(TraceFS REQUIRED) +find_package(TraceCmd REQUIRED) + include(${KS_DIR}/build/FindJSONC.cmake) find_package(Doxygen) @@ -80,8 +84,7 @@ endif (CMAKE_BUILD_TYPE MATCHES Package) include_directories(${KS_DIR}/src/ ${KS_DIR}/build/src/ ${JSONC_INCLUDE_DIR} - ${TRACECMD_INCLUDE_DIR} - ${TRACEFS_INCLUDE_DIR}) + ${TRACECMD_INCLUDE_DIR}) message("") message(STATUS "C flags : " ${CMAKE_C_FLAGS}) diff --git a/build/FindTraceCmd.cmake b/build/FindTraceCmd.cmake index 02f4529..23eefed 100644 --- a/build/FindTraceCmd.cmake +++ b/build/FindTraceCmd.cmake @@ -1,106 +1,78 @@ -# Find traceevent and trace-cmd -# This module finds an installed trace-cmd package. -# -# It sets the following variables: -# TRACEEVENT_LIBRARY, traceevent the library. -# TRACEEVENT_FOUND, If false, do not try to use traceevent. -# -# TRACECMD_INCLUDE_DIR, where to find trace-cmd header. -# TRACEFS_INCLUDE_DIR, where to find tracefs header. -# TRACEFS_LIBRARY, the tracefs library. -# TRACECMD_LIBRARY, the trace-cmd library. -# TRACECMD_FOUND, If false, do not try to use trace-cmd. +# SPDX-License-Identifier: LGPL-2.1 -# MESSAGE(" Looking for trace-cmd ...") +#[=======================================================================[.rst: +FindTraceCmd +------- -# First search in the user provided paths. -if (CMAKE_BUILD_TYPE MATCHES Debug) +Finds the tracecmd library. - find_program(TRACECMD_EXECUTABLE NAMES trace-cmd - PATHS $ENV{TRACE_CMD}/tracecmd/ - NO_DEFAULT_PATH) +Imported Targets +^^^^^^^^^^^^^^^^ -endif (CMAKE_BUILD_TYPE MATCHES Debug) +This module defines the :prop_tgt:`IMPORTED` targets: -if (NOT TRACECMD_EXECUTABLE) +``trace::cmd`` + Defined if the system has libtracecmd. - set(TRACECMD_EXECUTABLE "${_INSTALL_PREFIX}/bin/trace-cmd") +Result Variables +^^^^^^^^^^^^^^^^ -endif (NOT TRACECMD_EXECUTABLE) +``TraceCmd_FOUND`` + True if the system has the libtracecmd library. +``TraceCmd_VERSION`` + The version of the libtracecmd library which was found. +``TraceCmd_INCLUDE_DIRS`` + Include directories needed to use libtracecmd. +``TraceCmd_LIBRARIES`` + Libraries needed to link to libtracecmd. -find_path(TRACECMD_INCLUDE_DIR NAMES trace-cmd/trace-cmd.h - PATHS $ENV{TRACE_CMD}/include/ - NO_DEFAULT_PATH) +Cache Variables +^^^^^^^^^^^^^^^ -find_path(TRACEFS_INCLUDE_DIR NAMES tracefs/tracefs.h - PATHS $ENV{TRACE_CMD}/include/ - NO_DEFAULT_PATH) +``TraceCmd_INCLUDE_DIR`` + The directory containing ``trace-cmd.h``. +``TraceCmd_LIBRARY`` + The path to the tracecmd library. -find_library(TRACECMD_LIBRARY NAMES trace-cmd/libtracecmd.a - PATHS $ENV{TRACE_CMD}/lib/ - NO_DEFAULT_PATH) +#]=======================================================================] -find_library(TRACEFS_LIBRARY NAMES tracefs/libtracefs.a - PATHS $ENV{TRACE_CMD}/lib/ - NO_DEFAULT_PATH) +find_package(PkgConfig QUIET) +pkg_check_modules(PC_TraceCmd QUIET libtracecmd) -find_library(TRACEEVENT_LIBRARY NAMES traceevent/libtraceevent.a - PATHS $ENV{TRACE_CMD}/lib/ - NO_DEFAULT_PATH) +set(TraceCmd_VERSION ${PC_TraceCmd_VERSION}) +set(TraceCmd_DEFINITIONS ${PC_TraceCmd_CFLAGS_OTHER}) -# If not found, search in the default system paths. Note that if the previous -# search was successful "find_path" will do nothing this time. -find_program(TRACECMD_EXECUTABLE NAMES trace-cmd) -find_path(TRACECMD_INCLUDE_DIR NAMES trace-cmd/trace-cmd.h) -find_path(TRACEFS_INCLUDE_DIR NAMES tracefs/tracefs.h) -find_library(TRACECMD_LIBRARY NAMES trace-cmd/libtracecmd.so) -find_library(TRACEFS_LIBRARY NAMES tracefs/libtracefs.so) -find_library(TRACEEVENT_LIBRARY NAMES traceevent/libtraceevent.so) +find_path(TraceCmd_INCLUDE_DIR NAMES trace-cmd/trace-cmd.h + HINTS ${PC_TraceCmd_INCLUDE_DIRS} + ${PC_TraceCmd_INCLUDEDIR}) -IF (TRACECMD_INCLUDE_DIR AND TRACECMD_LIBRARY) +find_library(TraceCmd_LIBRARY NAMES tracecmd libtracecmd + HINTS ${PC_TraceCmd_LIBDIR} + ${PC_TraceCmdLIBRARY_DIRS}) - SET(TRACECMD_FOUND TRUE) +mark_as_advanced(TraceCmd_INCLUDE_DIR TraceCmd_LIBRARY) -ENDIF (TRACECMD_INCLUDE_DIR AND TRACECMD_LIBRARY) +include(FindPackageHandleStandardArgs) -IF (TRACECMD_FOUND) +find_package_handle_standard_args(TraceCmd DEFAULT_MSG + TraceCmd_LIBRARY TraceCmd_INCLUDE_DIR) - MESSAGE(STATUS "Found trace-cmd: ${TRACECMD_LIBRARY}") +if(TraceCmd_FOUND) -ELSE (TRACECMD_FOUND) + set(TraceCmd_LIBRARIES ${TraceCmd_LIBRARY}) + set(TraceCmd_INCLUDE_DIRS ${TraceCmd_INCLUDE_DIR}) - MESSAGE(FATAL_ERROR "\nCould not find trace-cmd!\n") + if(NOT TARGET trace::cmd) + add_library(trace::cmd UNKNOWN IMPORTED) -ENDIF (TRACECMD_FOUND) + set_target_properties(trace::cmd + PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${TraceCmd_INCLUDE_DIRS}" + INTERFACE_COMPILE_DEFINITIONS "${TraceCmd_DEFINITIONS}" + IMPORTED_LINK_INTERFACE_LANGUAGES "C" + IMPORTED_LOCATION "${TraceCmd_LIBRARIES}") + endif() -IF (TRACEFS_INCLUDE_DIR AND TRACEFS_LIBRARY) +endif() - SET(TRACEFS_FOUND TRUE) - -ENDIF (TRACEFS_INCLUDE_DIR AND TRACEFS_LIBRARY) - -IF (TRACEFS_FOUND) - - MESSAGE(STATUS "Found tracefs: ${TRACEFS_LIBRARY}") - -ELSE (TRACEFS_FOUND) - - MESSAGE(FATAL_ERROR "\nCould not find tracefs!\n") - -ENDIF (TRACEFS_FOUND) - -IF (TRACEEVENT_LIBRARY) - - SET(TRACEEVENT_FOUND TRUE) - -ENDIF (TRACEEVENT_LIBRARY) - -IF (TRACEEVENT_FOUND) - - MESSAGE(STATUS "Found traceevent: ${TRACEEVENT_LIBRARY}") - -ELSE (TRACEEVENT_FOUND) - - MESSAGE(FATAL_ERROR "\nCould not find libtraceevent!\n") - -ENDIF (TRACEEVENT_FOUND) +find_program(TRACECMD_EXECUTABLE NAMES trace-cmd) diff --git a/build/FindTraceEvent.cmake b/build/FindTraceEvent.cmake new file mode 100644 index 0000000..ae391b6 --- /dev/null +++ b/build/FindTraceEvent.cmake @@ -0,0 +1,76 @@ +# SPDX-License-Identifier: LGPL-2.1 + +#[=======================================================================[.rst: +FindTraceevent +------- + +Finds the traceevent library. + +Imported Targets +^^^^^^^^^^^^^^^^ + +This module defines the :prop_tgt:`IMPORTED` targets: + +``trace::event`` + Defined if the system has libtraceevent. + +Result Variables +^^^^^^^^^^^^^^^^ + +``TraceEvent_FOUND`` + True if the system has the libtraceevent library. +``TraceEvent_VERSION`` + The version of the libtraceevent library which was found. +``TraceEvent_INCLUDE_DIRS`` + Include directories needed to use libtraceevent. +``TraceEvent_LIBRARIES`` + Libraries needed to link to libtraceevent. + +Cache Variables +^^^^^^^^^^^^^^^ + +``TraceEvent_INCLUDE_DIR`` + The directory containing ``event-parse.h``. +``TraceEvent_LIBRARY`` + The path to the traceevent library. + +#]=======================================================================] + +find_package(PkgConfig QUIET) +pkg_check_modules(PC_TraceEvent QUIET libtraceevent) + +set(TraceEvent_VERSION ${PC_TraceEvent_VERSION}) +set(TraceEvent_DEFINITIONS ${PC_TraceEvent_CFLAGS_OTHER}) + +find_path(TraceEvent_INCLUDE_DIR NAMES traceevent/event-parse.h + HINTS ${PC_TraceEvent_INCLUDE_DIRS} + ${PC_TraceEvent_INCLUDEDIR}) + +find_library(TraceEvent_LIBRARY NAMES traceevent libtraceevent + HINTS ${PC_TraceEvent_LIBDIR} + ${PC_TraceEventLIBRARY_DIRS}) + +mark_as_advanced(TraceEvent_INCLUDE_DIR TraceEvent_LIBRARY) + +include(FindPackageHandleStandardArgs) + +find_package_handle_standard_args(TraceEvent DEFAULT_MSG + TraceEvent_LIBRARY TraceEvent_INCLUDE_DIR) + +if(TraceEvent_FOUND) + + set(TraceEvent_LIBRARIES ${TraceEvent_LIBRARY}) + set(TraceEvent_INCLUDE_DIRS ${TraceEvent_INCLUDE_DIR}) + + if(NOT TARGET trace::event) + add_library(trace::event UNKNOWN IMPORTED) + + set_target_properties(trace::event + PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${TraceEvent_INCLUDE_DIRS}" + INTERFACE_COMPILE_DEFINITIONS "${TraceEvent_DEFINITIONS}" + IMPORTED_LINK_INTERFACE_LANGUAGES "C" + IMPORTED_LOCATION "${TraceEvent_LIBRARIES}") + endif() + +endif() diff --git a/build/FindTraceFS.cmake b/build/FindTraceFS.cmake new file mode 100644 index 0000000..82fa012 --- /dev/null +++ b/build/FindTraceFS.cmake @@ -0,0 +1,76 @@ +# SPDX-License-Identifier: LGPL-2.1 + +#[=======================================================================[.rst: +FindTraceFS +------- + +Finds the tracefs library. + +Imported Targets +^^^^^^^^^^^^^^^^ + +This module defines the :prop_tgt:`IMPORTED` targets: + +``trace::fs`` + Defined if the system has libtracefs. + +Result Variables +^^^^^^^^^^^^^^^^ + +``TraceFS_FOUND`` + True if the system has the libtracefs library. +``TraceFS_VERSION`` + The version of the libtracefs library which was found. +``TraceFS_INCLUDE_DIRS`` + Include directories needed to use libtracefs. +``TraceFS_LIBRARIES`` + Libraries needed to link to libtracefs. + +Cache Variables +^^^^^^^^^^^^^^^ + +``TraceFS_INCLUDE_DIR`` + The directory containing ``tracefs.h``. +``TraceFS_LIBRARY`` + The path to the tracefs library. + +#]=======================================================================] + +find_package(PkgConfig QUIET) +pkg_check_modules(PC_TraceFS QUIET libtracefs) + +set(TraceFS_VERSION ${PC_TraceFS_VERSION}) +set(TraceFS_DEFINITIONS ${PC_TraceFS_CFLAGS_OTHER}) + +find_path(TraceFS_INCLUDE_DIR NAMES tracefs/tracefs.h + HINTS ${PC_TraceFS_INCLUDE_DIRS} + ${PC_TraceFS_INCLUDEDIR}) + +find_library(TraceFS_LIBRARY NAMES tracefs libtracefs + HINTS ${PC_TraceFS_LIBDIR} + ${PC_TraceFSLIBRARY_DIRS}) + +mark_as_advanced(TraceFS_INCLUDE_DIR TraceFS_LIBRARY) + +include(FindPackageHandleStandardArgs) + +find_package_handle_standard_args(TraceFS DEFAULT_MSG + TraceFS_LIBRARY TraceFS_INCLUDE_DIR) + +if(TraceFS_FOUND) + + set(TraceFS_LIBRARIES ${TraceFS_LIBRARY}) + set(TraceFS_INCLUDE_DIRS ${TraceFS_INCLUDE_DIR}) + + if(NOT TARGET trace::fs) + add_library(trace::fs UNKNOWN IMPORTED) + + set_target_properties(trace::fs + PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${TraceFS_INCLUDE_DIRS}" + INTERFACE_COMPILE_DEFINITIONS "${TraceFS_DEFINITIONS}" + IMPORTED_LINK_INTERFACE_LANGUAGES "C" + IMPORTED_LOCATION "${TraceFS_LIBRARIES}") + endif() + +endif() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 457c100..7663d44 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,9 +7,9 @@ add_library(kshark SHARED libkshark.c libkshark-configio.c libkshark-collection.c) -target_link_libraries(kshark ${TRACECMD_LIBRARY} - ${TRACEFS_LIBRARY} - ${TRACEEVENT_LIBRARY} +target_link_libraries(kshark trace::cmd + trace::fs + trace::event ${JSONC_LIBRARY} ${CMAKE_DL_LIBS}) diff --git a/src/libkshark.c b/src/libkshark.c index 42dc08c..a540da2 100644 --- a/src/libkshark.c +++ b/src/libkshark.c @@ -141,7 +141,7 @@ bool kshark_open(struct kshark_context *kshark_ctx, const char *file) kshark_free_task_list(kshark_ctx); - handle = tracecmd_open(file); + handle = tracecmd_open_head(file); if (!handle) return false; @@ -696,7 +696,7 @@ static ssize_t get_records(struct kshark_context *kshark_ctx, int pid; int cpu; - n_cpus = tracecmd_cpus(kshark_ctx->handle); + n_cpus = tep_get_cpus(kshark_ctx->pevent); cpu_list = calloc(n_cpus, sizeof(*cpu_list)); if (!cpu_list) return -ENOMEM; @@ -867,7 +867,7 @@ ssize_t kshark_load_data_entries(struct kshark_context *kshark_ctx, if (total < 0) goto fail; - n_cpus = tracecmd_cpus(kshark_ctx->handle); + n_cpus = tep_get_cpus(kshark_ctx->pevent); rows = calloc(total, sizeof(struct kshark_entry *)); if (!rows) @@ -923,7 +923,7 @@ ssize_t kshark_load_data_records(struct kshark_context *kshark_ctx, if (total < 0) goto fail; - n_cpus = tracecmd_cpus(kshark_ctx->handle); + n_cpus = tep_get_cpus(kshark_ctx->pevent); rows = calloc(total, sizeof(struct tep_record *)); if (!rows) @@ -1047,7 +1047,7 @@ size_t kshark_load_data_matrix(struct kshark_context *kshark_ctx, if (total < 0) goto fail; - n_cpus = tracecmd_cpus(kshark_ctx->handle); + n_cpus = tep_get_cpus(kshark_ctx->pevent); status = data_matrix_alloc(total, offset_array, cpu_array, diff --git a/src/libkshark.h b/src/libkshark.h index 0d6c50d..a44f46e 100644 --- a/src/libkshark.h +++ b/src/libkshark.h @@ -26,7 +26,7 @@ extern "C" { // trace-cmd #include "trace-cmd/trace-cmd.h" -#include "trace-cmd/trace-filter-hash.h" +#include "trace-filter-hash.h" #include "traceevent/event-parse.h" #include "tracefs/tracefs.h" diff --git a/src/trace-filter-hash.h b/src/trace-filter-hash.h new file mode 100644 index 0000000..4111c41 --- /dev/null +++ b/src/trace-filter-hash.h @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: LGPL-2.1 */ +/* + * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt + * Copyright (C) 2018 VMware Inc, Steven Rostedt + * + */ +#ifndef _TRACE_FILTER_HASH_H +#define _TRACE_FILTER_HASH_H + +#include + +struct tracecmd_filter_id_item { + struct tracecmd_filter_id_item *next; + int id; +}; + +struct tracecmd_filter_id { + struct tracecmd_filter_id_item **hash; + int count; +}; + +/** + * tracecmd_quick_hash - A quick (non secured) hash alogirthm + * @val: The value to perform the hash on + * @bits: The size in bits you need to return + * + * This is a quick hashing function adapted from Donald E. Knuth's 32 + * bit multiplicative hash. See The Art of Computer Programming (TAOCP). + * Multiplication by the Prime number, closest to the golden ratio of + * 2^32. + * + * @bits is used to max the result for use cases that require + * a power of 2 return value that is less than 32 bits. Any value + * of @bits greater than 31 (or zero), will simply return the full hash on @val. + */ +static inline uint32_t tracecmd_quick_hash(uint32_t val, unsigned int bits) +{ + val *= UINT32_C(2654435761); + + if (!bits || bits > 31) + return val; + + return val & ((1 << bits) - 1); +} + +struct tracecmd_filter_id_item * + tracecmd_filter_id_find(struct tracecmd_filter_id *hash, int id); +void tracecmd_filter_id_add(struct tracecmd_filter_id *hash, int id); +void tracecmd_filter_id_remove(struct tracecmd_filter_id *hash, int id); +void tracecmd_filter_id_clear(struct tracecmd_filter_id *hash); +struct tracecmd_filter_id *tracecmd_filter_id_hash_alloc(void); +void tracecmd_filter_id_hash_free(struct tracecmd_filter_id *hash); +struct tracecmd_filter_id * + tracecmd_filter_id_hash_copy(struct tracecmd_filter_id *hash); +int *tracecmd_filter_ids(struct tracecmd_filter_id *hash); +int tracecmd_filter_id_compare(struct tracecmd_filter_id *hash1, + struct tracecmd_filter_id *hash2); + +static inline int tracecmd_filter_task_count(struct tracecmd_filter_id *hash) +{ + return hash->count; +} + +#endif /* _TRACE_FILTER_HASH_H */ From patchwork Mon Jan 4 17:46:43 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11997141 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id AB9BDC433E6 for ; Mon, 4 Jan 2021 17:48:23 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 6DE06206E4 for ; Mon, 4 Jan 2021 17:48:23 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726168AbhADRsX (ORCPT ); Mon, 4 Jan 2021 12:48:23 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34264 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725889AbhADRsX (ORCPT ); Mon, 4 Jan 2021 12:48:23 -0500 Received: from mail-ed1-x531.google.com (mail-ed1-x531.google.com [IPv6:2a00:1450:4864:20::531]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 59F55C061796 for ; Mon, 4 Jan 2021 09:47:42 -0800 (PST) Received: by mail-ed1-x531.google.com with SMTP id dk8so28284579edb.1 for ; Mon, 04 Jan 2021 09:47:42 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=81vGH0iMwIAkf4tHA+ImXIpvhCqTk2+NEpwOsQU5k38=; b=W3Bs+iq8hoAeLpnx3FvJ5VkwAniwJ8CYALsgvqnM3b4huyReDOR3aLfZPluKENzaIh GpGpV2ah5G8r3lvrJJ6DmrM+hx+2OfHEtvT7Md2IRx2FSbL7279m8D7kEqACXSllRJlo PpvYRWcGR1n3K0mBAEV5P3az9ko5Pu/eTIZwy79ez7mpSIqhSns8YCn/4Rf398I1ThBI sHzoCdnZft8MQ5Bbt/QhWw9CQ84kI3S9pJibM7twqCkLm/5CcB2fX54CKQve2quMOJVl vUIy1+ubO0H4lwxbqKgPpgCoQZMW7MSJ4eoZd2WcYP7V9CIbg/tYaSHVwrS1yiFI8T7p U2Nw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=81vGH0iMwIAkf4tHA+ImXIpvhCqTk2+NEpwOsQU5k38=; b=bsyfeIQzpsDyI+6U45msLYyYcuHGlqHnaQnmey6VHUMPeO2xY8y5MoPu3D0ufzwX4V HYFGsEOAFB9afG2i0xViUWg7Vc4qbWjKHi3HPlmVZJ+zfFa1RY21NdZWD2cb8f3VCYr5 rnauwjKGTS/gkQhUvdTC9hCT1EEsqa1c0h2yexlvsLOeb11PtfM7MtFXoYiuUki3J208 nTVw6zLLHfAvFhpWAogkafATSCwKAXdGy6F/CFLOAT1GBcGTa9e5/2JFnOk7oJOWOnKb TO3z5kmoXjyLiS24f7xYuReFZ0UKO4TQOsJGsweCPMroEWoixanAtCxDahscEfjRkNB6 gl4A== X-Gm-Message-State: AOAM533hOdM3l2cUfuaojT4BxUnCsRyntFK5AZbAwKOA6hQPzzlfS3Vv /mUQmqdjuiB5F0B/q6K6agE= X-Google-Smtp-Source: ABdhPJxspIBr6tPnYp8uSwRIvzDLMuDH9UNi/6z/eywR8RP54ZUA0Oe9lF5YklMue+LkoQgvgRg6xQ== X-Received: by 2002:aa7:c543:: with SMTP id s3mr71065900edr.88.1609782459760; Mon, 04 Jan 2021 09:47:39 -0800 (PST) Received: from localhost.localdomain ([95.87.199.238]) by smtp.gmail.com with ESMTPSA id l14sm44107750edq.35.2021.01.04.09.47.38 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 04 Jan 2021 09:47:39 -0800 (PST) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v8 03/44] kernel-shark: Add license information Date: Mon, 4 Jan 2021 19:46:43 +0200 Message-Id: <20210104174724.70404-4-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210104174724.70404-1-y.karadz@gmail.com> References: <20210104174724.70404-1-y.karadz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org The GPL-2.0 and LGPL-2.1licenses are added to the LICENSES directory. Signed-off-by: Yordan Karadzhov (VMware) --- LICENSES/GPL-2.0 | 359 +++++++++++++++++++++++++++++++++ LICENSES/LGPL-2.1 | 503 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 862 insertions(+) create mode 100644 LICENSES/GPL-2.0 create mode 100644 LICENSES/LGPL-2.1 diff --git a/LICENSES/GPL-2.0 b/LICENSES/GPL-2.0 new file mode 100644 index 0000000..ff0812f --- /dev/null +++ b/LICENSES/GPL-2.0 @@ -0,0 +1,359 @@ +Valid-License-Identifier: GPL-2.0 +Valid-License-Identifier: GPL-2.0-only +Valid-License-Identifier: GPL-2.0+ +Valid-License-Identifier: GPL-2.0-or-later +SPDX-URL: https://spdx.org/licenses/GPL-2.0.html +Usage-Guide: + To use this license in source code, put one of the following SPDX + tag/value pairs into a comment according to the placement + guidelines in the licensing rules documentation. + For 'GNU General Public License (GPL) version 2 only' use: + SPDX-License-Identifier: GPL-2.0 + or + SPDX-License-Identifier: GPL-2.0-only + For 'GNU General Public License (GPL) version 2 or any later version' use: + SPDX-License-Identifier: GPL-2.0+ + or + SPDX-License-Identifier: GPL-2.0-or-later +License-Text: + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/LICENSES/LGPL-2.1 b/LICENSES/LGPL-2.1 new file mode 100644 index 0000000..27bb434 --- /dev/null +++ b/LICENSES/LGPL-2.1 @@ -0,0 +1,503 @@ +Valid-License-Identifier: LGPL-2.1 +Valid-License-Identifier: LGPL-2.1+ +SPDX-URL: https://spdx.org/licenses/LGPL-2.1.html +Usage-Guide: + To use this license in source code, put one of the following SPDX + tag/value pairs into a comment according to the placement + guidelines in the licensing rules documentation. + For 'GNU Lesser General Public License (LGPL) version 2.1 only' use: + SPDX-License-Identifier: LGPL-2.1 + For 'GNU Lesser General Public License (LGPL) version 2.1 or any later + version' use: + SPDX-License-Identifier: LGPL-2.1+ +License-Text: + +GNU LESSER GENERAL PUBLIC LICENSE +Version 2.1, February 1999 + +Copyright (C) 1991, 1999 Free Software Foundation, Inc. +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts as +the successor of the GNU Library Public License, version 2, hence the +version number 2.1.] + +Preamble + +The licenses for most software are designed to take away your freedom to +share and change it. By contrast, the GNU General Public Licenses are +intended to guarantee your freedom to share and change free software--to +make sure the software is free for all its users. + +This license, the Lesser General Public License, applies to some specially +designated software packages--typically libraries--of the Free Software +Foundation and other authors who decide to use it. You can use it too, but +we suggest you first think carefully about whether this license or the +ordinary General Public License is the better strategy to use in any +particular case, based on the explanations below. + +When we speak of free software, we are referring to freedom of use, not +price. Our General Public Licenses are designed to make sure that you have +the freedom to distribute copies of free software (and charge for this +service if you wish); that you receive source code or can get it if you +want it; that you can change the software and use pieces of it in new free +programs; and that you are informed that you can do these things. + +To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for you if +you distribute copies of the library or if you modify it. + +For example, if you distribute copies of the library, whether gratis or for +a fee, you must give the recipients all the rights that we gave you. You +must make sure that they, too, receive or can get the source code. If you +link other code with the library, you must provide complete object files to +the recipients, so that they can relink them with the library after making +changes to the library and recompiling it. And you must show them these +terms so they know their rights. + +We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + +To protect each distributor, we want to make it very clear that there is no +warranty for the free library. Also, if the library is modified by someone +else and passed on, the recipients should know that what they have is not +the original version, so that the original author's reputation will not be +affected by problems that might be introduced by others. + +Finally, software patents pose a constant threat to the existence of any +free program. We wish to make sure that a company cannot effectively +restrict the users of a free program by obtaining a restrictive license +from a patent holder. Therefore, we insist that any patent license obtained +for a version of the library must be consistent with the full freedom of +use specified in this license. + +Most GNU software, including some libraries, is covered by the ordinary GNU +General Public License. This license, the GNU Lesser General Public +License, applies to certain designated libraries, and is quite different +from the ordinary General Public License. We use this license for certain +libraries in order to permit linking those libraries into non-free +programs. + +When a program is linked with a library, whether statically or using a +shared library, the combination of the two is legally speaking a combined +work, a derivative of the original library. The ordinary General Public +License therefore permits such linking only if the entire combination fits +its criteria of freedom. The Lesser General Public License permits more lax +criteria for linking other code with the library. + +We call this license the "Lesser" General Public License because it does +Less to protect the user's freedom than the ordinary General Public +License. It also provides other free software developers Less of an +advantage over competing non-free programs. These disadvantages are the +reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + +For example, on rare occasions, there may be a special need to encourage +the widest possible use of a certain library, so that it becomes a de-facto +standard. To achieve this, non-free programs must be allowed to use the +library. A more frequent case is that a free library does the same job as +widely used non-free libraries. In this case, there is little to gain by +limiting the free library to free software only, so we use the Lesser +General Public License. + +In other cases, permission to use a particular library in non-free programs +enables a greater number of people to use a large body of free +software. For example, permission to use the GNU C Library in non-free +programs enables many more people to use the whole GNU operating system, as +well as its variant, the GNU/Linux operating system. + +Although the Lesser General Public License is Less protective of the users' +freedom, it does ensure that the user of a program that is linked with the +Library has the freedom and the wherewithal to run that program using a +modified version of the Library. + +The precise terms and conditions for copying, distribution and modification +follow. Pay close attention to the difference between a "work based on the +library" and a "work that uses the library". The former contains code +derived from the library, whereas the latter must be combined with the +library in order to run. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License Agreement applies to any software library or other program + which contains a notice placed by the copyright holder or other + authorized party saying it may be distributed under the terms of this + Lesser General Public License (also called "this License"). Each + licensee is addressed as "you". + + A "library" means a collection of software functions and/or data + prepared so as to be conveniently linked with application programs + (which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work which + has been distributed under these terms. A "work based on the Library" + means either the Library or any derivative work under copyright law: + that is to say, a work containing the Library or a portion of it, either + verbatim or with modifications and/or translated straightforwardly into + another language. (Hereinafter, translation is included without + limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for making + modifications to it. For a library, complete source code means all the + source code for all modules it contains, plus any associated interface + definition files, plus the scripts used to control compilation and + installation of the library. + + Activities other than copying, distribution and modification are not + covered by this License; they are outside its scope. The act of running + a program using the Library is not restricted, and output from such a + program is covered only if its contents constitute a work based on the + Library (independent of the use of the Library in a tool for writing + it). Whether that is true depends on what the Library does and what the + program that uses the Library does. + +1. You may copy and distribute verbatim copies of the Library's complete + source code as you receive it, in any medium, provided that you + conspicuously and appropriately publish on each copy an appropriate + copyright notice and disclaimer of warranty; keep intact all the notices + that refer to this License and to the absence of any warranty; and + distribute a copy of this License along with the Library. + + You may charge a fee for the physical act of transferring a copy, and + you may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Library or any portion of it, + thus forming a work based on the Library, and copy and distribute such + modifications or work under the terms of Section 1 above, provided that + you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices stating + that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no charge to + all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a table + of data to be supplied by an application program that uses the + facility, other than as an argument passed when the facility is + invoked, then you must make a good faith effort to ensure that, in + the event an application does not supply such function or table, the + facility still operates, and performs whatever part of its purpose + remains meaningful. + + (For example, a function in a library to compute square roots has a + purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must be + optional: if the application does not supply it, the square root + function must still compute square roots.) + + These requirements apply to the modified work as a whole. If + identifiable sections of that work are not derived from the Library, and + can be reasonably considered independent and separate works in + themselves, then this License, and its terms, do not apply to those + sections when you distribute them as separate works. But when you + distribute the same sections as part of a whole which is a work based on + the Library, the distribution of the whole must be on the terms of this + License, whose permissions for other licensees extend to the entire + whole, and thus to each and every part regardless of who wrote it. + + Thus, it is not the intent of this section to claim rights or contest + your rights to work written entirely by you; rather, the intent is to + exercise the right to control the distribution of derivative or + collective works based on the Library. + + In addition, mere aggregation of another work not based on the Library + with the Library (or with a work based on the Library) on a volume of a + storage or distribution medium does not bring the other work under the + scope of this License. + +3. You may opt to apply the terms of the ordinary GNU General Public + License instead of this License to a given copy of the Library. To do + this, you must alter all the notices that refer to this License, so that + they refer to the ordinary GNU General Public License, version 2, + instead of to this License. (If a newer version than version 2 of the + ordinary GNU General Public License has appeared, then you can specify + that version instead if you wish.) Do not make any other change in these + notices. + + Once this change is made in a given copy, it is irreversible for that + copy, so the ordinary GNU General Public License applies to all + subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of the + Library into a program that is not a library. + +4. You may copy and distribute the Library (or a portion or derivative of + it, under Section 2) in object code or executable form under the terms + of Sections 1 and 2 above provided that you accompany it with the + complete corresponding machine-readable source code, which must be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange. + + If distribution of object code is made by offering access to copy from a + designated place, then offering equivalent access to copy the source + code from the same place satisfies the requirement to distribute the + source code, even though third parties are not compelled to copy the + source along with the object code. + +5. A program that contains no derivative of any portion of the Library, but + is designed to work with the Library by being compiled or linked with + it, is called a "work that uses the Library". Such a work, in isolation, + is not a derivative work of the Library, and therefore falls outside the + scope of this License. + + However, linking a "work that uses the Library" with the Library creates + an executable that is a derivative of the Library (because it contains + portions of the Library), rather than a "work that uses the + library". The executable is therefore covered by this License. Section 6 + states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file + that is part of the Library, the object code for the work may be a + derivative work of the Library even though the source code is + not. Whether this is true is especially significant if the work can be + linked without the Library, or if the work is itself a library. The + threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data structure + layouts and accessors, and small macros and small inline functions (ten + lines or less in length), then the use of the object file is + unrestricted, regardless of whether it is legally a derivative + work. (Executables containing this object code plus portions of the + Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may + distribute the object code for the work under the terms of Section + 6. Any executables containing that work also fall under Section 6, + whether or not they are linked directly with the Library itself. + +6. As an exception to the Sections above, you may also combine or link a + "work that uses the Library" with the Library to produce a work + containing portions of the Library, and distribute that work under terms + of your choice, provided that the terms permit modification of the work + for the customer's own use and reverse engineering for debugging such + modifications. + + You must give prominent notice with each copy of the work that the + Library is used in it and that the Library and its use are covered by + this License. You must supply a copy of this License. If the work during + execution displays copyright notices, you must include the copyright + notice for the Library among them, as well as a reference directing the + user to the copy of this License. Also, you must do one of these things: + + a) Accompany the work with the complete corresponding machine-readable + source code for the Library including whatever changes were used in + the work (which must be distributed under Sections 1 and 2 above); + and, if the work is an executable linked with the Library, with the + complete machine-readable "work that uses the Library", as object + code and/or source code, so that the user can modify the Library and + then relink to produce a modified executable containing the modified + Library. (It is understood that the user who changes the contents of + definitions files in the Library will not necessarily be able to + recompile the application to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a copy + of the library already present on the user's computer system, rather + than copying library functions into the executable, and (2) will + operate properly with a modified version of the library, if the user + installs one, as long as the modified version is interface-compatible + with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at least three + years, to give the same user the materials specified in Subsection + 6a, above, for a charge no more than the cost of performing this + distribution. + + d) If distribution of the work is made by offering access to copy from a + designated place, offer equivalent access to copy the above specified + materials from the same place. + + e) Verify that the user has already received a copy of these materials + or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the Library" + must include any data and utility programs needed for reproducing the + executable from it. However, as a special exception, the materials to be + distributed need not include anything that is normally distributed (in + either source or binary form) with the major components (compiler, + kernel, and so on) of the operating system on which the executable runs, + unless that component itself accompanies the executable. + + It may happen that this requirement contradicts the license restrictions + of other proprietary libraries that do not normally accompany the + operating system. Such a contradiction means you cannot use both them + and the Library together in an executable that you distribute. + +7. You may place library facilities that are a work based on the Library + side-by-side in a single library together with other library facilities + not covered by this License, and distribute such a combined library, + provided that the separate distribution of the work based on the Library + and of the other library facilities is otherwise permitted, and provided + that you do these two things: + + a) Accompany the combined library with a copy of the same work based on + the Library, uncombined with any other library facilities. This must + be distributed under the terms of the Sections above. + + b) Give prominent notice with the combined library of the fact that part + of it is a work based on the Library, and explaining where to find + the accompanying uncombined form of the same work. + +8. You may not copy, modify, sublicense, link with, or distribute the + Library except as expressly provided under this License. Any attempt + otherwise to copy, modify, sublicense, link with, or distribute the + Library is void, and will automatically terminate your rights under this + License. However, parties who have received copies, or rights, from you + under this License will not have their licenses terminated so long as + such parties remain in full compliance. + +9. You are not required to accept this License, since you have not signed + it. However, nothing else grants you permission to modify or distribute + the Library or its derivative works. These actions are prohibited by law + if you do not accept this License. Therefore, by modifying or + distributing the Library (or any work based on the Library), you + indicate your acceptance of this License to do so, and all its terms and + conditions for copying, distributing or modifying the Library or works + based on it. + +10. Each time you redistribute the Library (or any work based on the + Library), the recipient automatically receives a license from the + original licensor to copy, distribute, link with or modify the Library + subject to these terms and conditions. You may not impose any further + restrictions on the recipients' exercise of the rights granted + herein. You are not responsible for enforcing compliance by third + parties with this License. + +11. If, as a consequence of a court judgment or allegation of patent + infringement or for any other reason (not limited to patent issues), + conditions are imposed on you (whether by court order, agreement or + otherwise) that contradict the conditions of this License, they do not + excuse you from the conditions of this License. If you cannot + distribute so as to satisfy simultaneously your obligations under this + License and any other pertinent obligations, then as a consequence you + may not distribute the Library at all. For example, if a patent license + would not permit royalty-free redistribution of the Library by all + those who receive copies directly or indirectly through you, then the + only way you could satisfy both it and this License would be to refrain + entirely from distribution of the Library. + + If any portion of this section is held invalid or unenforceable under + any particular circumstance, the balance of the section is intended to + apply, and the section as a whole is intended to apply in other + circumstances. + + It is not the purpose of this section to induce you to infringe any + patents or other property right claims or to contest validity of any + such claims; this section has the sole purpose of protecting the + integrity of the free software distribution system which is implemented + by public license practices. Many people have made generous + contributions to the wide range of software distributed through that + system in reliance on consistent application of that system; it is up + to the author/donor to decide if he or she is willing to distribute + software through any other system and a licensee cannot impose that + choice. + + This section is intended to make thoroughly clear what is believed to + be a consequence of the rest of this License. + +12. If the distribution and/or use of the Library is restricted in certain + countries either by patents or by copyrighted interfaces, the original + copyright holder who places the Library under this License may add an + explicit geographical distribution limitation excluding those + countries, so that distribution is permitted only in or among countries + not thus excluded. In such case, this License incorporates the + limitation as if written in the body of this License. + +13. The Free Software Foundation may publish revised and/or new versions of + the Lesser General Public License from time to time. Such new versions + will be similar in spirit to the present version, but may differ in + detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the Library + specifies a version number of this License which applies to it and "any + later version", you have the option of following the terms and + conditions either of that version or of any later version published by + the Free Software Foundation. If the Library does not specify a license + version number, you may choose any version ever published by the Free + Software Foundation. + +14. If you wish to incorporate parts of the Library into other free + programs whose distribution conditions are incompatible with these, + write to the author to ask for permission. For software which is + copyrighted by the Free Software Foundation, write to the Free Software + Foundation; we sometimes make exceptions for this. Our decision will be + guided by the two goals of preserving the free status of all + derivatives of our free software and of promoting the sharing and reuse + of software generally. + +NO WARRANTY + +15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY + FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN + OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES + PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER + EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE + ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH + YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL + NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING + WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR + REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR + DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL + DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY + (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED + INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF + THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR + OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Libraries + +If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + +To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + +one line to give the library's name and an idea of what it does. +Copyright (C) year name of author + +This library is free software; you can redistribute it and/or modify it +under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or (at +your option) any later version. + +This library is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License +for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this library; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add +information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + +Yoyodyne, Inc., hereby disclaims all copyright interest in +the library `Frob' (a library for tweaking knobs) written +by James Random Hacker. + +signature of Ty Coon, 1 April 1990 +Ty Coon, President of Vice +That's all there is to it! From patchwork Mon Jan 4 17:46:44 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11997139 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 15B04C433E0 for ; Mon, 4 Jan 2021 17:48:23 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id D1A03206E4 for ; Mon, 4 Jan 2021 17:48:22 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726098AbhADRsW (ORCPT ); Mon, 4 Jan 2021 12:48:22 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34262 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725889AbhADRsW (ORCPT ); Mon, 4 Jan 2021 12:48:22 -0500 Received: from mail-ed1-x52f.google.com (mail-ed1-x52f.google.com [IPv6:2a00:1450:4864:20::52f]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 09717C061795 for ; Mon, 4 Jan 2021 09:47:42 -0800 (PST) Received: by mail-ed1-x52f.google.com with SMTP id dk8so28284564edb.1 for ; Mon, 04 Jan 2021 09:47:41 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=xrqfjcx8+apSca3gz79+n8M1aFpRLw6AXfLNdpf8fU0=; b=BxUDvE5zLFIIZXViJaDnYTgO0dn52D6I+9vdcqUvjpLVRkQan0Aho+zYFEiI9raVil 0Mu0rulzJBDTYyNqIpGAgLI5le3JtvuiI44Md2nkcFQQEMpcR+OrdfEwA9jy5eKcOLeX HodtX4dP6XlrUc0sB2bgkx1eie9ZpmXvqnDRExioPqQjUY5wmTF1GMCqCBUyhf/A0xeF n4UIj5UBgZ7hlFyiyvXeD8cL94TXTd96txZz27M/p7vXjwm4NxeDzvjHT3fPMC0hLyax gJqPe9PMKaWZZ21axfElzxkZVGthZ3ta5d58tewOGDEpfjDG8YYbSojXHalrtm+RWABh zdhw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=xrqfjcx8+apSca3gz79+n8M1aFpRLw6AXfLNdpf8fU0=; b=TIdy3fmfGUsaCoJ3Bs1zpM/qWPql/mV3los71z6Yg0WqPsgKHR+J6cex4QGb7DkDNp urkCynpxzJl1LfnMrsbXR7wB+fVDEZdnZHvOa0VuSKTXdrvBAd49ZkrtB4dO0NC6btFf tV1VOOQX8jvMUhRn1YCnUYzzEcOw6D3B7g0r9MhdBvG2UCiMvu59EV0BQVpesLUaqa6z 9HvoHpMx5yfqZ2UuH0CzXf0peXW40vENvSHQdGGbvYdTTzQ0+soZGPg58eFVLJl0pt4p vyFWql4R0ZAABErVgGriYO/AN2C3sYdTqMRGQ48cslkcstCfCUG4hJJpihUFVi2lxaC2 wZbQ== X-Gm-Message-State: AOAM532IkKvZ42k7u5uWXQxmQ21+juC3UvdrfYTJryfjQX2Q3CT1jwYH QE0YgKjgBnZRwhIJxTxesYTyNsFkLF/pag== X-Google-Smtp-Source: ABdhPJzkwzKosHp6T06RuAzN6ZBAgUEffxm5CXnwLCqrKbS1uQYf0Hw39eyXb9yk8k5rdZBtK2RbTQ== X-Received: by 2002:a50:fd18:: with SMTP id i24mr73048643eds.146.1609782460876; Mon, 04 Jan 2021 09:47:40 -0800 (PST) Received: from localhost.localdomain ([95.87.199.238]) by smtp.gmail.com with ESMTPSA id l14sm44107750edq.35.2021.01.04.09.47.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 04 Jan 2021 09:47:40 -0800 (PST) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" , David Runge Subject: [PATCH v8 04/44] kernel-shark: Change the CMake minimum version required Date: Mon, 4 Jan 2021 19:46:44 +0200 Message-Id: <20210104174724.70404-5-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210104174724.70404-1-y.karadz@gmail.com> References: <20210104174724.70404-1-y.karadz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org We use some CMake features that are available since version 3.1.2 Suggested-by: David Runge Signed-off-by: Yordan Karadzhov (VMware) --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3ea40f0..d75f179 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ # Check if cmake has the required version -cmake_minimum_required(VERSION 2.8.11 FATAL_ERROR) +cmake_minimum_required(VERSION 3.1.2 FATAL_ERROR) # Set the name and version of the project project(kernel-shark) From patchwork Mon Jan 4 17:46:45 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11997143 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id EB4CFC433DB for ; Mon, 4 Jan 2021 17:48:58 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id A38FC20715 for ; Mon, 4 Jan 2021 17:48:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726189AbhADRs6 (ORCPT ); Mon, 4 Jan 2021 12:48:58 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34356 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725840AbhADRs6 (ORCPT ); Mon, 4 Jan 2021 12:48:58 -0500 Received: from mail-ed1-x534.google.com (mail-ed1-x534.google.com [IPv6:2a00:1450:4864:20::534]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 316FAC061798 for ; Mon, 4 Jan 2021 09:47:43 -0800 (PST) Received: by mail-ed1-x534.google.com with SMTP id i24so28215632edj.8 for ; Mon, 04 Jan 2021 09:47:43 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=yLWJrmcmsw/Q4BjbHxuxlypKW9YjouYM3r+4oORK2iI=; b=u1dBPuYAKNv2v4MBH9y/wIdrYqmzaLg+0QiQWB6mRzN9HhHgBXBZpNnvpgN46/mCOF L5bxJqHjM0X3TVyL+6uwcFhZ1Nicxtmw9/RjjgkoQd3ZVgRiC0Exsg3MxiHq8TdLKUqQ jdoVSVqGiLmKSIoa5FleSmd3SH5mAEltz+V+pjb49P5e2YxwSuusUczZjfU79dBseCpa c28PgpYRKjuh1yXLZ9UtdhXBbbo98/48d1Oo/VOgJxoJgSl1z/F7icien01+tIEDz4It +qiEnUt//n0FjM8PYJpXWYPS4CgWSKTRQi6NQSIid+VQVWcYRT/b/TOIaA5MZA/+zme3 9gAA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=yLWJrmcmsw/Q4BjbHxuxlypKW9YjouYM3r+4oORK2iI=; b=fifVcFlXWD/FPKQse4rPce4sIoJtdHjNIhyHvHJ9ZSMPq5aI6qnCgGzUGmhtMEXlNM 3nJKI0Ut2vAKX2hVjPjRCrSTnXbwsTsp+QSeYrWPWmfw6n8i4hT9vx9b5InbmgXmV/Nm M/Dj7FDmmwBTvYOmXwtE/W1RORPzq+2fcLeNY87y2ulFYXNk78Wx6ZNnVmi9Ukhptg59 eqPX5eyUMQeFKSM6DVxXUqFG+sz1DbkIDqAU4k/KJUafS2wYoYJwF4ldOFh2hE78W1nA 71Pvs/1duiCnWquZPVsENr28xwvtyiUcVb14n7p2le83omCXxaPUItDE5imVpNcsQR8l Yahg== X-Gm-Message-State: AOAM530p7jqozBRsMw1PyY4Vo3wy5JGKoIZpHx+TGjWLhzv7CMxoxBCW vfuz2N0NTja7XNC/TQGgPBs= X-Google-Smtp-Source: ABdhPJyWJB/Svu/gE7Vwo4dhvH4T+hjKyyLdx05VbrEx5o8SJsT0nQTtpJ+dkt08x2IWHH8dWaDOEA== X-Received: by 2002:a05:6402:139a:: with SMTP id b26mr72372139edv.47.1609782461838; Mon, 04 Jan 2021 09:47:41 -0800 (PST) Received: from localhost.localdomain ([95.87.199.238]) by smtp.gmail.com with ESMTPSA id l14sm44107750edq.35.2021.01.04.09.47.41 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 04 Jan 2021 09:47:41 -0800 (PST) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v8 05/44] kernel-shark: Change default libraries install location Date: Mon, 4 Jan 2021 19:46:45 +0200 Message-Id: <20210104174724.70404-6-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210104174724.70404-1-y.karadz@gmail.com> References: <20210104174724.70404-1-y.karadz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org The default installation path for the KernelShark libraries changes from /usr/local/lib/kernelshark/ to /usr/local/lib64 (or /usr/local/lib on 32 but systems). This is done in order to be consistent with the install locations of libtraceevent, libtracefs and libtracecmd. Signed-off-by: Yordan Karadzhov (VMware) --- CMakeLists.txt | 28 ++++++++++++++++++++++------ src/CMakeLists.txt | 6 +++--- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d75f179..76dd84a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,13 +13,28 @@ message("\n project: Kernel Shark: (version: ${KS_VERSION_STRING})\n") set(KS_DIR ${CMAKE_SOURCE_DIR}) -if (NOT _INSTALL_PREFIX) +execute_process(COMMAND bash "-c" "echo __LP64__ | ${CMAKE_C_COMPILER} -E -x c - | tail -n 1" + OUTPUT_VARIABLE __LP64__) + +if (NOT _INSTALL_PREFIX AND NOT _LIBDIR) + set(_INSTALL_PREFIX "/usr/local") -endif (NOT _INSTALL_PREFIX) -if (NOT _LIBDIR) - set(_LIBDIR "${_INSTALL_PREFIX}/lib") -endif (NOT _LIBDIR) + if (__LP64__ MATCHES 1) + set(_LIBDIR "/usr/local/lib64") + else (__LP64__ MATCHES 1) + set(_LIBDIR "/usr/local/lib") + endif (__LP64__ MATCHES 1) + +elseif (NOT _INSTALL_PREFIX) + + set(_INSTALL_PREFIX "/usr/local") + +elseif (NOT _LIBDIR) + + set(_LIBDIR "${_INSTALL_PREFIX}/lib") + +endif () set(CMAKE_MODULE_PATH "${KS_DIR}/build") find_package(TraceEvent REQUIRED) @@ -69,7 +84,8 @@ set(KS_ICON_FIN KS_icon_fin.svg) set(KS_LOGO KS_logo_symbol.svg) set(KS_LOGO_LABEL KS_logo_horizontal.svg) -set(CMAKE_INSTALL_RPATH "${_LIBDIR}/${KS_APP_NAME}/") +set(CMAKE_INSTALL_RPATH "${_LIBDIR}") +set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) if (CMAKE_BUILD_TYPE MATCHES Package) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7663d44..25c372b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -15,7 +15,7 @@ target_link_libraries(kshark trace::cmd set_target_properties(kshark PROPERTIES SUFFIX ".so.${KS_VERSION_STRING}") -install(TARGETS kshark LIBRARY DESTINATION ${_LIBDIR}/${KS_APP_NAME}) +install(TARGETS kshark LIBRARY DESTINATION ${_LIBDIR}) if (OPENGL_FOUND AND GLUT_FOUND) @@ -29,7 +29,7 @@ if (OPENGL_FOUND AND GLUT_FOUND) set_target_properties(kshark-plot PROPERTIES SUFFIX ".so.${KS_VERSION_STRING}") - install(TARGETS kshark-plot LIBRARY DESTINATION ${_LIBDIR}/${KS_APP_NAME}) + install(TARGETS kshark-plot LIBRARY DESTINATION ${_LIBDIR}) endif (OPENGL_FOUND AND GLUT_FOUND) @@ -82,7 +82,7 @@ if (Qt5Widgets_FOUND AND Qt5Network_FOUND) install(TARGETS ${KS_APP_NAME} kshark-record kshark-gui RUNTIME DESTINATION ${_INSTALL_PREFIX}/bin/ - LIBRARY DESTINATION ${_LIBDIR}/${KS_APP_NAME}/) + LIBRARY DESTINATION ${_LIBDIR}) install(FILES "${KS_DIR}/${KS_APP_NAME}.desktop" DESTINATION ${_INSTALL_PREFIX}/share/applications/) From patchwork Mon Jan 4 17:46:46 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11997145 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1960EC433E0 for ; Mon, 4 Jan 2021 17:48:59 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id CB5D2216C4 for ; Mon, 4 Jan 2021 17:48:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1725840AbhADRs6 (ORCPT ); Mon, 4 Jan 2021 12:48:58 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34358 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725889AbhADRs6 (ORCPT ); Mon, 4 Jan 2021 12:48:58 -0500 Received: from mail-ed1-x533.google.com (mail-ed1-x533.google.com [IPv6:2a00:1450:4864:20::533]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id F1CDFC06179A for ; Mon, 4 Jan 2021 09:47:43 -0800 (PST) Received: by mail-ed1-x533.google.com with SMTP id j16so28229185edr.0 for ; Mon, 04 Jan 2021 09:47:43 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=bBJzBc1VWa6muzHjFVaPIDr6CrRz3Ngm8w3deLRJEMI=; b=ha50D0aukPjG4aoLUnb17yPxEzwQ2IMU6ID8hNSj6rCjLDvyg+TjjV+qVQIIMKwTAB 9ma3PZvX4iFmk3UbSIa5dRnN80Iva3ifbDmYWxQbXaXFXUKqFEQNAHUfu9IHexgmL9Bu i/NtLre4Hu9EXI8wmfosuZ5jgS4G/IFNyQ5iO4w9Sa1Akr3+wZzsCJZRG3FvzwJU1CBU UAxBF12QTmXJqdjsQkMydelrGHLNLcdi6xvSoK4seIHBdW7usFH427zrdXT0wUsLPPnu VoZLWSrZV0i7C12AW1PnLpU2TUvvCR7V6LjPZ+YAKRcqoAzDOmGIN8rhHXFsjE8gsHjB h1yw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=bBJzBc1VWa6muzHjFVaPIDr6CrRz3Ngm8w3deLRJEMI=; b=uIWiIzBLgI37omnQWQbjJ7TlB8u8BzfDod00NvZdzLGUT8akCbGtPT+BwlAHY7shc/ 4H3wU0P6HS/d93pcqnA2O9DiVVOfJfbbRMrr33vf4GsgoNwXH/4ErdOrosy6klRM1Td9 jH8bFN6txEp7a0Nl218nJLMuvMKei1Lpifl5UfV+MPkefWS+kgCzseTwKYGdz0vx3AkE rM3woALnwZ1LBrxWmtl+lN/A9OKbFZ2onp2akprmqUHvtFZTJ6LNXTLYXpKgNf5nrwtJ j1kJSiKq9HkpANhoBKNG2kOQnT0XPGF+IRxkdOjf0nr1spgq7rTOiSj7ic3NkbJ6QS+5 bxmg== X-Gm-Message-State: AOAM530N043z2+XanYHqjlQvEy67OHk0HJK79CbMbNH9FW1gMcpJ4XNF mNzDcp9o2Sb/NewDBjZWD0E= X-Google-Smtp-Source: ABdhPJxdw8CkY5ehw4WI0imLOjM7mi0uTELuEpThH6NSB1bBHhq45E97fh6C43C64xxgFyCIgM0i6w== X-Received: by 2002:a50:d685:: with SMTP id r5mr73894325edi.248.1609782462724; Mon, 04 Jan 2021 09:47:42 -0800 (PST) Received: from localhost.localdomain ([95.87.199.238]) by smtp.gmail.com with ESMTPSA id l14sm44107750edq.35.2021.01.04.09.47.41 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 04 Jan 2021 09:47:42 -0800 (PST) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v8 06/44] kernel-shark: Split the installation in two components Date: Mon, 4 Jan 2021 19:46:46 +0200 Message-Id: <20210104174724.70404-7-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210104174724.70404-1-y.karadz@gmail.com> References: <20210104174724.70404-1-y.karadz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org The user can choose to install only the KernelShark GUI via "install_gui.sh". It is also possible to install the library headers and this can be done via "install_libkshark-devel.sh". The changes implemented in this patch are inspired by the talk "Deep CMake For Library Authors" presented at CppCon 2019 by Craig Scott. https://crascit.com/2019/10/16/cppcon-2019-deep-cmake-for-library-authors/ Signed-off-by: Yordan Karadzhov (VMware) --- CMakeLists.txt | 2 +- build/cmake_uninstall.sh | 23 ++++++++++---- build/install_gui.sh | 1 + build/install_libkshark-devel.sh | 1 + src/CMakeLists.txt | 53 ++++++++++++++++++++++++++------ src/plugins/CMakeLists.txt | 3 +- 6 files changed, 65 insertions(+), 18 deletions(-) create mode 100755 build/install_gui.sh create mode 100755 build/install_libkshark-devel.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index 76dd84a..2e970c3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -84,7 +84,7 @@ set(KS_ICON_FIN KS_icon_fin.svg) set(KS_LOGO KS_logo_symbol.svg) set(KS_LOGO_LABEL KS_logo_horizontal.svg) -set(CMAKE_INSTALL_RPATH "${_LIBDIR}") +set(CMAKE_INSTALL_RPATH "${_LIBDIR}" "$ORIGIN") set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) if (CMAKE_BUILD_TYPE MATCHES Package) diff --git a/build/cmake_uninstall.sh b/build/cmake_uninstall.sh index ae9eab5..0d93a8d 100755 --- a/build/cmake_uninstall.sh +++ b/build/cmake_uninstall.sh @@ -4,14 +4,25 @@ CYAN='\e[36m' PURPLE='\e[35m' NC='\e[0m' # No Color +uninstall () { + NAME=$1 + NAME=${NAME##*_} + NAME=${NAME%.*} + NAME=${NAME/manifest/the project} + if [ -e $1 ] + then + echo -e "${CYAN}Uninstall " $NAME"...${NC}" + xargs rm -v < $1 + rm -f $1 + fi +} + if [[ $EUID -ne 0 ]]; then echo -e "${PURPLE}Permission denied${NC}" 1>&2 exit 100 fi -if [ -e install_manifest.txt ] -then - echo -e "${CYAN}Uninstall the project...${NC}" - xargs rm -v < install_manifest.txt - rm -f install_manifest.txt -fi +for manifest in "$search_dir"${PWD}/install_manifest* +do + uninstall $manifest +done diff --git a/build/install_gui.sh b/build/install_gui.sh new file mode 100755 index 0000000..1583fb9 --- /dev/null +++ b/build/install_gui.sh @@ -0,0 +1 @@ +sudo cmake -DCOMPONENT=kernelshark -P cmake_install.cmake diff --git a/build/install_libkshark-devel.sh b/build/install_libkshark-devel.sh new file mode 100755 index 0000000..a7e420d --- /dev/null +++ b/build/install_libkshark-devel.sh @@ -0,0 +1 @@ +sudo cmake -DCOMPONENT=libkshark-devel -P cmake_install.cmake diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 25c372b..d51e5ee 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,5 +1,7 @@ message("\n src ...") +set(KS_INCLUDS_DESTINATION "${_INSTALL_PREFIX}/include/${KS_APP_NAME}") + message(STATUS "libkshark") add_library(kshark SHARED libkshark.c libkshark-model.c @@ -13,9 +15,25 @@ target_link_libraries(kshark trace::cmd ${JSONC_LIBRARY} ${CMAKE_DL_LIBS}) -set_target_properties(kshark PROPERTIES SUFFIX ".so.${KS_VERSION_STRING}") +set_target_properties(kshark PROPERTIES + SOVERSION ${KS_VERSION_MAJOR} + VERSION ${KS_VERSION_STRING}) + + +install(TARGETS kshark + LIBRARY DESTINATION ${_LIBDIR} + COMPONENT kernelshark + NAMELINK_COMPONENT libkshark-devel + INCLUDES DESTINATION ${_INSTALL_PREFIX}/include/${KS_APP_NAME} + COMPONENT libkshark-devel + ARCHIVE DESTINATION ${_LIBDIR} + COMPONENT libkshark-devel) -install(TARGETS kshark LIBRARY DESTINATION ${_LIBDIR}) +install(FILES "${KS_DIR}/src/libkshark.h" + "${KS_DIR}/src/libkshark-model.h" + "${KS_DIR}/src/libkshark-plugin.h" + DESTINATION ${KS_INCLUDS_DESTINATION} + COMPONENT libkshark-devel) if (OPENGL_FOUND AND GLUT_FOUND) @@ -27,9 +45,18 @@ if (OPENGL_FOUND AND GLUT_FOUND) ${GLUT_LIBRARY} ${OPENGL_LIBRARIES}) - set_target_properties(kshark-plot PROPERTIES SUFFIX ".so.${KS_VERSION_STRING}") + set_target_properties(kshark-plot PROPERTIES + SOVERSION ${KS_VERSION_MAJOR} + VERSION ${KS_VERSION_STRING}) - install(TARGETS kshark-plot LIBRARY DESTINATION ${_LIBDIR}) + install(TARGETS kshark-plot + LIBRARY DESTINATION ${_LIBDIR} + COMPONENT kernelshark + NAMELINK_COMPONENT libkshark-devel + INCLUDES DESTINATION ${_INSTALL_PREFIX}/include/${KS_APP_NAME} + COMPONENT libkshark-devel + ARCHIVE DESTINATION ${_LIBDIR} + COMPONENT libkshark-devel) endif (OPENGL_FOUND AND GLUT_FOUND) @@ -81,21 +108,27 @@ if (Qt5Widgets_FOUND AND Qt5Network_FOUND) target_link_libraries(kshark-record kshark-gui) install(TARGETS ${KS_APP_NAME} kshark-record kshark-gui - RUNTIME DESTINATION ${_INSTALL_PREFIX}/bin/ - LIBRARY DESTINATION ${_LIBDIR}) + RUNTIME DESTINATION ${_INSTALL_PREFIX}/bin/ + COMPONENT kernelshark + LIBRARY DESTINATION ${_LIBDIR} + COMPONENT kernelshark) install(FILES "${KS_DIR}/${KS_APP_NAME}.desktop" - DESTINATION ${_INSTALL_PREFIX}/share/applications/) + DESTINATION ${_INSTALL_PREFIX}/share/applications/ + COMPONENT kernelshark) install(FILES "${KS_DIR}/icons/${KS_ICON}" "${KS_DIR}/icons/${KS_ICON_FIN}" - DESTINATION ${_INSTALL_PREFIX}/share/icons/${KS_APP_NAME}) + DESTINATION ${_INSTALL_PREFIX}/share/icons/${KS_APP_NAME} + COMPONENT kernelshark) install(FILES "${KS_DIR}/org.freedesktop.kshark-record.policy" - DESTINATION /usr/share/polkit-1/actions/) + DESTINATION /usr/share/polkit-1/actions/ + COMPONENT kernelshark) install(PROGRAMS "${KS_DIR}/bin/kshark-su-record" - DESTINATION ${_INSTALL_PREFIX}/bin/) + DESTINATION ${_INSTALL_PREFIX}/bin/ + COMPONENT kernelshark) endif (Qt5Widgets_FOUND AND Qt5Network_FOUND) diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt index 6c77179..2da73f8 100644 --- a/src/plugins/CMakeLists.txt +++ b/src/plugins/CMakeLists.txt @@ -28,6 +28,7 @@ BUILD_PLUGIN(NAME missed_events list(APPEND PLUGIN_LIST "missed_events default") # This plugin will be loaded by default install(TARGETS sched_events missed_events - LIBRARY DESTINATION ${KS_PLUGIN_INSTALL_PREFIX}) + LIBRARY DESTINATION ${KS_PLUGIN_INSTALL_PREFIX} + COMPONENT kernelshark) set(PLUGINS ${PLUGIN_LIST} PARENT_SCOPE) From patchwork Mon Jan 4 17:46:47 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11997149 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-20.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,MENTIONS_GIT_HOSTING,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id D37DCC433E9 for ; Mon, 4 Jan 2021 17:48:59 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 8BBFD20715 for ; Mon, 4 Jan 2021 17:48:59 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726253AbhADRs7 (ORCPT ); Mon, 4 Jan 2021 12:48:59 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34360 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725889AbhADRs7 (ORCPT ); Mon, 4 Jan 2021 12:48:59 -0500 Received: from mail-ej1-x635.google.com (mail-ej1-x635.google.com [IPv6:2a00:1450:4864:20::635]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E662AC06179E for ; Mon, 4 Jan 2021 09:47:44 -0800 (PST) Received: by mail-ej1-x635.google.com with SMTP id t16so8341535ejf.13 for ; Mon, 04 Jan 2021 09:47:44 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=vTSEvEHiG9btkhRtDWM+LLB87T6qgZkDrEAVaT92JLU=; b=qRF0fwHVB/vSz9mO0PF9tqSI8nwfE0McSevy8RkTA75n6XQjW1Mse5DXhN4YF0KJ6Y tIsrOj+m7D769jrb5963OPqjECjmME78UFw0fjLFEy4DuM1SI0Aa9+VpukGDipF7AyH4 4sGeABeJ0LfoTYDsxX/Blg9UnDR4ls96Ic18eIDlE+a0OvG0rn06ECcxTknWwgZ34N2F 1cxTn//vcHfEnW8zkS+gM8E0Ec6zRBjr8D8Bbv3HWKDl9Zb75a87By+mEOr/OehhxCYI T61r/DWr53Of1MM36wKn3fh96cmlnuraBNWDEpZT2XhhhIgQApjgTBWO50+nV1fJsRFa D57A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=vTSEvEHiG9btkhRtDWM+LLB87T6qgZkDrEAVaT92JLU=; b=rDJ/Vv0Wu6Sg+49vI6lE0FepEe4GQbz8xa7rTXaqOBmucMogeLWIDkqO5a+50FYg5o BMgrW3/vTF+5s5DF18AVgCxwkhf616ljwcGGHPm7jw0zLVYxEXoLWQztTILAwc9Kaqqw YX5GimMx7enLN0N9sCswTahnKdbOVP0PmU5uzsauqMhscUnY3JRquKHClc+LN4uez/7r F32CDToWa/m/nDIn+J06mPkKcgYWeTgKBgKww+SvE0uc5ABppj51AYtG5eSjci6orWUB 7yrsjPQggy+f8h3ySy9obkVHsG5wEZgNNoegaUiYqoepmb/uhWnBMmbTJS8gSefYjuI7 lacg== X-Gm-Message-State: AOAM532Rf2xxNCyMFiB3CzThQ06/6hu+tWwyAntG42MxL5EfMIv6L0Hb /tPnaMQXbZWq5DzNrZU5wpk= X-Google-Smtp-Source: ABdhPJwhWNeygmCdlUSd41YYwXHq8Gt5E1v1YfTd6Y7QdBCDC1yXi46JLWkt+rtSjkM9tnX+1tEPAQ== X-Received: by 2002:a17:907:1182:: with SMTP id uz2mr69143308ejb.183.1609782463636; Mon, 04 Jan 2021 09:47:43 -0800 (PST) Received: from localhost.localdomain ([95.87.199.238]) by smtp.gmail.com with ESMTPSA id l14sm44107750edq.35.2021.01.04.09.47.42 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 04 Jan 2021 09:47:43 -0800 (PST) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v8 07/44] kernel-shark: Update README Date: Mon, 4 Jan 2021 19:46:47 +0200 Message-Id: <20210104174724.70404-8-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210104174724.70404-1-y.karadz@gmail.com> References: <20210104174724.70404-1-y.karadz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org KernelShark is now separated from libtraceevent, libtracefs and trace-cmd. The README file has to be updated to reflect this. Signed-off-by: Yordan Karadzhov (VMware) --- README | 129 ++++++++++++++++++++++++++++++--------------------------- 1 file changed, 67 insertions(+), 62 deletions(-) diff --git a/README b/README index 6c360bb..d7efd96 100644 --- a/README +++ b/README @@ -1,11 +1,13 @@ -This directory contains the new Qt-based version of the KernelShark GUI. +KernelShark is a front-end reader of tracing data. +The official repository is here: + https://git.kernel.org/pub/scm/utils/trace-cmd/kernel-shark.git Third Party Software: ------------------------------------------------------------ KernelShark has the following external dependencies: - Cmake, Json-C, OpenGL/Glut, Qt5Base. + Cmake, Json-C, OpenGL/Glut, Qt5Base, libtraceevent, libtracefs, trace-cmd. 1. In order to install the packages on Ubuntu do the following: sudo apt-get install build-essential git cmake libjson-c-dev -y @@ -25,88 +27,91 @@ KernelShark has the following external dependencies: dnf install graphviz doxygen -y -Building: ------------------------------------------------------------- -1. Follow the instructions given in trace-cmd/README and build -the original trace-cmd end traceevent libraries. - -2. Building KernelShark: - -2.1 There is a simple and expert way to build KernelShark +3. In order to install the final dependencies do the following: + git clone https://git.kernel.org/pub/scm/libs/libtrace/libtraceevent.git/ + cd libtraceevent + make + sudo make install -2.1.1 Option 1 (simple) : build KernelShark as part of trace-cmd + git clone https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/ + cd libtracefs + make + sudo make install - (from the toplevel git tree) + git clone https://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/ + cd trace-cmd + make + sudo make install_libs - make gui +Building: +------------------------------------------------------------ +2. Building KernelShark: - This will build the necessary parts of trace-cmd needed - by KernelShark. + cd kernel-shark/build + cmake ../ + make + sudo make install -2.1.1.1 By default, the installation prefix is "/usr/local". It can be -changed by passing in "prefix" to the build. +2.1 In order to create a Doxygen documentation add -D_DOXYGEN_DOC=1 +as a CMake Command-Line option. - make prefix=/usr gui +2.2 KernelShark has multiple build types. By default, the build type is +RelWithDebInfo, which will build a release candidate with debug information. +To change the type, pass in the option CMAKE_BUILD_TYPE=. -2.1.1.2 Use "make clean" if you want to delete all already compiled objects. -This will also clean up all the files created by cmake. + cmake -DCMAKE_BUILD_TYPE=Debug - for "-g" option + cmake -DCMAKE_BUILD_TYPE=Release - for "-O3" option + cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo - for "-g -O2" option (default) + cmake -DCMAKE_BUILD_TYPE=MinSizeRel - for "-Os" option -2.1.1.3 KernelShark has multiple build types (See section 2.1.2.3 for more -details. By default, the build type is RelWithDebInfo, which will build -a release candidate with debug information. To change the type, pass in -the option BUILD_TYPE= to the make process. +2.3 In addition to the standard CMake build types (Debug, Release, +RelWithDebInfo, MinSizeRel) KernelShark supports a "Package" build type. +By default this build type adds the "-O3" compiler flag. Package maintainers +can chose their own compiler flags by providing the corresponding +CMAKE_XXXX_FLAGS_PACKAGE Command-Line options (see the example below). - make BUILD_TYPE=Debug gui - for "-g" option - make BUILD_TYPE=Release gui - for "-O3" option - make BUILD_TYPE=RelWithDebInfo gui - for "-g -O2" option (default) - make BUILD_TYPE=MinSizeRel gui - for "-Os" option +-- Note that when built as a "Package" the RPATH-s of the executables are +set directly to _INSTALL_PREFIX/lib/kernelshark/ -2.1.2 Option 2 (expert) : standalone build of KernelShark (for hackers only) +2.4 After building the code "kernel-shark/lib" will contain all libraries +and "kernel-shark/bin" will contain all executables. - (note, you may need to do a normal make from the toplevel git - tree before performing the following) +2.5 Use the script "cmake_clean.sh" if you want to delete all already +compiled objects and all files generated by CMake. - make - cd kernel-shark/build - cmake ../ - make + cd kernel-shark/build + ./cmake-clean.sh - This gives you a bit more control as you may easily pass in - your own cmake options. +2.6 By default, installation prefix is "/usr/local". It can be changed using +-D_INSTALL_PREFIX= as a CMake Command-Line option. -2.1.2.1 In order to create a Doxygen documentation add -D_DOXYGEN_DOC=1 -as a CMake Command-Line option. +2.7 In order to uninstall KernelShark do: + cd kernel-shark/build + ./cmake_uninstall.sh -2.1.2.2 By default, installation prefix is "/usr/local". It can be changed using --D_INSTALL_PREFIX= as a CMake Command-Line option. -2.1.2.3 In addition to the standard CMake build types (Debug, Release, -RelWithDebInfo, MinSizeRel) KernelShark supports a "Package" build type. -By default this build type adds the "-O2" compiler flag. Package maintainers -can chose their own compiler flags by providing the corresponding -CMAKE_XXXX_FLAGS_PACKAGE Command-Line options (see the example below). +CMake examples: --- Note that when built as a "Package" the RPATH-s of the executables are -set directly to _INSTALL_PREFIX/lib/kernelshark/ + cmake -D_DOXYGEN_DOC=1 -D_INSTALL_PREFIX=/usr ../ --- Note that when built as a "Debug" the "Record" dialog will try to use the -version of the trace-cmd executable from the build location. In all other cases -the dialog will derive the absolut path to the trace-cmd executable from -"_INSTALL_PREFIX", hence the dialog will use the installed version. + cmake -DCMAKE_BUILD_TYPE=Package -DCMAKE_C_FLAGS_PACKAGE="-O3 -pedantic" ../ -If no build types is specified, the type will be "RelWithDebInfo". -Examples: +Contributions: +------------------------------------------------------------ +3. For questions about the use of the library, please send email to: + linux-trace-users@vger.kernel.org - cmake -D_DOXYGEN_DOC=1 -D_INSTALL_PREFIX=/usr ../ + Subscribe: http://vger.kernel.org/vger-lists.html#linux-trace-users + Archives: https://lore.kernel.org/linux-trace-users/ - cmake -DCMAKE_BUILD_TYPE=Package -DCMAKE_C_FLAGS_PACKAGE="-O3 -pedantic" ../ +3.1 For contributions to development, please send patches to: + linux-trace-devel@vger.kernel.org -2.1.2.4 Use the script "cmake_clean.sh" if you want to delete all already -compiled objects and all files generated by CMake. + Subscribe: http://vger.kernel.org/vger-lists.html#linux-trace-devel + Archives: https://lore.kernel.org/linux-trace-devel/ - cd kernel-shark/build - ./cmake-clean.sh +3.2 Note, this project follows the style of submitting patches as described +by the Linux kernel. -3. After building the code "kernel-shark/lib" will contain all libraries -and "kernel-shark/bin" will contain all executables. + https://www.kernel.org/doc/html/v5.4/process/submitting-patches.html From patchwork Mon Jan 4 17:46:48 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11997147 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 05290C433E6 for ; Mon, 4 Jan 2021 17:49:00 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id B0AF2216C4 for ; Mon, 4 Jan 2021 17:48:59 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1725889AbhADRs7 (ORCPT ); Mon, 4 Jan 2021 12:48:59 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34362 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726246AbhADRs7 (ORCPT ); Mon, 4 Jan 2021 12:48:59 -0500 Received: from mail-ed1-x52a.google.com (mail-ed1-x52a.google.com [IPv6:2a00:1450:4864:20::52a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id BF920C06179F for ; Mon, 4 Jan 2021 09:47:45 -0800 (PST) Received: by mail-ed1-x52a.google.com with SMTP id u19so28247584edx.2 for ; Mon, 04 Jan 2021 09:47:45 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=D3d713R5HfwUlCpz7BWPRY7IZsIzoZ+VHWYRKptPN1c=; b=OgUczI2at6KgB/5jPlwUqcccSPCGI3Vyo2XGctwIfTrW1vaetYrwqrmYmtDyEgDte6 6/vnE9seMwH6d/ZRw4ucgHGozjzYaocB6u2O+wqNehgzrpwA8Ondca9YBdFWVbIpe2C+ En1NSEKLfVX6wTVN2gV5OniazjaCI47Fgq7iBWcjYEsGJY9OCCm/yeXWe3dZmT/eZ7WJ UqXRR7hvwZZ3tbQFBHCR/KeFOqvgNSVSfTnZmXYY9Bog6sD/k0X5hTBgKg624BnzivSG VhzbG/8aR6TYssJzV3Eayp8pNvssNfBoXSDtbdo0NC6M7zKoRLgUmw/hHlT1UiUBNuJ1 2FIg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=D3d713R5HfwUlCpz7BWPRY7IZsIzoZ+VHWYRKptPN1c=; b=sXCAIzVQvESkzsjq1GCaKhD/gsIezUHvw4SY5Va4vYbFrlsWe5Ku1DrofjROrqO7j+ s4JAc1Pambf+IzHHQ2699cDR8B/0XVRmL6/L+lgjXeXQb1kVhIUtv/dJPfFCXShP4NpT /DQ3b+x3o4LTP3nOyNX3UrEx8q3udfVqYDEIZNVB2Cw7ODdaaKedYqoGIw2dYLtwMr7Z dpT8X7kXdCVtw3kuCFtTllVIlGQyMhFjKJ2XTSLHa1MQxGlzIERwt4wabBands4SY6IC l7HaSNBP2meRPXysXm6kTYH/qEGBBGU2SWffvQmw/Au9XUEc//XukQrT58eIKLSHzlyI E+Rw== X-Gm-Message-State: AOAM531XGM9XyuqxSvSYleeLzYn4QIFpHWLaF0Bry3p3AMmoMIJ2bbG3 Hom4wuIqkLF3rkyG3VHo36EaEqhumuthcA== X-Google-Smtp-Source: ABdhPJws58ArGKZliPzk1tiDhxMMOTokBT/KZfMVRdgo9fHPkrtGHamDaHlsQPoDzVcLcgP0lQRQmQ== X-Received: by 2002:a05:6402:22e1:: with SMTP id dn1mr73299294edb.347.1609782464517; Mon, 04 Jan 2021 09:47:44 -0800 (PST) Received: from localhost.localdomain ([95.87.199.238]) by smtp.gmail.com with ESMTPSA id l14sm44107750edq.35.2021.01.04.09.47.43 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 04 Jan 2021 09:47:43 -0800 (PST) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v8 08/44] kernel-shark: Define build target for JSONC Date: Mon, 4 Jan 2021 19:46:48 +0200 Message-Id: <20210104174724.70404-9-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210104174724.70404-1-y.karadz@gmail.com> References: <20210104174724.70404-1-y.karadz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org Having the JSONC libs as a target simplifies the build/linking of the KernelShark libraries and executables because all necessary information, like include paths, compiler flags and library names can be retriever directly from the target. The patch also adds to FindJSONC.cmake the standard module documentation header. Signed-off-by: Yordan Karadzhov (VMware) --- CMakeLists.txt | 3 +- build/FindJSONC.cmake | 70 +++++++++++++++++++++++++++++++++---------- src/CMakeLists.txt | 2 +- 3 files changed, 57 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e970c3..f21d734 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,7 +41,7 @@ find_package(TraceEvent REQUIRED) find_package(TraceFS REQUIRED) find_package(TraceCmd REQUIRED) -include(${KS_DIR}/build/FindJSONC.cmake) +find_package(JSONC REQUIRED) find_package(Doxygen) @@ -99,7 +99,6 @@ endif (CMAKE_BUILD_TYPE MATCHES Package) include_directories(${KS_DIR}/src/ ${KS_DIR}/build/src/ - ${JSONC_INCLUDE_DIR} ${TRACECMD_INCLUDE_DIR}) message("") diff --git a/build/FindJSONC.cmake b/build/FindJSONC.cmake index 3bae20f..2d0d41f 100644 --- a/build/FindJSONC.cmake +++ b/build/FindJSONC.cmake @@ -1,13 +1,44 @@ -# - Try to find json-c -# https://cmake.org/Wiki/CMake:How_To_Find_Libraries -# Once done this will define -# JSONC_FOUND - System has json-c -# JSONC_INCLUDE_DIRS - The json-c include directories -# JSONC_LIBRARIES - The libraries needed to use json-c -# JSONC_DEFINITIONS - Compiler switches required for using json-c - -find_package(PkgConfig) + +#[=======================================================================[.rst: +FindJSONC +------- + +Finds the traceevent library. + +Imported Targets +^^^^^^^^^^^^^^^^ + +This module defines the :prop_tgt:`IMPORTED` targets: + +``jsonc::jsonc`` + Defined if the system has json-c. + +Result Variables +^^^^^^^^^^^^^^^^ + +``JSONC_FOUND`` + True if the system has the json-c library. +``JSONC_VERSION`` + The version of the json-c library which was found. +``JSONC_INCLUDE_DIRS`` + Include directories needed to use json-c. +``JSONC_LIBRARIES`` + Libraries needed to link to json-c. + +Cache Variables +^^^^^^^^^^^^^^^ + +``JSONC_INCLUDE_DIR`` + The directory containing ``json.h``. +``JSONC_LIBRARY`` + The path to the traceevent library. + +#]=======================================================================] + +find_package(PkgConfig QUIET) pkg_check_modules(PC_JSONC QUIET json-c) + +set(JSONC_VERSION ${PC_JSONC_VERSION}) set(JSONC_DEFINITIONS ${PC_JSONC_CFLAGS_OTHER}) find_path(JSONC_INCLUDE_DIR json.h @@ -20,19 +51,28 @@ find_library(JSONC_LIBRARY NAMES json-c libjson-c find_library(JSONC_LIBRARY NAMES json-c libjson-c HINTS ${PC_JSON-C_LIBDIR} ${PC_JSON-C_LIBRARY_DIRS}) +mark_as_advanced(JSONC_INCLUDE_DIR JSONC_LIBRARY) + include(FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set JSONC_FOUND to TRUE # if all listed variables are TRUE find_package_handle_standard_args(JSONC DEFAULT_MSG JSONC_LIBRARY JSONC_INCLUDE_DIR) -if (NOT JSONC_FOUND) +if(JSONC_FOUND) - message(FATAL_ERROR "Json-C is Required!\n") + set(JSONC_LIBRARIES ${JSONC_LIBRARY}) + set(JSONC_INCLUDE_DIRS ${JSONC_INCLUDE_DIR}) -endif (NOT JSONC_FOUND) + if(NOT TARGET jsonc::jsonc) + add_library(jsonc::jsonc UNKNOWN IMPORTED) -mark_as_advanced(JSONC_INCLUDE_DIR JSONC_LIBRARY) + set_target_properties(jsonc::jsonc + PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${JSONC_INCLUDE_DIRS}" + INTERFACE_COMPILE_DEFINITIONS "${JSONC_DEFINITIONS}" + IMPORTED_LINK_INTERFACE_LANGUAGES "C" + IMPORTED_LOCATION "${JSONC_LIBRARIES}") + endif() -set(JSONC_LIBRARIES ${JSONC_LIBRARY}) -set(JSONC_INCLUDE_DIRS ${JSONC_INCLUDE_DIR}) +endif() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d51e5ee..ecd448c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -12,7 +12,7 @@ add_library(kshark SHARED libkshark.c target_link_libraries(kshark trace::cmd trace::fs trace::event - ${JSONC_LIBRARY} + jsonc::jsonc ${CMAKE_DL_LIBS}) set_target_properties(kshark PROPERTIES From patchwork Mon Jan 4 17:46:49 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11997151 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id C3D43C433E0 for ; Mon, 4 Jan 2021 17:49:02 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 9AB1020715 for ; Mon, 4 Jan 2021 17:49:02 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726308AbhADRtC (ORCPT ); Mon, 4 Jan 2021 12:49:02 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34374 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726246AbhADRtC (ORCPT ); Mon, 4 Jan 2021 12:49:02 -0500 Received: from mail-ed1-x52b.google.com (mail-ed1-x52b.google.com [IPv6:2a00:1450:4864:20::52b]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A5797C0617A0 for ; Mon, 4 Jan 2021 09:47:46 -0800 (PST) Received: by mail-ed1-x52b.google.com with SMTP id b73so28212332edf.13 for ; Mon, 04 Jan 2021 09:47:46 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=6J3nlTQiUnlAkEkX8AHZUBwDx72VEWyI0gNMQxrXqkc=; b=KhuWpg1DEYdWZYuwINh9oJbaWiQHpxOnmOCTnt71v4up/0lDMR9fNNgdtMtUiHvBd6 fyAm4t9o9O3ltx2vgqgVUUZ36pUN+6Q73v0LFPVXofxTWk+uHfrKXbVtaXsw9qNsNSiN 5dStIm3qDAP0LKXscIR8gzX9t65jk/1Hi6A4ccOHvO5nwkS2yaEaZkiAS4riMU9tJSH2 shy6BZ7ECyMPcjxl8BhC6IGvNkAcGKIhD7YOW0ogn+a/LU2ysKHlueiFskbbgt/ybynL 99kIzvD3DuRujy+2u6pIH5LqWhK7mWxUjrUNjPpkm1aA9kdfLAH7hU1e8FvA1vsL8EhP EcxA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=6J3nlTQiUnlAkEkX8AHZUBwDx72VEWyI0gNMQxrXqkc=; b=OnNpp7NZskjpqZLm6ChtIkGnsGvMnOpvvxR3awCmD4OJ216qvQl8a9fptke37b06Op Jn1D2bCXORAwPJtVmdfkrq2fYsOVksVgFqzWfnC+HPBGVo3hlH0QjjhIDLC4h2iDa1Bo HPGn5inEm4yTyJlsUGXkyqyszhXTqOgtgP83RXBSqBbg6YzCFbgAiz0atxg2c46bG6B+ WLRoPIAl992mv1oPeq7ap/oGR6nk5V3B9ScXyIZGuYeTqPQ2NnCL53T6iUzwwXqbp7El 7gQ6abIZp48uXIMwmNWonXnHBsl+bvflm5+ArOPHG4bcSyMEYue8Oog2OBKdZQCzSqys cLvw== X-Gm-Message-State: AOAM531Rp6cOGvYM6qoIIfGakv0bLyVuDrr8eDGEtBef9eS4dECKrhTi pWJ+Xry2fAizDhnV6mkGZMM= X-Google-Smtp-Source: ABdhPJwearDCOIdvZQ/ZbrA3bYEmXe/aATpd04EysddlRUmgaYL7QzkrGXqHpYqL6Uj03z3zmxij7Q== X-Received: by 2002:a05:6402:1045:: with SMTP id e5mr72476449edu.40.1609782465477; Mon, 04 Jan 2021 09:47:45 -0800 (PST) Received: from localhost.localdomain ([95.87.199.238]) by smtp.gmail.com with ESMTPSA id l14sm44107750edq.35.2021.01.04.09.47.44 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 04 Jan 2021 09:47:45 -0800 (PST) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v8 09/44] kernel-shark: Use only signed types in kshark_entry Date: Mon, 4 Jan 2021 19:46:49 +0200 Message-Id: <20210104174724.70404-10-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210104174724.70404-1-y.karadz@gmail.com> References: <20210104174724.70404-1-y.karadz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org Using uint64_t for the value of the offset was just wrong. According to the POSIX standard off_t is a signed integer type with unspecified size. Here we stick to a 64 bit integer, because this size guaranties optimal packing of the kshark_entry structure. Using unsigned values for the timestamps is also a source of problems and has been a reason for the introduction of multiple bugs in the past. In principal the value of the timestamps cannot be negative. However, this value must have the same type as the values used to define the state of the visualization model, like the range of the model or the size of the bin. The model state definitions should not take negative values as well, however their values are recalculated automatically when the user browses the data and those calculations may result in negative values in some corner cases. Because of this it is better to use a signed integer type and treat the negative values as an indicator of an error rather than have the negative result of the calculations casted into unsigned type which results into unpredictable behavior of the model. Signed-off-by: Yordan Karadzhov (VMware) --- src/libkshark.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libkshark.h b/src/libkshark.h index a44f46e..3b57e0b 100644 --- a/src/libkshark.h +++ b/src/libkshark.h @@ -61,7 +61,7 @@ struct kshark_entry { int32_t event_id; /** The offset into the trace file, used to find the record. */ - uint64_t offset; + int64_t offset; /** * The time of the record in nano seconds. The value is taken from @@ -69,7 +69,7 @@ struct kshark_entry { * dependent. The time usually is the timestamp from when the system * started. */ - uint64_t ts; + int64_t ts; }; /** Size of the task's hash table. */ From patchwork Mon Jan 4 17:46:50 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11997153 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 3A3ADC433DB for ; Mon, 4 Jan 2021 17:49:03 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 0A6ED20715 for ; Mon, 4 Jan 2021 17:49:03 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726336AbhADRtC (ORCPT ); Mon, 4 Jan 2021 12:49:02 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34376 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726300AbhADRtC (ORCPT ); Mon, 4 Jan 2021 12:49:02 -0500 Received: from mail-ed1-x533.google.com (mail-ed1-x533.google.com [IPv6:2a00:1450:4864:20::533]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 754D0C0617A2 for ; Mon, 4 Jan 2021 09:47:47 -0800 (PST) Received: by mail-ed1-x533.google.com with SMTP id cm17so28242963edb.4 for ; Mon, 04 Jan 2021 09:47:47 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=QLycbyjL0opw4QhyZtP6Cd/e066iSR39csPtYlEnn3M=; b=mhlCcacpvEatVU9SNZgThKE1ZnZSXlL2ybozy5B020jrc0z+KTADcjmeQIrrZOIaGW r2j4Xru8RGuhmYtcVFPXkRhmPMGy0elj9Bn7bLRrVYfiBAjekgs1vsXgS9vcN63T4hTn DCI6zRvs+12cxgijT9qfIh4wFc0o8mnhMpJBU4ovIw3vbhY2a5W1xaJCQFfmeRhbu/lb hTwaiO2qvBHp5ERQvWovr8LPFyFNhlCuPufEtIGGjJvdwe5bkh2l3UlreGtzFSv8Fwwr 6Vp8vUvPcAOltcES/+lQD/VYQO1C7IlfHP9iomx9JCFRghpTBkESwF+S3c+ykgUoMbzV SlVg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=QLycbyjL0opw4QhyZtP6Cd/e066iSR39csPtYlEnn3M=; b=MNaVERERIyJAALp1Of3MZ/c3YufcX52baBzqsLEXL1nnhk0ZEzc7tfad1oVMjRTrNt PL2q3owRQRj5SeNPWXfdDJsCk7TUBl5I4ibDR+RDGK911kPL5yClwZ0lcS3u6ZYjQew+ YlnTWOfyH8yH4gDDh6TclDNegpo+FgyQhia2L5NhEFBh9AqDyozwj1KP3TTWXU+cGu1g Eki8tYOBuhTDEVbMtuve1XLMhncWgq62bWrSnuD8cg3y2LMq31QRbVgGMB2UmnKPC6Q6 9TQtYpCZ9L5hkKoCnsyaRIX51q8POzHIEtQr/3wWoBW+pEKpyaLVQKOi+NGj8gBNVKMM fm8w== X-Gm-Message-State: AOAM533MvkZa69yTdvwWjNFRbPvA+A2iRbzZfOhPs7UBGYFWsm3jKF/g b7avPkWiJApBEPH0FaLMCDA= X-Google-Smtp-Source: ABdhPJzVY5Ini2CA/e+4XI0zCg7eL8fYJJWNicf3iJ7l1qQQK1TrOgD0l0bMnoZ0usHBZcsKCPZpnA== X-Received: by 2002:a05:6402:d0a:: with SMTP id eb10mr19199055edb.305.1609782466297; Mon, 04 Jan 2021 09:47:46 -0800 (PST) Received: from localhost.localdomain ([95.87.199.238]) by smtp.gmail.com with ESMTPSA id l14sm44107750edq.35.2021.01.04.09.47.45 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 04 Jan 2021 09:47:45 -0800 (PST) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v8 10/44] kernel-shark: Add stream_id to kshark_entry Date: Mon, 4 Jan 2021 19:46:50 +0200 Message-Id: <20210104174724.70404-11-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210104174724.70404-1-y.karadz@gmail.com> References: <20210104174724.70404-1-y.karadz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org kshark_entry contains all information from one trace record, needed in order to visualize the time-series of trace records. Here we reorganize the data fields of kshark_entry in order to make room for the unique identifier of the Data stream this entry belongs to. The Id is coded in 16 bits that are taken from the "event_id" field. So far "event_id" uses 32 bits but we found that it is coded with only 16 bits inside the kernel, so we still have 16 unused (spare) bits in it. Because the stream Id is coded with int8_t, we need to set an upper limit (127) for the total number of Data streams loaded. Signed-off-by: Yordan Karadzhov (VMware) --- src/libkshark.h | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/libkshark.h b/src/libkshark.h index 3b57e0b..f279d16 100644 --- a/src/libkshark.h +++ b/src/libkshark.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1 */ /* - * Copyright (C) 2017 VMware Inc, Yordan Karadzhov + * Copyright (C) 2017 VMware Inc, Yordan Karadzhov (VMware) */ /** @@ -51,15 +51,18 @@ struct kshark_entry { */ uint16_t visible; + /** Data stream identifier. */ + int16_t stream_id; + + /** Unique Id of the trace event type. */ + int16_t event_id; + /** The CPU core of the record. */ int16_t cpu; /** The PID of the task the record was generated. */ int32_t pid; - /** Unique Id ot the trace event type. */ - int32_t event_id; - /** The offset into the trace file, used to find the record. */ int64_t offset; From patchwork Mon Jan 4 17:46:51 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11997159 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 95CD4C433E9 for ; Mon, 4 Jan 2021 17:49:03 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 683D120700 for ; Mon, 4 Jan 2021 17:49:03 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726328AbhADRtD (ORCPT ); Mon, 4 Jan 2021 12:49:03 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34378 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726246AbhADRtC (ORCPT ); Mon, 4 Jan 2021 12:49:02 -0500 Received: from mail-ed1-x529.google.com (mail-ed1-x529.google.com [IPv6:2a00:1450:4864:20::529]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5E7F3C0617A3 for ; Mon, 4 Jan 2021 09:47:48 -0800 (PST) Received: by mail-ed1-x529.google.com with SMTP id p22so28204288edu.11 for ; Mon, 04 Jan 2021 09:47:48 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=P+/g/9EdaO4++eVkQ3ka4WeX8HnI37b9SgSMdAF/oh4=; b=HAhNsxp5BfxcEi0Hh2wl/k5a8FEn+3Tog1FEJcX32euuoSQLM7qulyQBe/zsHovyQY cX0PpTIHEEhStxJ6NEtl1vHIN2kDrNoHk9xDeiGQ21eeZxdhyKlnR6liHsooGMuKAtbl mYULcfN3PdsrXT2io3y53EUgzkErbB6LNZ6UdB3LMyfB6ZYlQ9ZRtWTd6N+122ZE7f1H iiPfdCug+nbkNuRn7BFQJDJDbZl7HQxxs+gFb3wNnoCW4wKETSbwod5Z8E3Go8wN0Q2J vvGHeePkWYn7/fgP3984KE4rpH2d28/Y0L99gGwOV7AGNSdnkUP12Tuslskg7ehIXQsV cVfA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=P+/g/9EdaO4++eVkQ3ka4WeX8HnI37b9SgSMdAF/oh4=; b=hLjuD8eXi9uYCKOoWuVjFJV4Axz13xHWEbQTdBXCjE3zUo9pXMTRWCPUpDBR44ZGlP Hc3v/hFXd5xi3hjz+rSZQcCKCDoFufKiRWerIxMyYiNqZkXvgy/ERcCuJOQprZGqcNeL 0GzSOI+nqcBcAROwdkdgeB0/4GsCtQlOieUd/EICbDf+9sFPLXiW7s5m2yOSs8PA5zq2 QkYBgcpqCcVtlhHjx+tu3y5XqnPKM2sN2XPCJDQMBtuqSFMQulNeBWSp3gFOWV6sLa7Q w6+zuUy2okujmN4UaEo/V9swfUIWwgbsGEvgbfLCdurCQOLL5slA/IJHTHLRaYP+6rF3 VGpg== X-Gm-Message-State: AOAM531TlT7oY8gNlyAIrUeyDLKoXxwdaCajQnoUkTj4DkniYi6nZrvh 7ZfHhCyc/Gv7UmHxAqHWaQs= X-Google-Smtp-Source: ABdhPJx+lG2XqHdMvaIyP7193frcx5sStVxouRBDlpj9CsUALgBwYMLmm13a6mb/9H0He/sJw3SxuA== X-Received: by 2002:a50:b944:: with SMTP id m62mr71227268ede.182.1609782467121; Mon, 04 Jan 2021 09:47:47 -0800 (PST) Received: from localhost.localdomain ([95.87.199.238]) by smtp.gmail.com with ESMTPSA id l14sm44107750edq.35.2021.01.04.09.47.46 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 04 Jan 2021 09:47:46 -0800 (PST) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v8 11/44] kernel-shark: Introduce libkshark-hash Date: Mon, 4 Jan 2021 19:46:51 +0200 Message-Id: <20210104174724.70404-12-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210104174724.70404-1-y.karadz@gmail.com> References: <20210104174724.70404-1-y.karadz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org So far KernelShark have been using an implementation of a hash table from trace-cmd/include/trace-cmd/trace-filter-hash.h. However it turns that KernelShark is the only user of trace-filter-hash, which means that it make more sense to make this implementation of the hash table part of KernelShark. In this patch we adapt the original trace-cmd implementation and change the naming convention used. Signed-off-by: Yordan Karadzhov (VMware) --- src/CMakeLists.txt | 1 + src/libkshark-hash.c | 239 +++++++++++++++++++++++++++++++++++++++++++ src/libkshark.h | 46 +++++++++ 3 files changed, 286 insertions(+) create mode 100644 src/libkshark-hash.c diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ecd448c..e1a4b5f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,6 +4,7 @@ set(KS_INCLUDS_DESTINATION "${_INSTALL_PREFIX}/include/${KS_APP_NAME}") message(STATUS "libkshark") add_library(kshark SHARED libkshark.c + libkshark-hash.c libkshark-model.c libkshark-plugin.c libkshark-configio.c diff --git a/src/libkshark-hash.c b/src/libkshark-hash.c new file mode 100644 index 0000000..89c021b --- /dev/null +++ b/src/libkshark-hash.c @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: LGPL-2.1 + +/* + * Copyright (C) 2009, Steven Rostedt + * Copyright (C) 2018 VMware Inc, Steven Rostedt + */ + +/** + * @file libkshark-hash.c + * @brief Hash table of integer Id numbers. + */ + +// C +#include +#include +#include + +// KernelShark +#include "libkshark.h" + +/** + * @brief: quick_hash - A quick (non secured) hash alogirthm + * @param val: The value to perform the hash on + * @param bits: The size in bits you need to return + * + * This is a quick hashing function adapted from Donald E. Knuth's 32 + * bit multiplicative hash. See The Art of Computer Programming (TAOCP). + * Multiplication by the Prime number, closest to the golden ratio of + * 2^32. + * + * "bits" is used to max the result for use cases that require + * a power of 2 return value that is less than 32 bits. Any value + * of "bits" greater than 31 (or zero), will simply return the full hash + * on "val". + */ +static inline uint32_t quick_hash(uint32_t val, unsigned int bits) +{ + val *= UINT32_C(2654435761); + + if (!bits || bits > 31) + return val; + + return val & ((1 << bits) - 1); +} + +static size_t hash_size(struct kshark_hash_id *hash) +{ + return (1 << hash->n_bits); +} + +/** + * Create new hash table of Ids. + */ +struct kshark_hash_id *kshark_hash_id_alloc(size_t n_bits) +{ + struct kshark_hash_id *hash; + size_t size; + + hash = calloc(1, sizeof(*hash)); + if (!hash) + goto fail; + + hash->n_bits = n_bits; + hash->count = 0; + + size = hash_size(hash); + hash->hash = calloc(size, sizeof(*hash->hash)); + if (!hash->hash) + goto fail; + + return hash; + + fail: + fprintf(stderr, "Failed to allocate memory for hash table.\n"); + kshark_hash_id_free(hash); + return NULL; +} + +/** Free the hash table of Ids. */ +void kshark_hash_id_free(struct kshark_hash_id *hash) +{ + if (!hash) + return; + + if (hash->hash) { + kshark_hash_id_clear(hash); + free(hash->hash); + } + + free(hash); +} + +/** + * @brief Check if an Id with a given value exists in this hash table. + */ +bool kshark_hash_id_find(struct kshark_hash_id *hash, int id) +{ + uint32_t key = quick_hash(id, hash->n_bits); + struct kshark_hash_id_item *item; + + for (item = hash->hash[key]; item; item = item->next) + if (item->id == id) + break; + + return !!(unsigned long) item; +} + +/** + * @brief Add Id to the hash table. + * + * @param hash: The hash table to add to. + * @param id: The Id number to be added. + * + * @returns Zero if the Id already exists in the table, one if the Id has been + * added and negative errno code on failure. + */ +int kshark_hash_id_add(struct kshark_hash_id *hash, int id) +{ + uint32_t key = quick_hash(id, hash->n_bits); + struct kshark_hash_id_item *item; + + if (kshark_hash_id_find(hash, id)) + return 0; + + item = calloc(1, sizeof(*item)); + if (!item) { + fprintf(stderr, + "Failed to allocate memory for hash table item.\n"); + return -ENOMEM; + } + + item->id = id; + item->next = hash->hash[key]; + hash->hash[key] = item; + hash->count++; + + return 1; +} + +/** + * @brief Remove Id from the hash table. + */ +void kshark_hash_id_remove(struct kshark_hash_id *hash, int id) +{ + struct kshark_hash_id_item *item, **next; + int key = quick_hash(id, hash->n_bits); + + next = &hash->hash[key]; + while (*next) { + if ((*next)->id == id) + break; + next = &(*next)->next; + } + + if (!*next) + return; + + assert(hash->count); + + hash->count--; + item = *next; + *next = item->next; + + free(item); +} + +/** Remove (free) all Ids (items) from this hash table. */ +void kshark_hash_id_clear(struct kshark_hash_id *hash) +{ + struct kshark_hash_id_item *item, *next; + size_t size; + int i; + + if (!hash || ! hash->hash) + return; + + size = hash_size(hash); + for (i = 0; i < size; i++) { + next = hash->hash[i]; + if (!next) + continue; + + hash->hash[i] = NULL; + while (next) { + item = next; + next = item->next; + free(item); + } + } + + hash->count = 0; +} + +static int compare_ids(const void* a, const void* b) +{ + int arg1 = *(const int*)a; + int arg2 = *(const int*)b; + + if (arg1 < arg2) + return -1; + + if (arg1 > arg2) + return 1; + + return 0; +} + +/** + * @brief Get a sorted array containing all Ids of this hash table. + */ +int *kshark_hash_ids(struct kshark_hash_id *hash) +{ + struct kshark_hash_id_item *item; + size_t size = hash_size(hash); + int count = 0, i; + int *ids; + + if (!hash->count) + return NULL; + + ids = calloc(hash->count, sizeof(*ids)); + if (!ids) { + fprintf(stderr, + "Failed to allocate memory for Id array.\n"); + return NULL; + } + + for (i = 0; i < size; i++) { + item = hash->hash[i]; + while (item) { + ids[count++] = item->id; + item = item->next; + } + } + + qsort(ids, hash->count, sizeof(*ids), compare_ids); + + return ids; +} diff --git a/src/libkshark.h b/src/libkshark.h index f279d16..e34a32f 100644 --- a/src/libkshark.h +++ b/src/libkshark.h @@ -75,6 +75,52 @@ struct kshark_entry { int64_t ts; }; +/** Size of the hash table of PIDs in terms of bits being used by the key. */ +#define KS_TASK_HASH_NBITS 16 + +/** Size of the hash table of Ids in terms of bits being used by the key. */ +#define KS_FILTER_HASH_NBITS 8 + +/** A bucket for the hash table of integer Id numbers (kshark_hash_id). */ +struct kshark_hash_id_item { + /** Pointer to the Id in this bucket. */ + struct kshark_hash_id_item *next; + + /** The Id value. */ + int id; +}; + +/** + * Hash table of integer Id numbers. To be used for fast filter of trace + * entries. + */ +struct kshark_hash_id { + /** Array of buckets. */ + struct kshark_hash_id_item **hash; + + /** The number of Ids in the table. */ + size_t count; + + /** + * The number of bits used by the hashing function. + * Note that the number of buckets in the table if given by + * 1 << n_bits. + */ + size_t n_bits; +}; + +bool kshark_hash_id_find(struct kshark_hash_id *hash, int id); + +int kshark_hash_id_add(struct kshark_hash_id *hash, int id); + +void kshark_hash_id_clear(struct kshark_hash_id *hash); + +struct kshark_hash_id *kshark_hash_id_alloc(size_t n_bits); + +void kshark_hash_id_free(struct kshark_hash_id *hash); + +int *kshark_hash_ids(struct kshark_hash_id *hash); + /** Size of the task's hash table. */ #define KS_TASK_HASH_SHIFT 16 #define KS_TASK_HASH_SIZE (1 << KS_TASK_HASH_SHIFT) From patchwork Mon Jan 4 17:46:52 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11997157 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5B22BC433E6 for ; Mon, 4 Jan 2021 17:49:03 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 3473F216C4 for ; Mon, 4 Jan 2021 17:49:03 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726300AbhADRtD (ORCPT ); Mon, 4 Jan 2021 12:49:03 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34380 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726328AbhADRtC (ORCPT ); Mon, 4 Jan 2021 12:49:02 -0500 Received: from mail-ed1-x52d.google.com (mail-ed1-x52d.google.com [IPv6:2a00:1450:4864:20::52d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 556A3C0617A4 for ; Mon, 4 Jan 2021 09:47:49 -0800 (PST) Received: by mail-ed1-x52d.google.com with SMTP id dk8so28285037edb.1 for ; Mon, 04 Jan 2021 09:47:49 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=HHmo4BcO4eeVSB5OUqaHh7TImckHRctgu9l9aSCrjiY=; b=PvuUtCnVRj5048JKxaLLtFTvmskDfcJM0tTytqG5cAOJXIQVYIF+XFNH94hvgvt11l CHPhkELk/CSVtY6yfA4Dgpc8aadBSz5neZ/PmshTOVW7WgKuKtrYNgb391xFjdDooGqX fRx43eEADaNQYEPXjEEkoVzUsSq5lr6gmOOS04thNSJi9ec5fQcLW+DEYq4BZoUdEZpJ PbNBofAgh6c+C3n7pD0bJ6XpwENJ6vOmB3jogy3yTNobaD4/xwbEhpy1g6a8e6J6zXwY Ppe+Zj6rLFObFlYwoABKRYYvvn5q9/Kz1nimMm3MYbQB/8kZlhURqSYy3Eae5qweu8C6 POcg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=HHmo4BcO4eeVSB5OUqaHh7TImckHRctgu9l9aSCrjiY=; b=DbzoPbGyDxKY65hR/gfVWCE7FMWv4WfT9r92Qai6SFDQcZlX6WEIlqwjJ8MItPEfLP OujM8wIoJ+KsZ2xRB9r/mzaIejMfV+miASvHVHVXNBPskNEKPv+FkD3c1O9WF7mFOmhV qQZC0PlJVEJlbzvbTjRvJxJzc8yqVBxORjTcwkmpEee92olbgTDZ9LaqeZI8jSTETtoi TI+KdnnnTIVmBvJcQBZbUhZut1H34ZvJMcgxhtuyuDwX2OWXD69sPBlLv06T2kFDQOpi rLoVIrglFE3DbgIkoLfgapJGUB55iCbzhVPUa3jjqU1NhhgzaImEdQtSKniSRa2LP/hx 3ROA== X-Gm-Message-State: AOAM532ly0W0KzRyTu0aafqb0BAQrdHN+19lrZQWwiIhx3TrR9KifYFV XMuzU5GkdXzZdYkZtDkYMo3CpJgWIA6Q/w== X-Google-Smtp-Source: ABdhPJx4pL+UBOKU3qUeOT5lmoucJu6Ed96CQFH2yV39uVtac+n3+YjJJvTxbLXpzzlvi3Yr5MlM4w== X-Received: by 2002:a50:ed04:: with SMTP id j4mr73340901eds.84.1609782468069; Mon, 04 Jan 2021 09:47:48 -0800 (PST) Received: from localhost.localdomain ([95.87.199.238]) by smtp.gmail.com with ESMTPSA id l14sm44107750edq.35.2021.01.04.09.47.47 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 04 Jan 2021 09:47:47 -0800 (PST) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v8 12/44] kernel-shark: Introduce Data streams Date: Mon, 4 Jan 2021 19:46:52 +0200 Message-Id: <20210104174724.70404-13-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210104174724.70404-1-y.karadz@gmail.com> References: <20210104174724.70404-1-y.karadz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org With the help of Data stream, KernelShark will be able to load and merge multiple trace files (streams). Each stream can have different plugins or filters, registered for it, which means that the raw trace data of the streams can have different formats, and will allow for a great degree of customization of the provided data visualization. In this patch we only provide the basic definitions. The actual integration of the Data streams into the C API of KernelShark will happen in the following patches. Signed-off-by: Yordan Karadzhov (VMware) --- src/libkshark.h | 199 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 199 insertions(+) diff --git a/src/libkshark.h b/src/libkshark.h index e34a32f..f2e29ac 100644 --- a/src/libkshark.h +++ b/src/libkshark.h @@ -121,6 +121,205 @@ void kshark_hash_id_free(struct kshark_hash_id *hash); int *kshark_hash_ids(struct kshark_hash_id *hash); +struct kshark_data_stream; + +/** A function type to be used by the method interface of the data stream. */ +typedef char *(*stream_get_str_func) (struct kshark_data_stream *, + const struct kshark_entry *); + +/** A function type to be used by the method interface of the data stream. */ +typedef const int (*stream_get_int_func) (struct kshark_data_stream *, + const struct kshark_entry *); + +/** A function type to be used by the method interface of the data stream. */ +typedef int (*stream_find_id_func) (struct kshark_data_stream *, + const char *); + +/** A function type to be used by the method interface of the data stream. */ +typedef int *(*stream_get_ids_func) (struct kshark_data_stream *); + +/** A function type to be used by the method interface of the data stream. */ +typedef int (*stream_get_names_func) (struct kshark_data_stream *, + const struct kshark_entry *, + char ***); + +/** Event field format identifier. */ +typedef enum kshark_event_field_format { + /** A field of unknown type. */ + KS_INVALID_FIELD, + + /** Integer number */ + KS_INTEGER_FIELD, + + /** Floating-point number */ + KS_FLOAT_FIELD +} kshark_event_field_format; + +/** A function type to be used by the method interface of the data stream. */ +typedef kshark_event_field_format +(*stream_event_field_type) (struct kshark_data_stream *, + const struct kshark_entry *, + const char *); + +/** A function type to be used by the method interface of the data stream. */ +typedef const int (*stream_read_event_field) (struct kshark_data_stream *, + const struct kshark_entry *, + const char *, + int64_t *); + +/** A function type to be used by the method interface of the data stream. */ +typedef const int (*stream_read_record_field) (struct kshark_data_stream *, + void *, + const char *, + int64_t *); + +struct kshark_context; + +/** A function type to be used by the method interface of the data stream. */ +typedef ssize_t (*load_entries_func) (struct kshark_data_stream *, + struct kshark_context *, + struct kshark_entry ***); + +/** A function type to be used by the method interface of the data stream. */ +typedef ssize_t (*load_matrix_func) (struct kshark_data_stream *, + struct kshark_context *, + int16_t **event_array, + int16_t **cpu_array, + int32_t **pid_array, + int64_t **offset_array, + int64_t **ts_array); + +/** Data interface identifier. */ +typedef enum kshark_data_interface_id { + /** An interface with unknown type. */ + KS_INVALID_INTERFACE, + + /** Generic interface suitable for Ftrace data. */ + KS_GENERIC_DATA_INTERFACE, +} kshark_data_interface_id; + +/** + * Structure representing the interface of methods used to operate over + * the data from a given stream. + */ +struct kshark_generic_stream_interface { + /** Interface version identifier. */ + kshark_data_interface_id type; /* MUST BE FIRST ENTRY. */ + + /** Method used to retrieve the Process Id of the entry. */ + stream_get_int_func get_pid; + + /** Method used to retrieve the Event Id of the entry. */ + stream_get_int_func get_event_id; + + /** Method used to retrieve the Event name of the entry. */ + stream_get_str_func get_event_name; + + /** Method used to retrieve the Task name of the entry. */ + stream_get_str_func get_task; + + /** Method used to retrieve the Info string of the entry. */ + stream_get_str_func get_info; + + /** + * Method used to retrieve an unspecified auxiliary info of the trace + * record. + */ + stream_get_str_func aux_info; + + /** Method used to retrieve Id of the Event from its name. */ + stream_find_id_func find_event_id; + + /** Method used to retrieve the array of Ids of all Events. */ + stream_get_ids_func get_all_event_ids; + + /** Method used to dump the entry's content to string. */ + stream_get_str_func dump_entry; + + /** + * Method used to retrieve the array of all field names of a given + * event. + */ + stream_get_names_func get_all_event_field_names; + + /** Method used to access the type of an event's data field. */ + stream_event_field_type get_event_field_type; + + /** Method used to access the value of an event's data field. */ + stream_read_event_field read_event_field_int64; + + /** Method used to access the value of an event's data field. */ + stream_read_record_field read_record_field_int64; + + /** Method used to load the data in the form of entries. */ + load_entries_func load_entries; + + /** Method used to load the data in matrix form. */ + load_matrix_func load_matrix; + + /** Generic data handle. */ + void *handle; +}; + +/** The limit in size of the data format identifier string. */ +#define KS_DATA_FORMAT_SIZE 15 + +/** Structure representing a stream of trace data. */ +struct kshark_data_stream { + /** Data stream identifier. */ + uint16_t stream_id; + + /** The number of CPUs presented in this data stream. */ + int n_cpus; + + /** + * The number of distinct event types presented in this data stream. + */ + int n_events; + + /** The Process Id of the Idle task. */ + int idle_pid; + + /** Trace data file pathname. */ + char *file; + + /** Stream name. */ + char *name; + + /** Hash table of task PIDs. */ + struct kshark_hash_id *tasks; + + /** A mutex, used to protect the access to the input file. */ + pthread_mutex_t input_mutex; + + /** Hash of tasks to filter on. */ + struct kshark_hash_id *show_task_filter; + + /** Hash of tasks to not display. */ + struct kshark_hash_id *hide_task_filter; + + /** Hash of events to filter on. */ + struct kshark_hash_id *show_event_filter; + + /** Hash of events to not display. */ + struct kshark_hash_id *hide_event_filter; + + /** Hash of CPUs to filter on. */ + struct kshark_hash_id *show_cpu_filter; + + /** Hash of CPUs to not display. */ + struct kshark_hash_id *hide_cpu_filter; + + /** The type of the data. */ + char data_format[KS_DATA_FORMAT_SIZE]; + + /** + * The interface of methods used to operate over the data from a given + * stream. + */ + void *interface; +}; + /** Size of the task's hash table. */ #define KS_TASK_HASH_SHIFT 16 #define KS_TASK_HASH_SIZE (1 << KS_TASK_HASH_SHIFT) From patchwork Mon Jan 4 17:46:53 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11997155 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id DC6E7C43381 for ; Mon, 4 Jan 2021 17:49:03 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id ADCF420700 for ; Mon, 4 Jan 2021 17:49:03 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726363AbhADRtD (ORCPT ); Mon, 4 Jan 2021 12:49:03 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34382 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726345AbhADRtD (ORCPT ); Mon, 4 Jan 2021 12:49:03 -0500 Received: from mail-ej1-x62b.google.com (mail-ej1-x62b.google.com [IPv6:2a00:1450:4864:20::62b]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 4FC51C0617A5 for ; Mon, 4 Jan 2021 09:47:50 -0800 (PST) Received: by mail-ej1-x62b.google.com with SMTP id x16so37918859ejj.7 for ; Mon, 04 Jan 2021 09:47:50 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=ULB2jnd1vmjrM+/QI8t6ZBRrmLCk7MNWpicnjlzF32o=; b=FrJRIZNK/ksqfvgQjh7I4VeM4DVK4aFh/lO7XO+9UcU38Zjul2vtNsA500CBgtkqqv ZBt5Kstsl9kZwiz6JvkELse9slvh9wuNaMK6EhttMc61oJCXVlQQ8i+h3U9C3Vq2mgBp NkLbE2XpOLByFsiWnhK9+1OKhliEH+r8vILVmljSZK0U808HWSd7dtZEvSich8Lhtcdn Yg88EnS2UrwnWLcFBkLlkCpS5DIvMxxjTjZUXqMwCeO4ZM0M+CAgLJlPxu9DeWzg8RRO fSOBpNc72UAH6Jm36si6/h2+PPOWWQQLxNR/TAJp4+p66etBK389JKaoIELcTaM0XdP5 uqhQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=ULB2jnd1vmjrM+/QI8t6ZBRrmLCk7MNWpicnjlzF32o=; b=KQnZH6PJNp+FRcMIyNeGTAriOIVuoUtRE2zBymLukU9a8vWmkL4ulnwsvWSJ1PRdys 0ZYV0uXdmZ/hjRaeCa0FgBImFC3uK0zCerCUHIthqiQVDGegjRnBUg9wAA3t7sJzwv9s ah/5CFcKCfywuMiYTWHuuh0HnIToerOM1g3BtGjANFeCFNP/EmukYyXcwFkzjkxS8ZAg lIIX+N+JA0eExRJskUTRenB1ZfvWXGDw6jVzUlCBpXj8J/4PB5e5XQRboRpSLjucaOTA ssMBLHcHLWnGdujoF29wF/DbAjuAzx0aaC8yAr2noGlQOViVP9hFWna7r6asJgZUWwNX VHOQ== X-Gm-Message-State: AOAM533HNJTu9VJZ4lJUlQOgfCsBPYk361omtBnGO0QYF4eL+ErEhrzJ Xe5dY21NUUC9S2srH69r2b0= X-Google-Smtp-Source: ABdhPJynOrAhwH0m4qxsMx5I7OHFBAdD/Nt9992QLE7lrrcqbhxaMZhgApZ5hn0veVz9eo9To8gu0w== X-Received: by 2002:a17:906:9588:: with SMTP id r8mr66199013ejx.148.1609782469078; Mon, 04 Jan 2021 09:47:49 -0800 (PST) Received: from localhost.localdomain ([95.87.199.238]) by smtp.gmail.com with ESMTPSA id l14sm44107750edq.35.2021.01.04.09.47.48 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 04 Jan 2021 09:47:48 -0800 (PST) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v8 13/44] kernel-shark: Rename static methods in libkshark Date: Mon, 4 Jan 2021 19:46:53 +0200 Message-Id: <20210104174724.70404-14-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210104174724.70404-1-y.karadz@gmail.com> References: <20210104174724.70404-1-y.karadz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org "kshark" prefix is needed only for the public methods of the API. We are changing the names of those static methods because we need to use those names when integrating the data streams into the API.i Signed-off-by: Yordan Karadzhov (VMware) --- src/libkshark.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libkshark.c b/src/libkshark.c index a540da2..654a262 100644 --- a/src/libkshark.c +++ b/src/libkshark.c @@ -1097,8 +1097,8 @@ size_t kshark_load_data_matrix(struct kshark_context *kshark_ctx, return -ENOMEM; } -static const char *kshark_get_latency(struct tep_handle *pe, - struct tep_record *record) +static const char *get_latency(struct tep_handle *pe, + struct tep_record *record) { if (!record) return NULL; @@ -1108,7 +1108,7 @@ static const char *kshark_get_latency(struct tep_handle *pe, return seq.buffer; } -static const char *kshark_get_info(struct tep_handle *pe, +static const char *get_info(struct tep_handle *pe, struct tep_record *record, struct tep_event *event) { @@ -1233,7 +1233,7 @@ const char *kshark_get_latency_easy(struct kshark_entry *entry) pthread_mutex_lock(&kshark_ctx->input_mutex); data = tracecmd_read_at(kshark_ctx->handle, entry->offset, NULL); - lat = kshark_get_latency(kshark_ctx->pevent, data); + lat = get_latency(kshark_ctx->pevent, data); tracecmd_free_record(data); pthread_mutex_unlock(&kshark_ctx->input_mutex); @@ -1376,7 +1376,7 @@ const char *kshark_get_info_easy(struct kshark_entry *entry) event_id = tep_data_type(kshark_ctx->pevent, data); event = tep_find_event(kshark_ctx->pevent, event_id); if (event) - info = kshark_get_info(kshark_ctx->pevent, data, event); + info = get_info(kshark_ctx->pevent, data, event); tracecmd_free_record(data); @@ -1471,7 +1471,7 @@ char* kshark_dump_entry(const struct kshark_entry *entry) event = tep_find_event(kshark_ctx->pevent, entry->event_id); event_name = event? event->name : "[UNKNOWN EVENT]"; - lat = kshark_get_latency(kshark_ctx->pevent, data); + lat = get_latency(kshark_ctx->pevent, data); size = asprintf(&temp_str, "%" PRIu64 "; %s-%i; CPU %i; %s;", entry->ts, @@ -1480,7 +1480,7 @@ char* kshark_dump_entry(const struct kshark_entry *entry) entry->cpu, lat); - info = kshark_get_info(kshark_ctx->pevent, data, event); + info = get_info(kshark_ctx->pevent, data, event); if (size > 0) { size = asprintf(&entry_str, "%s %s; %s; 0x%x", From patchwork Mon Jan 4 17:46:54 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11997161 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 13762C4332D for ; Mon, 4 Jan 2021 17:49:04 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id CC82420770 for ; Mon, 4 Jan 2021 17:49:03 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726345AbhADRtD (ORCPT ); Mon, 4 Jan 2021 12:49:03 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34384 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726246AbhADRtD (ORCPT ); Mon, 4 Jan 2021 12:49:03 -0500 Received: from mail-ed1-x52c.google.com (mail-ed1-x52c.google.com [IPv6:2a00:1450:4864:20::52c]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7B4EDC0617A6 for ; Mon, 4 Jan 2021 09:47:51 -0800 (PST) Received: by mail-ed1-x52c.google.com with SMTP id cm17so28243207edb.4 for ; Mon, 04 Jan 2021 09:47:51 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=lMNNOUb/ouqjbzY7yArxcC48LxEZEHOqLQq81787ceM=; b=NjX1pNyyD3luv/2zsa+fwbYRc0hrfogiIEojgV+hc9jsyUc2+EK6MXSb8uS2/NHtyk 8D0OA7F0E3mdgNB4C7SXM8PgTW7Ibj5qjWOPjOmgu0+YsH9HZ8KKBjeImAD4drC9VyoA Q0elXkzYFUlwnPHdAlj7iP+K2FAsuUBD9Xcdw+o2nFgRRkhTXTmZSwZGVtqbql2vzRCW 0LVD65MZ/DdNCAJ1FKh/KeLCzIP8I7VTwqYL9AdTro2SIdXuVwckqTmizFL/IfzsCnFF gUIeBV+dskH0MAnztcdAx0g/zMKFMTnLGKVJ3nbYsh2dwPEpsoLeb5nyLM/S2eMd4L8o eGtQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=lMNNOUb/ouqjbzY7yArxcC48LxEZEHOqLQq81787ceM=; b=TOTUuFeEbE3jJ4n2zFusLeXr14Q6AlBY1WNfC2mZZ+nn5Y42x3DPMYt1Dh+HrEiaHF G6py33oSHMu3LlxsoFFCnNYjDSTGAg8qCg5a0RTDDKFUHuCCprPFTu8/YgjEyjvbdXj3 NBN8d6KzyDPI55oOlaJO/nfSe8EzsZUTErRRtOsHjW2S4QeSO2F0jQjcCfrx52zeZDJ3 AZv115EqKurfNjQd2RmQAluq9/ddoTXaVNHqAWP3pkbHH3TI7CwcgFVFzCegLkJC0S73 0X4e7w0b9dkuCcfcMc3q4Z9h4wCCbHwjKY3JcDzOSiOU7vhU15nE8MkMfhdEGoaFKoie WXPA== X-Gm-Message-State: AOAM530pTFcIvpJIVT1IvHV+j4V7sormBZqSIf2YfslI1vkfyvRxzqrt lhJgndP+zqL+V20wL5bHb9U= X-Google-Smtp-Source: ABdhPJxjFQc90hTtJmzmh/TcDCu2ANAQa7+aC9qc+FF7ZSgO7MlLksp5OarKCCRFjosqTIkZf87+zA== X-Received: by 2002:aa7:c749:: with SMTP id c9mr72891876eds.3.1609782470030; Mon, 04 Jan 2021 09:47:50 -0800 (PST) Received: from localhost.localdomain ([95.87.199.238]) by smtp.gmail.com with ESMTPSA id l14sm44107750edq.35.2021.01.04.09.47.49 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 04 Jan 2021 09:47:49 -0800 (PST) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v8 14/44] kernel-shark: Add basic methods for Data streams Date: Mon, 4 Jan 2021 19:46:54 +0200 Message-Id: <20210104174724.70404-15-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210104174724.70404-1-y.karadz@gmail.com> References: <20210104174724.70404-1-y.karadz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org Here we introduce the basic mechanisms for using data streams. For the moment these are just stand alone definitions and the integration with the API is yet to be introduced in the following patches. Signed-off-by: Yordan Karadzhov (VMware) --- src/libkshark.c | 716 ++++++++++++++++++++++++++++++++++++++++++++++++ src/libkshark.h | 104 +++++++ 2 files changed, 820 insertions(+) diff --git a/src/libkshark.c b/src/libkshark.c index 654a262..21fabf7 100644 --- a/src/libkshark.c +++ b/src/libkshark.c @@ -166,6 +166,258 @@ bool kshark_open(struct kshark_context *kshark_ctx, const char *file) return true; } +static void kshark_stream_free(struct kshark_data_stream *stream) +{ + if (!stream) + return; + + kshark_hash_id_free(stream->show_task_filter); + kshark_hash_id_free(stream->hide_task_filter); + + kshark_hash_id_free(stream->show_event_filter); + kshark_hash_id_free(stream->hide_event_filter); + + kshark_hash_id_free(stream->show_cpu_filter); + kshark_hash_id_free(stream->hide_cpu_filter); + + kshark_hash_id_free(stream->tasks); + + free(stream->file); + free(stream->name); + free(stream->interface); + free(stream); +} + +static struct kshark_data_stream *kshark_stream_alloc() +{ + struct kshark_data_stream *stream; + + stream = calloc(1, sizeof(*stream)); + if (!stream) + goto fail; + + stream->show_task_filter = kshark_hash_id_alloc(KS_FILTER_HASH_NBITS); + stream->hide_task_filter = kshark_hash_id_alloc(KS_FILTER_HASH_NBITS); + + stream->show_event_filter = kshark_hash_id_alloc(KS_FILTER_HASH_NBITS); + stream->hide_event_filter = kshark_hash_id_alloc(KS_FILTER_HASH_NBITS); + + stream->show_cpu_filter = kshark_hash_id_alloc(KS_FILTER_HASH_NBITS); + stream->hide_cpu_filter = kshark_hash_id_alloc(KS_FILTER_HASH_NBITS); + + stream->tasks = kshark_hash_id_alloc(KS_TASK_HASH_NBITS); + + if (!stream->show_task_filter || + !stream->hide_task_filter || + !stream->show_event_filter || + !stream->hide_event_filter || + !stream->tasks) { + goto fail; + } + + kshark_set_data_format(stream->data_format, KS_INVALID_DATA); + stream->name = strdup(KS_UNNAMED); + + return stream; + + fail: + kshark_stream_free(stream); + + return NULL; +} +/** + * The maximum number of Data streams that can be added simultaneously. + * The limit is determined by the 16 bit integer used to store the stream Id + * inside struct kshark_entry. + */ +#define KS_MAX_STREAM_ID INT16_MAX + +/** + * Bit mask (0 - 15) used when converting indexes to pointers and vise-versa. + */ +#define INDEX_MASK UINT16_MAX + +/** + * Bit mask (16 - 31/63) used when converting indexes to pointers and + * vise-versa. + */ +#define INVALID_STREAM_MASK (~((unsigned long) INDEX_MASK)) + +static int index_from_ptr(void *ptr) +{ + unsigned long index = (unsigned long) ptr; + + return (int) (index & INDEX_MASK); +} + +static void *index_to_ptr(unsigned int index) +{ + unsigned long p; + + p = INVALID_STREAM_MASK | index; + + return (void *) p; +} + +static bool kshark_is_valid_stream(void *ptr) +{ + unsigned long p = (unsigned long) ptr; + bool v = !((p & ~INDEX_MASK) == INVALID_STREAM_MASK); + + return p && v; +} + +/** + * @brief Add new Data stream. + * + * @param kshark_ctx: Input location for context pointer. + * + * @returns Zero on success or a negative errno code on failure. + */ +int kshark_add_stream(struct kshark_context *kshark_ctx) +{ + struct kshark_data_stream *stream; + int new_stream; + + if(kshark_ctx->stream_info.next_free_stream_id > KS_MAX_STREAM_ID) + return -ENODEV; + + if (kshark_ctx->stream_info.next_free_stream_id == + kshark_ctx->stream_info.array_size) { + size_t new_size = 2 * kshark_ctx->stream_info.array_size; + struct kshark_data_stream **streams_tmp; + + streams_tmp = realloc(kshark_ctx->stream, + new_size * sizeof(*kshark_ctx->stream)); + if (!streams_tmp) + return -ENOMEM; + + kshark_ctx->stream = streams_tmp; + kshark_ctx->stream_info.array_size = new_size; + } + + stream = kshark_stream_alloc(); + if (!stream) + return -ENOMEM; + + if (pthread_mutex_init(&stream->input_mutex, NULL) != 0) { + kshark_stream_free(stream); + return -EAGAIN; + } + + if (kshark_ctx->stream_info.next_free_stream_id > + kshark_ctx->stream_info.max_stream_id) { + new_stream = ++kshark_ctx->stream_info.max_stream_id; + + kshark_ctx->stream_info.next_free_stream_id = new_stream + 1; + + kshark_ctx->stream[new_stream] = stream; + stream->stream_id = new_stream; + } else { + new_stream = kshark_ctx->stream_info.next_free_stream_id; + + kshark_ctx->stream_info.next_free_stream_id = + index_from_ptr(kshark_ctx->stream[new_stream]); + + kshark_ctx->stream[new_stream] = stream; + stream->stream_id = new_stream; + } + + kshark_ctx->n_streams++; + + return stream->stream_id; +} + +/** + * @brief Remove Data stream. + * + * @param kshark_ctx: Input location for context pointer. + * @param sd: Data stream identifier. + * + * @returns Zero on success or a negative errno code on failure. + */ +int kshark_remove_stream(struct kshark_context *kshark_ctx, int sd) +{ + struct kshark_data_stream *stream; + + if (sd < 0 || + sd > kshark_ctx->stream_info.max_stream_id || + !kshark_is_valid_stream(kshark_ctx->stream[sd])) + return -EFAULT; + + stream = kshark_ctx->stream[sd]; + + pthread_mutex_destroy(&stream->input_mutex); + + kshark_stream_free(stream); + kshark_ctx->stream[sd] = + index_to_ptr(kshark_ctx->stream_info.next_free_stream_id); + kshark_ctx->stream_info.next_free_stream_id = sd; + kshark_ctx->n_streams--; + + return 0; +} + +/** + * @brief Get the Data stream object having given Id. + * + * @param kshark_ctx: Input location for context pointer. + * @param sd: Data stream identifier. + * + * @returns Pointer to a Data stream object if the sream exists. Otherwise + * NULL. + */ +struct kshark_data_stream * +kshark_get_data_stream(struct kshark_context *kshark_ctx, int sd) +{ + if (sd >= 0 && sd <= kshark_ctx->stream_info.max_stream_id) + if (kshark_ctx->stream[sd] && + kshark_is_valid_stream(kshark_ctx->stream[sd]) && + kshark_ctx->stream[sd]->interface) + return kshark_ctx->stream[sd]; + + return NULL; +} + +/** + * @brief Get the Data stream object corresponding to a given entry + * + * @param entry: Input location for the KernelShark entry. + * + * @returns Pointer to a Data stream object on success. Otherwise NULL. + */ +struct kshark_data_stream * +kshark_get_stream_from_entry(const struct kshark_entry *entry) +{ + struct kshark_context *kshark_ctx = NULL; + + if (!kshark_instance(&kshark_ctx)) + return NULL; + + return kshark_get_data_stream(kshark_ctx, entry->stream_id); +} + +/** + * @brief Get an array containing the Ids of all opened Trace data streams. + * The User is responsible for freeing the array. + * + * @param kshark_ctx: Input location for context pointer. + */ +int *kshark_all_streams(struct kshark_context *kshark_ctx) +{ + int *ids, i, count = 0; + + ids = calloc(kshark_ctx->n_streams, (sizeof(*ids))); + if (!ids) + return NULL; + + for (i = 0; i <= kshark_ctx->stream_info.max_stream_id; ++i) + if (kshark_ctx->stream[i] && + kshark_is_valid_stream(kshark_ctx->stream[i])) + ids[count++] = i; + + return ids; +} /** * @brief Close the trace data file and free the trace data handle. * @@ -252,6 +504,470 @@ void kshark_free(struct kshark_context *kshark_ctx) free(kshark_ctx); } +/** + * @brief Get the name of the command/task from its Process Id. + * + * @param sd: Data stream identifier. + * @param pid: Process Id of the command/task. + */ +char *kshark_comm_from_pid(int sd, int pid) +{ + struct kshark_generic_stream_interface *interface; + struct kshark_context *kshark_ctx = NULL; + struct kshark_data_stream *stream; + struct kshark_entry e; + + if (!kshark_instance(&kshark_ctx)) + return NULL; + + stream = kshark_get_data_stream(kshark_ctx, sd); + if (!stream) + return NULL; + + interface = stream->interface; + if (interface->type == KS_GENERIC_DATA_INTERFACE && + interface->get_task) { + e.visible = KS_PLUGIN_UNTOUCHED_MASK; + e.pid = pid; + + return interface->get_task(stream, &e); + } + + return NULL; +} + +/** + * @brief Get the name of the event from its Id. + * + * @param sd: Data stream identifier. + * @param event_id: The unique Id of the event type. + */ +char *kshark_event_from_id(int sd, int event_id) +{ + struct kshark_generic_stream_interface *interface; + struct kshark_context *kshark_ctx = NULL; + struct kshark_data_stream *stream; + struct kshark_entry e; + + if (!kshark_instance(&kshark_ctx)) + return NULL; + + stream = kshark_get_data_stream(kshark_ctx, sd); + if (!stream) + return NULL; + + interface = stream->interface; + if (interface->type == KS_GENERIC_DATA_INTERFACE && + interface->get_event_name) { + e.visible = KS_PLUGIN_UNTOUCHED_MASK; + e.event_id = event_id; + + return interface->get_event_name(stream, &e); + } + + return NULL; +} + +/** + * @brief Get the original process Id of the entry. Using this function make + * sense only in cases when the original value can be overwritten by + * plugins. If you know that no plugins are loaded use "entry->pid" + * directly. + * + * @param entry: Input location for an entry. + */ +int kshark_get_pid(const struct kshark_entry *entry) +{ + struct kshark_generic_stream_interface *interface; + struct kshark_data_stream *stream = + kshark_get_stream_from_entry(entry); + + if (!stream) + return -EFAULT; + + interface = stream->interface; + if (interface->type == KS_GENERIC_DATA_INTERFACE && + interface->get_pid) + return interface->get_pid(stream, entry); + + return -EFAULT; +} + +/** + * @brief Get the original event Id of the entry. Using this function make + * sense only in cases when the original value can be overwritten by + * plugins. If you know that no plugins are loaded use "entry->event_id" + * directly. + * + * @param entry: Input location for an entry. + */ +int kshark_get_event_id(const struct kshark_entry *entry) +{ + struct kshark_generic_stream_interface *interface; + struct kshark_data_stream *stream = + kshark_get_stream_from_entry(entry); + + if (!stream) + return -EFAULT; + + interface = stream->interface; + if (interface->type == KS_GENERIC_DATA_INTERFACE && + interface->get_event_id) + return interface->get_event_id(stream, entry); + + return -EFAULT; +} + +/** + * @brief Get an array of all event Ids for a given data stream. + * + * @param stream: Input location for a Trace data stream pointer. + * + * @returns An array of event Ids. The user is responsible for freeing the + * outputted array. + */ +int *kshark_get_all_event_ids(struct kshark_data_stream *stream) +{ + struct kshark_generic_stream_interface *interface; + + if (!stream) + return NULL; + + interface = stream->interface; + if (interface->type == KS_GENERIC_DATA_INTERFACE && + interface->get_all_event_ids) + return interface->get_all_event_ids(stream); + + return NULL; +} + +/** + * @brief Find the event Ids corresponding to a given event name. + * + * @param stream: Input location for a Trace data stream pointer. + * @param event_name: The name of the event. + * + * @returns Event Ids number. + */ +int kshark_find_event_id(struct kshark_data_stream *stream, + const char *event_name) +{ + struct kshark_generic_stream_interface *interface; + + if (!stream) + return -EFAULT; + + interface = stream->interface; + if (interface->type == KS_GENERIC_DATA_INTERFACE && + interface->find_event_id) + return interface->find_event_id(stream, event_name); + + return -EFAULT; +} + +/** + * @brief Find the event name corresponding to a given entry. + * + * @param entry: Input location for an entry. + * + * @returns The mane of the event on success, or NULL in case of failure. + * The use is responsible for freeing the output string. + */ +char *kshark_get_event_name(const struct kshark_entry *entry) +{ + struct kshark_generic_stream_interface *interface; + struct kshark_data_stream *stream = + kshark_get_stream_from_entry(entry); + + if (!stream) + return NULL; + + interface = stream->interface; + if (interface->type == KS_GENERIC_DATA_INTERFACE && + interface->get_event_name) + return interface->get_event_name(stream, entry); + + return NULL; +} + +/** + * @brief Find the task name corresponding to a given entry. + * + * @param entry: Input location for an entry. + * + * @returns The mane of the task on success, or NULL in case of failure. + * The use is responsible for freeing the output string. + */ +char *kshark_get_task(const struct kshark_entry *entry) +{ + struct kshark_generic_stream_interface *interface; + struct kshark_data_stream *stream = + kshark_get_stream_from_entry(entry); + + if (!stream) + return NULL; + + interface = stream->interface; + if (interface->type == KS_GENERIC_DATA_INTERFACE && + interface->get_task) + return interface->get_task(stream, entry); + + return NULL; +} + +/** + * @brief Get the basic information (text) about the entry. + * + * @param entry: Input location for an entry. + * + * @returns A the info text. The user is responsible for freeing the + * outputted string. + */ +char *kshark_get_info(const struct kshark_entry *entry) +{ + struct kshark_generic_stream_interface *interface; + struct kshark_data_stream *stream = + kshark_get_stream_from_entry(entry); + + if (!stream) + return NULL; + + interface = stream->interface; + if (interface->type == KS_GENERIC_DATA_INTERFACE && + interface->get_info) + return interface->get_info(stream, entry); + + return NULL; +} + +/** + * @brief Get the auxiliary information about the entry. In the case of + * TEP (Ftrace) data, this function provides the latency info. + * + * @param entry: Input location for an entry. + * + * @returns A the auxiliary text info. The user is responsible for freeing the + * outputted string. + */ +char *kshark_get_aux_info(const struct kshark_entry *entry) +{ + struct kshark_generic_stream_interface *interface; + struct kshark_data_stream *stream = + kshark_get_stream_from_entry(entry); + + if (!stream) + return NULL; + + interface = stream->interface; + if (interface->type == KS_GENERIC_DATA_INTERFACE && + interface->aux_info) + return interface->aux_info(stream, entry); + + return NULL; +} + +/** + * @brief Get an array of all data field names associated with a given entry. + * + * @param entry: Input location for an entry. + * @param fields: Output location of the array of field names. The user is + * responsible for freeing the elements of the outputted array. + * + * @returns Total number of event fields on success, or a negative errno in + * the case of a failure. + */ +int kshark_get_all_event_field_names(const struct kshark_entry *entry, + char ***fields) +{ + struct kshark_generic_stream_interface *interface; + struct kshark_data_stream *stream = + kshark_get_stream_from_entry(entry); + + if (!stream) + return -EFAULT; + + interface = stream->interface; + if (interface->type == KS_GENERIC_DATA_INTERFACE && + interface->get_all_event_field_names) + return interface->get_all_event_field_names(stream, + entry, fields); + + return -EFAULT; +} + +/** + * @brief Get the value type of an event field corresponding to a given entry. + * + * @param entry: Input location for an entry. + * @param field: The name of the data field. + * + * @returns The type of the data field on success, or KS_INVALID_FIELD in + * the case of a failure. + */ +kshark_event_field_format +kshark_get_event_field_type(const struct kshark_entry *entry, + const char *field) +{ + struct kshark_generic_stream_interface *interface; + struct kshark_data_stream *stream = + kshark_get_stream_from_entry(entry); + + if (!stream) + return KS_INVALID_FIELD; + + interface = stream->interface; + if (interface->type == KS_GENERIC_DATA_INTERFACE && + interface->get_event_field_type) + return interface->get_event_field_type(stream, entry, field); + + return KS_INVALID_FIELD; +} + +/** + * @brief Get the value of an event field in a given trace record. + * + * @param stream: Input location for a Trace data stream pointer. + * @param rec: Input location for a record. + * @param field: The name of the data field. + * @param val: Output location for the value of the field. + * + * @returns Zero on success or a negative errno in the case of a failure. + */ +int kshark_read_record_field_int(struct kshark_data_stream *stream, void *rec, + const char *field, int64_t *val) +{ + struct kshark_generic_stream_interface *interface; + + if (!stream) + return -EFAULT; + + interface = stream->interface; + if (interface->type == KS_GENERIC_DATA_INTERFACE && + interface->read_record_field_int64) + return interface->read_record_field_int64(stream, rec, + field, val); + + return -EFAULT; +} + +/** + * @brief Get the value of an event field corresponding to a given entry. + * The value is retrieved via the offset in the file of the original + * record. + * + * @param entry: Input location for an entry. + * @param field: The name of the data field. + * @param val: Output location for the value of the field. + * + * @returns Zero on success or a negative errno in the case of a failure. + */ +int kshark_read_event_field_int(const struct kshark_entry *entry, + const char* field, int64_t *val) +{ + struct kshark_generic_stream_interface *interface; + struct kshark_data_stream *stream = + kshark_get_stream_from_entry(entry); + + if (!stream) + return -EFAULT; + + interface = stream->interface; + if (interface->type == KS_GENERIC_DATA_INTERFACE && + interface->read_event_field_int64) + return interface->read_event_field_int64(stream, entry, field, val); + + return -EFAULT; +} + +/** @brief Print the entry. */ +void kshark_print_entry(const struct kshark_entry *entry) +{ + char *entry_str = kshark_dump_entry(entry); + + if (!entry_str) + puts("(nil)"); + + puts(entry_str); + free(entry_str); +} + +/** + * @brief Load the content of the trace data file asociated with a given + * Data stream into an array of kshark_entries. + * If one or more filters are set, the "visible" fields of each entry + * is updated according to the criteria provided by the filters. The + * field "filter_mask" of the session's context is used to control the + * level of visibility/invisibility of the filtered entries. + * + * @param kshark_ctx: Input location for context pointer. + * @param sd: Data stream identifier. + * @param data_rows: Output location for the trace data. The user is + * responsible for freeing the elements of the outputted + * array. + * + * @returns The size of the outputted data in the case of success, or a + * negative error code on failure. + */ +ssize_t kshark_load_entries(struct kshark_context *kshark_ctx, int sd, + struct kshark_entry ***data_rows) +{ + struct kshark_generic_stream_interface *interface; + struct kshark_data_stream *stream = + kshark_get_data_stream(kshark_ctx, sd); + + if (!stream) + return -EFAULT; + + interface = stream->interface; + if (interface->type == KS_GENERIC_DATA_INTERFACE && + interface->load_entries) + return interface->load_entries(stream, kshark_ctx, data_rows); + + return -EFAULT; +} + +/** + * @brief Load the content of the trace data file asociated with a given + * Data stream into a data matrix. The user is responsible + * for freeing the outputted data. + * + * @param kshark_ctx: Input location for context pointer. + * @param sd: Data stream identifier. + * @param cpu_array: Output location for the CPU column (array) of the matrix. + * @param event_array: Output location for the Event Id column (array) of the + * matrix. + * @param pid_array: Output location for the PID column (array) of the matrix. + * @param offset_array: Output location for the offset column (array) of the + * matrix. + * @param ts_array: Output location for the time stamp column (array) of the + * matrix. + */ +ssize_t kshark_load_matrix(struct kshark_context *kshark_ctx, int sd, + int16_t **event_array, + int16_t **cpu_array, + int32_t **pid_array, + int64_t **offset_array, + int64_t **ts_array) +{ + struct kshark_generic_stream_interface *interface; + struct kshark_data_stream *stream = + kshark_get_data_stream(kshark_ctx, sd); + + if (!stream) + return -EFAULT; + + interface = stream->interface; + if (interface->type == KS_GENERIC_DATA_INTERFACE && + interface->load_matrix) + return interface->load_matrix(stream, kshark_ctx, event_array, + cpu_array, + pid_array, + offset_array, + ts_array); + + return -EFAULT; +} + static struct kshark_task_list * kshark_find_task(struct kshark_context *kshark_ctx, uint32_t key, int pid) { diff --git a/src/libkshark.h b/src/libkshark.h index f2e29ac..00c1163 100644 --- a/src/libkshark.h +++ b/src/libkshark.h @@ -121,6 +121,19 @@ void kshark_hash_id_free(struct kshark_hash_id *hash); int *kshark_hash_ids(struct kshark_hash_id *hash); +/* Quiet warnings over documenting simple structures */ +//! @cond Doxygen_Suppress + +static const char top_name[] = { 0x1b, 0x00 }; // Non printable character + +//! @endcond + +/** + * Non printable character used for the name in the case when the name has to + * be ignored. + */ +#define KS_UNNAMED (char *) &top_name + struct kshark_data_stream; /** A function type to be used by the method interface of the data stream. */ @@ -264,6 +277,9 @@ struct kshark_generic_stream_interface { /** The limit in size of the data format identifier string. */ #define KS_DATA_FORMAT_SIZE 15 +/** Data format identifier string indicating invalid data. */ +#define KS_INVALID_DATA "invalid data" + /** Structure representing a stream of trace data. */ struct kshark_data_stream { /** Data stream identifier. */ @@ -320,6 +336,12 @@ struct kshark_data_stream { void *interface; }; +static inline char *kshark_set_data_format(char *dest_format, + const char *src_format) +{ + return strncpy(dest_format, src_format, KS_DATA_FORMAT_SIZE - 1); +} + /** Size of the task's hash table. */ #define KS_TASK_HASH_SHIFT 16 #define KS_TASK_HASH_SIZE (1 << KS_TASK_HASH_SHIFT) @@ -333,8 +355,32 @@ struct kshark_task_list { int pid; }; +/** + * Structure representing the parameters of the stream descriptor array owned + * by the kshark session. + */ +struct kshark_stream_array_descriptor { + /** The identifier of the Data stream added. */ + int max_stream_id; + + /** The the next free Data stream identifier (index). */ + int next_free_stream_id; + + /** The capacity of the array of stream objects (pointers). */ + int array_size; +}; + /** Structure representing a kshark session. */ struct kshark_context { + /** Array of data stream descriptors. */ + struct kshark_data_stream **stream; + + /** The number of data streams. */ + int n_streams; + + /** Parameters of the stream descriptor array. */ + struct kshark_stream_array_descriptor stream_info; + /** Input handle for the trace data file. */ struct tracecmd_input *handle; @@ -392,6 +438,18 @@ bool kshark_instance(struct kshark_context **kshark_ctx); bool kshark_open(struct kshark_context *kshark_ctx, const char *file); +int kshark_add_stream(struct kshark_context *kshark_ctx); + +int kshark_remove_stream(struct kshark_context *kshark_ctx, int sd); + +struct kshark_data_stream * +kshark_get_data_stream(struct kshark_context *kshark_ctx, int sd); + +struct kshark_data_stream * +kshark_get_stream_from_entry(const struct kshark_entry *entry); + +int *kshark_all_streams(struct kshark_context *kshark_ctx); + ssize_t kshark_load_data_entries(struct kshark_context *kshark_ctx, struct kshark_entry ***data_rows); @@ -411,6 +469,10 @@ void kshark_close(struct kshark_context *kshark_ctx); void kshark_free(struct kshark_context *kshark_ctx); +char *kshark_comm_from_pid(int sd, int pid); + +char *kshark_event_from_id(int sd, int event_id); + int kshark_get_pid_easy(struct kshark_entry *entry); const char *kshark_get_task_easy(struct kshark_entry *entry); @@ -427,6 +489,48 @@ void kshark_convert_nano(uint64_t time, uint64_t *sec, uint64_t *usec); char* kshark_dump_entry(const struct kshark_entry *entry); +void kshark_print_entry(const struct kshark_entry *entry); + +int kshark_get_pid(const struct kshark_entry *entry); + +int kshark_get_event_id(const struct kshark_entry *entry); + +int *kshark_get_all_event_ids(struct kshark_data_stream *stream); + +int kshark_find_event_id(struct kshark_data_stream *stream, + const char *event_name); + +char *kshark_get_event_name(const struct kshark_entry *entry); + +char *kshark_get_task(const struct kshark_entry *entry); + +char *kshark_get_info(const struct kshark_entry *entry); + +char *kshark_get_aux_info(const struct kshark_entry *entry); + +kshark_event_field_format +kshark_get_event_field_type(const struct kshark_entry *entry, + const char *field); + +int kshark_get_all_event_field_names(const struct kshark_entry *entry, + char ***field); + +int kshark_read_record_field_int(struct kshark_data_stream *stream, void *rec, + const char *field, int64_t *val); + +int kshark_read_event_field_int(const struct kshark_entry *entry, + const char* field, int64_t *val); + +ssize_t kshark_load_entries(struct kshark_context *kshark_ctx, int sd, + struct kshark_entry ***data_rows); + +ssize_t kshark_load_matrix(struct kshark_context *kshark_ctx, int sd, + int16_t **event_array, + int16_t **cpu_array, + int32_t **pid_array, + int64_t **offset_array, + int64_t **ts_array); + /** * Custom entry info function type. To be user for dumping info for custom * KernelShark entryes. From patchwork Mon Jan 4 17:46:55 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11997163 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id ECD6BC433DB for ; Mon, 4 Jan 2021 17:49:13 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id C02E620715 for ; Mon, 4 Jan 2021 17:49:13 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726396AbhADRtN (ORCPT ); Mon, 4 Jan 2021 12:49:13 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34356 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726124AbhADRtN (ORCPT ); Mon, 4 Jan 2021 12:49:13 -0500 Received: from mail-ej1-x635.google.com (mail-ej1-x635.google.com [IPv6:2a00:1450:4864:20::635]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 2E10CC0617A7 for ; Mon, 4 Jan 2021 09:47:52 -0800 (PST) Received: by mail-ej1-x635.google.com with SMTP id jx16so37858566ejb.10 for ; Mon, 04 Jan 2021 09:47:52 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=WSebb87Uo8I2zgaV5YxVw5E5SsOxC0qmbyAHqjbq5P0=; b=K86cxsxAeKPrnEVvLIfPZGQllxiZ7AcZu/+MbiS7utNdWz1WbM5nrlpknB0K4awtUr EU5jLebT9ubROqOZH1Yd0yeid6WPs2scpE2V7DoqusN2wnsDt/duqPr3LAXdXBuvDerc iHNhF/lWUWvOGdGSPAhCIrhV1Kw/nl3mbp9JEe/jj4nBrgMXiKbj3lclz7J9zPSY2cpr fuRyIUAHmrrCFvTrATbr1wpGQ+PAeHl1/S3rEeoJGihpdEMKYmMZbeaTmyS9QTVMEI7I UW2uy1D3QCAdQ7PEZ1+wFznW5NCkqucTFjQjdt5OxE7X5h2hTP9wjRDeDw+BdN38WjpR KZoA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=WSebb87Uo8I2zgaV5YxVw5E5SsOxC0qmbyAHqjbq5P0=; b=t46nVk1sw/E1sdm25onvtchPw3WkrK7KkX02HzBWP4Kv/roJ2ykarK1RcZARZj0y/P DfDnxShg7B8Er6FAZrCl0jLRneYgiHC9JZJpfWuAWemKt+kUw2x7pRnDF59C8Je9/6/3 MPbZ2SC9Dk3P5Kr92Q4wlDeIGaPEN6lTy6iVJMS311uWXrgwZxhWEJWkHFnX1KVJMXA6 hmkgwt/JnxAp3s6asX1iY8VkjV5Mzc5JYwUy5csfovZvuW+tyc7eUohWaVi6651zHrRD JaQRUSd1woRo2hfYzT4cN8jl59MbA/tBv5oDIcJpNIqZPO/5hEyEu4ZSduUV6ICDCE2t 8MkA== X-Gm-Message-State: AOAM533HMZI3XbUVtfCFZhjoRgVTbpBP7+UdW7Ob70Vwk3pMu2kDRw6N I181EM+fmCTXnDb3ABLzDko= X-Google-Smtp-Source: ABdhPJxnYWkvL+1Fzcjg+67gnjr5McAppjiw9fcDbRKvJh8xoGl22/XQegDKyDex1qlfDr6nxKi45A== X-Received: by 2002:a17:906:c408:: with SMTP id u8mr66716993ejz.364.1609782470933; Mon, 04 Jan 2021 09:47:50 -0800 (PST) Received: from localhost.localdomain ([95.87.199.238]) by smtp.gmail.com with ESMTPSA id l14sm44107750edq.35.2021.01.04.09.47.50 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 04 Jan 2021 09:47:50 -0800 (PST) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v8 15/44] kernel-shark: Housekeeping before implementing stream interface Date: Mon, 4 Jan 2021 19:46:55 +0200 Message-Id: <20210104174724.70404-16-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210104174724.70404-1-y.karadz@gmail.com> References: <20210104174724.70404-1-y.karadz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org kshark_load data_matrix() is a method that is used only in trace-cruncher prototype. The version of this function that is currently part of the API is anyway not compatible with the latest version of trace-cruncher because of the mismatch of the argument types, so we can directly remove the existing implementation. An equivalent functionality will be provided as part of the implantation of the Data stream interface in the following patch. Here we just make kshark_data_matrix_alloc() and unset_event_filter_flag() public, because the methods will be used in the stream interface implementation. Signed-off-by: Yordan Karadzhov (VMware) --- src/libkshark.c | 121 ++++++++---------------------------------------- src/libkshark.h | 28 ++++++++--- 2 files changed, 40 insertions(+), 109 deletions(-) diff --git a/src/libkshark.c b/src/libkshark.c index 21fabf7..14484eb 100644 --- a/src/libkshark.c +++ b/src/libkshark.c @@ -1190,21 +1190,6 @@ bool kshark_filter_is_set(struct kshark_context *kshark_ctx) - kshark_this_filter_is_set(kshark_ctx->hide_event_filter); } -static inline void unset_event_filter_flag(struct kshark_context *kshark_ctx, - struct kshark_entry *e) -{ - /* - * All entries, filtered-out by the event filters, will be treated - * differently, when visualized. Because of this, ignore the value - * of the GRAPH_VIEW flag provided by the user via - * kshark_ctx->filter_mask. The value of the EVENT_VIEW flag in - * kshark_ctx->filter_mask will be used instead. - */ - int event_mask = kshark_ctx->filter_mask & ~KS_GRAPH_VIEW_FILTER_MASK; - - e->visible &= ~event_mask; -} - static void set_all_visible(uint16_t *v) { /* Keep the original value of the PLUGIN_UNTOUCHED bit flag. */ *v |= 0xFF & ~KS_PLUGIN_UNTOUCHED_MASK; @@ -1680,11 +1665,25 @@ static inline void free_ptr(void *ptr) free(*(void **)ptr); } -static bool data_matrix_alloc(size_t n_rows, uint64_t **offset_array, - uint16_t **cpu_array, - uint64_t **ts_array, - uint16_t **pid_array, - int **event_array) +/** + * @brief Allocate data arrays (matrix columns) to be used to load the tracing + * data into a data matrix form. + * + * @param n_rows: Number matrix rows to be allocated. Must be equal to the + * number of trace records. + * @param cpu_array: Output location for the CPU Id column. + * @param pid_array: Output location for the PID column. + * @param event_array: Output location for the Event Id column. + * @param offset_array: Output location for the record offset column. + * @param ts_array: Output location for the timestamp column. + * + * @returns True on success. Else false. + */ +bool kshark_data_matrix_alloc(size_t n_rows, int16_t **event_array, + int16_t **cpu_array, + int32_t **pid_array, + int64_t **offset_array, + int64_t **ts_array) { if (offset_array) { *offset_array = calloc(n_rows, sizeof(**offset_array)); @@ -1731,88 +1730,6 @@ static bool data_matrix_alloc(size_t n_rows, uint64_t **offset_array, return false; } -/** - * @brief Load the content of the trace data file into a table / matrix made - * of columns / arrays of data. The user is responsible for freeing the - * elements of the outputted array - * - * @param kshark_ctx: Input location for the session context pointer. - * @param offset_array: Output location for the array of record offsets. - * @param cpu_array: Output location for the array of CPU Ids. - * @param ts_array: Output location for the array of timestamps. - * @param pid_array: Output location for the array of Process Ids. - * @param event_array: Output location for the array of Event Ids. - * - * @returns The size of the outputted arrays in the case of success, or a - * negative error code on failure. - */ -size_t kshark_load_data_matrix(struct kshark_context *kshark_ctx, - uint64_t **offset_array, - uint16_t **cpu_array, - uint64_t **ts_array, - uint16_t **pid_array, - int **event_array) -{ - enum rec_type type = REC_ENTRY; - struct rec_list **rec_list; - ssize_t count, total = 0; - bool status; - int n_cpus; - - total = get_records(kshark_ctx, &rec_list, type); - if (total < 0) - goto fail; - - n_cpus = tep_get_cpus(kshark_ctx->pevent); - - status = data_matrix_alloc(total, offset_array, - cpu_array, - ts_array, - pid_array, - event_array); - if (!status) - goto fail_free; - - for (count = 0; count < total; count++) { - int next_cpu; - - next_cpu = pick_next_cpu(rec_list, n_cpus, type); - if (next_cpu >= 0) { - struct rec_list *rec = rec_list[next_cpu]; - struct kshark_entry *e = &rec->entry; - - if (offset_array) - (*offset_array)[count] = e->offset; - - if (cpu_array) - (*cpu_array)[count] = e->cpu; - - if (ts_array) - (*ts_array)[count] = e->ts; - - if (pid_array) - (*pid_array)[count] = e->pid; - - if (event_array) - (*event_array)[count] = e->event_id; - - rec_list[next_cpu] = rec_list[next_cpu]->next; - free(rec); - } - } - - /* There should be no entries left in rec_list. */ - free_rec_list(rec_list, n_cpus, type); - return total; - - fail_free: - free_rec_list(rec_list, n_cpus, type); - - fail: - fprintf(stderr, "Failed to allocate memory during data loading.\n"); - return -ENOMEM; -} - static const char *get_latency(struct tep_handle *pe, struct tep_record *record) { diff --git a/src/libkshark.h b/src/libkshark.h index 00c1163..db3a5a7 100644 --- a/src/libkshark.h +++ b/src/libkshark.h @@ -456,13 +456,6 @@ ssize_t kshark_load_data_entries(struct kshark_context *kshark_ctx, ssize_t kshark_load_data_records(struct kshark_context *kshark_ctx, struct tep_record ***data_rows); -size_t kshark_load_data_matrix(struct kshark_context *kshark_ctx, - uint64_t **offset_array, - uint16_t **cpu_array, - uint64_t **ts_array, - uint16_t **pid_array, - int **event_array); - ssize_t kshark_get_task_pids(struct kshark_context *kshark_ctx, int **pids); void kshark_close(struct kshark_context *kshark_ctx); @@ -617,6 +610,21 @@ bool kshark_this_filter_is_set(struct tracecmd_filter_id *filter); bool kshark_filter_is_set(struct kshark_context *kshark_ctx); +static inline void unset_event_filter_flag(struct kshark_context *kshark_ctx, + struct kshark_entry *e) +{ + /* + * All entries, filtered-out by the event filters, will be treated + * differently, when visualized. Because of this, ignore the value + * of the GRAPH_VIEW flag provided by the user via + * stream->filter_mask. The value of the EVENT_VIEW flag in + * stream->filter_mask will be used instead. + */ + int event_mask = kshark_ctx->filter_mask & ~KS_GRAPH_VIEW_FILTER_MASK; + + e->visible &= ~event_mask; +} + void kshark_filter_entries(struct kshark_context *kshark_ctx, struct kshark_entry **data, size_t n_entries); @@ -995,6 +1003,12 @@ struct kshark_config_doc *kshark_open_config_file(const char *file_name, struct kshark_config_doc *kshark_json_to_conf(struct json_object *jobj); +bool kshark_data_matrix_alloc(size_t n_rows, int16_t **event_array, + int16_t **cpu_array, + int32_t **pid_array, + int64_t **offset_array, + int64_t **ts_array); + #ifdef __cplusplus } #endif From patchwork Mon Jan 4 17:46:56 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11997165 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 3D7E0C433E0 for ; Mon, 4 Jan 2021 17:49:14 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 05C6720715 for ; Mon, 4 Jan 2021 17:49:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726124AbhADRtN (ORCPT ); Mon, 4 Jan 2021 12:49:13 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34358 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726246AbhADRtN (ORCPT ); Mon, 4 Jan 2021 12:49:13 -0500 Received: from mail-ej1-x630.google.com (mail-ej1-x630.google.com [IPv6:2a00:1450:4864:20::630]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D3D8CC0617A9 for ; Mon, 4 Jan 2021 09:47:53 -0800 (PST) Received: by mail-ej1-x630.google.com with SMTP id n26so37884757eju.6 for ; Mon, 04 Jan 2021 09:47:53 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=sNjDM2vh8jptrQcA7+8fkVmqBY/AvB+kse/yZvvALBg=; b=B1HEz0C5avI2L7tprBNgEqGXGCrLRnv2rtP1dyoURV6vTMcJXMQDBAyWkaZjvhzwrB DSQcbzxP2flrZg7p4oa6CgLiYUmrY/7BqTxY+KDHZ9dttdxAM6YFtCO/KOg6Haut190n 6w0K5WLVuSFzM+8+TfCDhLuQGc8FFbXF9anGOgqaNEMm5rYbKtkfDmnMqtJdvl/XZou0 0f6lyN3n1/h9kzsEXYS7oiHs9Q56trJ4+p/qzocK5WDJ+g978efPYXNLbNCZchuXexuA 3xLXGx7CoawcQplhVFEeeN2evPvZlRIgUVjPswHNcJJWG/Bj3UkPLp8YFrQcDhxjqySq HBGA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=sNjDM2vh8jptrQcA7+8fkVmqBY/AvB+kse/yZvvALBg=; b=kTWM4dnNRTWcvaftCnz1+MqPHq3p2sRxNiJo0wxt+FSZ7R0Q5M/6AteME/6nPzaOKy bRkB16hIumR/uh+pMI123mm+xY8Eo6sBdosCu6qUTSamMCiS42mF7eYObj2AN1n7Ybpo HRJJRIbZSof9XXV44c7huftsU5M8JJFBmXsbCJ7R1MIllBhvmIRuJ1rrc0SK4LeOAsoB 9Vnx/Eqno92RncR3hKMy21dy2iUddS8nXZaXM4G0ZqPvcIrlML2WDfjM9dJ+7+41Ju07 RGfXq5B+E3tVzCrUbvyEfoeDAvkSW2czJI/y/WF3nWksW/sKGwcQn4SllXzeAiwqGxlb Fh6Q== X-Gm-Message-State: AOAM530RuMHUokC90BbgGTvXecSsAo8VgQL24wt/ixKz0J8EiGm0HFSm UAd6P3co3mfTDvhSlFVxwDtKRIO+Byx+Bg== X-Google-Smtp-Source: ABdhPJyi/3mQ+40vguKBMAhkt9CPrPk7Qv0o/902Lwej+qDxHhH0e3n2HalQqucgUGCDTLVzpJKceA== X-Received: by 2002:a17:907:b09:: with SMTP id h9mr68739195ejl.155.1609782472221; Mon, 04 Jan 2021 09:47:52 -0800 (PST) Received: from localhost.localdomain ([95.87.199.238]) by smtp.gmail.com with ESMTPSA id l14sm44107750edq.35.2021.01.04.09.47.51 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 04 Jan 2021 09:47:51 -0800 (PST) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v8 16/44] kernel-shark: Add stream interface for trace-cmd data Date: Mon, 4 Jan 2021 19:46:56 +0200 Message-Id: <20210104174724.70404-17-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210104174724.70404-1-y.karadz@gmail.com> References: <20210104174724.70404-1-y.karadz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org Here we provide an implementation of the Data stream interface that will process the trace-cmd (Ftrace) data. This can be considered the most essential change in the transformation of the C API towards version 2. However, for the moment we only have stand alone definitions that are not made functional yet. The actual integration with the API will be introduced in the following patches. Signed-off-by: Yordan Karadzhov (VMware) --- src/CMakeLists.txt | 1 + src/libkshark-tepdata.c | 1512 +++++++++++++++++++++++++++++++++++++++ src/libkshark-tepdata.h | 63 ++ 3 files changed, 1576 insertions(+) create mode 100644 src/libkshark-tepdata.c create mode 100644 src/libkshark-tepdata.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e1a4b5f..ce82d30 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,6 +7,7 @@ add_library(kshark SHARED libkshark.c libkshark-hash.c libkshark-model.c libkshark-plugin.c + libkshark-tepdata.c libkshark-configio.c libkshark-collection.c) diff --git a/src/libkshark-tepdata.c b/src/libkshark-tepdata.c new file mode 100644 index 0000000..7b2ac36 --- /dev/null +++ b/src/libkshark-tepdata.c @@ -0,0 +1,1512 @@ +// SPDX-License-Identifier: LGPL-2.1 + +/* + * Copyright (C) 2019 VMware Inc, Yordan Karadzhov (VMware) + */ + +/** + * @file libkshark-tepdata.c + * @brief Interface for processing of FTRACE (trace-cmd) data. + */ + + +// C +#ifndef _GNU_SOURCE +/** Use GNU C Library. */ +#define _GNU_SOURCE +#endif // _GNU_SOURCE + +#include +#include +#include + +// trace-cmd +#include "trace-cmd/trace-cmd.h" +#include "tracefs/tracefs.h" + +// KernelShark +#include "libkshark.h" +#include "libkshark-plugin.h" +#include "libkshark-tepdata.h" + +static __thread struct trace_seq seq; + +static bool init_thread_seq(void) +{ + if (!seq.buffer) + trace_seq_init(&seq); + + return seq.buffer != NULL; +} + +/** Structure for handling all unique attributes of the FTRACE data. */ +struct tepdata_handle { + /** Page event used to parse the page. */ + struct tep_handle *tep; /* MUST BE FIRST ENTRY */ + + /** Input handle for the trace data file. */ + struct tracecmd_input *input; + + /** + * Filter allowing sophisticated filtering based on the content of + * the event. + */ + struct tep_event_filter *advanced_event_filter; + + /** The unique Id of the sched_switch_event event. */ + int sched_switch_event_id; + + /** Pointer to the sched_switch_next_field format descriptor. */ + struct tep_format_field *sched_switch_next_field; + + /** Pointer to the sched_switch_comm_field format descriptor. */ + struct tep_format_field *sched_switch_comm_field; +}; + +static inline int get_tepdate_handle(struct kshark_data_stream *stream, + struct tepdata_handle **handle) +{ + struct kshark_generic_stream_interface *interface; + + interface = stream->interface; + if (!interface) + return -EFAULT; + + *handle = interface->handle; + + return 0; +} + +/** Get the Page event object used to parse the page. */ +struct tep_handle *kshark_get_tep(struct kshark_data_stream *stream) +{ + struct tepdata_handle *tep_handle; + int ret; + + ret = get_tepdate_handle(stream, &tep_handle); + if (ret < 0) + return NULL; + + return tep_handle->tep; +} + +/** Get the input handle for the trace data file */ +struct tracecmd_input *kshark_get_tep_input(struct kshark_data_stream *stream) +{ + struct tepdata_handle *tep_handle; + int ret; + + ret = get_tepdate_handle(stream, &tep_handle); + if (ret < 0) + return NULL; + + return tep_handle->input; +} + +static inline struct tep_event_filter * +get_adv_filter(struct kshark_data_stream *stream) +{ + struct tepdata_handle *tep_handle; + int ret; + + ret = get_tepdate_handle(stream, &tep_handle); + if (ret < 0) + return NULL; + + return tep_handle->advanced_event_filter; +} + +static int get_sched_switch_id(struct kshark_data_stream *stream) +{ + struct tepdata_handle *tep_handle; + int ret; + + ret = get_tepdate_handle(stream, &tep_handle); + if (ret < 0) + return ret; + + return tep_handle->sched_switch_event_id; +} + +static struct tep_format_field *get_sched_next(struct kshark_data_stream *stream) +{ + struct tepdata_handle *tep_handle; + int ret; + + ret = get_tepdate_handle(stream, &tep_handle); + if (ret < 0) + return NULL; + + return tep_handle->sched_switch_next_field; +} + +static struct tep_format_field *get_sched_comm(struct kshark_data_stream *stream) +{ + struct tepdata_handle *tep_handle; + int ret; + + ret = get_tepdate_handle(stream, &tep_handle); + if (ret < 0) + return NULL; + + return tep_handle->sched_switch_comm_field; +} + +static void set_entry_values(struct kshark_data_stream *stream, + struct tep_record *record, + struct kshark_entry *entry) +{ + struct tep_handle *tep = kshark_get_tep(stream); + + if (!tep) + return; + + /* Offset of the record */ + entry->offset = record->offset; + + /* CPU Id of the record */ + entry->cpu = record->cpu; + + /* Time stamp of the record */ + entry->ts = record->ts; + + /* Event Id of the record */ + entry->event_id = tep_data_type(tep, record); + + /* + * Is visible mask. This default value means that the entry + * is visible everywhere. + */ + entry->visible = 0xFF; + + /* Process Id of the record */ + entry->pid = tep_data_pid(tep, record); +} + +/** Prior time offset of the "missed_events" entry. */ +#define ME_ENTRY_TIME_SHIFT 10 + +static void missed_events_action(struct kshark_data_stream *stream, + struct tep_record *record, + struct kshark_entry *entry) +{ + /* + * Use the offset field of the entry to store the number of missed + * events. + */ + entry->offset = record->missed_events; + + entry->cpu = record->cpu; + + /* + * Position the "missed_events" entry a bit before (in time) + * the original record. + */ + entry->ts = record->ts - ME_ENTRY_TIME_SHIFT; + + /* All custom entries must have negative event Identifiers. */ + entry->event_id = KS_EVENT_OVERFLOW; + + entry->visible = 0xFF; + + entry->pid = tep_data_pid(kshark_get_tep(stream), record); +} + +/** + * rec_list is used to pass the data to the load functions. + * The rec_list will contain the list of entries from the source, + * and will be a link list of per CPU entries. + */ +struct rec_list { + union { + /* Used by kshark_load_data_records */ + struct { + /** next pointer, matches entry->next */ + struct rec_list *next; + /** pointer to the raw record data */ + struct tep_record *rec; + }; + /** entry - Used for kshark_load_data_entries() */ + struct kshark_entry entry; + }; +}; + +static int get_next_pid(struct kshark_data_stream *stream, + struct tep_record *record) +{ + unsigned long long val; + int ret; + + ret = tep_read_number_field(get_sched_next(stream), + record->data, &val); + + return ret ? : val; +} + +static void register_command(struct kshark_data_stream *stream, + struct tep_record *record, + int pid) +{ + struct tep_format_field *comm_field = get_sched_comm(stream); + const char *comm = record->data + comm_field->offset; + /* + * TODO: The retrieve of the name of the command above needs to be + * implemented as a wrapper function in libtracevent. + */ + + if (!tep_is_pid_registered(kshark_get_tep(stream), pid)) + tep_register_comm(kshark_get_tep(stream), comm, pid); +} + +/** + * rec_type defines what type of rec_list is being used. + */ +enum rec_type { + REC_RECORD, + REC_ENTRY, +}; + +static void free_rec_list(struct rec_list **rec_list, int n_cpus, + enum rec_type type) +{ + struct rec_list *temp_rec; + int cpu; + + for (cpu = 0; cpu < n_cpus; ++cpu) { + while (rec_list[cpu]) { + temp_rec = rec_list[cpu]; + rec_list[cpu] = temp_rec->next; + if (type == REC_RECORD) + tracecmd_free_record(temp_rec->rec); + free(temp_rec); + } + } + free(rec_list); +} + +static ssize_t get_records(struct kshark_context *kshark_ctx, + struct kshark_data_stream *stream, + struct rec_list ***rec_list, + enum rec_type type) +{ + struct tep_event_filter *adv_filter = NULL; + struct tracecmd_input *input; + struct rec_list **temp_next; + struct rec_list **cpu_list; + struct rec_list *temp_rec; + struct tep_record *rec; + ssize_t count, total = 0; + int pid, next_pid, cpu; + + input = kshark_get_tep_input(stream); + if (!input) + return -EFAULT; + + cpu_list = calloc(stream->n_cpus, sizeof(*cpu_list)); + if (!cpu_list) + return -ENOMEM; + + if (type == REC_ENTRY) + adv_filter = get_adv_filter(stream); + + for (cpu = 0; cpu < stream->n_cpus; ++cpu) { + count = 0; + cpu_list[cpu] = NULL; + temp_next = &cpu_list[cpu]; + + rec = tracecmd_read_cpu_first(kshark_get_tep_input(stream), cpu); + while (rec) { + *temp_next = temp_rec = calloc(1, sizeof(*temp_rec)); + if (!temp_rec) + goto fail; + + temp_rec->next = NULL; + + switch (type) { + case REC_RECORD: + temp_rec->rec = rec; + pid = tep_data_pid(kshark_get_tep(stream), rec); + break; + case REC_ENTRY: { + struct kshark_entry *entry; + + if (rec->missed_events) { + /* + * Insert a custom "missed_events" entry just + * befor this record. + */ + entry = &temp_rec->entry; + missed_events_action(stream, rec, entry); + + entry->stream_id = stream->stream_id; + + temp_next = &temp_rec->next; + ++count; + + /* Now allocate a new rec_list node and comtinue. */ + *temp_next = temp_rec = calloc(1, sizeof(*temp_rec)); + if (!temp_rec) + goto fail; + } + + entry = &temp_rec->entry; + set_entry_values(stream, rec, entry); + + if (entry->event_id == get_sched_switch_id(stream)) { + next_pid = get_next_pid(stream, rec); + if (next_pid >= 0) + register_command(stream, rec, next_pid); + } + + entry->stream_id = stream->stream_id; + + pid = entry->pid; + + /* Apply advanced event filtering. */ + if (adv_filter && adv_filter->filters && + tep_filter_match(adv_filter, rec) != FILTER_MATCH) + unset_event_filter_flag(kshark_ctx, entry); + + tracecmd_free_record(rec); + break; + } /* REC_ENTRY */ + } + + kshark_hash_id_add(stream->tasks, pid); + + temp_next = &temp_rec->next; + + ++count; + rec = tracecmd_read_data(kshark_get_tep_input(stream), cpu); + } + + total += count; + } + + *rec_list = cpu_list; + return total; + + fail: + free_rec_list(cpu_list, stream->n_cpus, type); + return -ENOMEM; +} + +static int pick_next_cpu(struct rec_list **rec_list, int n_cpus, + enum rec_type type) +{ + uint64_t ts = 0; + uint64_t rec_ts; + int next_cpu = -1; + int cpu; + + for (cpu = 0; cpu < n_cpus; ++cpu) { + if (!rec_list[cpu]) + continue; + + switch (type) { + case REC_RECORD: + rec_ts = rec_list[cpu]->rec->ts; + break; + case REC_ENTRY: + rec_ts = rec_list[cpu]->entry.ts; + break; + default: + return -1; + } + if (!ts || rec_ts < ts) { + ts = rec_ts; + next_cpu = cpu; + } + } + + return next_cpu; +} + +/** + * @brief Load the content of the trace data file asociated with a given + * Data stream into an array of kshark_entries. This function + * provides an abstraction of the entries from the raw data + * that is read, however the "latency" and the "info" fields can be + * accessed only via the offset into the file. This makes the access + * to these two fields much slower. + * If one or more filters are set, the "visible" fields of each entry + * is updated according to the criteria provided by the filters. The + * field "filter_mask" of the session's context is used to control the + * level of visibility/invisibility of the filtered entries. + * + * @param stream: Input location for the FTRACE data stream pointer. + * @param kshark_ctx: Input location for context pointer. + * @param data_rows: Output location for the trace data. The user is + * responsible for freeing the elements of the outputted + * array. + * + * @returns The size of the outputted data in the case of success, or a + * negative error code on failure. + */ +ssize_t tepdata_load_entries(struct kshark_data_stream *stream, + struct kshark_context *kshark_ctx, + struct kshark_entry ***data_rows) +{ + enum rec_type type = REC_ENTRY; + struct kshark_entry **rows; + struct rec_list **rec_list; + ssize_t count, total = 0; + + total = get_records(kshark_ctx, stream, &rec_list, type); + if (total < 0) + goto fail; + + rows = calloc(total, sizeof(struct kshark_entry *)); + if (!rows) + goto fail_free; + + for (count = 0; count < total; count++) { + int next_cpu; + + next_cpu = pick_next_cpu(rec_list, stream->n_cpus, type); + + if (next_cpu >= 0) { + rows[count] = &rec_list[next_cpu]->entry; + rec_list[next_cpu] = rec_list[next_cpu]->next; + } + } + + /* There should be no entries left in rec_list. */ + free_rec_list(rec_list, stream->n_cpus, type); + *data_rows = rows; + + return total; + + fail_free: + free_rec_list(rec_list, stream->n_cpus, type); + + fail: + fprintf(stderr, "Failed to allocate memory during data loading.\n"); + return -ENOMEM; +} + +static ssize_t tepdata_load_matrix(struct kshark_data_stream *stream, + struct kshark_context *kshark_ctx, + int16_t **event_array, + int16_t **cpu_array, + int32_t **pid_array, + int64_t **offset_array, + int64_t **ts_array) +{ + enum rec_type type = REC_ENTRY; + struct rec_list **rec_list; + ssize_t count, total = 0; + bool status; + + total = get_records(kshark_ctx, stream, &rec_list, type); + if (total < 0) + goto fail; + + status = kshark_data_matrix_alloc(total, event_array, + cpu_array, + pid_array, + offset_array, + ts_array); + if (!status) + goto fail_free; + + for (count = 0; count < total; count++) { + int next_cpu; + + next_cpu = pick_next_cpu(rec_list, stream->n_cpus, type); + if (next_cpu >= 0) { + struct rec_list *rec = rec_list[next_cpu]; + struct kshark_entry *e = &rec->entry; + + if (offset_array) + (*offset_array)[count] = e->offset; + + if (cpu_array) + (*cpu_array)[count] = e->cpu; + + if (ts_array) + (*ts_array)[count] = e->ts; + + if (pid_array) + (*pid_array)[count] = e->pid; + + if (event_array) + (*event_array)[count] = e->event_id; + + rec_list[next_cpu] = rec_list[next_cpu]->next; + free(rec); + } + } + + /* There should be no entries left in rec_list. */ + free_rec_list(rec_list, stream->n_cpus, type); + return total; + + fail_free: + free_rec_list(rec_list, stream->n_cpus, type); + + fail: + fprintf(stderr, "Failed to allocate memory during data loading.\n"); + return -ENOMEM; +} + +/** + * @brief Load the content of the trace data file into an array of + * tep_records. Use this function only if you need fast access + * to all fields of the record. + * + * @param kshark_ctx: Input location for the session context pointer. + * @param sd: Data stream identifier. + * @param data_rows: Output location for the trace data. Use tracecmd_free_record() + * to free the elements of the outputted array. + * + * @returns The size of the outputted data in the case of success, or a + * negative error code on failure. + */ +ssize_t kshark_load_tep_records(struct kshark_context *kshark_ctx, int sd, + struct tep_record ***data_rows) +{ + struct kshark_data_stream *stream; + enum rec_type type = REC_RECORD; + struct rec_list **rec_list; + struct rec_list *temp_rec; + struct tep_record **rows; + struct tep_record *rec; + ssize_t count, total = 0; + + if (*data_rows) + free(*data_rows); + + stream = kshark_get_data_stream(kshark_ctx, sd); + if (!stream) + return -EBADF; + + total = get_records(kshark_ctx, stream, &rec_list, type); + if (total < 0) + goto fail; + + rows = calloc(total, sizeof(struct tep_record *)); + if (!rows) + goto fail_free; + + for (count = 0; count < total; count++) { + int next_cpu; + + next_cpu = pick_next_cpu(rec_list, stream->n_cpus, type); + + if (next_cpu >= 0) { + rec = rec_list[next_cpu]->rec; + rows[count] = rec; + + temp_rec = rec_list[next_cpu]; + rec_list[next_cpu] = rec_list[next_cpu]->next; + free(temp_rec); + /* The record is still referenced in rows */ + } + } + + /* There should be no records left in rec_list. */ + free_rec_list(rec_list, stream->n_cpus, type); + *data_rows = rows; + return total; + + fail_free: + free_rec_list(rec_list, stream->n_cpus, type); + + fail: + fprintf(stderr, "Failed to allocate memory during data loading.\n"); + return -ENOMEM; +} + +static const int tepdata_get_event_id(struct kshark_data_stream *stream, + const struct kshark_entry *entry) +{ + int event_id = KS_EMPTY_BIN; + struct tep_record *record; + + if (entry->visible & KS_PLUGIN_UNTOUCHED_MASK) { + event_id = entry->event_id; + } else { + /* + * The entry has been touched by a plugin callback function. + * Because of this we do not trust the value of + * "entry->event_id". + * + * Currently the data reading operations are not thread-safe. + * Use a mutex to protect the access. + */ + pthread_mutex_lock(&stream->input_mutex); + + record = tracecmd_read_at(kshark_get_tep_input(stream), + entry->offset, NULL); + + if (record) + event_id = tep_data_type(kshark_get_tep(stream), record); + + tracecmd_free_record(record); + + pthread_mutex_unlock(&stream->input_mutex); + } + + return (event_id == -1)? -EFAULT : event_id; +} + +static char* missed_events_dump(struct kshark_data_stream *stream, + const struct kshark_entry *entry, + bool get_info) +{ + char *buffer; + int size = 0; + + if (get_info) + size = asprintf(&buffer, "missed_events=%i", + (int) entry->offset); + else + size = asprintf(&buffer, "missed_events"); + + if (size > 0) + return buffer; + + return NULL; +} + +static char *tepdata_get_event_name(struct kshark_data_stream *stream, + const struct kshark_entry *entry) +{ + struct kshark_generic_stream_interface *interface; + struct tep_event *event; + char *buffer; + + interface = stream->interface; + if (!interface) + return NULL; + + int event_id = interface->get_event_id(stream, entry); + if (event_id == -EFAULT) + return NULL; + + if (event_id < 0) { + switch (event_id) { + case KS_EVENT_OVERFLOW: + return missed_events_dump(stream, entry, false); + default: + return NULL; + } + } + + /* + * Currently the data reading operations are not thread-safe. + * Use a mutex to protect the access. + */ + pthread_mutex_lock(&stream->input_mutex); + + event = tep_find_event(kshark_get_tep(stream), event_id); + + pthread_mutex_unlock(&stream->input_mutex); + + if (!event || + asprintf(&buffer, "%s/%s", event->system, event->name) <= 0) + return NULL; + + return buffer; +} + +static const int tepdata_get_pid(struct kshark_data_stream *stream, + const struct kshark_entry *entry) +{ + struct tep_record *record; + int pid = KS_EMPTY_BIN; + + if (entry->visible & KS_PLUGIN_UNTOUCHED_MASK) { + pid = entry->pid; + } else { + /* + * The entry has been touched by a plugin callback function. + * Because of this we do not trust the value of "entry->pid". + * + * Currently the data reading operations are not thread-safe. + * Use a mutex to protect the access. + */ + pthread_mutex_lock(&stream->input_mutex); + + record = tracecmd_read_at(kshark_get_tep_input(stream), + entry->offset, NULL); + + if (record) + pid = tep_data_pid(kshark_get_tep(stream), record); + + tracecmd_free_record(record); + + pthread_mutex_unlock(&stream->input_mutex); + } + + return pid; +} + +static char *tepdata_get_task(struct kshark_data_stream *stream, + const struct kshark_entry *entry) +{ + struct kshark_generic_stream_interface *interface = stream->interface; + const char *task; + int pid; + + if (!interface) + return NULL; + + pid = interface->get_pid(stream, entry); + task = tep_data_comm_from_pid(kshark_get_tep(stream), pid); + + return task ? strdup(task) : NULL; +} + +static char *tepdata_get_latency(struct kshark_data_stream *stream, + const struct kshark_entry *entry) +{ + struct tep_record *record; + char *buffer; + + /* Check if this is a "Missed event" (event_id < 0). */ + if (!init_thread_seq() || entry->event_id < 0) + return NULL; + + /* + * Currently the data reading operations are not thread-safe. + * Use a mutex to protect the access. + */ + pthread_mutex_lock(&stream->input_mutex); + + record = tracecmd_read_at(kshark_get_tep_input(stream), entry->offset, NULL); + + if (!record) + return NULL; + + trace_seq_reset(&seq); + tep_print_event(kshark_get_tep(stream), &seq, record, + "%s", TEP_PRINT_LATENCY); + + tracecmd_free_record(record); + + pthread_mutex_unlock(&stream->input_mutex); + + if (asprintf(&buffer, "%s", seq.buffer) <= 0) + return NULL; + + return buffer; +} + +static char *get_info_str(struct kshark_data_stream *stream, + struct tep_record *record, + struct tep_event *event) +{ + char *pos, *buffer; + + if (!init_thread_seq() || !record || !event) + return NULL; + + trace_seq_reset(&seq); + tep_print_event(kshark_get_tep(stream), &seq, record, + "%s", TEP_PRINT_INFO); + + /* + * The event info string contains a trailing newline. + * Remove this newline. + */ + if ((pos = strchr(seq.buffer, '\n')) != NULL) + *pos = '\0'; + + if (asprintf(&buffer, "%s", seq.buffer) <= 0) + return NULL; + + return buffer; +} + +static char *tepdata_get_info(struct kshark_data_stream *stream, + const struct kshark_entry *entry) +{ + struct tep_record *record; + struct tep_event *event; + char *info = NULL; + int event_id; + + if (entry->event_id < 0) { + switch (entry->event_id) { + case KS_EVENT_OVERFLOW: + return missed_events_dump(stream, entry, true); + default: + return NULL; + } + } + + /* + * Currently the data reading operations are not thread-safe. + * Use a mutex to protect the access. + */ + pthread_mutex_lock(&stream->input_mutex); + + record = tracecmd_read_at(kshark_get_tep_input(stream), entry->offset, NULL); + if (!record) { + pthread_mutex_unlock(&stream->input_mutex); + return NULL; + } + + event_id = tep_data_type(kshark_get_tep(stream), record); + event = tep_find_event(kshark_get_tep(stream), event_id); + + if (event) + info = get_info_str(stream, record, event); + + tracecmd_free_record(record); + + pthread_mutex_unlock(&stream->input_mutex); + + return info; +} + +static int *tepdata_get_event_ids(struct kshark_data_stream *stream) +{ + struct tep_event **events; + int i, *evt_ids; + + events = tep_list_events(kshark_get_tep(stream), TEP_EVENT_SORT_SYSTEM); + if (!events) + return NULL; + + evt_ids = calloc(stream->n_events, sizeof(*evt_ids)); + if (!evt_ids) + return NULL; + + for (i = 0; i < stream->n_events ; ++i) + evt_ids[i] = events[i]->id; + + return evt_ids; +} + +static int tepdata_get_field_names(struct kshark_data_stream *stream, + const struct kshark_entry *entry, + char ***fields_str) +{ + struct tep_format_field *field, **fields; + struct tep_event *event; + int i= 0, nr_fields; + char **buffer; + + *fields_str = NULL; + event = tep_find_event(kshark_get_tep(stream), entry->event_id); + if (!event) + return 0; + + nr_fields = event->format.nr_fields + event->format.nr_common; + buffer = calloc(nr_fields, sizeof(**fields_str)); + if (!buffer) + return -ENOMEM; + + /* Add all common fields. */ + fields = tep_event_common_fields(event); + if (!fields) + goto fail; + + for (field = *fields; field; field = field->next) + if (asprintf(&buffer[i++], "%s", field->name) <= 0) + goto fail; + + free(fields); + + /* Add all unique fields. */ + fields = tep_event_fields(event); + if (!fields) + goto fail; + + for (field = *fields; field; field = field->next) + if (asprintf(&buffer[i++], "%s", field->name) <= 0) + goto fail; + + free(fields); + + *fields_str = buffer; + return nr_fields; + + fail: + for (i = 0; i < nr_fields; ++i) + free(buffer[i]); + + return -EFAULT; +} + +/** + * Custom entry info function type. To be user for dumping info for custom + * KernelShark entryes. + */ +typedef char *(tepdata_custom_info_func)(struct kshark_data_stream *, + const struct kshark_entry *, + bool); + +static char* tepdata_dump_custom_entry(struct kshark_data_stream *stream, + const struct kshark_entry *entry, + tepdata_custom_info_func info_func) +{ + char *entry_str; + int size = 0; + + size = asprintf(&entry_str, "%" PRIu64 "; %s-%i; CPU %i; ; %s; %s; 0x%x", + entry->ts, + tep_data_comm_from_pid(kshark_get_tep(stream), entry->pid), + entry->pid, + entry->cpu, + info_func(stream, entry, false), + info_func(stream, entry, true), + entry->visible); + + if (size > 0) + return entry_str; + + return NULL; +} + +/** + * @brief Dump into a string the content of one entry. The function allocates + * a null terminated string and returns a pointer to this string. + * + * @param stream: Input location for the FTRACE data stream pointer. + * @param entry: A Kernel Shark entry to be printed. + * + * @returns The returned string contains a semicolon-separated list of data + * fields. The user has to free the returned string. + */ +static char *tepdata_dump_entry(struct kshark_data_stream *stream, + const struct kshark_entry *entry) +{ + char *entry_str, *task, *latency, *event, *info; + struct kshark_generic_stream_interface *interface; + struct kshark_context *kshark_ctx = NULL; + int n = 0; + + if (!kshark_instance(&kshark_ctx) || !init_thread_seq()) + return NULL; + + interface = stream->interface; + if (!interface) + return NULL; + + if (entry->event_id >= 0) { + if (kshark_get_tep(stream)) { + task = interface->get_task(stream, entry); + latency = interface->aux_info(stream, entry); + event = interface->get_event_name(stream, entry); + info = interface->get_info(stream, entry); + n = asprintf(&entry_str, + "%i; %" PRIu64 "; %s-%i; CPU %i; %s; %s; %s; 0x%x", + entry->stream_id, + entry->ts, + task, + interface->get_pid(stream, entry), + entry->cpu, + latency, + event, + info, + entry->visible); + + free(task); + free(latency); + free(event); + free(info); + } else { + n = asprintf(&entry_str, + "%i; %li; [UNKNOWN TASK]-%i; CPU %i; ; [UNKNOWN EVENT]; [NO INFO]; 0x%x", + entry->stream_id, + entry->ts, + interface->get_pid(stream, entry), + entry->cpu, + entry->visible); + } + + if (n < 1) + return NULL; + } else { + switch (entry->event_id) { + case KS_EVENT_OVERFLOW: + entry_str = tepdata_dump_custom_entry(stream, entry, + missed_events_dump); + default: + return NULL; + } + } + + return entry_str; +} + +static const int tepdata_find_event_id(struct kshark_data_stream *stream, + const char *event_name) +{ + struct tep_event *event; + char *buffer, *system, *name; + + if (asprintf(&buffer, "%s", event_name) < 1) + return -1; + + system = strtok(buffer, "/"); + name = strtok(NULL, ""); + if (!system || !name) + return -1; + + event = tep_find_event_by_name(kshark_get_tep(stream), system, name); + + free(buffer); + + if (!event) + return -1; + + return event->id; +} + +static struct tep_format_field * +get_evt_field(struct kshark_data_stream *stream, + int event_id, const char *field_name) +{ + struct tep_event *event = tep_find_event(kshark_get_tep(stream), + event_id); + if (!event) + return NULL; + + return tep_find_any_field(event, field_name); +} + +/** + * @brief Get the type of a trace record field. For the moment only integer + * fields are supported. + * + * @param stream: Input location for the FTRACE data stream pointer. + * @param entry: Input location for the Kernel Shark entry asociated with thes + * record. + * @param field: The name of the field. + * + * @returns KS_INTEGER_FIELD in case the field has an integer type. Otherwise + * KS_INVALID_FIELD. + */ +kshark_event_field_format +tepdata_get_field_type(struct kshark_data_stream *stream, + const struct kshark_entry *entry, + const char *field) +{ + struct tep_format_field *evt_field; + int mask = ~(TEP_FIELD_IS_SIGNED | + TEP_FIELD_IS_LONG | + TEP_FIELD_IS_FLAG); + + evt_field = get_evt_field(stream, entry->event_id, field); + if (!evt_field) + return KS_INVALID_FIELD; + + if (mask & evt_field->flags) + return KS_INVALID_FIELD; + + return KS_INTEGER_FIELD; +} + +/** + * @brief Get the value of a trace record field. + * + * @param stream: Input location for the FTRACE data stream pointer. + * @param rec: Input location for the trace record. + * @param field: The name of the field. + * @param val: Output location for the field value. + * + * @returns Returns 0 on success, otherwise a negative error code.. + */ +int tepdata_read_record_field(struct kshark_data_stream *stream, + void *rec, + const char *field, int64_t *val) +{ + struct tep_format_field *evt_field; + struct tep_record *record = rec; + int event_id, ret; + + if (!record) + return -EFAULT; + + event_id = tep_data_type(kshark_get_tep(stream), record); + evt_field = get_evt_field(stream, event_id, field); + if (!evt_field) + return -EINVAL; + + ret = tep_read_number_field(evt_field, record->data, + (unsigned long long *) val); + + return ret; +} + +/** + * @brief Get the value of a trace record field. + * + * @param stream: Input location for the FTRACE data stream pointer. + * @param entry: Input location for the Kernel Shark entry asociated with thes + * record. + * @param field: The name of the field. + * @param val: Output location for the field value. + * + * @returns Returns 0 on success, otherwise a negative error code. + */ +int tepdata_read_event_field(struct kshark_data_stream *stream, + const struct kshark_entry *entry, + const char *field, int64_t *val) +{ + struct tep_format_field *evt_field; + struct tep_record *record; + int ret; + + evt_field = get_evt_field(stream, entry->event_id, field); + if (!evt_field) + return -EINVAL; + + record = tracecmd_read_at(kshark_get_tep_input(stream), + entry->offset, NULL); + if (!record) + return -EFAULT; + + ret = tep_read_number_field(evt_field, record->data, + (unsigned long long *) val); + tracecmd_free_record(record); + + return ret; +} + +/** Initialize all methods used by a stream of FTRACE data. */ +static void kshark_tep_init_methods(struct kshark_generic_stream_interface *interface) +{ + if (!interface) + return; + + interface->get_pid = tepdata_get_pid; + interface->get_task = tepdata_get_task; + interface->get_event_id = tepdata_get_event_id; + interface->get_event_name = tepdata_get_event_name; + interface->aux_info= tepdata_get_latency; + interface->get_info = tepdata_get_info; + interface->find_event_id = tepdata_find_event_id; + interface->get_all_event_ids = tepdata_get_event_ids; + interface->dump_entry = tepdata_dump_entry; + interface->get_all_event_field_names = tepdata_get_field_names; + interface->get_event_field_type = tepdata_get_field_type; + interface->read_record_field_int64 = tepdata_read_record_field; + interface->read_event_field_int64 = tepdata_read_event_field; + interface->load_entries = tepdata_load_entries; + interface->load_matrix = tepdata_load_matrix; +} + +/** Find a host stream from the same tracing session, that has guest information */ +static struct tracecmd_input * +kshark_tep_find_merge_peer(struct kshark_context *kshark_ctx, + struct tracecmd_input *handle) +{ + struct tracecmd_input *peer_handle = NULL; + struct kshark_data_stream *peer_stream; + unsigned long long trace_id; + int *stream_ids = NULL; + int ret; + int i; + + trace_id = tracecmd_get_traceid(handle); + if (!trace_id) + goto out; + + stream_ids = kshark_all_streams(kshark_ctx); + if (!stream_ids) + goto out; + + for (i = 0; i < kshark_ctx->n_streams - 1; i++) { + peer_stream = kshark_get_data_stream(kshark_ctx, stream_ids[i]); + if (!peer_stream || !kshark_is_tep(peer_stream)) + continue; + + peer_handle = kshark_get_tep_input(peer_stream); + if (!peer_handle) + continue; + + ret = tracecmd_get_guest_cpumap(peer_handle, trace_id, + NULL, NULL, NULL); + if (!ret) + break; + } + + if (i == kshark_ctx->n_streams) + peer_handle = NULL; + +out: + free(stream_ids); + return peer_handle; +} + +/** The Process Id of the Idle tasks is zero. */ +#define LINUX_IDLE_TASK_PID 0 + +static int kshark_tep_stream_init(struct kshark_data_stream *stream, + struct tracecmd_input *input) +{ + struct kshark_generic_stream_interface *interface; + struct tepdata_handle *tep_handle; + struct tep_event *event; + + stream->interface = interface = calloc(1, sizeof(*interface)); + if (!interface) + return -ENOMEM; + + interface->type = KS_GENERIC_DATA_INTERFACE; + + tep_handle = calloc(1, sizeof(*tep_handle)); + if (!tep_handle) + goto fail; + + tep_handle->input = input; + tep_handle->tep = tracecmd_get_tep(tep_handle->input); + if (!tep_handle->tep) + goto fail; + + tep_handle->sched_switch_event_id = -EINVAL; + event = tep_find_event_by_name(tep_handle->tep, + "sched", "sched_switch"); + if (event) { + tep_handle->sched_switch_event_id = event->id; + + tep_handle->sched_switch_next_field = + tep_find_any_field(event, "next_pid"); + + tep_handle->sched_switch_comm_field = + tep_find_field(event, "next_comm"); + } + + stream->n_cpus = tep_get_cpus(tep_handle->tep); + stream->n_events = tep_get_events_count(tep_handle->tep); + stream->idle_pid = LINUX_IDLE_TASK_PID; + + tep_handle->advanced_event_filter = + tep_filter_alloc(tep_handle->tep); + + kshark_tep_init_methods(interface); + + interface->handle = tep_handle; + + return 0; + + fail: + free(tep_handle); + free(interface); + stream->interface = NULL; + return -EFAULT; +} + +static inline char *set_tep_format(struct kshark_data_stream *stream) +{ + return kshark_set_data_format(stream->data_format, + TEP_DATA_FORMAT_IDENTIFIER); +} + +/** Check is the file contains TEP tracing data. */ +bool kshark_tep_check_data(const char *file_name) +{ + /* + * TODO: This is very naive. Implement more appropriate check. Ideally + * it should be part of the trace-cmd library. + */ + char *ext = strrchr(file_name, '.'); + if (ext && strcmp(ext, ".dat") == 0) { + return true; + } + + return false; +} + +/** Initialize the FTRACE data input (from file). */ +int kshark_tep_init_input(struct kshark_data_stream *stream) +{ + struct kshark_context *kshark_ctx = NULL; + struct tracecmd_input *merge_peer; + struct tracecmd_input *input; + + if (!kshark_instance(&kshark_ctx) || !init_thread_seq()) + return -EEXIST; + + /* + * Turn off function trace indent and turn on show parent + * if possible. + */ + tep_plugin_add_option("ftrace:parent", "1"); + tep_plugin_add_option("ftrace:indent", "0"); + + input = tracecmd_open_head(stream->file); + if (!input) + return -EEXIST; + + /* Find a merge peer from the same tracing session. */ + merge_peer = kshark_tep_find_merge_peer(kshark_ctx, input); + if (merge_peer) + tracecmd_pair_peer(input, merge_peer); + + /* Read the tracing data from the file. */ + if (tracecmd_init_data(input) < 0) + goto fail; + + /* Initialize the stream asociated with the main buffer. */ + if (kshark_tep_stream_init(stream, input) < 0) + goto fail; + + stream->name = strdup(KS_UNNAMED); + + return 0; + + fail: + tracecmd_close(input); + return -EFAULT; +} + +/** Initialize using the locally available tracing events. */ +int kshark_tep_init_local(struct kshark_data_stream *stream) +{ + struct kshark_generic_stream_interface *interface; + struct tepdata_handle *tep_handle; + + stream->interface = interface = calloc(1, sizeof(*interface)); + if (!interface) + return -ENOMEM; + + interface->type = KS_GENERIC_DATA_INTERFACE; + + tep_handle = calloc(1, sizeof(*tep_handle)); + if (!tep_handle) + goto fail; + + tep_handle->tep = tracefs_local_events(tracefs_tracing_dir()); + if (!tep_handle->tep) + goto fail; + + stream->n_events = tep_get_events_count(tep_handle->tep); + stream->n_cpus = tep_get_cpus(tep_handle->tep); + set_tep_format(stream); + if (asprintf(&stream->file, "Local system") <= 0) + goto fail; + + interface->handle = tep_handle; + kshark_tep_init_methods(interface); + + return 0; + + fail: + free(tep_handle); + free(interface); + stream->interface = NULL; + return -EFAULT; +} + +/** Method used to close a stream of FTRACE data. */ +int kshark_tep_close_interface(struct kshark_data_stream *stream) +{ + struct kshark_generic_stream_interface *interface = stream->interface; + struct tepdata_handle *tep_handle; + + if (!interface) + return -EFAULT; + + tep_handle = interface->handle; + if (!tep_handle) + return -EFAULT; + + if (seq.buffer) + trace_seq_destroy(&seq); + + if (tep_handle->advanced_event_filter) { + tep_filter_reset(tep_handle->advanced_event_filter); + tep_filter_free(tep_handle->advanced_event_filter); + tep_handle->advanced_event_filter = NULL; + } + + if (tep_handle->input) + tracecmd_close(tep_handle->input); + + free(tep_handle); + interface->handle = NULL; + + return 0; +} + +/** Check if the filter any filter is set. */ +bool kshark_tep_filter_is_set(struct kshark_data_stream *stream) +{ + struct tep_event_filter *adv_filter = get_adv_filter(stream); + + if (adv_filter && adv_filter->filters) + return true; + + return false; +} + +/** + * @brief Add a filter based on the content of the event. + * + * @param stream: Input location for the FTRACE data stream pointer. + * @param filter_str: The definition of the filter. + * + * @returns 0 if the filter was successfully added or a negative error code. + */ +int kshark_tep_add_filter_str(struct kshark_data_stream *stream, + const char *filter_str) +{ + struct tep_event_filter *adv_filter = get_adv_filter(stream); + int ret = tep_filter_add_filter_str(adv_filter, filter_str); + + if (ret < 0) { + char error_str[200]; + int error_status = + tep_strerror(kshark_get_tep(stream), ret, error_str, + sizeof(error_str)); + + if (error_status == 0) + fprintf(stderr, "filter failed due to: %s\n", + error_str); + } + + return ret; +} + +/** + * @brief Get a string showing the filter definition. + * + * @param stream: Input location for the FTRACE data stream pointer. + * @param event_id: The unique Id of the event type of the filter. + * + * @returns A string that displays the filter contents. This string must be + * freed with free(str). NULL is returned if no filter is found or + * allocation failed. + */ +char *kshark_tep_filter_make_string(struct kshark_data_stream *stream, + int event_id) +{ + struct tep_event_filter *adv_filter = get_adv_filter(stream); + + return tep_filter_make_string(adv_filter, event_id); +} + +/** + * @brief Remove a filter based on the content of the event. + * + * @param stream: Input location for the FTRACE data stream pointer. + * @param event_id: The unique Id of the event type of the filter. + * + * @return 1: if an event was removed or 0 if the event was not found. + */ +int kshark_tep_filter_remove_event(struct kshark_data_stream *stream, + int event_id) +{ + struct tep_event_filter *adv_filter = get_adv_filter(stream); + + return tep_filter_remove_event(adv_filter, event_id); +} + +/** Reset all filters based on the content of the event. */ +void kshark_tep_filter_reset(struct kshark_data_stream *stream) +{ + return tep_filter_reset(get_adv_filter(stream)); +} + +/** Get an array of available tracer plugins. */ +char **kshark_tracecmd_local_tracers() +{ + return tracefs_tracers(tracefs_tracing_dir()); +} diff --git a/src/libkshark-tepdata.h b/src/libkshark-tepdata.h new file mode 100644 index 0000000..529f1e9 --- /dev/null +++ b/src/libkshark-tepdata.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: LGPL-2.1 */ + +/* + * Copyright (C) 2019 VMware Inc, Yordan Karadzhov (VMware) + */ + +/** + * @file libkshark-tepdata.h + * @brief Interface for processing of FTRACE (trace-cmd) data. + */ + +#ifndef _KSHARK_TEPDATA_H +#define _KSHARK_TEPDATA_H + +// KernelShark +#include "libkshark.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Unique identifier of the TEP tracing data format. */ +#define TEP_DATA_FORMAT_IDENTIFIER "tep data" + +/** Check if this Data stream corresponds to a TEP tracing data. */ +static inline bool kshark_is_tep(struct kshark_data_stream *stream) +{ + return strcmp(stream->data_format, TEP_DATA_FORMAT_IDENTIFIER) == 0; +} + +bool kshark_tep_check_data(const char *file_name); + +int kshark_tep_init_input(struct kshark_data_stream *stream); + +int kshark_tep_init_local(struct kshark_data_stream *stream); + +int kshark_tep_close_interface(struct kshark_data_stream *stream); + +bool kshark_tep_filter_is_set(struct kshark_data_stream *stream); + +int kshark_tep_add_filter_str(struct kshark_data_stream *stream, + const char *filter_str); + +char *kshark_tep_filter_make_string(struct kshark_data_stream *stream, + int event_id); + +int kshark_tep_filter_remove_event(struct kshark_data_stream *stream, + int event_id); + +void kshark_tep_filter_reset(struct kshark_data_stream *stream); + +char **kshark_tracecmd_local_tracers(); + +struct tep_record; + +ssize_t kshark_load_tep_records(struct kshark_context *kshark_ctx, int sd, + struct tep_record ***data_rows); + +#ifdef __cplusplus +} +#endif + +#endif // _KSHARK_TEPDATA_H From patchwork Mon Jan 4 17:46:57 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11997201 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0F4D3C433DB for ; Mon, 4 Jan 2021 17:49:39 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id D947F20715 for ; Mon, 4 Jan 2021 17:49:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727341AbhADRti (ORCPT ); Mon, 4 Jan 2021 12:49:38 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34476 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727310AbhADRti (ORCPT ); Mon, 4 Jan 2021 12:49:38 -0500 Received: from mail-ej1-x635.google.com (mail-ej1-x635.google.com [IPv6:2a00:1450:4864:20::635]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5A540C0617AA for ; Mon, 4 Jan 2021 09:47:54 -0800 (PST) Received: by mail-ej1-x635.google.com with SMTP id d17so37831005ejy.9 for ; Mon, 04 Jan 2021 09:47:54 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=XynZx8mlfSH4Iv0AtOqnkf6GHN6nsdOTeoSDbCbk7WY=; b=doGinOIi2FOq0OJhd3678pSRt7ekSiIX2SwCHg2BOy2a2nT1o/eTbS2f6aw6irD5sC fWd+NZe2D/joZVCN9DJDH7sfFaDqWnCL1pzsEQeeZyxcbvqADsA7GkzS6avaanPBqwK9 yI6dHqS4WDet9SN+H8WZoMj2V5ypI9ATcsSeKjjOWQpsj3J7SL1CVKm12WR7ffJ4Vw0H rDZwmpfGFIh3s/VTAnMdPhv8fOXwYWGOaFILrdnhJhNSMF56qTKv3DwQ7eNFPgWx0+UM AxwGEdtejbpohLz7BRRalTHcPh1+2OUeBSIlLgcubJGflxS9zUlAMV9Oj+CxkoQ8No1K 9xMg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=XynZx8mlfSH4Iv0AtOqnkf6GHN6nsdOTeoSDbCbk7WY=; b=ksGjmm2dDsTiewDfXkZZhuodoYlu0dVQHHyD6mVfqvZf5Du8fgRwJOc88D0eLhVYTq RFPFXHUc21XDdaGxo8wY6Wqu0wm6RVSJEvINAtYEopOxnHnN2I+bTTzjG80Ff8OiPQ/F b6tTwWDhFfWdaCtlgsUgId+ds0qrA/smVrRPSqf25dXEmewrJkx+c5lUjuzgypC2LW7a 2/8e6vW0OHYZNlVkm1aSW35Ukvhyte5pZOW4wAcBSRxocAUJklcrEzhQjW0qW7TT2mK+ WTsUt9LpQfczbskLZbyESyBBbb4PksICCjolPHJjOGKthnnl9tun/SeXk+hnnEt4dGMu cYiA== X-Gm-Message-State: AOAM532mQTUsJEBvZiz2gHGPIeLf6MUOQlxQwekJmkPlRemeMy5c1pN2 uME8gOxO1r88mnF/C6WCCI8= X-Google-Smtp-Source: ABdhPJxoF83zPG0kTOi5xNSnRxsTTSKiK9lIZTKK5wIXehW3GuzNCEknhZbCaskIthuGNZd1BeRKbg== X-Received: by 2002:a17:906:6693:: with SMTP id z19mr65715780ejo.376.1609782473073; Mon, 04 Jan 2021 09:47:53 -0800 (PST) Received: from localhost.localdomain ([95.87.199.238]) by smtp.gmail.com with ESMTPSA id l14sm44107750edq.35.2021.01.04.09.47.52 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 04 Jan 2021 09:47:52 -0800 (PST) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v8 17/44] kernel-shark: Start introducing KernelShark 2.0 Date: Mon, 4 Jan 2021 19:46:57 +0200 Message-Id: <20210104174724.70404-18-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210104174724.70404-1-y.karadz@gmail.com> References: <20210104174724.70404-1-y.karadz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org This is the very first patch from a series of patches that will introduce KernelShark 2.0. The new version will take advantage of the definition of Data streams and will imply significant changes in the low level C API, used by the KernelShark GUI. As a consequence of the API modifications, significant changes are needed in every source file of the project. Because, we want to avoid having a huge and ugly single commit that will be extremely hard to review, we will introduce a regression. We will first disable the compilation of all sub-components of KernelShark, except the API itself. This is done by bypassing the search of the necessary third party components (OpenGL and Qt). Later we will introduce the changes of the API in several sub-steps and will re-enable one by one the modified components of the GUI. Signed-off-by: Yordan Karadzhov (VMware) --- CMakeLists.txt | 10 ++++---- build/deff.h.cmake | 14 ++--------- examples/CMakeLists.txt | 54 ++++++++++++++++++++--------------------- src/CMakeLists.txt | 6 ++--- 4 files changed, 37 insertions(+), 47 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f21d734..cac2fa6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,12 +45,12 @@ find_package(JSONC REQUIRED) find_package(Doxygen) -set(OpenGL_GL_PREFERENCE LEGACY) -find_package(OpenGL) -find_package(GLUT) +# set(OpenGL_GL_PREFERENCE LEGACY) +# find_package(OpenGL) +# find_package(GLUT) -find_package(Qt5Widgets 5.7.1) -find_package(Qt5Network) +# find_package(Qt5Widgets 5.7.1) +# find_package(Qt5Network) if (Qt5Widgets_FOUND) message(STATUS "Found Qt5Widgets: (version ${Qt5Widgets_VERSION})") diff --git a/build/deff.h.cmake b/build/deff.h.cmake index 79726ff..e398c0c 100644 --- a/build/deff.h.cmake +++ b/build/deff.h.cmake @@ -23,17 +23,7 @@ /** "pkexec" executable. */ #cmakedefine DO_AS_ROOT "@DO_AS_ROOT@" -#ifdef __cplusplus - - #include - - /** - * String containing semicolon-separated list of plugin names. - * The plugins to be loaded when KernelShark starts are tagged - * with "default". - */ - const QString plugins = "@PLUGINS@"; - -#endif /* __cplusplus */ +/** Semicolon-separated list of plugin names. */ +#define KS_BUILTIN_PLUGINS "@PLUGINS@" #endif // _KS_CONFIG_H diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 032f305..f6ed897 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -4,30 +4,30 @@ message(STATUS "dataload") add_executable(dload dataload.c) target_link_libraries(dload kshark) -message(STATUS "datafilter") -add_executable(dfilter datafilter.c) -target_link_libraries(dfilter kshark) - -message(STATUS "datahisto") -add_executable(dhisto datahisto.c) -target_link_libraries(dhisto kshark) - -message(STATUS "confogio") -add_executable(confio configio.c) -target_link_libraries(confio kshark) - -message(STATUS "dataplot") -add_executable(dplot dataplot.cpp) -target_link_libraries(dplot kshark-plot) - -if (Qt5Widgets_FOUND) - - message(STATUS "widgetdemo") - add_executable(widgetdemo widgetdemo.cpp) - target_link_libraries(widgetdemo kshark-gui) - - message(STATUS "cmd_split") - add_executable(cmd_split cmd_split.cpp) - target_link_libraries(cmd_split kshark-gui) - -endif (Qt5Widgets_FOUND) +# message(STATUS "datafilter") +# add_executable(dfilter datafilter.c) +# target_link_libraries(dfilter kshark) +# +# message(STATUS "datahisto") +# add_executable(dhisto datahisto.c) +# target_link_libraries(dhisto kshark) +# +# message(STATUS "confogio") +# add_executable(confio configio.c) +# target_link_libraries(confio kshark) +# +# message(STATUS "dataplot") +# add_executable(dplot dataplot.cpp) +# target_link_libraries(dplot kshark-plot) +# +# if (Qt5Widgets_FOUND) +# +# message(STATUS "widgetdemo") +# add_executable(widgetdemo widgetdemo.cpp) +# target_link_libraries(widgetdemo kshark-gui) +# +# message(STATUS "cmd_split") +# add_executable(cmd_split cmd_split.cpp) +# target_link_libraries(cmd_split kshark-gui) +# +# endif (Qt5Widgets_FOUND) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ce82d30..fac1d5e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -5,10 +5,10 @@ set(KS_INCLUDS_DESTINATION "${_INSTALL_PREFIX}/include/${KS_APP_NAME}") message(STATUS "libkshark") add_library(kshark SHARED libkshark.c libkshark-hash.c - libkshark-model.c +# libkshark-model.c libkshark-plugin.c libkshark-tepdata.c - libkshark-configio.c +# libkshark-configio.c libkshark-collection.c) target_link_libraries(kshark trace::cmd @@ -134,7 +134,7 @@ if (Qt5Widgets_FOUND AND Qt5Network_FOUND) endif (Qt5Widgets_FOUND AND Qt5Network_FOUND) -add_subdirectory(plugins) +# add_subdirectory(plugins) find_program(DO_AS_ROOT pkexec) From patchwork Mon Jan 4 17:46:58 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11997203 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2EE15C433E0 for ; Mon, 4 Jan 2021 17:49:39 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 0202F20770 for ; Mon, 4 Jan 2021 17:49:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727310AbhADRti (ORCPT ); Mon, 4 Jan 2021 12:49:38 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34478 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727335AbhADRti (ORCPT ); Mon, 4 Jan 2021 12:49:38 -0500 Received: from mail-ej1-x632.google.com (mail-ej1-x632.google.com [IPv6:2a00:1450:4864:20::632]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C65CBC0617AB for ; Mon, 4 Jan 2021 09:47:55 -0800 (PST) Received: by mail-ej1-x632.google.com with SMTP id qw4so37871460ejb.12 for ; Mon, 04 Jan 2021 09:47:55 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=RjxwpyHqEzk3yAyo5yk7ptxZTAuAtbGJIIArO8Zu5O8=; b=ruz8tHLVNlxOulU229X5KhqT9t+xdNTZdaUtRxbJ1x2+ND7Gsbh0a06MZ0oqGV15nb pl9Ie0nREXO4HP5wkDj1U7SZFu7dDsicd+nLRxUhh2AQ6XA28mKgWXyKiwYPK/VnDOxA pUky0LCH7b63S01wWwjJRbI5cyH1n/sKF0XUbHlLJunFdD7Gylt2TpGEPckB5x6cm2FQ VyBsWNUuhZUuMXvczbt9HZl2loderxXViDF0aBKbYDEa1bnXP6NXnd6czDKPQz+4pG02 J3dDW7V0HdWohgUH5C18ONIe0JrwdMFLp2YuL+NJ147lUG7PcvD+ejZ1MHi99yr4sasA 2gOg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=RjxwpyHqEzk3yAyo5yk7ptxZTAuAtbGJIIArO8Zu5O8=; b=KtkJRX/31bP1m9rIPKsSJJquDWtyqYEcESTfVSf6Lna/iAjbmp0Adnk1Vpqv7SuMF4 rzfrSbIn4UV1YzUlxZ5kI4tthiFq1+Q4wzglxXJud5YFTEuiXxoABKq2V/bASaZ25PB3 899OeU85QWyCA5hQaKIhK7qLwWChw7UdEbb2ppcTU34LYDsUgtP8wVEZJlvj7qOAMGSK XVTxANvfzRV5sq1LAle7WPEpeGjvClymxIvZ3FIVcL8OPz9aFzvVwM3TvhmMJ4/hvDYn C8myig4ayszTt7v20QP94e21eCu6gHj9cmllSkiv3VMAGPnfRHRK2MnLwY1gLXlFEcD1 sGJg== X-Gm-Message-State: AOAM531TJ5PJQPu99uLQ4x5nUTWD/NBzOohlG+HHSGN9yH9IXMwaiVn8 1l39mrKKBcqUkIQnsFdtCgw= X-Google-Smtp-Source: ABdhPJy1sNEYmyGqomzTXdyCZjoYz308rmozIzmVbFdFQKpFrY5d22I1326dpODU3k9RRSj/ciaD5Q== X-Received: by 2002:a17:906:74d9:: with SMTP id z25mr67043200ejl.217.1609782474328; Mon, 04 Jan 2021 09:47:54 -0800 (PST) Received: from localhost.localdomain ([95.87.199.238]) by smtp.gmail.com with ESMTPSA id l14sm44107750edq.35.2021.01.04.09.47.53 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 04 Jan 2021 09:47:53 -0800 (PST) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v8 18/44] kernel-shark: Start using data streams Date: Mon, 4 Jan 2021 19:46:58 +0200 Message-Id: <20210104174724.70404-19-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210104174724.70404-1-y.karadz@gmail.com> References: <20210104174724.70404-1-y.karadz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org Here we switch to using the trace data readout, provided by the Data stream interface. The actual implementation of the new readout was done in the previous commits. Signed-off-by: Yordan Karadzhov (VMware) --- examples/dataload.c | 28 ++-- src/libkshark.c | 345 ++++++++++++++++++-------------------------- src/libkshark.h | 14 +- 3 files changed, 166 insertions(+), 221 deletions(-) diff --git a/examples/dataload.c b/examples/dataload.c index 15c5de0..e259b3a 100644 --- a/examples/dataload.c +++ b/examples/dataload.c @@ -19,8 +19,7 @@ int main(int argc, char **argv) struct kshark_entry **data = NULL; ssize_t r, n_rows, n_tasks; char *entry_str; - bool status; - int *pids; + int sd, *pids; /* Create a new kshark session. */ kshark_ctx = NULL; @@ -29,30 +28,28 @@ int main(int argc, char **argv) /* Open a trace data file produced by trace-cmd. */ if (argc > 1) - status = kshark_open(kshark_ctx, argv[1]); + sd = kshark_open(kshark_ctx, argv[1]); else - status = kshark_open(kshark_ctx, default_file); + sd = kshark_open(kshark_ctx, default_file); - if (!status) { + if (sd < 0) { kshark_free(kshark_ctx); return 1; } /* Load the content of the file into an array of entries. */ - n_rows = kshark_load_data_entries(kshark_ctx, &data); + n_rows = kshark_load_entries(kshark_ctx, sd, &data); if (n_rows < 1) { kshark_free(kshark_ctx); return 1; } /* Print to the screen the list of all tasks. */ - n_tasks = kshark_get_task_pids(kshark_ctx, &pids); + n_tasks = kshark_get_task_pids(kshark_ctx, sd, &pids); for (r = 0; r < n_tasks; ++r) { - const char *task_str = - tep_data_comm_from_pid(kshark_ctx->pevent, - pids[r]); - + char *task_str = kshark_comm_from_pid(sd, pids[r]); printf("task: %s-%i\n", task_str, pids[r]); + free(task_str); } free(pids); @@ -69,11 +66,8 @@ int main(int argc, char **argv) puts("\n...\n"); /* Print the last 10 entries. */ - for (r = n_rows - 10; r < n_rows; ++r) { - entry_str = kshark_dump_entry(data[r]); - puts(entry_str); - free(entry_str); - } + for (r = n_rows - 10; r < n_rows; ++r) + kshark_print_entry(data[r]); /* Free the memory. */ for (r = 0; r < n_rows; ++r) @@ -82,7 +76,7 @@ int main(int argc, char **argv) free(data); /* Close the file. */ - kshark_close(kshark_ctx); + kshark_close(kshark_ctx, sd); /* Close the session. */ kshark_free(kshark_ctx); diff --git a/src/libkshark.c b/src/libkshark.c index 14484eb..d337f49 100644 --- a/src/libkshark.c +++ b/src/libkshark.c @@ -19,6 +19,7 @@ // KernelShark #include "libkshark.h" +#include "libkshark-tepdata.h" static __thread struct trace_seq seq; @@ -32,6 +33,9 @@ static bool kshark_default_context(struct kshark_context **context) if (!kshark_ctx) return false; + kshark_ctx->stream = calloc(KS_DEFAULT_NUM_STREAMS, + sizeof(*kshark_ctx->stream)); + kshark_ctx->event_handlers = NULL; kshark_ctx->collections = NULL; kshark_ctx->plugins = NULL; @@ -47,6 +51,9 @@ static bool kshark_default_context(struct kshark_context **context) kshark_ctx->filter_mask = 0x0; + kshark_ctx->stream_info.array_size = KS_DEFAULT_NUM_STREAMS; + kshark_ctx->stream_info.max_stream_id = -1; + /* Will free kshark_context_handler. */ kshark_free(NULL); @@ -108,62 +115,30 @@ bool kshark_instance(struct kshark_context **kshark_ctx) return true; } -static void kshark_free_task_list(struct kshark_context *kshark_ctx) -{ - struct kshark_task_list *task; - int i; - - if (!kshark_ctx) - return; - - for (i = 0; i < KS_TASK_HASH_SIZE; ++i) { - while (kshark_ctx->tasks[i]) { - task = kshark_ctx->tasks[i]; - kshark_ctx->tasks[i] = task->next; - free(task); - } - } -} - /** * @brief Open and prepare for reading a trace data file specified by "file". - * If the specified file does not exist, or contains no trace data, - * the function returns false. * * @param kshark_ctx: Input location for context pointer. * @param file: The file to load. * - * @returns True on success, or false on failure. + * @returns The Id number of the data stream associated with this file on success. + * Otherwise a negative errno code. */ -bool kshark_open(struct kshark_context *kshark_ctx, const char *file) +int kshark_open(struct kshark_context *kshark_ctx, const char *file) { - struct tracecmd_input *handle; - - kshark_free_task_list(kshark_ctx); + int sd, rt; - handle = tracecmd_open_head(file); - if (!handle) - return false; + sd = kshark_add_stream(kshark_ctx); + if (sd < 0) + return sd; - if (pthread_mutex_init(&kshark_ctx->input_mutex, NULL) != 0) { - tracecmd_close(handle); - return false; + rt = kshark_stream_open(kshark_ctx->stream[sd], file); + if (rt < 0) { + kshark_remove_stream(kshark_ctx, sd); + return rt; } - kshark_ctx->handle = handle; - kshark_ctx->pevent = tracecmd_get_tep(handle); - - kshark_ctx->advanced_event_filter = - tep_filter_alloc(kshark_ctx->pevent); - - /* - * Turn off function trace indent and turn on show parent - * if possible. - */ - tep_plugin_add_option("ftrace:parent", "1"); - tep_plugin_add_option("ftrace:indent", "0"); - - return true; + return sd; } static void kshark_stream_free(struct kshark_data_stream *stream) @@ -328,6 +303,36 @@ int kshark_add_stream(struct kshark_context *kshark_ctx) return stream->stream_id; } +/** + * @brief Use an existing Trace data stream to open and prepare for reading + * a trace data file specified by "file". + * + * @param stream: Input location for a Trace data stream pointer. + * @param file: The file to load. + * + * @returns Zero on success or a negative error code in the case of an errno. + */ +int kshark_stream_open(struct kshark_data_stream *stream, const char *file) +{ + struct kshark_context *kshark_ctx = NULL; + + if (!stream || !kshark_instance(&kshark_ctx)) + return -EFAULT; + + stream->file = strdup(file); + if (!stream->file) + return -ENOMEM; + + if (kshark_tep_check_data(file)) { + kshark_set_data_format(stream->data_format, + TEP_DATA_FORMAT_IDENTIFIER); + + return kshark_tep_init_input(stream); + } + + return -ENODATA; +} + /** * @brief Remove Data stream. * @@ -418,45 +423,74 @@ int *kshark_all_streams(struct kshark_context *kshark_ctx) return ids; } + +static int kshark_stream_close(struct kshark_data_stream *stream) +{ + struct kshark_context *kshark_ctx = NULL; + + if (!stream || !kshark_instance(&kshark_ctx)) + return -EFAULT; + + /* + * All filters are file specific. Make sure that all Process Ids and + * Event Ids from this file are not going to be used with another file. + */ + kshark_hash_id_clear(stream->show_task_filter); + kshark_hash_id_clear(stream->hide_task_filter); + kshark_hash_id_clear(stream->show_event_filter); + kshark_hash_id_clear(stream->hide_event_filter); + kshark_hash_id_clear(stream->show_cpu_filter); + kshark_hash_id_clear(stream->hide_cpu_filter); + + if (kshark_is_tep(stream)) + return kshark_tep_close_interface(stream); + + return -ENODATA; +} + /** * @brief Close the trace data file and free the trace data handle. * * @param kshark_ctx: Input location for the session context pointer. + * @param sd: Data stream identifier. + * + * @returns Zero on success or a negative errno code on failure. */ -void kshark_close(struct kshark_context *kshark_ctx) +int kshark_close(struct kshark_context *kshark_ctx, int sd) { - if (!kshark_ctx || !kshark_ctx->handle) - return; + struct kshark_data_stream *stream; + int ret; - /* - * All filters are file specific. Make sure that the Pids and Event Ids - * from this file are not going to be used with another file. - */ - tracecmd_filter_id_clear(kshark_ctx->show_task_filter); - tracecmd_filter_id_clear(kshark_ctx->hide_task_filter); - tracecmd_filter_id_clear(kshark_ctx->show_event_filter); - tracecmd_filter_id_clear(kshark_ctx->hide_event_filter); - tracecmd_filter_id_clear(kshark_ctx->show_cpu_filter); - tracecmd_filter_id_clear(kshark_ctx->hide_cpu_filter); - - if (kshark_ctx->advanced_event_filter) { - tep_filter_reset(kshark_ctx->advanced_event_filter); - tep_filter_free(kshark_ctx->advanced_event_filter); - kshark_ctx->advanced_event_filter = NULL; - } + stream = kshark_get_data_stream(kshark_ctx, sd); + if (!stream) + return -EFAULT; + + ret = kshark_stream_close(stream); + kshark_remove_stream(kshark_ctx, stream->stream_id); + + return ret; +} + +/** + * @brief Close all currently open trace data file and free the trace data handle. + * + * @param kshark_ctx: Input location for the session context pointer. + */ +void kshark_close_all(struct kshark_context *kshark_ctx) +{ + int i, *stream_ids, n_streams; + + stream_ids = kshark_all_streams(kshark_ctx); /* - * All data collections are file specific. Make sure that collections - * from this file are not going to be used with another file. + * Get a copy of shark_ctx->n_streams befor you start closing. Be aware + * that kshark_close() will decrement shark_ctx->n_streams. */ - kshark_free_collection_list(kshark_ctx->collections); - kshark_ctx->collections = NULL; + n_streams = kshark_ctx->n_streams; + for (i = 0; i < n_streams; ++i) + kshark_close(kshark_ctx, stream_ids[i]); - tracecmd_close(kshark_ctx->handle); - kshark_ctx->handle = NULL; - kshark_ctx->pevent = NULL; - - pthread_mutex_destroy(&kshark_ctx->input_mutex); + free(stream_ids); } /** @@ -464,7 +498,7 @@ void kshark_close(struct kshark_context *kshark_ctx) * open trace data files and before your application terminates. * * @param kshark_ctx: Optional input location for session context pointer. - * If it points to a context of a sessuin, that sessuin + * If it points to a context of a session, that session * will be deinitialize. If it points to NULL, it will * deinitialize the current session. */ @@ -478,25 +512,12 @@ void kshark_free(struct kshark_context *kshark_ctx) /* kshark_ctx_handler will be set to NULL below. */ } - tracecmd_filter_id_hash_free(kshark_ctx->show_task_filter); - tracecmd_filter_id_hash_free(kshark_ctx->hide_task_filter); - - tracecmd_filter_id_hash_free(kshark_ctx->show_event_filter); - tracecmd_filter_id_hash_free(kshark_ctx->hide_event_filter); + kshark_close_all(kshark_ctx); - tracecmd_filter_id_hash_free(kshark_ctx->show_cpu_filter); - tracecmd_filter_id_hash_free(kshark_ctx->hide_cpu_filter); + free(kshark_ctx->stream); - if (kshark_ctx->plugins) { - kshark_handle_plugins(kshark_ctx, KSHARK_PLUGIN_CLOSE); + if (kshark_ctx->plugins) kshark_free_plugin_list(kshark_ctx->plugins); - kshark_free_event_handler_list(kshark_ctx->event_handlers); - } - - kshark_free_task_list(kshark_ctx); - - if (seq.buffer) - trace_seq_destroy(&seq); if (kshark_ctx == kshark_context_handler) kshark_context_handler = NULL; @@ -879,6 +900,31 @@ int kshark_read_event_field_int(const struct kshark_entry *entry, return -EFAULT; } +/** + * @brief Get a summary of the entry. + * + * @param entry: Input location for an entry. + * + * @returns A summary text info. The user is responsible for freeing the + * outputted string. + */ +char *kshark_dump_entry(const struct kshark_entry *entry) +{ + struct kshark_generic_stream_interface *interface; + struct kshark_data_stream *stream = + kshark_get_stream_from_entry(entry); + + if (!stream) + return NULL; + + interface = stream->interface; + if (interface->type == KS_GENERIC_DATA_INTERFACE && + interface->dump_entry) + return interface->dump_entry(stream, entry); + + return NULL; +} + /** @brief Print the entry. */ void kshark_print_entry(const struct kshark_entry *entry) { @@ -1009,57 +1055,24 @@ kshark_add_task(struct kshark_context *kshark_ctx, int pid) * the loaded trace data file. * * @param kshark_ctx: Input location for context pointer. + * @param sd: Data stream identifier. * @param pids: Output location for the Pids of the tasks. The user is * responsible for freeing the elements of the outputted array. * * @returns The size of the outputted array of Pids in the case of success, * or a negative error code on failure. */ -ssize_t kshark_get_task_pids(struct kshark_context *kshark_ctx, int **pids) +ssize_t kshark_get_task_pids(struct kshark_context *kshark_ctx, int sd, + int **pids) { - size_t i, pid_count = 0, pid_size = KS_TASK_HASH_SIZE; - struct kshark_task_list *list; - int *temp_pids; - - *pids = calloc(pid_size, sizeof(int)); - if (!*pids) - goto fail; - - for (i = 0; i < KS_TASK_HASH_SIZE; ++i) { - list = kshark_ctx->tasks[i]; - while (list) { - (*pids)[pid_count] = list->pid; - list = list->next; - if (++pid_count >= pid_size) { - pid_size *= 2; - temp_pids = realloc(*pids, pid_size * sizeof(int)); - if (!temp_pids) { - goto fail; - } - *pids = temp_pids; - } - } - } - - if (pid_count) { - temp_pids = realloc(*pids, pid_count * sizeof(int)); - if (!temp_pids) - goto fail; - - /* Paranoid: In the unlikely case of shrinking *pids, realloc moves it */ - *pids = temp_pids; - } else { - free(*pids); - *pids = NULL; - } + struct kshark_data_stream *stream; - return pid_count; + stream = kshark_get_data_stream(kshark_ctx, sd); + if (!stream) + return -EBADF; -fail: - fprintf(stderr, "Failed to allocate memory for Task Pids.\n"); - free(*pids); - *pids = NULL; - return -ENOMEM; + *pids = kshark_hash_ids(stream->tasks); + return stream->tasks->count; } static bool filter_find(struct tracecmd_filter_id *filter, int pid, @@ -2071,76 +2084,6 @@ char* kshark_dump_custom_entry(struct kshark_context *kshark_ctx, return NULL; } -/** - * @brief Dump into a string the content of one entry. The function allocates - * a null terminated string and returns a pointer to this string. The - * user has to free the returned string. - * - * @param entry: A Kernel Shark entry to be printed. - * - * @returns The returned string contains a semicolon-separated list of data - * fields. - */ -char* kshark_dump_entry(const struct kshark_entry *entry) -{ - const char *event_name, *task, *lat, *info; - struct kshark_context *kshark_ctx; - char *temp_str, *entry_str; - int size = 0; - - kshark_ctx = NULL; - if (!kshark_instance(&kshark_ctx) || !init_thread_seq()) - return NULL; - - task = tep_data_comm_from_pid(kshark_ctx->pevent, entry->pid); - - if (entry->event_id >= 0) { - struct tep_event *event; - struct tep_record *data; - - data = tracecmd_read_at(kshark_ctx->handle, entry->offset, - NULL); - - event = tep_find_event(kshark_ctx->pevent, entry->event_id); - - event_name = event? event->name : "[UNKNOWN EVENT]"; - lat = get_latency(kshark_ctx->pevent, data); - - size = asprintf(&temp_str, "%" PRIu64 "; %s-%i; CPU %i; %s;", - entry->ts, - task, - entry->pid, - entry->cpu, - lat); - - info = get_info(kshark_ctx->pevent, data, event); - - if (size > 0) { - size = asprintf(&entry_str, "%s %s; %s; 0x%x", - temp_str, - event_name, - info, - entry->visible); - - free(temp_str); - } - - tracecmd_free_record(data); - if (size < 1) - entry_str = NULL; - } else { - switch (entry->event_id) { - case KS_EVENT_OVERFLOW: - entry_str = kshark_dump_custom_entry(kshark_ctx, entry, - missed_events_dump); - default: - entry_str = NULL; - } - } - - return entry_str; -} - /** * @brief Binary search inside a time-sorted array of kshark_entries. * diff --git a/src/libkshark.h b/src/libkshark.h index db3a5a7..adf5b40 100644 --- a/src/libkshark.h +++ b/src/libkshark.h @@ -342,6 +342,9 @@ static inline char *kshark_set_data_format(char *dest_format, return strncpy(dest_format, src_format, KS_DATA_FORMAT_SIZE - 1); } +/** Hard-coded default number of data streams available at initialization. */ +#define KS_DEFAULT_NUM_STREAMS 256 + /** Size of the task's hash table. */ #define KS_TASK_HASH_SHIFT 16 #define KS_TASK_HASH_SIZE (1 << KS_TASK_HASH_SHIFT) @@ -436,7 +439,9 @@ struct kshark_context { bool kshark_instance(struct kshark_context **kshark_ctx); -bool kshark_open(struct kshark_context *kshark_ctx, const char *file); +int kshark_open(struct kshark_context *kshark_ctx, const char *file); + +int kshark_stream_open(struct kshark_data_stream *stream, const char *file); int kshark_add_stream(struct kshark_context *kshark_ctx); @@ -456,9 +461,12 @@ ssize_t kshark_load_data_entries(struct kshark_context *kshark_ctx, ssize_t kshark_load_data_records(struct kshark_context *kshark_ctx, struct tep_record ***data_rows); -ssize_t kshark_get_task_pids(struct kshark_context *kshark_ctx, int **pids); +ssize_t kshark_get_task_pids(struct kshark_context *kshark_ctx, int sd, + int **pids); + +int kshark_close(struct kshark_context *kshark_ctx, int sd); -void kshark_close(struct kshark_context *kshark_ctx); +void kshark_close_all(struct kshark_context *kshark_ctx); void kshark_free(struct kshark_context *kshark_ctx); From patchwork Mon Jan 4 17:46:59 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11997167 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 387A9C43381 for ; Mon, 4 Jan 2021 17:49:15 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id EFB01216C4 for ; Mon, 4 Jan 2021 17:49:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726418AbhADRtO (ORCPT ); Mon, 4 Jan 2021 12:49:14 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34360 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726246AbhADRtO (ORCPT ); Mon, 4 Jan 2021 12:49:14 -0500 Received: from mail-ed1-x530.google.com (mail-ed1-x530.google.com [IPv6:2a00:1450:4864:20::530]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 3E6CDC0617B0 for ; Mon, 4 Jan 2021 09:47:57 -0800 (PST) Received: by mail-ed1-x530.google.com with SMTP id j16so28229883edr.0 for ; Mon, 04 Jan 2021 09:47:57 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=AvWDKS4WWcoFIb+YWLg8K2ermx+OI8H2TaVx6oNORWk=; b=V2XsJGPfLgZHfT15jJTBog+zgMh+uHZXTXuVDhvxoVRC316/uYoqMn3WWRB0Qnny3K UpGnPyp5a1RCHuUneww/wrYcNQ3prRFk8WyJo+pMX6Wwh9OeKiFAQ8wbSbcEuzjhwcGA GbTz3xwsHZeFY8kPNwDCBACN8jPLEwByY0nkw2lkA+Fb/osFj2naq0sNx02RQmWrNvQs iOW6agLL78mvApBDwgYpwGxMbSFs3bNP5M709G0g7cQLBIyqvcKLfLtP/QuwYZR/z9wN ULhgpamQ/j06vB7A+p2RZ3qoIcwZ1RlFO4wWm2iMiTD4jwTLJKIB54b7bY0IZFR0+2Ab 9hoQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=AvWDKS4WWcoFIb+YWLg8K2ermx+OI8H2TaVx6oNORWk=; b=XPuFeSd5N4Xv2ZpOLM9nNym5o4K2NtP7y2KHnq3wlrVPxxHATP+K4F4OuXJNo71THy bYMolVgsFVE5bwC1C9aIgXWON6B228RXtHXsm6Z2cxuEucxxNKE+IfxWLcR2D+AaYSkb /PHqcmPXqw8F/S/Oo1pA5RdyVO4g2qcwaJk2zeiCM5niWzQKenE/cr3i1H2tSsE6I2WQ 2aCY2LUfyUo9qgrPxoSwJpzrkjXeGiTJcSEBNyiSsFLcZae4TxHXDfGMrqvZo4H32aKT GgxOwEXjcFLROgXvHY9iiQlklN1cIJjZFSbdZccGD0X8177g+ddZLHQujGeBkLvK5e+Z Y7oQ== X-Gm-Message-State: AOAM5333vUQ1ZAS00EhLrwPfZ8jKzNi1VNfJiFGKe/MIMsntd5RneFfQ c/GNq6BIkLSUW0SB/+pIfsE= X-Google-Smtp-Source: ABdhPJwEVYZzB+G/AiDQZPd+UwYg89Od+pDPIuI4ye7FgBE2Pw87mKq+0qRG5xsH+rmQozWBCnyg5Q== X-Received: by 2002:aa7:d846:: with SMTP id f6mr71289976eds.55.1609782475596; Mon, 04 Jan 2021 09:47:55 -0800 (PST) Received: from localhost.localdomain ([95.87.199.238]) by smtp.gmail.com with ESMTPSA id l14sm44107750edq.35.2021.01.04.09.47.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 04 Jan 2021 09:47:55 -0800 (PST) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v8 19/44] kernel-shark: Remove dead code Date: Mon, 4 Jan 2021 19:46:59 +0200 Message-Id: <20210104174724.70404-20-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210104174724.70404-1-y.karadz@gmail.com> References: <20210104174724.70404-1-y.karadz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org Clean up the old readout code that is no longer used after switching to the implementation provided by the Data stream interface. Signed-off-by: Yordan Karadzhov (VMware) --- src/libkshark.c | 825 +----------------------------------------------- src/libkshark.h | 70 +--- 2 files changed, 8 insertions(+), 887 deletions(-) diff --git a/src/libkshark.c b/src/libkshark.c index d337f49..f5a5aeb 100644 --- a/src/libkshark.c +++ b/src/libkshark.c @@ -6,7 +6,7 @@ /** * @file libkshark.c - * @brief API for processing of FTRACE (trace-cmd) data. + * @brief API for processing of tracing data. */ /** Use GNU C Library. */ @@ -21,8 +21,6 @@ #include "libkshark.h" #include "libkshark-tepdata.h" -static __thread struct trace_seq seq; - static struct kshark_context *kshark_context_handler = NULL; static bool kshark_default_context(struct kshark_context **context) @@ -35,6 +33,8 @@ static bool kshark_default_context(struct kshark_context **context) kshark_ctx->stream = calloc(KS_DEFAULT_NUM_STREAMS, sizeof(*kshark_ctx->stream)); + kshark_ctx->stream_info.array_size = KS_DEFAULT_NUM_STREAMS; + kshark_ctx->stream_info.max_stream_id = -1; kshark_ctx->event_handlers = NULL; kshark_ctx->collections = NULL; @@ -65,14 +65,6 @@ static bool kshark_default_context(struct kshark_context **context) return true; } -static bool init_thread_seq(void) -{ - if (!seq.buffer) - trace_seq_init(&seq); - - return seq.buffer != NULL; -} - /** * @brief Initialize a kshark session. This function must be called before * calling any other kshark function. If the session has been @@ -109,9 +101,6 @@ bool kshark_instance(struct kshark_context **kshark_ctx) } } - if (!init_thread_seq()) - return false; - return true; } @@ -478,19 +467,10 @@ int kshark_close(struct kshark_context *kshark_ctx, int sd) */ void kshark_close_all(struct kshark_context *kshark_ctx) { - int i, *stream_ids, n_streams; - - stream_ids = kshark_all_streams(kshark_ctx); - - /* - * Get a copy of shark_ctx->n_streams befor you start closing. Be aware - * that kshark_close() will decrement shark_ctx->n_streams. - */ - n_streams = kshark_ctx->n_streams; - for (i = 0; i < n_streams; ++i) - kshark_close(kshark_ctx, stream_ids[i]); + int i; - free(stream_ids); + for (i = 0; i <= kshark_ctx->stream_info.max_stream_id; ++i) + kshark_close(kshark_ctx, i); } /** @@ -1014,42 +994,6 @@ ssize_t kshark_load_matrix(struct kshark_context *kshark_ctx, int sd, return -EFAULT; } -static struct kshark_task_list * -kshark_find_task(struct kshark_context *kshark_ctx, uint32_t key, int pid) -{ - struct kshark_task_list *list; - - for (list = kshark_ctx->tasks[key]; list; list = list->next) { - if (list->pid == pid) - return list; - } - - return NULL; -} - -static struct kshark_task_list * -kshark_add_task(struct kshark_context *kshark_ctx, int pid) -{ - struct kshark_task_list *list; - uint32_t key; - - key = tracecmd_quick_hash(pid, KS_TASK_HASH_SHIFT); - - list = kshark_find_task(kshark_ctx, key, pid); - if (list) - return list; - - list = malloc(sizeof(*list)); - if (!list) - return NULL; - - list->pid = pid; - list->next = kshark_ctx->tasks[key]; - kshark_ctx->tasks[key] = list; - - return list; -} - /** * @brief Get an array containing the Process Ids of all tasks presented in * the loaded trace data file. @@ -1217,7 +1161,7 @@ static void set_all_visible(uint16_t *v) { * are filtered-out. * WARNING: Do not use this function if the advanced filter is set. * Applying the advanced filter requires access to prevent_record, - * hence the data has to be reloaded using kshark_load_data_entries(). + * hence the data has to be reloaded using kshark_load_entries(). * * @param kshark_ctx: Input location for the session context pointer. * @param data: Input location for the trace data to be filtered. @@ -1278,400 +1222,6 @@ void kshark_clear_all_filters(struct kshark_context *kshark_ctx, set_all_visible(&data[i]->visible); } -static void kshark_set_entry_values(struct kshark_context *kshark_ctx, - struct tep_record *record, - struct kshark_entry *entry) -{ - /* Offset of the record */ - entry->offset = record->offset; - - /* CPU Id of the record */ - entry->cpu = record->cpu; - - /* Time stamp of the record */ - entry->ts = record->ts; - - /* Event Id of the record */ - entry->event_id = tep_data_type(kshark_ctx->pevent, record); - - /* - * Is visible mask. This default value means that the entry - * is visible everywhere. - */ - entry->visible = 0xFF; - - /* Process Id of the record */ - entry->pid = tep_data_pid(kshark_ctx->pevent, record); -} - -/** Prior time offset of the "missed_events" entry. */ -#define ME_ENTRY_TIME_SHIFT 10 - -static void missed_events_action(struct kshark_context *kshark_ctx, - struct tep_record *record, - struct kshark_entry *entry) -{ - /* - * Use the offset field of the entry to store the number of missed - * events. - */ - entry->offset = record->missed_events; - - entry->cpu = record->cpu; - - /* - * Position the "missed_events" entry a bit before (in time) - * the original record. - */ - entry->ts = record->ts - ME_ENTRY_TIME_SHIFT; - - /* All custom entries must have negative event Identifiers. */ - entry->event_id = KS_EVENT_OVERFLOW; - - entry->visible = 0xFF; - - entry->pid = tep_data_pid(kshark_ctx->pevent, record); -} - -static const char* missed_events_dump(struct kshark_context *kshark_ctx, - const struct kshark_entry *entry, - bool get_info) -{ - int size = 0; - static char *buffer; - - if (get_info) - size = asprintf(&buffer, "missed_events=%i", (int) entry->offset); - else - size = asprintf(&buffer, "missed_events"); - if (size > 0) - return buffer; - - return NULL; -} - -/** - * rec_list is used to pass the data to the load functions. - * The rec_list will contain the list of entries from the source, - * and will be a link list of per CPU entries. - */ -struct rec_list { - union { - /* Used by kshark_load_data_records */ - struct { - /** next pointer, matches entry->next */ - struct rec_list *next; - /** pointer to the raw record data */ - struct tep_record *rec; - }; - /** entry - Used for kshark_load_data_entries() */ - struct kshark_entry entry; - }; -}; - -/** - * rec_type defines what type of rec_list is being used. - */ -enum rec_type { - REC_RECORD, - REC_ENTRY, -}; - -static void free_rec_list(struct rec_list **rec_list, int n_cpus, - enum rec_type type) -{ - struct rec_list *temp_rec; - int cpu; - - for (cpu = 0; cpu < n_cpus; ++cpu) { - while (rec_list[cpu]) { - temp_rec = rec_list[cpu]; - rec_list[cpu] = temp_rec->next; - if (type == REC_RECORD) - tracecmd_free_record(temp_rec->rec); - free(temp_rec); - } - } - free(rec_list); -} - -static ssize_t get_records(struct kshark_context *kshark_ctx, - struct rec_list ***rec_list, enum rec_type type) -{ - struct kshark_event_handler *evt_handler; - struct tep_event_filter *adv_filter; - struct kshark_task_list *task; - struct tep_record *rec; - struct rec_list **temp_next; - struct rec_list **cpu_list; - struct rec_list *temp_rec; - size_t count, total = 0; - int n_cpus; - int pid; - int cpu; - - n_cpus = tep_get_cpus(kshark_ctx->pevent); - cpu_list = calloc(n_cpus, sizeof(*cpu_list)); - if (!cpu_list) - return -ENOMEM; - - /* Just to shorten the name */ - if (type == REC_ENTRY) - adv_filter = kshark_ctx->advanced_event_filter; - - for (cpu = 0; cpu < n_cpus; ++cpu) { - count = 0; - cpu_list[cpu] = NULL; - temp_next = &cpu_list[cpu]; - - rec = tracecmd_read_cpu_first(kshark_ctx->handle, cpu); - while (rec) { - *temp_next = temp_rec = calloc(1, sizeof(*temp_rec)); - if (!temp_rec) - goto fail; - - temp_rec->next = NULL; - - switch (type) { - case REC_RECORD: - temp_rec->rec = rec; - pid = tep_data_pid(kshark_ctx->pevent, rec); - break; - case REC_ENTRY: { - struct kshark_entry *entry; - int ret; - - if (rec->missed_events) { - /* - * Insert a custom "missed_events" entry just - * befor this record. - */ - entry = &temp_rec->entry; - missed_events_action(kshark_ctx, rec, entry); - - temp_next = &temp_rec->next; - ++count; - - /* Now allocate a new rec_list node and comtinue. */ - *temp_next = temp_rec = calloc(1, sizeof(*temp_rec)); - } - - entry = &temp_rec->entry; - kshark_set_entry_values(kshark_ctx, rec, entry); - - /* Execute all plugin-provided actions (if any). */ - evt_handler = kshark_ctx->event_handlers; - while ((evt_handler = kshark_find_event_handler(evt_handler, - entry->event_id))) { - evt_handler->event_func(kshark_ctx, rec, entry); - evt_handler = evt_handler->next; - entry->visible &= ~KS_PLUGIN_UNTOUCHED_MASK; - } - - pid = entry->pid; - /* Apply event filtering. */ - ret = FILTER_MATCH; - if (adv_filter->filters) - ret = tep_filter_match(adv_filter, rec); - - if (!kshark_show_event(kshark_ctx, entry->event_id) || - ret != FILTER_MATCH) { - unset_event_filter_flag(kshark_ctx, entry); - } - - /* Apply CPU filtering. */ - if (!kshark_show_cpu(kshark_ctx, entry->pid)) { - entry->visible &= ~kshark_ctx->filter_mask; - } - - /* Apply task filtering. */ - if (!kshark_show_task(kshark_ctx, entry->pid)) { - entry->visible &= ~kshark_ctx->filter_mask; - } - tracecmd_free_record(rec); - break; - } /* REC_ENTRY */ - } - - task = kshark_add_task(kshark_ctx, pid); - if (!task) { - tracecmd_free_record(rec); - goto fail; - } - - temp_next = &temp_rec->next; - - ++count; - rec = tracecmd_read_data(kshark_ctx->handle, cpu); - } - - total += count; - } - - *rec_list = cpu_list; - return total; - - fail: - free_rec_list(cpu_list, n_cpus, type); - return -ENOMEM; -} - -static int pick_next_cpu(struct rec_list **rec_list, int n_cpus, - enum rec_type type) -{ - uint64_t ts = 0; - uint64_t rec_ts; - int next_cpu = -1; - int cpu; - - for (cpu = 0; cpu < n_cpus; ++cpu) { - if (!rec_list[cpu]) - continue; - - switch (type) { - case REC_RECORD: - rec_ts = rec_list[cpu]->rec->ts; - break; - case REC_ENTRY: - rec_ts = rec_list[cpu]->entry.ts; - break; - } - if (!ts || rec_ts < ts) { - ts = rec_ts; - next_cpu = cpu; - } - } - - return next_cpu; -} - -/** - * @brief Load the content of the trace data file into an array of - * kshark_entries. This function provides an abstraction of the - * entries from the raw data that is read, however the "latency" - * and the "info" fields can be accessed only via the offset - * into the file. This makes the access to these two fields much - * slower. - * If one or more filters are set, the "visible" fields of each entry - * is updated according to the criteria provided by the filters. The - * field "filter_mask" of the session's context is used to control the - * level of visibility/invisibility of the filtered entries. - * - * @param kshark_ctx: Input location for context pointer. - * @param data_rows: Output location for the trace data. The user is - * responsible for freeing the elements of the outputted - * array. - * - * @returns The size of the outputted data in the case of success, or a - * negative error code on failure. - */ -ssize_t kshark_load_data_entries(struct kshark_context *kshark_ctx, - struct kshark_entry ***data_rows) -{ - struct kshark_entry **rows; - struct rec_list **rec_list; - enum rec_type type = REC_ENTRY; - ssize_t count, total = 0; - int n_cpus; - - if (*data_rows) - free(*data_rows); - - total = get_records(kshark_ctx, &rec_list, type); - if (total < 0) - goto fail; - - n_cpus = tep_get_cpus(kshark_ctx->pevent); - - rows = calloc(total, sizeof(struct kshark_entry *)); - if (!rows) - goto fail_free; - - for (count = 0; count < total; count++) { - int next_cpu; - - next_cpu = pick_next_cpu(rec_list, n_cpus, type); - - if (next_cpu >= 0) { - rows[count] = &rec_list[next_cpu]->entry; - rec_list[next_cpu] = rec_list[next_cpu]->next; - } - } - - free_rec_list(rec_list, n_cpus, type); - *data_rows = rows; - return total; - - fail_free: - free_rec_list(rec_list, n_cpus, type); - - fail: - fprintf(stderr, "Failed to allocate memory during data loading.\n"); - return -ENOMEM; -} - -/** - * @brief Load the content of the trace data file into an array of - * tep_records. Use this function only if you need fast access - * to all fields of the record. - * - * @param kshark_ctx: Input location for the session context pointer. - * @param data_rows: Output location for the trace data. Use tracecmd_free_record() - * to free the elements of the outputted array. - * - * @returns The size of the outputted data in the case of success, or a - * negative error code on failure. - */ -ssize_t kshark_load_data_records(struct kshark_context *kshark_ctx, - struct tep_record ***data_rows) -{ - struct tep_record **rows; - struct tep_record *rec; - struct rec_list **rec_list; - struct rec_list *temp_rec; - enum rec_type type = REC_RECORD; - ssize_t count, total = 0; - int n_cpus; - - total = get_records(kshark_ctx, &rec_list, type); - if (total < 0) - goto fail; - - n_cpus = tep_get_cpus(kshark_ctx->pevent); - - rows = calloc(total, sizeof(struct tep_record *)); - if (!rows) - goto fail_free; - - for (count = 0; count < total; count++) { - int next_cpu; - - next_cpu = pick_next_cpu(rec_list, n_cpus, type); - - if (next_cpu >= 0) { - rec = rec_list[next_cpu]->rec; - rows[count] = rec; - - temp_rec = rec_list[next_cpu]; - rec_list[next_cpu] = rec_list[next_cpu]->next; - free(temp_rec); - /* The record is still referenced in rows */ - } - } - - /* There should be no records left in rec_list */ - free_rec_list(rec_list, n_cpus, type); - *data_rows = rows; - return total; - - fail_free: - free_rec_list(rec_list, n_cpus, type); - - fail: - fprintf(stderr, "Failed to allocate memory during data loading.\n"); - return -ENOMEM; -} - static inline void free_ptr(void *ptr) { if (ptr) @@ -1743,294 +1293,6 @@ bool kshark_data_matrix_alloc(size_t n_rows, int16_t **event_array, return false; } -static const char *get_latency(struct tep_handle *pe, - struct tep_record *record) -{ - if (!record) - return NULL; - - trace_seq_reset(&seq); - tep_print_event(pe, &seq, record, "%s", TEP_PRINT_LATENCY); - return seq.buffer; -} - -static const char *get_info(struct tep_handle *pe, - struct tep_record *record, - struct tep_event *event) -{ - char *pos; - - if (!record || !event) - return NULL; - - trace_seq_reset(&seq); - tep_print_event(pe, &seq, record, "%s", TEP_PRINT_INFO); - - /* - * The event info string contains a trailing newline. - * Remove this newline. - */ - if ((pos = strchr(seq.buffer, '\n')) != NULL) - *pos = '\0'; - - return seq.buffer; -} - -/** - * @brief This function allows for an easy access to the original value of the - * Process Id as recorded in the tep_record object. The record is read - * from the file only in the case of an entry being touched by a plugin. - * Be aware that using the kshark_get_X_easy functions can be - * inefficient if you need an access to more than one of the data fields - * of the record. - * - * @param entry: Input location for the KernelShark entry. - * - * @returns The original value of the Process Id as recorded in the - * tep_record object on success, otherwise negative error code. - */ -int kshark_get_pid_easy(struct kshark_entry *entry) -{ - struct kshark_context *kshark_ctx = NULL; - struct tep_record *data; - int pid; - - if (!kshark_instance(&kshark_ctx)) - return -ENODEV; - - if (entry->visible & KS_PLUGIN_UNTOUCHED_MASK) { - pid = entry->pid; - } else { - /* - * The entry has been touched by a plugin callback function. - * Because of this we do not trust the value of "entry->pid". - * - * Currently the data reading operations are not thread-safe. - * Use a mutex to protect the access. - */ - pthread_mutex_lock(&kshark_ctx->input_mutex); - - data = tracecmd_read_at(kshark_ctx->handle, entry->offset, - NULL); - pid = tep_data_pid(kshark_ctx->pevent, data); - tracecmd_free_record(data); - - pthread_mutex_unlock(&kshark_ctx->input_mutex); - } - - return pid; -} - -/** - * @brief This function allows for an easy access to the original value of the - * Task name as recorded in the tep_record object. The record is read - * from the file only in the case of an entry being touched by a plugin. - * Be aware that using the kshark_get_X_easy functions can be - * inefficient if you need an access to more than one of the data fields - * of the record. - * - * @param entry: Input location for the KernelShark entry. - * - * @returns The original name of the task, retrieved from the Process Id - * recorded in the tep_record object on success, otherwise NULL. - */ -const char *kshark_get_task_easy(struct kshark_entry *entry) -{ - struct kshark_context *kshark_ctx = NULL; - int pid = kshark_get_pid_easy(entry); - - if (pid < 0) - return NULL; - - kshark_instance(&kshark_ctx); - return tep_data_comm_from_pid(kshark_ctx->pevent, pid); -} - -/** - * @brief This function allows for an easy access to the latency information - * recorded in the tep_record object. The record is read from the file - * using the offset field of kshark_entry. Be aware that using the - * kshark_get_X_easy functions can be inefficient if you need an access - * to more than one of the data fields of the record. - * - * @param entry: Input location for the KernelShark entry. - * - * @returns On success the function returns a string showing the latency - * information, coded into 5 fields: - * interrupts disabled, need rescheduling, hard/soft interrupt, - * preempt count and lock depth. On failure it returns NULL. - */ -const char *kshark_get_latency_easy(struct kshark_entry *entry) -{ - struct kshark_context *kshark_ctx = NULL; - struct tep_record *data; - const char *lat; - - if (!kshark_instance(&kshark_ctx)) - return NULL; - - if (entry->event_id < 0) - return NULL; - - /* - * Currently the data reading operations are not thread-safe. - * Use a mutex to protect the access. - */ - pthread_mutex_lock(&kshark_ctx->input_mutex); - - data = tracecmd_read_at(kshark_ctx->handle, entry->offset, NULL); - lat = get_latency(kshark_ctx->pevent, data); - tracecmd_free_record(data); - - pthread_mutex_unlock(&kshark_ctx->input_mutex); - - return lat; -} - -/** - * @brief This function allows for an easy access to the original value of the - * Event Id as recorded in the tep_record object. The record is read - * from the file only in the case of an entry being touched by a plugin. - * Be aware that using the kshark_get_X_easy functions can be - * inefficient if you need an access to more than one of the data fields - * of the record. - * - * @param entry: Input location for the KernelShark entry. - * - * @returns The original value of the Event Id as recorded in the - * tep_record object on success, otherwise negative error code. - */ -int kshark_get_event_id_easy(struct kshark_entry *entry) -{ - struct kshark_context *kshark_ctx = NULL; - struct tep_record *data; - int event_id; - - if (!kshark_instance(&kshark_ctx)) - return -ENODEV; - - if (entry->visible & KS_PLUGIN_UNTOUCHED_MASK) { - event_id = entry->event_id; - } else { - /* - * The entry has been touched by a plugin callback function. - * Because of this we do not trust the value of - * "entry->event_id". - * - * Currently the data reading operations are not thread-safe. - * Use a mutex to protect the access. - */ - pthread_mutex_lock(&kshark_ctx->input_mutex); - - data = tracecmd_read_at(kshark_ctx->handle, entry->offset, - NULL); - event_id = tep_data_type(kshark_ctx->pevent, data); - tracecmd_free_record(data); - - pthread_mutex_unlock(&kshark_ctx->input_mutex); - } - - return (event_id == -1)? -EFAULT : event_id; -} - -/** - * @brief This function allows for an easy access to the original name of the - * trace event as recorded in the tep_record object. The record is read - * from the file only in the case of an entry being touched by a plugin. - * Be aware that using the kshark_get_X_easy functions can be - * inefficient if you need an access to more than one of the data fields - * of the record. - * - * @param entry: Input location for the KernelShark entry. - * - * @returns The mane of the trace event recorded in the tep_record object on - * success, otherwise "[UNKNOWN EVENT]" or NULL. - */ -const char *kshark_get_event_name_easy(struct kshark_entry *entry) -{ - struct kshark_context *kshark_ctx = NULL; - struct tep_event *event; - - int event_id = kshark_get_event_id_easy(entry); - if (event_id == -EFAULT) - return NULL; - - kshark_instance(&kshark_ctx); - - if (event_id < 0) { - switch (event_id) { - case KS_EVENT_OVERFLOW: - return missed_events_dump(kshark_ctx, entry, false); - default: - return NULL; - } - } - - /* - * Currently the data reading operations are not thread-safe. - * Use a mutex to protect the access. - */ - pthread_mutex_lock(&kshark_ctx->input_mutex); - event = tep_find_event(kshark_ctx->pevent, event_id); - pthread_mutex_unlock(&kshark_ctx->input_mutex); - - if (event) - return event->name; - - return "[UNKNOWN EVENT]"; -} - -/** - * @brief This function allows for an easy access to the tep_record's info - * streang. The record is read from the file using the offset field of - * kshark_entry. Be aware that using the kshark_get_X_easy functions can - * be inefficient if you need an access to more than one of the data - * fields of the record. - * - * @param entry: Input location for the KernelShark entry. - * - * @returns A string showing the data output of the trace event on success, - * otherwise NULL. - */ -const char *kshark_get_info_easy(struct kshark_entry *entry) -{ - struct kshark_context *kshark_ctx = NULL; - struct tep_event *event; - struct tep_record *data; - const char *info = NULL; - int event_id; - - if (!kshark_instance(&kshark_ctx)) - return NULL; - - if (entry->event_id < 0) { - switch (entry->event_id) { - case KS_EVENT_OVERFLOW: - return missed_events_dump(kshark_ctx, entry, true); - default: - return NULL; - } - } - - /* - * Currently the data reading operations are not thread-safe. - * Use a mutex to protect the access. - */ - pthread_mutex_lock(&kshark_ctx->input_mutex); - - data = tracecmd_read_at(kshark_ctx->handle, entry->offset, NULL); - event_id = tep_data_type(kshark_ctx->pevent, data); - event = tep_find_event(kshark_ctx->pevent, event_id); - if (event) - info = get_info(kshark_ctx->pevent, data, event); - - tracecmd_free_record(data); - - pthread_mutex_unlock(&kshark_ctx->input_mutex); - - return info; -} - /** * @brief Convert the timestamp of the trace record (nanosecond precision) into * seconds and microseconds. @@ -2047,43 +1309,6 @@ void kshark_convert_nano(uint64_t time, uint64_t *sec, uint64_t *usec) *usec = (time - s * 1000000000ULL) / 1000; } -/** - * @brief Dump into a string the content a custom entry. The function allocates - * a null terminated string and returns a pointer to this string. - * - * @param kshark_ctx: Input location for the session context pointer. - * @param entry: A Kernel Shark entry to be printed. - * @param info_func: - * - * @returns The returned string contains a semicolon-separated list of data - * fields. The user has to free the returned string. - */ -char* kshark_dump_custom_entry(struct kshark_context *kshark_ctx, - const struct kshark_entry *entry, - kshark_custom_info_func info_func) -{ - const char *event_name, *task, *info; - char *entry_str; - int size = 0; - - task = tep_data_comm_from_pid(kshark_ctx->pevent, entry->pid); - event_name = info_func(kshark_ctx, entry, false); - info = info_func(kshark_ctx, entry, true); - - size = asprintf(&entry_str, "%" PRIu64 "; %s-%i; CPU %i; ; %s; %s", - entry->ts, - task, - entry->pid, - entry->cpu, - event_name, - info); - - if (size > 0) - return entry_str; - - return NULL; -} - /** * @brief Binary search inside a time-sorted array of kshark_entries. * @@ -2120,42 +1345,6 @@ ssize_t kshark_find_entry_by_time(uint64_t time, return h; } -/** - * @brief Binary search inside a time-sorted array of tep_records. - * - * @param time: The value of time to search for. - * @param data: Input location for the trace data. - * @param l: Array index specifying the lower edge of the range to search in. - * @param h: Array index specifying the upper edge of the range to search in. - * - * @returns On success, the first tep_record inside the range, having a - timestamp equal or bigger than "time". - If all entries inside the range have timestamps greater than "time" - the function returns BSEARCH_ALL_GREATER (negative value). - If all entries inside the range have timestamps smaller than "time" - the function returns BSEARCH_ALL_SMALLER (negative value). - */ -ssize_t kshark_find_record_by_time(uint64_t time, - struct tep_record **data, - size_t l, size_t h) -{ - size_t mid; - - if (data[l]->ts > time) - return BSEARCH_ALL_GREATER; - - if (data[h]->ts < time) - return BSEARCH_ALL_SMALLER; - - /* - * After executing the BSEARCH macro, "l" will be the index of the last - * record having timestamp < time and "h" will be the index of the - * first record having timestamp >= time. - */ - BSEARCH(h, l, data[mid]->ts < time); - return h; -} - /** * @brief Simple Pid matching function to be user for data requests. * diff --git a/src/libkshark.h b/src/libkshark.h index adf5b40..06d7178 100644 --- a/src/libkshark.h +++ b/src/libkshark.h @@ -6,7 +6,7 @@ /** * @file libkshark.h - * @brief API for processing of FTRACE (trace-cmd) data. + * @brief API for processing of tracing data. */ #ifndef _LIB_KSHARK_H @@ -27,8 +27,6 @@ extern "C" { // trace-cmd #include "trace-cmd/trace-cmd.h" #include "trace-filter-hash.h" -#include "traceevent/event-parse.h" -#include "tracefs/tracefs.h" // KernelShark #include "libkshark-plugin.h" @@ -345,19 +343,6 @@ static inline char *kshark_set_data_format(char *dest_format, /** Hard-coded default number of data streams available at initialization. */ #define KS_DEFAULT_NUM_STREAMS 256 -/** Size of the task's hash table. */ -#define KS_TASK_HASH_SHIFT 16 -#define KS_TASK_HASH_SIZE (1 << KS_TASK_HASH_SHIFT) - -/** Linked list of tasks. */ -struct kshark_task_list { - /** Pointer to the next task's PID. */ - struct kshark_task_list *next; - - /** PID of a task. */ - int pid; -}; - /** * Structure representing the parameters of the stream descriptor array owned * by the kshark session. @@ -384,15 +369,6 @@ struct kshark_context { /** Parameters of the stream descriptor array. */ struct kshark_stream_array_descriptor stream_info; - /** Input handle for the trace data file. */ - struct tracecmd_input *handle; - - /** Page event used to parse the page. */ - struct tep_handle *pevent; - - /** Hash table of task PIDs. */ - struct kshark_task_list *tasks[KS_TASK_HASH_SIZE]; - /** A mutex, used to protect the access to the input file. */ pthread_mutex_t input_mutex; @@ -455,12 +431,6 @@ kshark_get_stream_from_entry(const struct kshark_entry *entry); int *kshark_all_streams(struct kshark_context *kshark_ctx); -ssize_t kshark_load_data_entries(struct kshark_context *kshark_ctx, - struct kshark_entry ***data_rows); - -ssize_t kshark_load_data_records(struct kshark_context *kshark_ctx, - struct tep_record ***data_rows); - ssize_t kshark_get_task_pids(struct kshark_context *kshark_ctx, int sd, int **pids); @@ -474,18 +444,6 @@ char *kshark_comm_from_pid(int sd, int pid); char *kshark_event_from_id(int sd, int event_id); -int kshark_get_pid_easy(struct kshark_entry *entry); - -const char *kshark_get_task_easy(struct kshark_entry *entry); - -const char *kshark_get_latency_easy(struct kshark_entry *entry); - -int kshark_get_event_id_easy(struct kshark_entry *entry); - -const char *kshark_get_event_name_easy(struct kshark_entry *entry); - -const char *kshark_get_info_easy(struct kshark_entry *entry); - void kshark_convert_nano(uint64_t time, uint64_t *sec, uint64_t *usec); char* kshark_dump_entry(const struct kshark_entry *entry); @@ -532,18 +490,6 @@ ssize_t kshark_load_matrix(struct kshark_context *kshark_ctx, int sd, int64_t **offset_array, int64_t **ts_array); -/** - * Custom entry info function type. To be user for dumping info for custom - * KernelShark entryes. - */ -typedef const char *(kshark_custom_info_func)(struct kshark_context *, - const struct kshark_entry *, - bool); - -char* kshark_dump_custom_entry(struct kshark_context *kshark_ctx, - const struct kshark_entry *entry, - kshark_custom_info_func info_func); - /** Bit masks used to control the visibility of the entry after filtering. */ enum kshark_filter_masks { /** @@ -666,10 +612,6 @@ ssize_t kshark_find_entry_by_time(uint64_t time, struct kshark_entry **data_rows, size_t l, size_t h); -ssize_t kshark_find_record_by_time(uint64_t time, - struct tep_record **data_rows, - size_t l, size_t h); - bool kshark_match_pid(struct kshark_context *kshark_ctx, struct kshark_entry *e, int pid); @@ -954,16 +896,6 @@ bool kshark_export_adv_filters(struct kshark_context *kshark_ctx, bool kshark_import_adv_filters(struct kshark_context *kshark_ctx, struct kshark_config_doc *conf); -bool kshark_export_event_filter(struct tep_handle *pevent, - struct tracecmd_filter_id *filter, - const char *filter_name, - struct kshark_config_doc *conf); - -int kshark_import_event_filter(struct tep_handle *pevent, - struct tracecmd_filter_id *filter, - const char *filter_name, - struct kshark_config_doc *conf); - bool kshark_export_user_mask(struct kshark_context *kshark_ctx, struct kshark_config_doc **conf); From patchwork Mon Jan 4 17:47:00 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11997207 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id EFF96C433E6 for ; Mon, 4 Jan 2021 17:49:39 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id C15C620715 for ; Mon, 4 Jan 2021 17:49:39 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727391AbhADRtj (ORCPT ); Mon, 4 Jan 2021 12:49:39 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34480 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727335AbhADRtj (ORCPT ); Mon, 4 Jan 2021 12:49:39 -0500 Received: from mail-ed1-x52e.google.com (mail-ed1-x52e.google.com [IPv6:2a00:1450:4864:20::52e]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id CD7EEC0617B1 for ; Mon, 4 Jan 2021 09:47:58 -0800 (PST) Received: by mail-ed1-x52e.google.com with SMTP id u19so28248347edx.2 for ; Mon, 04 Jan 2021 09:47:58 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=cpy1KdqXftX7ZNiYHQyJ20TQKfbE0Y7ZsMH87V6Cmcs=; b=a1JtJy5jAzPhWQKwmJ6XVakRBt1qYs7tVlr9uZjvVExlPrAeg+9iKffIiHdd4QYPT5 bv7x+3OY6NxFqK/wnt2RmKKwY0ohuS338SlMO6RB+4zaIyqoZyeusDNrfYerijQyKZBM KyzLBd+RnM7MoV02/QwNMX51EjMNRURdr9G0T4/du10TJnfE4Xhn21ZINd46oeydihpH hGQ9w5HhMDDj/GDsg7JxfcVvxqbfai5O5o4tP5irGmpVs0RFvy8Pedh1q8lbK/jAANRa vLehoL+QPMvGkbypZr71jFdfBxLtTInlp/iVQCOCEpKxuWYkgxpJ7LNUxh1NqoyQSl2S 7ZSw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=cpy1KdqXftX7ZNiYHQyJ20TQKfbE0Y7ZsMH87V6Cmcs=; b=kk6ihBT4iYhu5f7slB9sy27SBVt83EkJoBKbN9D5QzBZJmKCqgGepb84J5YJ4TbfoW UdWB+a6F32xZNpolVb74La4Vy3R5yZzYPiTVRDNQohalWtNVk/7tLAULRRa5S88NWWyC O3jjTDRNWcgfYycO7x+u3eYpSz6Cm0UokRZuA1Et6kxXqD6jYZHvEdUWraq+gfuMAa4l nDzYTBm0MQEMs3v0XT7yQy8wwAwM2/u84bWCUC28WxzplVl/WxkmQIyy6XIqcruKCe+t /L2GaShTz/pf9urSOZumjmL1zSP6+2Uuuuj7naAhgTNRfTgBbJ7TG52UfZhcNpZEdPi2 fBpQ== X-Gm-Message-State: AOAM531QEQvRy2A2f0zzIGWRFFwtHlYIu5CeqPbL1FR+AkQxgJSoVIde Z1HL8Sv4zVhn+DzeEUCQ//I= X-Google-Smtp-Source: ABdhPJyja04EaWzrBGY4sffcHqtBwYvdcFut/qCwKnze7/7kJ8ZDw8kQWWwsEBGllEb0CcADWh9tZA== X-Received: by 2002:a50:b905:: with SMTP id m5mr19550270ede.292.1609782477147; Mon, 04 Jan 2021 09:47:57 -0800 (PST) Received: from localhost.localdomain ([95.87.199.238]) by smtp.gmail.com with ESMTPSA id l14sm44107750edq.35.2021.01.04.09.47.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 04 Jan 2021 09:47:56 -0800 (PST) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v8 20/44] kernel-shark: Redesign the plugin interface Date: Mon, 4 Jan 2021 19:47:00 +0200 Message-Id: <20210104174724.70404-21-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210104174724.70404-1-y.karadz@gmail.com> References: <20210104174724.70404-1-y.karadz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org This patch can be treated as an almost complete rewrite of the way the C API of KernelShark deals with plugins. First of all, the two pluggable user actions (one executed during data loading and another executed during plotting) have separate handlers. This way the user can register only one of the two callback functions, if this is what is needed. The second substantial change is that instead of having a single interface for loading the plugin, we now have 3 different interfaces. The one that exists in version 1 of KernelShark is now renamed to Data Processing Interface (dpi). The first new interface for loading can be used to register user provided implementation of the Data stream readout and is called Data Readout Interface (dri). Via this plugin loading interface the user can open trace data having an arbitrary format. In order to make this possible the user has to provide a plugin that contains an implementation of the data readout methods defined by the kshark_data_stream_interface and to register all those methods. The second new plugin loading interface is called Control interface and can be used to provide the plugin with an access to the GUI's Main window object. Via this interface the plugin can became capable to modify the GUI. Such a modification for example can be to add new dialog to the menus or to change the state of the GUI (to select entry 1with the marker, to zoom, ...). Signed-off-by: Yordan Karadzhov (VMware) --- src/libkshark-plugin.c | 658 +++++++++++++++++++++++++++++++++++------ src/libkshark-plugin.h | 294 ++++++++++++++---- src/libkshark.c | 48 ++- src/libkshark.h | 28 +- 4 files changed, 866 insertions(+), 162 deletions(-) diff --git a/src/libkshark-plugin.c b/src/libkshark-plugin.c index 4b21392..74a5862 100644 --- a/src/libkshark-plugin.c +++ b/src/libkshark-plugin.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: LGPL-2.1 /* - * Copyright (C) 2017 VMware Inc, Yordan Karadzhov + * Copyright (C) 2017 VMware Inc, Yordan Karadzhov (VMware) */ /** @@ -13,8 +13,7 @@ #ifndef _GNU_SOURCE /** Use GNU C Library. */ #define _GNU_SOURCE - -#endif +#endif // _GNU_SOURCE #include #include @@ -25,25 +24,39 @@ // KernelShark #include "libkshark-plugin.h" +#include "libkshark-tepdata.h" #include "libkshark.h" -static struct kshark_event_handler * -gui_event_handler_alloc(int event_id, - kshark_plugin_event_handler_func evt_func, - kshark_plugin_draw_handler_func dw_func) +static struct kshark_event_proc_handler * +data_event_handler_alloc(int event_id, + kshark_plugin_event_handler_func evt_func) { - struct kshark_event_handler *handler = malloc(sizeof(*handler)); + struct kshark_event_proc_handler *handler = malloc(sizeof(*handler)); if (!handler) { - fprintf(stderr, - "failed to allocate memory for gui eventhandler"); + fputs("failed to allocate memory for event handler\n", stderr); return NULL; } handler->next = NULL; handler->id = event_id; handler->event_func = evt_func; - handler->draw_func = dw_func; + + return handler; +} + +static struct kshark_draw_handler * +data_draw_handler_alloc(kshark_plugin_draw_handler_func draw_func) +{ + struct kshark_draw_handler *handler = malloc(sizeof(*handler)); + + if (!handler) { + fputs("failed to allocate memory for draw handler\n", stderr); + return NULL; + } + + handler->next = NULL; + handler->draw_func = draw_func; return handler; } @@ -55,8 +68,8 @@ gui_event_handler_alloc(int event_id, * @param handlers: Input location for the Event handler list. * @param event_id: Event Id to search for. */ -struct kshark_event_handler * -kshark_find_event_handler(struct kshark_event_handler *handlers, int event_id) +struct kshark_event_proc_handler * +kshark_find_event_handler(struct kshark_event_proc_handler *handlers, int event_id) { for (; handlers; handlers = handlers->next) if (handlers->id == event_id) @@ -68,26 +81,25 @@ kshark_find_event_handler(struct kshark_event_handler *handlers, int event_id) /** * @brief Add new event handler to an existing list of handlers. * - * @param handlers: Input location for the Event handler list. + * @param stream: Input location for a Trace data stream pointer. * @param event_id: Event Id. * @param evt_func: Input location for an Event action provided by the plugin. - * @param dw_func: Input location for a Draw action provided by the plugin. * * @returns Zero on success, or a negative error code on failure. */ -int kshark_register_event_handler(struct kshark_event_handler **handlers, +int kshark_register_event_handler(struct kshark_data_stream *stream, int event_id, - kshark_plugin_event_handler_func evt_func, - kshark_plugin_draw_handler_func dw_func) + kshark_plugin_event_handler_func evt_func) { - struct kshark_event_handler *handler = - gui_event_handler_alloc(event_id, evt_func, dw_func); + struct kshark_event_proc_handler *handler = + data_event_handler_alloc(event_id, evt_func); if(!handler) return -ENOMEM; - handler->next = *handlers; - *handlers = handler; + handler->next = stream->event_handlers; + stream->event_handlers = handler; + return 0; } @@ -95,30 +107,29 @@ int kshark_register_event_handler(struct kshark_event_handler **handlers, * @brief Search the list for a specific plugin handle. If such a plugin handle * exists, unregister (remove and free) this handle from the list. * - * @param handlers: Input location for the Event handler list. + * @param stream: Input location for a Trace data stream pointer. * @param event_id: Event Id of the plugin handler to be unregistered. - * @param evt_func: Event action function of the handler to be unregistered. - * @param dw_func: Draw action function of the handler to be unregistered. + * @param evt_func: Event action function to be unregistered. */ -void kshark_unregister_event_handler(struct kshark_event_handler **handlers, - int event_id, - kshark_plugin_event_handler_func evt_func, - kshark_plugin_draw_handler_func dw_func) +int kshark_unregister_event_handler(struct kshark_data_stream *stream, + int event_id, + kshark_plugin_event_handler_func evt_func) { - struct kshark_event_handler **last; + struct kshark_event_proc_handler **last; - for (last = handlers; *last; last = &(*last)->next) { + for (last = &stream->event_handlers; *last; last = &(*last)->next) { if ((*last)->id == event_id && - (*last)->event_func == evt_func && - (*last)->draw_func == dw_func) { - struct kshark_event_handler *this_handler; + (*last)->event_func == evt_func) { + struct kshark_event_proc_handler *this_handler; this_handler = *last; *last = this_handler->next; free(this_handler); - return; + return 0; } } + + return -EFAULT; } /** @@ -126,9 +137,9 @@ void kshark_unregister_event_handler(struct kshark_event_handler **handlers, * * @param handlers: Input location for the Event handler list. */ -void kshark_free_event_handler_list(struct kshark_event_handler *handlers) +void kshark_free_event_handler_list(struct kshark_event_proc_handler *handlers) { - struct kshark_event_handler *last; + struct kshark_event_proc_handler *last; while (handlers) { last = handlers; @@ -137,98 +148,255 @@ void kshark_free_event_handler_list(struct kshark_event_handler *handlers) } } +/** + * @brief Add new event handler to an existing list of handlers. + * + * @param stream: Input location for a Trace data stream pointer. + * @param draw_func: Input location for a Draw action provided by the plugin. + * + * @returns Zero on success, or a negative error code on failure. + */ +int kshark_register_draw_handler(struct kshark_data_stream *stream, + kshark_plugin_draw_handler_func draw_func) +{ + struct kshark_draw_handler *handler = data_draw_handler_alloc(draw_func); + + if(!handler) + return -ENOMEM; + + handler->next = stream->draw_handlers; + stream->draw_handlers = handler; + + return 0; +} + +/** + * @brief Search the list for a specific plugin handle. If such a plugin handle + * exists, unregister (remove and free) this handle from the list. + * + * @param stream: Input location for a Trace data stream pointer. + * @param draw_func: Draw action function to be unregistered. + */ +void kshark_unregister_draw_handler(struct kshark_data_stream *stream, + kshark_plugin_draw_handler_func draw_func) +{ + struct kshark_draw_handler **last; + + for (last = &stream->draw_handlers; *last; last = &(*last)->next) { + if ((*last)->draw_func == draw_func) { + struct kshark_draw_handler *this_handler; + this_handler = *last; + *last = this_handler->next; + free(this_handler); + + return; + } + } +} + +/** + * @brief Free all DRaw handlers in a given list. + * + * @param handlers: Input location for the Draw handler list. + */ +void kshark_free_draw_handler_list(struct kshark_draw_handler *handlers) +{ + struct kshark_draw_handler *last; + + while (handlers) { + last = handlers; + handlers = handlers->next; + free(last); + } +} + +/** Close and free this plugin. */ +static void free_plugin(struct kshark_plugin_list *plugin) +{ + dlclose(plugin->handle); + + if (plugin->process_interface){ + free(plugin->process_interface->name); + free(plugin->process_interface); + } + + if (plugin->readout_interface) { + free(plugin->readout_interface->name); + free(plugin->readout_interface); + } + + free(plugin->name); + free(plugin->file); + free(plugin); +} + /** * @brief Allocate memory for a new plugin. Add this plugin to the list of - * plugins used by the session. + * plugins. * * @param kshark_ctx: Input location for the session context pointer. + * @param name: The name of the plugin to register. * @param file: The plugin object file to load. * - * @returns Zero on success, or a negative error code on failure. + * @returns The plugin object on success, or NULL on failure. */ -int kshark_register_plugin(struct kshark_context *kshark_ctx, - const char *file) +struct kshark_plugin_list * +kshark_register_plugin(struct kshark_context *kshark_ctx, + const char *name, + const char *file) { - struct kshark_plugin_list *plugin = kshark_ctx->plugins; + kshark_plugin_load_func init_func, close_func; + kshark_check_data_func check_func; + kshark_format_func format_func; + struct kshark_plugin_list *plugin; struct stat st; int ret; - while (plugin) { - if (strcmp(plugin->file, file) == 0) - return -EEXIST; + printf("loading plugin \"%s\" from %s\n", name, file); - plugin = plugin->next; + plugin = kshark_find_plugin(kshark_ctx->plugins, file); + if(plugin) { + fputs("the plugin is already loaded.\n", stderr); + return NULL; } ret = stat(file, &st); if (ret < 0) { - fprintf(stderr, "plugin %s not found\n", file); - return -ENODEV; + fprintf(stderr, "plugin %s not found.\n", file); + return NULL; } - plugin = calloc(sizeof(struct kshark_plugin_list), 1); + plugin = calloc(1, sizeof(struct kshark_plugin_list)); if (!plugin) { - fprintf(stderr, "failed to allocate memory for plugin\n"); - return -ENOMEM; + fputs("failed to allocate memory for plugin.\n", stderr); + return NULL; } - if (asprintf(&plugin->file, "%s", file) <= 0) { + plugin->handle = dlopen(file, RTLD_NOW | RTLD_GLOBAL); + if (!plugin->handle) { fprintf(stderr, - "failed to allocate memory for plugin file name"); - return -ENOMEM; + "failed to open plugin file.\n%s\n", + dlerror()); + goto fail; } - plugin->handle = dlopen(plugin->file, RTLD_NOW | RTLD_GLOBAL); - if (!plugin->handle) + plugin->file = strdup(file); + plugin->name = strdup(name); + if (!plugin->file|| !plugin->name) goto fail; - plugin->init = dlsym(plugin->handle, - KSHARK_PLUGIN_INITIALIZER_NAME); + plugin->ctrl_interface = + dlsym(plugin->handle, KSHARK_MENU_PLUGIN_INITIALIZER_NAME); + + init_func = dlsym(plugin->handle, + KSHARK_PLOT_PLUGIN_INITIALIZER_NAME); + + close_func = dlsym(plugin->handle, + KSHARK_PLOT_PLUGIN_DEINITIALIZER_NAME); + + if (init_func && close_func) { + plugin->process_interface = + calloc(1, sizeof(*plugin->process_interface)); + + if (!plugin->process_interface) + goto fail; + + plugin->process_interface->name = strdup(plugin->name); + if (!plugin->process_interface->name) + goto fail; + + plugin->process_interface->init = init_func; + plugin->process_interface->close = close_func; + } else if (init_func || close_func) { + fprintf(stderr, + "incomplete draw interface found (will be ignored).\n%s\n", + dlerror()); + } + + init_func = dlsym(plugin->handle, + KSHARK_INPUT_INITIALIZER_NAME); + + close_func = dlsym(plugin->handle, + KSHARK_INPUT_DEINITIALIZER_NAME); + + check_func = dlsym(plugin->handle, + KSHARK_INPUT_CHECK_NAME); - plugin->close = dlsym(plugin->handle, - KSHARK_PLUGIN_DEINITIALIZER_NAME); + format_func = dlsym(plugin->handle, + KSHARK_INPUT_FORMAT_NAME); - if (!plugin->init || !plugin->close) + if (init_func && close_func && check_func && format_func) { + plugin->readout_interface = + calloc(1, sizeof(*plugin->readout_interface)); + + if (!plugin->readout_interface) + goto fail; + + plugin->readout_interface->name = strdup(plugin->name); + if (!plugin->readout_interface->name) + goto fail; + + plugin->readout_interface->init = init_func; + plugin->readout_interface->close = close_func; + plugin->readout_interface->check_data = check_func; + + kshark_set_data_format(plugin->readout_interface->data_format, + format_func()); + + kshark_register_input(kshark_ctx, plugin->readout_interface); + } else if (init_func || close_func || check_func) { + fprintf(stderr, + "incomplete input interface found (will be ignored).\n%s\n", + dlerror()); + } + + if (!plugin->process_interface && + !plugin->readout_interface && + !plugin->ctrl_interface) { + fputs("no interfaces found in this plugin.\n", stderr); goto fail; + } plugin->next = kshark_ctx->plugins; kshark_ctx->plugins = plugin; + kshark_ctx->n_plugins++; - return 0; + return plugin; fail: - fprintf(stderr, "cannot load plugin '%s'\n%s\n", - plugin->file, dlerror()); + fprintf(stderr, "cannot load plugin '%s'\n", file); - if (plugin->handle) { + if (plugin->handle) dlclose(plugin->handle); - plugin->handle = NULL; - } - free(plugin); + free_plugin(plugin); - return EFAULT; + return NULL; } /** * @brief Unrgister a plugin. * - * @param kshark_ctx: Input location for context pointer. + * @param kshark_ctx: Input location for the session context pointer. + * @param name: The name of the plugin to unregister. * @param file: The plugin object file to unregister. */ void kshark_unregister_plugin(struct kshark_context *kshark_ctx, + const char *name, const char *file) { struct kshark_plugin_list **last; for (last = &kshark_ctx->plugins; *last; last = &(*last)->next) { - if (strcmp((*last)->file, file) == 0) { + if (strcmp((*last)->process_interface->name, name) == 0 && + strcmp((*last)->file, file) == 0) { struct kshark_plugin_list *this_plugin; + this_plugin = *last; *last = this_plugin->next; + free_plugin(this_plugin); - dlclose(this_plugin->handle); - free(this_plugin); + kshark_ctx->n_plugins--; return; } @@ -248,48 +416,346 @@ void kshark_free_plugin_list(struct kshark_plugin_list *plugins) last = plugins; plugins = plugins->next; - free(last->file); - dlclose(last->handle); + free_plugin(last); + } +} + +/** + * @brief Register a data readout interface (input). + * + * @param kshark_ctx: Input location for the context pointer. + * @param plugin: Input location for the data readout interface (input). + */ +struct kshark_dri_list * +kshark_register_input(struct kshark_context *kshark_ctx, + struct kshark_dri *plugin) +{ + struct kshark_dri_list *input; + struct kshark_dri_list **last; + const char *name_err, *format_err; + + if (strcmp(plugin->data_format, TEP_DATA_FORMAT_IDENTIFIER) == 0) { + name_err = "built in"; + format_err = TEP_DATA_FORMAT_IDENTIFIER; + goto conflict; + } + + for (last = &kshark_ctx->inputs; *last; last = &(*last)->next) + if (strcmp((*last)->interface->name, plugin->name) == 0 || + strcmp((*last)->interface->data_format, plugin->data_format) == 0 ) { + name_err = (*last)->interface->name; + format_err = (*last)->interface->data_format; + goto conflict; + } + + input = calloc(1, sizeof(*input)); + if (!input) { + fputs("failed to allocate memory for readout plugin.\n", stderr); + return NULL; + } + + input->interface = plugin; + input->next = kshark_ctx->inputs; + kshark_ctx->inputs = input; + + return input; + + conflict: + fprintf(stderr, + "Failed to register readout plugin (name=\'%s\', data_format=\'%s\')\n", + plugin->name, plugin->data_format); + + fprintf(stderr, + "Conflict with registered readout (name=\'%s\', data_format=\'%s\')\n", + name_err, format_err); + + return NULL; +} + +/** + * @brief Unrgister a data readout interface (input). + * + * @param kshark_ctx: Input location for the context pointer. + * @param name: The data readout's name. + */ +void kshark_unregister_input(struct kshark_context *kshark_ctx, + const char *name) +{ + struct kshark_dri_list **last; + + for (last = &kshark_ctx->inputs; *last; last = &(*last)->next) { + if (strcmp((*last)->interface->name, name) == 0) { + struct kshark_dri_list *this_input; + this_input = *last; + *last = this_input->next; + + free(this_input); + + return; + } + } +} +/** + * @brief Free a list of plugin interfaces. + * + * @param plugins: Input location for the plugins list. + */ +void +kshark_free_dpi_list(struct kshark_dpi_list *plugins) +{ + struct kshark_dpi_list *last; + + while (plugins) { + last = plugins; + plugins = plugins->next; free(last); } } /** - * @brief Use this function to initialize/update/deinitialize all registered - * plugins. + * @brief Find a plugin by its library file. * - * @param kshark_ctx: Input location for context pointer. + * @param plugins: A list of plugins to search in. + * @param lib: The plugin object file to load. + * + * @returns The plugin object on success, or NULL on failure. + */ +struct kshark_plugin_list * +kshark_find_plugin(struct kshark_plugin_list *plugins, const char *lib) +{ + for (; plugins; plugins = plugins->next) + if (strcmp(plugins->file, lib) == 0) + return plugins; + + return NULL; +} + +/** + * @brief Find a plugin by its name. + * + * @param plugins: A list of plugins to search in. + * @param name: The plugin object file to load. + * + * @returns The plugin object on success, or NULL on failure. + */ +struct kshark_plugin_list * +kshark_find_plugin_by_name(struct kshark_plugin_list *plugins, + const char *name) +{ + for (; plugins; plugins = plugins->next) + if (strcmp(plugins->name, name) == 0) + return plugins; + + return NULL; +} + +/** + * @brief Register plugin to a given data stream without initializing it. + * In order to initialize this plugin use kshark_handle_dpi() or + * kshark_handle_all_dpis(). + * + * @param stream: Input location for a Trace data stream pointer. + * @param plugin: Input location for the data processing interface. + * @param active: If false, the plugin will be registered but disabled. + * Otherwise the plugin will be active. + * + * @returns The plugin object on success, or NULL on failure. + */ +struct kshark_dpi_list * +kshark_register_plugin_to_stream(struct kshark_data_stream *stream, + struct kshark_dpi *plugin, + bool active) +{ + struct kshark_dpi_list *plugin_list = stream->plugins; + + /* Check if the plugin is already registered to this stream. */ + while (plugin_list) { + if (strcmp(plugin_list->interface->name, plugin->name) == 0 && + plugin_list->interface->init == plugin->init && + plugin_list->interface->close == plugin->close) { + /* + * The plugin has been registered already. Check if it + * is initialized and if this is the case, close the + * existing initialization. This way we guarantee a + * clean new initialization from. + */ + if (plugin_list->status & KSHARK_PLUGIN_LOADED) + kshark_handle_dpi(stream, plugin_list, + KSHARK_PLUGIN_CLOSE); + + plugin_list->status = + active ? KSHARK_PLUGIN_ENABLED : 0; + + return plugin_list; + } + + plugin_list = plugin_list->next; + } + + plugin_list = calloc(1, sizeof(*plugin_list)); + plugin_list->interface = plugin; + + if (active) + plugin_list->status = KSHARK_PLUGIN_ENABLED; + + plugin_list->next = stream->plugins; + stream->plugins = plugin_list; + stream->n_plugins++; + + return plugin_list; +} + +/** + * @brief Unregister plugin to a given data stream. + * + * @param stream: Input location for a Trace data stream pointer. + * @param plugin: Input location for the data processing interface. + */ +void kshark_unregister_plugin_from_stream(struct kshark_data_stream *stream, + struct kshark_dpi *plugin) +{ + struct kshark_dpi_list **last; + + for (last = &stream->plugins; *last; last = &(*last)->next) { + if ((*last)->interface->init == plugin->init && + (*last)->interface->close == plugin->close && + strcmp((*last)->interface->name, plugin->name) == 0) { + struct kshark_dpi_list *this_plugin; + + this_plugin = *last; + *last = this_plugin->next; + this_plugin->interface->close(stream); + free(this_plugin); + + stream->n_plugins--; + + return; + } + } +} + +static void plugin_init(struct kshark_data_stream *stream, + struct kshark_dpi_list *plugin) +{ + int handler_count = plugin->interface->init(stream); + + if (handler_count > 0) { + plugin->status &= ~KSHARK_PLUGIN_FAILED; + plugin->status |= KSHARK_PLUGIN_LOADED; + } else { + if (strcmp(stream->name, KS_UNNAMED) == 0) { + fprintf(stderr, + "plugin \"%s\" failed to initialize on stream %s\n", + plugin->interface->name, + stream->file); + } else { + fprintf(stderr, + "plugin \"%s\" failed to initialize on stream %s:%s\n", + plugin->interface->name, + stream->file, + stream->name); + } + + plugin->status |= KSHARK_PLUGIN_FAILED; + plugin->status &= ~KSHARK_PLUGIN_LOADED; + } +} + +static void plugin_close(struct kshark_data_stream *stream, + struct kshark_dpi_list *plugin) +{ + plugin->interface->close(stream); + plugin->status &= ~KSHARK_PLUGIN_LOADED; +} + +/** + * @brief Use this function to initialize/update/deinitialize a plugin for + * a given Data stream. + * + * @param stream: Input location for a Trace data stream pointer. + * @param plugin: The plugin to be handled. * @param task_id: Action identifier specifying the action to be executed. * * @returns The number of successful added/removed plugin handlers on success, * or a negative error code on failure. */ -int kshark_handle_plugins(struct kshark_context *kshark_ctx, - enum kshark_plugin_actions task_id) +int kshark_handle_dpi(struct kshark_data_stream *stream, + struct kshark_dpi_list *plugin, + enum kshark_plugin_actions task_id) { - struct kshark_plugin_list *plugin; int handler_count = 0; - for (plugin = kshark_ctx->plugins; plugin; plugin = plugin->next) { - switch (task_id) { - case KSHARK_PLUGIN_INIT: - handler_count += plugin->init(kshark_ctx); - break; + switch (task_id) { + case KSHARK_PLUGIN_INIT: + if (plugin->status & KSHARK_PLUGIN_ENABLED) + plugin_init(stream, plugin); - case KSHARK_PLUGIN_UPDATE: - plugin->close(kshark_ctx); - handler_count += plugin->init(kshark_ctx); - break; + break; - case KSHARK_PLUGIN_CLOSE: - handler_count += plugin->close(kshark_ctx); - break; + case KSHARK_PLUGIN_UPDATE: + if (plugin->status & KSHARK_PLUGIN_LOADED) + plugin_close(stream, plugin); - default: - return -EINVAL; - } + plugin->status &= ~KSHARK_PLUGIN_FAILED; + + if (plugin->status & KSHARK_PLUGIN_ENABLED) + plugin_init(stream, plugin); + + break; + + case KSHARK_PLUGIN_CLOSE: + if (plugin->status & KSHARK_PLUGIN_LOADED) + plugin_close(stream, plugin); + + plugin->status &= ~KSHARK_PLUGIN_FAILED; + break; + + default: + return -EINVAL; } return handler_count; } + +/** + * @brief Use this function to initialize/update/deinitialize all registered + * data processing plugins for a given Data stream. + * + * @param stream: Input location for a Trace data stream pointer. + * @param task_id: Action identifier specifying the action to be executed. Can + * be KSHARK_PLUGIN_INIT, KSHARK_PLUGIN_UPDATE or + * KSHARK_PLUGIN_CLOSE. + * + * @returns The number of successful added/removed plugin handlers on success, + * or a negative error code on failure. + */ +int kshark_handle_all_dpis(struct kshark_data_stream *stream, + enum kshark_plugin_actions task_id) +{ + struct kshark_dpi_list *plugin; + int handler_count = 0; + + for (plugin = stream->plugins; plugin; plugin = plugin->next) + handler_count += + kshark_handle_dpi(stream, plugin, task_id); + + return handler_count; +} + +/** + * @brief Free all readout interfaces in a given list. + * + * @param inputs: Input location for the inputs list. + */ +void kshark_free_dri_list(struct kshark_dri_list *inputs) +{ + struct kshark_dri_list *last; + + while (inputs) { + last = inputs; + inputs = inputs->next; + + free(last); + } +} diff --git a/src/libkshark-plugin.h b/src/libkshark-plugin.h index b3cf1c6..1a642ad 100644 --- a/src/libkshark-plugin.h +++ b/src/libkshark-plugin.h @@ -4,10 +4,10 @@ * Copyright (C) 2016 Red Hat Inc, Steven Rostedt */ - /** - * @file libkshark-plugin.h - * @brief KernelShark plugins. - */ +/** + * @file libkshark-plugin.h + * @brief KernelShark plugins. + */ #ifndef _KSHARK_PLUGIN_H #define _KSHARK_PLUGIN_H @@ -16,35 +16,51 @@ extern "C" { #endif // __cplusplus -// trace-cmd -#include "traceevent/event-parse.h" +// C +#include +#include /* Quiet warnings over documenting simple structures */ //! @cond Doxygen_Suppress -#define KSHARK_PLUGIN_INITIALIZER kshark_plugin_initializer - -#define KSHARK_PLUGIN_DEINITIALIZER kshark_plugin_deinitializer - #define _MAKE_STR(x) #x + #define MAKE_STR(x) _MAKE_STR(x) -#define KSHARK_PLUGIN_INITIALIZER_NAME MAKE_STR(KSHARK_PLUGIN_INITIALIZER) +#define KSHARK_PLOT_PLUGIN_INITIALIZER kshark_data_plugin_initializer -#define KSHARK_PLUGIN_DEINITIALIZER_NAME MAKE_STR(KSHARK_PLUGIN_DEINITIALIZER) +#define KSHARK_PLOT_PLUGIN_DEINITIALIZER kshark_data_plugin_deinitializer -struct kshark_context; +#define KSHARK_PLOT_PLUGIN_INITIALIZER_NAME MAKE_STR(KSHARK_PLOT_PLUGIN_INITIALIZER) + +#define KSHARK_PLOT_PLUGIN_DEINITIALIZER_NAME MAKE_STR(KSHARK_PLOT_PLUGIN_DEINITIALIZER) + +#define KSHARK_MENU_PLUGIN_INITIALIZER kshark_plugin_menu_initializer + +#define KSHARK_MENU_PLUGIN_INITIALIZER_NAME MAKE_STR(KSHARK_MENU_PLUGIN_INITIALIZER) + +#define KSHARK_INPUT_INITIALIZER kshark_input_initializer + +#define KSHARK_INPUT_DEINITIALIZER kshark_input_deinitializer + +#define KSHARK_INPUT_CHECK kshark_input_check + +#define KSHARK_INPUT_FORMAT kshark_input_format + +#define KSHARK_INPUT_INITIALIZER_NAME MAKE_STR(KSHARK_INPUT_INITIALIZER) +#define KSHARK_INPUT_DEINITIALIZER_NAME MAKE_STR(KSHARK_INPUT_DEINITIALIZER) + +#define KSHARK_INPUT_CHECK_NAME MAKE_STR(KSHARK_INPUT_CHECK) + +#define KSHARK_INPUT_FORMAT_NAME MAKE_STR(KSHARK_INPUT_FORMAT) + +struct kshark_data_stream; +struct kshark_context; struct kshark_entry; //! @endcond -/** - * A function type to be used when defining load/reload/unload plugin - * functions. - */ -typedef int (*kshark_plugin_load_func)(struct kshark_context *); - struct kshark_trace_histo; /** @@ -57,18 +73,17 @@ struct kshark_cpp_argv { }; /** A function type to be used when defining plugin functions for drawing. */ -typedef void -(*kshark_plugin_draw_handler_func)(struct kshark_cpp_argv *argv, - int val, int draw_action); +typedef void (*kshark_plugin_draw_handler_func)(struct kshark_cpp_argv *argv, + int sd, + int val, + int draw_action); /** * A function type to be used when defining plugin functions for data * manipulation. */ -typedef void -(*kshark_plugin_event_handler_func)(struct kshark_context *kshark_ctx, - struct tep_record *rec, - struct kshark_entry *e); +typedef void (*kshark_plugin_event_handler_func)(struct kshark_data_stream *stream, + void *rec, struct kshark_entry *e); /** Plugin action identifier. */ enum kshark_plugin_actions { @@ -89,30 +104,39 @@ enum kshark_plugin_actions { * plugins. */ KSHARK_PLUGIN_CLOSE, +}; +/** Plotting action identifier. */ +enum kshark_plotting_actions { /** * Task draw action. This action identifier is used by the plugin draw * function. */ - KSHARK_PLUGIN_TASK_DRAW, + KSHARK_TASK_DRAW = 1 << 0, /** * CPU draw action. This action identifier is used by the plugin draw * function. */ - KSHARK_PLUGIN_CPU_DRAW, + KSHARK_CPU_DRAW = 1 << 1, + + /** + * Draw action for the Host graph in Virtual Combos. This action + * identifier is used by the plugin draw function. + */ + KSHARK_HOST_DRAW = 1 << 2, + + /** + * Draw action for the Guest graph in Virtual Combos. This action + * identifier is used by the plugin draw function. + */ + KSHARK_GUEST_DRAW = 1 << 3, }; -/** - * Plugin Event handler structure, defining the properties of the required - * kshark_entry. - */ -struct kshark_event_handler { +/** Plugin's Trace event processing handler structure. */ +struct kshark_event_proc_handler { /** Pointer to the next Plugin Event handler. */ - struct kshark_event_handler *next; - - /** Unique Id ot the trace event type. */ - int id; + struct kshark_event_proc_handler *next; /** * Event action function. This action can be used to modify the content @@ -120,6 +144,28 @@ struct kshark_event_handler { */ kshark_plugin_event_handler_func event_func; + /** Unique Id ot the trace event type. */ + int id; +}; + +struct kshark_event_proc_handler * +kshark_find_event_handler(struct kshark_event_proc_handler *handlers, int event_id); + +int kshark_register_event_handler(struct kshark_data_stream *stream, + int event_id, + kshark_plugin_event_handler_func evt_func); + +int kshark_unregister_event_handler(struct kshark_data_stream *stream, + int event_id, + kshark_plugin_event_handler_func evt_func); + +void kshark_free_event_handler_list(struct kshark_event_proc_handler *handlers); + +/** Plugin's drawing handler structure. */ +struct kshark_draw_handler { + /** Pointer to the next Plugin Event handler. */ + struct kshark_draw_handler *next; + /** * Draw action function. This action can be used to draw additional * graphical elements (shapes) for all kshark_entries having Event Ids @@ -128,31 +174,73 @@ struct kshark_event_handler { kshark_plugin_draw_handler_func draw_func; }; -struct kshark_event_handler * -kshark_find_event_handler(struct kshark_event_handler *handlers, int event_id); +int kshark_register_draw_handler(struct kshark_data_stream *stream, + kshark_plugin_draw_handler_func draw_func); -int kshark_register_event_handler(struct kshark_event_handler **handlers, - int event_id, - kshark_plugin_event_handler_func evt_func, - kshark_plugin_draw_handler_func dw_func); +void kshark_unregister_draw_handler(struct kshark_data_stream *stream, + kshark_plugin_draw_handler_func draw_func); -void kshark_unregister_event_handler(struct kshark_event_handler **handlers, - int event_id, - kshark_plugin_event_handler_func evt_func, - kshark_plugin_draw_handler_func dw_func); +void kshark_free_draw_handler_list(struct kshark_draw_handler *handlers); -void kshark_free_event_handler_list(struct kshark_event_handler *handlers); +/** + * A function type to be used when defining load/reload/unload plugin + * functions. + */ +typedef int (*kshark_plugin_load_func)(struct kshark_data_stream *); -/** Linked list of plugins. */ -struct kshark_plugin_list { - /** Pointer to the next Plugin. */ - struct kshark_plugin_list *next; +/** + * A function type to be used when defining data check function for the plugin. + */ +typedef bool (*kshark_check_data_func)(const char *file_name); - /** The plugin object file to load. */ - char *file; +/** + * A function type to be used when defining data format function for the plugin. + */ +typedef const char *(*kshark_format_func) (); + +/** + * A function type to be used when defining plugin's configuration/control + * function. + */ +typedef void *(*kshark_plugin_ctrl_func)(void *); + +/** The limit in size of the data format identifier string. */ +#define KS_DATA_FORMAT_SIZE 15 - /** Plugin Event handler. */ - void *handle; +/** Plugable Data Readout Interface (dri). */ +struct kshark_dri { + /** A short name for this data input. */ + char *name; + + /** Data format identifier. */ + char data_format[KS_DATA_FORMAT_SIZE]; + + /** Callback function for initialization of the data input. */ + kshark_plugin_load_func init; + + /** Callback function for deinitialization of the data input. */ + kshark_plugin_load_func close; + + /** + * Callback function for checking if the data input is applicable for + * a given data file. + */ + kshark_check_data_func check_data; +}; + +/** Linked list of Data Readout Interfaces (dri). */ +struct kshark_dri_list { + /** Pointer to the next input interface. */ + struct kshark_dri_list *next; + + /** Pointer to the interface of methods used by the input. */ + struct kshark_dri *interface; +}; + +/** Plugable Data Processing Interface (dpi). */ +struct kshark_dpi { + /** The plugin's short name. */ + char *name; /** Callback function for initialization of the plugin. */ kshark_plugin_load_func init; @@ -161,16 +249,102 @@ struct kshark_plugin_list { kshark_plugin_load_func close; }; -int kshark_register_plugin(struct kshark_context *kshark_ctx, - const char *file); +/** Linked list of data processing interfaces (dpi). */ +struct kshark_dpi_list { + /** Pointer to the next plugin interface. */ + struct kshark_dpi_list *next; + + /** Pointer to the interface of methods used by the plugin. */ + struct kshark_dpi *interface; + + /** + * The status of the interface. + */ + int status; +}; + +struct kshark_dri_list * +kshark_register_input(struct kshark_context *kshark_ctx, + struct kshark_dri *plugin); + +void kshark_unregister_input(struct kshark_context *kshark_ctx, + const char *file); + +void kshark_free_dri_list(struct kshark_dri_list *inputs); + +/** Linked list of plugins. */ +struct kshark_plugin_list { + /** Pointer to the next plugin. */ + struct kshark_plugin_list *next; + + /** The plugin's short name. */ + char *name; + + /** The plugin object file to load. */ + char *file; + + /** Plugin's object file handler. */ + void *handle; + + /** + * Control interface of the plugin. Can be used to configure + * the plugin. + */ + kshark_plugin_ctrl_func ctrl_interface; + + /** The interface of methods used by a data processing plugin. */ + struct kshark_dpi *process_interface; + + /** The interface of methods used by a data readout plugin. */ + struct kshark_dri *readout_interface; +}; + +/** Plugin status identifiers. */ +enum kshark_plugin_status { + /** The plugin is enabled. */ + KSHARK_PLUGIN_ENABLED = 1 << 0, + + /** The plugin is successfully loaded. */ + KSHARK_PLUGIN_LOADED = 1 << 1, + + /** The plugin failed to initialization. */ + KSHARK_PLUGIN_FAILED = 1 << 2, +}; + +struct kshark_plugin_list * +kshark_register_plugin(struct kshark_context *kshark_ctx, + const char *name, + const char *file); void kshark_unregister_plugin(struct kshark_context *kshark_ctx, + const char *name, const char *file); void kshark_free_plugin_list(struct kshark_plugin_list *plugins); -int kshark_handle_plugins(struct kshark_context *kshark_ctx, - enum kshark_plugin_actions task_id); +void kshark_free_dpi_list(struct kshark_dpi_list *plugins); + +struct kshark_plugin_list * +kshark_find_plugin(struct kshark_plugin_list *plugins, const char *file); + +struct kshark_plugin_list * +kshark_find_plugin_by_name(struct kshark_plugin_list *plugins, + const char *name); + +struct kshark_dpi_list * +kshark_register_plugin_to_stream(struct kshark_data_stream *stream, + struct kshark_dpi *plugin, + bool active); + +void kshark_unregister_plugin_from_stream(struct kshark_data_stream *stream, + struct kshark_dpi *plugin); + +int kshark_handle_dpi(struct kshark_data_stream *stream, + struct kshark_dpi_list *plugin, + enum kshark_plugin_actions task_id); + +int kshark_handle_all_dpis(struct kshark_data_stream *stream, + enum kshark_plugin_actions task_id); #ifdef __cplusplus } diff --git a/src/libkshark.c b/src/libkshark.c index f5a5aeb..d85f894 100644 --- a/src/libkshark.c +++ b/src/libkshark.c @@ -16,6 +16,7 @@ #include #include #include +#include // KernelShark #include "libkshark.h" @@ -36,7 +37,6 @@ static bool kshark_default_context(struct kshark_context **context) kshark_ctx->stream_info.array_size = KS_DEFAULT_NUM_STREAMS; kshark_ctx->stream_info.max_stream_id = -1; - kshark_ctx->event_handlers = NULL; kshark_ctx->collections = NULL; kshark_ctx->plugins = NULL; @@ -304,6 +304,7 @@ int kshark_add_stream(struct kshark_context *kshark_ctx) int kshark_stream_open(struct kshark_data_stream *stream, const char *file) { struct kshark_context *kshark_ctx = NULL; + struct kshark_dri_list *input; if (!stream || !kshark_instance(&kshark_ctx)) return -EFAULT; @@ -319,6 +320,14 @@ int kshark_stream_open(struct kshark_data_stream *stream, const char *file) return kshark_tep_init_input(stream); } + for (input = kshark_ctx->inputs; input; input = input->next) + if (input->interface->check_data(file)) { + strcpy(stream->data_format, + input->interface->data_format); + + return input->interface->init(stream); + } + return -ENODATA; } @@ -416,6 +425,7 @@ int *kshark_all_streams(struct kshark_context *kshark_ctx) static int kshark_stream_close(struct kshark_data_stream *stream) { struct kshark_context *kshark_ctx = NULL; + struct kshark_dri_list *input; if (!stream || !kshark_instance(&kshark_ctx)) return -EFAULT; @@ -434,6 +444,10 @@ static int kshark_stream_close(struct kshark_data_stream *stream) if (kshark_is_tep(stream)) return kshark_tep_close_interface(stream); + for (input = kshark_ctx->inputs; input; input = input->next) + if (strcmp(stream->data_format, input->interface->data_format) == 0) + return input->interface->close(stream); + return -ENODATA; } @@ -454,6 +468,13 @@ int kshark_close(struct kshark_context *kshark_ctx, int sd) if (!stream) return -EFAULT; + /* Close all active plugins for this stream. */ + if (stream->plugins) { + kshark_handle_all_dpis(stream, KSHARK_PLUGIN_CLOSE); + kshark_free_event_handler_list(stream->event_handlers); + kshark_free_dpi_list(stream->plugins); + } + ret = kshark_stream_close(stream); kshark_remove_stream(kshark_ctx, stream->stream_id); @@ -499,6 +520,8 @@ void kshark_free(struct kshark_context *kshark_ctx) if (kshark_ctx->plugins) kshark_free_plugin_list(kshark_ctx->plugins); + kshark_free_dri_list(kshark_ctx->inputs); + if (kshark_ctx == kshark_context_handler) kshark_context_handler = NULL; @@ -1222,6 +1245,29 @@ void kshark_clear_all_filters(struct kshark_context *kshark_ctx, set_all_visible(&data[i]->visible); } +/** + * @brief Process all registered event-specific plugin actions. + * + * @param stream: Input location for a Trace data stream pointer. + * @param record: Input location for the trace record. + * @param entry: Output location for entry. + */ +void kshark_plugin_actions(struct kshark_data_stream *stream, + void *record, struct kshark_entry *entry) +{ + if (stream->event_handlers) { + /* Execute all plugin-provided actions for this event (if any). */ + struct kshark_event_proc_handler *evt_handler = stream->event_handlers; + + while ((evt_handler = kshark_find_event_handler(evt_handler, + entry->event_id))) { + evt_handler->event_func(stream, record, entry); + evt_handler = evt_handler->next; + entry->visible &= ~KS_PLUGIN_UNTOUCHED_MASK; + } + } +} + static inline void free_ptr(void *ptr) { if (ptr) diff --git a/src/libkshark.h b/src/libkshark.h index 06d7178..07c586d 100644 --- a/src/libkshark.h +++ b/src/libkshark.h @@ -272,9 +272,6 @@ struct kshark_generic_stream_interface { void *handle; }; -/** The limit in size of the data format identifier string. */ -#define KS_DATA_FORMAT_SIZE 15 - /** Data format identifier string indicating invalid data. */ #define KS_INVALID_DATA "invalid data" @@ -327,6 +324,18 @@ struct kshark_data_stream { /** The type of the data. */ char data_format[KS_DATA_FORMAT_SIZE]; + /** List of Plugin interfaces. */ + struct kshark_dpi_list *plugins; + + /** The number of plugins registered for this stream.*/ + int n_plugins; + + /** List of Plugin's Event handlers. */ + struct kshark_event_proc_handler *event_handlers; + + /** List of Plugin's Draw handlers. */ + struct kshark_draw_handler *draw_handlers; + /** * The interface of methods used to operate over the data from a given * stream. @@ -406,11 +415,17 @@ struct kshark_context { /** List of Data collections. */ struct kshark_entry_collection *collections; + /** List of data readout interfaces. */ + struct kshark_dri_list *inputs; + + /** The number of readout interfaces. */ + int n_inputs; + /** List of Plugins. */ struct kshark_plugin_list *plugins; - /** List of Plugin Event handlers. */ - struct kshark_event_handler *event_handlers; + /** The number of plugins. */ + int n_plugins; }; bool kshark_instance(struct kshark_context **kshark_ctx); @@ -587,6 +602,9 @@ void kshark_clear_all_filters(struct kshark_context *kshark_ctx, struct kshark_entry **data, size_t n_entries); +void kshark_plugin_actions(struct kshark_data_stream *stream, + void *record, struct kshark_entry *entry); + /** Search failed identifiers. */ enum kshark_search_failed { /** All entries have timestamps greater timestamps. */ From patchwork Mon Jan 4 17:47:01 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11997169 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 121D4C433E9 for ; Mon, 4 Jan 2021 17:49:15 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id C3BF720715 for ; Mon, 4 Jan 2021 17:49:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726419AbhADRtO (ORCPT ); Mon, 4 Jan 2021 12:49:14 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34362 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726418AbhADRtO (ORCPT ); Mon, 4 Jan 2021 12:49:14 -0500 Received: from mail-ej1-x633.google.com (mail-ej1-x633.google.com [IPv6:2a00:1450:4864:20::633]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 391BCC0617B9 for ; Mon, 4 Jan 2021 09:48:00 -0800 (PST) Received: by mail-ej1-x633.google.com with SMTP id b9so37958370ejy.0 for ; Mon, 04 Jan 2021 09:48:00 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=cSDjU4MSgVBs1TzOUd5I+G9ZXhWxSxGXQcJDOhUJLmg=; b=Ui7i9/eLiCUvPe7BWlCtEQ061R44VFv4cX3TG/wl+0rltc1nYviT2/MWoRnB3vUm4T vV4cTk8ehpfrWy8qncKlN4FQct+JKPZAccch86u7gjDP0SVUgr4f8WOloIto4cC33WbC Hn3RyaCFLQiVZsPnWQygRgHJPonUfJeOrqGvGkFgUKbJozGWnXcjhXPvthSt/lyx0l76 7NzZ+c235mcOihUWRfz5SjbnOOTHRNvIqDgKxxmWe4PVoqtcN1GBQ5UZ9l6/EZTVFJEN p6Qhpq5mk6XTQZhEFJtuDI02OwtVOhy9MQCorWJFsDljGcToLVmBi15xqa9F7S5qlLK5 zb2A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=cSDjU4MSgVBs1TzOUd5I+G9ZXhWxSxGXQcJDOhUJLmg=; b=f2JqpNnQP388hCJW3UlUsJneRKGrjfvR8WQPHITky3HFMw6xlVaQBtJ/s3O1NkF6iK YGkFdOpbgox3NjLTgoRzHTfhkyIzDqzv8TXzx7yt20vOwULyne/h81Y002ioisaL0S7e dcU2ymI2TYbLemgcHNzowD9QYJ29Cv5eeYn2yw3jdb3ugkoULz8xGYlKVfdqw3etDd9N 7W4QGv5W3kfcOd5hFTkw2VZNzcQLdc8nVaK+YdCx5dSfjpD6TEiVEBXSbq+0G9XCffUy rkR8y5J5DkFTBvsrfxq8DlwqfoSIjrQXOUvmjdvWElb4s5V97batkJJFUswJIffbwB85 yMTQ== X-Gm-Message-State: AOAM530mzBc6BCNsN/Vv1Zmbh32ijc/pAVWGNenSJRPOWH51Rg5gPcnw MepY09j+vC8AhquFxqh0Fh86C3MH7Vuj8Q== X-Google-Smtp-Source: ABdhPJz90Ny0Ujql7x+YTLiNA5iXSgblNAWeFSdaV3wSKD53i+sYdHku7bA1U8IFAaed6aFmCE+0iA== X-Received: by 2002:a17:906:451:: with SMTP id e17mr65489459eja.228.1609782478379; Mon, 04 Jan 2021 09:47:58 -0800 (PST) Received: from localhost.localdomain ([95.87.199.238]) by smtp.gmail.com with ESMTPSA id l14sm44107750edq.35.2021.01.04.09.47.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 04 Jan 2021 09:47:57 -0800 (PST) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v8 21/44] kernel-shark: Complete the stream integration Date: Mon, 4 Jan 2021 19:47:01 +0200 Message-Id: <20210104174724.70404-22-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210104174724.70404-1-y.karadz@gmail.com> References: <20210104174724.70404-1-y.karadz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org The patch contains a number various relatively small modifications needed in order to finalize the integration. Unfortunately those changes are hard to disentangle so we will do everything in a single patch. Signed-off-by: Yordan Karadzhov (VMware) --- examples/CMakeLists.txt | 8 +- examples/datafilter.c | 67 +++--- src/CMakeLists.txt | 1 + src/libkshark-collection.c | 121 ++++++++-- src/libkshark-tepdata.c | 391 ++++++++++++++++++++++++++++++++ src/libkshark-tepdata.h | 49 ++++ src/libkshark.c | 454 +++++++++++++++++++++++++------------ src/libkshark.h | 133 ++++++----- src/trace-filter-hash.h | 64 ------ 9 files changed, 959 insertions(+), 329 deletions(-) delete mode 100644 src/trace-filter-hash.h diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index f6ed897..8d40e42 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -4,10 +4,10 @@ message(STATUS "dataload") add_executable(dload dataload.c) target_link_libraries(dload kshark) -# message(STATUS "datafilter") -# add_executable(dfilter datafilter.c) -# target_link_libraries(dfilter kshark) -# +message(STATUS "datafilter") +add_executable(dfilter datafilter.c) +target_link_libraries(dfilter kshark) + # message(STATUS "datahisto") # add_executable(dhisto datahisto.c) # target_link_libraries(dhisto kshark) diff --git a/examples/datafilter.c b/examples/datafilter.c index bebc181..38afab8 100644 --- a/examples/datafilter.c +++ b/examples/datafilter.c @@ -7,22 +7,22 @@ // C #include #include +#include // KernelShark #include "libkshark.h" +#include "libkshark-tepdata.h" const char *default_file = "trace.dat"; int main(int argc, char **argv) { - ssize_t i, n_rows, n_tasks, n_evts, count; + size_t i, sd, n_rows, n_tasks, n_evts, count; struct kshark_context *kshark_ctx; + struct kshark_data_stream *stream; struct kshark_entry **data = NULL; - struct tep_event_filter *adv_filter; - struct tep_event *event; + int *pids, *evt_ids; char *entry_str; - bool status; - int *pids; /* Create a new kshark session. */ kshark_ctx = NULL; @@ -31,32 +31,30 @@ int main(int argc, char **argv) /* Open a trace data file produced by trace-cmd. */ if (argc > 1) - status = kshark_open(kshark_ctx, argv[1]); + sd = kshark_open(kshark_ctx, argv[1]); else - status = kshark_open(kshark_ctx, default_file); + sd = kshark_open(kshark_ctx, default_file); - if (!status) { + if (sd < 0) { kshark_free(kshark_ctx); return 1; } /* Load the content of the file into an array of entries. */ - n_rows = kshark_load_data_entries(kshark_ctx, &data); - if (n_rows < 1) { - kshark_free(kshark_ctx); - return 1; - } + n_rows = kshark_load_entries(kshark_ctx, sd, &data); /* Filter the trace data coming from trace-cmd. */ - n_tasks = kshark_get_task_pids(kshark_ctx, &pids); + n_tasks = kshark_get_task_pids(kshark_ctx, sd, &pids); + stream = kshark_get_data_stream(kshark_ctx, sd); for (i = 0; i < n_tasks; ++i) { - const char *task_str = - tep_data_comm_from_pid(kshark_ctx->pevent, - pids[i]); + char *task_str = + kshark_comm_from_pid(sd, pids[i]); if (strcmp(task_str, "trace-cmd") == 0) - kshark_filter_add_id(kshark_ctx, KS_HIDE_TASK_FILTER, - pids[i]); + kshark_filter_add_id(kshark_ctx, sd, + KS_HIDE_TASK_FILTER, + pids[i]); + free(task_str); } free(pids); @@ -66,7 +64,8 @@ int main(int argc, char **argv) * filterd entris in text format. */ kshark_ctx->filter_mask = KS_TEXT_VIEW_FILTER_MASK; - kshark_filter_entries(kshark_ctx, data, n_rows); + kshark_ctx->filter_mask |= KS_EVENT_VIEW_FILTER_MASK; + kshark_filter_stream_entries(kshark_ctx, sd, data, n_rows); /* Print to the screen the first 10 visible entries. */ count = 0; @@ -87,15 +86,19 @@ int main(int argc, char **argv) puts("\n\n"); /* Show only "sched" events. */ - n_evts = tep_get_events_count(kshark_ctx->pevent); + n_evts = stream->n_events; + evt_ids = kshark_get_all_event_ids(kshark_ctx->stream[sd]); for (i = 0; i < n_evts; ++i) { - event = tep_get_event(kshark_ctx->pevent, i); - if (strcmp(event->system, "sched") == 0) - kshark_filter_add_id(kshark_ctx, KS_SHOW_EVENT_FILTER, - event->id); + char *event_str = + kshark_event_from_id(sd, evt_ids[i]); + if (strstr(event_str, "sched/")) + kshark_filter_add_id(kshark_ctx, sd, + KS_SHOW_EVENT_FILTER, + evt_ids[i]); + free(event_str); } - kshark_filter_entries(kshark_ctx, data, n_rows); + kshark_filter_stream_entries(kshark_ctx, sd, data, n_rows); /* Print to the screen the first 10 visible entries. */ count = 0; @@ -116,19 +119,17 @@ int main(int argc, char **argv) puts("\n\n"); /* Clear all filters. */ - kshark_filter_clear(kshark_ctx, KS_HIDE_TASK_FILTER); - kshark_filter_clear(kshark_ctx, KS_SHOW_EVENT_FILTER); + kshark_filter_clear(kshark_ctx, sd, KS_HIDE_TASK_FILTER); + kshark_filter_clear(kshark_ctx, sd, KS_SHOW_EVENT_FILTER); /* Use the Advanced filter to do event content based filtering. */ - adv_filter = kshark_ctx->advanced_event_filter; - tep_filter_add_filter_str(adv_filter, - "sched/sched_wakeup:target_cpu==1"); + kshark_tep_add_filter_str(stream, "sched/sched_wakeup:target_cpu>1"); /* The Advanced filter requires reloading the data. */ for (i = 0; i < n_rows; ++i) free(data[i]); - n_rows = kshark_load_data_entries(kshark_ctx, &data); + n_rows = kshark_load_entries(kshark_ctx, sd, &data); count = 0; for (i = 0; i < n_rows; ++i) { @@ -149,7 +150,7 @@ int main(int argc, char **argv) free(data); /* Close the file. */ - kshark_close(kshark_ctx); + kshark_close(kshark_ctx, sd); /* Close the session. */ kshark_free(kshark_ctx); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fac1d5e..1e6ff44 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -34,6 +34,7 @@ install(TARGETS kshark install(FILES "${KS_DIR}/src/libkshark.h" "${KS_DIR}/src/libkshark-model.h" "${KS_DIR}/src/libkshark-plugin.h" + "${KS_DIR}/src/libkshark-tepdata.h" DESTINATION ${KS_INCLUDS_DESTINATION} COMPONENT libkshark-devel) diff --git a/src/libkshark-collection.c b/src/libkshark-collection.c index 66cdbca..915983b 100644 --- a/src/libkshark-collection.c +++ b/src/libkshark-collection.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: LGPL-2.1 /* - * Copyright (C) 2018 VMware Inc, Yordan Karadzhov + * Copyright (C) 2018 VMware Inc, Yordan Karadzhov (VMware) */ /** @@ -11,9 +11,11 @@ // C #include +#include #include #include #include +#include // KernelShark #include "libkshark.h" @@ -74,7 +76,9 @@ kshark_data_collection_alloc(struct kshark_context *kshark_ctx, ssize_t first, size_t n_rows, matching_condition_func cond, - int val, + int sd, + int *values, + int n_val, size_t margin) { struct kshark_entry_collection *col_ptr = NULL; @@ -117,7 +121,7 @@ kshark_data_collection_alloc(struct kshark_context *kshark_ctx, } for (i = first + margin; i < end; ++i) { - if (!cond(kshark_ctx, data[i], val)) { + if (!cond(kshark_ctx, data[i], sd, values)) { /* * The entry is irrelevant for this collection. * Do nothing. @@ -147,7 +151,7 @@ kshark_data_collection_alloc(struct kshark_context *kshark_ctx, } } else if (good_data && data[i]->next && - !cond(kshark_ctx, data[i]->next, val)) { + !cond(kshark_ctx, data[i]->next, sd, values)) { /* * Break the collection here. Add some margin data * after the data of interest. @@ -168,7 +172,7 @@ kshark_data_collection_alloc(struct kshark_context *kshark_ctx, */ if (i + margin >= j) { for (;j < i + margin; ++j) { - if (cond(kshark_ctx, data[j], val)) { + if (cond(kshark_ctx, data[j], sd, values)) { /* * Good data has been found. * Continue extending the @@ -228,9 +232,15 @@ kshark_data_collection_alloc(struct kshark_context *kshark_ctx, } col_ptr->cond = cond; - col_ptr->val = val; + col_ptr->n_val = n_val; + col_ptr->stream_id = sd; + col_ptr->values = malloc(n_val * sizeof(*col_ptr->values)); + memcpy(col_ptr->values, values, n_val * sizeof(*col_ptr->values)); col_ptr->size = resume_count; + if (!col_ptr->size) + free(col_list); + for (i = 0; i < col_ptr->size; ++i) { assert(col_list->type == COLLECTION_RESUME); col_ptr->resume_points[i] = col_list->index; @@ -316,8 +326,8 @@ map_collection_request_init(const struct kshark_entry_collection *col, size_t req_end; if (req->next || col->size == 0) { - fprintf(stderr, "Unexpected input in "); - fprintf(stderr, "map_collection_request_init()\n"); + fprintf(stderr, + "Unexpected input in map_collection_request_init()\n"); goto do_nothing; } @@ -477,7 +487,8 @@ map_collection_back_request(const struct kshark_entry_collection *col, kshark_entry_request_alloc(req_first, 0, req->cond, - req->val, + req->sd, + req->values, req->vis_only, req->vis_mask); @@ -561,7 +572,8 @@ map_collection_front_request(const struct kshark_entry_collection *col, kshark_entry_request_alloc(req_first, 0, req->cond, - req->val, + req->sd, + req->values, req->vis_only, req->vis_mask); @@ -702,25 +714,41 @@ kshark_get_collection_entry_back(struct kshark_entry_request *req, return entry; } +static bool val_compare(int *val_a, int *val_b, size_t n_val) +{ + size_t i; + + for (i = 0; i < n_val; ++i) + if (val_a[i] != val_b[i]) + return false; + + return true; +} + /** * @brief Search the list of Data collections and find the collection defined * with a given Matching condition function and value. * * @param col: Input location for the Data collection list. * @param cond: Matching condition function. - * @param val: Matching condition value, used by the Matching condition - * function. + * @param sd: Data stream identifier. + * @param values: Array of matching condition value, used by the Matching + * condition function. + * @param n_val: The size of the array of Matching values. * * @returns Pointer to a Data collections on success, or NULL on failure. */ struct kshark_entry_collection * kshark_find_data_collection(struct kshark_entry_collection *col, matching_condition_func cond, - int val) + int sd, int *values, size_t n_val) { while (col) { - if (col->cond == cond && col->val == val) - return col; + if (col->cond == cond && + col->stream_id == sd && + col->n_val == n_val && + val_compare(col->values, values, n_val)) + return col; col = col->next; } @@ -748,6 +776,7 @@ static void kshark_free_data_collection(struct kshark_entry_collection *col) { free(col->resume_points); free(col->break_points); + free(col->values); free(col); } @@ -761,7 +790,10 @@ static void kshark_free_data_collection(struct kshark_entry_collection *col) * @param n_rows: The size of the inputted data. * @param cond: Matching condition function for the collection to be * registered. - * @param val: Matching condition value of for collection to be registered. + * @param sd: Data stream identifier. + * @param values: Array of matching condition value, used by the Matching + * condition function. + * @param n_val: The size of the array of Matching values. * @param margin: The size of the additional (margin) data which do not * satisfy the matching condition, but is added at the * beginning and at the end of each interval of the collection @@ -776,7 +808,8 @@ kshark_register_data_collection(struct kshark_context *kshark_ctx, struct kshark_entry **data, size_t n_rows, matching_condition_func cond, - int val, + int sd, + int *values, size_t n_val, size_t margin) { struct kshark_entry_collection *col; @@ -784,7 +817,7 @@ kshark_register_data_collection(struct kshark_context *kshark_ctx, col = kshark_add_collection_to_list(kshark_ctx, &kshark_ctx->collections, data, n_rows, - cond, val, + cond, sd, values, n_val, margin); return col; @@ -801,7 +834,10 @@ kshark_register_data_collection(struct kshark_context *kshark_ctx, * @param n_rows: The size of the inputted data. * @param cond: Matching condition function for the collection to be * registered. - * @param val: Matching condition value of for collection to be registered. + * @param sd: Data stream identifier. + * @param values: Array of matching condition value, used by the Matching + * condition function. + * @param n_val: The size of the array of Matching values. * @param margin: The size of the additional (margin) data which do not * satisfy the matching condition, but is added at the * beginning and at the end of each interval of the collection @@ -817,14 +853,18 @@ kshark_add_collection_to_list(struct kshark_context *kshark_ctx, struct kshark_entry **data, size_t n_rows, matching_condition_func cond, - int val, + int sd, int *values, size_t n_val, size_t margin) { struct kshark_entry_collection *col; + if (!data || n_rows == 0) + return NULL; + col = kshark_data_collection_alloc(kshark_ctx, data, 0, n_rows, - cond, val, + cond, sd, + values, n_val, margin); if (col) { @@ -844,18 +884,23 @@ kshark_add_collection_to_list(struct kshark_context *kshark_ctx, * @param col: Input location for the Data collection list. * @param cond: Matching condition function of the collection to be * unregistered. - * - * @param val: Matching condition value of the collection to be unregistered. + * @param sd: Data stream identifier. + * @param values: Array of matching condition value, used by the Matching + * condition function. + * @param n_val: The size of the array of Matching values. */ void kshark_unregister_data_collection(struct kshark_entry_collection **col, matching_condition_func cond, - int val) + int sd, int *values, size_t n_val) { struct kshark_entry_collection **last = col; struct kshark_entry_collection *list; for (list = *col; list; list = list->next) { - if (list->cond == cond && list->val == val) { + if (list->cond == cond && + list->stream_id == sd && + list->n_val == n_val && + val_compare(list->values, values, n_val)) { *last = list->next; kshark_free_data_collection(list); return; @@ -865,6 +910,32 @@ void kshark_unregister_data_collection(struct kshark_entry_collection **col, } } +/** + * @brief Unregister all Data collections associated with a given Data stream. + * + * @param col: Input location for the Data collection list. + * @param sd: Data stream identifier. + */ +void kshark_unregister_stream_collections(struct kshark_entry_collection **col, + int sd) +{ + struct kshark_entry_collection **last = col; + struct kshark_entry_collection *list; + + list = *col; + while (list) { + if (list->stream_id == sd) { + *last = list->next; + kshark_free_data_collection(list); + list = *last; + continue; + } + + last = &list->next; + list = list->next; + } +} + /** * @brief Free all Data collections in a given list. * diff --git a/src/libkshark-tepdata.c b/src/libkshark-tepdata.c index 7b2ac36..f2482f9 100644 --- a/src/libkshark-tepdata.c +++ b/src/libkshark-tepdata.c @@ -362,6 +362,9 @@ static ssize_t get_records(struct kshark_context *kshark_ctx, pid = entry->pid; + /* Apply Id filtering. */ + kshark_apply_filters(kshark_ctx, stream, entry); + /* Apply advanced event filtering. */ if (adv_filter && adv_filter->filters && tep_filter_match(adv_filter, rec) != FILTER_MATCH) @@ -1235,6 +1238,44 @@ out: return peer_handle; } +/** A list of built in default plugins for FTRACE (trace-cmd) data. */ +const char *tep_plugin_names[] = { + "sched_events", + "missed_events", + "kvm_combo", +}; + +/** + * Register to the data stream all default plugins for FTRACE (trace-cmd) data. + */ +int kshark_tep_handle_plugins(struct kshark_context *kshark_ctx, int sd) +{ + struct kshark_plugin_list *plugin; + struct kshark_data_stream *stream; + int i, n_tep_plugins; + + n_tep_plugins = (sizeof(tep_plugin_names) / sizeof((tep_plugin_names)[0])); + stream = kshark_get_data_stream(kshark_ctx, sd); + if (!stream) + return -EEXIST; + + for (i = 0; i < n_tep_plugins; ++i) { + plugin = kshark_find_plugin_by_name(kshark_ctx->plugins, + tep_plugin_names[i]); + + if (plugin && plugin->process_interface) { + kshark_register_plugin_to_stream(stream, + plugin->process_interface, + true); + } else { + fprintf(stderr, "Plugin \"%s\" not found.\n", + tep_plugin_names[i]); + } + } + + return kshark_handle_all_dpis(stream, KSHARK_PLUGIN_INIT); +} + /** The Process Id of the Idle tasks is zero. */ #define LINUX_IDLE_TASK_PID 0 @@ -1299,6 +1340,210 @@ static inline char *set_tep_format(struct kshark_data_stream *stream) TEP_DATA_FORMAT_IDENTIFIER); } +static struct tracecmd_input *get_top_input(struct kshark_context *kshark_ctx, + int sd) +{ + struct kshark_data_stream *top_stream; + + top_stream = kshark_get_data_stream(kshark_ctx, sd); + if (!top_stream) + return NULL; + + return kshark_get_tep_input(top_stream); +} + +/** + * @brief Get an array containing the names of all buffers in FTRACE data + * file. + * + * @param kshark_ctx: Input location for context pointer. + * @param sd: Data stream identifier of the top buffers in the FTRACE data + * file. + * @param n_buffers: Output location for the size of the outputted array, + * or a negative error code on failure. + * + * @returns Array of strings on success, or NULL on failure. The user is + * responsible for freeing the elements of the outputted array. + */ +char **kshark_tep_get_buffer_names(struct kshark_context *kshark_ctx, int sd, + int *n_buffers) +{ + struct tracecmd_input *top_input; + char **buffer_names; + int i, n; + + top_input = get_top_input(kshark_ctx, sd); + if (!top_input) { + *n_buffers = -EFAULT; + return NULL; + } + + n = tracecmd_buffer_instances(top_input); + buffer_names = calloc(n, sizeof(char *)); + if (!buffer_names) { + *n_buffers = -ENOMEM; + return NULL; + } + + for (i = 0; i < n; ++i) { + buffer_names[i] = + strdup(tracecmd_buffer_instance_name(top_input, i)); + if (!buffer_names[i]) + goto free_all; + } + + *n_buffers = n; + return buffer_names; + + free_all: + for (i = 0; i < n; ++i) + free(buffer_names[i]); + free(buffer_names); + + *n_buffers = -ENOMEM; + return NULL; +} + +static void set_stream_fields(struct tracecmd_input *top_input, int i, + const char *file, + const char *name, + struct kshark_data_stream *buffer_stream, + struct tracecmd_input **buffer_input) +{ + *buffer_input = tracecmd_buffer_instance_handle(top_input, i); + + buffer_stream->name = strdup(name); + buffer_stream->file = strdup(file); + set_tep_format(buffer_stream); +} + +/** + * @brief Open a given buffers in FTRACE (trace-cmd) data file. + * + * @param kshark_ctx: Input location for context pointer. + * @param sd: Data stream identifier of the top buffers in the FTRACE data + * file. + * @param buffer_name: The name of the buffer to open. + * + * @returns Data stream identifier of the buffer on success. Otherwise a + * negative error code. + */ +int kshark_tep_open_buffer(struct kshark_context *kshark_ctx, int sd, + const char *buffer_name) +{ + struct kshark_data_stream *top_stream, *buffer_stream; + struct tracecmd_input *top_input, *buffer_input; + int i, sd_buffer, n_buffers, ret = -ENODATA; + char **names; + + top_stream = kshark_get_data_stream(kshark_ctx, sd); + if (!top_stream) + return -EFAULT; + + top_input = kshark_get_tep_input(top_stream); + if (!top_input) + return -EFAULT; + + names = kshark_tep_get_buffer_names(kshark_ctx, sd, &n_buffers); + if (!names) + return n_buffers; + + sd_buffer = kshark_add_stream(kshark_ctx); + buffer_stream = kshark_get_data_stream(kshark_ctx, sd_buffer); + if (!buffer_stream) + return -EFAULT; + + for (i = 0; i < n_buffers; ++i) { + if (strcmp(buffer_name, names[i]) == 0) { + set_stream_fields(top_input, i, + top_stream->file, + buffer_name, + buffer_stream, + &buffer_input); + + if (!buffer_stream->name || !buffer_stream->file) { + free(buffer_stream->name); + free(buffer_stream->file); + + ret = -ENOMEM; + break; + } + + ret = kshark_tep_stream_init(buffer_stream, + buffer_input); + break; + } + } + + for (i = 0; i < n_buffers; ++i) + free(names[i]); + free(names); + + return (ret < 0)? ret : buffer_stream->stream_id; +} + +/** + * @brief Initialize data streams for all buffers in a FTRACE (trace-cmd) data + * file. + * + * @param kshark_ctx: Input location for context pointer. + * @param sd: Data stream identifier of the top buffers in the FTRACE data + * file. + * + * @returns The total number of data streams initialized on success. Otherwise + * a negative error code. + */ +int kshark_tep_init_all_buffers(struct kshark_context *kshark_ctx, + int sd) +{ + struct kshark_data_stream *top_stream, *buffer_stream; + struct tracecmd_input *buffer_input; + struct tracecmd_input *top_input; + int i, n_buffers, sd_buffer, ret; + + top_stream = kshark_get_data_stream(kshark_ctx, sd); + if (!top_stream) + return -EFAULT; + + top_input = kshark_get_tep_input(top_stream); + if (!top_input) + return -EFAULT; + + n_buffers = tracecmd_buffer_instances(top_input); + for (i = 0; i < n_buffers; ++i) { + sd_buffer = kshark_add_stream(kshark_ctx); + if (sd_buffer < 0) + return -EFAULT; + + buffer_stream = kshark_ctx->stream[sd_buffer]; + + set_stream_fields(top_input, i, + top_stream->file, + tracecmd_buffer_instance_name(top_input, i), + buffer_stream, + &buffer_input); + + if (!buffer_stream->name || !buffer_stream->file) { + free(buffer_stream->name); + free(buffer_stream->file); + ret = -ENOMEM; + break; + } + + ret = kshark_tep_stream_init(buffer_stream, buffer_input); + if (ret != 0) + return -EFAULT; + } + + return n_buffers; +} + +/** Is this a stream corresponding to the "top" buffer in the file. */ +bool kshark_tep_is_top_stream(struct kshark_data_stream *stream) +{ + return strcmp(stream->name, KS_UNNAMED) == 0; +} + /** Check is the file contains TEP tracing data. */ bool kshark_tep_check_data(const char *file_name) { @@ -1510,3 +1755,149 @@ char **kshark_tracecmd_local_tracers() { return tracefs_tracers(tracefs_tracing_dir()); } + +/** + * @brief Free an array, allocated by kshark_tracecmd_get_hostguest_mapping() API + * + * + * @param map: Array, allocated by kshark_tracecmd_get_hostguest_mapping() API + * @param count: Number of entries in the array + * + */ +void kshark_tracecmd_free_hostguest_map(struct kshark_host_guest_map *map, int count) +{ + int i; + + if (!map) + return; + for (i = 0; i < count; i++) { + free(map[i].guest_name); + free(map[i].cpu_pid); + memset(&map[i], 0, sizeof(*map)); + } + free(map); +} + +/** + * @brief Get mapping of guest VCPU to host task, running that VCPU. + * Array of mappings for each guest is allocated and returned + * in map input parameter. + * + * + * @param map: Returns allocated array of kshark_host_guest_map structures, each + * one describing VCPUs mapping of one guest. + * + * @return The number of entries in the *map array, or a negative error code on + * failure. + */ +int kshark_tracecmd_get_hostguest_mapping(struct kshark_host_guest_map **map) +{ + struct kshark_host_guest_map *gmap = NULL; + struct tracecmd_input *peer_handle = NULL; + struct kshark_data_stream *peer_stream; + struct tracecmd_input *guest_handle = NULL; + struct kshark_data_stream *guest_stream; + struct kshark_context *kshark_ctx = NULL; + unsigned long long trace_id; + const char *name; + int vcpu_count; + const int *cpu_pid; + int *stream_ids; + int i, j, k; + int count = 0; + int ret; + + if (!map || !kshark_instance(&kshark_ctx)) + return -EFAULT; + if (*map) + return -EEXIST; + + stream_ids = kshark_all_streams(kshark_ctx); + for (i = 0; i < kshark_ctx->n_streams; i++) { + guest_stream = kshark_get_data_stream(kshark_ctx, stream_ids[i]); + if (!guest_stream || !kshark_is_tep(guest_stream)) + continue; + guest_handle = kshark_get_tep_input(guest_stream); + if (!guest_handle) + continue; + trace_id = tracecmd_get_traceid(guest_handle); + if (!trace_id) + continue; + for (j = 0; j < kshark_ctx->n_streams; j++) { + if (stream_ids[i] == stream_ids[j]) + continue; + peer_stream = kshark_get_data_stream(kshark_ctx, stream_ids[j]); + if (!peer_stream || !kshark_is_tep(guest_stream)) + continue; + peer_handle = kshark_get_tep_input(peer_stream); + if (!peer_handle) + continue; + ret = tracecmd_get_guest_cpumap(peer_handle, trace_id, + &name, &vcpu_count, &cpu_pid); + if (!ret && vcpu_count) { + gmap = realloc(*map, + (count + 1) * sizeof(struct kshark_host_guest_map)); + if (!gmap) + goto mem_error; + *map = gmap; + memset(&gmap[count], 0, sizeof(struct kshark_host_guest_map)); + count++; + gmap[count - 1].guest_id = stream_ids[i]; + gmap[count - 1].host_id = stream_ids[j]; + gmap[count - 1].guest_name = strdup(name); + if (!gmap[count - 1].guest_name) + goto mem_error; + gmap[count - 1].vcpu_count = vcpu_count; + gmap[count - 1].cpu_pid = malloc(sizeof(int) * vcpu_count); + if (!gmap[count - 1].cpu_pid) + goto mem_error; + for (k = 0; k < vcpu_count; k++) + gmap[count - 1].cpu_pid[k] = cpu_pid[k]; + break; + } + } + } + + free(stream_ids); + return count; + +mem_error: + free(stream_ids); + if (*map) { + kshark_tracecmd_free_hostguest_map(*map, count); + *map = NULL; + } + + return -ENOMEM; +} + +/** + * @brief Find the data stream corresponding the top buffer of a FTRACE + * (trace-cmd) data file. + * + * @param kshark_ctx: Input location for context pointer. + * @param file: The name of the file. + * + * @returns Data stream identifier of the top buffers in the FTRACE data + * fileon success. Otherwise a negative error code. + */ +int kshark_tep_find_top_stream(struct kshark_context *kshark_ctx, + const char *file) +{ + struct kshark_data_stream *top_stream = NULL, *stream; + int i, *stream_ids = kshark_all_streams(kshark_ctx); + + for (i = 0; i < kshark_ctx->n_streams; ++i) { + stream = kshark_ctx->stream[stream_ids[i]]; + if (strcmp(stream->file, file) == 0 && + kshark_tep_is_top_stream(stream)) + top_stream = stream; + } + + free(stream_ids); + + if (!top_stream) + return -EEXIST; + + return top_stream->stream_id; +} diff --git a/src/libkshark-tepdata.h b/src/libkshark-tepdata.h index 529f1e9..c9bb3ce 100644 --- a/src/libkshark-tepdata.h +++ b/src/libkshark-tepdata.h @@ -51,11 +51,60 @@ void kshark_tep_filter_reset(struct kshark_data_stream *stream); char **kshark_tracecmd_local_tracers(); +struct tep_handle; + +struct tep_handle *kshark_get_tep(struct kshark_data_stream *stream); + +struct tracecmd_input; + +struct tracecmd_input *kshark_get_tep_input(struct kshark_data_stream *stream); + struct tep_record; ssize_t kshark_load_tep_records(struct kshark_context *kshark_ctx, int sd, struct tep_record ***data_rows); +/** + * Structure representing the mapping between the virtual CPUs and their + * corresponding processes in the host. + */ +struct kshark_host_guest_map { + /** ID of guest stream */ + int guest_id; + + /** ID of host stream */ + int host_id; + + /** Guest name */ + char *guest_name; + + /** Number of guest's CPUs in *cpu_pid array */ + int vcpu_count; + + /** Array of host task PIDs, index is the VCPU id */ + int *cpu_pid; +}; + +void kshark_tracecmd_free_hostguest_map(struct kshark_host_guest_map *map, + int count); + +int kshark_tracecmd_get_hostguest_mapping(struct kshark_host_guest_map **map); + +char **kshark_tep_get_buffer_names(struct kshark_context *kshark_ctx, int sd, + int *n_buffers); + +int kshark_tep_open_buffer(struct kshark_context *kshark_ctx, int sd, + const char *buffer_name); + +int kshark_tep_init_all_buffers(struct kshark_context *kshark_ctx, int sd); + +int kshark_tep_handle_plugins(struct kshark_context *kshark_ctx, int sd); + +int kshark_tep_find_top_stream(struct kshark_context *kshark_ctx, + const char *file); + +bool kshark_tep_is_top_stream(struct kshark_data_stream *stream); + #ifdef __cplusplus } #endif diff --git a/src/libkshark.c b/src/libkshark.c index d85f894..edbea9c 100644 --- a/src/libkshark.c +++ b/src/libkshark.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: LGPL-2.1 /* - * Copyright (C) 2017 VMware Inc, Yordan Karadzhov + * Copyright (C) 2017 VMware Inc, Yordan Karadzhov (VMware) */ /** @@ -9,8 +9,10 @@ * @brief API for processing of tracing data. */ +#ifndef _GNU_SOURCE /** Use GNU C Library. */ -#define _GNU_SOURCE 1 +#define _GNU_SOURCE +#endif // _GNU_SOURCE // C #include @@ -37,23 +39,6 @@ static bool kshark_default_context(struct kshark_context **context) kshark_ctx->stream_info.array_size = KS_DEFAULT_NUM_STREAMS; kshark_ctx->stream_info.max_stream_id = -1; - kshark_ctx->collections = NULL; - kshark_ctx->plugins = NULL; - - kshark_ctx->show_task_filter = tracecmd_filter_id_hash_alloc(); - kshark_ctx->hide_task_filter = tracecmd_filter_id_hash_alloc(); - - kshark_ctx->show_event_filter = tracecmd_filter_id_hash_alloc(); - kshark_ctx->hide_event_filter = tracecmd_filter_id_hash_alloc(); - - kshark_ctx->show_cpu_filter = tracecmd_filter_id_hash_alloc(); - kshark_ctx->hide_cpu_filter = tracecmd_filter_id_hash_alloc(); - - kshark_ctx->filter_mask = 0x0; - - kshark_ctx->stream_info.array_size = KS_DEFAULT_NUM_STREAMS; - kshark_ctx->stream_info.max_stream_id = -1; - /* Will free kshark_context_handler. */ kshark_free(NULL); @@ -468,6 +453,12 @@ int kshark_close(struct kshark_context *kshark_ctx, int sd) if (!stream) return -EFAULT; + /* + * All data collections are file specific. Make sure that collections + * from this file are not going to be used with another file. + */ + kshark_unregister_stream_collections(&kshark_ctx->collections, sd); + /* Close all active plugins for this stream. */ if (stream->plugins) { kshark_handle_all_dpis(stream, KSHARK_PLUGIN_CLOSE); @@ -1042,103 +1033,134 @@ ssize_t kshark_get_task_pids(struct kshark_context *kshark_ctx, int sd, return stream->tasks->count; } -static bool filter_find(struct tracecmd_filter_id *filter, int pid, +static bool filter_find(struct kshark_hash_id *filter, int pid, bool test) { return !filter || !filter->count || - !!(unsigned long)tracecmd_filter_id_find(filter, pid) == test; + kshark_hash_id_find(filter, pid) == test; +} + +static bool kshark_show_task(struct kshark_data_stream *stream, int pid) +{ + return filter_find(stream->show_task_filter, pid, true) && + filter_find(stream->hide_task_filter, pid, false); +} + +static bool kshark_show_event(struct kshark_data_stream *stream, int pid) +{ + return filter_find(stream->show_event_filter, pid, true) && + filter_find(stream->hide_event_filter, pid, false); } -static bool kshark_show_task(struct kshark_context *kshark_ctx, int pid) +static bool kshark_show_cpu(struct kshark_data_stream *stream, int cpu) { - return filter_find(kshark_ctx->show_task_filter, pid, true) && - filter_find(kshark_ctx->hide_task_filter, pid, false); + return filter_find(stream->show_cpu_filter, cpu, true) && + filter_find(stream->hide_cpu_filter, cpu, false); } -static bool kshark_show_event(struct kshark_context *kshark_ctx, int pid) +static struct kshark_hash_id *get_filter(struct kshark_context *kshark_ctx, + int sd, + enum kshark_filter_type filter_id) { - return filter_find(kshark_ctx->show_event_filter, pid, true) && - filter_find(kshark_ctx->hide_event_filter, pid, false); + struct kshark_data_stream *stream; + + stream = kshark_get_data_stream(kshark_ctx, sd); + if (!stream) + return NULL; + + return kshark_get_filter(stream, filter_id); } -static bool kshark_show_cpu(struct kshark_context *kshark_ctx, int cpu) +/** + * @brief Get an Id Filter. + * + * @param stream: Input location for a Trace data stream pointer. + * @param filter_id: Identifier of the filter. + */ +struct kshark_hash_id * +kshark_get_filter(struct kshark_data_stream *stream, + enum kshark_filter_type filter_id) { - return filter_find(kshark_ctx->show_cpu_filter, cpu, true) && - filter_find(kshark_ctx->hide_cpu_filter, cpu, false); + switch (filter_id) { + case KS_SHOW_CPU_FILTER: + return stream->show_cpu_filter; + case KS_HIDE_CPU_FILTER: + return stream->hide_cpu_filter; + case KS_SHOW_EVENT_FILTER: + return stream->show_event_filter; + case KS_HIDE_EVENT_FILTER: + return stream->hide_event_filter; + case KS_SHOW_TASK_FILTER: + return stream->show_task_filter; + case KS_HIDE_TASK_FILTER: + return stream->hide_task_filter; + default: + return NULL; + } } /** - * @brief Add an Id value to the filster specified by "filter_id". + * @brief Add an Id value to the filter specified by "filter_id". * * @param kshark_ctx: Input location for the session context pointer. + * @param sd: Data stream identifier. * @param filter_id: Identifier of the filter. * @param id: Id value to be added to the filter. */ -void kshark_filter_add_id(struct kshark_context *kshark_ctx, +void kshark_filter_add_id(struct kshark_context *kshark_ctx, int sd, int filter_id, int id) { - struct tracecmd_filter_id *filter; + struct kshark_hash_id *filter; - switch (filter_id) { - case KS_SHOW_CPU_FILTER: - filter = kshark_ctx->show_cpu_filter; - break; - case KS_HIDE_CPU_FILTER: - filter = kshark_ctx->hide_cpu_filter; - break; - case KS_SHOW_EVENT_FILTER: - filter = kshark_ctx->show_event_filter; - break; - case KS_HIDE_EVENT_FILTER: - filter = kshark_ctx->hide_event_filter; - break; - case KS_SHOW_TASK_FILTER: - filter = kshark_ctx->show_task_filter; - break; - case KS_HIDE_TASK_FILTER: - filter = kshark_ctx->hide_task_filter; - break; - default: - return; + filter = get_filter(kshark_ctx, sd, filter_id); + if (filter) + kshark_hash_id_add(filter, id); +} + +/** + * @brief Get an array containing all Ids associated with a given Id Filter. + * + * @param kshark_ctx: Input location for context pointer. + * @param sd: Data stream identifier. + * @param filter_id: Identifier of the filter. + * @param n: Output location for the size of the returned array. + * + * @return The user is responsible for freeing the array. + */ +int *kshark_get_filter_ids(struct kshark_context *kshark_ctx, int sd, + int filter_id, int *n) +{ + struct kshark_hash_id *filter; + + filter = get_filter(kshark_ctx, sd, filter_id); + if (filter) { + if (n) + *n = filter->count; + + return kshark_hash_ids(filter); } - tracecmd_filter_id_add(filter, id); + if (n) + *n = 0; + + return NULL; } /** - * @brief Clear (reset) the filster specified by "filter_id". + * @brief Clear (reset) the filter specified by "filter_id". * * @param kshark_ctx: Input location for the session context pointer. + * @param sd: Data stream identifier. * @param filter_id: Identifier of the filter. */ -void kshark_filter_clear(struct kshark_context *kshark_ctx, int filter_id) +void kshark_filter_clear(struct kshark_context *kshark_ctx, int sd, + int filter_id) { - struct tracecmd_filter_id *filter; - - switch (filter_id) { - case KS_SHOW_CPU_FILTER: - filter = kshark_ctx->show_cpu_filter; - break; - case KS_HIDE_CPU_FILTER: - filter = kshark_ctx->hide_cpu_filter; - break; - case KS_SHOW_EVENT_FILTER: - filter = kshark_ctx->show_event_filter; - break; - case KS_HIDE_EVENT_FILTER: - filter = kshark_ctx->hide_event_filter; - break; - case KS_SHOW_TASK_FILTER: - filter = kshark_ctx->show_task_filter; - break; - case KS_HIDE_TASK_FILTER: - filter = kshark_ctx->hide_task_filter; - break; - default: - return; - } + struct kshark_hash_id *filter; - tracecmd_filter_id_clear(filter); + filter = get_filter(kshark_ctx, sd, filter_id); + if (filter) + kshark_hash_id_clear(filter); } /** @@ -1148,7 +1170,7 @@ void kshark_filter_clear(struct kshark_context *kshark_ctx, int filter_id) * * @returns True if the Id filter is set, otherwise False. */ -bool kshark_this_filter_is_set(struct tracecmd_filter_id *filter) +bool kshark_this_filter_is_set(struct kshark_hash_id *filter) { return filter && filter->count; } @@ -1157,17 +1179,49 @@ bool kshark_this_filter_is_set(struct tracecmd_filter_id *filter) * @brief Check if an Id filter is set. * * @param kshark_ctx: Input location for the session context pointer. + * @param sd: Data stream identifier. * - * @returns True if at least one Id filter is set, otherwise False. + * @returns True if at least one Id filter of the stream is set, otherwise + * False. */ -bool kshark_filter_is_set(struct kshark_context *kshark_ctx) +bool kshark_filter_is_set(struct kshark_context *kshark_ctx, int sd) { - return kshark_this_filter_is_set(kshark_ctx->show_task_filter) || -- kshark_this_filter_is_set(kshark_ctx->hide_task_filter) || -- kshark_this_filter_is_set(kshark_ctx->show_cpu_filter) || -- kshark_this_filter_is_set(kshark_ctx->hide_cpu_filter) || -- kshark_this_filter_is_set(kshark_ctx->show_event_filter) || -- kshark_this_filter_is_set(kshark_ctx->hide_event_filter); + struct kshark_data_stream *stream; + + stream = kshark_get_data_stream(kshark_ctx, sd); + if (!stream) + return false; + + return kshark_this_filter_is_set(stream->show_task_filter) || + kshark_this_filter_is_set(stream->hide_task_filter) || + kshark_this_filter_is_set(stream->show_cpu_filter) || + kshark_this_filter_is_set(stream->hide_cpu_filter) || + kshark_this_filter_is_set(stream->show_event_filter) || + kshark_this_filter_is_set(stream->hide_event_filter); +} + +/** + * @brief Apply filters to a given entry. + * + * @param kshark_ctx: Input location for the session context pointer. + * @param stream: Input location for a Trace data stream pointer. + * @param entry: Input location for entry. + */ +void kshark_apply_filters(struct kshark_context *kshark_ctx, + struct kshark_data_stream *stream, + struct kshark_entry *entry) +{ + /* Apply event filtering. */ + if (!kshark_show_event(stream, entry->event_id)) + unset_event_filter_flag(kshark_ctx, entry); + + /* Apply CPU filtering. */ + if (!kshark_show_cpu(stream, entry->cpu)) + entry->visible &= ~kshark_ctx->filter_mask; + + /* Apply task filtering. */ + if (!kshark_show_task(stream, entry->pid)) + entry->visible &= ~kshark_ctx->filter_mask; } static void set_all_visible(uint16_t *v) { @@ -1175,56 +1229,100 @@ static void set_all_visible(uint16_t *v) { *v |= 0xFF & ~KS_PLUGIN_UNTOUCHED_MASK; } +static void filter_entries(struct kshark_context *kshark_ctx, int sd, + struct kshark_entry **data, size_t n_entries) +{ + struct kshark_data_stream *stream = NULL; + size_t i; + + /* Sanity checks before starting. */ + if (sd >= 0) { + /* We will filter particular Data stream. */ + stream = kshark_get_data_stream(kshark_ctx, sd); + if (!stream) + return; + + if (kshark_is_tep(stream) && + kshark_tep_filter_is_set(stream)) { + /* The advanced filter is set. */ + fprintf(stderr, + "Failed to filter (sd = %i)!\n", sd); + fprintf(stderr, + "Reset the Advanced filter or reload the data.\n"); + + return; + } + + if (!kshark_filter_is_set(kshark_ctx, sd)) + return; + } + + /* Apply only the Id filters. */ + for (i = 0; i < n_entries; ++i) { + if (sd >= 0) { + /* + * We only filter particular stream. Chack is the entry + * belongs to this stream. + */ + if (data[i]->stream_id != sd) + continue; + } else { + /* We filter all streams. */ + stream = kshark_ctx->stream[data[i]->stream_id]; + } + + /* Start with and entry which is visible everywhere. */ + set_all_visible(&data[i]->visible); + + /* Apply Id filtering. */ + kshark_apply_filters(kshark_ctx, stream, data[i]); + } +} + /** * @brief This function loops over the array of entries specified by "data" - * and "n_entries" and sets the "visible" fields of each entry - * according to the criteria provided by the filters of the session's - * context. The field "filter_mask" of the session's context is used to - * control the level of visibility/invisibility of the entries which - * are filtered-out. + * and "n_entries" and sets the "visible" fields of each entry from a + * given Data stream according to the criteria provided by the filters + * of the session's context. The field "filter_mask" of the session's + * context is used to control the level of visibility/invisibility of + * the entries which are filtered-out. * WARNING: Do not use this function if the advanced filter is set. * Applying the advanced filter requires access to prevent_record, * hence the data has to be reloaded using kshark_load_entries(). * * @param kshark_ctx: Input location for the session context pointer. + * @param sd: Data stream identifier. * @param data: Input location for the trace data to be filtered. * @param n_entries: The size of the inputted data. */ -void kshark_filter_entries(struct kshark_context *kshark_ctx, - struct kshark_entry **data, - size_t n_entries) +void kshark_filter_stream_entries(struct kshark_context *kshark_ctx, + int sd, + struct kshark_entry **data, + size_t n_entries) { - int i; - - if (kshark_ctx->advanced_event_filter->filters) { - /* The advanced filter is set. */ - fprintf(stderr, - "Failed to filter!\n"); - fprintf(stderr, - "Reset the Advanced filter or reload the data.\n"); - return; - } - - if (!kshark_filter_is_set(kshark_ctx)) - return; - - /* Apply only the Id filters. */ - for (i = 0; i < n_entries; ++i) { - /* Start with and entry which is visible everywhere. */ - set_all_visible(&data[i]->visible); - - /* Apply event filtering. */ - if (!kshark_show_event(kshark_ctx, data[i]->event_id)) - unset_event_filter_flag(kshark_ctx, data[i]); - - /* Apply CPU filtering. */ - if (!kshark_show_cpu(kshark_ctx, data[i]->cpu)) - data[i]->visible &= ~kshark_ctx->filter_mask; + if (sd >= 0) + filter_entries(kshark_ctx, sd, data, n_entries); +} - /* Apply task filtering. */ - if (!kshark_show_task(kshark_ctx, data[i]->pid)) - data[i]->visible &= ~kshark_ctx->filter_mask; - } +/** + * @brief This function loops over the array of entries specified by "data" + * and "n_entries" and sets the "visible" fields of each entry from + * all Data stream according to the criteria provided by the filters + * of the session's context. The field "filter_mask" of the session's + * context is used to control the level of visibility/invisibility of + * the entries which are filtered-out. + * WARNING: Do not use this function if the advanced filter is set. + * Applying the advanced filter requires access to prevent_record, + * hence the data has to be reloaded using kshark_load_data_entries(). + * + * @param kshark_ctx: Input location for the session context pointer. + * @param data: Input location for the trace data to be filtered. + * @param n_entries: The size of the inputted data. + */ +void kshark_filter_all_entries(struct kshark_context *kshark_ctx, + struct kshark_entry **data, size_t n_entries) +{ + filter_entries(kshark_ctx, -1, data, n_entries); } /** @@ -1241,6 +1339,7 @@ void kshark_clear_all_filters(struct kshark_context *kshark_ctx, size_t n_entries) { int i; + for (i = 0; i < n_entries; ++i) set_all_visible(&data[i]->visible); } @@ -1364,15 +1463,15 @@ void kshark_convert_nano(uint64_t time, uint64_t *sec, uint64_t *usec) * @param h: Array index specifying the upper edge of the range to search in. * * @returns On success, the first kshark_entry inside the range, having a - timestamp equal or bigger than "time". - If all entries inside the range have timestamps greater than "time" - the function returns BSEARCH_ALL_GREATER (negative value). - If all entries inside the range have timestamps smaller than "time" - the function returns BSEARCH_ALL_SMALLER (negative value). + * timestamp equal or bigger than "time". + * If all entries inside the range have timestamps greater than "time" + * the function returns BSEARCH_ALL_GREATER (negative value). + * If all entries inside the range have timestamps smaller than "time" + * the function returns BSEARCH_ALL_SMALLER (negative value). */ -ssize_t kshark_find_entry_by_time(uint64_t time, - struct kshark_entry **data, - size_t l, size_t h) +ssize_t kshark_find_entry_by_time(int64_t time, + struct kshark_entry **data, + size_t l, size_t h) { size_t mid; @@ -1396,15 +1495,16 @@ ssize_t kshark_find_entry_by_time(uint64_t time, * * @param kshark_ctx: Input location for the session context pointer. * @param e: kshark_entry to be checked. + * @param sd: Data stream identifier. * @param pid: Matching condition value. * * @returns True if the Pid of the entry matches the value of "pid". * Else false. */ bool kshark_match_pid(struct kshark_context *kshark_ctx, - struct kshark_entry *e, int pid) + struct kshark_entry *e, int sd, int *pid) { - if (e->pid == pid) + if (e->stream_id == sd && e->pid == *pid) return true; return false; @@ -1415,20 +1515,80 @@ bool kshark_match_pid(struct kshark_context *kshark_ctx, * * @param kshark_ctx: Input location for the session context pointer. * @param e: kshark_entry to be checked. + * @param sd: Data stream identifier. * @param cpu: Matching condition value. * * @returns True if the Cpu of the entry matches the value of "cpu". * Else false. */ bool kshark_match_cpu(struct kshark_context *kshark_ctx, - struct kshark_entry *e, int cpu) + struct kshark_entry *e, int sd, int *cpu) { - if (e->cpu == cpu) + if (e->stream_id == sd && e->cpu == *cpu) return true; return false; } +/** + * @brief Simple event Id matching function to be user for data requests. + * + * @param kshark_ctx: Input location for the session context pointer. + * @param e: kshark_entry to be checked. + * @param sd: Data stream identifier. + * @param event_id: Matching condition value. + * + * @returns True if the event Id of the entry matches the value of "event_id". + * Else false. + */ +bool kshark_match_event_id(struct kshark_context *kshark_ctx, + struct kshark_entry *e, int sd, int *event_id) +{ + return e->stream_id == sd && e->event_id == *event_id; +} + +/** + * @brief Simple Event Id and PID matching function to be user for data requests. + * + * @param kshark_ctx: Input location for the session context pointer. + * @param e: kshark_entry to be checked. + * @param sd: Data stream identifier. + * @param values: An array of matching condition value. + * values[0] is the matches PID and values[1] is the matches event Id. + * + * @returns True if the event Id of the entry matches the values. + * Else false. + */ +bool kshark_match_event_and_pid(struct kshark_context *kshark_ctx, + struct kshark_entry *e, + int sd, int *values) +{ + return e->stream_id == sd && + e->event_id == values[0] && + e->pid == values[1]; +} + +/** + * @brief Simple Event Id and CPU matching function to be user for data requests. + * + * @param kshark_ctx: Input location for the session context pointer. + * @param e: kshark_entry to be checked. + * @param sd: Data stream identifier. + * @param values: An array of matching condition value. + * values[0] is the matches PID and values[1] is the matches event Id. + * + * @returns True if the event Id of the entry matches the values. + * Else false. + */ +bool kshark_match_event_and_cpu(struct kshark_context *kshark_ctx, + struct kshark_entry *e, + int sd, int *values) +{ + return e->stream_id == sd && + e->event_id == values[0] && + e->cpu == values[1]; +} + /** * @brief Create Data request. The request defines the properties of the * requested kshark_entry. @@ -1437,8 +1597,9 @@ bool kshark_match_cpu(struct kshark_context *kshark_ctx, * where the search starts. * @param n: Number of array elements to search in. * @param cond: Matching condition function. - * @param val: Matching condition value, used by the Matching condition - * function. + * @param sd: Data stream identifier. + * @param values: Matching condition values, used by the Matching condition + * function. * @param vis_only: If true, a visible entry is requested. * @param vis_mask: If "vis_only" is true, use this mask to specify the level * of visibility of the requested entry. @@ -1449,7 +1610,7 @@ bool kshark_match_cpu(struct kshark_context *kshark_ctx, */ struct kshark_entry_request * kshark_entry_request_alloc(size_t first, size_t n, - matching_condition_func cond, int val, + matching_condition_func cond, int sd, int *values, bool vis_only, int vis_mask) { struct kshark_entry_request *req = malloc(sizeof(*req)); @@ -1464,7 +1625,8 @@ kshark_entry_request_alloc(size_t first, size_t n, req->first = first; req->n = n; req->cond = cond; - req->val = val; + req->sd = sd; + req->values = values; req->vis_only = vis_only; req->vis_mask = vis_mask; @@ -1518,7 +1680,7 @@ get_entry(const struct kshark_entry_request *req, */ assert((inc > 0 && start < end) || (inc < 0 && start > end)); for (i = start; i != end; i += inc) { - if (req->cond(kshark_ctx, data[i], req->val)) { + if (req->cond(kshark_ctx, data[i], req->sd, req->values)) { /* * Data satisfying the condition has been found. */ diff --git a/src/libkshark.h b/src/libkshark.h index 07c586d..0a560f1 100644 --- a/src/libkshark.h +++ b/src/libkshark.h @@ -14,6 +14,7 @@ // C #include +#include #include #include @@ -24,10 +25,6 @@ extern "C" { #endif -// trace-cmd -#include "trace-cmd/trace-cmd.h" -#include "trace-filter-hash.h" - // KernelShark #include "libkshark-plugin.h" @@ -378,27 +375,6 @@ struct kshark_context { /** Parameters of the stream descriptor array. */ struct kshark_stream_array_descriptor stream_info; - /** A mutex, used to protect the access to the input file. */ - pthread_mutex_t input_mutex; - - /** Hash of tasks to filter on. */ - struct tracecmd_filter_id *show_task_filter; - - /** Hash of tasks to not display. */ - struct tracecmd_filter_id *hide_task_filter; - - /** Hash of events to filter on. */ - struct tracecmd_filter_id *show_event_filter; - - /** Hash of events to not display. */ - struct tracecmd_filter_id *hide_event_filter; - - /** Hash of CPUs to filter on. */ - struct tracecmd_filter_id *show_cpu_filter; - - /** Hash of CPUs to not display. */ - struct tracecmd_filter_id *hide_cpu_filter; - /** * Bit mask, controlling the visibility of the entries after filtering. * If given bit is set here, all entries which are filtered-out will @@ -406,12 +382,6 @@ struct kshark_context { */ uint8_t filter_mask; - /** - * Filter allowing sophisticated filtering based on the content of - * the event. - */ - struct tep_event_filter *advanced_event_filter; - /** List of Data collections. */ struct kshark_entry_collection *collections; @@ -570,14 +540,22 @@ enum kshark_filter_type { KS_HIDE_CPU_FILTER, }; -void kshark_filter_add_id(struct kshark_context *kshark_ctx, +struct kshark_hash_id * +kshark_get_filter(struct kshark_data_stream *stream, + enum kshark_filter_type filter_id); + +void kshark_filter_add_id(struct kshark_context *kshark_ctx, int sd, int filter_id, int id); -void kshark_filter_clear(struct kshark_context *kshark_ctx, int filter_id); +int *kshark_get_filter_ids(struct kshark_context *kshark_ctx, int sd, + int filter_id, int *n); + +void kshark_filter_clear(struct kshark_context *kshark_ctx, int sd, + int filter_id); -bool kshark_this_filter_is_set(struct tracecmd_filter_id *filter); +bool kshark_this_filter_is_set(struct kshark_hash_id *filter); -bool kshark_filter_is_set(struct kshark_context *kshark_ctx); +bool kshark_filter_is_set(struct kshark_context *kshark_ctx, int sd); static inline void unset_event_filter_flag(struct kshark_context *kshark_ctx, struct kshark_entry *e) @@ -594,9 +572,16 @@ static inline void unset_event_filter_flag(struct kshark_context *kshark_ctx, e->visible &= ~event_mask; } -void kshark_filter_entries(struct kshark_context *kshark_ctx, - struct kshark_entry **data, - size_t n_entries); +void kshark_apply_filters(struct kshark_context *kshark_ctx, + struct kshark_data_stream *stream, + struct kshark_entry *entry); + +void kshark_filter_stream_entries(struct kshark_context *kshark_ctx, int sd, + struct kshark_entry **data, + size_t n_entries); + +void kshark_filter_all_entries(struct kshark_context *kshark_ctx, + struct kshark_entry **data, size_t n_entries); void kshark_clear_all_filters(struct kshark_context *kshark_ctx, struct kshark_entry **data, @@ -607,10 +592,10 @@ void kshark_plugin_actions(struct kshark_data_stream *stream, /** Search failed identifiers. */ enum kshark_search_failed { - /** All entries have timestamps greater timestamps. */ + /** All entries have greater timestamps. */ BSEARCH_ALL_GREATER = -1, - /** All entries have timestamps smaller timestamps. */ + /** All entries have smaller timestamps. */ BSEARCH_ALL_SMALLER = -2, }; @@ -626,15 +611,26 @@ enum kshark_search_failed { } \ }) -ssize_t kshark_find_entry_by_time(uint64_t time, +ssize_t kshark_find_entry_by_time(int64_t time, struct kshark_entry **data_rows, size_t l, size_t h); bool kshark_match_pid(struct kshark_context *kshark_ctx, - struct kshark_entry *e, int pid); + struct kshark_entry *e, int sd, int *pid); bool kshark_match_cpu(struct kshark_context *kshark_ctx, - struct kshark_entry *e, int cpu); + struct kshark_entry *e, int sd, int *cpu); + +bool kshark_match_event_id(struct kshark_context *kshark_ctx, + struct kshark_entry *e, int sd, int *event_id); + +bool kshark_match_event_and_pid(struct kshark_context *kshark_ctx, + struct kshark_entry *e, + int sd, int *values); + +bool kshark_match_event_and_cpu(struct kshark_context *kshark_ctx, + struct kshark_entry *e, + int sd, int *values); /** * Empty bin identifier. @@ -652,7 +648,7 @@ bool kshark_match_cpu(struct kshark_context *kshark_ctx, /** Matching condition function type. To be user for data requests */ typedef bool (matching_condition_func)(struct kshark_context*, struct kshark_entry*, - int); + int, int*); /** * Data request structure, defining the properties of the required @@ -674,10 +670,13 @@ struct kshark_entry_request { /** Matching condition function. */ matching_condition_func *cond; + /** Data stream identifier. */ + int sd; + /** * Matching condition value, used by the Matching condition function. */ - int val; + int *values; /** If true, a visible entry is requested. */ bool vis_only; @@ -691,7 +690,7 @@ struct kshark_entry_request { struct kshark_entry_request * kshark_entry_request_alloc(size_t first, size_t n, - matching_condition_func cond, int val, + matching_condition_func cond, int sd, int *values, bool vis_only, int vis_mask); void kshark_free_entry_request(struct kshark_entry_request *req); @@ -708,8 +707,8 @@ kshark_get_entry_back(const struct kshark_entry_request *req, /** * Data collections are used to optimize the search for an entry having an - * abstract property, defined by a Matching condition function and a value. - * When a collection is processed, the data which is relevant for the + * abstract property, defined by a Matching condition function and an array of + * values. When a collection is processed, the data which is relevant for the * collection is enclosed in "Data intervals", defined by pairs of "Resume" and * "Break" points. It is guaranteed that the data outside of the intervals * contains no entries satisfying the abstract matching condition. However, the @@ -726,11 +725,17 @@ struct kshark_entry_collection { /** Matching condition function, used to define the collections. */ matching_condition_func *cond; + /** Data stream identifier. */ + int stream_id; + /** - * Matching condition value, used by the Matching condition finction - * to define the collections. + * Array of matching condition values, used by the Matching condition + * finction to define the collection. */ - int val; + int *values; + + /** The suze of the array of matching condition values. */ + int n_val; /** * Array of indexes defining the beginning of each individual data @@ -753,26 +758,30 @@ kshark_add_collection_to_list(struct kshark_context *kshark_ctx, struct kshark_entry **data, size_t n_rows, matching_condition_func cond, - int val, + int sd, int *values, size_t n_val, size_t margin); struct kshark_entry_collection * kshark_register_data_collection(struct kshark_context *kshark_ctx, struct kshark_entry **data, size_t n_rows, - matching_condition_func cond, int val, + matching_condition_func cond, + int sd, int *values, size_t n_val, size_t margin); void kshark_unregister_data_collection(struct kshark_entry_collection **col, matching_condition_func cond, - int val); + int sd, int *values, size_t n_val); struct kshark_entry_collection * kshark_find_data_collection(struct kshark_entry_collection *col, matching_condition_func cond, - int val); + int sd, int *values, size_t n_val); void kshark_reset_data_collection(struct kshark_entry_collection *col); +void kshark_unregister_stream_collections(struct kshark_entry_collection **col, + int sd); + void kshark_free_collection_list(struct kshark_entry_collection *col); const struct kshark_entry * @@ -914,17 +923,27 @@ bool kshark_export_adv_filters(struct kshark_context *kshark_ctx, bool kshark_import_adv_filters(struct kshark_context *kshark_ctx, struct kshark_config_doc *conf); +bool kshark_export_event_filter(struct kshark_data_stream *stream, + enum kshark_filter_type filter_type, + const char *filter_name, + struct kshark_config_doc *conf); + +int kshark_import_event_filter(struct kshark_data_stream *stream, + enum kshark_filter_type filter_type, + const char *filter_name, + struct kshark_config_doc *conf); + bool kshark_export_user_mask(struct kshark_context *kshark_ctx, struct kshark_config_doc **conf); bool kshark_import_user_mask(struct kshark_context *kshark_ctx, struct kshark_config_doc *conf); -bool kshark_export_filter_array(struct tracecmd_filter_id *filter, +bool kshark_export_filter_array(struct kshark_hash_id *filter, const char *filter_name, struct kshark_config_doc *conf); -bool kshark_import_filter_array(struct tracecmd_filter_id *filter, +bool kshark_import_filter_array(struct kshark_hash_id *filter, const char *filter_name, struct kshark_config_doc *conf); diff --git a/src/trace-filter-hash.h b/src/trace-filter-hash.h deleted file mode 100644 index 4111c41..0000000 --- a/src/trace-filter-hash.h +++ /dev/null @@ -1,64 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1 */ -/* - * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt - * Copyright (C) 2018 VMware Inc, Steven Rostedt - * - */ -#ifndef _TRACE_FILTER_HASH_H -#define _TRACE_FILTER_HASH_H - -#include - -struct tracecmd_filter_id_item { - struct tracecmd_filter_id_item *next; - int id; -}; - -struct tracecmd_filter_id { - struct tracecmd_filter_id_item **hash; - int count; -}; - -/** - * tracecmd_quick_hash - A quick (non secured) hash alogirthm - * @val: The value to perform the hash on - * @bits: The size in bits you need to return - * - * This is a quick hashing function adapted from Donald E. Knuth's 32 - * bit multiplicative hash. See The Art of Computer Programming (TAOCP). - * Multiplication by the Prime number, closest to the golden ratio of - * 2^32. - * - * @bits is used to max the result for use cases that require - * a power of 2 return value that is less than 32 bits. Any value - * of @bits greater than 31 (or zero), will simply return the full hash on @val. - */ -static inline uint32_t tracecmd_quick_hash(uint32_t val, unsigned int bits) -{ - val *= UINT32_C(2654435761); - - if (!bits || bits > 31) - return val; - - return val & ((1 << bits) - 1); -} - -struct tracecmd_filter_id_item * - tracecmd_filter_id_find(struct tracecmd_filter_id *hash, int id); -void tracecmd_filter_id_add(struct tracecmd_filter_id *hash, int id); -void tracecmd_filter_id_remove(struct tracecmd_filter_id *hash, int id); -void tracecmd_filter_id_clear(struct tracecmd_filter_id *hash); -struct tracecmd_filter_id *tracecmd_filter_id_hash_alloc(void); -void tracecmd_filter_id_hash_free(struct tracecmd_filter_id *hash); -struct tracecmd_filter_id * - tracecmd_filter_id_hash_copy(struct tracecmd_filter_id *hash); -int *tracecmd_filter_ids(struct tracecmd_filter_id *hash); -int tracecmd_filter_id_compare(struct tracecmd_filter_id *hash1, - struct tracecmd_filter_id *hash2); - -static inline int tracecmd_filter_task_count(struct tracecmd_filter_id *hash) -{ - return hash->count; -} - -#endif /* _TRACE_FILTER_HASH_H */ From patchwork Mon Jan 4 17:47:02 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11997205 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2169BC43381 for ; Mon, 4 Jan 2021 17:49:40 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id F2F9D20770 for ; Mon, 4 Jan 2021 17:49:39 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727335AbhADRtj (ORCPT ); Mon, 4 Jan 2021 12:49:39 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34482 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727357AbhADRtj (ORCPT ); Mon, 4 Jan 2021 12:49:39 -0500 Received: from mail-ed1-x52c.google.com (mail-ed1-x52c.google.com [IPv6:2a00:1450:4864:20::52c]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 737CAC0617BA for ; Mon, 4 Jan 2021 09:48:00 -0800 (PST) Received: by mail-ed1-x52c.google.com with SMTP id i24so28216635edj.8 for ; Mon, 04 Jan 2021 09:48:00 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=hAY2AEiYfFKT+kwld1MeX36wih3RUZTbFeLFlqojP7Y=; b=MfZY03XpYu/KpEeK2MIkYp4D8Q+BRPFGluRzKuDHPEm5xMNpoFE3vpQitz59xoe8Qu 1mUYC8P/s1H2DLki5h6Y3wkVnjncldoQ+Xyo2OZNf5MmxSm7p7DzjF9pAiBedjzVrsdF gUUJ17C6Md2CGSjpUOVlNiFnW7A9DyDM7/YmHN7OCjWFNquILTMEtIidIKb5a6aHV9NI /nXLEcXlpwhIANTQQ+Fmz6raChGut8xqM5ikBKoMk8qoZY8AFiYkH3gKelawi4ehmA6l 11tckAvnmQd4mVFWEYlaW+ZpokCC7gGJqpgrR4GA37wov7old7rOBf0hSyMzjEh4GtSb I//Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=hAY2AEiYfFKT+kwld1MeX36wih3RUZTbFeLFlqojP7Y=; b=DCrBbNz5oUerjH+NF+ncK0teDPZEWGheEz7JM5t5stQWmyN2g5AT0tVDHRWjoy1AkF oPuSp4u4/q4aHpkWeBQcXeIimC1+Z3il05zbzBfkfdJFbd7EuD/ZTkJVJhep3c2rH4iW ir9AyQ/iAkUG/puSqiYuvxcQ9EYvnFnDm8qsJt35a3ZmIpJ3xnC12eGic3VJHJiHYCLK PmBnfVlISUWJjpmWgr8UbupXhagUELVoGv10xS5NkBCgzVQ2ZQMKuqiEmmgaDmGFCdw6 pEFEJxFEck8V5CUCgRe89xJywTqUWeoZxm7QfmKasTOUO/V3TS6ZqT+9RZZW0zwOLmgC h1hQ== X-Gm-Message-State: AOAM5300NIb4pI4tV9tC1AwWDP9j5WUPT+H8OSce9jPrsh7O11uaVcR3 Iw2820Waj6DaLXuomTIc0/4= X-Google-Smtp-Source: ABdhPJxuaQa5NufGGvBCmbf+PIhFYEfBqaCrztbqbq+odWgyXcgX7PC6SaPuZMy/dQcuH0OEUdgL0A== X-Received: by 2002:a50:b746:: with SMTP id g64mr71695527ede.33.1609782479151; Mon, 04 Jan 2021 09:47:59 -0800 (PST) Received: from localhost.localdomain ([95.87.199.238]) by smtp.gmail.com with ESMTPSA id l14sm44107750edq.35.2021.01.04.09.47.58 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 04 Jan 2021 09:47:58 -0800 (PST) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v8 22/44] kernel-shark: Provide merging of multiple data streams Date: Mon, 4 Jan 2021 19:47:02 +0200 Message-Id: <20210104174724.70404-23-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210104174724.70404-1-y.karadz@gmail.com> References: <20210104174724.70404-1-y.karadz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org The C API provides loading of the trace data in two different forms. The first one is an array of kshark_entries and is being used by the KernelShark GUI. The second is a matrix-like structure that has all the fields of the kshark_entry stored in separate arrays, forming the columns of the matrix. The second form of the data is used by trace-cruncher. In this patch we add methods for merging of several data streams into a single data set. Both kshark_entries and matrix forms of the data are supported. This patch includes a simple example that demonstrate how to open a file that contains multiple buffers. Each buffers is loaded into a separate Data stream and those streams are merged together. Signed-off-by: Yordan Karadzhov (VMware) --- examples/CMakeLists.txt | 4 + examples/multibufferload.c | 53 ++++++++ src/libkshark.c | 255 +++++++++++++++++++++++++++++++++++++ src/libkshark.h | 47 +++++++ 4 files changed, 359 insertions(+) create mode 100644 examples/multibufferload.c diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 8d40e42..831eee2 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -8,6 +8,10 @@ message(STATUS "datafilter") add_executable(dfilter datafilter.c) target_link_libraries(dfilter kshark) +message(STATUS "multibufferload") +add_executable(mbload multibufferload.c) +target_link_libraries(mbload kshark) + # message(STATUS "datahisto") # add_executable(dhisto datahisto.c) # target_link_libraries(dhisto kshark) diff --git a/examples/multibufferload.c b/examples/multibufferload.c new file mode 100644 index 0000000..ff15513 --- /dev/null +++ b/examples/multibufferload.c @@ -0,0 +1,53 @@ +#include +#include + +#include "libkshark.h" +#include "libkshark-tepdata.h" + +const char *default_file = "trace.dat"; + +int main(int argc, char **argv) +{ + struct kshark_context *kshark_ctx; + struct kshark_entry **data = NULL; + ssize_t r, n_rows; + int sd; + + /* Create a new kshark session. */ + kshark_ctx = NULL; + if (!kshark_instance(&kshark_ctx)) + return 1; + + /* Open a trace data file produced by trace-cmd. */ + if (argc > 1) + sd = kshark_open(kshark_ctx, argv[1]); + else + sd = kshark_open(kshark_ctx, default_file); + + if (sd < 0) { + kshark_free(kshark_ctx); + return 1; + } + + /* Initialize data streams for all buffers in this file. */ + kshark_tep_init_all_buffers(kshark_ctx, sd); + + /* Load all buffers. */ + n_rows = kshark_load_all_entries(kshark_ctx, &data); + + /* Print to the screen the first 20 entries. */ + for (r = 0; r < 20; ++r) + kshark_print_entry(data[r]); + + /* Free the memory. */ + for (r = 0; r < n_rows; ++r) + free(data[r]); + free(data); + + kshark_close_all(kshark_ctx); + + /* Close the session. */ + kshark_free(kshark_ctx); + + return 0; +} diff --git a/src/libkshark.c b/src/libkshark.c index edbea9c..cc8bd93 100644 --- a/src/libkshark.c +++ b/src/libkshark.c @@ -1763,3 +1763,258 @@ kshark_get_entry_back(const struct kshark_entry_request *req, return get_entry(req, data, index, req->first, end, -1); } + +static int first_in_time_entry(struct kshark_entry_data_set *buffer, int n_buffers, size_t *count) +{ + int64_t t_min = INT64_MAX; + int i, min = -1; + + for (i = 0; i < n_buffers; ++i) { + if (count[i] == buffer[i].n_rows) + continue; + + if (t_min > buffer[i].data[count[i]]->ts) { + t_min = buffer[i].data[count[i]]->ts; + min = i; + } + } + + return min; +} + +/** + * @brief Merge trace data streams. + * + * @param buffers: Input location for the data-sets to be merged. + * @param n_buffers: The number of the data-sets to be merged. + * + * @returns Merged and sorted in time trace data entries. The user is + * responsible for freeing the elements of the outputted array. + */ +struct kshark_entry ** +kshark_merge_data_entries(struct kshark_entry_data_set *buffers, int n_buffers) +{ + struct kshark_entry **merged_data; + size_t i, tot = 0, count[n_buffers]; + int i_first; + + if (n_buffers < 2) { + fputs("kshark_merge_data_entries needs multipl data sets.\n", + stderr); + return NULL; + } + + for (i = 0; i < n_buffers; ++i) { + count[i] = 0; + if (buffers[i].n_rows > 0) + tot += buffers[i].n_rows; + } + + merged_data = calloc(tot, sizeof(*merged_data)); + if (!merged_data) { + fputs("Failed to allocate memory for mergeing data entries.\n", + stderr); + return NULL; + } + + for (i = 0; i < tot; ++i) { + i_first = first_in_time_entry(buffers, n_buffers, count); + assert(i_first >= 0); + merged_data[i] = buffers[i_first].data[count[i_first]]; + ++count[i_first]; + } + + return merged_data; +} + +static ssize_t load_all_entries(struct kshark_context *kshark_ctx, + struct kshark_entry **loaded_rows, + ssize_t n_loaded, + int sd_first_new, int n_streams, + struct kshark_entry ***data_rows) +{ + int i, j = 0, n_data_sets; + ssize_t data_size = 0; + + if (n_streams <= 0 || sd_first_new < 0) + return data_size; + + n_data_sets = n_streams - sd_first_new; + if (loaded_rows && n_loaded > 0) + ++n_data_sets; + + struct kshark_entry_data_set buffers[n_data_sets]; + memset(buffers, 0, sizeof(buffers)); + + if (loaded_rows && n_loaded > 0) { + /* Add the data that is already loaded. */ + data_size = buffers[n_data_sets - 1].n_rows = n_loaded; + buffers[n_data_sets - 1].data = loaded_rows; + } + + /* Add the data of the new streams. */ + for (i = sd_first_new; i < n_streams; ++i) { + buffers[j].data = NULL; + buffers[j].n_rows = kshark_load_entries(kshark_ctx, i, + &buffers[j].data); + + if (buffers[j].n_rows < 0) { + /* Loading failed. */ + data_size = buffers[j].n_rows; + goto error; + } + + data_size += buffers[j++].n_rows; + } + + if (n_data_sets == 1) { + *data_rows = buffers[0].data; + } else { + /* Merge all streams. */ + *data_rows = kshark_merge_data_entries(buffers, n_data_sets); + } + + error: + for (i = 1; i < n_data_sets; ++i) + free(buffers[i].data); + + return data_size; +} + +/** + * @brief Load the content of the all opened data file into an array of + * kshark_entries. + * If one or more filters are set, the "visible" fields of each entry + * is updated according to the criteria provided by the filters. The + * field "filter_mask" of the session's context is used to control the + * level of visibility/invisibility of the filtered entries. + * + * @param kshark_ctx: Input location for context pointer. + * @param data_rows: Output location for the trace data. The user is + * responsible for freeing the elements of the outputted + * array. + * + * @returns The size of the outputted data in the case of success, or a + * negative error code on failure. + */ +ssize_t kshark_load_all_entries(struct kshark_context *kshark_ctx, + struct kshark_entry ***data_rows) +{ + return load_all_entries(kshark_ctx, + NULL, 0, + 0, + kshark_ctx->n_streams, + data_rows); +} + +/** + * @brief Append the content of the all opened data file into an array of + * kshark_entries. + * If one or more filters are set, the "visible" fields of each entry + * is updated according to the criteria provided by the filters. The + * field "filter_mask" of the session's context is used to control the + * level of visibility/invisibility of the filtered entries. + * + * @param kshark_ctx: Input location for context pointer. + * @param prior_data: Input location for the already loaded trace data. + * @param n_prior_rows: The size of the already loaded trace data. + * @param sd_first_new: Data stream identifier of the first data stream to be + * appended. + * @param merged_data: Output location for the trace data. The user is + * responsible for freeing the elements of the outputted + * array. + * @returns The size of the outputted data in the case of success, or a + * negative error code on failure. + */ +ssize_t kshark_append_all_entries(struct kshark_context *kshark_ctx, + struct kshark_entry **prior_data, + ssize_t n_prior_rows, + int sd_first_new, + struct kshark_entry ***merged_data) +{ + return load_all_entries(kshark_ctx, + prior_data, + n_prior_rows, + sd_first_new, + kshark_ctx->n_streams, + merged_data); +} + +static int first_in_time_row(struct kshark_matrix_data_set *buffers, int n_buffers, size_t *count) +{ + int64_t t_min = INT64_MAX; + int i, min = -1; + + for (i = 0; i < n_buffers; ++i) { + if (count[i] == buffers[i].n_rows) + continue; + + if (t_min > buffers[i].ts_array[count[i]]) { + t_min = buffers[i].ts_array[count[i]]; + min = i; + } + } + + return min; +} + +/** + * @brief Merge trace data streams. + * + * @param buffers: Input location for the data-sets to be merged. + * @param n_buffers: The number of the data-sets to be merged. + * + * @returns Merged and sorted in time trace data matrix. The user is + * responsible for freeing the columns (arrays) of the outputted + * matrix. + */ +struct kshark_matrix_data_set +kshark_merge_data_matrices(struct kshark_matrix_data_set *buffers, int n_buffers) +{ + struct kshark_matrix_data_set merged_data; + size_t i, tot = 0, count[n_buffers]; + int i_first; + bool status; + + merged_data.n_rows = -1; + if (n_buffers < 2) { + fputs("kshark_merge_data_matrices needs multipl data sets.\n", + stderr); + goto end; + } + + for (i = 0; i < n_buffers; ++i) { + count[i] = 0; + if (buffers[i].n_rows > 0) + tot += buffers[i].n_rows; + } + + status = kshark_data_matrix_alloc(tot, &merged_data.event_array, + &merged_data.cpu_array, + &merged_data.pid_array, + &merged_data.offset_array, + &merged_data.ts_array); + if (!status) { + fputs("Failed to allocate memory for mergeing data matrices.\n", + stderr); + goto end; + } + + merged_data.n_rows = tot; + + for (i = 0; i < tot; ++i) { + i_first = first_in_time_row(buffers, n_buffers, count); + assert(i_first >= 0); + + merged_data.cpu_array[i] = buffers[i_first].cpu_array[count[i_first]]; + merged_data.pid_array[i] = buffers[i_first].pid_array[count[i_first]]; + merged_data.event_array[i] = buffers[i_first].event_array[count[i_first]]; + merged_data.offset_array[i] = buffers[i_first].offset_array[count[i_first]]; + merged_data.ts_array[i] = buffers[i_first].ts_array[count[i_first]]; + + ++count[i_first]; + } + + end: + return merged_data; +} diff --git a/src/libkshark.h b/src/libkshark.h index 0a560f1..edf3dcf 100644 --- a/src/libkshark.h +++ b/src/libkshark.h @@ -980,12 +980,59 @@ struct kshark_config_doc *kshark_open_config_file(const char *file_name, struct kshark_config_doc *kshark_json_to_conf(struct json_object *jobj); +/** Structure representing a data set made of KernelShark entries. */ +struct kshark_entry_data_set { + /** Array of entries pointers. */ + struct kshark_entry **data; + + /** The size of the data set. */ + ssize_t n_rows; +}; + +struct kshark_entry ** +kshark_merge_data_entries(struct kshark_entry_data_set *buffers, + int n_buffers); + +ssize_t kshark_load_all_entries(struct kshark_context *kshark_ctx, + struct kshark_entry ***data_rows); + +ssize_t kshark_append_all_entries(struct kshark_context *kshark_ctx, + struct kshark_entry **prior_data, + ssize_t n_prior_rows, + int first_streams, + struct kshark_entry ***merged_data); + bool kshark_data_matrix_alloc(size_t n_rows, int16_t **event_array, int16_t **cpu_array, int32_t **pid_array, int64_t **offset_array, int64_t **ts_array); +/** Structure representing a data set made of data columns (arrays). */ +struct kshark_matrix_data_set { + /** Event Id column. */ + int16_t *event_array; + + /** CPU Id column. */ + int16_t *cpu_array; + + /** PID column. */ + int32_t *pid_array; + + /** Record offset column. */ + int64_t *offset_array; + + /** Timestamp column. */ + int64_t *ts_array; + + /** The size of the data set. */ + ssize_t n_rows; +}; + +struct kshark_matrix_data_set +kshark_merge_data_matrices(struct kshark_matrix_data_set *buffers, + int n_buffers); + #ifdef __cplusplus } #endif From patchwork Mon Jan 4 17:47:03 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11997171 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-10.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE, SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id E01FFC433DB for ; Mon, 4 Jan 2021 17:49:17 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id AE19D20700 for ; Mon, 4 Jan 2021 17:49:17 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726452AbhADRtR (ORCPT ); Mon, 4 Jan 2021 12:49:17 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34374 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726246AbhADRtR (ORCPT ); Mon, 4 Jan 2021 12:49:17 -0500 Received: from mail-ej1-x62f.google.com (mail-ej1-x62f.google.com [IPv6:2a00:1450:4864:20::62f]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D4057C0617BB for ; Mon, 4 Jan 2021 09:48:01 -0800 (PST) Received: by mail-ej1-x62f.google.com with SMTP id g20so37953079ejb.1 for ; Mon, 04 Jan 2021 09:48:01 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=9+wcnKdFrT794q67uDcBNPS86AFrxTdbz7okn8AafxU=; b=OOVOawTdCw1W6p4Nmm74toFV/wOtNdC0V5dC1woYe+Xd9EbE1fcuNEu50nkiWgKPqY jqLuac5ybO/jYy4axA9ISDAm4an1ih1Wemt+zn6B3qWhF5szL5rOOg0sfQIHHu4lpPdP xjGzx0bBEuAyTfcRJFQFQx7xlsfE8cN2Ypb6cV5CwJMNYk6EUMzhKY1d3gL5MqDqr2jQ JQJL9CxWSgfkQqe32IiWQwKbkYjmAsnrqfQcSJB3RikSpompEgV2YiGlSXuEfAvIMG4v 63W3SxbnRyPa7Ue0nh7IlzYOqXN4FiIEfsLgufGcWULtQ3S2S5QUdu65LTBb7ll6SGOd IXTQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=9+wcnKdFrT794q67uDcBNPS86AFrxTdbz7okn8AafxU=; b=M0scShU/hmtEL+nM5vJ698yg9/WHspSxwdccvor9qtnSdP9r6KBIrWpzZz2eMmIcTz rwbas8M5ZfKR6LhTmueOIHiORFyodNY4skaF58OmvekdrKPBq99JcQ+OXMwgAh1YryAv /JPXEYFEnk6MY9MFSWhuNWsVTZOsMdMe5FbomTHgkRvx9oEWwYMKPEMf9rnXG7eu4mJx i88Hnu2J3LzVx9ZD+hwNg7z+9UQMUfKFnBblDMFEAZ4GR1lHShpdtmra5YEtAtUhzYsk iP7cLt/Qh2bXhBLDW7/9BfKj9vW1fYdLdDvqRqsw8U1AQJtk2BKSUtq7NOLPn4sb6y9P PNFA== X-Gm-Message-State: AOAM533f9rJ/yfq0gAfyGzNn2/4MzL24Ne2M4HjXMTUDS9blfFX6+Kfa HaWiBvUpAnF4VP+RvotD/s0= X-Google-Smtp-Source: ABdhPJwu93MJKOigQ5miY+VXCTFGCyySrmNx6SgNS0r/wPOWUMcML7iwN0W9G4C1L+x/A56RnX3EtA== X-Received: by 2002:a17:906:8301:: with SMTP id j1mr10578238ejx.397.1609782480420; Mon, 04 Jan 2021 09:48:00 -0800 (PST) Received: from localhost.localdomain ([95.87.199.238]) by smtp.gmail.com with ESMTPSA id l14sm44107750edq.35.2021.01.04.09.47.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 04 Jan 2021 09:47:59 -0800 (PST) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v8 23/44] kernel-shark: Integrate the stream definitions with data model Date: Mon, 4 Jan 2021 19:47:03 +0200 Message-Id: <20210104174724.70404-24-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210104174724.70404-1-y.karadz@gmail.com> References: <20210104174724.70404-1-y.karadz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org The visualization model is adapted in order to be able to distinguish the entries belonging to different Data streams. We re-enable the visualization model example as well. Signen-off-by: Yordan Karadzhov (VMware) --- examples/CMakeLists.txt | 8 ++-- examples/datahisto.c | 38 +++++++-------- src/CMakeLists.txt | 2 +- src/libkshark-model.c | 102 ++++++++++++++++++++++++---------------- src/libkshark-model.h | 34 +++++++------- 5 files changed, 102 insertions(+), 82 deletions(-) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 831eee2..2f6acea 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -12,10 +12,10 @@ message(STATUS "multibufferload") add_executable(mbload multibufferload.c) target_link_libraries(mbload kshark) -# message(STATUS "datahisto") -# add_executable(dhisto datahisto.c) -# target_link_libraries(dhisto kshark) -# +message(STATUS "datahisto") +add_executable(dhisto datahisto.c) +target_link_libraries(dhisto kshark) + # message(STATUS "confogio") # add_executable(confio configio.c) # target_link_libraries(confio kshark) diff --git a/examples/datahisto.c b/examples/datahisto.c index b177b08..568072d 100644 --- a/examples/datahisto.c +++ b/examples/datahisto.c @@ -7,6 +7,7 @@ // C #include #include +#include // KernelShark #include "libkshark.h" @@ -16,7 +17,7 @@ const char *default_file = "trace.dat"; -void dump_bin(struct kshark_trace_histo *histo, int bin, +void dump_bin(struct kshark_trace_histo *histo, int bin, int sd, const char *type, int val) { const struct kshark_entry *e_front, *e_back; @@ -26,22 +27,22 @@ void dump_bin(struct kshark_trace_histo *histo, int bin, printf("bin %i {\n", bin); if (strcmp(type, "cpu") == 0) { e_front = ksmodel_get_entry_front(histo, bin, true, - kshark_match_cpu, val, + kshark_match_cpu, sd, &val, NULL, &i_front); e_back = ksmodel_get_entry_back(histo, bin, true, - kshark_match_cpu, val, + kshark_match_cpu, sd, &val, NULL, &i_back); } else if (strcmp(type, "task") == 0) { e_front = ksmodel_get_entry_front(histo, bin, true, - kshark_match_pid, val, + kshark_match_pid, sd, &val, NULL, &i_front); e_back = ksmodel_get_entry_back(histo, bin, true, - kshark_match_pid, val, + kshark_match_pid, sd, &val, NULL, &i_back); } else { @@ -67,12 +68,12 @@ void dump_bin(struct kshark_trace_histo *histo, int bin, puts("}\n"); } -void dump_histo(struct kshark_trace_histo *histo, const char *type, int val) +void dump_histo(struct kshark_trace_histo *histo, int sd, const char *type, int val) { size_t bin; for (bin = 0; bin < histo->n_bins; ++bin) - dump_bin(histo, bin, type, val); + dump_bin(histo, bin, sd, type, val); } int main(int argc, char **argv) @@ -81,8 +82,7 @@ int main(int argc, char **argv) struct kshark_entry **data = NULL; struct kshark_trace_histo histo; ssize_t i, n_rows, n_tasks; - bool status; - int *pids; + int sd, *pids; /* Create a new kshark session. */ kshark_ctx = NULL; @@ -91,24 +91,24 @@ int main(int argc, char **argv) /* Open a trace data file produced by trace-cmd. */ if (argc > 1) - status = kshark_open(kshark_ctx, argv[1]); + sd = kshark_open(kshark_ctx, argv[1]); else - status = kshark_open(kshark_ctx, default_file); + sd = kshark_open(kshark_ctx, default_file); - if (!status) { + if (sd < 0) { kshark_free(kshark_ctx); return 1; } /* Load the content of the file into an array of entries. */ - n_rows = kshark_load_data_entries(kshark_ctx, &data); + n_rows = kshark_load_entries(kshark_ctx, sd, &data); if (n_rows < 1) { kshark_free(kshark_ctx); return 1; } /* Get a list of all tasks. */ - n_tasks = kshark_get_task_pids(kshark_ctx, &pids); + n_tasks = kshark_get_task_pids(kshark_ctx, sd, &pids); /* Initialize the Visualization Model. */ ksmodel_init(&histo); @@ -119,7 +119,7 @@ int main(int argc, char **argv) ksmodel_fill(&histo, data, n_rows); /* Dump the raw bins. */ - dump_histo(&histo, "", 0); + dump_histo(&histo, sd, "", 0); puts("\n...\n\n"); @@ -127,13 +127,13 @@ int main(int argc, char **argv) * Change the state of the model. Do 50% Zoom-In and dump only CPU 0. */ ksmodel_zoom_in(&histo, .50, -1); - dump_histo(&histo, "cpu", 0); + dump_histo(&histo, sd, "cpu", 0); puts("\n...\n\n"); /* Shift forward by two bins and this time dump only CPU 1. */ ksmodel_shift_forward(&histo, 2); - dump_histo(&histo, "cpu", 1); + dump_histo(&histo, sd, "cpu", 1); puts("\n...\n\n"); @@ -142,7 +142,7 @@ int main(int argc, char **argv) * Task. */ ksmodel_zoom_out(&histo, .10, N_BINS - 1); - dump_histo(&histo, "task", pids[n_tasks - 1]); + dump_histo(&histo, sd, "task", pids[n_tasks - 1]); /* Reset (clear) the model. */ ksmodel_clear(&histo); @@ -154,7 +154,7 @@ int main(int argc, char **argv) free(data); /* Close the file. */ - kshark_close(kshark_ctx); + kshark_close(kshark_ctx, sd); /* Close the session. */ kshark_free(kshark_ctx); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1e6ff44..da5448c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -5,7 +5,7 @@ set(KS_INCLUDS_DESTINATION "${_INSTALL_PREFIX}/include/${KS_APP_NAME}") message(STATUS "libkshark") add_library(kshark SHARED libkshark.c libkshark-hash.c -# libkshark-model.c + libkshark-model.c libkshark-plugin.c libkshark-tepdata.c # libkshark-configio.c diff --git a/src/libkshark-model.c b/src/libkshark-model.c index babac2a..97cff31 100644 --- a/src/libkshark-model.c +++ b/src/libkshark-model.c @@ -1,17 +1,19 @@ // SPDX-License-Identifier: LGPL-2.1 /* - * Copyright (C) 2017 VMware Inc, Yordan Karadzhov + * Copyright (C) 2017 VMware Inc, Yordan Karadzhov (VMware) */ - /** - * @file libkshark-model.c - * @brief Visualization model for FTRACE (trace-cmd) data. - */ +/** + * @file libkshark-model.c + * @brief Time series visualization model for tracing data. + */ // C #include #include +#include +#include // KernelShark #include "libkshark-model.h" @@ -782,7 +784,8 @@ static bool ksmodel_is_visible(struct kshark_entry *e) static struct kshark_entry_request * ksmodel_entry_front_request_alloc(struct kshark_trace_histo *histo, int bin, bool vis_only, - matching_condition_func func, int val) + matching_condition_func func, + int sd, int *values) { size_t first, n; @@ -794,14 +797,15 @@ ksmodel_entry_front_request_alloc(struct kshark_trace_histo *histo, first = ksmodel_first_index_at_bin(histo, bin); return kshark_entry_request_alloc(first, n, - func, val, + func, sd, values, vis_only, KS_GRAPH_VIEW_FILTER_MASK); } static struct kshark_entry_request * ksmodel_entry_back_request_alloc(struct kshark_trace_histo *histo, int bin, bool vis_only, - matching_condition_func func, int val) + matching_condition_func func, + int sd, int *values) { size_t first, n; @@ -813,7 +817,7 @@ ksmodel_entry_back_request_alloc(struct kshark_trace_histo *histo, first = ksmodel_last_index_at_bin(histo, bin); return kshark_entry_request_alloc(first, n, - func, val, + func, sd, values, vis_only, KS_GRAPH_VIEW_FILTER_MASK); } @@ -822,12 +826,13 @@ ksmodel_entry_back_request_alloc(struct kshark_trace_histo *histo, * * @param histo: Input location for the model descriptor. * @param bin: Bin id. + * @param sd: Data stream identifier. * @param cpu: Cpu Id. * * @returns Index of the first entry from a given Cpu in this bin. */ ssize_t ksmodel_first_index_at_cpu(struct kshark_trace_histo *histo, - int bin, int cpu) + int bin, int sd, int cpu) { size_t i, n, first, not_found = KS_EMPTY_BIN; @@ -838,7 +843,8 @@ ssize_t ksmodel_first_index_at_cpu(struct kshark_trace_histo *histo, first = ksmodel_first_index_at_bin(histo, bin); for (i = first; i < first + n; ++i) { - if (histo->data[i]->cpu == cpu) { + if (histo->data[i]->cpu == cpu && + histo->data[i]->stream_id == sd) { if (ksmodel_is_visible(histo->data[i])) return i; else @@ -854,12 +860,13 @@ ssize_t ksmodel_first_index_at_cpu(struct kshark_trace_histo *histo, * * @param histo: Input location for the model descriptor. * @param bin: Bin id. + * @param sd: Data stream identifier. * @param pid: Process Id of a task. * * @returns Index of the first entry from a given Task in this bin. */ ssize_t ksmodel_first_index_at_pid(struct kshark_trace_histo *histo, - int bin, int pid) + int bin, int sd, int pid) { size_t i, n, first, not_found = KS_EMPTY_BIN; @@ -870,7 +877,8 @@ ssize_t ksmodel_first_index_at_pid(struct kshark_trace_histo *histo, first = ksmodel_first_index_at_bin(histo, bin); for (i = first; i < first + n; ++i) { - if (histo->data[i]->pid == pid) { + if (histo->data[i]->pid == pid && + histo->data[i]->stream_id == sd) { if (ksmodel_is_visible(histo->data[i])) return i; else @@ -890,8 +898,9 @@ ssize_t ksmodel_first_index_at_pid(struct kshark_trace_histo *histo, * @param bin: Bin id. * @param vis_only: If true, a visible entry is requested. * @param func: Matching condition function. - * @param val: Matching condition value, used by the Matching condition - * function. + * @param sd: Data stream identifier. + * @param values: Matching condition values, used by the Matching condition + * function. * @param col: Optional input location for Data collection. * @param index: Optional output location for the index of the requested * entry inside the array. @@ -901,7 +910,7 @@ ssize_t ksmodel_first_index_at_pid(struct kshark_trace_histo *histo, const struct kshark_entry * ksmodel_get_entry_front(struct kshark_trace_histo *histo, int bin, bool vis_only, - matching_condition_func func, int val, + matching_condition_func func, int sd, int *values, struct kshark_entry_collection *col, ssize_t *index) { @@ -913,7 +922,7 @@ ksmodel_get_entry_front(struct kshark_trace_histo *histo, /* Set the position at the beginning of the bin and go forward. */ req = ksmodel_entry_front_request_alloc(histo, bin, vis_only, - func, val); + func, sd, values); if (!req) return NULL; @@ -937,8 +946,9 @@ ksmodel_get_entry_front(struct kshark_trace_histo *histo, * @param bin: Bin id. * @param vis_only: If true, a visible entry is requested. * @param func: Matching condition function. - * @param val: Matching condition value, used by the Matching condition - * function. + * @param sd: Data stream identifier. + * @param values: Matching condition values, used by the Matching condition + * function. * @param col: Optional input location for Data collection. * @param index: Optional output location for the index of the requested * entry inside the array. @@ -948,7 +958,7 @@ ksmodel_get_entry_front(struct kshark_trace_histo *histo, const struct kshark_entry * ksmodel_get_entry_back(struct kshark_trace_histo *histo, int bin, bool vis_only, - matching_condition_func func, int val, + matching_condition_func func, int sd, int *values, struct kshark_entry_collection *col, ssize_t *index) { @@ -960,7 +970,7 @@ ksmodel_get_entry_back(struct kshark_trace_histo *histo, /* Set the position at the end of the bin and go backwards. */ req = ksmodel_entry_back_request_alloc(histo, bin, vis_only, - func, val); + func, sd, values); if (!req) return NULL; @@ -998,6 +1008,7 @@ static int ksmodel_get_entry_pid(const struct kshark_entry *entry) * * @param histo: Input location for the model descriptor. * @param bin: Bin id. + * @param sd: Data stream identifier. * @param cpu: CPU Id. * @param vis_only: If true, a visible entry is requested. * @param col: Optional input location for Data collection. @@ -1008,7 +1019,7 @@ static int ksmodel_get_entry_pid(const struct kshark_entry *entry) * Identifier (KS_EMPTY_BIN or KS_FILTERED_BIN). */ int ksmodel_get_pid_front(struct kshark_trace_histo *histo, - int bin, int cpu, bool vis_only, + int bin, int sd, int cpu, bool vis_only, struct kshark_entry_collection *col, ssize_t *index) { @@ -1018,7 +1029,7 @@ int ksmodel_get_pid_front(struct kshark_trace_histo *histo, return KS_EMPTY_BIN; entry = ksmodel_get_entry_front(histo, bin, vis_only, - kshark_match_cpu, cpu, + kshark_match_cpu, sd, &cpu, col, index); return ksmodel_get_entry_pid(entry); @@ -1031,6 +1042,7 @@ int ksmodel_get_pid_front(struct kshark_trace_histo *histo, * * @param histo: Input location for the model descriptor. * @param bin: Bin id. + * @param sd: Data stream identifier. * @param cpu: CPU Id. * @param vis_only: If true, a visible entry is requested. * @param col: Optional input location for Data collection. @@ -1041,7 +1053,7 @@ int ksmodel_get_pid_front(struct kshark_trace_histo *histo, * Identifier (KS_EMPTY_BIN or KS_FILTERED_BIN). */ int ksmodel_get_pid_back(struct kshark_trace_histo *histo, - int bin, int cpu, bool vis_only, + int bin, int sd, int cpu, bool vis_only, struct kshark_entry_collection *col, ssize_t *index) { @@ -1051,7 +1063,7 @@ int ksmodel_get_pid_back(struct kshark_trace_histo *histo, return KS_EMPTY_BIN; entry = ksmodel_get_entry_back(histo, bin, vis_only, - kshark_match_cpu, cpu, + kshark_match_cpu, sd, &cpu, col, index); return ksmodel_get_entry_pid(entry); @@ -1080,6 +1092,7 @@ static int ksmodel_get_entry_cpu(const struct kshark_entry *entry) * * @param histo: Input location for the model descriptor. * @param bin: Bin id. + * @param sd: Data stream identifier. * @param pid: Process Id. * @param vis_only: If true, a visible entry is requested. * @param col: Optional input location for Data collection. @@ -1090,7 +1103,7 @@ static int ksmodel_get_entry_cpu(const struct kshark_entry *entry) * Identifier (KS_EMPTY_BIN or KS_FILTERED_BIN). */ int ksmodel_get_cpu_front(struct kshark_trace_histo *histo, - int bin, int pid, bool vis_only, + int bin, int sd, int pid, bool vis_only, struct kshark_entry_collection *col, ssize_t *index) { @@ -1100,7 +1113,7 @@ int ksmodel_get_cpu_front(struct kshark_trace_histo *histo, return KS_EMPTY_BIN; entry = ksmodel_get_entry_front(histo, bin, vis_only, - kshark_match_pid, pid, + kshark_match_pid, sd, &pid, col, index); return ksmodel_get_entry_cpu(entry); @@ -1113,6 +1126,7 @@ int ksmodel_get_cpu_front(struct kshark_trace_histo *histo, * * @param histo: Input location for the model descriptor. * @param bin: Bin id. + * @param sd: Data stream identifier. * @param pid: Process Id. * @param vis_only: If true, a visible entry is requested. * @param col: Optional input location for Data collection. @@ -1123,7 +1137,7 @@ int ksmodel_get_cpu_front(struct kshark_trace_histo *histo, * Identifier (KS_EMPTY_BIN or KS_FILTERED_BIN). */ int ksmodel_get_cpu_back(struct kshark_trace_histo *histo, - int bin, int pid, bool vis_only, + int bin, int sd, int pid, bool vis_only, struct kshark_entry_collection *col, ssize_t *index) { @@ -1133,7 +1147,7 @@ int ksmodel_get_cpu_back(struct kshark_trace_histo *histo, return KS_EMPTY_BIN; entry = ksmodel_get_entry_back(histo, bin, vis_only, - kshark_match_pid, pid, + kshark_match_pid, sd, &pid, col, index); @@ -1145,6 +1159,7 @@ int ksmodel_get_cpu_back(struct kshark_trace_histo *histo, * * @param histo: Input location for the model descriptor. * @param bin: Bin id. + * @param sd: Data stream identifier. * @param cpu: Cpu Id. * @param col: Optional input location for Data collection. * @param index: Optional output location for the index of the requested @@ -1153,7 +1168,7 @@ int ksmodel_get_cpu_back(struct kshark_trace_histo *histo, * @returns True, if a visible entry exists in this bin. Else false. */ bool ksmodel_cpu_visible_event_exist(struct kshark_trace_histo *histo, - int bin, int cpu, + int bin, int sd, int cpu, struct kshark_entry_collection *col, ssize_t *index) { @@ -1166,7 +1181,7 @@ bool ksmodel_cpu_visible_event_exist(struct kshark_trace_histo *histo, /* Set the position at the beginning of the bin and go forward. */ req = ksmodel_entry_front_request_alloc(histo, bin, true, - kshark_match_cpu, cpu); + kshark_match_cpu, sd, &cpu); if (!req) return false; @@ -1198,6 +1213,7 @@ bool ksmodel_cpu_visible_event_exist(struct kshark_trace_histo *histo, * * @param histo: Input location for the model descriptor. * @param bin: Bin id. + * @param sd: Data stream identifier. * @param pid: Process Id of the task. * @param col: Optional input location for Data collection. * @param index: Optional output location for the index of the requested @@ -1206,7 +1222,7 @@ bool ksmodel_cpu_visible_event_exist(struct kshark_trace_histo *histo, * @returns True, if a visible entry exists in this bin. Else false. */ bool ksmodel_task_visible_event_exist(struct kshark_trace_histo *histo, - int bin, int pid, + int bin, int sd, int pid, struct kshark_entry_collection *col, ssize_t *index) { @@ -1219,7 +1235,7 @@ bool ksmodel_task_visible_event_exist(struct kshark_trace_histo *histo, /* Set the position at the beginning of the bin and go forward. */ req = ksmodel_entry_front_request_alloc(histo, bin, true, - kshark_match_pid, pid); + kshark_match_pid, sd, &pid); if (!req) return false; @@ -1247,15 +1263,17 @@ bool ksmodel_task_visible_event_exist(struct kshark_trace_histo *histo, } static bool match_cpu_missed_events(struct kshark_context *kshark_ctx, - struct kshark_entry *e, int cpu) + struct kshark_entry *e, int sd, int *cpu) { - return e->event_id == -EOVERFLOW && e->cpu == cpu; + return e->event_id == KS_EVENT_OVERFLOW && + e->cpu == *cpu && e->stream_id == sd; } static bool match_pid_missed_events(struct kshark_context *kshark_ctx, - struct kshark_entry *e, int pid) + struct kshark_entry *e, int sd, int *pid) { - return e->event_id == -EOVERFLOW && e->pid == pid; + return e->event_id == KS_EVENT_OVERFLOW && + e->pid == *pid && e->stream_id == sd; } /** @@ -1264,6 +1282,7 @@ static bool match_pid_missed_events(struct kshark_context *kshark_ctx, * * @param histo: Input location for the model descriptor. * @param bin: Bin id. + * @param sd: Data stream identifier. * @param cpu: CPU Id. * @param col: Optional input location for Data collection. * @param index: Optional output location for the index of the requested @@ -1273,12 +1292,12 @@ static bool match_pid_missed_events(struct kshark_context *kshark_ctx, */ const struct kshark_entry * ksmodel_get_cpu_missed_events(struct kshark_trace_histo *histo, - int bin, int cpu, + int bin, int sd, int cpu, struct kshark_entry_collection *col, ssize_t *index) { return ksmodel_get_entry_front(histo, bin, true, - match_cpu_missed_events, cpu, + match_cpu_missed_events, sd, &cpu, col, index); } @@ -1288,6 +1307,7 @@ ksmodel_get_cpu_missed_events(struct kshark_trace_histo *histo, * * @param histo: Input location for the model descriptor. * @param bin: Bin id. + * @param sd: Data stream identifier. * @param pid: Process Id of the task. * @param col: Optional input location for Data collection. * @param index: Optional output location for the index of the requested @@ -1297,11 +1317,11 @@ ksmodel_get_cpu_missed_events(struct kshark_trace_histo *histo, */ const struct kshark_entry * ksmodel_get_task_missed_events(struct kshark_trace_histo *histo, - int bin, int pid, + int bin, int sd, int pid, struct kshark_entry_collection *col, ssize_t *index) { return ksmodel_get_entry_front(histo, bin, true, - match_pid_missed_events, pid, + match_pid_missed_events, sd, &pid, col, index); } diff --git a/src/libkshark-model.h b/src/libkshark-model.h index 47793b1..87e252e 100644 --- a/src/libkshark-model.h +++ b/src/libkshark-model.h @@ -1,13 +1,13 @@ /* SPDX-License-Identifier: LGPL-2.1 */ /* - * Copyright (C) 2017 VMware Inc, Yordan Karadzhov + * Copyright (C) 2017 VMware Inc, Yordan Karadzhov (VMware) */ - /** - * @file libkshark-model.h - * @brief Visualization model for FTRACE (trace-cmd) data. - */ +/** + * @file libkshark-model.h + * @brief Time series visualization model for tracing data. + */ #ifndef _LIB_KSHARK_MODEL_H #define _LIB_KSHARK_MODEL_H @@ -102,64 +102,64 @@ ssize_t ksmodel_first_index_at_bin(struct kshark_trace_histo *histo, int bin); ssize_t ksmodel_last_index_at_bin(struct kshark_trace_histo *histo, int bin); ssize_t ksmodel_first_index_at_cpu(struct kshark_trace_histo *histo, - int bin, int cpu); + int bin, int sd, int cpu); ssize_t ksmodel_first_index_at_pid(struct kshark_trace_histo *histo, - int bin, int pid); + int bin, int sd, int pid); const struct kshark_entry * ksmodel_get_entry_front(struct kshark_trace_histo *histo, int bin, bool vis_only, - matching_condition_func func, int val, + matching_condition_func func, int sd, int *values, struct kshark_entry_collection *col, ssize_t *index); const struct kshark_entry * ksmodel_get_entry_back(struct kshark_trace_histo *histo, int bin, bool vis_only, - matching_condition_func func, int val, + matching_condition_func func, int sd, int *values, struct kshark_entry_collection *col, ssize_t *index); int ksmodel_get_pid_front(struct kshark_trace_histo *histo, - int bin, int cpu, bool vis_only, + int bin, int sd, int cpu, bool vis_only, struct kshark_entry_collection *col, ssize_t *index); int ksmodel_get_pid_back(struct kshark_trace_histo *histo, - int bin, int cpu, bool vis_only, + int bin, int sd, int cpu, bool vis_only, struct kshark_entry_collection *col, ssize_t *index); int ksmodel_get_cpu_front(struct kshark_trace_histo *histo, - int bin, int pid, bool vis_only, + int bin, int sd, int pid, bool vis_only, struct kshark_entry_collection *col, ssize_t *index); int ksmodel_get_cpu_back(struct kshark_trace_histo *histo, - int bin, int pid, bool vis_only, + int bin, int sd, int pid, bool vis_only, struct kshark_entry_collection *col, ssize_t *index); bool ksmodel_cpu_visible_event_exist(struct kshark_trace_histo *histo, - int bin, int cpu, + int bin, int sd, int cpu, struct kshark_entry_collection *col, ssize_t *index); bool ksmodel_task_visible_event_exist(struct kshark_trace_histo *histo, - int bin, int pid, + int bin, int sd, int pid, struct kshark_entry_collection *col, ssize_t *index); const struct kshark_entry * ksmodel_get_cpu_missed_events(struct kshark_trace_histo *histo, - int bin, int cpu, + int bin, int sd, int cpu, struct kshark_entry_collection *col, ssize_t *index); const struct kshark_entry * ksmodel_get_task_missed_events(struct kshark_trace_histo *histo, - int bin, int pid, + int bin, int sd, int pid, struct kshark_entry_collection *col, ssize_t *index); From patchwork Mon Jan 4 17:47:04 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11997209 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9BAC9C433DB for ; Mon, 4 Jan 2021 17:49:43 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 636CE206B2 for ; Mon, 4 Jan 2021 17:49:43 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727434AbhADRtm (ORCPT ); Mon, 4 Jan 2021 12:49:42 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34494 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727357AbhADRtm (ORCPT ); Mon, 4 Jan 2021 12:49:42 -0500 Received: from mail-ej1-x632.google.com (mail-ej1-x632.google.com [IPv6:2a00:1450:4864:20::632]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 84590C0617BC for ; Mon, 4 Jan 2021 09:48:02 -0800 (PST) Received: by mail-ej1-x632.google.com with SMTP id 6so37899624ejz.5 for ; Mon, 04 Jan 2021 09:48:02 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=dZ0Md1ZVuFNbmAKxQLmv9j8FqeYHXxFKPJzenH94qMY=; b=E5kOZ7bsL02yoNcX6junxp95SIuuWG6WiAz7klh2OJJsEvTZe3WDMIQZ34SVONQ1vW f68Bid/M0nIZ4J66V8tMn+MkRmam5oAxTFakdvX/SCrDqq4RsmuYNyf7+xZaAju59V24 nsPp83hOl0D5+Kcqi4IneQDcEJY6GCR3ZXLoQIjgVpT420HdNDMCjl7o1RgXJb3vhdnl lKiM2MMjBNaynTyeUqAWuI9AGTZOUw931LGISZh6D1oWLu0Qgn8TT86WK7o5gekJ1r2w +NgriMTuor79DqRjAASWWOwtOZ8gFxmjPFeSdkivycNVCKsL9HBAHpVI/+fJLDAef3xz ZU+A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=dZ0Md1ZVuFNbmAKxQLmv9j8FqeYHXxFKPJzenH94qMY=; b=jaYTd9JGatD7nwcvUFQjDFlV+xAnLC8ya6Fqr61bTzILMiWs2oEPOt/BKCE0etuckl 7vxxUqA2d+HkRqC2sJkBMiiJPM5ZQkne829pdeQ1HlKM/nyOkdGbUskGU1Uwi7qdb6ry IsXh2S+Z9mIj2/1KSuLAK7OCOVkRb9g3Ba5HBUXknCuMvH0fgZlqIszjyEvbg+rueC7R 1ZYzoKQqxS/k/hzy7ofYjRPKDdUV7LlGJthmEh47/GiCjqUiIzGY9Jqd9dRgx8CW3AHS 5BWzDYkUOb+zjQ8+IxR3NB1IabPcd7PHps4IJ99DbRMUHvcA3bfXS0f5paU+CrXjqZa4 gblg== X-Gm-Message-State: AOAM533oQd9ulFQqSiQ+TzcJyni10gjk+TDbwgW5Hd8XRi2KUEV4+LP9 LpcJjV12aAaylOpQJ1PVu6/0fltDqu1t7A== X-Google-Smtp-Source: ABdhPJy6jsdW+O3icsQP6jw94T6JNro+zf5xCTcdl49aVZDXt39XHOOQdzhnzN5nLzt0OhXvbDzyow== X-Received: by 2002:a17:906:56ca:: with SMTP id an10mr67684516ejc.498.1609782481260; Mon, 04 Jan 2021 09:48:01 -0800 (PST) Received: from localhost.localdomain ([95.87.199.238]) by smtp.gmail.com with ESMTPSA id l14sm44107750edq.35.2021.01.04.09.48.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 04 Jan 2021 09:48:00 -0800 (PST) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v8 24/44] kernel-shark: Use only signed types for model defs Date: Mon, 4 Jan 2021 19:47:04 +0200 Message-Id: <20210104174724.70404-25-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210104174724.70404-1-y.karadz@gmail.com> References: <20210104174724.70404-1-y.karadz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org All variables that are used to describe the state of the model, like the range of the model or the size of the bin must have signed integer types. This is a continuation of the change started by switching to signed timestamps. Signed-off-by: Yordan Karadzhov (VMware) --- src/libkshark-model.c | 12 ++++++------ src/libkshark-model.h | 18 +++++++++--------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/libkshark-model.c b/src/libkshark-model.c index 97cff31..f3864bf 100644 --- a/src/libkshark-model.c +++ b/src/libkshark-model.c @@ -91,10 +91,10 @@ static bool ksmodel_histo_alloc(struct kshark_trace_histo *histo, size_t n) } static void ksmodel_set_in_range_bining(struct kshark_trace_histo *histo, - size_t n, uint64_t min, uint64_t max, + size_t n, int64_t min, int64_t max, bool force_in_range) { - uint64_t corrected_range, delta_range, range = max - min; + int64_t corrected_range, delta_range, range = max - min; struct kshark_entry *last; /* The size of the bin must be >= 1, hence the range must be >= n. */ @@ -163,7 +163,7 @@ static void ksmodel_set_in_range_bining(struct kshark_trace_histo *histo, * @param max: Upper edge of the time-window to be visualized. */ void ksmodel_set_bining(struct kshark_trace_histo *histo, - size_t n, uint64_t min, uint64_t max) + size_t n, int64_t min, int64_t max) { ksmodel_set_in_range_bining(histo, n, min, max, false); } @@ -256,7 +256,7 @@ static size_t ksmodel_set_upper_edge(struct kshark_trace_histo *histo) static void ksmodel_set_next_bin_edge(struct kshark_trace_histo *histo, size_t bin, size_t last_row) { - uint64_t time_min, time_max; + int64_t time_min, time_max; size_t next_bin = bin + 1; ssize_t row; @@ -598,9 +598,9 @@ void ksmodel_shift_backward(struct kshark_trace_histo *histo, size_t n) * @param histo: Input location for the model descriptor. * @param ts: position in time to be visualized. */ -void ksmodel_jump_to(struct kshark_trace_histo *histo, uint64_t ts) +void ksmodel_jump_to(struct kshark_trace_histo *histo, int64_t ts) { - uint64_t min, max, range_min; + int64_t min, max, range_min; if (ts > histo->min && ts < histo->max) { /* diff --git a/src/libkshark-model.h b/src/libkshark-model.h index 87e252e..b862480 100644 --- a/src/libkshark-model.h +++ b/src/libkshark-model.h @@ -58,16 +58,16 @@ struct kshark_trace_histo { * Lower edge of the time-window to be visualized. Only entries having * timestamp >= min will be visualized. */ - uint64_t min; + int64_t min; /** * Upper edge of the time-window to be visualized. Only entries having * timestamp <= max will be visualized. */ - uint64_t max; + int64_t max; /** The size in time for each bin. */ - uint64_t bin_size; + int64_t bin_size; /** Number of bins. */ int n_bins; @@ -78,7 +78,7 @@ void ksmodel_init(struct kshark_trace_histo *histo); void ksmodel_clear(struct kshark_trace_histo *histo); void ksmodel_set_bining(struct kshark_trace_histo *histo, - size_t n, uint64_t min, uint64_t max); + size_t n, int64_t min, int64_t max); void ksmodel_fill(struct kshark_trace_histo *histo, struct kshark_entry **data, size_t n); @@ -89,7 +89,7 @@ void ksmodel_shift_forward(struct kshark_trace_histo *histo, size_t n); void ksmodel_shift_backward(struct kshark_trace_histo *histo, size_t n); -void ksmodel_jump_to(struct kshark_trace_histo *histo, uint64_t ts); +void ksmodel_jump_to(struct kshark_trace_histo *histo, int64_t ts); void ksmodel_zoom_out(struct kshark_trace_histo *histo, double r, int mark); @@ -163,16 +163,16 @@ ksmodel_get_task_missed_events(struct kshark_trace_histo *histo, struct kshark_entry_collection *col, ssize_t *index); -static inline double ksmodel_bin_time(struct kshark_trace_histo *histo, +static inline int64_t ksmodel_bin_ts(struct kshark_trace_histo *histo, int bin) { - return (histo->min + bin*histo->bin_size) * 1e-9; + return (histo->min + bin*histo->bin_size); } -static inline uint64_t ksmodel_bin_ts(struct kshark_trace_histo *histo, +static inline double ksmodel_bin_time(struct kshark_trace_histo *histo, int bin) { - return (histo->min + bin*histo->bin_size); + return ksmodel_bin_ts(histo, bin) * 1e-9; } #ifdef __cplusplus From patchwork Mon Jan 4 17:47:05 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11997177 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id C5077C43381 for ; Mon, 4 Jan 2021 17:49:19 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 9B045206B2 for ; Mon, 4 Jan 2021 17:49:19 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726655AbhADRtT (ORCPT ); Mon, 4 Jan 2021 12:49:19 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34376 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726497AbhADRtR (ORCPT ); Mon, 4 Jan 2021 12:49:17 -0500 Received: from mail-ej1-x636.google.com (mail-ej1-x636.google.com [IPv6:2a00:1450:4864:20::636]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 8414EC0617BE for ; Mon, 4 Jan 2021 09:48:03 -0800 (PST) Received: by mail-ej1-x636.google.com with SMTP id b9so37958557ejy.0 for ; Mon, 04 Jan 2021 09:48:03 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=m9lQFJuvJrwOAGItYL0Yq0F1uOB4F66QAhKkaIHI7D8=; b=q3GcNjkBRF+0mZ+cYlr5AlismvFZRLPhWa6LotBRObndNAI7U982u9X8Ck+9CHeu93 gphCXdgkkze0yJGihY/6DGbdoph9pzz88IiIVVubS7NWuAZNP/PgCnvA2AVaisTdOiAM vBvfWSkbem2ueT8vyH26XWD4PpwlG9jHtZcnCh4iPuo+VFnN+Pgi+EC4g/h/yRCVRQgj m7gKc/O4dRqLLQv3dPOzVOMHCqwlrjrbhPi2urkDOoKBEVvXsUL8Tz390jPUu1R7nTHI j6G95+SZd52O2ccjVegvqMPe6CPoa4VOoEcc1xdqiXZisxewJOTKjN1CpqNQGg82DZtU bY2w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=m9lQFJuvJrwOAGItYL0Yq0F1uOB4F66QAhKkaIHI7D8=; b=VBb0Bq8+FaKs9GZt64B2jmTgVa8mxVKK3QePXd7vWA1YYjuMsmnOTwCupaV7fv44sn cYUeph4yxT75uQQhzVbTUDKUt7+o43z8fA+0IK2aryQgid5QM5b1AbsVg7lrtsbA/IKT nP7iVFi2yiEx03wLBwiG54xGfqen7jsMiRfw2vS32oIxQYJvYXtksh3BchhcsZQiBlnJ LpSFlqZNNRpclS3wUaOeKWNMTB4A8+kCyXjOSgQBkF3hnG6Rz8cVdBtlDxEBH20/b3oo siBo1gW1bHpffzTeHN7O/50CcTx6C2OKFESI3X2rroOrvPWFX1HngpqQKPkqbXdmj4Dw uq+g== X-Gm-Message-State: AOAM530AY5jS5SN3wXxCftNJVxVmxZqXyPykn3PoP9nKiCQH1ldMb4UM SI8Z+tgcX60TDuVrgQSDmIo= X-Google-Smtp-Source: ABdhPJwvEtcMrFVzUoQIfsPd+GNeH4YCCjvRQ0DSwfbo8gCpPjld5hQ/hEtzEQVMDybxEoxrQeYb6A== X-Received: by 2002:a17:906:5293:: with SMTP id c19mr68464483ejm.72.1609782482293; Mon, 04 Jan 2021 09:48:02 -0800 (PST) Received: from localhost.localdomain ([95.87.199.238]) by smtp.gmail.com with ESMTPSA id l14sm44107750edq.35.2021.01.04.09.48.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 04 Jan 2021 09:48:01 -0800 (PST) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v8 25/44] kernel-shark: Add ksmodel_get_bin() Date: Mon, 4 Jan 2021 19:47:05 +0200 Message-Id: <20210104174724.70404-26-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210104174724.70404-1-y.karadz@gmail.com> References: <20210104174724.70404-1-y.karadz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org The method retrieves the Id of the bin of the model that contains given entry. Signed-off-by: Yordan Karadzhov (VMware) --- src/libkshark-model.c | 27 +++++++++++++++++++++++++++ src/libkshark-model.h | 3 +++ 2 files changed, 30 insertions(+) diff --git a/src/libkshark-model.c b/src/libkshark-model.c index f3864bf..2301b06 100644 --- a/src/libkshark-model.c +++ b/src/libkshark-model.c @@ -1325,3 +1325,30 @@ ksmodel_get_task_missed_events(struct kshark_trace_histo *histo, match_pid_missed_events, sd, &pid, col, index); } + +/** + * @brief Find the bin Id of a give entry. + * + * @param histo: Input location for the model descriptor. + * @param entry: Input location for entry. + * + * @returns If the timestamp of the entry is inside the range of the model the + * function returns the Id of the bin it belongs to. + * If the timestamp of the entry is outside of the range of the model + * the function returns UPPER_OVERFLOW_BIN or LOWER_OVERFLOW_BIN + * (negative values). + */ +int ksmodel_get_bin(struct kshark_trace_histo *histo, + const struct kshark_entry *entry) +{ + if (entry->ts < histo->min) + return UPPER_OVERFLOW_BIN; + + if (entry->ts > histo->max) + return LOWER_OVERFLOW_BIN; + + if (entry->ts == histo->max) + return histo->n_bins - 1; + + return (entry->ts - histo->min) / histo->bin_size; +} diff --git a/src/libkshark-model.h b/src/libkshark-model.h index b862480..8989ee0 100644 --- a/src/libkshark-model.h +++ b/src/libkshark-model.h @@ -175,6 +175,9 @@ static inline double ksmodel_bin_time(struct kshark_trace_histo *histo, return ksmodel_bin_ts(histo, bin) * 1e-9; } +int ksmodel_get_bin(struct kshark_trace_histo *histo, + const struct kshark_entry *entry); + #ifdef __cplusplus } #endif // __cplusplus From patchwork Mon Jan 4 17:47:06 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11997173 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7FE30C433E6 for ; Mon, 4 Jan 2021 17:49:19 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 56B7220700 for ; Mon, 4 Jan 2021 17:49:19 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726616AbhADRtT (ORCPT ); Mon, 4 Jan 2021 12:49:19 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34378 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726246AbhADRtR (ORCPT ); Mon, 4 Jan 2021 12:49:17 -0500 Received: from mail-ej1-x62b.google.com (mail-ej1-x62b.google.com [IPv6:2a00:1450:4864:20::62b]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 54484C0617BF for ; Mon, 4 Jan 2021 09:48:04 -0800 (PST) Received: by mail-ej1-x62b.google.com with SMTP id b9so37958611ejy.0 for ; Mon, 04 Jan 2021 09:48:04 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=XKKQKSyh11GV8jmzTAm3bhr1xnYyypCdbLzIBowMB7U=; b=vgsBe0eqXrd6EiWuzb8Z4K5HnP4CvoBFXOtHJlZ5K0rhfhP+bDaE0tHby+FnICq4RD /dRJmX9DQ8dqRUoD1wqSArsn5G9CzRqhmy0Mcv4wvuagbhqq4n+/NQnADXn6WXLAVLUx O4jtJGvjS6FlklSiLcC2ZCdY4HZSnQf8yshlF9Vuh3lnpMmoVlpNVfZcPSKLcKS6d8vv ScmjcvoOYmfCxbHpFFMRsv3LgnbpTKftgtzk5BRL4Z6SHCcK3e9Ff7wGH7fyaJQ9cxb+ PCwS26JGi9u+54afPsks34yvdPP8ob93Ajlyu1UxoMZcic/RG7kA/7fd3IlwgmARcbXE 1Xjw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=XKKQKSyh11GV8jmzTAm3bhr1xnYyypCdbLzIBowMB7U=; b=f67BiaeEfKF4jkK3FW4i9SjUHMYWLQZ/AauK01SRjjPwIs4HLEPqisqUxKI1W6cXCR lSN5+SnLckZt8hfApX6bhPQJuJoP7niFrk1a0qvfviIjYb5EeeHEmSohYugS3y9wDYTO oo5ACy4ulBCSpRiVk/pprtqa+ntNUanhUUF0lsjsjVlEuFucPfsz/hfzUcTQML+wNnKR L3CSVAYv24eyVNYjw0h3KVlBnledOZrVO3tM7YHuQKReF8YPLtnPzrawfbQabsKIz1Iu tP8P6UnLX2uBrm9nHUCKJS89yb+Re0w53TgQoBIEZduJ2gUHxefHBTR4moWaTmnTlYS4 AHWw== X-Gm-Message-State: AOAM531Eh6il4hYcb86sgrKl3X0jnj7odzs+a1CY/w8W8GLMdxl/aERh v62wWjwQ5qFk+w8+NpV2xtk= X-Google-Smtp-Source: ABdhPJzSEfabpSnkn/fIBPXMFNUvct+1dWK/r8VSVnpZqhGopuDjdoSuL/OtdZ13x9F/XW98qRbWkw== X-Received: by 2002:a17:906:7f83:: with SMTP id f3mr56443269ejr.282.1609782483163; Mon, 04 Jan 2021 09:48:03 -0800 (PST) Received: from localhost.localdomain ([95.87.199.238]) by smtp.gmail.com with ESMTPSA id l14sm44107750edq.35.2021.01.04.09.48.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 04 Jan 2021 09:48:02 -0800 (PST) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v8 26/44] kernel-shark: Protect ksmodel_set_in_range_bining() Date: Mon, 4 Jan 2021 19:47:06 +0200 Message-Id: <20210104174724.70404-27-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210104174724.70404-1-y.karadz@gmail.com> References: <20210104174724.70404-1-y.karadz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org Handle the case when the number of bins is zero or negative. Signed-off-by: Yordan Karadzhov (VMware) --- src/libkshark-model.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/libkshark-model.c b/src/libkshark-model.c index 2301b06..44f829d 100644 --- a/src/libkshark-model.c +++ b/src/libkshark-model.c @@ -97,8 +97,19 @@ static void ksmodel_set_in_range_bining(struct kshark_trace_histo *histo, int64_t corrected_range, delta_range, range = max - min; struct kshark_entry *last; + if (n <= 0) { + histo->n_bins = histo->bin_size = 0; + histo->min = min; + histo->max = max; + + free(histo->bin_count); + free(histo->map); + + return; + } + /* The size of the bin must be >= 1, hence the range must be >= n. */ - if (n == 0 || range < n) { + if (range < n) { range = n; max = min + n; } From patchwork Mon Jan 4 17:47:07 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11997211 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id A096FC433E0 for ; Mon, 4 Jan 2021 17:49:43 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 7E90920715 for ; Mon, 4 Jan 2021 17:49:43 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727357AbhADRtn (ORCPT ); Mon, 4 Jan 2021 12:49:43 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34496 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727406AbhADRtm (ORCPT ); Mon, 4 Jan 2021 12:49:42 -0500 Received: from mail-ej1-x629.google.com (mail-ej1-x629.google.com [IPv6:2a00:1450:4864:20::629]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7E3B7C061382 for ; Mon, 4 Jan 2021 09:48:05 -0800 (PST) Received: by mail-ej1-x629.google.com with SMTP id ga15so5774097ejb.4 for ; Mon, 04 Jan 2021 09:48:05 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=6pblZ2szoZIMM38OYzSL6FEtzabm902Qt/+Z3+JswsQ=; b=E5qkPdPVvUwR4U05ynJmNcVZEGFAG8gNGfBCG5e2Zt63cIZzBokEhGBLERWNdzxK0R TBi5XhV4N2ypkIlHGozc17nMO0zeCLXVTG2MwXKM2uVrc1NIZEL7Hs3Qy0wGX6jBgGCJ O9MIJ5G8DCSiBHXpl79RdHnvPqQ//pp2CP94wqkqDi+fTz6arlab2+/Fz0awhZ7NfBST 4Z8UstElvbOMNjaHHaNbDN5CIkcwNNUCBIuup2r5DrjRNXe+fcJYqH3C7qGmBPetfmx+ Fm8hxZLZ/Sz671XxAuptowUnktef9fmP7VYsN5JmHZCWCLkl6z6DwKo9b0A3+3GkixlN 5Jvw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=6pblZ2szoZIMM38OYzSL6FEtzabm902Qt/+Z3+JswsQ=; b=dBZ/3RakXW+q0y8TeN8A9AOvM+M6b+c0akNUwB/A8X1KJtSKiClwHww6bi4C9oKCnF OAj4MiqS+S2rvxK/ewfXrmL13sYDQw0y5N21wFakCsPu7BGjGxnw3ygyP53b5n6KkjjS 3aANpVgS3F57m+FnHm+ShPnsXKivS0fLlL3gAXGZJ94+/p9DrDoVKE5fqNi+quB5wRxM R5KudtyoLLaoY/hUg/9xZReVHEzkUhoUsWbKDI5+apxGuoTeZI7fd2dWK/RzuvfA0F2n in90JaI2a5+6iMD16rv9GESaHTwpmJe8mkPs87k5IvLgBpxpEVBJ5fy6OYLUAe+lQZA6 +/1g== X-Gm-Message-State: AOAM530S4wEUvUJo1QTKC+8hs3oK2ZxZ3yiYoL96+Wn7JLm9ZdIIkktI Z4W7GDZ4pQHInRSEJmCOQaI= X-Google-Smtp-Source: ABdhPJwMiUgl3fdw4ZSGyFqcG7txJnigAyvcPVYkBVEjsJSCMQDfPr2zPXQQi5tayUTRxeMmBcLv8Q== X-Received: by 2002:a17:906:add7:: with SMTP id lb23mr71060756ejb.352.1609782484208; Mon, 04 Jan 2021 09:48:04 -0800 (PST) Received: from localhost.localdomain ([95.87.199.238]) by smtp.gmail.com with ESMTPSA id l14sm44107750edq.35.2021.01.04.09.48.03 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 04 Jan 2021 09:48:03 -0800 (PST) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v8 27/44] kernel-shark: Add methods for time calibration Date: Mon, 4 Jan 2021 19:47:07 +0200 Message-Id: <20210104174724.70404-28-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210104174724.70404-1-y.karadz@gmail.com> References: <20210104174724.70404-1-y.karadz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org We add an infrastructure for correcting the timestamps of the entries. This is needed in order to correlate Data streams that have been recorded using non-synchronized clocks. The infrastructure can handle an arbitrary timestamps correction formula, however for the moment we only provide calibration that adds a constant offset. Signed-off-by: Yordan Karadzhov (VMware) --- src/libkshark-tepdata.c | 13 +++++- src/libkshark.c | 99 +++++++++++++++++++++++++++++++++++++++++ src/libkshark.h | 27 +++++++++++ 3 files changed, 138 insertions(+), 1 deletion(-) diff --git a/src/libkshark-tepdata.c b/src/libkshark-tepdata.c index f2482f9..064393d 100644 --- a/src/libkshark-tepdata.c +++ b/src/libkshark-tepdata.c @@ -338,6 +338,9 @@ static ssize_t get_records(struct kshark_context *kshark_ctx, entry = &temp_rec->entry; missed_events_action(stream, rec, entry); + /* Apply time calibration. */ + kshark_postprocess_entry(stream, rec, entry); + entry->stream_id = stream->stream_id; temp_next = &temp_rec->next; @@ -360,6 +363,12 @@ static ssize_t get_records(struct kshark_context *kshark_ctx, entry->stream_id = stream->stream_id; + /* + * Post-process the content of the entry. This includes + * time calibration and event-specific plugin actions. + */ + kshark_postprocess_entry(stream, rec, entry); + pid = entry->pid; /* Apply Id filtering. */ @@ -527,8 +536,10 @@ static ssize_t tepdata_load_matrix(struct kshark_data_stream *stream, if (cpu_array) (*cpu_array)[count] = e->cpu; - if (ts_array) + if (ts_array) { + kshark_calib_entry(stream, e); (*ts_array)[count] = e->ts; + } if (pid_array) (*pid_array)[count] = e->pid; diff --git a/src/libkshark.c b/src/libkshark.c index cc8bd93..315c24f 100644 --- a/src/libkshark.c +++ b/src/libkshark.c @@ -131,6 +131,7 @@ static void kshark_stream_free(struct kshark_data_stream *stream) kshark_hash_id_free(stream->tasks); + free(stream->calib_array); free(stream->file); free(stream->name); free(stream->interface); @@ -1367,6 +1368,37 @@ void kshark_plugin_actions(struct kshark_data_stream *stream, } } +/** + * @brief Time calibration of the timestamp of the entry. + * + * @param stream: Input location for a Trace data stream pointer. + * @param entry: Output location for entry. + */ +void kshark_calib_entry(struct kshark_data_stream *stream, + struct kshark_entry *entry) +{ + if (stream->calib && stream->calib_array) { + /* Calibrate the timestamp of the entry. */ + stream->calib(&entry->ts, stream->calib_array); + } +} + +/** + * @brief Post-process the content of the entry. This includes time calibration + * and all registered event-specific plugin actions. + * + * @param stream: Input location for a Trace data stream pointer. + * @param record: Input location for the trace record. + * @param entry: Output location for entry. + */ +void kshark_postprocess_entry(struct kshark_data_stream *stream, + void *record, struct kshark_entry *entry) +{ + kshark_calib_entry(stream, entry); + + kshark_plugin_actions(stream, record, entry); +} + static inline void free_ptr(void *ptr) { if (ptr) @@ -1764,6 +1796,73 @@ kshark_get_entry_back(const struct kshark_entry_request *req, return get_entry(req, data, index, req->first, end, -1); } +static int compare_time(const void* a, const void* b) +{ + const struct kshark_entry *entry_a, *entry_b; + + entry_a = *(const struct kshark_entry **) a; + entry_b = *(const struct kshark_entry **) b; + + if (entry_a->ts > entry_b->ts) + return 1; + + if (entry_a->ts < entry_b->ts) + return -1; + + return 0; +} + +static void kshark_data_qsort(struct kshark_entry **entries, size_t size) +{ + qsort(entries, size, sizeof(struct kshark_entry *), compare_time); +} + +/** + * Add constant offset to the timestamp of the entry. To be used by the sream + * object as a System clock calibration callback function. + */ +void kshark_offset_calib(int64_t *ts, int64_t *argv) +{ + *ts += argv[0]; +} + +/** + * @brief Apply constant offset to the timestamps of all entries from a given + * Data stream. + * + * @param kshark_ctx: Input location for the session context pointer. + * @param entries: Input location for the trace data. + * @param size: The size of the trace data. + * @param sd: Data stream identifier. + * @param offset: The constant offset to be added (in nanosecond). + */ +void kshark_set_clock_offset(struct kshark_context *kshark_ctx, + struct kshark_entry **entries, size_t size, + int sd, int64_t offset) +{ + struct kshark_data_stream *stream; + int64_t correction; + + stream = kshark_get_data_stream(kshark_ctx, sd); + if (!stream) + return; + + if (!stream->calib_array) { + stream->calib = kshark_offset_calib; + stream->calib_array = calloc(1, sizeof(*stream->calib_array)); + stream->calib_array_size = 1; + } + + correction = offset - stream->calib_array[0]; + stream->calib_array[0] = offset; + + for (size_t i = 0; i < size; ++i) + if (entries[i]->stream_id == sd) + entries[i]->ts += correction; + + kshark_data_qsort(entries, size); +} + static int first_in_time_entry(struct kshark_entry_data_set *buffer, int n_buffers, size_t *count) { int64_t t_min = INT64_MAX; diff --git a/src/libkshark.h b/src/libkshark.h index edf3dcf..dce3dd2 100644 --- a/src/libkshark.h +++ b/src/libkshark.h @@ -129,6 +129,12 @@ static const char top_name[] = { 0x1b, 0x00 }; // Non printable character */ #define KS_UNNAMED (char *) &top_name +/** + * Timestamp calibration function type. To be user for system clock + * calibration. + */ +typedef void (*time_calib_func) (int64_t *, int64_t *); + struct kshark_data_stream; /** A function type to be used by the method interface of the data stream. */ @@ -327,6 +333,15 @@ struct kshark_data_stream { /** The number of plugins registered for this stream.*/ int n_plugins; + /** System clock calibration function. */ + time_calib_func calib; + + /** An array of time calibration constants. */ + int64_t *calib_array; + + /** The size of the array of time calibration constants. */ + size_t calib_array_size; + /** List of Plugin's Event handlers. */ struct kshark_event_proc_handler *event_handlers; @@ -590,6 +605,12 @@ void kshark_clear_all_filters(struct kshark_context *kshark_ctx, void kshark_plugin_actions(struct kshark_data_stream *stream, void *record, struct kshark_entry *entry); +void kshark_calib_entry(struct kshark_data_stream *stream, + struct kshark_entry *entry); + +void kshark_postprocess_entry(struct kshark_data_stream *stream, + void *record, struct kshark_entry *entry); + /** Search failed identifiers. */ enum kshark_search_failed { /** All entries have greater timestamps. */ @@ -980,6 +1001,12 @@ struct kshark_config_doc *kshark_open_config_file(const char *file_name, struct kshark_config_doc *kshark_json_to_conf(struct json_object *jobj); +void kshark_offset_calib(int64_t *ts, int64_t *atgv); + +void kshark_set_clock_offset(struct kshark_context *kshark_ctx, + struct kshark_entry **entries, size_t size, + int sd, int64_t offset); + /** Structure representing a data set made of KernelShark entries. */ struct kshark_entry_data_set { /** Array of entries pointers. */ From patchwork Mon Jan 4 17:47:08 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11997221 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 29F1BC43381 for ; Mon, 4 Jan 2021 17:49:44 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id D89DA206B2 for ; Mon, 4 Jan 2021 17:49:43 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727520AbhADRtn (ORCPT ); Mon, 4 Jan 2021 12:49:43 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34498 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727415AbhADRtm (ORCPT ); Mon, 4 Jan 2021 12:49:42 -0500 Received: from mail-ej1-x630.google.com (mail-ej1-x630.google.com [IPv6:2a00:1450:4864:20::630]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 424EFC061383 for ; Mon, 4 Jan 2021 09:48:07 -0800 (PST) Received: by mail-ej1-x630.google.com with SMTP id d17so37831880ejy.9 for ; Mon, 04 Jan 2021 09:48:07 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=4lgbQr3iEAPN/a2FPUKknRmUlbNhPBfhFpFICsoMSsI=; b=lW6PrtwsQYLvSoFqwJu30Yxn0JSq61qZnupKf+r37mdavrbJ6sK3zuU0DwzHP4p014 do8bV8kmZCz6+rvC/kwT75Flhi4eFB0MTRopGix2L9I7kNu5v+YYWasTg5fnjbC3Ta6s x+z5oFn4eUKaw3dUFl+Fg10AeJqYJ9uHMxZo5hDGxeM3B9GljHHNtmCw3W/SdpMvmL6d OSLT52Y0uA5sD/lYT2UnZ9tex1KsKuzdNM5vCnFYfbybihSeSL1xjIc242WNm6CEt6Bt 9JMkdJr3AO0OFJK7+yHvy7xZlzUfK9CdK8wZ2FcOfNcD06zgw77tU/xkLlykFayqeUxH wKgw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=4lgbQr3iEAPN/a2FPUKknRmUlbNhPBfhFpFICsoMSsI=; b=AKNXW/mNeK83Gl1uiCE2h8HC/cS2FEUIIJzY7lETGTINfWEOIv/FuQmpd5oyyCJFHe ZTyFf9pMgebElZl2DF+UXYj65SP6YFoGoEMziJuwj4jOAc8pLfSHmjVYGtX52UkEwLl2 aZwl3AhwWydRJjhOLHvZs++A41OU490ojkvm/tRKbu3vohTcm0fmYbHiz+IRK6pF9NYG 1QCB7Fc2F1/YaOcWyZQfcTB5kxtqczopd7drL923hCFkbIx/x81sdGc7EslPIgnQ/4ym gq9m6KE57n7pldXhnBT61tgMNjwv/+ZKDRFjU1eZfApEyl43dagkrLuptkRIk+DIhAgQ WnSQ== X-Gm-Message-State: AOAM531ycBwwPQfi4FMpRTHIwDfHRS/NVTmQej7kr+QYGR24YE8lOduK z1zXIU7jiuf0nbWtW5arS5D90Kl3WXIkkw== X-Google-Smtp-Source: ABdhPJzPCg3fCgXDsN4BkQdnXv7J8e+wTAvKxD9iCwpH1Q0QZad3MJoi401ozf3PCxC9OQ71N7BVOA== X-Received: by 2002:a17:906:7c49:: with SMTP id g9mr68106338ejp.185.1609782485384; Mon, 04 Jan 2021 09:48:05 -0800 (PST) Received: from localhost.localdomain ([95.87.199.238]) by smtp.gmail.com with ESMTPSA id l14sm44107750edq.35.2021.01.04.09.48.04 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 04 Jan 2021 09:48:04 -0800 (PST) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v8 28/44] kernel-shark: Integrate streams with libkshark-configio Date: Mon, 4 Jan 2021 19:47:08 +0200 Message-Id: <20210104174724.70404-29-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210104174724.70404-1-y.karadz@gmail.com> References: <20210104174724.70404-1-y.karadz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org The configuration input/output is adapted in order to be able to work with the new version of the C API. Now it can handle multiple Data streams. We are re-enabling the corresponding example as well. Signed-off-by: Yordan Karadzhov (VMware) --- examples/CMakeLists.txt | 8 +- examples/configio.c | 20 +- src/CMakeLists.txt | 2 +- src/libkshark-configio.c | 1208 ++++++++++++++++++++++++++++++++------ src/libkshark.h | 73 ++- 5 files changed, 1093 insertions(+), 218 deletions(-) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 2f6acea..b8bc79a 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -16,10 +16,10 @@ message(STATUS "datahisto") add_executable(dhisto datahisto.c) target_link_libraries(dhisto kshark) -# message(STATUS "confogio") -# add_executable(confio configio.c) -# target_link_libraries(confio kshark) -# +message(STATUS "confogio") +add_executable(confio configio.c) +target_link_libraries(confio kshark) + # message(STATUS "dataplot") # add_executable(dplot dataplot.cpp) # target_link_libraries(dplot kshark-plot) diff --git a/examples/configio.c b/examples/configio.c index faa116a..9710d53 100644 --- a/examples/configio.c +++ b/examples/configio.c @@ -7,22 +7,26 @@ int main(int argc, char **argv) { struct kshark_config_doc *conf, *filter, *hello; struct kshark_context *kshark_ctx; - int *ids = NULL, i; + struct kshark_data_stream *stream; + int sd, *ids = NULL, i; /* Create a new kshark session. */ kshark_ctx = NULL; if (!kshark_instance(&kshark_ctx)) return 1; + sd = kshark_add_stream(kshark_ctx); + stream = kshark_ctx->stream[sd]; + if (argc == 1) { - tracecmd_filter_id_add(kshark_ctx->show_task_filter, 314); - tracecmd_filter_id_add(kshark_ctx->show_task_filter, 42); + kshark_hash_id_add(stream->show_task_filter, 314); + kshark_hash_id_add(stream->show_task_filter, 42); /* Create a new Confog. doc. */ conf = kshark_config_new("foo.bar.config", KS_CONFIG_JSON); /* Add filter's info. */ - filter = kshark_export_all_filters(kshark_ctx, KS_CONFIG_JSON); + filter = kshark_export_all_filters(kshark_ctx, sd, KS_CONFIG_JSON); kshark_config_doc_add(conf, "Filters" ,filter); /* Add "Hello Kernel" message. */ @@ -39,11 +43,11 @@ int main(int argc, char **argv) /* Retrieve the filter's info. */ filter = kshark_config_alloc(KS_CONFIG_JSON); if (kshark_config_doc_get(conf, "Filters" ,filter)) { - kshark_import_all_filters(kshark_ctx, filter); + kshark_import_all_filters(kshark_ctx, sd, filter); /* Get the array of Ids to be fitered. */ - ids = tracecmd_filter_ids(kshark_ctx->show_task_filter); - for (i = 0; i < kshark_ctx->show_task_filter->count; ++i) + ids = kshark_hash_ids(stream->show_task_filter); + for (i = 0; i < stream->show_task_filter->count; ++i) printf("pid: %i\n", ids[i]); } @@ -58,7 +62,7 @@ int main(int argc, char **argv) } kshark_free_config_doc(conf); - + kshark_close(kshark_ctx, sd); kshark_free(kshark_ctx); return 0; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index da5448c..22bdc7c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -8,7 +8,7 @@ add_library(kshark SHARED libkshark.c libkshark-model.c libkshark-plugin.c libkshark-tepdata.c -# libkshark-configio.c + libkshark-configio.c libkshark-collection.c) target_link_libraries(kshark trace::cmd diff --git a/src/libkshark-configio.c b/src/libkshark-configio.c index cb7ca54..98098da 100644 --- a/src/libkshark-configio.c +++ b/src/libkshark-configio.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: LGPL-2.1 /* - * Copyright (C) 2018 VMware Inc, Yordan Karadzhov + * Copyright (C) 2018 VMware Inc, Yordan Karadzhov (VMware) */ /** @@ -13,15 +13,17 @@ #ifndef _GNU_SOURCE /** Use GNU C Library. */ #define _GNU_SOURCE - -#endif +#endif // _GNU_SOURCE #include +#include #include // KernelShark #include "libkshark.h" #include "libkshark-model.h" +#include "libkshark-plugin.h" +#include "libkshark-tepdata.h" static struct json_object *kshark_json_config_alloc(const char *type) { @@ -299,7 +301,7 @@ bool kshark_config_doc_get(struct kshark_config_doc *conf, case KS_CONFIG_JSON: json_object_put(val->conf_doc); if (!get_jval(conf, key, &val->conf_doc)) - goto fail; + goto not_found; return true; case KS_CONFIG_STRING: @@ -308,7 +310,7 @@ bool kshark_config_doc_get(struct kshark_config_doc *conf, goto fail; if (!get_jval(conf, key, &tmp->conf_doc)) - goto fail; + goto not_found; val->conf_doc = (char *) json_object_get_string(tmp->conf_doc); @@ -331,7 +333,9 @@ bool kshark_config_doc_get(struct kshark_config_doc *conf, return true; fail: - fprintf(stderr, "Failed to get config. document.\n"); + fprintf(stderr, "Failed to get config. document <%s>.\n", key); + + not_found: return false; } @@ -348,6 +352,19 @@ kshark_record_config_new(enum kshark_config_formats format) return kshark_config_new("kshark.config.record", format); } +/** + * @brief Create an empty Data Stream Configuration document. The type + * description is set to "kshark.config.stream". + * + * @returns kshark_config_doc instance on success, otherwise NULL. Use + * kshark_free_config_doc() to free the object. + */ +struct kshark_config_doc * +kshark_stream_config_new(enum kshark_config_formats format) +{ + return kshark_config_new("kshark.config.stream", format); +} + /** * @brief Create an empty Filter Configuration document. The type description * is set to "kshark.config.filter". @@ -361,6 +378,19 @@ kshark_filter_config_new(enum kshark_config_formats format) return kshark_config_new("kshark.config.filter", format); } +/** + * @brief Create an empty Session Configuration document. The type description + * is set to "kshark.config.filter". + * + * @returns kshark_config_doc instance on success, otherwise NULL. Use + * kshark_free_config_doc() to free the object. + */ +struct kshark_config_doc * +kshark_session_config_new(enum kshark_config_formats format) +{ + return kshark_config_new("kshark.config.session", format); +} + /** * @brief Create an empty Text Configuration document. The Text Configuration * documents do not use type descriptions. @@ -419,10 +449,11 @@ bool kshark_type_check(struct kshark_config_doc *conf, const char *type) } } -static bool kshark_trace_file_to_json(const char *file, +static bool kshark_trace_file_to_json(const char *file, const char *name, struct json_object *jobj) { - struct json_object *jfile_name, *jtime; + struct json_object *jfile_name, *jbuffer_name, *jtime; + char abs_path[FILENAME_MAX]; struct stat st; if (!file || !jobj) @@ -433,13 +464,21 @@ static bool kshark_trace_file_to_json(const char *file, return false; } - jfile_name = json_object_new_string(file); + if (!realpath(file, abs_path)) { + fprintf(stderr, "Unable to get absolute pathname for %s\n", + file); + return false; + } + + jfile_name = json_object_new_string(abs_path); + jbuffer_name = json_object_new_string(name); jtime = json_object_new_int64(st.st_mtime); if (!jfile_name || !jtime) goto fail; json_object_object_add(jobj, "file", jfile_name); + json_object_object_add(jobj, "name", jbuffer_name); json_object_object_add(jobj, "time", jtime); return true; @@ -458,13 +497,15 @@ static bool kshark_trace_file_to_json(const char *file, * Configuration document. * * @param file: The name of the file. + * @param name: The name of the data buffer. * @param format: Input location for the Configuration format identifier. * Currently only Json format is supported. * - * @returns True on success, otherwise False. + * @returns kshark_config_doc instance on success, otherwise NULL. Use + * kshark_free_config_doc() to free the object. */ struct kshark_config_doc * -kshark_export_trace_file(const char *file, +kshark_export_trace_file(const char *file, const char *name, enum kshark_config_formats format) { /* Create a new Configuration document. */ @@ -476,7 +517,7 @@ kshark_export_trace_file(const char *file, switch (format) { case KS_CONFIG_JSON: - kshark_trace_file_to_json(file, conf->conf_doc); + kshark_trace_file_to_json(file, name, conf->conf_doc); return conf; default: @@ -486,19 +527,30 @@ kshark_export_trace_file(const char *file, } } -static bool kshark_trace_file_from_json(const char **file, +static bool kshark_trace_file_from_json(const char **file, const char **name, + const char *type, struct json_object *jobj) { - struct json_object *jfile_name, *jtime; - const char *file_str; + struct json_object *jfile_name, *jbuffer_name, *jtime; + const char *file_str, *name_str; struct stat st; + char *header; int64_t time; + bool type_OK = true; if (!jobj) return false; - if (!kshark_json_type_check(jobj, "kshark.config.data") || + if (type) { + /* Make sure that the condition document has a correct type. */ + type_OK = false; + if (asprintf(&header, "kshark.config.%s", type) >= 0) + type_OK = kshark_json_type_check(jobj, header); + } + + if (!type_OK || !json_object_object_get_ex(jobj, "file", &jfile_name) || + !json_object_object_get_ex(jobj, "name", &jbuffer_name) || !json_object_object_get_ex(jobj, "time", &jtime)) { fprintf(stderr, "Failed to retrieve data file from json_object.\n"); @@ -506,6 +558,7 @@ static bool kshark_trace_file_from_json(const char **file, } file_str = json_object_get_string(jfile_name); + name_str = json_object_get_string(jbuffer_name); time = json_object_get_int64(jtime); if (stat(file_str, &st) != 0) { @@ -514,15 +567,24 @@ static bool kshark_trace_file_from_json(const char **file, } if (st.st_mtime != time) { - fprintf(stderr,"Timestamp mismatch!\nFile %s", file_str); + fprintf(stderr, "Timestamp mismatch! (%li!=%li)\nFile %s\n", + time, st.st_mtime, file_str); return false; } *file = file_str; + *name = name_str; return true; } +/* Quiet warnings over documenting simple structures */ +//! @cond Doxygen_Suppress + +#define TOP_BUFF_NAME "top buffer" + +//! @endcond + /** * @brief Read the name of a trace data file and its timestamp from a * Configuration document and check if such a file exists. @@ -532,27 +594,379 @@ static bool kshark_trace_file_from_json(const char **file, * @param conf: Input location for the kshark_config_doc instance. Currently * only Json format is supported. * - * @returns The name of the file on success, otherwise NULL. "conf" has - * the ownership over the returned string. + * @returns The Id number of the data stream associated with the loaded file on + * success, otherwise -1. "conf" has the ownership over the returned + * string. */ -const char* kshark_import_trace_file(struct kshark_context *kshark_ctx, - struct kshark_config_doc *conf) +int kshark_import_trace_file(struct kshark_context *kshark_ctx, + struct kshark_config_doc *conf) { - const char *file = NULL; + const char *file = NULL, *name = NULL; + int sd = -1; + switch (conf->format) { case KS_CONFIG_JSON: - if (kshark_trace_file_from_json(&file, conf->conf_doc)) - kshark_open(kshark_ctx, file); + if (kshark_trace_file_from_json(&file, &name, "data", + conf->conf_doc)) { + if (strcmp(name, KS_UNNAMED) == 0 || + strcmp(name, TOP_BUFF_NAME) == 0) { + sd = kshark_open(kshark_ctx, file); + } else { + int sd_top; + + sd_top = kshark_tep_find_top_stream(kshark_ctx, + file); + if (sd_top < 0) { + /* + * The "top" steam (buffer) has to be + * initialized first. + */ + sd_top = kshark_open(kshark_ctx, file); + } + + if (sd_top >= 0) + sd = kshark_tep_open_buffer(kshark_ctx, + sd_top, + name); + + if (sd >= 0) + kshark_tep_handle_plugins(kshark_ctx, sd); + } + } + + break; + default: + fprintf(stderr, "Document format %d not supported\n", + conf->format); break; + } + + return sd; +} + +static bool kshark_plugin_to_json(struct kshark_plugin_list *plugin, + struct json_object *jobj) +{ + struct json_object *jname = json_object_new_string(plugin->name); + + if (!kshark_trace_file_to_json(plugin->file, plugin->name, jobj) || + !jname) { + json_object_put(jname); + return false; + } + + json_object_object_add(jobj, "name", jname); + return true; +} + +/** + * @brief Record the name of a plugin's obj file and its timestamp into a + * Configuration document. + * + * @param plugin: The plugin to be expected. + * @param format: Input location for the Configuration format identifier. + * Currently only Json format is supported. + * + * @returns kshark_config_doc instance on success, otherwise NULL. Use + * kshark_free_config_doc() to free the object. + */ +struct kshark_config_doc * +kshark_export_plugin_file(struct kshark_plugin_list *plugin, + enum kshark_config_formats format) +{ + /* Create a new Configuration document. */ + struct kshark_config_doc *conf = + kshark_config_new("kshark.config.library", format); + + if (!conf) + return NULL; + + switch (format) { + case KS_CONFIG_JSON: + kshark_plugin_to_json(plugin, conf->conf_doc); + return conf; + + default: + fprintf(stderr, "Document format %d not supported\n", + conf->format); + return NULL; + } +} + +static bool kshark_all_plugins_to_json(struct kshark_context *kshark_ctx, + struct json_object *jobj) +{ + struct kshark_plugin_list *plugin = kshark_ctx->plugins; + struct json_object *jfile, *jlist; + + jlist = json_object_new_array(); + if (!jlist) + return false; + + while (plugin) { + jfile = json_object_new_object(); + if (!kshark_trace_file_to_json(plugin->file, plugin->name, jfile)) + goto fail; + + json_object_array_add(jlist, jfile); + plugin = plugin->next; + } + + json_object_object_add(jobj, "obj. files", jlist); + + return true; + + fail: + fprintf(stderr, "Failed to allocate memory for json_object.\n"); + json_object_put(jobj); + json_object_put(jlist); + return false; +} + +/** + * @brief Record the current list of registered plugins into a + * Configuration document. + * + * @param kshark_ctx: Input location for session context pointer. + * @param format: Input location for the Configuration format identifier. + * Currently only Json format is supported. + * + * @returns kshark_config_doc instance on success, otherwise NULL. Use + * kshark_free_config_doc() to free the object. + */ +struct kshark_config_doc * +kshark_export_all_plugins(struct kshark_context *kshark_ctx, + enum kshark_config_formats format) +{ + struct kshark_config_doc *conf = + kshark_config_new("kshark.config.plugins", KS_CONFIG_JSON); + + if (!conf) + return NULL; + + switch (format) { + case KS_CONFIG_JSON: + kshark_all_plugins_to_json(kshark_ctx, conf->conf_doc); + return conf; + + default: + fprintf(stderr, "Document format %d not supported\n", + conf->format); + return NULL; + } +} + +static bool kshark_plugin_from_json(struct kshark_context *kshark_ctx, + struct json_object *jobj) +{ + const char *file, *name; + + if (!kshark_trace_file_from_json(&file, &name, NULL, jobj)) { + fprintf(stderr, "Failed to import plugin!\n"); + return false; + } + + return !!(long) kshark_register_plugin(kshark_ctx, name, file); +} + +static bool kshark_all_plugins_from_json(struct kshark_context *kshark_ctx, + struct json_object *jobj) +{ + struct json_object *jlist, *jfile; + int i, n_plugins; + + if (!kshark_ctx || !jobj) + return false; + + if (!kshark_json_type_check(jobj, "kshark.config.plugins") || + !json_object_object_get_ex(jobj, "obj. files", &jlist) || + json_object_get_type(jlist) != json_type_array) + goto fail; + + n_plugins = json_object_array_length(jlist); + for (i = 0; i < n_plugins; ++i) { + jfile = json_object_array_get_idx(jlist, i); + if (!jfile) + goto fail; + + kshark_plugin_from_json(kshark_ctx, jfile); + } + + return true; + + fail: + json_object_put(jfile); + json_object_put(jlist); + return false; +} + +/** + * @brief Load the list of registered plugins from a Configuration + * document. + * + * @param kshark_ctx: Input location for session context pointer. + * @param conf: Input location for the kshark_config_doc instance. Currently + * only Json format is supported. + * + * @returns True, if plugins have been loaded. If the configuration + * document contains no data or in a case of an error, the function + * returns False. + */ +bool kshark_import_all_plugins(struct kshark_context *kshark_ctx, + struct kshark_config_doc *conf) +{ + switch (conf->format) { + case KS_CONFIG_JSON: + return kshark_all_plugins_from_json(kshark_ctx, + conf->conf_doc); + + default: + fprintf(stderr, "Document format %d not supported\n", + conf->format); + return false; + } +} + +static void kshark_stream_plugins_to_json(struct kshark_data_stream *stream, + struct json_object *jobj) +{ + struct kshark_dpi_list *plugin = stream->plugins; + struct json_object *jlist, *jplg; + bool active; + + jlist = json_object_new_array(); + while (plugin) { + jplg = json_object_new_array(); + json_object_array_add(jplg, + json_object_new_string(plugin->interface->name)); + + active = plugin->status & KSHARK_PLUGIN_ENABLED; + json_object_array_add(jplg, json_object_new_boolean(active)); + + json_object_array_add(jlist, jplg); + + plugin = plugin->next; + } + + json_object_object_add(jobj, "registered", jlist); +} + +/** + * @brief Record the current list of plugins registered for a given Data + * stream into a Configuration document. + * + * @param stream: Input location for a Trace data stream pointer. + * @param format: Input location for the Configuration format identifier. + * Currently only Json format is supported. + * + * @returns kshark_config_doc instance on success, otherwise NULL. Use + * kshark_free_config_doc() to free the object. + */ +struct kshark_config_doc * +kshark_export_stream_plugins(struct kshark_data_stream *stream, + enum kshark_config_formats format) +{ + struct kshark_config_doc *conf = + kshark_config_new("kshark.config.plugins", KS_CONFIG_JSON); + + if (!conf) + return NULL; + + switch (format) { + case KS_CONFIG_JSON: + kshark_stream_plugins_to_json(stream, conf->conf_doc); + return conf; default: fprintf(stderr, "Document format %d not supported\n", conf->format); return NULL; } +} + +static bool kshark_stream_plugins_from_json(struct kshark_context *kshark_ctx, + struct kshark_data_stream *stream, + struct json_object *jobj) +{ + struct json_object *jlist, *jplg, *jname, *jstatus; + struct kshark_plugin_list *plugin; + struct kshark_dpi_list *dpi_list; + struct kshark_dpi *dpi; + int i, n_plugins; + bool active; + + jplg = jname = jstatus = NULL; + + if (!kshark_ctx || !stream || !jobj) + return false; + + if (!kshark_json_type_check(jobj, "kshark.config.plugins") || + !json_object_object_get_ex(jobj, "registered", &jlist) || + json_object_get_type(jlist) != json_type_array) + goto fail; - return file; + n_plugins = json_object_array_length(jlist); + for (i = 0; i < n_plugins; ++i) { + jplg = json_object_array_get_idx(jlist, i); + if (!jplg || + json_object_get_type(jplg) != json_type_array || + json_object_array_length(jplg) != 2) + goto fail; + + jname = json_object_array_get_idx(jplg, 0); + jstatus = json_object_array_get_idx(jplg, 1); + if (!jname || !jstatus) + goto fail; + + plugin = kshark_find_plugin_by_name(kshark_ctx->plugins, + json_object_get_string(jname)); + + if (plugin) { + active = json_object_get_boolean(jstatus); + dpi = plugin->process_interface; + dpi_list = kshark_register_plugin_to_stream(stream, dpi, + active); + + kshark_handle_dpi(stream, dpi_list, KSHARK_PLUGIN_INIT); + } + } + + return true; + + fail: + json_object_put(jplg); + json_object_put(jlist); + return false; +} + +/** + * @brief Load the list of registered plugins for a given Data + * stream from a Configuration document. + * + * @param kshark_ctx: Input location for session context pointer. + * @param stream: Input location for a Trace data stream pointer. + * @param conf: Input location for the kshark_config_doc instance. Currently + * only Json format is supported. + * + * @returns True, if plugins have been loaded. If the configuration + * document contains no data or in a case of an error, the function + * returns False. + */ +bool kshark_import_stream_plugins(struct kshark_context *kshark_ctx, + struct kshark_data_stream *stream, + struct kshark_config_doc *conf) +{ + switch (conf->format) { + case KS_CONFIG_JSON: + return kshark_stream_plugins_from_json(kshark_ctx, stream, + conf->conf_doc); + + default: + fprintf(stderr, "Document format %d not supported\n", + conf->format); + return false; + } } static bool kshark_model_to_json(struct kshark_trace_histo *histo, @@ -593,14 +1007,13 @@ static bool kshark_model_to_json(struct kshark_trace_histo *histo, /** * @brief Record the current configuration of the Vis. model into a * Configuration document. - * Load the configuration of the Vis. model from a Configuration - * document. * * @param histo: Input location for the Vis. model descriptor. * @param format: Input location for the kshark_config_doc instance. Currently * only Json format is supported. * - * @returns True on success, otherwise False. + * @returns kshark_config_doc instance on success, otherwise NULL. Use + * free() to free the object. */ struct kshark_config_doc * kshark_export_model(struct kshark_trace_histo *histo, @@ -688,17 +1101,21 @@ bool kshark_import_model(struct kshark_trace_histo *histo, } } -static bool kshark_event_filter_to_json(struct tep_handle *pevent, - struct tracecmd_filter_id *filter, +static bool kshark_event_filter_to_json(struct kshark_data_stream *stream, + enum kshark_filter_type filter_type, const char *filter_name, struct json_object *jobj) { - json_object *jfilter_data, *jevent, *jsystem, *jname; - struct tep_event *event; - int i, evt, *ids, nr_events; - char *temp; + json_object *jfilter_data, *jname; + struct kshark_hash_id *filter; + char *name_str; + int i, *ids; + + filter = kshark_get_filter(stream, filter_type); + if (!filter) + return false; - jevent = jsystem = jname = NULL; + jname = NULL; /* * If this Json document already contains a description of the filter, @@ -707,7 +1124,7 @@ static bool kshark_event_filter_to_json(struct tep_handle *pevent, json_del_if_exist(jobj, filter_name); /* Get the array of Ids to be fitered. */ - ids = tracecmd_filter_ids(filter); + ids = kshark_hash_ids(filter); if (!ids) return true; @@ -716,32 +1133,15 @@ static bool kshark_event_filter_to_json(struct tep_handle *pevent, if (!jfilter_data) goto fail; - nr_events = tep_get_events_count(pevent); for (i = 0; i < filter->count; ++i) { - for (evt = 0; evt < nr_events; ++evt) { - event = tep_get_event(pevent, evt); - if (event->id == ids[i]) { - jevent = json_object_new_object(); - - temp = event->system; - jsystem = json_object_new_string(temp); - - temp = event->name; - jname = json_object_new_string(temp); - - if (!jevent || !jsystem || !jname) - goto fail; - - json_object_object_add(jevent, "system", - jsystem); - - json_object_object_add(jevent, "name", - jname); - - json_object_array_add(jfilter_data, jevent); + name_str = kshark_event_from_id(stream->stream_id, + ids[i]); + if (name_str) { + jname = json_object_new_string(name_str); + if (!jname) + goto fail; - break; - } + json_object_array_add(jfilter_data, jname); } } @@ -755,8 +1155,6 @@ static bool kshark_event_filter_to_json(struct tep_handle *pevent, fail: fprintf(stderr, "Failed to allocate memory for json_object.\n"); json_object_put(jfilter_data); - json_object_put(jevent); - json_object_put(jsystem); json_object_put(jname); free(ids); @@ -767,22 +1165,22 @@ static bool kshark_event_filter_to_json(struct tep_handle *pevent, * @brief Record the current configuration of an Event Id filter into a * Configuration document. * - * @param pevent: Input location for the Page event. - * @param filter: Input location for an Id filter. + * @param stream: Input location for a Trace data stream pointer. + * @param filter_type: Identifier of the filter. * @param filter_name: The name of the filter to show up in the Json document. * @param conf: Input location for the kshark_config_doc instance. Currently * only Json format is supported. * * @returns True on success, otherwise False. */ -bool kshark_export_event_filter(struct tep_handle *pevent, - struct tracecmd_filter_id *filter, +bool kshark_export_event_filter(struct kshark_data_stream *stream, + enum kshark_filter_type filter_type, const char *filter_name, struct kshark_config_doc *conf) { switch (conf->format) { case KS_CONFIG_JSON: - return kshark_event_filter_to_json(pevent, filter, + return kshark_event_filter_to_json(stream, filter_type, filter_name, conf->conf_doc); @@ -793,15 +1191,19 @@ bool kshark_export_event_filter(struct tep_handle *pevent, } } -static int kshark_event_filter_from_json(struct tep_handle *pevent, - struct tracecmd_filter_id *filter, - const char *filter_name, - struct json_object *jobj) +static int kshark_event_filter_from_json(struct kshark_data_stream *stream, + enum kshark_filter_type filter_type, + const char *filter_name, + struct json_object *jobj) { - json_object *jfilter, *jevent, *jsystem, *jname; - const char *system_str, *name_str; - struct tep_event *event; - int i, length, count = 0; + int i, length, event_id, count = 0; + struct kshark_hash_id *filter; + json_object *jfilter, *jevent; + const char *name_str; + + filter = kshark_get_filter(stream, filter_type); + if (!filter) + return 0; /* * Use the name of the filter to find the array of events associated @@ -819,20 +1221,13 @@ static int kshark_event_filter_from_json(struct tep_handle *pevent, length = json_object_array_length(jfilter); for (i = 0; i < length; ++i) { jevent = json_object_array_get_idx(jfilter, i); - - if (!json_object_object_get_ex(jevent, "system", &jsystem) || - !json_object_object_get_ex(jevent, "name", &jname)) - goto fail; - - system_str = json_object_get_string(jsystem); - name_str = json_object_get_string(jname); - - event = tep_find_event_by_name(pevent, system_str, name_str); - if (!event) + name_str = json_object_get_string(jevent); + event_id = kshark_find_event_id(stream, name_str); + if (event_id < 0) continue; - tracecmd_filter_id_add(filter, event->id); - ++count; + kshark_hash_id_add(filter, event_id); + count++; } if (count != length) @@ -842,32 +1237,32 @@ static int kshark_event_filter_from_json(struct tep_handle *pevent, fail: fprintf(stderr, "Failed to load event filter from json_object.\n"); - tracecmd_filter_id_clear(filter); + kshark_hash_id_clear(filter); return 0; } /** * @brief Load from Configuration document the configuration of an Event Id filter. * - * @param pevent: Input location for the Page event. - * @param filter: Input location for an Id filter. + * @param stream: Input location for a Trace data stream pointer. + * @param filter_type: Identifier of the filter. * @param filter_name: The name of the filter as showing up in the Config. * document. * @param conf: Input location for the kshark_config_doc instance. Currently * only Json format is supported. * * @returns The total number of events added to the filter. If not all events - * listed in the input configuration have been added successfully, - * the returned number is negative. + * listed in the input configuration have been added successfully, + * the returned number is negative. */ -int kshark_import_event_filter(struct tep_handle *pevent, - struct tracecmd_filter_id *filter, +int kshark_import_event_filter(struct kshark_data_stream *stream, + enum kshark_filter_type filter_type, const char *filter_name, struct kshark_config_doc *conf) { switch (conf->format) { case KS_CONFIG_JSON: - return kshark_event_filter_from_json(pevent, filter, + return kshark_event_filter_from_json(stream, filter_type, filter_name, conf->conf_doc); @@ -878,7 +1273,7 @@ int kshark_import_event_filter(struct tep_handle *pevent, } } -static bool kshark_filter_array_to_json(struct tracecmd_filter_id *filter, +static bool kshark_filter_array_to_json(struct kshark_hash_id *filter, const char *filter_name, struct json_object *jobj) { @@ -892,7 +1287,7 @@ static bool kshark_filter_array_to_json(struct tracecmd_filter_id *filter, json_del_if_exist(jobj, filter_name); /* Get the array of Ids to be filtered. */ - ids = tracecmd_filter_ids(filter); + ids = kshark_hash_ids(filter); if (!ids) return true; @@ -936,7 +1331,7 @@ static bool kshark_filter_array_to_json(struct tracecmd_filter_id *filter, * * @returns True on success, otherwise False. */ -bool kshark_export_filter_array(struct tracecmd_filter_id *filter, +bool kshark_export_filter_array(struct kshark_hash_id *filter, const char *filter_name, struct kshark_config_doc *conf) { @@ -952,7 +1347,7 @@ bool kshark_export_filter_array(struct tracecmd_filter_id *filter, } } -static bool kshark_filter_array_from_json(struct tracecmd_filter_id *filter, +static bool kshark_filter_array_from_json(struct kshark_hash_id *filter, const char *filter_name, struct json_object *jobj) { @@ -978,7 +1373,7 @@ static bool kshark_filter_array_from_json(struct tracecmd_filter_id *filter, if (!jpid) goto fail; - tracecmd_filter_id_add(filter, json_object_get_int(jpid)); + kshark_hash_id_add(filter, json_object_get_int(jpid)); } return true; @@ -1002,7 +1397,7 @@ static bool kshark_filter_array_from_json(struct tracecmd_filter_id *filter, * document contains no data for this particular filter or in a case * of an error, the function returns False. */ -bool kshark_import_filter_array(struct tracecmd_filter_id *filter, +bool kshark_import_filter_array(struct kshark_hash_id *filter, const char *filter_name, struct kshark_config_doc *conf) { @@ -1018,16 +1413,14 @@ bool kshark_import_filter_array(struct tracecmd_filter_id *filter, } } -static bool kshark_adv_filters_to_json(struct kshark_context *kshark_ctx, +static bool kshark_adv_filters_to_json(struct kshark_data_stream *stream, struct json_object *jobj) { - struct tep_event_filter *adv_filter = kshark_ctx->advanced_event_filter; - json_object *jfilter_data, *jevent, *jsystem, *jname, *jfilter; - struct tep_event **events; - char *str; - int i; + json_object *jfilter_data, *jevent, *jname, *jfilter; + char *filter_str; + int *events, i; - jevent = jsystem = jname = jfilter = NULL; + jevent = jname = jfilter = NULL; /* * If this Json document already contains a description of the model, @@ -1035,8 +1428,7 @@ static bool kshark_adv_filters_to_json(struct kshark_context *kshark_ctx, */ json_del_if_exist(jobj, KS_ADV_EVENT_FILTER_NAME); - if (!kshark_ctx->advanced_event_filter || - !kshark_ctx->advanced_event_filter->filters) + if (!kshark_tep_filter_is_set(stream)) return true; /* Create a Json array and fill the Id values into it. */ @@ -1044,24 +1436,23 @@ static bool kshark_adv_filters_to_json(struct kshark_context *kshark_ctx, if (!jfilter_data) goto fail; - events = tep_list_events(kshark_ctx->pevent, TEP_EVENT_SORT_SYSTEM); + events = kshark_get_all_event_ids(stream); if (!events) return false; - for (i = 0; events[i]; i++) { - str = tep_filter_make_string(adv_filter, - events[i]->id); - if (!str) + for (i = 0; i < stream->n_events; ++i) { + filter_str = kshark_tep_filter_make_string(stream, events[i]); + if (!filter_str) continue; jevent = json_object_new_object(); - jsystem = json_object_new_string(events[i]->system); - jname = json_object_new_string(events[i]->name); - jfilter = json_object_new_string(str); - if (!jevent || !jsystem || !jname || !jfilter) + jname = json_object_new_string(kshark_event_from_id(stream->stream_id, + events[i])); + + jfilter = json_object_new_string(filter_str); + if (!jevent || !jname || !jfilter) goto fail; - json_object_object_add(jevent, "system", jsystem); json_object_object_add(jevent, "name", jname); json_object_object_add(jevent, "condition", jfilter); @@ -1077,7 +1468,6 @@ static bool kshark_adv_filters_to_json(struct kshark_context *kshark_ctx, fprintf(stderr, "Failed to allocate memory for json_object.\n"); json_object_put(jfilter_data); json_object_put(jevent); - json_object_put(jsystem); json_object_put(jname); json_object_put(jfilter); @@ -1089,15 +1479,27 @@ static bool kshark_adv_filters_to_json(struct kshark_context *kshark_ctx, * Configuration document. * * @param kshark_ctx: Input location for session context pointer. + * @param sd: Data stream identifier. * @param conf: Input location for the kshark_config_doc instance. Currently * only Json format is supported. If NULL, a new Adv. Filter * Configuration document will be created. * * @returns True on success, otherwise False. */ -bool kshark_export_adv_filters(struct kshark_context *kshark_ctx, +bool kshark_export_adv_filters(struct kshark_context *kshark_ctx, int sd, struct kshark_config_doc **conf) { + struct kshark_data_stream *stream = + kshark_get_data_stream(kshark_ctx, sd); + + if (!stream) + return false; + + if (!kshark_is_tep(stream)) { + /* Nothing to export. */ + return true; + } + if (!*conf) *conf = kshark_filter_config_new(KS_CONFIG_JSON); @@ -1106,7 +1508,7 @@ bool kshark_export_adv_filters(struct kshark_context *kshark_ctx, switch ((*conf)->format) { case KS_CONFIG_JSON: - return kshark_adv_filters_to_json(kshark_ctx, + return kshark_adv_filters_to_json(stream, (*conf)->conf_doc); default: @@ -1116,11 +1518,10 @@ bool kshark_export_adv_filters(struct kshark_context *kshark_ctx, } } -static bool kshark_adv_filters_from_json(struct kshark_context *kshark_ctx, +static bool kshark_adv_filters_from_json(struct kshark_data_stream *stream, struct json_object *jobj) { - struct tep_event_filter *adv_filter = kshark_ctx->advanced_event_filter; - json_object *jfilter, *jsystem, *jname, *jcond; + json_object *jfilter, *jname, *jcond; int i, length, n, ret = 0; char *filter_str = NULL; @@ -1142,13 +1543,11 @@ static bool kshark_adv_filters_from_json(struct kshark_context *kshark_ctx, for (i = 0; i < length; ++i) { jfilter = json_object_array_get_idx(jfilter, i); - if (!json_object_object_get_ex(jfilter, "system", &jsystem) || - !json_object_object_get_ex(jfilter, "name", &jname) || + if (!json_object_object_get_ex(jfilter, "name", &jname) || !json_object_object_get_ex(jfilter, "condition", &jcond)) goto fail; - n = asprintf(&filter_str, "%s/%s:%s", - json_object_get_string(jsystem), + n = asprintf(&filter_str, "%s:%s", json_object_get_string(jname), json_object_get_string(jcond)); @@ -1157,8 +1556,7 @@ static bool kshark_adv_filters_from_json(struct kshark_context *kshark_ctx, goto fail; } - ret = tep_filter_add_filter_str(adv_filter, - filter_str); + ret = kshark_tep_add_filter_str(stream, filter_str); if (ret < 0) goto fail; } @@ -1167,16 +1565,6 @@ static bool kshark_adv_filters_from_json(struct kshark_context *kshark_ctx, fail: fprintf(stderr, "Failed to laod Advanced filters.\n"); - if (ret < 0) { - char error_str[200]; - int error_status = - tep_strerror(kshark_ctx->pevent, ret, error_str, - sizeof(error_str)); - - if (error_status == 0) - fprintf(stderr, "filter failed due to: %s\n", - error_str); - } free(filter_str); return false; @@ -1187,6 +1575,7 @@ static bool kshark_adv_filters_from_json(struct kshark_context *kshark_ctx, * filter. * * @param kshark_ctx: Input location for session context pointer. + * @param sd: Data stream identifier. * @param conf: Input location for the kshark_config_doc instance. Currently * only Json format is supported. * @@ -1194,12 +1583,18 @@ static bool kshark_adv_filters_from_json(struct kshark_context *kshark_ctx, * document contains no data for the Adv. filter or in a case of * an error, the function returns False. */ -bool kshark_import_adv_filters(struct kshark_context *kshark_ctx, +bool kshark_import_adv_filters(struct kshark_context *kshark_ctx, int sd, struct kshark_config_doc *conf) { + struct kshark_data_stream *stream = + kshark_get_data_stream(kshark_ctx, sd); + + if (!stream) + return false; + switch (conf->format) { case KS_CONFIG_JSON: - return kshark_adv_filters_from_json(kshark_ctx, + return kshark_adv_filters_from_json(stream, conf->conf_doc); default: @@ -1212,8 +1607,10 @@ bool kshark_import_adv_filters(struct kshark_context *kshark_ctx, static bool kshark_user_mask_to_json(struct kshark_context *kshark_ctx, struct json_object *jobj) { - uint8_t mask = kshark_ctx->filter_mask; json_object *jmask; + uint8_t mask; + + mask = kshark_ctx->filter_mask; jmask = json_object_new_int((int) mask); if (!jmask) @@ -1246,8 +1643,7 @@ bool kshark_export_user_mask(struct kshark_context *kshark_ctx, switch ((*conf)->format) { case KS_CONFIG_JSON: - return kshark_user_mask_to_json(kshark_ctx, - (*conf)->conf_doc); + return kshark_user_mask_to_json(kshark_ctx, (*conf)->conf_doc); default: fprintf(stderr, "Document format %d not supported\n", @@ -1296,8 +1692,7 @@ bool kshark_import_user_mask(struct kshark_context *kshark_ctx, { switch (conf->format) { case KS_CONFIG_JSON: - return kshark_user_mask_from_json(kshark_ctx, - conf->conf_doc); + return kshark_user_mask_from_json(kshark_ctx, conf->conf_doc); default: fprintf(stderr, "Document format %d not supported\n", @@ -1306,11 +1701,133 @@ bool kshark_import_user_mask(struct kshark_context *kshark_ctx, } } +static bool kshark_calib_array_from_json(struct kshark_context *kshark_ctx, + int sd, struct json_object *jobj) +{ + json_object *jcalib_argv, *jcalib; + int64_t *calib_argv = NULL; + int i, calib_length; + + if (!json_object_object_get_ex(jobj, "calib. array", &jcalib_argv) || + json_object_get_type(jcalib_argv) != json_type_array) + return false; + + calib_length = json_object_array_length(jcalib_argv); + if (!calib_length) + return false; + + calib_argv = calloc(calib_length, sizeof(*calib_argv)); + for (i = 0; i < calib_length; ++i) { + jcalib = json_object_array_get_idx(jcalib_argv, i); + calib_argv[i] = json_object_get_int64(jcalib); + } + + kshark_ctx->stream[sd]->calib = kshark_offset_calib; + kshark_ctx->stream[sd]->calib_array = calib_argv; + kshark_ctx->stream[sd]->calib_array_size = calib_length; + + return true; +} + +/** + * @brief Load from Configuration document the value of the time calibration + * constants into a Configuration document. + * + * @param kshark_ctx: Input location for session context pointer. + * @param sd: Data stream identifier. + * @param conf: Input location for the kshark_config_doc instance. Currently + * only Json format is supported. If NULL, a new Configuration + * document will be created. + * + * @returns True on success, otherwise False. + */ +bool kshark_import_calib_array(struct kshark_context *kshark_ctx, int sd, + struct kshark_config_doc *conf) +{ + switch (conf->format) { + case KS_CONFIG_JSON: + return kshark_calib_array_from_json(kshark_ctx, sd, conf->conf_doc); + + default: + fprintf(stderr, "Document format %d not supported\n", + conf->format); + return false; + } +} + +static bool kshark_calib_array_to_json(struct kshark_context *kshark_ctx, + int sd, struct json_object *jobj) +{ + json_object *jval = NULL, *jcalib = NULL; + struct kshark_data_stream *stream; + + stream = kshark_get_data_stream(kshark_ctx, sd); + if (!stream || !stream->calib_array_size) + goto fail; + + jcalib = json_object_new_array(); + if (!jcalib) + goto fail; + + for (size_t i = 0; i < stream->calib_array_size; ++i) { + jval = json_object_new_int64(stream->calib_array[i]); + if (!jval) + goto fail; + + json_object_array_add(jcalib, jval); + } + + /* Add the mask to the filter config document. */ + json_object_object_add(jobj, "calib. array", jcalib); + + return true; + + fail: + json_object_put(jval); + json_object_put(jcalib); + + return false; +} + +/** + * @brief Record the current values of the time calibration constants into + * a Configuration document. + * + * @param kshark_ctx: Input location for session context pointer. + * @param sd: Data stream identifier. + * @param conf: Input location for the kshark_config_doc instance. Currently + * only Json format is supported. If NULL, a new Configuration + * document will be created. + * + * @returns True on success, otherwise False. + */ +bool kshark_export_calib_array(struct kshark_context *kshark_ctx, int sd, + struct kshark_config_doc **conf) +{ + if (!*conf) + *conf = kshark_stream_config_new(KS_CONFIG_JSON); + + if (!*conf) + return false; + + switch ((*conf)->format) { + case KS_CONFIG_JSON: + return kshark_calib_array_to_json(kshark_ctx, sd, + (*conf)->conf_doc); + + default: + fprintf(stderr, "Document format %d not supported\n", + (*conf)->format); + return false; + } +} + /** * @brief Record the current configuration of "show task" and "hide task" * filters into a Json document. * * @param kshark_ctx: Input location for session context pointer. + * @param sd: Data stream identifier. * @param conf: Input location for the kshark_config_doc instance. Currently * only Json format is supported. If NULL, a new Filter * Configuration document will be created. @@ -1318,10 +1835,15 @@ bool kshark_import_user_mask(struct kshark_context *kshark_ctx, * @returns True, if a filter has been recorded. If both filters contain * no Id values or in a case of an error, the function returns False. */ -bool kshark_export_all_event_filters(struct kshark_context *kshark_ctx, +bool kshark_export_all_event_filters(struct kshark_context *kshark_ctx, int sd, struct kshark_config_doc **conf) { - bool ret = true; + struct kshark_data_stream *stream = + kshark_get_data_stream(kshark_ctx, sd); + bool ret; + + if (!stream) + return false; if (!*conf) *conf = kshark_filter_config_new(KS_CONFIG_JSON); @@ -1330,15 +1852,16 @@ bool kshark_export_all_event_filters(struct kshark_context *kshark_ctx, return false; /* Save a filter only if it contains Id values. */ - if (kshark_this_filter_is_set(kshark_ctx->show_event_filter)) - ret &= kshark_export_event_filter(kshark_ctx->pevent, - kshark_ctx->show_event_filter, + ret = true; + if (kshark_this_filter_is_set(stream->show_event_filter)) + ret &= kshark_export_event_filter(stream, + KS_SHOW_EVENT_FILTER, KS_SHOW_EVENT_FILTER_NAME, *conf); - if (kshark_this_filter_is_set(kshark_ctx->hide_event_filter)) - ret &= kshark_export_event_filter(kshark_ctx->pevent, - kshark_ctx->hide_event_filter, + if (kshark_this_filter_is_set(stream->hide_event_filter)) + ret &= kshark_export_event_filter(stream, + KS_HIDE_EVENT_FILTER, KS_HIDE_EVENT_FILTER_NAME, *conf); @@ -1350,6 +1873,7 @@ bool kshark_export_all_event_filters(struct kshark_context *kshark_ctx, * filters into a Configuration document. * * @param kshark_ctx: Input location for session context pointer. + * @param sd: Data stream identifier. * @param conf: Input location for the kshark_config_doc instance. Currently * only Json format is supported. If NULL, a new Filter * Configuration document will be created. @@ -1357,10 +1881,15 @@ bool kshark_export_all_event_filters(struct kshark_context *kshark_ctx, * @returns True, if a filter has been recorded. If both filters contain * no Id values or in a case of an error, the function returns False. */ -bool kshark_export_all_task_filters(struct kshark_context *kshark_ctx, +bool kshark_export_all_task_filters(struct kshark_context *kshark_ctx, int sd, struct kshark_config_doc **conf) { - bool ret = true; + struct kshark_data_stream *stream = + kshark_get_data_stream(kshark_ctx, sd); + bool ret; + + if (!stream) + return false; if (!*conf) *conf = kshark_filter_config_new(KS_CONFIG_JSON); @@ -1369,25 +1898,26 @@ bool kshark_export_all_task_filters(struct kshark_context *kshark_ctx, return false; /* Save a filter only if it contains Id values. */ - if (kshark_this_filter_is_set(kshark_ctx->show_task_filter)) - ret &= kshark_export_filter_array(kshark_ctx->show_task_filter, + ret = true; + if (kshark_this_filter_is_set(stream->show_task_filter)) + ret &= kshark_export_filter_array(stream->show_task_filter, KS_SHOW_TASK_FILTER_NAME, *conf); - if (kshark_this_filter_is_set(kshark_ctx->hide_task_filter)) - ret &= kshark_export_filter_array(kshark_ctx->hide_task_filter, + if (kshark_this_filter_is_set(stream->hide_task_filter)) + ret &= kshark_export_filter_array(stream->hide_task_filter, KS_HIDE_TASK_FILTER_NAME, *conf); return ret; } - /** * @brief Record the current configuration of "show cpu" and "hide cpu" * filters into a Configuration document. * * @param kshark_ctx: Input location for session context pointer. + * @param sd: Data stream identifier. * @param conf: Input location for the kshark_config_doc instance. Currently * only Json format is supported. If NULL, a new Filter * Configuration document will be created. @@ -1395,10 +1925,15 @@ bool kshark_export_all_task_filters(struct kshark_context *kshark_ctx, * @returns True, if a filter has been recorded. If both filters contain * no Id values or in a case of an error, the function returns False. */ -bool kshark_export_all_cpu_filters(struct kshark_context *kshark_ctx, +bool kshark_export_all_cpu_filters(struct kshark_context *kshark_ctx, int sd, struct kshark_config_doc **conf) { - bool ret = true; + struct kshark_data_stream *stream = + kshark_get_data_stream(kshark_ctx, sd); + bool ret; + + if (!stream) + return false; if (!*conf) *conf = kshark_filter_config_new(KS_CONFIG_JSON); @@ -1407,13 +1942,14 @@ bool kshark_export_all_cpu_filters(struct kshark_context *kshark_ctx, return false; /* Save a filter only if it contains Id values. */ - if (kshark_this_filter_is_set(kshark_ctx->show_cpu_filter)) - ret &= kshark_export_filter_array(kshark_ctx->show_cpu_filter, + ret = true; + if (kshark_this_filter_is_set(stream->show_cpu_filter)) + ret &= kshark_export_filter_array(stream->show_cpu_filter, KS_SHOW_CPU_FILTER_NAME, *conf); - if (kshark_this_filter_is_set(kshark_ctx->hide_cpu_filter)) - ret &= kshark_export_filter_array(kshark_ctx->hide_cpu_filter, + if (kshark_this_filter_is_set(stream->hide_cpu_filter)) + ret &= kshark_export_filter_array(stream->hide_cpu_filter, KS_HIDE_CPU_FILTER_NAME, *conf); @@ -1425,6 +1961,7 @@ bool kshark_export_all_cpu_filters(struct kshark_context *kshark_ctx, * and "hide event" filters. * * @param kshark_ctx: Input location for session context pointer. + * @param sd: Data stream identifier. * @param conf: Input location for the kshark_config_doc instance. Currently * only Json format is supported. * @@ -1432,18 +1969,23 @@ bool kshark_export_all_cpu_filters(struct kshark_context *kshark_ctx, * document contains no data for any event filter or in a case * of an error, the function returns False. */ -bool kshark_import_all_event_filters(struct kshark_context *kshark_ctx, +bool kshark_import_all_event_filters(struct kshark_context *kshark_ctx, int sd, struct kshark_config_doc *conf) { + struct kshark_data_stream *stream = + kshark_get_data_stream(kshark_ctx, sd); bool ret = false; - ret |= kshark_import_event_filter(kshark_ctx->pevent, - kshark_ctx->hide_event_filter, + if (!stream) + return false; + + ret |= kshark_import_event_filter(stream, + KS_HIDE_EVENT_FILTER, KS_HIDE_EVENT_FILTER_NAME, conf); - ret |= kshark_import_event_filter(kshark_ctx->pevent, - kshark_ctx->show_event_filter, + ret |= kshark_import_event_filter(stream, + KS_SHOW_EVENT_FILTER, KS_SHOW_EVENT_FILTER_NAME, conf); @@ -1455,6 +1997,7 @@ bool kshark_import_all_event_filters(struct kshark_context *kshark_ctx, * and "hide task" filters. * * @param kshark_ctx: Input location for session context pointer. + * @param sd: Data stream identifier. * @param conf: Input location for the kshark_config_doc instance. Currently * only Json format is supported. * @@ -1462,16 +2005,21 @@ bool kshark_import_all_event_filters(struct kshark_context *kshark_ctx, * document contains no data for any task filter or in a case of an * error, the function returns False. */ -bool kshark_import_all_task_filters(struct kshark_context *kshark_ctx, +bool kshark_import_all_task_filters(struct kshark_context *kshark_ctx, int sd, struct kshark_config_doc *conf) { + struct kshark_data_stream *stream = + kshark_get_data_stream(kshark_ctx, sd); bool ret = false; - ret |= kshark_import_filter_array(kshark_ctx->hide_task_filter, + if (!stream) + return false; + + ret |= kshark_import_filter_array(stream->hide_task_filter, KS_HIDE_TASK_FILTER_NAME, conf); - ret |= kshark_import_filter_array(kshark_ctx->show_task_filter, + ret |= kshark_import_filter_array(stream->show_task_filter, KS_SHOW_TASK_FILTER_NAME, conf); @@ -1483,6 +2031,7 @@ bool kshark_import_all_task_filters(struct kshark_context *kshark_ctx, * and "hide cpu" filters. * * @param kshark_ctx: Input location for session context pointer. + * @param sd: Data stream identifier. * @param conf: Input location for the kshark_config_doc instance. Currently * only Json format is supported. * @@ -1490,16 +2039,21 @@ bool kshark_import_all_task_filters(struct kshark_context *kshark_ctx, * document contains no data for any cpu filter or in a case of an * error, the function returns False. */ -bool kshark_import_all_cpu_filters(struct kshark_context *kshark_ctx, +bool kshark_import_all_cpu_filters(struct kshark_context *kshark_ctx, int sd, struct kshark_config_doc *conf) { + struct kshark_data_stream *stream = + kshark_get_data_stream(kshark_ctx, sd); bool ret = false; - ret |= kshark_import_filter_array(kshark_ctx->hide_cpu_filter, + if (!stream) + return false; + + ret |= kshark_import_filter_array(stream->hide_cpu_filter, KS_HIDE_CPU_FILTER_NAME, conf); - ret |= kshark_import_filter_array(kshark_ctx->show_cpu_filter, + ret |= kshark_import_filter_array(stream->show_cpu_filter, KS_SHOW_CPU_FILTER_NAME, conf); @@ -1511,6 +2065,7 @@ bool kshark_import_all_cpu_filters(struct kshark_context *kshark_ctx, * configuration of all filters. * * @param kshark_ctx: Input location for session context pointer. + * @param sd: Data stream identifier. * @param format: Input location for the kshark_config_doc instance. Currently * only Json format is supported. * @@ -1518,7 +2073,7 @@ bool kshark_import_all_cpu_filters(struct kshark_context *kshark_ctx, * kshark_free_config_doc() to free the object. */ struct kshark_config_doc * -kshark_export_all_filters(struct kshark_context *kshark_ctx, +kshark_export_all_filters(struct kshark_context *kshark_ctx, int sd, enum kshark_config_formats format) { /* Create a new Configuration document. */ @@ -1527,11 +2082,11 @@ kshark_export_all_filters(struct kshark_context *kshark_ctx, /* Save a filter only if it contains Id values. */ if (!conf || - !kshark_export_all_event_filters(kshark_ctx, &conf) || - !kshark_export_all_task_filters(kshark_ctx, &conf) || - !kshark_export_all_cpu_filters(kshark_ctx, &conf) || + !kshark_export_all_event_filters(kshark_ctx, sd, &conf) || + !kshark_export_all_task_filters(kshark_ctx, sd, &conf) || + !kshark_export_all_cpu_filters(kshark_ctx, sd, &conf) || !kshark_export_user_mask(kshark_ctx, &conf) || - !kshark_export_adv_filters(kshark_ctx, &conf)) { + !kshark_export_adv_filters(kshark_ctx, sd, &conf)) { kshark_free_config_doc(conf); return NULL; } @@ -1543,6 +2098,7 @@ kshark_export_all_filters(struct kshark_context *kshark_ctx, * @brief Load from a Configuration document the configuration of all filters. * * @param kshark_ctx: Input location for session context pointer. + * @param sd: Data stream identifier. * @param conf: Input location for the kshark_config_doc instance. Currently * only Json format is supported. * @@ -1550,19 +2106,289 @@ kshark_export_all_filters(struct kshark_context *kshark_ctx, * document contains no data for any filter or in a case of an error, * the function returns False. */ -bool kshark_import_all_filters(struct kshark_context *kshark_ctx, +bool kshark_import_all_filters(struct kshark_context *kshark_ctx, int sd, struct kshark_config_doc *conf) { bool ret; - ret = kshark_import_all_task_filters(kshark_ctx, conf); - ret |= kshark_import_all_cpu_filters(kshark_ctx, conf); - ret |= kshark_import_all_event_filters(kshark_ctx, conf); + + ret = kshark_import_all_task_filters(kshark_ctx, sd, conf); + ret |= kshark_import_all_cpu_filters(kshark_ctx, sd, conf); + ret |= kshark_import_all_event_filters(kshark_ctx, sd, conf); ret |= kshark_import_user_mask(kshark_ctx, conf); - ret |= kshark_import_adv_filters(kshark_ctx, conf); + ret |= kshark_import_adv_filters(kshark_ctx, sd, conf); return ret; } +/** + * @brief Create a Data Stream Configuration document. + * + * @param kshark_ctx: Input location for session context pointer. + * @param sd: Data stream identifier. + * @param format: Input location for the kshark_config_doc instance. Currently + * only Json format is supported. + * + * @returns kshark_config_doc instance on success, otherwise NULL. Use + * kshark_free_config_doc() to free the object. + */ +struct kshark_config_doc * +kshark_export_dstream(struct kshark_context *kshark_ctx, int sd, + enum kshark_config_formats format) +{ + struct kshark_config_doc *file_conf, *filter_conf, *sd_conf, *plg_conf; + struct kshark_config_doc *dstream_conf; + struct kshark_data_stream *stream; + + stream = kshark_get_data_stream(kshark_ctx, sd); + if (!stream) + return NULL; + + /* Create new Configuration documents. */ + dstream_conf = kshark_stream_config_new(format); + sd_conf = kshark_config_alloc(KS_CONFIG_JSON); + + sd_conf->conf_doc = json_object_new_int(sd); + + filter_conf = kshark_export_all_filters(kshark_ctx, sd, format); + + if (kshark_is_tep(stream) && kshark_tep_is_top_stream(stream)) + file_conf = kshark_export_trace_file(stream->file, + TOP_BUFF_NAME, + format); + else + file_conf = kshark_export_trace_file(stream->file, + stream->name, + format); + + plg_conf = kshark_export_stream_plugins(stream, format); + + if (!dstream_conf || + !sd_conf || + !filter_conf || + !file_conf || + !plg_conf) + goto fail; + + kshark_config_doc_add(dstream_conf, "stream id", sd_conf); + kshark_config_doc_add(dstream_conf, "data", file_conf); + kshark_config_doc_add(dstream_conf, "filters", filter_conf); + kshark_config_doc_add(dstream_conf, "plugins", plg_conf); + + if (stream->calib_array_size) + kshark_export_calib_array(kshark_ctx, sd, &dstream_conf); + + return dstream_conf; + + fail: + kshark_free_config_doc(dstream_conf); + kshark_free_config_doc(filter_conf); + kshark_free_config_doc(file_conf); + kshark_free_config_doc(plg_conf); + kshark_free_config_doc(sd_conf); + + return NULL; +} + +/** + * @brief Load Data Stream from a Configuration document. + * + * @param kshark_ctx: Input location for session context pointer. + * @param conf: Input location for the kshark_config_doc instance. Currently + * only Json format is supported. + * + * @returns The Stream Id on the loaded Data Stream on success, otherwise a + * negative error code. + */ +int kshark_import_dstream(struct kshark_context *kshark_ctx, + struct kshark_config_doc *conf) +{ + struct kshark_config_doc *file_conf, *filter_conf, *plg_conf; + struct kshark_data_stream *stream; + bool ret = false; + int sd = -EFAULT; + + if (!kshark_type_check(conf, "kshark.config.stream")) + return sd; + + file_conf = kshark_config_alloc(KS_CONFIG_JSON); + filter_conf = kshark_config_alloc(KS_CONFIG_JSON); + plg_conf = kshark_config_alloc(KS_CONFIG_JSON); + if (!file_conf || !filter_conf || !plg_conf) { + fprintf(stderr, + "Failed to allocate memory for Json document.\n"); + goto free; + } + + if (kshark_config_doc_get(conf, "data", file_conf) && + kshark_config_doc_get(conf, "filters", filter_conf) && + kshark_config_doc_get(conf, "plugins", plg_conf)) { + sd = kshark_import_trace_file(kshark_ctx, file_conf); + if (sd < 0) { + fprintf(stderr, + "Failed to import data file form Json document.\n"); + goto free; + } + + stream = kshark_ctx->stream[sd]; + kshark_import_calib_array(kshark_ctx, sd, conf); + ret = kshark_import_all_filters(kshark_ctx, sd, + filter_conf); + if (!ret) { + fprintf(stderr, + "Failed to import filters form Json document.\n"); + kshark_close(kshark_ctx, sd); + sd = -EFAULT; + goto free; + } + + ret = kshark_import_stream_plugins(kshark_ctx, stream, plg_conf); + + if (!ret) { + fprintf(stderr, + "Failed to import stream plugins form Json document.\n"); + kshark_close(kshark_ctx, sd); + sd = -EFAULT; + goto free; + } + } + + free: + /* Free only the kshark_config_doc objects. */ + free(file_conf); + free(filter_conf); + free(plg_conf); + + return sd; +} + +static bool +kshark_export_all_dstreams_to_json(struct kshark_context *kshark_ctx, + struct json_object *jobj) +{ + int *stream_ids = kshark_all_streams(kshark_ctx); + struct kshark_config_doc *dstream_conf; + struct json_object *jall_streams; + + json_del_if_exist(jobj, KS_DSTREAMS_NAME); + jall_streams = json_object_new_array(); + + for (int i = 0; i < kshark_ctx->n_streams; ++i) { + dstream_conf = kshark_export_dstream(kshark_ctx, stream_ids[i], + KS_CONFIG_JSON); + if (!dstream_conf) + goto fail; + + json_object_array_put_idx(jall_streams, i, dstream_conf->conf_doc); + + /* Free only the kshark_config_doc object. */ + free(dstream_conf); + } + + free(stream_ids); + + json_object_object_add(jobj, KS_DSTREAMS_NAME, jall_streams); + + return true; + + fail: + json_object_put(jall_streams); + free(stream_ids); + + return false; +} + +/** + * @brief Record the current configuration for all Data Streams into a Json + * document. + * + * @param kshark_ctx: Input location for session context pointer. + * @param conf: Input location for the kshark_config_doc instance. Currently + * only Json format is supported. If NULL, a new Configuration + * document will be created. + * + * @returns True on success, otherwise False. + */ +bool kshark_export_all_dstreams(struct kshark_context *kshark_ctx, + struct kshark_config_doc **conf) +{ + if (!*conf) + *conf = kshark_session_config_new(KS_CONFIG_JSON); + + if (!*conf) + return false; + + switch ((*conf)->format) { + case KS_CONFIG_JSON: + return kshark_export_all_dstreams_to_json(kshark_ctx, + (*conf)->conf_doc); + + default: + fprintf(stderr, "Document format %d not supported\n", + (*conf)->format); + return false; + } +} + +static ssize_t +kshark_import_all_dstreams_from_json(struct kshark_context *kshark_ctx, + struct json_object *jobj, + struct kshark_entry ***data_rows) +{ + struct kshark_config_doc dstream_conf; + json_object *jall_streams, *jstream; + int sd, i, length; + + if (!json_object_object_get_ex(jobj, KS_DSTREAMS_NAME, &jall_streams) || + json_object_get_type(jall_streams) != json_type_array) + return -EFAULT; + + length = json_object_array_length(jall_streams); + if (!length) + return -EFAULT; + + dstream_conf.format = KS_CONFIG_JSON; + for (i = 0; i < length; ++i) { + jstream = json_object_array_get_idx(jall_streams, i); + dstream_conf.conf_doc = jstream; + sd = kshark_import_dstream(kshark_ctx, &dstream_conf); + + if (sd < 0) + return -EFAULT; + } + + return kshark_load_all_entries(kshark_ctx, data_rows); +} + +/** + * @brief Load all Data Streams from a Configuration document. + * + * @param kshark_ctx: Input location for session context pointer. + * @param conf: Input location for the kshark_config_doc instance. Currently + * only Json format is supported. + * @param data_rows: Output location for the trace data. The user is + * responsible for freeing the elements of the outputted + * array. + * + * @returns The size of the outputted data in the case of success, or a + * negative error code on failure. + */ +ssize_t kshark_import_all_dstreams(struct kshark_context *kshark_ctx, + struct kshark_config_doc *conf, + struct kshark_entry ***data_rows) +{ + switch (conf->format) { + case KS_CONFIG_JSON: + return kshark_import_all_dstreams_from_json(kshark_ctx, + conf->conf_doc, + data_rows); + + default: + fprintf(stderr, "Document format %d not supported\n", + conf->format); + return -EFAULT; + } +} + static bool kshark_save_json_file(const char *file_name, struct json_object *jobj) { diff --git a/src/libkshark.h b/src/libkshark.h index dce3dd2..dd4f2b7 100644 --- a/src/libkshark.h +++ b/src/libkshark.h @@ -895,6 +895,12 @@ enum kshark_config_formats { */ #define KS_DATA_SOURCE_NAME "trace data" +/** + * Field name for the Configuration document describing all currently loaded + * data streams. + */ +#define KS_DSTREAMS_NAME "data streams" + struct kshark_config_doc * kshark_config_alloc(enum kshark_config_formats); @@ -906,9 +912,15 @@ void kshark_free_config_doc(struct kshark_config_doc *conf); struct kshark_config_doc * kshark_record_config_new(enum kshark_config_formats); +struct kshark_config_doc * +kshark_stream_config_new(enum kshark_config_formats); + struct kshark_config_doc * kshark_filter_config_new(enum kshark_config_formats); +struct kshark_config_doc * +kshark_session_config_new(enum kshark_config_formats format); + struct kshark_config_doc *kshark_string_config_alloc(void); bool kshark_type_check(struct kshark_config_doc *conf, const char *type); @@ -924,24 +936,42 @@ bool kshark_config_doc_get(struct kshark_config_doc *conf, struct kshark_trace_histo; struct kshark_config_doc * -kshark_export_trace_file(const char *file, +kshark_export_trace_file(const char *file, const char *name, enum kshark_config_formats format); -const char *kshark_import_trace_file(struct kshark_context *kshark_ctx, - struct kshark_config_doc *conf); +int kshark_import_trace_file(struct kshark_context *kshark_ctx, + struct kshark_config_doc *conf); + +struct kshark_config_doc * +kshark_export_plugin_file(struct kshark_plugin_list *plugin, + enum kshark_config_formats format); + +struct kshark_config_doc * +kshark_export_all_plugins(struct kshark_context *kshark_ctx, + enum kshark_config_formats format); + +bool kshark_import_all_plugins(struct kshark_context *kshark_ctx, + struct kshark_config_doc *conf); + +struct kshark_config_doc * +kshark_export_stream_plugins(struct kshark_data_stream *stream, + enum kshark_config_formats format); + +bool kshark_import_stream_plugins(struct kshark_context *kshark_ctx, + struct kshark_data_stream *stream, + struct kshark_config_doc *conf); struct kshark_config_doc * kshark_export_model(struct kshark_trace_histo *histo, enum kshark_config_formats format); - bool kshark_import_model(struct kshark_trace_histo *histo, struct kshark_config_doc *conf); -bool kshark_export_adv_filters(struct kshark_context *kshark_ctx, +bool kshark_export_adv_filters(struct kshark_context *kshark_ctx, int sd, struct kshark_config_doc **conf); -bool kshark_import_adv_filters(struct kshark_context *kshark_ctx, +bool kshark_import_adv_filters(struct kshark_context *kshark_ctx, int sd, struct kshark_config_doc *conf); bool kshark_export_event_filter(struct kshark_data_stream *stream, @@ -968,31 +998,46 @@ bool kshark_import_filter_array(struct kshark_hash_id *filter, const char *filter_name, struct kshark_config_doc *conf); -bool kshark_export_all_event_filters(struct kshark_context *kshark_ctx, +bool kshark_export_all_event_filters(struct kshark_context *kshark_ctx, int sd, struct kshark_config_doc **conf); -bool kshark_export_all_task_filters(struct kshark_context *kshark_ctx, +bool kshark_export_all_task_filters(struct kshark_context *kshark_ctx, int sd, struct kshark_config_doc **conf); -bool kshark_export_all_cpu_filters(struct kshark_context *kshark_ctx, +bool kshark_export_all_cpu_filters(struct kshark_context *kshark_ctx, int sd, struct kshark_config_doc **conf); struct kshark_config_doc * -kshark_export_all_filters(struct kshark_context *kshark_ctx, +kshark_export_all_filters(struct kshark_context *kshark_ctx, int sd, enum kshark_config_formats format); -bool kshark_import_all_event_filters(struct kshark_context *kshark_ctx, +bool kshark_import_all_event_filters(struct kshark_context *kshark_ctx, int sd, struct kshark_config_doc *conf); -bool kshark_import_all_task_filters(struct kshark_context *kshark_ctx, +bool kshark_import_all_task_filters(struct kshark_context *kshark_ctx, int sd, struct kshark_config_doc *conf); -bool kshark_import_all_cpu_filters(struct kshark_context *kshark_ctx, +bool kshark_import_all_cpu_filters(struct kshark_context *kshark_ctx, int sd, struct kshark_config_doc *conf); -bool kshark_import_all_filters(struct kshark_context *kshark_ctx, +bool kshark_import_all_filters(struct kshark_context *kshark_ctx, int sd, struct kshark_config_doc *conf); +struct kshark_config_doc * +kshark_export_dstream(struct kshark_context *kshark_ctx, int sd, + enum kshark_config_formats format); + +int kshark_import_dstream(struct kshark_context *kshark_ctx, + struct kshark_config_doc *conf); + +bool kshark_export_all_dstreams(struct kshark_context *kshark_ctx, + struct kshark_config_doc **conf); + +ssize_t kshark_import_all_dstreams(struct kshark_context *kshark_ctx, + struct kshark_config_doc *conf, + struct kshark_entry ***data_rows); + + bool kshark_save_config_file(const char *file_name, struct kshark_config_doc *conf); From patchwork Mon Jan 4 17:47:09 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11997179 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-20.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,MENTIONS_GIT_HOSTING,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 34456C4332B for ; Mon, 4 Jan 2021 17:49:20 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id DC8EC20700 for ; Mon, 4 Jan 2021 17:49:19 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726497AbhADRtT (ORCPT ); Mon, 4 Jan 2021 12:49:19 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34382 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726626AbhADRtT (ORCPT ); Mon, 4 Jan 2021 12:49:19 -0500 Received: from mail-ej1-x635.google.com (mail-ej1-x635.google.com [IPv6:2a00:1450:4864:20::635]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 95B33C061385 for ; Mon, 4 Jan 2021 09:48:09 -0800 (PST) Received: by mail-ej1-x635.google.com with SMTP id t16so8343180ejf.13 for ; Mon, 04 Jan 2021 09:48:09 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=P/o97RgRGP/ttFsXcNtkU5aij9V07Gwz3pd+0kXLx9w=; b=qgTaIUDTMFkZMwNnz0bvzm3DctTRTf2MTts3xtdQLF/XSJ0BXEzDa1fhPC9JAQ22Gu u3E2UkcADjTNwNQQgmGxRW8F/hUEUa2dYcYP5sp1pt8YChQCQuflpQcW41tQjkFreBe4 J5OTOLUTOhZM4tyx5C8uQlimXm7APTwu8PGTH0/dj4W7Ej37JE25DrF9Z8QHgePcgOFc kSwcv73Le8kmkANZrmk/ynUz1lqeQGseuk5Npd5t9HCDkprpzZWoc1kLNfPn3jfdbCoK NUyzCTrb5G2TgnP5pzCpfdsH7B7Tdll1+S8SGcjRTwQtHqogeRH/wW4qrdf2UOxAqhny ET0g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=P/o97RgRGP/ttFsXcNtkU5aij9V07Gwz3pd+0kXLx9w=; b=tSNnAZWwqILvZYRfJ4iUDqbSQmu46Cf3eUXDVAL33RQqS0nH3JQhcdtMeKPBySMB7g 1XAhWc5Di7dGunD2XKU9b/E/iBdG/aubFsZQIiP/STMMFFY/tcjbs96XbiI4vbmjqfyv ZF16Xr+bQb03a7ciuj5CZ21XuUnFD1UPVcuX9DZ77bxqkzSxTUdwcbnOZiGQC454HfIp 5cXEGQq5C8zbuj0uEKtNLarjt8ojUkQn1/Ntjg+d2bmjMt9JG3oavU7cmt1az9kznJb2 DkbvKQU6CRnCf6xZC8JZrlE4a/Qhhyjj3RzxyNdm+m253a2eQchH/V3PB/XETDaQLh9B CaoQ== X-Gm-Message-State: AOAM532OR8Nt214mJGo6X6YGnD6ng/ejEiqMWC9nSIlspJaGOubTH5Lg ZrMFbYccn+3iEqQWKMK40+IRZIuLkKohXQ== X-Google-Smtp-Source: ABdhPJyO7JsTPJd8owwWE8H+6oEclWIJhNCI8aaXR7UBSHlZ24hReA/jzQQuDb/HlkT+W9CBjxCopA== X-Received: by 2002:a17:907:1182:: with SMTP id uz2mr69144358ejb.183.1609782487121; Mon, 04 Jan 2021 09:48:07 -0800 (PST) Received: from localhost.localdomain ([95.87.199.238]) by smtp.gmail.com with ESMTPSA id l14sm44107750edq.35.2021.01.04.09.48.05 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 04 Jan 2021 09:48:06 -0800 (PST) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v8 29/44] kernel-shark: Add support for drawing text Date: Mon, 4 Jan 2021 19:47:09 +0200 Message-Id: <20210104174724.70404-30-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210104174724.70404-1-y.karadz@gmail.com> References: <20210104174724.70404-1-y.karadz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org OpenGL doesn't provide support for text rendering. Here we first re-enable the compilation of the OpenGL wrapper and add the capability of printing text to the OpenGL scene by using the single-file public domain library "stb_truetype": https://github.com/nothings/stb/blob/master/stb_truetype.h Signed-off-by: Yordan Karadzhov (VMware) --- CMakeLists.txt | 6 +- Documentation/doxygen/dox_config | 1 + src/CMakeLists.txt | 4 +- src/libkshark-plot.c | 256 +- src/libkshark-plot.h | 57 + src/stb_truetype.h | 5011 ++++++++++++++++++++++++++++++ 6 files changed, 5324 insertions(+), 11 deletions(-) create mode 100644 src/stb_truetype.h diff --git a/CMakeLists.txt b/CMakeLists.txt index cac2fa6..411163d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,9 +45,9 @@ find_package(JSONC REQUIRED) find_package(Doxygen) -# set(OpenGL_GL_PREFERENCE LEGACY) -# find_package(OpenGL) -# find_package(GLUT) +set(OpenGL_GL_PREFERENCE LEGACY) +find_package(OpenGL) +find_package(GLUT) # find_package(Qt5Widgets 5.7.1) # find_package(Qt5Network) diff --git a/Documentation/doxygen/dox_config b/Documentation/doxygen/dox_config index 89b9284..0bbeb3f 100644 --- a/Documentation/doxygen/dox_config +++ b/Documentation/doxygen/dox_config @@ -14,3 +14,4 @@ SORT_MEMBER_DOCS = NO STRICT_PROTO_MATCHING = YES DOT_MULTI_TARGETS = YES PROJECT_LOGO = ../../icons/KS_logo_stacked.svg +EXCLUDE = ../../src/stb_truetype.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 22bdc7c..cf3279e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -41,8 +41,8 @@ install(FILES "${KS_DIR}/src/libkshark.h" if (OPENGL_FOUND AND GLUT_FOUND) message(STATUS "libkshark-plot") - add_library(kshark-plot SHARED libkshark-plot.c - KsPlotTools.cpp) + add_library(kshark-plot SHARED libkshark-plot.c) +# KsPlotTools.cpp) target_link_libraries(kshark-plot kshark ${GLUT_LIBRARY} diff --git a/src/libkshark-plot.c b/src/libkshark-plot.c index 17d3b90..1c8ede4 100644 --- a/src/libkshark-plot.c +++ b/src/libkshark-plot.c @@ -1,13 +1,23 @@ /* SPDX-License-Identifier: LGPL-2.1 */ /* - * Copyright (C) 2018 VMware Inc, Yordan Karadzhov + * Copyright (C) 2018 VMware Inc, Yordan Karadzhov (VMware) */ - /** - * @file libkshark-plot.c - * @brief Basic tools for OpenGL plotting. - */ +/** + * @file libkshark-plot.c + * @brief Basic tools for OpenGL plotting. + */ + +// C +#ifndef _GNU_SOURCE +/** Use GNU C Library. */ +#define _GNU_SOURCE +#endif // _GNU_SOURCE + +#include +#include +#include // OpenGL #include @@ -16,6 +26,17 @@ // KernelShark #include "libkshark-plot.h" +/* + * STB TrueType (single-file public domain library) + * https://github.com/nothings/stb + */ +/** Generate implementation. */ +#define STB_TRUETYPE_IMPLEMENTATION + +/** Make the implementation private. */ +#define STBTT_STATIC +#include "stb_truetype.h" + /** * @brief Create an empty scene for drawing. * @@ -66,7 +87,7 @@ void ksplot_init_opengl(int dpr) glEnable(GL_POLYGON_SMOOTH); glLineWidth(1.5 * dpr); glPointSize(2.5 * dpr); - glClearColor(1, 1, 1, 1); + glClearColor(1, 1, 1, 1); // White } /** @@ -210,3 +231,226 @@ void ksplot_draw_polygon_contour(const struct ksplot_point *points, col, size); } + +/** + * @brief Find a TrueType font file. + * + * @param font_family: The family name of the font. + * @param font_name: The name of the font file without the extention. + * + * @returns A string containing the absolute path to the TrueType font file + * on success, or NULL on failure. The user is responsible for freeing + * the string. + */ +char *ksplot_find_font_file(const char *font_family, const char *font_name) +{ + char buffer[1024], *end; + char *cmd = NULL; + int ret; + FILE *f; + + /* + * This is sorta a hack. + * FIXME: Do this a bit more properly. + */ + ret = asprintf(&cmd, "fc-list \'%s\' |grep %s.ttf", font_family, + font_name); + if (ret <= 0) + goto fail; + + f = popen(cmd, "r"); + free(cmd); + if (f == NULL) + goto fail; + + end = fgets(buffer, sizeof(buffer), f); + pclose(f); + if (!end) + goto fail; + + end = strchr(buffer, ':'); + if (!end) + goto fail; + + return strndup(buffer, end - buffer); + + fail: + fprintf(stderr, "Failed to find font file.\n" ); + return NULL; +} + +/** The size of the bitmap matrix used to load the font. */ +#define KS_FONT_BITMAP_SIZE 1024 + +/** + * @brief Initialize a font. + * + * @param font: Output location for the font descriptor. + * @param size: The size of the font. + * @param file: Input location for the truetype font file. + */ +bool ksplot_init_font(struct ksplot_font *font, float size, const char *file) +{ + unsigned char bitmap[KS_FONT_BITMAP_SIZE * KS_FONT_BITMAP_SIZE]; + int ascent, descent, line_gap, lsb; + ssize_t buff_size, ret; + unsigned char *buffer; + stbtt_fontinfo info; + FILE *font_file; + struct stat st; + float scale; + + ret = stat(file, &st); + if (ret < 0) { + fprintf(stderr, "Font file %s not found.\n", file); + return NULL; + } + + font_file = fopen(file, "rb"); + if (!font_file) { + fprintf(stderr, "Failed to open font file!\n"); + return false; + } + + /* Get the size of the file. */ + buff_size = st.st_size; + + buffer = malloc(buff_size); + if (!buffer) { + fprintf(stderr, "Failed to allocat memory for font!\n"); + goto close_file; + } + + ret = fread(buffer, buff_size, 1, font_file); + fclose(font_file); + + if (ret == 0) { + fprintf(stderr, "Failed to read from font file!\n"); + goto free_buffer; + } + + if (!stbtt_InitFont(&info, buffer, 0)) { + fprintf(stderr, "Failed to init font!\n"); + goto free_buffer; + } + + /* Get the font's metrics. */ + scale = stbtt_ScaleForMappingEmToPixels(&info, size); + stbtt_GetFontVMetrics(&info, &ascent, &descent, &line_gap); + if (line_gap == 0) + line_gap = - descent; + /* + * Calculate the dimensions of the font. Note that the descent has + * a negative value. + */ + font->height = (- descent + ascent + line_gap) * scale; + font->base = (- descent + line_gap / 2) * scale; + font->size = size; + + /* + * The width of the 'z' character will be considered as an average + * character width. + */ + stbtt_GetCodepointHMetrics(&info, 'z', &font->char_width, &lsb); + font->char_width *= scale; + + ret = stbtt_BakeFontBitmap(buffer, + 0, + size, + bitmap, + KS_FONT_BITMAP_SIZE, + KS_FONT_BITMAP_SIZE, + KS_SPACE_CHAR, + KS_N_CHAR, + font->cdata); + + if (ret <= 0) { + fprintf(stderr, "Failed to bake font bitmap!\n"); + goto free_buffer; + } + + free(buffer); + + glGenTextures(1, &font->texture_id); + glBindTexture(GL_TEXTURE_2D, font->texture_id); + + glTexImage2D(GL_TEXTURE_2D, + 0, + GL_ALPHA, + KS_FONT_BITMAP_SIZE, + KS_FONT_BITMAP_SIZE, + 0, + GL_ALPHA, + GL_UNSIGNED_BYTE, + bitmap); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + return true; + + close_file: + fclose(font_file); + + free_buffer: + free(buffer); + return false; +} + +/** + * @brief Print(draw) a text. + * + * @param font: Intput location for the font descriptor. + * @param col: The color of the polygon. + * @param x: Horizontal coordinate of the beginning of the text. + * @param y: Verticalal coordinate of the beginning of the text. + * @param text: The text to be drawn. + */ +void ksplot_print_text(const struct ksplot_font *font, + const struct ksplot_color *col, + float x, float y, + const char *text) +{ + glEnable(GL_TEXTURE_2D); + + /* Set the color of the text. */ + if (col) + glColor3ub(col->red, col->green, col->blue); + else + glColor3ub(0, 0, 0); // Black + + glBindTexture(GL_TEXTURE_2D, font->texture_id); + glBegin(GL_QUADS); + for (; *text; ++text) { + if (*text < KS_SPACE_CHAR && *text > KS_TILDA_CHAR) + continue; + + stbtt_aligned_quad quad; + + /* "x" is incremented here to a new position. */ + stbtt_GetBakedQuad(font->cdata, + KS_FONT_BITMAP_SIZE, + KS_FONT_BITMAP_SIZE, + *text - KS_SPACE_CHAR, + &x, &y, + &quad, + 1); + + glTexCoord2f(quad.s0, quad.t1); + glVertex2f(quad.x0, quad.y1); + + glTexCoord2f(quad.s1, quad.t1); + glVertex2f(quad.x1, quad.y1); + + glTexCoord2f(quad.s1, quad.t0); + glVertex2f(quad.x1, quad.y0); + + glTexCoord2f(quad.s0, quad.t0); + glVertex2f(quad.x0, quad.y0); + } + + glEnd(); + glDisable(GL_TEXTURE_2D); +} diff --git a/src/libkshark-plot.h b/src/libkshark-plot.h index 9a4dbc0..0edf5d5 100644 --- a/src/libkshark-plot.h +++ b/src/libkshark-plot.h @@ -12,6 +12,15 @@ #ifndef _LIB_KSHARK_PLOT_H #define _LIB_KSHARK_PLOT_H +// C +#include + +/* + * STB TrueType (single-file public domain library) + * https://github.com/nothings/stb + */ +#include "stb_truetype.h" + #ifdef __cplusplus extern "C" { #endif @@ -62,6 +71,54 @@ void ksplot_draw_polygon_contour(const struct ksplot_point *points, const struct ksplot_color *col, float size); +/** The index of the "Space" character. */ +#define KS_SPACE_CHAR 32 + +/** The index of the "Tilda" character. */ +#define KS_TILDA_CHAR 126 + +/** Total number of characters supported for drawing. */ +#define KS_N_CHAR (KS_TILDA_CHAR - KS_SPACE_CHAR + 1) + +/** Structure defining a font. */ +struct ksplot_font { + /** Identifier of the font's texture. */ + GLuint texture_id; + + /** Font's texture baking data. */ + stbtt_bakedchar cdata[KS_N_CHAR]; + + /** The height of a text line. */ + int height; + + /** The vertical position of the font's baseline. */ + int base; + + /** The size of the font. */ + int size; + + /** + * The width of the 'z' character. To be use as an average character + * width. + */ + int char_width; +}; + +/** Check if the texture of the font is loaded. */ +static inline bool ksplot_font_is_loaded(struct ksplot_font *f) +{ + return f->texture_id > 1; +} + +char *ksplot_find_font_file(const char *font_family, const char *font_name); + +bool ksplot_init_font(struct ksplot_font *font, float size, const char *file); + +void ksplot_print_text(const struct ksplot_font *font, + const struct ksplot_color *col, + float x, float y, + const char *text); + #ifdef __cplusplus } #endif diff --git a/src/stb_truetype.h b/src/stb_truetype.h new file mode 100644 index 0000000..62595a1 --- /dev/null +++ b/src/stb_truetype.h @@ -0,0 +1,5011 @@ +// stb_truetype.h - v1.24 - public domain +// authored from 2009-2020 by Sean Barrett / RAD Game Tools +// +// ======================================================================= +// +// NO SECURITY GUARANTEE -- DO NOT USE THIS ON UNTRUSTED FONT FILES +// +// This library does no range checking of the offsets found in the file, +// meaning an attacker can use it to read arbitrary memory. +// +// ======================================================================= +// +// This library processes TrueType files: +// parse files +// extract glyph metrics +// extract glyph shapes +// render glyphs to one-channel bitmaps with antialiasing (box filter) +// render glyphs to one-channel SDF bitmaps (signed-distance field/function) +// +// Todo: +// non-MS cmaps +// crashproof on bad data +// hinting? (no longer patented) +// cleartype-style AA? +// optimize: use simple memory allocator for intermediates +// optimize: build edge-list directly from curves +// optimize: rasterize directly from curves? +// +// ADDITIONAL CONTRIBUTORS +// +// Mikko Mononen: compound shape support, more cmap formats +// Tor Andersson: kerning, subpixel rendering +// Dougall Johnson: OpenType / Type 2 font handling +// Daniel Ribeiro Maciel: basic GPOS-based kerning +// +// Misc other: +// Ryan Gordon +// Simon Glass +// github:IntellectualKitty +// Imanol Celaya +// Daniel Ribeiro Maciel +// +// Bug/warning reports/fixes: +// "Zer" on mollyrocket Fabian "ryg" Giesen github:NiLuJe +// Cass Everitt Martins Mozeiko github:aloucks +// stoiko (Haemimont Games) Cap Petschulat github:oyvindjam +// Brian Hook Omar Cornut github:vassvik +// Walter van Niftrik Ryan Griege +// David Gow Peter LaValle +// David Given Sergey Popov +// Ivan-Assen Ivanov Giumo X. Clanjor +// Anthony Pesch Higor Euripedes +// Johan Duparc Thomas Fields +// Hou Qiming Derek Vinyard +// Rob Loach Cort Stratton +// Kenney Phillis Jr. Brian Costabile +// Ken Voskuil (kaesve) +// +// VERSION HISTORY +// +// 1.24 (2020-02-05) fix warning +// 1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS) +// 1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined +// 1.21 (2019-02-25) fix warning +// 1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics() +// 1.19 (2018-02-11) GPOS kerning, STBTT_fmod +// 1.18 (2018-01-29) add missing function +// 1.17 (2017-07-23) make more arguments const; doc fix +// 1.16 (2017-07-12) SDF support +// 1.15 (2017-03-03) make more arguments const +// 1.14 (2017-01-16) num-fonts-in-TTC function +// 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts +// 1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual +// 1.11 (2016-04-02) fix unused-variable warning +// 1.10 (2016-04-02) user-defined fabs(); rare memory leak; remove duplicate typedef +// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use allocation userdata properly +// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges +// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; +// variant PackFontRanges to pack and render in separate phases; +// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); +// fixed an assert() bug in the new rasterizer +// replace assert() with STBTT_assert() in new rasterizer +// +// Full history can be found at the end of this file. +// +// LICENSE +// +// See end of file for license information. +// +// USAGE +// +// Include this file in whatever places need to refer to it. In ONE C/C++ +// file, write: +// #define STB_TRUETYPE_IMPLEMENTATION +// before the #include of this file. This expands out the actual +// implementation into that C/C++ file. +// +// To make the implementation private to the file that generates the implementation, +// #define STBTT_STATIC +// +// Simple 3D API (don't ship this, but it's fine for tools and quick start) +// stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture +// stbtt_GetBakedQuad() -- compute quad to draw for a given char +// +// Improved 3D API (more shippable): +// #include "stb_rect_pack.h" -- optional, but you really want it +// stbtt_PackBegin() +// stbtt_PackSetOversampling() -- for improved quality on small fonts +// stbtt_PackFontRanges() -- pack and renders +// stbtt_PackEnd() +// stbtt_GetPackedQuad() +// +// "Load" a font file from a memory buffer (you have to keep the buffer loaded) +// stbtt_InitFont() +// stbtt_GetFontOffsetForIndex() -- indexing for TTC font collections +// stbtt_GetNumberOfFonts() -- number of fonts for TTC font collections +// +// Render a unicode codepoint to a bitmap +// stbtt_GetCodepointBitmap() -- allocates and returns a bitmap +// stbtt_MakeCodepointBitmap() -- renders into bitmap you provide +// stbtt_GetCodepointBitmapBox() -- how big the bitmap must be +// +// Character advance/positioning +// stbtt_GetCodepointHMetrics() +// stbtt_GetFontVMetrics() +// stbtt_GetFontVMetricsOS2() +// stbtt_GetCodepointKernAdvance() +// +// Starting with version 1.06, the rasterizer was replaced with a new, +// faster and generally-more-precise rasterizer. The new rasterizer more +// accurately measures pixel coverage for anti-aliasing, except in the case +// where multiple shapes overlap, in which case it overestimates the AA pixel +// coverage. Thus, anti-aliasing of intersecting shapes may look wrong. If +// this turns out to be a problem, you can re-enable the old rasterizer with +// #define STBTT_RASTERIZER_VERSION 1 +// which will incur about a 15% speed hit. +// +// ADDITIONAL DOCUMENTATION +// +// Immediately after this block comment are a series of sample programs. +// +// After the sample programs is the "header file" section. This section +// includes documentation for each API function. +// +// Some important concepts to understand to use this library: +// +// Codepoint +// Characters are defined by unicode codepoints, e.g. 65 is +// uppercase A, 231 is lowercase c with a cedilla, 0x7e30 is +// the hiragana for "ma". +// +// Glyph +// A visual character shape (every codepoint is rendered as +// some glyph) +// +// Glyph index +// A font-specific integer ID representing a glyph +// +// Baseline +// Glyph shapes are defined relative to a baseline, which is the +// bottom of uppercase characters. Characters extend both above +// and below the baseline. +// +// Current Point +// As you draw text to the screen, you keep track of a "current point" +// which is the origin of each character. The current point's vertical +// position is the baseline. Even "baked fonts" use this model. +// +// Vertical Font Metrics +// The vertical qualities of the font, used to vertically position +// and space the characters. See docs for stbtt_GetFontVMetrics. +// +// Font Size in Pixels or Points +// The preferred interface for specifying font sizes in stb_truetype +// is to specify how tall the font's vertical extent should be in pixels. +// If that sounds good enough, skip the next paragraph. +// +// Most font APIs instead use "points", which are a common typographic +// measurement for describing font size, defined as 72 points per inch. +// stb_truetype provides a point API for compatibility. However, true +// "per inch" conventions don't make much sense on computer displays +// since different monitors have different number of pixels per +// inch. For example, Windows traditionally uses a convention that +// there are 96 pixels per inch, thus making 'inch' measurements have +// nothing to do with inches, and thus effectively defining a point to +// be 1.333 pixels. Additionally, the TrueType font data provides +// an explicit scale factor to scale a given font's glyphs to points, +// but the author has observed that this scale factor is often wrong +// for non-commercial fonts, thus making fonts scaled in points +// according to the TrueType spec incoherently sized in practice. +// +// DETAILED USAGE: +// +// Scale: +// Select how high you want the font to be, in points or pixels. +// Call ScaleForPixelHeight or ScaleForMappingEmToPixels to compute +// a scale factor SF that will be used by all other functions. +// +// Baseline: +// You need to select a y-coordinate that is the baseline of where +// your text will appear. Call GetFontBoundingBox to get the baseline-relative +// bounding box for all characters. SF*-y0 will be the distance in pixels +// that the worst-case character could extend above the baseline, so if +// you want the top edge of characters to appear at the top of the +// screen where y=0, then you would set the baseline to SF*-y0. +// +// Current point: +// Set the current point where the first character will appear. The +// first character could extend left of the current point; this is font +// dependent. You can either choose a current point that is the leftmost +// point and hope, or add some padding, or check the bounding box or +// left-side-bearing of the first character to be displayed and set +// the current point based on that. +// +// Displaying a character: +// Compute the bounding box of the character. It will contain signed values +// relative to . I.e. if it returns x0,y0,x1,y1, +// then the character should be displayed in the rectangle from +// to = 32 && *text < 128) { + stbtt_aligned_quad q; + stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9 + glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y0); + glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y0); + glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y1); + glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y1); + } + ++text; + } + glEnd(); +} +#endif +// +// +////////////////////////////////////////////////////////////////////////////// +// +// Complete program (this compiles): get a single bitmap, print as ASCII art +// +#if 0 +#include +#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation +#include "stb_truetype.h" + +char ttf_buffer[1<<25]; + +int main(int argc, char **argv) +{ + stbtt_fontinfo font; + unsigned char *bitmap; + int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20); + + fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb")); + + stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0)); + bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0); + + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) + putchar(" .:ioVM@"[bitmap[j*w+i]>>5]); + putchar('\n'); + } + return 0; +} +#endif +// +// Output: +// +// .ii. +// @@@@@@. +// V@Mio@@o +// :i. V@V +// :oM@@M +// :@@@MM@M +// @@o o@M +// :@@. M@M +// @@@o@@@@ +// :M@@V:@@. +// +////////////////////////////////////////////////////////////////////////////// +// +// Complete program: print "Hello World!" banner, with bugs +// +#if 0 +char buffer[24<<20]; +unsigned char screen[20][79]; + +int main(int arg, char **argv) +{ + stbtt_fontinfo font; + int i,j,ascent,baseline,ch=0; + float scale, xpos=2; // leave a little padding in case the character extends left + char *text = "Heljo World!"; // intentionally misspelled to show 'lj' brokenness + + fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb")); + stbtt_InitFont(&font, buffer, 0); + + scale = stbtt_ScaleForPixelHeight(&font, 15); + stbtt_GetFontVMetrics(&font, &ascent,0,0); + baseline = (int) (ascent*scale); + + while (text[ch]) { + int advance,lsb,x0,y0,x1,y1; + float x_shift = xpos - (float) floor(xpos); + stbtt_GetCodepointHMetrics(&font, text[ch], &advance, &lsb); + stbtt_GetCodepointBitmapBoxSubpixel(&font, text[ch], scale,scale,x_shift,0, &x0,&y0,&x1,&y1); + stbtt_MakeCodepointBitmapSubpixel(&font, &screen[baseline + y0][(int) xpos + x0], x1-x0,y1-y0, 79, scale,scale,x_shift,0, text[ch]); + // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong + // because this API is really for baking character bitmaps into textures. if you want to render + // a sequence of characters, you really need to render each bitmap to a temp buffer, then + // "alpha blend" that into the working buffer + xpos += (advance * scale); + if (text[ch+1]) + xpos += scale*stbtt_GetCodepointKernAdvance(&font, text[ch],text[ch+1]); + ++ch; + } + + for (j=0; j < 20; ++j) { + for (i=0; i < 78; ++i) + putchar(" .:ioVM@"[screen[j][i]>>5]); + putchar('\n'); + } + + return 0; +} +#endif + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +//// +//// INTEGRATION WITH YOUR CODEBASE +//// +//// The following sections allow you to supply alternate definitions +//// of C library functions used by stb_truetype, e.g. if you don't +//// link with the C runtime library. + +#ifdef STB_TRUETYPE_IMPLEMENTATION + // #define your own (u)stbtt_int8/16/32 before including to override this + #ifndef stbtt_uint8 + typedef unsigned char stbtt_uint8; + typedef signed char stbtt_int8; + typedef unsigned short stbtt_uint16; + typedef signed short stbtt_int16; + typedef unsigned int stbtt_uint32; + typedef signed int stbtt_int32; + #endif + + typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1]; + typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1]; + + // e.g. #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h + #ifndef STBTT_ifloor + #include + #define STBTT_ifloor(x) ((int) floor(x)) + #define STBTT_iceil(x) ((int) ceil(x)) + #endif + + #ifndef STBTT_sqrt + #include + #define STBTT_sqrt(x) sqrt(x) + #define STBTT_pow(x,y) pow(x,y) + #endif + + #ifndef STBTT_fmod + #include + #define STBTT_fmod(x,y) fmod(x,y) + #endif + + #ifndef STBTT_cos + #include + #define STBTT_cos(x) cos(x) + #define STBTT_acos(x) acos(x) + #endif + + #ifndef STBTT_fabs + #include + #define STBTT_fabs(x) fabs(x) + #endif + + // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h + #ifndef STBTT_malloc + #include + #define STBTT_malloc(x,u) ((void)(u),malloc(x)) + #define STBTT_free(x,u) ((void)(u),free(x)) + #endif + + #ifndef STBTT_assert + #include + #define STBTT_assert(x) assert(x) + #endif + + #ifndef STBTT_strlen + #include + #define STBTT_strlen(x) strlen(x) + #endif + + #ifndef STBTT_memcpy + #include + #define STBTT_memcpy memcpy + #define STBTT_memset memset + #endif +#endif + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//// +//// INTERFACE +//// +//// + +#ifndef __STB_INCLUDE_STB_TRUETYPE_H__ +#define __STB_INCLUDE_STB_TRUETYPE_H__ + +#ifdef STBTT_STATIC +#define STBTT_DEF static +#else +#define STBTT_DEF extern +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// private structure +typedef struct +{ + unsigned char *data; + int cursor; + int size; +} stbtt__buf; + +////////////////////////////////////////////////////////////////////////////// +// +// TEXTURE BAKING API +// +// If you use this API, you only have to call two functions ever. +// + +typedef struct +{ + unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap + float xoff,yoff,xadvance; +} stbtt_bakedchar; + +STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) + float pixel_height, // height of font in pixels + unsigned char *pixels, int pw, int ph, // bitmap to be filled in + int first_char, int num_chars, // characters to bake + stbtt_bakedchar *chardata); // you allocate this, it's num_chars long +// if return is positive, the first unused row of the bitmap +// if return is negative, returns the negative of the number of characters that fit +// if return is 0, no characters fit and no rows were used +// This uses a very crappy packing. + +typedef struct +{ + float x0,y0,s0,t0; // top-left + float x1,y1,s1,t1; // bottom-right +} stbtt_aligned_quad; + +STBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph, // same data as above + int char_index, // character to display + float *xpos, float *ypos, // pointers to current position in screen pixel space + stbtt_aligned_quad *q, // output: quad to draw + int opengl_fillrule); // true if opengl fill rule; false if DX9 or earlier +// Call GetBakedQuad with char_index = 'character - first_char', and it +// creates the quad you need to draw and advances the current position. +// +// The coordinate system used assumes y increases downwards. +// +// Characters will extend both above and below the current position; +// see discussion of "BASELINE" above. +// +// It's inefficient; you might want to c&p it and optimize it. + +STBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap); +// Query the font vertical metrics without having to create a font first. + + +////////////////////////////////////////////////////////////////////////////// +// +// NEW TEXTURE BAKING API +// +// This provides options for packing multiple fonts into one atlas, not +// perfectly but better than nothing. + +typedef struct +{ + unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap + float xoff,yoff,xadvance; + float xoff2,yoff2; +} stbtt_packedchar; + +typedef struct stbtt_pack_context stbtt_pack_context; +typedef struct stbtt_fontinfo stbtt_fontinfo; +#ifndef STB_RECT_PACK_VERSION +typedef struct stbrp_rect stbrp_rect; +#endif + +STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int width, int height, int stride_in_bytes, int padding, void *alloc_context); +// Initializes a packing context stored in the passed-in stbtt_pack_context. +// Future calls using this context will pack characters into the bitmap passed +// in here: a 1-channel bitmap that is width * height. stride_in_bytes is +// the distance from one row to the next (or 0 to mean they are packed tightly +// together). "padding" is the amount of padding to leave between each +// character (normally you want '1' for bitmaps you'll use as textures with +// bilinear filtering). +// +// Returns 0 on failure, 1 on success. + +STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc); +// Cleans up the packing context and frees all memory. + +#define STBTT_POINT_SIZE(x) (-(x)) + +STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size, + int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range); +// Creates character bitmaps from the font_index'th font found in fontdata (use +// font_index=0 if you don't know what that is). It creates num_chars_in_range +// bitmaps for characters with unicode values starting at first_unicode_char_in_range +// and increasing. Data for how to render them is stored in chardata_for_range; +// pass these to stbtt_GetPackedQuad to get back renderable quads. +// +// font_size is the full height of the character from ascender to descender, +// as computed by stbtt_ScaleForPixelHeight. To use a point size as computed +// by stbtt_ScaleForMappingEmToPixels, wrap the point size in STBTT_POINT_SIZE() +// and pass that result as 'font_size': +// ..., 20 , ... // font max minus min y is 20 pixels tall +// ..., STBTT_POINT_SIZE(20), ... // 'M' is 20 pixels tall + +typedef struct +{ + float font_size; + int first_unicode_codepoint_in_range; // if non-zero, then the chars are continuous, and this is the first codepoint + int *array_of_unicode_codepoints; // if non-zero, then this is an array of unicode codepoints + int num_chars; + stbtt_packedchar *chardata_for_range; // output + unsigned char h_oversample, v_oversample; // don't set these, they're used internally +} stbtt_pack_range; + +STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges); +// Creates character bitmaps from multiple ranges of characters stored in +// ranges. This will usually create a better-packed bitmap than multiple +// calls to stbtt_PackFontRange. Note that you can call this multiple +// times within a single PackBegin/PackEnd. + +STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample); +// Oversampling a font increases the quality by allowing higher-quality subpixel +// positioning, and is especially valuable at smaller text sizes. +// +// This function sets the amount of oversampling for all following calls to +// stbtt_PackFontRange(s) or stbtt_PackFontRangesGatherRects for a given +// pack context. The default (no oversampling) is achieved by h_oversample=1 +// and v_oversample=1. The total number of pixels required is +// h_oversample*v_oversample larger than the default; for example, 2x2 +// oversampling requires 4x the storage of 1x1. For best results, render +// oversampled textures with bilinear filtering. Look at the readme in +// stb/tests/oversample for information about oversampled fonts +// +// To use with PackFontRangesGather etc., you must set it before calls +// call to PackFontRangesGatherRects. + +STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip); +// If skip != 0, this tells stb_truetype to skip any codepoints for which +// there is no corresponding glyph. If skip=0, which is the default, then +// codepoints without a glyph recived the font's "missing character" glyph, +// typically an empty box by convention. + +STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, // same data as above + int char_index, // character to display + float *xpos, float *ypos, // pointers to current position in screen pixel space + stbtt_aligned_quad *q, // output: quad to draw + int align_to_integer); + +STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); +STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects); +STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); +// Calling these functions in sequence is roughly equivalent to calling +// stbtt_PackFontRanges(). If you more control over the packing of multiple +// fonts, or if you want to pack custom data into a font texture, take a look +// at the source to of stbtt_PackFontRanges() and create a custom version +// using these functions, e.g. call GatherRects multiple times, +// building up a single array of rects, then call PackRects once, +// then call RenderIntoRects repeatedly. This may result in a +// better packing than calling PackFontRanges multiple times +// (or it may not). + +// this is an opaque structure that you shouldn't mess with which holds +// all the context needed from PackBegin to PackEnd. +struct stbtt_pack_context { + void *user_allocator_context; + void *pack_info; + int width; + int height; + int stride_in_bytes; + int padding; + int skip_missing; + unsigned int h_oversample, v_oversample; + unsigned char *pixels; + void *nodes; +}; + +////////////////////////////////////////////////////////////////////////////// +// +// FONT LOADING +// +// + +STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data); +// This function will determine the number of fonts in a font file. TrueType +// collection (.ttc) files may contain multiple fonts, while TrueType font +// (.ttf) files only contain one font. The number of fonts can be used for +// indexing with the previous function where the index is between zero and one +// less than the total fonts. If an error occurs, -1 is returned. + +STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index); +// Each .ttf/.ttc file may have more than one font. Each font has a sequential +// index number starting from 0. Call this function to get the font offset for +// a given index; it returns -1 if the index is out of range. A regular .ttf +// file will only define one font and it always be at offset 0, so it will +// return '0' for index 0, and -1 for all other indices. + +// The following structure is defined publicly so you can declare one on +// the stack or as a global or etc, but you should treat it as opaque. +struct stbtt_fontinfo +{ + void * userdata; + unsigned char * data; // pointer to .ttf file + int fontstart; // offset of start of font + + int numGlyphs; // number of glyphs, needed for range checking + + int loca,head,glyf,hhea,hmtx,kern,gpos,svg; // table locations as offset from start of .ttf + int index_map; // a cmap mapping for our chosen character encoding + int indexToLocFormat; // format needed to map from glyph index to glyph + + stbtt__buf cff; // cff font data + stbtt__buf charstrings; // the charstring index + stbtt__buf gsubrs; // global charstring subroutines index + stbtt__buf subrs; // private charstring subroutines index + stbtt__buf fontdicts; // array of font dicts + stbtt__buf fdselect; // map from glyph to fontdict +}; + +STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset); +// Given an offset into the file that defines a font, this function builds +// the necessary cached info for the rest of the system. You must allocate +// the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't +// need to do anything special to free it, because the contents are pure +// value data with no additional data structures. Returns 0 on failure. + + +////////////////////////////////////////////////////////////////////////////// +// +// CHARACTER TO GLYPH-INDEX CONVERSIOn + +STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint); +// If you're going to perform multiple operations on the same character +// and you want a speed-up, call this function with the character you're +// going to process, then use glyph-based functions instead of the +// codepoint-based functions. +// Returns 0 if the character codepoint is not defined in the font. + + +////////////////////////////////////////////////////////////////////////////// +// +// CHARACTER PROPERTIES +// + +STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels); +// computes a scale factor to produce a font whose "height" is 'pixels' tall. +// Height is measured as the distance from the highest ascender to the lowest +// descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics +// and computing: +// scale = pixels / (ascent - descent) +// so if you prefer to measure height by the ascent only, use a similar calculation. + +STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels); +// computes a scale factor to produce a font whose EM size is mapped to +// 'pixels' tall. This is probably what traditional APIs compute, but +// I'm not positive. + +STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap); +// ascent is the coordinate above the baseline the font extends; descent +// is the coordinate below the baseline the font extends (i.e. it is typically negative) +// lineGap is the spacing between one row's descent and the next row's ascent... +// so you should advance the vertical position by "*ascent - *descent + *lineGap" +// these are expressed in unscaled coordinates, so you must multiply by +// the scale factor for a given size + +STBTT_DEF int stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap); +// analogous to GetFontVMetrics, but returns the "typographic" values from the OS/2 +// table (specific to MS/Windows TTF files). +// +// Returns 1 on success (table present), 0 on failure. + +STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1); +// the bounding box around all possible characters + +STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing); +// leftSideBearing is the offset from the current horizontal position to the left edge of the character +// advanceWidth is the offset from the current horizontal position to the next horizontal position +// these are expressed in unscaled coordinates + +STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2); +// an additional amount to add to the 'advance' value between ch1 and ch2 + +STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1); +// Gets the bounding box of the visible part of the glyph, in unscaled coordinates + +STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing); +STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2); +STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); +// as above, but takes one or more glyph indices for greater efficiency + +typedef struct stbtt_kerningentry +{ + int glyph1; // use stbtt_FindGlyphIndex + int glyph2; + int advance; +} stbtt_kerningentry; + +STBTT_DEF int stbtt_GetKerningTableLength(const stbtt_fontinfo *info); +STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length); +// Retrieves a complete list of all of the kerning pairs provided by the font +// stbtt_GetKerningTable never writes more than table_length entries and returns how many entries it did write. +// The table will be sorted by (a.glyph1 == b.glyph1)?(a.glyph2 < b.glyph2):(a.glyph1 < b.glyph1) + +////////////////////////////////////////////////////////////////////////////// +// +// GLYPH SHAPES (you probably don't need these, but they have to go before +// the bitmaps for C declaration-order reasons) +// + +#ifndef STBTT_vmove // you can predefine these to use different values (but why?) + enum { + STBTT_vmove=1, + STBTT_vline, + STBTT_vcurve, + STBTT_vcubic + }; +#endif + +#ifndef stbtt_vertex // you can predefine this to use different values + // (we share this with other code at RAD) + #define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file + typedef struct + { + stbtt_vertex_type x,y,cx,cy,cx1,cy1; + unsigned char type,padding; + } stbtt_vertex; +#endif + +STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index); +// returns non-zero if nothing is drawn for this glyph + +STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices); +STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices); +// returns # of vertices and fills *vertices with the pointer to them +// these are expressed in "unscaled" coordinates +// +// The shape is a series of contours. Each one starts with +// a STBTT_moveto, then consists of a series of mixed +// STBTT_lineto and STBTT_curveto segments. A lineto +// draws a line from previous endpoint to its x,y; a curveto +// draws a quadratic bezier from previous endpoint to +// its x,y, using cx,cy as the bezier control point. + +STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices); +// frees the data allocated above + +STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg); +STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg); +// fills svg with the character's SVG data. +// returns data size or 0 if SVG not found. + +////////////////////////////////////////////////////////////////////////////// +// +// BITMAP RENDERING +// + +STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata); +// frees the bitmap allocated below + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff); +// allocates a large-enough single-channel 8bpp bitmap and renders the +// specified character/glyph at the specified scale into it, with +// antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque). +// *width & *height are filled out with the width & height of the bitmap, +// which is stored left-to-right, top-to-bottom. +// +// xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff); +// the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel +// shift for the character + +STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint); +// the same as stbtt_GetCodepointBitmap, but you pass in storage for the bitmap +// in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap +// is clipped to out_w/out_h bytes. Call stbtt_GetCodepointBitmapBox to get the +// width and height and positioning info for it first. + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint); +// same as stbtt_MakeCodepointBitmap, but you can specify a subpixel +// shift for the character + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint); +// same as stbtt_MakeCodepointBitmapSubpixel, but prefiltering +// is performed (see stbtt_PackSetOversampling) + +STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); +// get the bbox of the bitmap centered around the glyph origin; so the +// bitmap width is ix1-ix0, height is iy1-iy0, and location to place +// the bitmap top left is (leftSideBearing*scale,iy0). +// (Note that the bitmap uses y-increases-down, but the shape uses +// y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.) + +STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); +// same as stbtt_GetCodepointBitmapBox, but you can specify a subpixel +// shift for the character + +// the following functions are equivalent to the above functions, but operate +// on glyph indices instead of Unicode codepoints (for efficiency) +STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff); +STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff); +STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph); +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph); +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int glyph); +STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); +STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); + + +// @TODO: don't expose this structure +typedef struct +{ + int w,h,stride; + unsigned char *pixels; +} stbtt__bitmap; + +// rasterize a shape with quadratic beziers into a bitmap +STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, // 1-channel bitmap to draw into + float flatness_in_pixels, // allowable error of curve in pixels + stbtt_vertex *vertices, // array of vertices defining shape + int num_verts, // number of vertices in above array + float scale_x, float scale_y, // scale applied to input vertices + float shift_x, float shift_y, // translation applied to input vertices + int x_off, int y_off, // another translation applied to input + int invert, // if non-zero, vertically flip shape + void *userdata); // context for to STBTT_MALLOC + +////////////////////////////////////////////////////////////////////////////// +// +// Signed Distance Function (or Field) rendering + +STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata); +// frees the SDF bitmap allocated below + +STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff); +STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff); +// These functions compute a discretized SDF field for a single character, suitable for storing +// in a single-channel texture, sampling with bilinear filtering, and testing against +// larger than some threshold to produce scalable fonts. +// info -- the font +// scale -- controls the size of the resulting SDF bitmap, same as it would be creating a regular bitmap +// glyph/codepoint -- the character to generate the SDF for +// padding -- extra "pixels" around the character which are filled with the distance to the character (not 0), +// which allows effects like bit outlines +// onedge_value -- value 0-255 to test the SDF against to reconstruct the character (i.e. the isocontour of the character) +// pixel_dist_scale -- what value the SDF should increase by when moving one SDF "pixel" away from the edge (on the 0..255 scale) +// if positive, > onedge_value is inside; if negative, < onedge_value is inside +// width,height -- output height & width of the SDF bitmap (including padding) +// xoff,yoff -- output origin of the character +// return value -- a 2D array of bytes 0..255, width*height in size +// +// pixel_dist_scale & onedge_value are a scale & bias that allows you to make +// optimal use of the limited 0..255 for your application, trading off precision +// and special effects. SDF values outside the range 0..255 are clamped to 0..255. +// +// Example: +// scale = stbtt_ScaleForPixelHeight(22) +// padding = 5 +// onedge_value = 180 +// pixel_dist_scale = 180/5.0 = 36.0 +// +// This will create an SDF bitmap in which the character is about 22 pixels +// high but the whole bitmap is about 22+5+5=32 pixels high. To produce a filled +// shape, sample the SDF at each pixel and fill the pixel if the SDF value +// is greater than or equal to 180/255. (You'll actually want to antialias, +// which is beyond the scope of this example.) Additionally, you can compute +// offset outlines (e.g. to stroke the character border inside & outside, +// or only outside). For example, to fill outside the character up to 3 SDF +// pixels, you would compare against (180-36.0*3)/255 = 72/255. The above +// choice of variables maps a range from 5 pixels outside the shape to +// 2 pixels inside the shape to 0..255; this is intended primarily for apply +// outside effects only (the interior range is needed to allow proper +// antialiasing of the font at *smaller* sizes) +// +// The function computes the SDF analytically at each SDF pixel, not by e.g. +// building a higher-res bitmap and approximating it. In theory the quality +// should be as high as possible for an SDF of this size & representation, but +// unclear if this is true in practice (perhaps building a higher-res bitmap +// and computing from that can allow drop-out prevention). +// +// The algorithm has not been optimized at all, so expect it to be slow +// if computing lots of characters or very large sizes. + + + +////////////////////////////////////////////////////////////////////////////// +// +// Finding the right font... +// +// You should really just solve this offline, keep your own tables +// of what font is what, and don't try to get it out of the .ttf file. +// That's because getting it out of the .ttf file is really hard, because +// the names in the file can appear in many possible encodings, in many +// possible languages, and e.g. if you need a case-insensitive comparison, +// the details of that depend on the encoding & language in a complex way +// (actually underspecified in truetype, but also gigantic). +// +// But you can use the provided functions in two possible ways: +// stbtt_FindMatchingFont() will use *case-sensitive* comparisons on +// unicode-encoded names to try to find the font you want; +// you can run this before calling stbtt_InitFont() +// +// stbtt_GetFontNameString() lets you get any of the various strings +// from the file yourself and do your own comparisons on them. +// You have to have called stbtt_InitFont() first. + + +STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags); +// returns the offset (not index) of the font that matches, or -1 if none +// if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold". +// if you use any other flag, use a font name like "Arial"; this checks +// the 'macStyle' header field; i don't know if fonts set this consistently +#define STBTT_MACSTYLE_DONTCARE 0 +#define STBTT_MACSTYLE_BOLD 1 +#define STBTT_MACSTYLE_ITALIC 2 +#define STBTT_MACSTYLE_UNDERSCORE 4 +#define STBTT_MACSTYLE_NONE 8 // <= not same as 0, this makes us check the bitfield is 0 + +STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2); +// returns 1/0 whether the first string interpreted as utf8 is identical to +// the second string interpreted as big-endian utf16... useful for strings from next func + +STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID); +// returns the string (which may be big-endian double byte, e.g. for unicode) +// and puts the length in bytes in *length. +// +// some of the values for the IDs are below; for more see the truetype spec: +// http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html +// http://www.microsoft.com/typography/otspec/name.htm + +enum { // platformID + STBTT_PLATFORM_ID_UNICODE =0, + STBTT_PLATFORM_ID_MAC =1, + STBTT_PLATFORM_ID_ISO =2, + STBTT_PLATFORM_ID_MICROSOFT =3 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_UNICODE + STBTT_UNICODE_EID_UNICODE_1_0 =0, + STBTT_UNICODE_EID_UNICODE_1_1 =1, + STBTT_UNICODE_EID_ISO_10646 =2, + STBTT_UNICODE_EID_UNICODE_2_0_BMP=3, + STBTT_UNICODE_EID_UNICODE_2_0_FULL=4 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT + STBTT_MS_EID_SYMBOL =0, + STBTT_MS_EID_UNICODE_BMP =1, + STBTT_MS_EID_SHIFTJIS =2, + STBTT_MS_EID_UNICODE_FULL =10 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes + STBTT_MAC_EID_ROMAN =0, STBTT_MAC_EID_ARABIC =4, + STBTT_MAC_EID_JAPANESE =1, STBTT_MAC_EID_HEBREW =5, + STBTT_MAC_EID_CHINESE_TRAD =2, STBTT_MAC_EID_GREEK =6, + STBTT_MAC_EID_KOREAN =3, STBTT_MAC_EID_RUSSIAN =7 +}; + +enum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID... + // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs + STBTT_MS_LANG_ENGLISH =0x0409, STBTT_MS_LANG_ITALIAN =0x0410, + STBTT_MS_LANG_CHINESE =0x0804, STBTT_MS_LANG_JAPANESE =0x0411, + STBTT_MS_LANG_DUTCH =0x0413, STBTT_MS_LANG_KOREAN =0x0412, + STBTT_MS_LANG_FRENCH =0x040c, STBTT_MS_LANG_RUSSIAN =0x0419, + STBTT_MS_LANG_GERMAN =0x0407, STBTT_MS_LANG_SPANISH =0x0409, + STBTT_MS_LANG_HEBREW =0x040d, STBTT_MS_LANG_SWEDISH =0x041D +}; + +enum { // languageID for STBTT_PLATFORM_ID_MAC + STBTT_MAC_LANG_ENGLISH =0 , STBTT_MAC_LANG_JAPANESE =11, + STBTT_MAC_LANG_ARABIC =12, STBTT_MAC_LANG_KOREAN =23, + STBTT_MAC_LANG_DUTCH =4 , STBTT_MAC_LANG_RUSSIAN =32, + STBTT_MAC_LANG_FRENCH =1 , STBTT_MAC_LANG_SPANISH =6 , + STBTT_MAC_LANG_GERMAN =2 , STBTT_MAC_LANG_SWEDISH =5 , + STBTT_MAC_LANG_HEBREW =10, STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33, + STBTT_MAC_LANG_ITALIAN =3 , STBTT_MAC_LANG_CHINESE_TRAD =19 +}; + +#ifdef __cplusplus +} +#endif + +#endif // __STB_INCLUDE_STB_TRUETYPE_H__ + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//// +//// IMPLEMENTATION +//// +//// + +#ifdef STB_TRUETYPE_IMPLEMENTATION + +#ifndef STBTT_MAX_OVERSAMPLE +#define STBTT_MAX_OVERSAMPLE 8 +#endif + +#if STBTT_MAX_OVERSAMPLE > 255 +#error "STBTT_MAX_OVERSAMPLE cannot be > 255" +#endif + +typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERSAMPLE-1)) == 0 ? 1 : -1]; + +#ifndef STBTT_RASTERIZER_VERSION +#define STBTT_RASTERIZER_VERSION 2 +#endif + +#ifdef _MSC_VER +#define STBTT__NOTUSED(v) (void)(v) +#else +#define STBTT__NOTUSED(v) (void)sizeof(v) +#endif + +////////////////////////////////////////////////////////////////////////// +// +// stbtt__buf helpers to parse data from file +// + +static stbtt_uint8 stbtt__buf_get8(stbtt__buf *b) +{ + if (b->cursor >= b->size) + return 0; + return b->data[b->cursor++]; +} + +static stbtt_uint8 stbtt__buf_peek8(stbtt__buf *b) +{ + if (b->cursor >= b->size) + return 0; + return b->data[b->cursor]; +} + +static void stbtt__buf_seek(stbtt__buf *b, int o) +{ + STBTT_assert(!(o > b->size || o < 0)); + b->cursor = (o > b->size || o < 0) ? b->size : o; +} + +static void stbtt__buf_skip(stbtt__buf *b, int o) +{ + stbtt__buf_seek(b, b->cursor + o); +} + +static stbtt_uint32 stbtt__buf_get(stbtt__buf *b, int n) +{ + stbtt_uint32 v = 0; + int i; + STBTT_assert(n >= 1 && n <= 4); + for (i = 0; i < n; i++) + v = (v << 8) | stbtt__buf_get8(b); + return v; +} + +static stbtt__buf stbtt__new_buf(const void *p, size_t size) +{ + stbtt__buf r; + STBTT_assert(size < 0x40000000); + r.data = (stbtt_uint8*) p; + r.size = (int) size; + r.cursor = 0; + return r; +} + +#define stbtt__buf_get16(b) stbtt__buf_get((b), 2) +#define stbtt__buf_get32(b) stbtt__buf_get((b), 4) + +static stbtt__buf stbtt__buf_range(const stbtt__buf *b, int o, int s) +{ + stbtt__buf r = stbtt__new_buf(NULL, 0); + if (o < 0 || s < 0 || o > b->size || s > b->size - o) return r; + r.data = b->data + o; + r.size = s; + return r; +} + +static stbtt__buf stbtt__cff_get_index(stbtt__buf *b) +{ + int count, start, offsize; + start = b->cursor; + count = stbtt__buf_get16(b); + if (count) { + offsize = stbtt__buf_get8(b); + STBTT_assert(offsize >= 1 && offsize <= 4); + stbtt__buf_skip(b, offsize * count); + stbtt__buf_skip(b, stbtt__buf_get(b, offsize) - 1); + } + return stbtt__buf_range(b, start, b->cursor - start); +} + +static stbtt_uint32 stbtt__cff_int(stbtt__buf *b) +{ + int b0 = stbtt__buf_get8(b); + if (b0 >= 32 && b0 <= 246) return b0 - 139; + else if (b0 >= 247 && b0 <= 250) return (b0 - 247)*256 + stbtt__buf_get8(b) + 108; + else if (b0 >= 251 && b0 <= 254) return -(b0 - 251)*256 - stbtt__buf_get8(b) - 108; + else if (b0 == 28) return stbtt__buf_get16(b); + else if (b0 == 29) return stbtt__buf_get32(b); + STBTT_assert(0); + return 0; +} + +static void stbtt__cff_skip_operand(stbtt__buf *b) { + int v, b0 = stbtt__buf_peek8(b); + STBTT_assert(b0 >= 28); + if (b0 == 30) { + stbtt__buf_skip(b, 1); + while (b->cursor < b->size) { + v = stbtt__buf_get8(b); + if ((v & 0xF) == 0xF || (v >> 4) == 0xF) + break; + } + } else { + stbtt__cff_int(b); + } +} + +static stbtt__buf stbtt__dict_get(stbtt__buf *b, int key) +{ + stbtt__buf_seek(b, 0); + while (b->cursor < b->size) { + int start = b->cursor, end, op; + while (stbtt__buf_peek8(b) >= 28) + stbtt__cff_skip_operand(b); + end = b->cursor; + op = stbtt__buf_get8(b); + if (op == 12) op = stbtt__buf_get8(b) | 0x100; + if (op == key) return stbtt__buf_range(b, start, end-start); + } + return stbtt__buf_range(b, 0, 0); +} + +static void stbtt__dict_get_ints(stbtt__buf *b, int key, int outcount, stbtt_uint32 *out) +{ + int i; + stbtt__buf operands = stbtt__dict_get(b, key); + for (i = 0; i < outcount && operands.cursor < operands.size; i++) + out[i] = stbtt__cff_int(&operands); +} + +static int stbtt__cff_index_count(stbtt__buf *b) +{ + stbtt__buf_seek(b, 0); + return stbtt__buf_get16(b); +} + +static stbtt__buf stbtt__cff_index_get(stbtt__buf b, int i) +{ + int count, offsize, start, end; + stbtt__buf_seek(&b, 0); + count = stbtt__buf_get16(&b); + offsize = stbtt__buf_get8(&b); + STBTT_assert(i >= 0 && i < count); + STBTT_assert(offsize >= 1 && offsize <= 4); + stbtt__buf_skip(&b, i*offsize); + start = stbtt__buf_get(&b, offsize); + end = stbtt__buf_get(&b, offsize); + return stbtt__buf_range(&b, 2+(count+1)*offsize+start, end - start); +} + +////////////////////////////////////////////////////////////////////////// +// +// accessors to parse data from file +// + +// on platforms that don't allow misaligned reads, if we want to allow +// truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE + +#define ttBYTE(p) (* (stbtt_uint8 *) (p)) +#define ttCHAR(p) (* (stbtt_int8 *) (p)) +#define ttFixed(p) ttLONG(p) + +static stbtt_uint16 ttUSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; } +static stbtt_int16 ttSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; } +static stbtt_uint32 ttULONG(stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } +static stbtt_int32 ttLONG(stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } + +#define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3)) +#define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3]) + +static int stbtt__isfont(stbtt_uint8 *font) +{ + // check the version number + if (stbtt_tag4(font, '1',0,0,0)) return 1; // TrueType 1 + if (stbtt_tag(font, "typ1")) return 1; // TrueType with type 1 font -- we don't support this! + if (stbtt_tag(font, "OTTO")) return 1; // OpenType with CFF + if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0 + if (stbtt_tag(font, "true")) return 1; // Apple specification for TrueType fonts + return 0; +} + +// @OPTIMIZE: binary search +static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag) +{ + stbtt_int32 num_tables = ttUSHORT(data+fontstart+4); + stbtt_uint32 tabledir = fontstart + 12; + stbtt_int32 i; + for (i=0; i < num_tables; ++i) { + stbtt_uint32 loc = tabledir + 16*i; + if (stbtt_tag(data+loc+0, tag)) + return ttULONG(data+loc+8); + } + return 0; +} + +static int stbtt_GetFontOffsetForIndex_internal(unsigned char *font_collection, int index) +{ + // if it's just a font, there's only one valid index + if (stbtt__isfont(font_collection)) + return index == 0 ? 0 : -1; + + // check if it's a TTC + if (stbtt_tag(font_collection, "ttcf")) { + // version 1? + if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { + stbtt_int32 n = ttLONG(font_collection+8); + if (index >= n) + return -1; + return ttULONG(font_collection+12+index*4); + } + } + return -1; +} + +static int stbtt_GetNumberOfFonts_internal(unsigned char *font_collection) +{ + // if it's just a font, there's only one valid font + if (stbtt__isfont(font_collection)) + return 1; + + // check if it's a TTC + if (stbtt_tag(font_collection, "ttcf")) { + // version 1? + if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { + return ttLONG(font_collection+8); + } + } + return 0; +} + +static stbtt__buf stbtt__get_subrs(stbtt__buf cff, stbtt__buf fontdict) +{ + stbtt_uint32 subrsoff = 0, private_loc[2] = { 0, 0 }; + stbtt__buf pdict; + stbtt__dict_get_ints(&fontdict, 18, 2, private_loc); + if (!private_loc[1] || !private_loc[0]) return stbtt__new_buf(NULL, 0); + pdict = stbtt__buf_range(&cff, private_loc[1], private_loc[0]); + stbtt__dict_get_ints(&pdict, 19, 1, &subrsoff); + if (!subrsoff) return stbtt__new_buf(NULL, 0); + stbtt__buf_seek(&cff, private_loc[1]+subrsoff); + return stbtt__cff_get_index(&cff); +} + +// since most people won't use this, find this table the first time it's needed +static int stbtt__get_svg(stbtt_fontinfo *info) +{ + stbtt_uint32 t; + if (info->svg < 0) { + t = stbtt__find_table(info->data, info->fontstart, "SVG "); + if (t) { + stbtt_uint32 offset = ttULONG(info->data + t + 2); + info->svg = t + offset; + } else { + info->svg = 0; + } + } + return info->svg; +} + +static int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, int fontstart) +{ + stbtt_uint32 cmap, t; + stbtt_int32 i,numTables; + + info->data = data; + info->fontstart = fontstart; + info->cff = stbtt__new_buf(NULL, 0); + + cmap = stbtt__find_table(data, fontstart, "cmap"); // required + info->loca = stbtt__find_table(data, fontstart, "loca"); // required + info->head = stbtt__find_table(data, fontstart, "head"); // required + info->glyf = stbtt__find_table(data, fontstart, "glyf"); // required + info->hhea = stbtt__find_table(data, fontstart, "hhea"); // required + info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); // required + info->kern = stbtt__find_table(data, fontstart, "kern"); // not required + info->gpos = stbtt__find_table(data, fontstart, "GPOS"); // not required + + if (!cmap || !info->head || !info->hhea || !info->hmtx) + return 0; + if (info->glyf) { + // required for truetype + if (!info->loca) return 0; + } else { + // initialization for CFF / Type2 fonts (OTF) + stbtt__buf b, topdict, topdictidx; + stbtt_uint32 cstype = 2, charstrings = 0, fdarrayoff = 0, fdselectoff = 0; + stbtt_uint32 cff; + + cff = stbtt__find_table(data, fontstart, "CFF "); + if (!cff) return 0; + + info->fontdicts = stbtt__new_buf(NULL, 0); + info->fdselect = stbtt__new_buf(NULL, 0); + + // @TODO this should use size from table (not 512MB) + info->cff = stbtt__new_buf(data+cff, 512*1024*1024); + b = info->cff; + + // read the header + stbtt__buf_skip(&b, 2); + stbtt__buf_seek(&b, stbtt__buf_get8(&b)); // hdrsize + + // @TODO the name INDEX could list multiple fonts, + // but we just use the first one. + stbtt__cff_get_index(&b); // name INDEX + topdictidx = stbtt__cff_get_index(&b); + topdict = stbtt__cff_index_get(topdictidx, 0); + stbtt__cff_get_index(&b); // string INDEX + info->gsubrs = stbtt__cff_get_index(&b); + + stbtt__dict_get_ints(&topdict, 17, 1, &charstrings); + stbtt__dict_get_ints(&topdict, 0x100 | 6, 1, &cstype); + stbtt__dict_get_ints(&topdict, 0x100 | 36, 1, &fdarrayoff); + stbtt__dict_get_ints(&topdict, 0x100 | 37, 1, &fdselectoff); + info->subrs = stbtt__get_subrs(b, topdict); + + // we only support Type 2 charstrings + if (cstype != 2) return 0; + if (charstrings == 0) return 0; + + if (fdarrayoff) { + // looks like a CID font + if (!fdselectoff) return 0; + stbtt__buf_seek(&b, fdarrayoff); + info->fontdicts = stbtt__cff_get_index(&b); + info->fdselect = stbtt__buf_range(&b, fdselectoff, b.size-fdselectoff); + } + + stbtt__buf_seek(&b, charstrings); + info->charstrings = stbtt__cff_get_index(&b); + } + + t = stbtt__find_table(data, fontstart, "maxp"); + if (t) + info->numGlyphs = ttUSHORT(data+t+4); + else + info->numGlyphs = 0xffff; + + info->svg = -1; + + // find a cmap encoding table we understand *now* to avoid searching + // later. (todo: could make this installable) + // the same regardless of glyph. + numTables = ttUSHORT(data + cmap + 2); + info->index_map = 0; + for (i=0; i < numTables; ++i) { + stbtt_uint32 encoding_record = cmap + 4 + 8 * i; + // find an encoding we understand: + switch(ttUSHORT(data+encoding_record)) { + case STBTT_PLATFORM_ID_MICROSOFT: + switch (ttUSHORT(data+encoding_record+2)) { + case STBTT_MS_EID_UNICODE_BMP: + case STBTT_MS_EID_UNICODE_FULL: + // MS/Unicode + info->index_map = cmap + ttULONG(data+encoding_record+4); + break; + } + break; + case STBTT_PLATFORM_ID_UNICODE: + // Mac/iOS has these + // all the encodingIDs are unicode, so we don't bother to check it + info->index_map = cmap + ttULONG(data+encoding_record+4); + break; + } + } + if (info->index_map == 0) + return 0; + + info->indexToLocFormat = ttUSHORT(data+info->head + 50); + return 1; +} + +STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint) +{ + stbtt_uint8 *data = info->data; + stbtt_uint32 index_map = info->index_map; + + stbtt_uint16 format = ttUSHORT(data + index_map + 0); + if (format == 0) { // apple byte encoding + stbtt_int32 bytes = ttUSHORT(data + index_map + 2); + if (unicode_codepoint < bytes-6) + return ttBYTE(data + index_map + 6 + unicode_codepoint); + return 0; + } else if (format == 6) { + stbtt_uint32 first = ttUSHORT(data + index_map + 6); + stbtt_uint32 count = ttUSHORT(data + index_map + 8); + if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count) + return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2); + return 0; + } else if (format == 2) { + STBTT_assert(0); // @TODO: high-byte mapping for japanese/chinese/korean + return 0; + } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges + stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1; + stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1; + stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10); + stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1; + + // do a binary search of the segments + stbtt_uint32 endCount = index_map + 14; + stbtt_uint32 search = endCount; + + if (unicode_codepoint > 0xffff) + return 0; + + // they lie from endCount .. endCount + segCount + // but searchRange is the nearest power of two, so... + if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2)) + search += rangeShift*2; + + // now decrement to bias correctly to find smallest + search -= 2; + while (entrySelector) { + stbtt_uint16 end; + searchRange >>= 1; + end = ttUSHORT(data + search + searchRange*2); + if (unicode_codepoint > end) + search += searchRange*2; + --entrySelector; + } + search += 2; + + { + stbtt_uint16 offset, start; + stbtt_uint16 item = (stbtt_uint16) ((search - endCount) >> 1); + + STBTT_assert(unicode_codepoint <= ttUSHORT(data + endCount + 2*item)); + start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item); + if (unicode_codepoint < start) + return 0; + + offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item); + if (offset == 0) + return (stbtt_uint16) (unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item)); + + return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item); + } + } else if (format == 12 || format == 13) { + stbtt_uint32 ngroups = ttULONG(data+index_map+12); + stbtt_int32 low,high; + low = 0; high = (stbtt_int32)ngroups; + // Binary search the right group. + while (low < high) { + stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high + stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12); + stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4); + if ((stbtt_uint32) unicode_codepoint < start_char) + high = mid; + else if ((stbtt_uint32) unicode_codepoint > end_char) + low = mid+1; + else { + stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8); + if (format == 12) + return start_glyph + unicode_codepoint-start_char; + else // format == 13 + return start_glyph; + } + } + return 0; // not found + } + // @TODO + STBTT_assert(0); + return 0; +} + +STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices) +{ + return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices); +} + +static void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy) +{ + v->type = type; + v->x = (stbtt_int16) x; + v->y = (stbtt_int16) y; + v->cx = (stbtt_int16) cx; + v->cy = (stbtt_int16) cy; +} + +static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index) +{ + int g1,g2; + + STBTT_assert(!info->cff.size); + + if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range + if (info->indexToLocFormat >= 2) return -1; // unknown index->glyph map format + + if (info->indexToLocFormat == 0) { + g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2; + g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2; + } else { + g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4); + g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4); + } + + return g1==g2 ? -1 : g1; // if length is 0, return -1 +} + +static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); + +STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) +{ + if (info->cff.size) { + stbtt__GetGlyphInfoT2(info, glyph_index, x0, y0, x1, y1); + } else { + int g = stbtt__GetGlyfOffset(info, glyph_index); + if (g < 0) return 0; + + if (x0) *x0 = ttSHORT(info->data + g + 2); + if (y0) *y0 = ttSHORT(info->data + g + 4); + if (x1) *x1 = ttSHORT(info->data + g + 6); + if (y1) *y1 = ttSHORT(info->data + g + 8); + } + return 1; +} + +STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1) +{ + return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1); +} + +STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index) +{ + stbtt_int16 numberOfContours; + int g; + if (info->cff.size) + return stbtt__GetGlyphInfoT2(info, glyph_index, NULL, NULL, NULL, NULL) == 0; + g = stbtt__GetGlyfOffset(info, glyph_index); + if (g < 0) return 1; + numberOfContours = ttSHORT(info->data + g); + return numberOfContours == 0; +} + +static int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_off, int start_off, + stbtt_int32 sx, stbtt_int32 sy, stbtt_int32 scx, stbtt_int32 scy, stbtt_int32 cx, stbtt_int32 cy) +{ + if (start_off) { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy); + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, sx,sy,scx,scy); + } else { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy); + else + stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0); + } + return num_vertices; +} + +static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +{ + stbtt_int16 numberOfContours; + stbtt_uint8 *endPtsOfContours; + stbtt_uint8 *data = info->data; + stbtt_vertex *vertices=0; + int num_vertices=0; + int g = stbtt__GetGlyfOffset(info, glyph_index); + + *pvertices = NULL; + + if (g < 0) return 0; + + numberOfContours = ttSHORT(data + g); + + if (numberOfContours > 0) { + stbtt_uint8 flags=0,flagcount; + stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0; + stbtt_int32 x,y,cx,cy,sx,sy, scx,scy; + stbtt_uint8 *points; + endPtsOfContours = (data + g + 10); + ins = ttUSHORT(data + g + 10 + numberOfContours * 2); + points = data + g + 10 + numberOfContours * 2 + 2 + ins; + + n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2); + + m = n + 2*numberOfContours; // a loose bound on how many vertices we might need + vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata); + if (vertices == 0) + return 0; + + next_move = 0; + flagcount=0; + + // in first pass, we load uninterpreted data into the allocated array + // above, shifted to the end of the array so we won't overwrite it when + // we create our final data starting from the front + + off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated + + // first load flags + + for (i=0; i < n; ++i) { + if (flagcount == 0) { + flags = *points++; + if (flags & 8) + flagcount = *points++; + } else + --flagcount; + vertices[off+i].type = flags; + } + + // now load x coordinates + x=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 2) { + stbtt_int16 dx = *points++; + x += (flags & 16) ? dx : -dx; // ??? + } else { + if (!(flags & 16)) { + x = x + (stbtt_int16) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].x = (stbtt_int16) x; + } + + // now load y coordinates + y=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 4) { + stbtt_int16 dy = *points++; + y += (flags & 32) ? dy : -dy; // ??? + } else { + if (!(flags & 32)) { + y = y + (stbtt_int16) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].y = (stbtt_int16) y; + } + + // now convert them to our format + num_vertices=0; + sx = sy = cx = cy = scx = scy = 0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + x = (stbtt_int16) vertices[off+i].x; + y = (stbtt_int16) vertices[off+i].y; + + if (next_move == i) { + if (i != 0) + num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + + // now start the new one + start_off = !(flags & 1); + if (start_off) { + // if we start off with an off-curve point, then when we need to find a point on the curve + // where we can start, and we need to save some state for when we wraparound. + scx = x; + scy = y; + if (!(vertices[off+i+1].type & 1)) { + // next point is also a curve point, so interpolate an on-point curve + sx = (x + (stbtt_int32) vertices[off+i+1].x) >> 1; + sy = (y + (stbtt_int32) vertices[off+i+1].y) >> 1; + } else { + // otherwise just use the next point as our start point + sx = (stbtt_int32) vertices[off+i+1].x; + sy = (stbtt_int32) vertices[off+i+1].y; + ++i; // we're using point i+1 as the starting point, so skip it + } + } else { + sx = x; + sy = y; + } + stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,sx,sy,0,0); + was_off = 0; + next_move = 1 + ttUSHORT(endPtsOfContours+j*2); + ++j; + } else { + if (!(flags & 1)) { // if it's a curve + if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy); + cx = x; + cy = y; + was_off = 1; + } else { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy); + else + stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0); + was_off = 0; + } + } + } + num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + } else if (numberOfContours < 0) { + // Compound shapes. + int more = 1; + stbtt_uint8 *comp = data + g + 10; + num_vertices = 0; + vertices = 0; + while (more) { + stbtt_uint16 flags, gidx; + int comp_num_verts = 0, i; + stbtt_vertex *comp_verts = 0, *tmp = 0; + float mtx[6] = {1,0,0,1,0,0}, m, n; + + flags = ttSHORT(comp); comp+=2; + gidx = ttSHORT(comp); comp+=2; + + if (flags & 2) { // XY values + if (flags & 1) { // shorts + mtx[4] = ttSHORT(comp); comp+=2; + mtx[5] = ttSHORT(comp); comp+=2; + } else { + mtx[4] = ttCHAR(comp); comp+=1; + mtx[5] = ttCHAR(comp); comp+=1; + } + } + else { + // @TODO handle matching point + STBTT_assert(0); + } + if (flags & (1<<3)) { // WE_HAVE_A_SCALE + mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE + mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO + mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[2] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + } + + // Find transformation scales. + m = (float) STBTT_sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]); + n = (float) STBTT_sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]); + + // Get indexed glyph. + comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts); + if (comp_num_verts > 0) { + // Transform vertices. + for (i = 0; i < comp_num_verts; ++i) { + stbtt_vertex* v = &comp_verts[i]; + stbtt_vertex_type x,y; + x=v->x; y=v->y; + v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + x=v->cx; y=v->cy; + v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + } + // Append vertices. + tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata); + if (!tmp) { + if (vertices) STBTT_free(vertices, info->userdata); + if (comp_verts) STBTT_free(comp_verts, info->userdata); + return 0; + } + if (num_vertices > 0) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex)); + STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex)); + if (vertices) STBTT_free(vertices, info->userdata); + vertices = tmp; + STBTT_free(comp_verts, info->userdata); + num_vertices += comp_num_verts; + } + // More components ? + more = flags & (1<<5); + } + } else { + // numberOfCounters == 0, do nothing + } + + *pvertices = vertices; + return num_vertices; +} + +typedef struct +{ + int bounds; + int started; + float first_x, first_y; + float x, y; + stbtt_int32 min_x, max_x, min_y, max_y; + + stbtt_vertex *pvertices; + int num_vertices; +} stbtt__csctx; + +#define STBTT__CSCTX_INIT(bounds) {bounds,0, 0,0, 0,0, 0,0,0,0, NULL, 0} + +static void stbtt__track_vertex(stbtt__csctx *c, stbtt_int32 x, stbtt_int32 y) +{ + if (x > c->max_x || !c->started) c->max_x = x; + if (y > c->max_y || !c->started) c->max_y = y; + if (x < c->min_x || !c->started) c->min_x = x; + if (y < c->min_y || !c->started) c->min_y = y; + c->started = 1; +} + +static void stbtt__csctx_v(stbtt__csctx *c, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy, stbtt_int32 cx1, stbtt_int32 cy1) +{ + if (c->bounds) { + stbtt__track_vertex(c, x, y); + if (type == STBTT_vcubic) { + stbtt__track_vertex(c, cx, cy); + stbtt__track_vertex(c, cx1, cy1); + } + } else { + stbtt_setvertex(&c->pvertices[c->num_vertices], type, x, y, cx, cy); + c->pvertices[c->num_vertices].cx1 = (stbtt_int16) cx1; + c->pvertices[c->num_vertices].cy1 = (stbtt_int16) cy1; + } + c->num_vertices++; +} + +static void stbtt__csctx_close_shape(stbtt__csctx *ctx) +{ + if (ctx->first_x != ctx->x || ctx->first_y != ctx->y) + stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->first_x, (int)ctx->first_y, 0, 0, 0, 0); +} + +static void stbtt__csctx_rmove_to(stbtt__csctx *ctx, float dx, float dy) +{ + stbtt__csctx_close_shape(ctx); + ctx->first_x = ctx->x = ctx->x + dx; + ctx->first_y = ctx->y = ctx->y + dy; + stbtt__csctx_v(ctx, STBTT_vmove, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0); +} + +static void stbtt__csctx_rline_to(stbtt__csctx *ctx, float dx, float dy) +{ + ctx->x += dx; + ctx->y += dy; + stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0); +} + +static void stbtt__csctx_rccurve_to(stbtt__csctx *ctx, float dx1, float dy1, float dx2, float dy2, float dx3, float dy3) +{ + float cx1 = ctx->x + dx1; + float cy1 = ctx->y + dy1; + float cx2 = cx1 + dx2; + float cy2 = cy1 + dy2; + ctx->x = cx2 + dx3; + ctx->y = cy2 + dy3; + stbtt__csctx_v(ctx, STBTT_vcubic, (int)ctx->x, (int)ctx->y, (int)cx1, (int)cy1, (int)cx2, (int)cy2); +} + +static stbtt__buf stbtt__get_subr(stbtt__buf idx, int n) +{ + int count = stbtt__cff_index_count(&idx); + int bias = 107; + if (count >= 33900) + bias = 32768; + else if (count >= 1240) + bias = 1131; + n += bias; + if (n < 0 || n >= count) + return stbtt__new_buf(NULL, 0); + return stbtt__cff_index_get(idx, n); +} + +static stbtt__buf stbtt__cid_get_glyph_subrs(const stbtt_fontinfo *info, int glyph_index) +{ + stbtt__buf fdselect = info->fdselect; + int nranges, start, end, v, fmt, fdselector = -1, i; + + stbtt__buf_seek(&fdselect, 0); + fmt = stbtt__buf_get8(&fdselect); + if (fmt == 0) { + // untested + stbtt__buf_skip(&fdselect, glyph_index); + fdselector = stbtt__buf_get8(&fdselect); + } else if (fmt == 3) { + nranges = stbtt__buf_get16(&fdselect); + start = stbtt__buf_get16(&fdselect); + for (i = 0; i < nranges; i++) { + v = stbtt__buf_get8(&fdselect); + end = stbtt__buf_get16(&fdselect); + if (glyph_index >= start && glyph_index < end) { + fdselector = v; + break; + } + start = end; + } + } + if (fdselector == -1) stbtt__new_buf(NULL, 0); + return stbtt__get_subrs(info->cff, stbtt__cff_index_get(info->fontdicts, fdselector)); +} + +static int stbtt__run_charstring(const stbtt_fontinfo *info, int glyph_index, stbtt__csctx *c) +{ + int in_header = 1, maskbits = 0, subr_stack_height = 0, sp = 0, v, i, b0; + int has_subrs = 0, clear_stack; + float s[48]; + stbtt__buf subr_stack[10], subrs = info->subrs, b; + float f; + +#define STBTT__CSERR(s) (0) + + // this currently ignores the initial width value, which isn't needed if we have hmtx + b = stbtt__cff_index_get(info->charstrings, glyph_index); + while (b.cursor < b.size) { + i = 0; + clear_stack = 1; + b0 = stbtt__buf_get8(&b); + switch (b0) { + // @TODO implement hinting + case 0x13: // hintmask + case 0x14: // cntrmask + if (in_header) + maskbits += (sp / 2); // implicit "vstem" + in_header = 0; + stbtt__buf_skip(&b, (maskbits + 7) / 8); + break; + + case 0x01: // hstem + case 0x03: // vstem + case 0x12: // hstemhm + case 0x17: // vstemhm + maskbits += (sp / 2); + break; + + case 0x15: // rmoveto + in_header = 0; + if (sp < 2) return STBTT__CSERR("rmoveto stack"); + stbtt__csctx_rmove_to(c, s[sp-2], s[sp-1]); + break; + case 0x04: // vmoveto + in_header = 0; + if (sp < 1) return STBTT__CSERR("vmoveto stack"); + stbtt__csctx_rmove_to(c, 0, s[sp-1]); + break; + case 0x16: // hmoveto + in_header = 0; + if (sp < 1) return STBTT__CSERR("hmoveto stack"); + stbtt__csctx_rmove_to(c, s[sp-1], 0); + break; + + case 0x05: // rlineto + if (sp < 2) return STBTT__CSERR("rlineto stack"); + for (; i + 1 < sp; i += 2) + stbtt__csctx_rline_to(c, s[i], s[i+1]); + break; + + // hlineto/vlineto and vhcurveto/hvcurveto alternate horizontal and vertical + // starting from a different place. + + case 0x07: // vlineto + if (sp < 1) return STBTT__CSERR("vlineto stack"); + goto vlineto; + case 0x06: // hlineto + if (sp < 1) return STBTT__CSERR("hlineto stack"); + for (;;) { + if (i >= sp) break; + stbtt__csctx_rline_to(c, s[i], 0); + i++; + vlineto: + if (i >= sp) break; + stbtt__csctx_rline_to(c, 0, s[i]); + i++; + } + break; + + case 0x1F: // hvcurveto + if (sp < 4) return STBTT__CSERR("hvcurveto stack"); + goto hvcurveto; + case 0x1E: // vhcurveto + if (sp < 4) return STBTT__CSERR("vhcurveto stack"); + for (;;) { + if (i + 3 >= sp) break; + stbtt__csctx_rccurve_to(c, 0, s[i], s[i+1], s[i+2], s[i+3], (sp - i == 5) ? s[i + 4] : 0.0f); + i += 4; + hvcurveto: + if (i + 3 >= sp) break; + stbtt__csctx_rccurve_to(c, s[i], 0, s[i+1], s[i+2], (sp - i == 5) ? s[i+4] : 0.0f, s[i+3]); + i += 4; + } + break; + + case 0x08: // rrcurveto + if (sp < 6) return STBTT__CSERR("rcurveline stack"); + for (; i + 5 < sp; i += 6) + stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); + break; + + case 0x18: // rcurveline + if (sp < 8) return STBTT__CSERR("rcurveline stack"); + for (; i + 5 < sp - 2; i += 6) + stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); + if (i + 1 >= sp) return STBTT__CSERR("rcurveline stack"); + stbtt__csctx_rline_to(c, s[i], s[i+1]); + break; + + case 0x19: // rlinecurve + if (sp < 8) return STBTT__CSERR("rlinecurve stack"); + for (; i + 1 < sp - 6; i += 2) + stbtt__csctx_rline_to(c, s[i], s[i+1]); + if (i + 5 >= sp) return STBTT__CSERR("rlinecurve stack"); + stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); + break; + + case 0x1A: // vvcurveto + case 0x1B: // hhcurveto + if (sp < 4) return STBTT__CSERR("(vv|hh)curveto stack"); + f = 0.0; + if (sp & 1) { f = s[i]; i++; } + for (; i + 3 < sp; i += 4) { + if (b0 == 0x1B) + stbtt__csctx_rccurve_to(c, s[i], f, s[i+1], s[i+2], s[i+3], 0.0); + else + stbtt__csctx_rccurve_to(c, f, s[i], s[i+1], s[i+2], 0.0, s[i+3]); + f = 0.0; + } + break; + + case 0x0A: // callsubr + if (!has_subrs) { + if (info->fdselect.size) + subrs = stbtt__cid_get_glyph_subrs(info, glyph_index); + has_subrs = 1; + } + // fallthrough + case 0x1D: // callgsubr + if (sp < 1) return STBTT__CSERR("call(g|)subr stack"); + v = (int) s[--sp]; + if (subr_stack_height >= 10) return STBTT__CSERR("recursion limit"); + subr_stack[subr_stack_height++] = b; + b = stbtt__get_subr(b0 == 0x0A ? subrs : info->gsubrs, v); + if (b.size == 0) return STBTT__CSERR("subr not found"); + b.cursor = 0; + clear_stack = 0; + break; + + case 0x0B: // return + if (subr_stack_height <= 0) return STBTT__CSERR("return outside subr"); + b = subr_stack[--subr_stack_height]; + clear_stack = 0; + break; + + case 0x0E: // endchar + stbtt__csctx_close_shape(c); + return 1; + + case 0x0C: { // two-byte escape + float dx1, dx2, dx3, dx4, dx5, dx6, dy1, dy2, dy3, dy4, dy5, dy6; + float dx, dy; + int b1 = stbtt__buf_get8(&b); + switch (b1) { + // @TODO These "flex" implementations ignore the flex-depth and resolution, + // and always draw beziers. + case 0x22: // hflex + if (sp < 7) return STBTT__CSERR("hflex stack"); + dx1 = s[0]; + dx2 = s[1]; + dy2 = s[2]; + dx3 = s[3]; + dx4 = s[4]; + dx5 = s[5]; + dx6 = s[6]; + stbtt__csctx_rccurve_to(c, dx1, 0, dx2, dy2, dx3, 0); + stbtt__csctx_rccurve_to(c, dx4, 0, dx5, -dy2, dx6, 0); + break; + + case 0x23: // flex + if (sp < 13) return STBTT__CSERR("flex stack"); + dx1 = s[0]; + dy1 = s[1]; + dx2 = s[2]; + dy2 = s[3]; + dx3 = s[4]; + dy3 = s[5]; + dx4 = s[6]; + dy4 = s[7]; + dx5 = s[8]; + dy5 = s[9]; + dx6 = s[10]; + dy6 = s[11]; + //fd is s[12] + stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3); + stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6); + break; + + case 0x24: // hflex1 + if (sp < 9) return STBTT__CSERR("hflex1 stack"); + dx1 = s[0]; + dy1 = s[1]; + dx2 = s[2]; + dy2 = s[3]; + dx3 = s[4]; + dx4 = s[5]; + dx5 = s[6]; + dy5 = s[7]; + dx6 = s[8]; + stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, 0); + stbtt__csctx_rccurve_to(c, dx4, 0, dx5, dy5, dx6, -(dy1+dy2+dy5)); + break; + + case 0x25: // flex1 + if (sp < 11) return STBTT__CSERR("flex1 stack"); + dx1 = s[0]; + dy1 = s[1]; + dx2 = s[2]; + dy2 = s[3]; + dx3 = s[4]; + dy3 = s[5]; + dx4 = s[6]; + dy4 = s[7]; + dx5 = s[8]; + dy5 = s[9]; + dx6 = dy6 = s[10]; + dx = dx1+dx2+dx3+dx4+dx5; + dy = dy1+dy2+dy3+dy4+dy5; + if (STBTT_fabs(dx) > STBTT_fabs(dy)) + dy6 = -dy; + else + dx6 = -dx; + stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3); + stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6); + break; + + default: + return STBTT__CSERR("unimplemented"); + } + } break; + + default: + if (b0 != 255 && b0 != 28 && (b0 < 32 || b0 > 254)) + return STBTT__CSERR("reserved operator"); + + // push immediate + if (b0 == 255) { + f = (float)(stbtt_int32)stbtt__buf_get32(&b) / 0x10000; + } else { + stbtt__buf_skip(&b, -1); + f = (float)(stbtt_int16)stbtt__cff_int(&b); + } + if (sp >= 48) return STBTT__CSERR("push stack overflow"); + s[sp++] = f; + clear_stack = 0; + break; + } + if (clear_stack) sp = 0; + } + return STBTT__CSERR("no endchar"); + +#undef STBTT__CSERR +} + +static int stbtt__GetGlyphShapeT2(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +{ + // runs the charstring twice, once to count and once to output (to avoid realloc) + stbtt__csctx count_ctx = STBTT__CSCTX_INIT(1); + stbtt__csctx output_ctx = STBTT__CSCTX_INIT(0); + if (stbtt__run_charstring(info, glyph_index, &count_ctx)) { + *pvertices = (stbtt_vertex*)STBTT_malloc(count_ctx.num_vertices*sizeof(stbtt_vertex), info->userdata); + output_ctx.pvertices = *pvertices; + if (stbtt__run_charstring(info, glyph_index, &output_ctx)) { + STBTT_assert(output_ctx.num_vertices == count_ctx.num_vertices); + return output_ctx.num_vertices; + } + } + *pvertices = NULL; + return 0; +} + +static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) +{ + stbtt__csctx c = STBTT__CSCTX_INIT(1); + int r = stbtt__run_charstring(info, glyph_index, &c); + if (x0) *x0 = r ? c.min_x : 0; + if (y0) *y0 = r ? c.min_y : 0; + if (x1) *x1 = r ? c.max_x : 0; + if (y1) *y1 = r ? c.max_y : 0; + return r ? c.num_vertices : 0; +} + +STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +{ + if (!info->cff.size) + return stbtt__GetGlyphShapeTT(info, glyph_index, pvertices); + else + return stbtt__GetGlyphShapeT2(info, glyph_index, pvertices); +} + +STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing) +{ + stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34); + if (glyph_index < numOfLongHorMetrics) { + if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*glyph_index); + if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2); + } else { + if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1)); + if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics)); + } +} + +STBTT_DEF int stbtt_GetKerningTableLength(const stbtt_fontinfo *info) +{ + stbtt_uint8 *data = info->data + info->kern; + + // we only look at the first table. it must be 'horizontal' and format 0. + if (!info->kern) + return 0; + if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 + return 0; + if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format + return 0; + + return ttUSHORT(data+10); +} + +STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length) +{ + stbtt_uint8 *data = info->data + info->kern; + int k, length; + + // we only look at the first table. it must be 'horizontal' and format 0. + if (!info->kern) + return 0; + if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 + return 0; + if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format + return 0; + + length = ttUSHORT(data+10); + if (table_length < length) + length = table_length; + + for (k = 0; k < length; k++) + { + table[k].glyph1 = ttUSHORT(data+18+(k*6)); + table[k].glyph2 = ttUSHORT(data+20+(k*6)); + table[k].advance = ttSHORT(data+22+(k*6)); + } + + return length; +} + +static int stbtt__GetGlyphKernInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) +{ + stbtt_uint8 *data = info->data + info->kern; + stbtt_uint32 needle, straw; + int l, r, m; + + // we only look at the first table. it must be 'horizontal' and format 0. + if (!info->kern) + return 0; + if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 + return 0; + if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format + return 0; + + l = 0; + r = ttUSHORT(data+10) - 1; + needle = glyph1 << 16 | glyph2; + while (l <= r) { + m = (l + r) >> 1; + straw = ttULONG(data+18+(m*6)); // note: unaligned read + if (needle < straw) + r = m - 1; + else if (needle > straw) + l = m + 1; + else + return ttSHORT(data+22+(m*6)); + } + return 0; +} + +static stbtt_int32 stbtt__GetCoverageIndex(stbtt_uint8 *coverageTable, int glyph) +{ + stbtt_uint16 coverageFormat = ttUSHORT(coverageTable); + switch(coverageFormat) { + case 1: { + stbtt_uint16 glyphCount = ttUSHORT(coverageTable + 2); + + // Binary search. + stbtt_int32 l=0, r=glyphCount-1, m; + int straw, needle=glyph; + while (l <= r) { + stbtt_uint8 *glyphArray = coverageTable + 4; + stbtt_uint16 glyphID; + m = (l + r) >> 1; + glyphID = ttUSHORT(glyphArray + 2 * m); + straw = glyphID; + if (needle < straw) + r = m - 1; + else if (needle > straw) + l = m + 1; + else { + return m; + } + } + } break; + + case 2: { + stbtt_uint16 rangeCount = ttUSHORT(coverageTable + 2); + stbtt_uint8 *rangeArray = coverageTable + 4; + + // Binary search. + stbtt_int32 l=0, r=rangeCount-1, m; + int strawStart, strawEnd, needle=glyph; + while (l <= r) { + stbtt_uint8 *rangeRecord; + m = (l + r) >> 1; + rangeRecord = rangeArray + 6 * m; + strawStart = ttUSHORT(rangeRecord); + strawEnd = ttUSHORT(rangeRecord + 2); + if (needle < strawStart) + r = m - 1; + else if (needle > strawEnd) + l = m + 1; + else { + stbtt_uint16 startCoverageIndex = ttUSHORT(rangeRecord + 4); + return startCoverageIndex + glyph - strawStart; + } + } + } break; + + default: { + // There are no other cases. + STBTT_assert(0); + } break; + } + + return -1; +} + +static stbtt_int32 stbtt__GetGlyphClass(stbtt_uint8 *classDefTable, int glyph) +{ + stbtt_uint16 classDefFormat = ttUSHORT(classDefTable); + switch(classDefFormat) + { + case 1: { + stbtt_uint16 startGlyphID = ttUSHORT(classDefTable + 2); + stbtt_uint16 glyphCount = ttUSHORT(classDefTable + 4); + stbtt_uint8 *classDef1ValueArray = classDefTable + 6; + + if (glyph >= startGlyphID && glyph < startGlyphID + glyphCount) + return (stbtt_int32)ttUSHORT(classDef1ValueArray + 2 * (glyph - startGlyphID)); + + classDefTable = classDef1ValueArray + 2 * glyphCount; + } break; + + case 2: { + stbtt_uint16 classRangeCount = ttUSHORT(classDefTable + 2); + stbtt_uint8 *classRangeRecords = classDefTable + 4; + + // Binary search. + stbtt_int32 l=0, r=classRangeCount-1, m; + int strawStart, strawEnd, needle=glyph; + while (l <= r) { + stbtt_uint8 *classRangeRecord; + m = (l + r) >> 1; + classRangeRecord = classRangeRecords + 6 * m; + strawStart = ttUSHORT(classRangeRecord); + strawEnd = ttUSHORT(classRangeRecord + 2); + if (needle < strawStart) + r = m - 1; + else if (needle > strawEnd) + l = m + 1; + else + return (stbtt_int32)ttUSHORT(classRangeRecord + 4); + } + + classDefTable = classRangeRecords + 6 * classRangeCount; + } break; + + default: { + // There are no other cases. + STBTT_assert(0); + } break; + } + + return -1; +} + +// Define to STBTT_assert(x) if you want to break on unimplemented formats. +#define STBTT_GPOS_TODO_assert(x) + +static stbtt_int32 stbtt__GetGlyphGPOSInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) +{ + stbtt_uint16 lookupListOffset; + stbtt_uint8 *lookupList; + stbtt_uint16 lookupCount; + stbtt_uint8 *data; + stbtt_int32 i; + + if (!info->gpos) return 0; + + data = info->data + info->gpos; + + if (ttUSHORT(data+0) != 1) return 0; // Major version 1 + if (ttUSHORT(data+2) != 0) return 0; // Minor version 0 + + lookupListOffset = ttUSHORT(data+8); + lookupList = data + lookupListOffset; + lookupCount = ttUSHORT(lookupList); + + for (i=0; i> 1; + pairValue = pairValueArray + (2 + valueRecordPairSizeInBytes) * m; + secondGlyph = ttUSHORT(pairValue); + straw = secondGlyph; + if (needle < straw) + r = m - 1; + else if (needle > straw) + l = m + 1; + else { + stbtt_int16 xAdvance = ttSHORT(pairValue + 2); + return xAdvance; + } + } + } break; + + case 2: { + stbtt_uint16 valueFormat1 = ttUSHORT(table + 4); + stbtt_uint16 valueFormat2 = ttUSHORT(table + 6); + + stbtt_uint16 classDef1Offset = ttUSHORT(table + 8); + stbtt_uint16 classDef2Offset = ttUSHORT(table + 10); + int glyph1class = stbtt__GetGlyphClass(table + classDef1Offset, glyph1); + int glyph2class = stbtt__GetGlyphClass(table + classDef2Offset, glyph2); + + stbtt_uint16 class1Count = ttUSHORT(table + 12); + stbtt_uint16 class2Count = ttUSHORT(table + 14); + STBTT_assert(glyph1class < class1Count); + STBTT_assert(glyph2class < class2Count); + + // TODO: Support more formats. + STBTT_GPOS_TODO_assert(valueFormat1 == 4); + if (valueFormat1 != 4) return 0; + STBTT_GPOS_TODO_assert(valueFormat2 == 0); + if (valueFormat2 != 0) return 0; + + if (glyph1class >= 0 && glyph1class < class1Count && glyph2class >= 0 && glyph2class < class2Count) { + stbtt_uint8 *class1Records = table + 16; + stbtt_uint8 *class2Records = class1Records + 2 * (glyph1class * class2Count); + stbtt_int16 xAdvance = ttSHORT(class2Records + 2 * glyph2class); + return xAdvance; + } + } break; + + default: { + // There are no other cases. + STBTT_assert(0); + break; + }; + } + } + break; + }; + + default: + // TODO: Implement other stuff. + break; + } + } + + return 0; +} + +STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int g1, int g2) +{ + int xAdvance = 0; + + if (info->gpos) + xAdvance += stbtt__GetGlyphGPOSInfoAdvance(info, g1, g2); + else if (info->kern) + xAdvance += stbtt__GetGlyphKernInfoAdvance(info, g1, g2); + + return xAdvance; +} + +STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2) +{ + if (!info->kern && !info->gpos) // if no kerning table, don't waste time looking up both codepoint->glyphs + return 0; + return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2)); +} + +STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing) +{ + stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing); +} + +STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap) +{ + if (ascent ) *ascent = ttSHORT(info->data+info->hhea + 4); + if (descent) *descent = ttSHORT(info->data+info->hhea + 6); + if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8); +} + +STBTT_DEF int stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap) +{ + int tab = stbtt__find_table(info->data, info->fontstart, "OS/2"); + if (!tab) + return 0; + if (typoAscent ) *typoAscent = ttSHORT(info->data+tab + 68); + if (typoDescent) *typoDescent = ttSHORT(info->data+tab + 70); + if (typoLineGap) *typoLineGap = ttSHORT(info->data+tab + 72); + return 1; +} + +STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1) +{ + *x0 = ttSHORT(info->data + info->head + 36); + *y0 = ttSHORT(info->data + info->head + 38); + *x1 = ttSHORT(info->data + info->head + 40); + *y1 = ttSHORT(info->data + info->head + 42); +} + +STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height) +{ + int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6); + return (float) height / fheight; +} + +STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels) +{ + int unitsPerEm = ttUSHORT(info->data + info->head + 18); + return pixels / unitsPerEm; +} + +STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v) +{ + STBTT_free(v, info->userdata); +} + +STBTT_DEF stbtt_uint8 *stbtt_FindSVGDoc(const stbtt_fontinfo *info, int gl) +{ + int i; + stbtt_uint8 *data = info->data; + stbtt_uint8 *svg_doc_list = data + stbtt__get_svg((stbtt_fontinfo *) info); + + int numEntries = ttUSHORT(svg_doc_list); + stbtt_uint8 *svg_docs = svg_doc_list + 2; + + for(i=0; i= ttUSHORT(svg_doc)) && (gl <= ttUSHORT(svg_doc + 2))) + return svg_doc; + } + return 0; +} + +STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg) +{ + stbtt_uint8 *data = info->data; + stbtt_uint8 *svg_doc; + + if (info->svg == 0) + return 0; + + svg_doc = stbtt_FindSVGDoc(info, gl); + if (svg_doc != NULL) { + *svg = (char *) data + info->svg + ttULONG(svg_doc + 4); + return ttULONG(svg_doc + 8); + } else { + return 0; + } +} + +STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg) +{ + return stbtt_GetGlyphSVG(info, stbtt_FindGlyphIndex(info, unicode_codepoint), svg); +} + +////////////////////////////////////////////////////////////////////////////// +// +// antialiasing software rasterizer +// + +STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + int x0=0,y0=0,x1,y1; // =0 suppresses compiler warning + if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) { + // e.g. space character + if (ix0) *ix0 = 0; + if (iy0) *iy0 = 0; + if (ix1) *ix1 = 0; + if (iy1) *iy1 = 0; + } else { + // move to integral bboxes (treating pixels as little squares, what pixels get touched)? + if (ix0) *ix0 = STBTT_ifloor( x0 * scale_x + shift_x); + if (iy0) *iy0 = STBTT_ifloor(-y1 * scale_y + shift_y); + if (ix1) *ix1 = STBTT_iceil ( x1 * scale_x + shift_x); + if (iy1) *iy1 = STBTT_iceil (-y0 * scale_y + shift_y); + } +} + +STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1); +} + +STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetGlyphBitmapBoxSubpixel(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y,shift_x,shift_y, ix0,iy0,ix1,iy1); +} + +STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y,0.0f,0.0f, ix0,iy0,ix1,iy1); +} + +////////////////////////////////////////////////////////////////////////////// +// +// Rasterizer + +typedef struct stbtt__hheap_chunk +{ + struct stbtt__hheap_chunk *next; +} stbtt__hheap_chunk; + +typedef struct stbtt__hheap +{ + struct stbtt__hheap_chunk *head; + void *first_free; + int num_remaining_in_head_chunk; +} stbtt__hheap; + +static void *stbtt__hheap_alloc(stbtt__hheap *hh, size_t size, void *userdata) +{ + if (hh->first_free) { + void *p = hh->first_free; + hh->first_free = * (void **) p; + return p; + } else { + if (hh->num_remaining_in_head_chunk == 0) { + int count = (size < 32 ? 2000 : size < 128 ? 800 : 100); + stbtt__hheap_chunk *c = (stbtt__hheap_chunk *) STBTT_malloc(sizeof(stbtt__hheap_chunk) + size * count, userdata); + if (c == NULL) + return NULL; + c->next = hh->head; + hh->head = c; + hh->num_remaining_in_head_chunk = count; + } + --hh->num_remaining_in_head_chunk; + return (char *) (hh->head) + sizeof(stbtt__hheap_chunk) + size * hh->num_remaining_in_head_chunk; + } +} + +static void stbtt__hheap_free(stbtt__hheap *hh, void *p) +{ + *(void **) p = hh->first_free; + hh->first_free = p; +} + +static void stbtt__hheap_cleanup(stbtt__hheap *hh, void *userdata) +{ + stbtt__hheap_chunk *c = hh->head; + while (c) { + stbtt__hheap_chunk *n = c->next; + STBTT_free(c, userdata); + c = n; + } +} + +typedef struct stbtt__edge { + float x0,y0, x1,y1; + int invert; +} stbtt__edge; + + +typedef struct stbtt__active_edge +{ + struct stbtt__active_edge *next; + #if STBTT_RASTERIZER_VERSION==1 + int x,dx; + float ey; + int direction; + #elif STBTT_RASTERIZER_VERSION==2 + float fx,fdx,fdy; + float direction; + float sy; + float ey; + #else + #error "Unrecognized value of STBTT_RASTERIZER_VERSION" + #endif +} stbtt__active_edge; + +#if STBTT_RASTERIZER_VERSION == 1 +#define STBTT_FIXSHIFT 10 +#define STBTT_FIX (1 << STBTT_FIXSHIFT) +#define STBTT_FIXMASK (STBTT_FIX-1) + +static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) +{ + stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); + float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); + STBTT_assert(z != NULL); + if (!z) return z; + + // round dx down to avoid overshooting + if (dxdy < 0) + z->dx = -STBTT_ifloor(STBTT_FIX * -dxdy); + else + z->dx = STBTT_ifloor(STBTT_FIX * dxdy); + + z->x = STBTT_ifloor(STBTT_FIX * e->x0 + z->dx * (start_point - e->y0)); // use z->dx so when we offset later it's by the same amount + z->x -= off_x * STBTT_FIX; + + z->ey = e->y1; + z->next = 0; + z->direction = e->invert ? 1 : -1; + return z; +} +#elif STBTT_RASTERIZER_VERSION == 2 +static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) +{ + stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); + float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); + STBTT_assert(z != NULL); + //STBTT_assert(e->y0 <= start_point); + if (!z) return z; + z->fdx = dxdy; + z->fdy = dxdy != 0.0f ? (1.0f/dxdy) : 0.0f; + z->fx = e->x0 + dxdy * (start_point - e->y0); + z->fx -= off_x; + z->direction = e->invert ? 1.0f : -1.0f; + z->sy = e->y0; + z->ey = e->y1; + z->next = 0; + return z; +} +#else +#error "Unrecognized value of STBTT_RASTERIZER_VERSION" +#endif + +#if STBTT_RASTERIZER_VERSION == 1 +// note: this routine clips fills that extend off the edges... ideally this +// wouldn't happen, but it could happen if the truetype glyph bounding boxes +// are wrong, or if the user supplies a too-small bitmap +static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight) +{ + // non-zero winding fill + int x0=0, w=0; + + while (e) { + if (w == 0) { + // if we're currently at zero, we need to record the edge start point + x0 = e->x; w += e->direction; + } else { + int x1 = e->x; w += e->direction; + // if we went to zero, we need to draw + if (w == 0) { + int i = x0 >> STBTT_FIXSHIFT; + int j = x1 >> STBTT_FIXSHIFT; + + if (i < len && j >= 0) { + if (i == j) { + // x0,x1 are the same pixel, so compute combined coverage + scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> STBTT_FIXSHIFT); + } else { + if (i >= 0) // add antialiasing for x0 + scanline[i] = scanline[i] + (stbtt_uint8) (((STBTT_FIX - (x0 & STBTT_FIXMASK)) * max_weight) >> STBTT_FIXSHIFT); + else + i = -1; // clip + + if (j < len) // add antialiasing for x1 + scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & STBTT_FIXMASK) * max_weight) >> STBTT_FIXSHIFT); + else + j = len; // clip + + for (++i; i < j; ++i) // fill pixels between x0 and x1 + scanline[i] = scanline[i] + (stbtt_uint8) max_weight; + } + } + } + } + + e = e->next; + } +} + +static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) +{ + stbtt__hheap hh = { 0, 0, 0 }; + stbtt__active_edge *active = NULL; + int y,j=0; + int max_weight = (255 / vsubsample); // weight per vertical scanline + int s; // vertical subsample index + unsigned char scanline_data[512], *scanline; + + if (result->w > 512) + scanline = (unsigned char *) STBTT_malloc(result->w, userdata); + else + scanline = scanline_data; + + y = off_y * vsubsample; + e[n].y0 = (off_y + result->h) * (float) vsubsample + 1; + + while (j < result->h) { + STBTT_memset(scanline, 0, result->w); + for (s=0; s < vsubsample; ++s) { + // find center of pixel for this scanline + float scan_y = y + 0.5f; + stbtt__active_edge **step = &active; + + // update all active edges; + // remove all active edges that terminate before the center of this scanline + while (*step) { + stbtt__active_edge * z = *step; + if (z->ey <= scan_y) { + *step = z->next; // delete from list + STBTT_assert(z->direction); + z->direction = 0; + stbtt__hheap_free(&hh, z); + } else { + z->x += z->dx; // advance to position for current scanline + step = &((*step)->next); // advance through list + } + } + + // resort the list if needed + for(;;) { + int changed=0; + step = &active; + while (*step && (*step)->next) { + if ((*step)->x > (*step)->next->x) { + stbtt__active_edge *t = *step; + stbtt__active_edge *q = t->next; + + t->next = q->next; + q->next = t; + *step = q; + changed = 1; + } + step = &(*step)->next; + } + if (!changed) break; + } + + // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline + while (e->y0 <= scan_y) { + if (e->y1 > scan_y) { + stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y, userdata); + if (z != NULL) { + // find insertion point + if (active == NULL) + active = z; + else if (z->x < active->x) { + // insert at front + z->next = active; + active = z; + } else { + // find thing to insert AFTER + stbtt__active_edge *p = active; + while (p->next && p->next->x < z->x) + p = p->next; + // at this point, p->next->x is NOT < z->x + z->next = p->next; + p->next = z; + } + } + } + ++e; + } + + // now process all active edges in XOR fashion + if (active) + stbtt__fill_active_edges(scanline, result->w, active, max_weight); + + ++y; + } + STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w); + ++j; + } + + stbtt__hheap_cleanup(&hh, userdata); + + if (scanline != scanline_data) + STBTT_free(scanline, userdata); +} + +#elif STBTT_RASTERIZER_VERSION == 2 + +// the edge passed in here does not cross the vertical line at x or the vertical line at x+1 +// (i.e. it has already been clipped to those) +static void stbtt__handle_clipped_edge(float *scanline, int x, stbtt__active_edge *e, float x0, float y0, float x1, float y1) +{ + if (y0 == y1) return; + STBTT_assert(y0 < y1); + STBTT_assert(e->sy <= e->ey); + if (y0 > e->ey) return; + if (y1 < e->sy) return; + if (y0 < e->sy) { + x0 += (x1-x0) * (e->sy - y0) / (y1-y0); + y0 = e->sy; + } + if (y1 > e->ey) { + x1 += (x1-x0) * (e->ey - y1) / (y1-y0); + y1 = e->ey; + } + + if (x0 == x) + STBTT_assert(x1 <= x+1); + else if (x0 == x+1) + STBTT_assert(x1 >= x); + else if (x0 <= x) + STBTT_assert(x1 <= x); + else if (x0 >= x+1) + STBTT_assert(x1 >= x+1); + else + STBTT_assert(x1 >= x && x1 <= x+1); + + if (x0 <= x && x1 <= x) + scanline[x] += e->direction * (y1-y0); + else if (x0 >= x+1 && x1 >= x+1) + ; + else { + STBTT_assert(x0 >= x && x0 <= x+1 && x1 >= x && x1 <= x+1); + scanline[x] += e->direction * (y1-y0) * (1-((x0-x)+(x1-x))/2); // coverage = 1 - average x position + } +} + +static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, stbtt__active_edge *e, float y_top) +{ + float y_bottom = y_top+1; + + while (e) { + // brute force every pixel + + // compute intersection points with top & bottom + STBTT_assert(e->ey >= y_top); + + if (e->fdx == 0) { + float x0 = e->fx; + if (x0 < len) { + if (x0 >= 0) { + stbtt__handle_clipped_edge(scanline,(int) x0,e, x0,y_top, x0,y_bottom); + stbtt__handle_clipped_edge(scanline_fill-1,(int) x0+1,e, x0,y_top, x0,y_bottom); + } else { + stbtt__handle_clipped_edge(scanline_fill-1,0,e, x0,y_top, x0,y_bottom); + } + } + } else { + float x0 = e->fx; + float dx = e->fdx; + float xb = x0 + dx; + float x_top, x_bottom; + float sy0,sy1; + float dy = e->fdy; + STBTT_assert(e->sy <= y_bottom && e->ey >= y_top); + + // compute endpoints of line segment clipped to this scanline (if the + // line segment starts on this scanline. x0 is the intersection of the + // line with y_top, but that may be off the line segment. + if (e->sy > y_top) { + x_top = x0 + dx * (e->sy - y_top); + sy0 = e->sy; + } else { + x_top = x0; + sy0 = y_top; + } + if (e->ey < y_bottom) { + x_bottom = x0 + dx * (e->ey - y_top); + sy1 = e->ey; + } else { + x_bottom = xb; + sy1 = y_bottom; + } + + if (x_top >= 0 && x_bottom >= 0 && x_top < len && x_bottom < len) { + // from here on, we don't have to range check x values + + if ((int) x_top == (int) x_bottom) { + float height; + // simple case, only spans one pixel + int x = (int) x_top; + height = sy1 - sy0; + STBTT_assert(x >= 0 && x < len); + scanline[x] += e->direction * (1-((x_top - x) + (x_bottom-x))/2) * height; + scanline_fill[x] += e->direction * height; // everything right of this pixel is filled + } else { + int x,x1,x2; + float y_crossing, step, sign, area; + // covers 2+ pixels + if (x_top > x_bottom) { + // flip scanline vertically; signed area is the same + float t; + sy0 = y_bottom - (sy0 - y_top); + sy1 = y_bottom - (sy1 - y_top); + t = sy0, sy0 = sy1, sy1 = t; + t = x_bottom, x_bottom = x_top, x_top = t; + dx = -dx; + dy = -dy; + t = x0, x0 = xb, xb = t; + } + + x1 = (int) x_top; + x2 = (int) x_bottom; + // compute intersection with y axis at x1+1 + y_crossing = (x1+1 - x0) * dy + y_top; + + sign = e->direction; + // area of the rectangle covered from y0..y_crossing + area = sign * (y_crossing-sy0); + // area of the triangle (x_top,y0), (x+1,y0), (x+1,y_crossing) + scanline[x1] += area * (1-((x_top - x1)+(x1+1-x1))/2); + + step = sign * dy; + for (x = x1+1; x < x2; ++x) { + scanline[x] += area + step/2; + area += step; + } + y_crossing += dy * (x2 - (x1+1)); + + STBTT_assert(STBTT_fabs(area) <= 1.01f); + + scanline[x2] += area + sign * (1-((x2-x2)+(x_bottom-x2))/2) * (sy1-y_crossing); + + scanline_fill[x2] += sign * (sy1-sy0); + } + } else { + // if edge goes outside of box we're drawing, we require + // clipping logic. since this does not match the intended use + // of this library, we use a different, very slow brute + // force implementation + int x; + for (x=0; x < len; ++x) { + // cases: + // + // there can be up to two intersections with the pixel. any intersection + // with left or right edges can be handled by splitting into two (or three) + // regions. intersections with top & bottom do not necessitate case-wise logic. + // + // the old way of doing this found the intersections with the left & right edges, + // then used some simple logic to produce up to three segments in sorted order + // from top-to-bottom. however, this had a problem: if an x edge was epsilon + // across the x border, then the corresponding y position might not be distinct + // from the other y segment, and it might ignored as an empty segment. to avoid + // that, we need to explicitly produce segments based on x positions. + + // rename variables to clearly-defined pairs + float y0 = y_top; + float x1 = (float) (x); + float x2 = (float) (x+1); + float x3 = xb; + float y3 = y_bottom; + + // x = e->x + e->dx * (y-y_top) + // (y-y_top) = (x - e->x) / e->dx + // y = (x - e->x) / e->dx + y_top + float y1 = (x - x0) / dx + y_top; + float y2 = (x+1 - x0) / dx + y_top; + + if (x0 < x1 && x3 > x2) { // three segments descending down-right + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else if (x3 < x1 && x0 > x2) { // three segments descending down-left + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); + } else if (x0 < x1 && x3 > x1) { // two segments across x, down-right + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); + } else if (x3 < x1 && x0 > x1) { // two segments across x, down-left + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); + } else if (x0 < x2 && x3 > x2) { // two segments across x+1, down-right + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else if (x3 < x2 && x0 > x2) { // two segments across x+1, down-left + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else { // one segment + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x3,y3); + } + } + } + } + e = e->next; + } +} + +// directly AA rasterize edges w/o supersampling +static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) +{ + stbtt__hheap hh = { 0, 0, 0 }; + stbtt__active_edge *active = NULL; + int y,j=0, i; + float scanline_data[129], *scanline, *scanline2; + + STBTT__NOTUSED(vsubsample); + + if (result->w > 64) + scanline = (float *) STBTT_malloc((result->w*2+1) * sizeof(float), userdata); + else + scanline = scanline_data; + + scanline2 = scanline + result->w; + + y = off_y; + e[n].y0 = (float) (off_y + result->h) + 1; + + while (j < result->h) { + // find center of pixel for this scanline + float scan_y_top = y + 0.0f; + float scan_y_bottom = y + 1.0f; + stbtt__active_edge **step = &active; + + STBTT_memset(scanline , 0, result->w*sizeof(scanline[0])); + STBTT_memset(scanline2, 0, (result->w+1)*sizeof(scanline[0])); + + // update all active edges; + // remove all active edges that terminate before the top of this scanline + while (*step) { + stbtt__active_edge * z = *step; + if (z->ey <= scan_y_top) { + *step = z->next; // delete from list + STBTT_assert(z->direction); + z->direction = 0; + stbtt__hheap_free(&hh, z); + } else { + step = &((*step)->next); // advance through list + } + } + + // insert all edges that start before the bottom of this scanline + while (e->y0 <= scan_y_bottom) { + if (e->y0 != e->y1) { + stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y_top, userdata); + if (z != NULL) { + if (j == 0 && off_y != 0) { + if (z->ey < scan_y_top) { + // this can happen due to subpixel positioning and some kind of fp rounding error i think + z->ey = scan_y_top; + } + } + STBTT_assert(z->ey >= scan_y_top); // if we get really unlucky a tiny bit of an edge can be out of bounds + // insert at front + z->next = active; + active = z; + } + } + ++e; + } + + // now process all active edges + if (active) + stbtt__fill_active_edges_new(scanline, scanline2+1, result->w, active, scan_y_top); + + { + float sum = 0; + for (i=0; i < result->w; ++i) { + float k; + int m; + sum += scanline2[i]; + k = scanline[i] + sum; + k = (float) STBTT_fabs(k)*255 + 0.5f; + m = (int) k; + if (m > 255) m = 255; + result->pixels[j*result->stride + i] = (unsigned char) m; + } + } + // advance all the edges + step = &active; + while (*step) { + stbtt__active_edge *z = *step; + z->fx += z->fdx; // advance to position for current scanline + step = &((*step)->next); // advance through list + } + + ++y; + ++j; + } + + stbtt__hheap_cleanup(&hh, userdata); + + if (scanline != scanline_data) + STBTT_free(scanline, userdata); +} +#else +#error "Unrecognized value of STBTT_RASTERIZER_VERSION" +#endif + +#define STBTT__COMPARE(a,b) ((a)->y0 < (b)->y0) + +static void stbtt__sort_edges_ins_sort(stbtt__edge *p, int n) +{ + int i,j; + for (i=1; i < n; ++i) { + stbtt__edge t = p[i], *a = &t; + j = i; + while (j > 0) { + stbtt__edge *b = &p[j-1]; + int c = STBTT__COMPARE(a,b); + if (!c) break; + p[j] = p[j-1]; + --j; + } + if (i != j) + p[j] = t; + } +} + +static void stbtt__sort_edges_quicksort(stbtt__edge *p, int n) +{ + /* threshold for transitioning to insertion sort */ + while (n > 12) { + stbtt__edge t; + int c01,c12,c,m,i,j; + + /* compute median of three */ + m = n >> 1; + c01 = STBTT__COMPARE(&p[0],&p[m]); + c12 = STBTT__COMPARE(&p[m],&p[n-1]); + /* if 0 >= mid >= end, or 0 < mid < end, then use mid */ + if (c01 != c12) { + /* otherwise, we'll need to swap something else to middle */ + int z; + c = STBTT__COMPARE(&p[0],&p[n-1]); + /* 0>mid && midn => n; 0 0 */ + /* 0n: 0>n => 0; 0 n */ + z = (c == c12) ? 0 : n-1; + t = p[z]; + p[z] = p[m]; + p[m] = t; + } + /* now p[m] is the median-of-three */ + /* swap it to the beginning so it won't move around */ + t = p[0]; + p[0] = p[m]; + p[m] = t; + + /* partition loop */ + i=1; + j=n-1; + for(;;) { + /* handling of equality is crucial here */ + /* for sentinels & efficiency with duplicates */ + for (;;++i) { + if (!STBTT__COMPARE(&p[i], &p[0])) break; + } + for (;;--j) { + if (!STBTT__COMPARE(&p[0], &p[j])) break; + } + /* make sure we haven't crossed */ + if (i >= j) break; + t = p[i]; + p[i] = p[j]; + p[j] = t; + + ++i; + --j; + } + /* recurse on smaller side, iterate on larger */ + if (j < (n-i)) { + stbtt__sort_edges_quicksort(p,j); + p = p+i; + n = n-i; + } else { + stbtt__sort_edges_quicksort(p+i, n-i); + n = j; + } + } +} + +static void stbtt__sort_edges(stbtt__edge *p, int n) +{ + stbtt__sort_edges_quicksort(p, n); + stbtt__sort_edges_ins_sort(p, n); +} + +typedef struct +{ + float x,y; +} stbtt__point; + +static void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, float shift_x, float shift_y, int off_x, int off_y, int invert, void *userdata) +{ + float y_scale_inv = invert ? -scale_y : scale_y; + stbtt__edge *e; + int n,i,j,k,m; +#if STBTT_RASTERIZER_VERSION == 1 + int vsubsample = result->h < 8 ? 15 : 5; +#elif STBTT_RASTERIZER_VERSION == 2 + int vsubsample = 1; +#else + #error "Unrecognized value of STBTT_RASTERIZER_VERSION" +#endif + // vsubsample should divide 255 evenly; otherwise we won't reach full opacity + + // now we have to blow out the windings into explicit edge lists + n = 0; + for (i=0; i < windings; ++i) + n += wcount[i]; + + e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); // add an extra one as a sentinel + if (e == 0) return; + n = 0; + + m=0; + for (i=0; i < windings; ++i) { + stbtt__point *p = pts + m; + m += wcount[i]; + j = wcount[i]-1; + for (k=0; k < wcount[i]; j=k++) { + int a=k,b=j; + // skip the edge if horizontal + if (p[j].y == p[k].y) + continue; + // add edge from j to k to the list + e[n].invert = 0; + if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) { + e[n].invert = 1; + a=j,b=k; + } + e[n].x0 = p[a].x * scale_x + shift_x; + e[n].y0 = (p[a].y * y_scale_inv + shift_y) * vsubsample; + e[n].x1 = p[b].x * scale_x + shift_x; + e[n].y1 = (p[b].y * y_scale_inv + shift_y) * vsubsample; + ++n; + } + } + + // now sort the edges by their highest point (should snap to integer, and then by x) + //STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare); + stbtt__sort_edges(e, n); + + // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule + stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata); + + STBTT_free(e, userdata); +} + +static void stbtt__add_point(stbtt__point *points, int n, float x, float y) +{ + if (!points) return; // during first pass, it's unallocated + points[n].x = x; + points[n].y = y; +} + +// tessellate until threshold p is happy... @TODO warped to compensate for non-linear stretching +static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n) +{ + // midpoint + float mx = (x0 + 2*x1 + x2)/4; + float my = (y0 + 2*y1 + y2)/4; + // versus directly drawn line + float dx = (x0+x2)/2 - mx; + float dy = (y0+y2)/2 - my; + if (n > 16) // 65536 segments on one curve better be enough! + return 1; + if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA + stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1); + stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1); + } else { + stbtt__add_point(points, *num_points,x2,y2); + *num_points = *num_points+1; + } + return 1; +} + +static void stbtt__tesselate_cubic(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3, float objspace_flatness_squared, int n) +{ + // @TODO this "flatness" calculation is just made-up nonsense that seems to work well enough + float dx0 = x1-x0; + float dy0 = y1-y0; + float dx1 = x2-x1; + float dy1 = y2-y1; + float dx2 = x3-x2; + float dy2 = y3-y2; + float dx = x3-x0; + float dy = y3-y0; + float longlen = (float) (STBTT_sqrt(dx0*dx0+dy0*dy0)+STBTT_sqrt(dx1*dx1+dy1*dy1)+STBTT_sqrt(dx2*dx2+dy2*dy2)); + float shortlen = (float) STBTT_sqrt(dx*dx+dy*dy); + float flatness_squared = longlen*longlen-shortlen*shortlen; + + if (n > 16) // 65536 segments on one curve better be enough! + return; + + if (flatness_squared > objspace_flatness_squared) { + float x01 = (x0+x1)/2; + float y01 = (y0+y1)/2; + float x12 = (x1+x2)/2; + float y12 = (y1+y2)/2; + float x23 = (x2+x3)/2; + float y23 = (y2+y3)/2; + + float xa = (x01+x12)/2; + float ya = (y01+y12)/2; + float xb = (x12+x23)/2; + float yb = (y12+y23)/2; + + float mx = (xa+xb)/2; + float my = (ya+yb)/2; + + stbtt__tesselate_cubic(points, num_points, x0,y0, x01,y01, xa,ya, mx,my, objspace_flatness_squared,n+1); + stbtt__tesselate_cubic(points, num_points, mx,my, xb,yb, x23,y23, x3,y3, objspace_flatness_squared,n+1); + } else { + stbtt__add_point(points, *num_points,x3,y3); + *num_points = *num_points+1; + } +} + +// returns number of contours +static stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata) +{ + stbtt__point *points=0; + int num_points=0; + + float objspace_flatness_squared = objspace_flatness * objspace_flatness; + int i,n=0,start=0, pass; + + // count how many "moves" there are to get the contour count + for (i=0; i < num_verts; ++i) + if (vertices[i].type == STBTT_vmove) + ++n; + + *num_contours = n; + if (n == 0) return 0; + + *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata); + + if (*contour_lengths == 0) { + *num_contours = 0; + return 0; + } + + // make two passes through the points so we don't need to realloc + for (pass=0; pass < 2; ++pass) { + float x=0,y=0; + if (pass == 1) { + points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata); + if (points == NULL) goto error; + } + num_points = 0; + n= -1; + for (i=0; i < num_verts; ++i) { + switch (vertices[i].type) { + case STBTT_vmove: + // start the next contour + if (n >= 0) + (*contour_lengths)[n] = num_points - start; + ++n; + start = num_points; + + x = vertices[i].x, y = vertices[i].y; + stbtt__add_point(points, num_points++, x,y); + break; + case STBTT_vline: + x = vertices[i].x, y = vertices[i].y; + stbtt__add_point(points, num_points++, x, y); + break; + case STBTT_vcurve: + stbtt__tesselate_curve(points, &num_points, x,y, + vertices[i].cx, vertices[i].cy, + vertices[i].x, vertices[i].y, + objspace_flatness_squared, 0); + x = vertices[i].x, y = vertices[i].y; + break; + case STBTT_vcubic: + stbtt__tesselate_cubic(points, &num_points, x,y, + vertices[i].cx, vertices[i].cy, + vertices[i].cx1, vertices[i].cy1, + vertices[i].x, vertices[i].y, + objspace_flatness_squared, 0); + x = vertices[i].x, y = vertices[i].y; + break; + } + } + (*contour_lengths)[n] = num_points - start; + } + + return points; +error: + STBTT_free(points, userdata); + STBTT_free(*contour_lengths, userdata); + *contour_lengths = 0; + *num_contours = 0; + return NULL; +} + +STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata) +{ + float scale = scale_x > scale_y ? scale_y : scale_x; + int winding_count = 0; + int *winding_lengths = NULL; + stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata); + if (windings) { + stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, userdata); + STBTT_free(winding_lengths, userdata); + STBTT_free(windings, userdata); + } +} + +STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata) +{ + STBTT_free(bitmap, userdata); +} + +STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff) +{ + int ix0,iy0,ix1,iy1; + stbtt__bitmap gbm; + stbtt_vertex *vertices; + int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); + + if (scale_x == 0) scale_x = scale_y; + if (scale_y == 0) { + if (scale_x == 0) { + STBTT_free(vertices, info->userdata); + return NULL; + } + scale_y = scale_x; + } + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,&ix1,&iy1); + + // now we get the size + gbm.w = (ix1 - ix0); + gbm.h = (iy1 - iy0); + gbm.pixels = NULL; // in case we error + + if (width ) *width = gbm.w; + if (height) *height = gbm.h; + if (xoff ) *xoff = ix0; + if (yoff ) *yoff = iy0; + + if (gbm.w && gbm.h) { + gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata); + if (gbm.pixels) { + gbm.stride = gbm.w; + + stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, 1, info->userdata); + } + } + STBTT_free(vertices, info->userdata); + return gbm.pixels; +} + +STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y, 0.0f, 0.0f, glyph, width, height, xoff, yoff); +} + +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph) +{ + int ix0,iy0; + stbtt_vertex *vertices; + int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); + stbtt__bitmap gbm; + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0); + gbm.pixels = output; + gbm.w = out_w; + gbm.h = out_h; + gbm.stride = out_stride; + + if (gbm.w && gbm.h) + stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0,iy0, 1, info->userdata); + + STBTT_free(vertices, info->userdata); +} + +STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph) +{ + stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, glyph); +} + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff); +} + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint) +{ + stbtt_MakeGlyphBitmapSubpixelPrefilter(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, oversample_x, oversample_y, sub_x, sub_y, stbtt_FindGlyphIndex(info,codepoint)); +} + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint) +{ + stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint)); +} + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff); +} + +STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint) +{ + stbtt_MakeCodepointBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, codepoint); +} + +////////////////////////////////////////////////////////////////////////////// +// +// bitmap baking +// +// This is SUPER-CRAPPY packing to keep source code small + +static int stbtt_BakeFontBitmap_internal(unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) + float pixel_height, // height of font in pixels + unsigned char *pixels, int pw, int ph, // bitmap to be filled in + int first_char, int num_chars, // characters to bake + stbtt_bakedchar *chardata) +{ + float scale; + int x,y,bottom_y, i; + stbtt_fontinfo f; + f.userdata = NULL; + if (!stbtt_InitFont(&f, data, offset)) + return -1; + STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels + x=y=1; + bottom_y = 1; + + scale = stbtt_ScaleForPixelHeight(&f, pixel_height); + + for (i=0; i < num_chars; ++i) { + int advance, lsb, x0,y0,x1,y1,gw,gh; + int g = stbtt_FindGlyphIndex(&f, first_char + i); + stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb); + stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1); + gw = x1-x0; + gh = y1-y0; + if (x + gw + 1 >= pw) + y = bottom_y, x = 1; // advance to next row + if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row + return -i; + STBTT_assert(x+gw < pw); + STBTT_assert(y+gh < ph); + stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g); + chardata[i].x0 = (stbtt_int16) x; + chardata[i].y0 = (stbtt_int16) y; + chardata[i].x1 = (stbtt_int16) (x + gw); + chardata[i].y1 = (stbtt_int16) (y + gh); + chardata[i].xadvance = scale * advance; + chardata[i].xoff = (float) x0; + chardata[i].yoff = (float) y0; + x = x + gw + 1; + if (y+gh+1 > bottom_y) + bottom_y = y+gh+1; + } + return bottom_y; +} + +STBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule) +{ + float d3d_bias = opengl_fillrule ? 0 : -0.5f; + float ipw = 1.0f / pw, iph = 1.0f / ph; + const stbtt_bakedchar *b = chardata + char_index; + int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5f); + int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5f); + + q->x0 = round_x + d3d_bias; + q->y0 = round_y + d3d_bias; + q->x1 = round_x + b->x1 - b->x0 + d3d_bias; + q->y1 = round_y + b->y1 - b->y0 + d3d_bias; + + q->s0 = b->x0 * ipw; + q->t0 = b->y0 * iph; + q->s1 = b->x1 * ipw; + q->t1 = b->y1 * iph; + + *xpos += b->xadvance; +} + +////////////////////////////////////////////////////////////////////////////// +// +// rectangle packing replacement routines if you don't have stb_rect_pack.h +// + +#ifndef STB_RECT_PACK_VERSION + +typedef int stbrp_coord; + +//////////////////////////////////////////////////////////////////////////////////// +// // +// // +// COMPILER WARNING ?!?!? // +// // +// // +// if you get a compile warning due to these symbols being defined more than // +// once, move #include "stb_rect_pack.h" before #include "stb_truetype.h" // +// // +//////////////////////////////////////////////////////////////////////////////////// + +typedef struct +{ + int width,height; + int x,y,bottom_y; +} stbrp_context; + +typedef struct +{ + unsigned char x; +} stbrp_node; + +struct stbrp_rect +{ + stbrp_coord x,y; + int id,w,h,was_packed; +}; + +static void stbrp_init_target(stbrp_context *con, int pw, int ph, stbrp_node *nodes, int num_nodes) +{ + con->width = pw; + con->height = ph; + con->x = 0; + con->y = 0; + con->bottom_y = 0; + STBTT__NOTUSED(nodes); + STBTT__NOTUSED(num_nodes); +} + +static void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects) +{ + int i; + for (i=0; i < num_rects; ++i) { + if (con->x + rects[i].w > con->width) { + con->x = 0; + con->y = con->bottom_y; + } + if (con->y + rects[i].h > con->height) + break; + rects[i].x = con->x; + rects[i].y = con->y; + rects[i].was_packed = 1; + con->x += rects[i].w; + if (con->y + rects[i].h > con->bottom_y) + con->bottom_y = con->y + rects[i].h; + } + for ( ; i < num_rects; ++i) + rects[i].was_packed = 0; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// bitmap baking +// +// This is SUPER-AWESOME (tm Ryan Gordon) packing using stb_rect_pack.h. If +// stb_rect_pack.h isn't available, it uses the BakeFontBitmap strategy. + +STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int pw, int ph, int stride_in_bytes, int padding, void *alloc_context) +{ + stbrp_context *context = (stbrp_context *) STBTT_malloc(sizeof(*context) ,alloc_context); + int num_nodes = pw - padding; + stbrp_node *nodes = (stbrp_node *) STBTT_malloc(sizeof(*nodes ) * num_nodes,alloc_context); + + if (context == NULL || nodes == NULL) { + if (context != NULL) STBTT_free(context, alloc_context); + if (nodes != NULL) STBTT_free(nodes , alloc_context); + return 0; + } + + spc->user_allocator_context = alloc_context; + spc->width = pw; + spc->height = ph; + spc->pixels = pixels; + spc->pack_info = context; + spc->nodes = nodes; + spc->padding = padding; + spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw; + spc->h_oversample = 1; + spc->v_oversample = 1; + spc->skip_missing = 0; + + stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes); + + if (pixels) + STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels + + return 1; +} + +STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc) +{ + STBTT_free(spc->nodes , spc->user_allocator_context); + STBTT_free(spc->pack_info, spc->user_allocator_context); +} + +STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample) +{ + STBTT_assert(h_oversample <= STBTT_MAX_OVERSAMPLE); + STBTT_assert(v_oversample <= STBTT_MAX_OVERSAMPLE); + if (h_oversample <= STBTT_MAX_OVERSAMPLE) + spc->h_oversample = h_oversample; + if (v_oversample <= STBTT_MAX_OVERSAMPLE) + spc->v_oversample = v_oversample; +} + +STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip) +{ + spc->skip_missing = skip; +} + +#define STBTT__OVER_MASK (STBTT_MAX_OVERSAMPLE-1) + +static void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) +{ + unsigned char buffer[STBTT_MAX_OVERSAMPLE]; + int safe_w = w - kernel_width; + int j; + STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze + for (j=0; j < h; ++j) { + int i; + unsigned int total; + STBTT_memset(buffer, 0, kernel_width); + + total = 0; + + // make kernel_width a constant in common cases so compiler can optimize out the divide + switch (kernel_width) { + case 2: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 2); + } + break; + case 3: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 3); + } + break; + case 4: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 4); + } + break; + case 5: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 5); + } + break; + default: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / kernel_width); + } + break; + } + + for (; i < w; ++i) { + STBTT_assert(pixels[i] == 0); + total -= buffer[i & STBTT__OVER_MASK]; + pixels[i] = (unsigned char) (total / kernel_width); + } + + pixels += stride_in_bytes; + } +} + +static void stbtt__v_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) +{ + unsigned char buffer[STBTT_MAX_OVERSAMPLE]; + int safe_h = h - kernel_width; + int j; + STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze + for (j=0; j < w; ++j) { + int i; + unsigned int total; + STBTT_memset(buffer, 0, kernel_width); + + total = 0; + + // make kernel_width a constant in common cases so compiler can optimize out the divide + switch (kernel_width) { + case 2: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 2); + } + break; + case 3: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 3); + } + break; + case 4: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 4); + } + break; + case 5: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 5); + } + break; + default: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); + } + break; + } + + for (; i < h; ++i) { + STBTT_assert(pixels[i*stride_in_bytes] == 0); + total -= buffer[i & STBTT__OVER_MASK]; + pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); + } + + pixels += 1; + } +} + +static float stbtt__oversample_shift(int oversample) +{ + if (!oversample) + return 0.0f; + + // The prefilter is a box filter of width "oversample", + // which shifts phase by (oversample - 1)/2 pixels in + // oversampled space. We want to shift in the opposite + // direction to counter this. + return (float)-(oversample - 1) / (2.0f * (float)oversample); +} + +// rects array must be big enough to accommodate all characters in the given ranges +STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) +{ + int i,j,k; + int missing_glyph_added = 0; + + k=0; + for (i=0; i < num_ranges; ++i) { + float fh = ranges[i].font_size; + float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); + ranges[i].h_oversample = (unsigned char) spc->h_oversample; + ranges[i].v_oversample = (unsigned char) spc->v_oversample; + for (j=0; j < ranges[i].num_chars; ++j) { + int x0,y0,x1,y1; + int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; + int glyph = stbtt_FindGlyphIndex(info, codepoint); + if (glyph == 0 && (spc->skip_missing || missing_glyph_added)) { + rects[k].w = rects[k].h = 0; + } else { + stbtt_GetGlyphBitmapBoxSubpixel(info,glyph, + scale * spc->h_oversample, + scale * spc->v_oversample, + 0,0, + &x0,&y0,&x1,&y1); + rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1); + rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1); + if (glyph == 0) + missing_glyph_added = 1; + } + ++k; + } + } + + return k; +} + +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int prefilter_x, int prefilter_y, float *sub_x, float *sub_y, int glyph) +{ + stbtt_MakeGlyphBitmapSubpixel(info, + output, + out_w - (prefilter_x - 1), + out_h - (prefilter_y - 1), + out_stride, + scale_x, + scale_y, + shift_x, + shift_y, + glyph); + + if (prefilter_x > 1) + stbtt__h_prefilter(output, out_w, out_h, out_stride, prefilter_x); + + if (prefilter_y > 1) + stbtt__v_prefilter(output, out_w, out_h, out_stride, prefilter_y); + + *sub_x = stbtt__oversample_shift(prefilter_x); + *sub_y = stbtt__oversample_shift(prefilter_y); +} + +// rects array must be big enough to accommodate all characters in the given ranges +STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) +{ + int i,j,k, missing_glyph = -1, return_value = 1; + + // save current values + int old_h_over = spc->h_oversample; + int old_v_over = spc->v_oversample; + + k = 0; + for (i=0; i < num_ranges; ++i) { + float fh = ranges[i].font_size; + float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); + float recip_h,recip_v,sub_x,sub_y; + spc->h_oversample = ranges[i].h_oversample; + spc->v_oversample = ranges[i].v_oversample; + recip_h = 1.0f / spc->h_oversample; + recip_v = 1.0f / spc->v_oversample; + sub_x = stbtt__oversample_shift(spc->h_oversample); + sub_y = stbtt__oversample_shift(spc->v_oversample); + for (j=0; j < ranges[i].num_chars; ++j) { + stbrp_rect *r = &rects[k]; + if (r->was_packed && r->w != 0 && r->h != 0) { + stbtt_packedchar *bc = &ranges[i].chardata_for_range[j]; + int advance, lsb, x0,y0,x1,y1; + int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; + int glyph = stbtt_FindGlyphIndex(info, codepoint); + stbrp_coord pad = (stbrp_coord) spc->padding; + + // pad on left and top + r->x += pad; + r->y += pad; + r->w -= pad; + r->h -= pad; + stbtt_GetGlyphHMetrics(info, glyph, &advance, &lsb); + stbtt_GetGlyphBitmapBox(info, glyph, + scale * spc->h_oversample, + scale * spc->v_oversample, + &x0,&y0,&x1,&y1); + stbtt_MakeGlyphBitmapSubpixel(info, + spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w - spc->h_oversample+1, + r->h - spc->v_oversample+1, + spc->stride_in_bytes, + scale * spc->h_oversample, + scale * spc->v_oversample, + 0,0, + glyph); + + if (spc->h_oversample > 1) + stbtt__h_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w, r->h, spc->stride_in_bytes, + spc->h_oversample); + + if (spc->v_oversample > 1) + stbtt__v_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w, r->h, spc->stride_in_bytes, + spc->v_oversample); + + bc->x0 = (stbtt_int16) r->x; + bc->y0 = (stbtt_int16) r->y; + bc->x1 = (stbtt_int16) (r->x + r->w); + bc->y1 = (stbtt_int16) (r->y + r->h); + bc->xadvance = scale * advance; + bc->xoff = (float) x0 * recip_h + sub_x; + bc->yoff = (float) y0 * recip_v + sub_y; + bc->xoff2 = (x0 + r->w) * recip_h + sub_x; + bc->yoff2 = (y0 + r->h) * recip_v + sub_y; + + if (glyph == 0) + missing_glyph = j; + } else if (spc->skip_missing) { + return_value = 0; + } else if (r->was_packed && r->w == 0 && r->h == 0 && missing_glyph >= 0) { + ranges[i].chardata_for_range[j] = ranges[i].chardata_for_range[missing_glyph]; + } else { + return_value = 0; // if any fail, report failure + } + + ++k; + } + } + + // restore original values + spc->h_oversample = old_h_over; + spc->v_oversample = old_v_over; + + return return_value; +} + +STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects) +{ + stbrp_pack_rects((stbrp_context *) spc->pack_info, rects, num_rects); +} + +STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges) +{ + stbtt_fontinfo info; + int i,j,n, return_value = 1; + //stbrp_context *context = (stbrp_context *) spc->pack_info; + stbrp_rect *rects; + + // flag all characters as NOT packed + for (i=0; i < num_ranges; ++i) + for (j=0; j < ranges[i].num_chars; ++j) + ranges[i].chardata_for_range[j].x0 = + ranges[i].chardata_for_range[j].y0 = + ranges[i].chardata_for_range[j].x1 = + ranges[i].chardata_for_range[j].y1 = 0; + + n = 0; + for (i=0; i < num_ranges; ++i) + n += ranges[i].num_chars; + + rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context); + if (rects == NULL) + return 0; + + info.userdata = spc->user_allocator_context; + stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata,font_index)); + + n = stbtt_PackFontRangesGatherRects(spc, &info, ranges, num_ranges, rects); + + stbtt_PackFontRangesPackRects(spc, rects, n); + + return_value = stbtt_PackFontRangesRenderIntoRects(spc, &info, ranges, num_ranges, rects); + + STBTT_free(rects, spc->user_allocator_context); + return return_value; +} + +STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size, + int first_unicode_codepoint_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range) +{ + stbtt_pack_range range; + range.first_unicode_codepoint_in_range = first_unicode_codepoint_in_range; + range.array_of_unicode_codepoints = NULL; + range.num_chars = num_chars_in_range; + range.chardata_for_range = chardata_for_range; + range.font_size = font_size; + return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1); +} + +STBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap) +{ + int i_ascent, i_descent, i_lineGap; + float scale; + stbtt_fontinfo info; + stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata, index)); + scale = size > 0 ? stbtt_ScaleForPixelHeight(&info, size) : stbtt_ScaleForMappingEmToPixels(&info, -size); + stbtt_GetFontVMetrics(&info, &i_ascent, &i_descent, &i_lineGap); + *ascent = (float) i_ascent * scale; + *descent = (float) i_descent * scale; + *lineGap = (float) i_lineGap * scale; +} + +STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer) +{ + float ipw = 1.0f / pw, iph = 1.0f / ph; + const stbtt_packedchar *b = chardata + char_index; + + if (align_to_integer) { + float x = (float) STBTT_ifloor((*xpos + b->xoff) + 0.5f); + float y = (float) STBTT_ifloor((*ypos + b->yoff) + 0.5f); + q->x0 = x; + q->y0 = y; + q->x1 = x + b->xoff2 - b->xoff; + q->y1 = y + b->yoff2 - b->yoff; + } else { + q->x0 = *xpos + b->xoff; + q->y0 = *ypos + b->yoff; + q->x1 = *xpos + b->xoff2; + q->y1 = *ypos + b->yoff2; + } + + q->s0 = b->x0 * ipw; + q->t0 = b->y0 * iph; + q->s1 = b->x1 * ipw; + q->t1 = b->y1 * iph; + + *xpos += b->xadvance; +} + +////////////////////////////////////////////////////////////////////////////// +// +// sdf computation +// + +#define STBTT_min(a,b) ((a) < (b) ? (a) : (b)) +#define STBTT_max(a,b) ((a) < (b) ? (b) : (a)) + +static int stbtt__ray_intersect_bezier(float orig[2], float ray[2], float q0[2], float q1[2], float q2[2], float hits[2][2]) +{ + float q0perp = q0[1]*ray[0] - q0[0]*ray[1]; + float q1perp = q1[1]*ray[0] - q1[0]*ray[1]; + float q2perp = q2[1]*ray[0] - q2[0]*ray[1]; + float roperp = orig[1]*ray[0] - orig[0]*ray[1]; + + float a = q0perp - 2*q1perp + q2perp; + float b = q1perp - q0perp; + float c = q0perp - roperp; + + float s0 = 0., s1 = 0.; + int num_s = 0; + + if (a != 0.0) { + float discr = b*b - a*c; + if (discr > 0.0) { + float rcpna = -1 / a; + float d = (float) STBTT_sqrt(discr); + s0 = (b+d) * rcpna; + s1 = (b-d) * rcpna; + if (s0 >= 0.0 && s0 <= 1.0) + num_s = 1; + if (d > 0.0 && s1 >= 0.0 && s1 <= 1.0) { + if (num_s == 0) s0 = s1; + ++num_s; + } + } + } else { + // 2*b*s + c = 0 + // s = -c / (2*b) + s0 = c / (-2 * b); + if (s0 >= 0.0 && s0 <= 1.0) + num_s = 1; + } + + if (num_s == 0) + return 0; + else { + float rcp_len2 = 1 / (ray[0]*ray[0] + ray[1]*ray[1]); + float rayn_x = ray[0] * rcp_len2, rayn_y = ray[1] * rcp_len2; + + float q0d = q0[0]*rayn_x + q0[1]*rayn_y; + float q1d = q1[0]*rayn_x + q1[1]*rayn_y; + float q2d = q2[0]*rayn_x + q2[1]*rayn_y; + float rod = orig[0]*rayn_x + orig[1]*rayn_y; + + float q10d = q1d - q0d; + float q20d = q2d - q0d; + float q0rd = q0d - rod; + + hits[0][0] = q0rd + s0*(2.0f - 2.0f*s0)*q10d + s0*s0*q20d; + hits[0][1] = a*s0+b; + + if (num_s > 1) { + hits[1][0] = q0rd + s1*(2.0f - 2.0f*s1)*q10d + s1*s1*q20d; + hits[1][1] = a*s1+b; + return 2; + } else { + return 1; + } + } +} + +static int equal(float *a, float *b) +{ + return (a[0] == b[0] && a[1] == b[1]); +} + +static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex *verts) +{ + int i; + float orig[2], ray[2] = { 1, 0 }; + float y_frac; + int winding = 0; + + orig[0] = x; + orig[1] = y; + + // make sure y never passes through a vertex of the shape + y_frac = (float) STBTT_fmod(y, 1.0f); + if (y_frac < 0.01f) + y += 0.01f; + else if (y_frac > 0.99f) + y -= 0.01f; + orig[1] = y; + + // test a ray from (-infinity,y) to (x,y) + for (i=0; i < nverts; ++i) { + if (verts[i].type == STBTT_vline) { + int x0 = (int) verts[i-1].x, y0 = (int) verts[i-1].y; + int x1 = (int) verts[i ].x, y1 = (int) verts[i ].y; + if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { + float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; + if (x_inter < x) + winding += (y0 < y1) ? 1 : -1; + } + } + if (verts[i].type == STBTT_vcurve) { + int x0 = (int) verts[i-1].x , y0 = (int) verts[i-1].y ; + int x1 = (int) verts[i ].cx, y1 = (int) verts[i ].cy; + int x2 = (int) verts[i ].x , y2 = (int) verts[i ].y ; + int ax = STBTT_min(x0,STBTT_min(x1,x2)), ay = STBTT_min(y0,STBTT_min(y1,y2)); + int by = STBTT_max(y0,STBTT_max(y1,y2)); + if (y > ay && y < by && x > ax) { + float q0[2],q1[2],q2[2]; + float hits[2][2]; + q0[0] = (float)x0; + q0[1] = (float)y0; + q1[0] = (float)x1; + q1[1] = (float)y1; + q2[0] = (float)x2; + q2[1] = (float)y2; + if (equal(q0,q1) || equal(q1,q2)) { + x0 = (int)verts[i-1].x; + y0 = (int)verts[i-1].y; + x1 = (int)verts[i ].x; + y1 = (int)verts[i ].y; + if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { + float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; + if (x_inter < x) + winding += (y0 < y1) ? 1 : -1; + } + } else { + int num_hits = stbtt__ray_intersect_bezier(orig, ray, q0, q1, q2, hits); + if (num_hits >= 1) + if (hits[0][0] < 0) + winding += (hits[0][1] < 0 ? -1 : 1); + if (num_hits >= 2) + if (hits[1][0] < 0) + winding += (hits[1][1] < 0 ? -1 : 1); + } + } + } + } + return winding; +} + +static float stbtt__cuberoot( float x ) +{ + if (x<0) + return -(float) STBTT_pow(-x,1.0f/3.0f); + else + return (float) STBTT_pow( x,1.0f/3.0f); +} + +// x^3 + c*x^2 + b*x + a = 0 +static int stbtt__solve_cubic(float a, float b, float c, float* r) +{ + float s = -a / 3; + float p = b - a*a / 3; + float q = a * (2*a*a - 9*b) / 27 + c; + float p3 = p*p*p; + float d = q*q + 4*p3 / 27; + if (d >= 0) { + float z = (float) STBTT_sqrt(d); + float u = (-q + z) / 2; + float v = (-q - z) / 2; + u = stbtt__cuberoot(u); + v = stbtt__cuberoot(v); + r[0] = s + u + v; + return 1; + } else { + float u = (float) STBTT_sqrt(-p/3); + float v = (float) STBTT_acos(-STBTT_sqrt(-27/p3) * q / 2) / 3; // p3 must be negative, since d is negative + float m = (float) STBTT_cos(v); + float n = (float) STBTT_cos(v-3.141592/2)*1.732050808f; + r[0] = s + u * 2 * m; + r[1] = s - u * (m + n); + r[2] = s - u * (m - n); + + //STBTT_assert( STBTT_fabs(((r[0]+a)*r[0]+b)*r[0]+c) < 0.05f); // these asserts may not be safe at all scales, though they're in bezier t parameter units so maybe? + //STBTT_assert( STBTT_fabs(((r[1]+a)*r[1]+b)*r[1]+c) < 0.05f); + //STBTT_assert( STBTT_fabs(((r[2]+a)*r[2]+b)*r[2]+c) < 0.05f); + return 3; + } +} + +STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff) +{ + float scale_x = scale, scale_y = scale; + int ix0,iy0,ix1,iy1; + int w,h; + unsigned char *data; + + if (scale == 0) return NULL; + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale, scale, 0.0f,0.0f, &ix0,&iy0,&ix1,&iy1); + + // if empty, return NULL + if (ix0 == ix1 || iy0 == iy1) + return NULL; + + ix0 -= padding; + iy0 -= padding; + ix1 += padding; + iy1 += padding; + + w = (ix1 - ix0); + h = (iy1 - iy0); + + if (width ) *width = w; + if (height) *height = h; + if (xoff ) *xoff = ix0; + if (yoff ) *yoff = iy0; + + // invert for y-downwards bitmaps + scale_y = -scale_y; + + { + int x,y,i,j; + float *precompute; + stbtt_vertex *verts; + int num_verts = stbtt_GetGlyphShape(info, glyph, &verts); + data = (unsigned char *) STBTT_malloc(w * h, info->userdata); + precompute = (float *) STBTT_malloc(num_verts * sizeof(float), info->userdata); + + for (i=0,j=num_verts-1; i < num_verts; j=i++) { + if (verts[i].type == STBTT_vline) { + float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y; + float x1 = verts[j].x*scale_x, y1 = verts[j].y*scale_y; + float dist = (float) STBTT_sqrt((x1-x0)*(x1-x0) + (y1-y0)*(y1-y0)); + precompute[i] = (dist == 0) ? 0.0f : 1.0f / dist; + } else if (verts[i].type == STBTT_vcurve) { + float x2 = verts[j].x *scale_x, y2 = verts[j].y *scale_y; + float x1 = verts[i].cx*scale_x, y1 = verts[i].cy*scale_y; + float x0 = verts[i].x *scale_x, y0 = verts[i].y *scale_y; + float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2; + float len2 = bx*bx + by*by; + if (len2 != 0.0f) + precompute[i] = 1.0f / (bx*bx + by*by); + else + precompute[i] = 0.0f; + } else + precompute[i] = 0.0f; + } + + for (y=iy0; y < iy1; ++y) { + for (x=ix0; x < ix1; ++x) { + float val; + float min_dist = 999999.0f; + float sx = (float) x + 0.5f; + float sy = (float) y + 0.5f; + float x_gspace = (sx / scale_x); + float y_gspace = (sy / scale_y); + + int winding = stbtt__compute_crossings_x(x_gspace, y_gspace, num_verts, verts); // @OPTIMIZE: this could just be a rasterization, but needs to be line vs. non-tesselated curves so a new path + + for (i=0; i < num_verts; ++i) { + float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y; + + // check against every point here rather than inside line/curve primitives -- @TODO: wrong if multiple 'moves' in a row produce a garbage point, and given culling, probably more efficient to do within line/curve + float dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy); + if (dist2 < min_dist*min_dist) + min_dist = (float) STBTT_sqrt(dist2); + + if (verts[i].type == STBTT_vline) { + float x1 = verts[i-1].x*scale_x, y1 = verts[i-1].y*scale_y; + + // coarse culling against bbox + //if (sx > STBTT_min(x0,x1)-min_dist && sx < STBTT_max(x0,x1)+min_dist && + // sy > STBTT_min(y0,y1)-min_dist && sy < STBTT_max(y0,y1)+min_dist) + float dist = (float) STBTT_fabs((x1-x0)*(y0-sy) - (y1-y0)*(x0-sx)) * precompute[i]; + STBTT_assert(i != 0); + if (dist < min_dist) { + // check position along line + // x' = x0 + t*(x1-x0), y' = y0 + t*(y1-y0) + // minimize (x'-sx)*(x'-sx)+(y'-sy)*(y'-sy) + float dx = x1-x0, dy = y1-y0; + float px = x0-sx, py = y0-sy; + // minimize (px+t*dx)^2 + (py+t*dy)^2 = px*px + 2*px*dx*t + t^2*dx*dx + py*py + 2*py*dy*t + t^2*dy*dy + // derivative: 2*px*dx + 2*py*dy + (2*dx*dx+2*dy*dy)*t, set to 0 and solve + float t = -(px*dx + py*dy) / (dx*dx + dy*dy); + if (t >= 0.0f && t <= 1.0f) + min_dist = dist; + } + } else if (verts[i].type == STBTT_vcurve) { + float x2 = verts[i-1].x *scale_x, y2 = verts[i-1].y *scale_y; + float x1 = verts[i ].cx*scale_x, y1 = verts[i ].cy*scale_y; + float box_x0 = STBTT_min(STBTT_min(x0,x1),x2); + float box_y0 = STBTT_min(STBTT_min(y0,y1),y2); + float box_x1 = STBTT_max(STBTT_max(x0,x1),x2); + float box_y1 = STBTT_max(STBTT_max(y0,y1),y2); + // coarse culling against bbox to avoid computing cubic unnecessarily + if (sx > box_x0-min_dist && sx < box_x1+min_dist && sy > box_y0-min_dist && sy < box_y1+min_dist) { + int num=0; + float ax = x1-x0, ay = y1-y0; + float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2; + float mx = x0 - sx, my = y0 - sy; + float res[3],px,py,t,it; + float a_inv = precompute[i]; + if (a_inv == 0.0) { // if a_inv is 0, it's 2nd degree so use quadratic formula + float a = 3*(ax*bx + ay*by); + float b = 2*(ax*ax + ay*ay) + (mx*bx+my*by); + float c = mx*ax+my*ay; + if (a == 0.0) { // if a is 0, it's linear + if (b != 0.0) { + res[num++] = -c/b; + } + } else { + float discriminant = b*b - 4*a*c; + if (discriminant < 0) + num = 0; + else { + float root = (float) STBTT_sqrt(discriminant); + res[0] = (-b - root)/(2*a); + res[1] = (-b + root)/(2*a); + num = 2; // don't bother distinguishing 1-solution case, as code below will still work + } + } + } else { + float b = 3*(ax*bx + ay*by) * a_inv; // could precompute this as it doesn't depend on sample point + float c = (2*(ax*ax + ay*ay) + (mx*bx+my*by)) * a_inv; + float d = (mx*ax+my*ay) * a_inv; + num = stbtt__solve_cubic(b, c, d, res); + } + if (num >= 1 && res[0] >= 0.0f && res[0] <= 1.0f) { + t = res[0], it = 1.0f - t; + px = it*it*x0 + 2*t*it*x1 + t*t*x2; + py = it*it*y0 + 2*t*it*y1 + t*t*y2; + dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); + if (dist2 < min_dist * min_dist) + min_dist = (float) STBTT_sqrt(dist2); + } + if (num >= 2 && res[1] >= 0.0f && res[1] <= 1.0f) { + t = res[1], it = 1.0f - t; + px = it*it*x0 + 2*t*it*x1 + t*t*x2; + py = it*it*y0 + 2*t*it*y1 + t*t*y2; + dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); + if (dist2 < min_dist * min_dist) + min_dist = (float) STBTT_sqrt(dist2); + } + if (num >= 3 && res[2] >= 0.0f && res[2] <= 1.0f) { + t = res[2], it = 1.0f - t; + px = it*it*x0 + 2*t*it*x1 + t*t*x2; + py = it*it*y0 + 2*t*it*y1 + t*t*y2; + dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); + if (dist2 < min_dist * min_dist) + min_dist = (float) STBTT_sqrt(dist2); + } + } + } + } + if (winding == 0) + min_dist = -min_dist; // if outside the shape, value is negative + val = onedge_value + pixel_dist_scale * min_dist; + if (val < 0) + val = 0; + else if (val > 255) + val = 255; + data[(y-iy0)*w+(x-ix0)] = (unsigned char) val; + } + } + STBTT_free(precompute, info->userdata); + STBTT_free(verts, info->userdata); + } + return data; +} + +STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphSDF(info, scale, stbtt_FindGlyphIndex(info, codepoint), padding, onedge_value, pixel_dist_scale, width, height, xoff, yoff); +} + +STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata) +{ + STBTT_free(bitmap, userdata); +} + +////////////////////////////////////////////////////////////////////////////// +// +// font name matching -- recommended not to use this +// + +// check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string +static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2) +{ + stbtt_int32 i=0; + + // convert utf16 to utf8 and compare the results while converting + while (len2) { + stbtt_uint16 ch = s2[0]*256 + s2[1]; + if (ch < 0x80) { + if (i >= len1) return -1; + if (s1[i++] != ch) return -1; + } else if (ch < 0x800) { + if (i+1 >= len1) return -1; + if (s1[i++] != 0xc0 + (ch >> 6)) return -1; + if (s1[i++] != 0x80 + (ch & 0x3f)) return -1; + } else if (ch >= 0xd800 && ch < 0xdc00) { + stbtt_uint32 c; + stbtt_uint16 ch2 = s2[2]*256 + s2[3]; + if (i+3 >= len1) return -1; + c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000; + if (s1[i++] != 0xf0 + (c >> 18)) return -1; + if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((c ) & 0x3f)) return -1; + s2 += 2; // plus another 2 below + len2 -= 2; + } else if (ch >= 0xdc00 && ch < 0xe000) { + return -1; + } else { + if (i+2 >= len1) return -1; + if (s1[i++] != 0xe0 + (ch >> 12)) return -1; + if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((ch ) & 0x3f)) return -1; + } + s2 += 2; + len2 -= 2; + } + return i; +} + +static int stbtt_CompareUTF8toUTF16_bigendian_internal(char *s1, int len1, char *s2, int len2) +{ + return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((stbtt_uint8*) s1, len1, (stbtt_uint8*) s2, len2); +} + +// returns results in whatever encoding you request... but note that 2-byte encodings +// will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare +STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID) +{ + stbtt_int32 i,count,stringOffset; + stbtt_uint8 *fc = font->data; + stbtt_uint32 offset = font->fontstart; + stbtt_uint32 nm = stbtt__find_table(fc, offset, "name"); + if (!nm) return NULL; + + count = ttUSHORT(fc+nm+2); + stringOffset = nm + ttUSHORT(fc+nm+4); + for (i=0; i < count; ++i) { + stbtt_uint32 loc = nm + 6 + 12 * i; + if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2) + && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) { + *length = ttUSHORT(fc+loc+8); + return (const char *) (fc+stringOffset+ttUSHORT(fc+loc+10)); + } + } + return NULL; +} + +static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id) +{ + stbtt_int32 i; + stbtt_int32 count = ttUSHORT(fc+nm+2); + stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4); + + for (i=0; i < count; ++i) { + stbtt_uint32 loc = nm + 6 + 12 * i; + stbtt_int32 id = ttUSHORT(fc+loc+6); + if (id == target_id) { + // find the encoding + stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4); + + // is this a Unicode encoding? + if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) { + stbtt_int32 slen = ttUSHORT(fc+loc+8); + stbtt_int32 off = ttUSHORT(fc+loc+10); + + // check if there's a prefix match + stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen); + if (matchlen >= 0) { + // check for target_id+1 immediately following, with same encoding & language + if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) { + slen = ttUSHORT(fc+loc+12+8); + off = ttUSHORT(fc+loc+12+10); + if (slen == 0) { + if (matchlen == nlen) + return 1; + } else if (matchlen < nlen && name[matchlen] == ' ') { + ++matchlen; + if (stbtt_CompareUTF8toUTF16_bigendian_internal((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen)) + return 1; + } + } else { + // if nothing immediately following + if (matchlen == nlen) + return 1; + } + } + } + + // @TODO handle other encodings + } + } + return 0; +} + +static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags) +{ + stbtt_int32 nlen = (stbtt_int32) STBTT_strlen((char *) name); + stbtt_uint32 nm,hd; + if (!stbtt__isfont(fc+offset)) return 0; + + // check italics/bold/underline flags in macStyle... + if (flags) { + hd = stbtt__find_table(fc, offset, "head"); + if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0; + } + + nm = stbtt__find_table(fc, offset, "name"); + if (!nm) return 0; + + if (flags) { + // if we checked the macStyle flags, then just check the family and ignore the subfamily + if (stbtt__matchpair(fc, nm, name, nlen, 16, -1)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 1, -1)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; + } else { + if (stbtt__matchpair(fc, nm, name, nlen, 16, 17)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 1, 2)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; + } + + return 0; +} + +static int stbtt_FindMatchingFont_internal(unsigned char *font_collection, char *name_utf8, stbtt_int32 flags) +{ + stbtt_int32 i; + for (i=0;;++i) { + stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i); + if (off < 0) return off; + if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags)) + return off; + } +} + +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif + +STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, + float pixel_height, unsigned char *pixels, int pw, int ph, + int first_char, int num_chars, stbtt_bakedchar *chardata) +{ + return stbtt_BakeFontBitmap_internal((unsigned char *) data, offset, pixel_height, pixels, pw, ph, first_char, num_chars, chardata); +} + +STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index) +{ + return stbtt_GetFontOffsetForIndex_internal((unsigned char *) data, index); +} + +STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data) +{ + return stbtt_GetNumberOfFonts_internal((unsigned char *) data); +} + +STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset) +{ + return stbtt_InitFont_internal(info, (unsigned char *) data, offset); +} + +STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags) +{ + return stbtt_FindMatchingFont_internal((unsigned char *) fontdata, (char *) name, flags); +} + +STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2) +{ + return stbtt_CompareUTF8toUTF16_bigendian_internal((char *) s1, len1, (char *) s2, len2); +} + +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic pop +#endif + +#endif // STB_TRUETYPE_IMPLEMENTATION + + +// FULL VERSION HISTORY +// +// 1.19 (2018-02-11) OpenType GPOS kerning (horizontal only), STBTT_fmod +// 1.18 (2018-01-29) add missing function +// 1.17 (2017-07-23) make more arguments const; doc fix +// 1.16 (2017-07-12) SDF support +// 1.15 (2017-03-03) make more arguments const +// 1.14 (2017-01-16) num-fonts-in-TTC function +// 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts +// 1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual +// 1.11 (2016-04-02) fix unused-variable warning +// 1.10 (2016-04-02) allow user-defined fabs() replacement +// fix memory leak if fontsize=0.0 +// fix warning from duplicate typedef +// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use alloc userdata for PackFontRanges +// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges +// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; +// allow PackFontRanges to pack and render in separate phases; +// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); +// fixed an assert() bug in the new rasterizer +// replace assert() with STBTT_assert() in new rasterizer +// 1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine) +// also more precise AA rasterizer, except if shapes overlap +// remove need for STBTT_sort +// 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC +// 1.04 (2015-04-15) typo in example +// 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes +// 1.02 (2014-12-10) fix various warnings & compile issues w/ stb_rect_pack, C++ +// 1.01 (2014-12-08) fix subpixel position when oversampling to exactly match +// non-oversampled; STBTT_POINT_SIZE for packed case only +// 1.00 (2014-12-06) add new PackBegin etc. API, w/ support for oversampling +// 0.99 (2014-09-18) fix multiple bugs with subpixel rendering (ryg) +// 0.9 (2014-08-07) support certain mac/iOS fonts without an MS platformID +// 0.8b (2014-07-07) fix a warning +// 0.8 (2014-05-25) fix a few more warnings +// 0.7 (2013-09-25) bugfix: subpixel glyph bug fixed in 0.5 had come back +// 0.6c (2012-07-24) improve documentation +// 0.6b (2012-07-20) fix a few more warnings +// 0.6 (2012-07-17) fix warnings; added stbtt_ScaleForMappingEmToPixels, +// stbtt_GetFontBoundingBox, stbtt_IsGlyphEmpty +// 0.5 (2011-12-09) bugfixes: +// subpixel glyph renderer computed wrong bounding box +// first vertex of shape can be off-curve (FreeSans) +// 0.4b (2011-12-03) fixed an error in the font baking example +// 0.4 (2011-12-01) kerning, subpixel rendering (tor) +// bugfixes for: +// codepoint-to-glyph conversion using table fmt=12 +// codepoint-to-glyph conversion using table fmt=4 +// stbtt_GetBakedQuad with non-square texture (Zer) +// updated Hello World! sample to use kerning and subpixel +// fixed some warnings +// 0.3 (2009-06-24) cmap fmt=12, compound shapes (MM) +// userdata, malloc-from-userdata, non-zero fill (stb) +// 0.2 (2009-03-11) Fix unsigned/signed char warnings +// 0.1 (2009-03-09) First public release +// + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ From patchwork Mon Jan 4 17:47:10 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11997213 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH,MAILING_LIST_MULTI, MENTIONS_GIT_HOSTING,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 437BAC4332B for ; Mon, 4 Jan 2021 17:49:44 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 216A020715 for ; Mon, 4 Jan 2021 17:49:44 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727530AbhADRtn (ORCPT ); Mon, 4 Jan 2021 12:49:43 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34500 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727475AbhADRtm (ORCPT ); Mon, 4 Jan 2021 12:49:42 -0500 Received: from mail-ej1-x62d.google.com (mail-ej1-x62d.google.com [IPv6:2a00:1450:4864:20::62d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 113AFC061384 for ; Mon, 4 Jan 2021 09:48:09 -0800 (PST) Received: by mail-ej1-x62d.google.com with SMTP id q22so37959648eja.2 for ; Mon, 04 Jan 2021 09:48:08 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=N18nqDVkTO7oT7I2PPf6G5+lJnx0aQoNQBe98h8pNrA=; b=SuQKydeYpLk7bn56pg7/YPjvbnVlKg7OBgXHJ1q6j5a/vu2hfndOMivz5PgWIyEfiD GsdXcLsQFBBzdDXY1El6Pw54e6QsqHzh8fqowu1QQ0NoSKFKOh5OLimCK2J/ovPvV1yM 1Jt+Goxx0VCQOc/fVD026Yse3yXM1qTy8A2PTT93KARldQrNkqvjpBRFN+den1AcQfn+ tOvXDJBLovpwLA+bpXyq6B4A2lVIi4cieaTRckaEBF1WVeTYaDmpuheA9eif+Wfdu+Fr FslZw6hJf9PfrM5uttTpaEMiZ1mc9A98IlNY5vbaekfaKU1ah1ayLvTgb7lnYi+ObAAb LunQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=N18nqDVkTO7oT7I2PPf6G5+lJnx0aQoNQBe98h8pNrA=; b=CKoMgGytJNnio8jQWfQhRZ2u5IOQLpKxe6PD90xT/UL4U4DDwdeq/OczhtSxm/Bp7I BFzrXvZNKFBpmNcc+SVPqk+rF2K6DQYOL81GFwOlytm5KyZXrnpv4lv5Hu7GJIOz8GwQ 0hHGA5bcimkcCl/3P/PATEHali11pfUEeRV4cMsOoeXtHX1SnguuE/Mfzx3Vwuj844Xr OWNOiVNg7rBMXm5VgoKv0pdmaUPCT5vhDgMul7QvgoJuy2umzBp4pXJewNAWH+x6Cjdb KAYXND/n+VjLkgQBCA+fJdSEv1sDihH4e4zhWeZZNz00z+ALNcStIPQ3Yy4iaSHmqhNZ 0JIA== X-Gm-Message-State: AOAM532nSmVhdhDQCdu50m1lh24SkW5aWR0ODCKvTW/eb0PRTTIvO6uL LDeoz+Tr8BXZt8CF+T4xfG8= X-Google-Smtp-Source: ABdhPJyU7+qSUfMN8JvSyEMQBu0cpmR1O2TzjImC5U2q0AL1bGrepErsmDu+P61pMw+On2haJ7wjGQ== X-Received: by 2002:a17:906:4e46:: with SMTP id g6mr48772668ejw.243.1609782487852; Mon, 04 Jan 2021 09:48:07 -0800 (PST) Received: from localhost.localdomain ([95.87.199.238]) by smtp.gmail.com with ESMTPSA id l14sm44107750edq.35.2021.01.04.09.48.07 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 04 Jan 2021 09:48:07 -0800 (PST) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v8 30/44] kernel-shark: Make GLUT optional dependency Date: Mon, 4 Jan 2021 19:47:10 +0200 Message-Id: <20210104174724.70404-31-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210104174724.70404-1-y.karadz@gmail.com> References: <20210104174724.70404-1-y.karadz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org GLUT is not needed in order to build the KernelShark GUI. Currently it is being used only by the plotting example, so it makes more sense to removed it from the list of compulsory third party packages. The patch optimizes the way the OpenGL and GLUT headers are included and removes some duplicated code. Signen-off-by: Yordan Karadzhov (VMware) --- build/deff.h.cmake | 3 +++ src/CMakeLists.txt | 4 ++-- src/libkshark-plot.c | 20 +++++++------------- src/libkshark-plot.h | 11 +++++++++++ 4 files changed, 23 insertions(+), 15 deletions(-) diff --git a/build/deff.h.cmake b/build/deff.h.cmake index e398c0c..868ffec 100644 --- a/build/deff.h.cmake +++ b/build/deff.h.cmake @@ -23,6 +23,9 @@ /** "pkexec" executable. */ #cmakedefine DO_AS_ROOT "@DO_AS_ROOT@" +/** GLUT has been found. */ +#cmakedefine GLUT_FOUND + /** Semicolon-separated list of plugin names. */ #define KS_BUILTIN_PLUGINS "@PLUGINS@" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cf3279e..fe440db 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -38,7 +38,7 @@ install(FILES "${KS_DIR}/src/libkshark.h" DESTINATION ${KS_INCLUDS_DESTINATION} COMPONENT libkshark-devel) -if (OPENGL_FOUND AND GLUT_FOUND) +if (OPENGL_FOUND) message(STATUS "libkshark-plot") add_library(kshark-plot SHARED libkshark-plot.c) @@ -61,7 +61,7 @@ if (OPENGL_FOUND AND GLUT_FOUND) ARCHIVE DESTINATION ${_LIBDIR} COMPONENT libkshark-devel) -endif (OPENGL_FOUND AND GLUT_FOUND) +endif (OPENGL_FOUND) if (Qt5Widgets_FOUND AND Qt5Network_FOUND) diff --git a/src/libkshark-plot.c b/src/libkshark-plot.c index 1c8ede4..c9a3530 100644 --- a/src/libkshark-plot.c +++ b/src/libkshark-plot.c @@ -19,10 +19,6 @@ #include #include -// OpenGL -#include -#include - // KernelShark #include "libkshark-plot.h" @@ -37,6 +33,10 @@ #define STBTT_STATIC #include "stb_truetype.h" +#ifdef GLUT_FOUND + +#include + /** * @brief Create an empty scene for drawing. * @@ -61,17 +61,11 @@ void ksplot_make_scene(int width, int height) /* Open the screen window. */ glutCreateWindow("KernelShark Plot"); - /* - * Set the origin of the coordinate system to be the top left corner. - * The "Y" coordinate is inverted. - */ - gluOrtho2D(0, width, height, 0); - glViewport(0, 0, width, height); - - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); + ksplot_resize_opengl(width, height); } +#endif // GLUT_FOUND + /** * @brief Initialize OpenGL. * diff --git a/src/libkshark-plot.h b/src/libkshark-plot.h index 0edf5d5..063740d 100644 --- a/src/libkshark-plot.h +++ b/src/libkshark-plot.h @@ -15,6 +15,10 @@ // C #include +// OpenGL +#include +#include + /* * STB TrueType (single-file public domain library) * https://github.com/nothings/stb @@ -25,6 +29,9 @@ extern "C" { #endif +// KernelShark +#include "KsCmakeDef.hpp" + /** Structure defining a RGB color. */ struct ksplot_color { /** The Red component of the color. */ @@ -46,8 +53,12 @@ struct ksplot_point { int y; }; +#ifdef GLUT_FOUND + void ksplot_make_scene(int width, int height); +#endif // GLUT_FOUND + void ksplot_init_opengl(int dpr); void ksplot_resize_opengl(int width, int height); From patchwork Mon Jan 4 17:47:11 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11997175 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9B49AC433E0 for ; Mon, 4 Jan 2021 17:49:19 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 75B2820770 for ; Mon, 4 Jan 2021 17:49:19 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726246AbhADRtT (ORCPT ); Mon, 4 Jan 2021 12:49:19 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34384 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726655AbhADRtS (ORCPT ); Mon, 4 Jan 2021 12:49:18 -0500 Received: from mail-ej1-x62c.google.com (mail-ej1-x62c.google.com [IPv6:2a00:1450:4864:20::62c]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 3E338C061386 for ; Mon, 4 Jan 2021 09:48:10 -0800 (PST) Received: by mail-ej1-x62c.google.com with SMTP id ce23so37888869ejb.8 for ; Mon, 04 Jan 2021 09:48:10 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=zKDG/9XZbnGW4hCDetg6B4UM+/yqlFKJwoWhpahy2EM=; b=CrgSuc7i0aePli35g6A+EOBip51HFsyA0gf2thI9CqPsddRH6mWeskG8gOe9Q+lDCe 3ia4BairaZkcE1FHnYwHkt9dse2FJxe/JFoot2OYVKREe7TQX3LPabiGCmQUgt3zTYym 0V8LzdWx/NYa4t6KJ1i9UsgLTtkTz2d1qdXcqbwziK0sTmueSM79CTc919Vger65tB5D RfLZOgy8i3yomVKWLLpA1jOQgV61bkWV4MhCn5sLlgRIJ8jhJYpwxMvJrXRv9nfS2XCT BUUMVwi71tgdsAcdysVApbNJIk1Y+sYSjZ1/+idOIN1jFHy8+na6qvAygmRacg6LjnSM Azzg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=zKDG/9XZbnGW4hCDetg6B4UM+/yqlFKJwoWhpahy2EM=; b=NOUigK1NLEBwWU6q/xqWqHXo1v5kYy6HyYxi+tl9l7HXTBWFdo2TcME+G3UsK59DH5 xRAvrokCXxLzpLKXMlsZ8dansOegiblF5GvPy2X9vYuKh3YFxYJ0/VrSAzy/q9CY8N5t wCNXRxDwI1/rlHh3IzQXpgJtSHsTxTZkWud/RazABmeh+MZj0C8EyCCJstH4ul5t0DR7 t9LUekCL5vVX7ol2RlnvmMD1akdG1z+0mp2ytQAVAzSyndxaQaTw0VylD6VBSVwMZqH7 Gytx/NgK7wpNuN+AeBh1z2Xjhc++B3SB1BcZGH60ho9guv9wv/ZUXfwvUU2wnuYSQ2nu AgTw== X-Gm-Message-State: AOAM530EkHLej3Afnnydcv4kXAHT43yJLc8dd703+WTqMUG5C3hw8bEN GahZ44CMllhGQf0t/fWPI70= X-Google-Smtp-Source: ABdhPJzVZAMK6qnegBjH7WQkOr7a2qHy6ZwhzLuWL4OpMgMiaUnMecA82yaUunEMLPJBAgg7G6YW9A== X-Received: by 2002:a17:906:4050:: with SMTP id y16mr65557260ejj.537.1609782488984; Mon, 04 Jan 2021 09:48:08 -0800 (PST) Received: from localhost.localdomain ([95.87.199.238]) by smtp.gmail.com with ESMTPSA id l14sm44107750edq.35.2021.01.04.09.48.07 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 04 Jan 2021 09:48:08 -0800 (PST) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v8 31/44] kernel-shark: Add ksplot_draw_polyline() Date: Mon, 4 Jan 2021 19:47:11 +0200 Message-Id: <20210104174724.70404-32-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210104174724.70404-1-y.karadz@gmail.com> References: <20210104174724.70404-1-y.karadz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org The method draws continuous line between an ordered array of points (poly-line). Signed-off-by: Yordan Karadzhov (VMware) --- src/libkshark-plot.c | 32 ++++++++++++++++++++++++++------ src/libkshark-plot.h | 5 +++++ 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/src/libkshark-plot.c b/src/libkshark-plot.c index c9a3530..597e5c2 100644 --- a/src/libkshark-plot.c +++ b/src/libkshark-plot.c @@ -151,6 +151,30 @@ void ksplot_draw_line(const struct ksplot_point *a, glEnd(); } +/** + * @brief Draw the a polyline. + * + * @param points: Input location for the array of points defining the polyline. + * @param n_points: The size of the array of points. + * @param col: The color of the polyline. + * @param size: The size of the polyline. + */ +void ksplot_draw_polyline(const struct ksplot_point *points, + size_t n_points, + const struct ksplot_color *col, + float size) +{ + if (!points || !n_points || !col || size < .5f) + return; + + /* Loop over the points of the polygon and draw connecting lines. */ + for(size_t i = 1; i < n_points; ++i) + ksplot_draw_line(&points[i - 1], + &points[i], + col, + size); +} + /** * @brief Draw a polygon. * @@ -212,12 +236,8 @@ void ksplot_draw_polygon_contour(const struct ksplot_point *points, if (!points || !n_points || !col || size < .5f) return; - /* Loop over the points of the polygon and draw connecting lines. */ - for(size_t i = 1; i < n_points; ++i) - ksplot_draw_line(&points[i - 1], - &points[i], - col, - size); + /* Loop over the points of the polygon and draw a polyline. */ + ksplot_draw_polyline(points, n_points, col, size); /* Close the contour. */ ksplot_draw_line(&points[0], diff --git a/src/libkshark-plot.h b/src/libkshark-plot.h index 063740d..d881b20 100644 --- a/src/libkshark-plot.h +++ b/src/libkshark-plot.h @@ -72,6 +72,11 @@ void ksplot_draw_line(const struct ksplot_point *a, const struct ksplot_color *col, float size); +void ksplot_draw_polyline(const struct ksplot_point *points, + size_t n_points, + const struct ksplot_color *col, + float size); + void ksplot_draw_polygon(const struct ksplot_point *points, size_t n_points, const struct ksplot_color *col, From patchwork Mon Jan 4 17:47:12 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11997217 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 67C8FC433E6 for ; Mon, 4 Jan 2021 17:49:44 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 42B86206B2 for ; Mon, 4 Jan 2021 17:49:44 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727475AbhADRto (ORCPT ); Mon, 4 Jan 2021 12:49:44 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34502 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727406AbhADRtn (ORCPT ); Mon, 4 Jan 2021 12:49:43 -0500 Received: from mail-ej1-x634.google.com (mail-ej1-x634.google.com [IPv6:2a00:1450:4864:20::634]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1EA3AC061387 for ; Mon, 4 Jan 2021 09:48:11 -0800 (PST) Received: by mail-ej1-x634.google.com with SMTP id jx16so37859776ejb.10 for ; Mon, 04 Jan 2021 09:48:11 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=YKJ0ck+D7lqWZ+gFM/UvvOKOIz8ieUwF4FUzkeQfx6I=; b=YxuUnMv72oT1frWhiouKTziXT07VRGxUjLgB6y0gsOIi/0TnRXQnwm1to8FVWSvSOb Y87NYR9fzx9JG7xbBIC2fvPkrv3fsB+sRx07Ug7rwYL9F0PqQ4GJCqDLjJqoavS9bPJv ZAtAKh4KvDlGGXsi7KDHAMA42kZ0OkYQHd3qxRVop0HQjlfHDgtt4WWw/wbb4aNlSAyR VsNYFLLe3at7rryRRrtyeRRHZkzotRV/iKQUx6K9yCgXpTrtV4IxkbJjnx838xQcUy0H XhgdhPMOTkgVPmsxpXcBhSuo05pPpXtl/bgpbcy3gSxUSPSFpQCacpVEuS01cFuBB32w Nfig== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=YKJ0ck+D7lqWZ+gFM/UvvOKOIz8ieUwF4FUzkeQfx6I=; b=Kz2r+Fdhv9h1t3Q5jeBmr95qndlXGSWZb12c0Zubhm6C1eei3CyJu1qYbjeMjKQpZf KQktwPf802OjpNyR2lznm3x7l9kHPXdYFI5bpVhMZkCZovMw1SSkgnkKh3M5pCfWmL64 9JppVDskvuUpJObf1/RWHlNMBiIXxr4JEC9xs1RlCOcvFWkjlD5ANyfMJc8tip+WKvor o968Qkekiql94v67RcFfeRD88dN3tpP67gXuSfBjHQzrsOHtoV+gLigjgym2UhIcmkNy QjGtS51AzxdOCCdhugvCtBYBZHvWLy/5EJ+/caCjbWspAelFRF5HrYmksPOPo9BdVmYg vw0g== X-Gm-Message-State: AOAM532sg4UV4wkK4yTuu7agcfq5egdIJcdvPU5TI/f2RpSummPT+MG6 GerdS1JCiPxSy80XIADhifY= X-Google-Smtp-Source: ABdhPJwezPfnrSBmIbRjGTDpqCpL2WjwAiehkMPnJCVSWAfikxiohbCPYQo9IyrFBsHY4dR+/QOyOQ== X-Received: by 2002:a17:906:9588:: with SMTP id r8mr66199910ejx.148.1609782489907; Mon, 04 Jan 2021 09:48:09 -0800 (PST) Received: from localhost.localdomain ([95.87.199.238]) by smtp.gmail.com with ESMTPSA id l14sm44107750edq.35.2021.01.04.09.48.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 04 Jan 2021 09:48:09 -0800 (PST) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" , Slavomir Kaslev Subject: [PATCH v8 32/44] kernel-shark: Optimize ksplot_draw_polygon() Date: Mon, 4 Jan 2021 19:47:12 +0200 Message-Id: <20210104174724.70404-33-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210104174724.70404-1-y.karadz@gmail.com> References: <20210104174724.70404-1-y.karadz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org Using a point inside the polygon to start the Triangle Fan looked more intuitive to me when I first implemented this method. I guess this was because when you use an internal point to start, the resulting set of triangles looks like fan indeed. However, as pointed out by Slavomir Kaslev, the Triangle Fan can be started from any corner of the polygon. Suggested-by: Slavomir Kaslev Signed-off-by: Yordan Karadzhov (VMware) --- src/libkshark-plot.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/libkshark-plot.c b/src/libkshark-plot.c index 597e5c2..5fdf4ca 100644 --- a/src/libkshark-plot.c +++ b/src/libkshark-plot.c @@ -201,18 +201,9 @@ void ksplot_draw_polygon(const struct ksplot_point *points, return; } - /* Obtain a point inside the surface of the polygon. */ - struct ksplot_point in_point; - in_point.x = (points[0].x + points[2].x) / 2; - in_point.y = (points[0].y + points[2].y) / 2; - - /* - * Draw a Triangle Fan using the internal point as a central - * vertex. - */ + /* Draw a Triangle Fan. */ glBegin(GL_TRIANGLE_FAN); glColor3ub(col->red, col->green, col->blue); - glVertex2i(in_point.x, in_point.y); for (size_t i = 0; i < n_points; ++i) glVertex2i(points[i].x, points[i].y); From patchwork Mon Jan 4 17:47:13 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11997219 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id CBE5AC43332 for ; Mon, 4 Jan 2021 17:49:44 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id A0F1120715 for ; Mon, 4 Jan 2021 17:49:44 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727525AbhADRto (ORCPT ); Mon, 4 Jan 2021 12:49:44 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34504 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727529AbhADRtn (ORCPT ); Mon, 4 Jan 2021 12:49:43 -0500 Received: from mail-ed1-x52f.google.com (mail-ed1-x52f.google.com [IPv6:2a00:1450:4864:20::52f]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 313C5C061388 for ; Mon, 4 Jan 2021 09:48:12 -0800 (PST) Received: by mail-ed1-x52f.google.com with SMTP id y24so28197871edt.10 for ; Mon, 04 Jan 2021 09:48:12 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=QMdBqes0O1rtDdupS5aMe2/0Trfu9V4omes3kcRL98M=; b=hcCqY57nLoqmsfaIiJyn5ozwWt930vScve5uNj+BM5Zr798ykIoBrcWWOniNzKSnJL MbGoaBMB3naC+Y+PIGssGPQ+xQPk53qKTrEizjGBirnbxN+e8TGBPdApqiek7thAq0En LdiwPiin8trr4Rv9FtnFVqKwtOykmNDtA+RBwhxJ2jSRG75j+6LvJFb7WPdd8j/YEqVb VnArFPffUK82/3hd/2vkkFEO9RzYQTqC4kmU4+E7YmOmgs7ECsGBRTOjv8xTkq6hulJH oBgYmBsO4yxkg29J9D/yCzQPivKpjCLdCiQ0+u/4HpyubwLluRiAfVoiFqeR34Ec3mmo j49Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=QMdBqes0O1rtDdupS5aMe2/0Trfu9V4omes3kcRL98M=; b=RApV/gjEjKBa1nJCpHnrih7dVj/+vDo7BD9omOudnPr5iJHCD06CZ155npMsBaJ43u K3KBC/1xo5AFKsRYFf+wSHcdpIJvlryGMlQBfFt0X/Z/jcpeb90EBAampCFh7RM39alP jUxx0jR2aKlKSZxslEWaCSPGtVkHe0Ps/XD4xW53nsfctYiWixF7WfGIpBS9TOBUSOnQ 3bv/6dq0WXaNzbYXuUz1XLfsdlss5xOHpHo5RocP/Vgh8F91DG+QC+fltJUIBNZOppkJ GrSqNUROC5Ob8rdJDAmmHjiclMsS5BfipoHCAGBNefrctUuvgUXy+JGWPCu55W2pwJh2 IsVQ== X-Gm-Message-State: AOAM533kESMf+oyD9/pyMowARyiehNkXTw7rBDKpwCMtSZgFTLgopA32 qjenYqUBNWJUbe2nFgqNvkg= X-Google-Smtp-Source: ABdhPJwq2RvZm9QWZ2Bbomp19dlza0F12g9iXLfqRg0WWW7Ojgzik1G5belzRwQxkWcdW1QaMxKlRQ== X-Received: by 2002:aa7:c543:: with SMTP id s3mr71067785edr.88.1609782490959; Mon, 04 Jan 2021 09:48:10 -0800 (PST) Received: from localhost.localdomain ([95.87.199.238]) by smtp.gmail.com with ESMTPSA id l14sm44107750edq.35.2021.01.04.09.48.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 04 Jan 2021 09:48:10 -0800 (PST) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v8 33/44] kernel-shark: Add basic infrastructure for testing Date: Mon, 4 Jan 2021 19:47:13 +0200 Message-Id: <20210104174724.70404-34-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210104174724.70404-1-y.karadz@gmail.com> References: <20210104174724.70404-1-y.karadz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org The tasting infrastructure is based on GTest (CMake-provided feature for automated testing) and Boost Test Library. In the patch we also provide a very simple test of adding/removing Data streams. For the moment this single test is used mainly to validate the proper functioning of the tasting infrastructure itself. A proper test coverage of the libraries is yet to be implemented. In a following patch we will integrate the tasting infrastructure into a "github Actions" workflow that will help the CI process. Signed-off-by: Yordan Karadzhov (VMware) --- CMakeLists.txt | 9 ++++++++ build/cmake_clean.sh | 4 ++++ tests/CMakeLists.txt | 14 ++++++++++++ tests/libkshark-tests.cpp | 48 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 75 insertions(+) create mode 100644 tests/CMakeLists.txt create mode 100644 tests/libkshark-tests.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 411163d..9abacd0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -57,6 +57,8 @@ if (Qt5Widgets_FOUND) endif (Qt5Widgets_FOUND) +find_package (Boost COMPONENTS unit_test_framework) + set(LIBRARY_OUTPUT_PATH "${KS_DIR}/lib") set(EXECUTABLE_OUTPUT_PATH "${KS_DIR}/bin") @@ -109,6 +111,13 @@ message(STATUS "Linker flags : " ${CMAKE_EXE_LINKER_FLAGS}) add_subdirectory(${KS_DIR}/src) add_subdirectory(${KS_DIR}/examples) +if (Boost_FOUND) + + enable_testing() + add_subdirectory(${KS_DIR}/tests) + +endif() + if (_DOXYGEN_DOC AND DOXYGEN_FOUND) message("\n doxygen documentation ...") diff --git a/build/cmake_clean.sh b/build/cmake_clean.sh index 2e7c8f9..207abd7 100755 --- a/build/cmake_clean.sh +++ b/build/cmake_clean.sh @@ -2,9 +2,13 @@ make clean rm CMakeCache.txt rm cmake_install.cmake rm Makefile +rm CTestTestfile.cmake +rm -f DartConfiguration.tcl rm -rf CMakeFiles/ rm -rf src/ rm -rf examples/ +rm -rf tests/ +rm -rf Testing/ rm -f ../lib/* rm ../kernelshark.desktop rm ../org.freedesktop.kshark-record.policy diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..a1e3085 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,14 @@ +message("\n tests ...") + +set(KS_TEST_DIR "${KS_DIR}/tests") +set(EXECUTABLE_OUTPUT_PATH ${KS_TEST_DIR}) + +add_executable(kshark-tests libkshark-tests.cpp) +target_include_directories(kshark-tests PRIVATE ${Boost_INCLUDE_DIRS}) +target_compile_definitions(kshark-tests PRIVATE "BOOST_TEST_DYN_LINK=1") +target_link_libraries(kshark-tests kshark + ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) + +add_test(NAME "libkshark_tests" + COMMAND ${KS_TEST_DIR}/kshark-tests --log_format=HRF + WORKING_DIRECTORY ${KS_TEST_DIR}) diff --git a/tests/libkshark-tests.cpp b/tests/libkshark-tests.cpp new file mode 100644 index 0000000..27c1171 --- /dev/null +++ b/tests/libkshark-tests.cpp @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: LGPL-2.1 + +/* + * Copyright (C) 2020 VMware Inc, Yordan Karadzhov (VMware) + */ + +// Boost +#define BOOST_TEST_MODULE KernelSharkTests +#include + +// KernelShark +#include "libkshark.h" + +#define N_TEST_STREAMS 1000 + +BOOST_AUTO_TEST_CASE(add_remove_streams) +{ + struct kshark_context *kshark_ctx = NULL; + int sd, free = 0, i; + + kshark_instance(&kshark_ctx); + + for (i = 0; i < N_TEST_STREAMS; ++i) { + sd = kshark_add_stream(kshark_ctx); + BOOST_CHECK_EQUAL(sd, free); + + kshark_add_stream(kshark_ctx); + + free = i / 2; + kshark_remove_stream(kshark_ctx, free); + sd = kshark_add_stream(kshark_ctx); + BOOST_CHECK_EQUAL(sd, free); + + free = i / 2 + 1; + kshark_remove_stream(kshark_ctx, free); + } + + BOOST_CHECK_EQUAL(kshark_ctx->n_streams, N_TEST_STREAMS); + + while (sd > 0) + sd = kshark_add_stream(kshark_ctx); + + BOOST_CHECK_EQUAL(kshark_ctx->n_streams, INT16_MAX + 1); + BOOST_CHECK_EQUAL(kshark_ctx->stream_info.array_size, INT16_MAX + 1); + BOOST_CHECK_EQUAL(sd, -ENODEV); + + kshark_close_all(kshark_ctx); +} From patchwork Mon Jan 4 17:47:14 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11997181 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-20.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,MENTIONS_GIT_HOSTING,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 050F6C433DB for ; Mon, 4 Jan 2021 17:49:29 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id D314620700 for ; Mon, 4 Jan 2021 17:49:28 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726719AbhADRt2 (ORCPT ); Mon, 4 Jan 2021 12:49:28 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34356 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726626AbhADRt2 (ORCPT ); Mon, 4 Jan 2021 12:49:28 -0500 Received: from mail-ej1-x62a.google.com (mail-ej1-x62a.google.com [IPv6:2a00:1450:4864:20::62a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 244BAC061389 for ; Mon, 4 Jan 2021 09:48:13 -0800 (PST) Received: by mail-ej1-x62a.google.com with SMTP id qw4so37872579ejb.12 for ; Mon, 04 Jan 2021 09:48:13 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=7N3iHQmzaB0c/IGxHOSi/RxAkqgGzZImjc6G+ESXAgg=; b=dzMI6Kygr4qSWs3QO1xbjeEOfljIvCJYYLxx+fo2N9x/kWKn/vqyt/mi4MZFqzkP3Q OUcbMw9E0rWJhlGJ3iJUVIRNeQbJZm2ngpsgFQb/b+8RVBUcdnYPbycLh5litVxH4OLs oG59SSR9TQA3T1wn/po+Y3BdijAUGjfArz42ShE8vRaiSjPRbGWmnBS4RGlq1VMYSzh1 oGy5T7IcL4fXIbRmkazBAPXV6RRefNgkEMeP+Iso3PUiCkMhfiN3aVjzVV8XAGgZWmtS /h5yoo55XaebGoaEDm8J9EibIc7atMt5BVHY97DExm43gobQWSk02UJR8IpWfpD9oP4A PFKA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=7N3iHQmzaB0c/IGxHOSi/RxAkqgGzZImjc6G+ESXAgg=; b=Fh/3pOABPtBRjJMZoIXiXJ1hitmhpQIZOXeLzx0oPkdgKZzm2YsvYTbLWp/QdL2i1o TKv6ceswSRpu2X1RtV9G91sIvGZksrlj+Ivc8pmqOFGJk19uaao+rGIcHCBGSetk4c52 hqx80HKFlKAaWX1B6iWEC0BoWnMHFpiluGr4iB92ATV853mjXPC5vyo6712gUL+o1/XI XZD40A8MgWHo+ClpyoKhb1HzqXRxTWhOTbMlt3wFWWdKM3lVxoxrLrr/aRMMlgluSBi8 Hm82RsOtI/swOll8i7u+0Dmi9WgyoaZk729o5yPsYo6SqR7tHuuku0cYhVe3MrSPuJoK gEFQ== X-Gm-Message-State: AOAM533XCK0mOv7RRF3dcE9LoTowzodve045/g0pTlQTSfbMS7rWWsVD ZqNcKfwCQgfn/r+ZJDML4rtEB2QApsNxMw== X-Google-Smtp-Source: ABdhPJz8dl/W6E739W+7hDvA97/a1bTyaMunjhpLjsRiBjx2frPK6NDaW4Ug5ZMi5deEDENf3K7KWA== X-Received: by 2002:a17:907:b09:: with SMTP id h9mr68740106ejl.155.1609782491844; Mon, 04 Jan 2021 09:48:11 -0800 (PST) Received: from localhost.localdomain ([95.87.199.238]) by smtp.gmail.com with ESMTPSA id l14sm44107750edq.35.2021.01.04.09.48.11 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 04 Jan 2021 09:48:11 -0800 (PST) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v8 34/44] kernel-shark: Add "github Actions" workflow Date: Mon, 4 Jan 2021 19:47:14 +0200 Message-Id: <20210104174724.70404-35-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210104174724.70404-1-y.karadz@gmail.com> References: <20210104174724.70404-1-y.karadz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org From: Yordan Karadzhov The workflow will install all third party libraries, will build KernelShark and will run the tests. Signed-off-by: Yordan Karadzhov (VMware) --- .github/workflows/main.yml | 63 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 .github/workflows/main.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..a35f003 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,63 @@ +name: KernelShark CI (CMAKE) + +on: [push] + +env: + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + BUILD_TYPE: Release + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Install Dependencies + shell: bash + run: | + sudo apt-get update + sudo apt-get install build-essential git cmake libjson-c-dev -y + sudo apt-get install freeglut3-dev libxmu-dev libxi-dev -y + sudo apt-get install qtbase5-dev libboost-all-dev -y + git clone https://git.kernel.org/pub/scm/libs/libtrace/libtraceevent.git/ + cd libtraceevent + make + sudo make install + cd .. + git clone https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/ + cd libtracefs + make + sudo make install + cd .. + git clone git://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git + cd trace-cmd + make + sudo make install + sudo make install_libs + - name: Create Build Environment + # Some projects don't allow in-source building, so create a separate build directory + # We'll use this as our working directory for all subsequent commands + run: cmake -E make_directory ${{runner.workspace}}/build + + - name: Configure CMake + # Use a bash shell so we can use the same syntax for environment variable + # access regardless of the host operating system + shell: bash + working-directory: ${{runner.workspace}}/build + # Note the current convention is to use the -S and -B options here to specify source + # and build directories, but this is only available with CMake 3.13 and higher. + # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 + run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_C_FLAGS_RELEASE="-Werror" + + - name: Build + working-directory: ${{runner.workspace}}/build + shell: bash + # Execute the build. You can specify a specific target with "--target " + run: cmake --build . --config $BUILD_TYPE + + - name: Test + working-directory: ${{runner.workspace}}/build + shell: bash + # Execute tests defined by the CMake configuration. + run: ctest -C $BUILD_TYPE From patchwork Mon Jan 4 17:47:15 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11997183 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4FBB0C433E0 for ; Mon, 4 Jan 2021 17:49:29 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 24858206B2 for ; Mon, 4 Jan 2021 17:49:29 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726896AbhADRt2 (ORCPT ); Mon, 4 Jan 2021 12:49:28 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34358 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726626AbhADRt2 (ORCPT ); Mon, 4 Jan 2021 12:49:28 -0500 Received: from mail-ed1-x52c.google.com (mail-ed1-x52c.google.com [IPv6:2a00:1450:4864:20::52c]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 07808C06138A for ; Mon, 4 Jan 2021 09:48:14 -0800 (PST) Received: by mail-ed1-x52c.google.com with SMTP id r5so28194096eda.12 for ; Mon, 04 Jan 2021 09:48:13 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=qLHvOgsMCInonzbV5NA4to3a6X6Gs34yYw2Zrmjhe8M=; b=CeMpJ10KgFcoA2AK+7nCSCA1fmPeHjOhVyn7b5uB0uui1O+2ByFWh5KF01jcMmEH5T M8GL7piSNC89jP036HPKMh0UbQj7x4iBe1OAA+eLTO3sSFOI9+yBtRx1GkD1eVlL5KNY 9q7gUzel2SEpcTEfhv12vJcIQDeRoa2GyK2yA6tovszX5p8f5EONzY1abysDUTp67qkd dwB8m71iqH/z4H97ijCoBC3sqYNco3MG4D56Uyfp6Pqzl2yiST+6SMD1KdZSyeh1FTek RvVnTanBDTuaCVFNvYECwRTEs/om672BLc2GUWNlPSeWx6fBcfFWQ28q5OrgYR8uMb90 cIvg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=qLHvOgsMCInonzbV5NA4to3a6X6Gs34yYw2Zrmjhe8M=; b=JOOZ9Db1L/lNFQYYEHgfrGLYFufPcnQYOkT5fD/LNKV/s0btWGui6f+RZiEazOuaV7 vhVQLq5AY16pAOZWJHdSqo8+/9kQAdbe8Qd7RXsnPbdoCCjGmhPfIyPqk39O4o0P4OtC pN9Sw+Cw142t/g1k2NEHEwKjU9eWCW3rvf0s5B83OI3JBtInBNJgAeBO4oSLoVmIMRMw yxx6AZJKsKRMhnrDLbG9JOrYst/NdttbhS9/Vf603y2DWTk1XBRrjV9elVFexZqZL73a H1wggOLTE0hWPTfMDA8HYQeo4fN2Tr9V+KSEg0FhtGXydZI6f0YQ9kMrg9WKewDiMeNU pabw== X-Gm-Message-State: AOAM533rzksrtaI9755eHO6CwDXOVPT4ZRJ2eo0MvzMujSWLBGMKItkw Py0OFnD959MiQ0PXb+KYERg= X-Google-Smtp-Source: ABdhPJyS4T2ug2rMJpZKfge/5dYJ+qq+U66VyKZtK5G/RsYXN4XhMSBqnkZLrkbwpG0zas7QBQaFDw== X-Received: by 2002:a05:6402:139a:: with SMTP id b26mr72374032edv.47.1609782492762; Mon, 04 Jan 2021 09:48:12 -0800 (PST) Received: from localhost.localdomain ([95.87.199.238]) by smtp.gmail.com with ESMTPSA id l14sm44107750edq.35.2021.01.04.09.48.12 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 04 Jan 2021 09:48:12 -0800 (PST) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v8 35/44] kernel-shark: Integrate streams with KsPlotTools Date: Mon, 4 Jan 2021 19:47:15 +0200 Message-Id: <20210104174724.70404-36-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210104174724.70404-1-y.karadz@gmail.com> References: <20210104174724.70404-1-y.karadz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org The C++ ploting toolls are adapted in order to be able to work with the new version of the C API. Now we can handle multiple Data streams. Signed-off-by: Yordan Karadzhov (VMware) --- src/CMakeLists.txt | 4 +- src/KsPlotTools.cpp | 94 ++++++++++++++++++++++++++++++++------------- src/KsPlotTools.hpp | 4 +- 3 files changed, 71 insertions(+), 31 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fe440db..9646cbb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -41,8 +41,8 @@ install(FILES "${KS_DIR}/src/libkshark.h" if (OPENGL_FOUND) message(STATUS "libkshark-plot") - add_library(kshark-plot SHARED libkshark-plot.c) -# KsPlotTools.cpp) + add_library(kshark-plot SHARED libkshark-plot.c + KsPlotTools.cpp) target_link_libraries(kshark-plot kshark ${GLUT_LIBRARY} diff --git a/src/KsPlotTools.cpp b/src/KsPlotTools.cpp index fe3008e..113a6c0 100644 --- a/src/KsPlotTools.cpp +++ b/src/KsPlotTools.cpp @@ -10,16 +10,13 @@ */ // C +#include #include // C++ #include #include -// OpenGL -#include -#include - // KernelShark #include "KsPlotTools.hpp" @@ -109,29 +106,45 @@ void Color::setRainbowColor(int n) ColorTable getTaskColorTable() { struct kshark_context *kshark_ctx(nullptr); + int nTasks(0), pid, *pids, i(0), *streamIds; + std::vector tempPids; ColorTable colors; - int nTasks, pid, *pids, i(0); if (!kshark_instance(&kshark_ctx)) return colors; - nTasks = kshark_get_task_pids(kshark_ctx, &pids); - if (!nTasks) + streamIds = kshark_all_streams(kshark_ctx); + for (int j = 0; j < kshark_ctx->n_streams; ++j) { + nTasks = kshark_get_task_pids(kshark_ctx, streamIds[j], &pids); + tempPids.insert(tempPids.end(), pids, pids + nTasks); + free(pids); + } + + free(streamIds); + + if (!tempPids.size()) return colors; - std::vector temp_pids(pids, pids + nTasks); - std::sort(temp_pids.begin(), temp_pids.end()); + std::sort(tempPids.begin(), tempPids.end()); - free(pids); + /* Erase duplicates. */ + tempPids.erase(unique(tempPids.begin(), tempPids.end()), + tempPids.end()); - if (temp_pids[i] == 0) { - /* The "Idle" process (pid = 0) will be plotted in black. */ + /* Erase negative (error codes). */ + auto isNegative = [](int i) {return i < 0;}; + auto it = remove_if(tempPids.begin(), tempPids.end(), isNegative); + tempPids.erase(it, tempPids.end()); + + nTasks = tempPids.size(); + if (tempPids[i] == 0) { + /* Pid <= 0 will be plotted in black. */ colors[i++] = {}; } for (; i < nTasks; ++i) { - pid = temp_pids[i]; - colors[pid].setRainbowColor(i - 1); + pid = tempPids[i]; + colors[pid].setRainbowColor(i); } return colors; @@ -145,16 +158,26 @@ ColorTable getTaskColorTable() */ ColorTable getCPUColorTable() { - struct kshark_context *kshark_ctx(nullptr); + kshark_context *kshark_ctx(nullptr); + int nCPUs, nCPUMax(0), *streamIds; + kshark_data_stream *stream; ColorTable colors; - int nCPUs; if (!kshark_instance(&kshark_ctx)) return colors; - nCPUs = tep_get_cpus(kshark_ctx->pevent); - for (int i = 0; i < nCPUs; ++i) - colors[i].setRainbowColor(i); + streamIds = kshark_all_streams(kshark_ctx); + for (int i = 0; i < kshark_ctx->n_streams; ++i) { + stream = kshark_get_data_stream(kshark_ctx, streamIds[i]); + nCPUs = stream->n_cpus; + if (nCPUMax < nCPUs) + nCPUMax = nCPUs; + } + + free(streamIds); + + for (int i = 0; i < nCPUMax; ++i) + colors[i].setRainbowColor(i + 8); return colors; } @@ -788,9 +811,10 @@ void Graph::setBin(int bin, int pidF, int pidB, const Color &col, uint8_t m) /** * @brief Process a CPU Graph. * + * @param sd: Data stream identifier. * @param cpu: The CPU core. */ -void Graph::fillCPUGraph(int cpu) +void Graph::fillCPUGraph(int sd, int cpu) { struct kshark_entry *eFront; int pidFront(0), pidBack(0); @@ -804,6 +828,7 @@ void Graph::fillCPUGraph(int cpu) eFront = nullptr; pidFront = ksmodel_get_pid_front(_histoPtr, bin, + sd, cpu, true, _collectionPtr, @@ -813,6 +838,7 @@ void Graph::fillCPUGraph(int cpu) eFront = _histoPtr->data[index]; pidBack = ksmodel_get_pid_back(_histoPtr, bin, + sd, cpu, true, _collectionPtr, @@ -820,10 +846,11 @@ void Graph::fillCPUGraph(int cpu) pidBackNoFilter = ksmodel_get_pid_back(_histoPtr, bin, - cpu, - false, - _collectionPtr, - nullptr); + sd, + cpu, + false, + _collectionPtr, + nullptr); if (pidBack != pidBackNoFilter) pidBack = KS_FILTERED_BIN; @@ -832,6 +859,7 @@ void Graph::fillCPUGraph(int cpu) if (eFront) { if (!(eFront->visible & KS_EVENT_VIEW_FILTER_MASK) && ksmodel_cpu_visible_event_exist(_histoPtr, bin, + sd, cpu, _collectionPtr, &index)) { @@ -874,6 +902,7 @@ void Graph::fillCPUGraph(int cpu) */ pidBackNoFilter = ksmodel_get_pid_back(_histoPtr, LOWER_OVERFLOW_BIN, + sd, cpu, false, _collectionPtr, @@ -882,6 +911,7 @@ void Graph::fillCPUGraph(int cpu) /* Now get the Pid back, applying filters. */ pidBack = ksmodel_get_pid_back(_histoPtr, LOWER_OVERFLOW_BIN, + sd, cpu, true, _collectionPtr, @@ -916,9 +946,10 @@ void Graph::fillCPUGraph(int cpu) /** * @brief Process a Task Graph. * + * @param sd: Data stream identifier. * @param pid: The Process Id of the Task. */ -void Graph::fillTaskGraph(int pid) +void Graph::fillTaskGraph(int sd, int pid) { int cpuFront, cpuBack(0), pidFront(0), pidBack(0), lastCpu(-1), bin(0); struct kshark_entry *eFront; @@ -972,6 +1003,7 @@ void Graph::fillTaskGraph(int pid) */ int cpuPid = ksmodel_get_pid_back(_histoPtr, bin, + sd, lastCpu, false, nullptr, // No collection @@ -998,6 +1030,7 @@ void Graph::fillTaskGraph(int pid) eFront = nullptr; /* Get the CPU used by this task. */ cpuFront = ksmodel_get_cpu_front(_histoPtr, bin, + sd, pid, false, _collectionPtr, @@ -1006,6 +1039,7 @@ void Graph::fillTaskGraph(int pid) eFront = _histoPtr->data[index]; cpuBack = ksmodel_get_cpu_back(_histoPtr, bin, + sd, pid, false, _collectionPtr, @@ -1020,6 +1054,7 @@ void Graph::fillTaskGraph(int pid) */ pidFront = ksmodel_get_pid_front(_histoPtr, bin, + sd, cpuFront, false, _collectionPtr, @@ -1027,6 +1062,7 @@ void Graph::fillTaskGraph(int pid) pidBack = ksmodel_get_pid_back(_histoPtr, bin, + sd, cpuBack, false, _collectionPtr, @@ -1037,6 +1073,7 @@ void Graph::fillTaskGraph(int pid) if (!(eFront->visible & KS_EVENT_VIEW_FILTER_MASK) && ksmodel_task_visible_event_exist(_histoPtr, bin, + sd, pid, _collectionPtr, &index)) { @@ -1062,8 +1099,9 @@ void Graph::fillTaskGraph(int pid) * No data from this Task in the very first bin. Use the Lower * Overflow Bin to retrieve the CPU used by the task (if any). */ - cpuFront = ksmodel_get_cpu_back(_histoPtr, LOWER_OVERFLOW_BIN, pid, - false, _collectionPtr, nullptr); + cpuFront = ksmodel_get_cpu_back(_histoPtr, LOWER_OVERFLOW_BIN, + sd, pid, + false, _collectionPtr, nullptr); if (cpuFront >= 0) { /* * The Lower Overflow Bin contains data from this Task. @@ -1075,6 +1113,7 @@ void Graph::fillTaskGraph(int pid) pidCpu0 = ksmodel_get_pid_back(_histoPtr, 0, + sd, cpuFront, false, _collectionPtr, @@ -1082,6 +1121,7 @@ void Graph::fillTaskGraph(int pid) pidCpuLOB = ksmodel_get_pid_back(_histoPtr, LOWER_OVERFLOW_BIN, + sd, cpuFront, false, _collectionPtr, diff --git a/src/KsPlotTools.hpp b/src/KsPlotTools.hpp index ee447f1..6e66373 100644 --- a/src/KsPlotTools.hpp +++ b/src/KsPlotTools.hpp @@ -432,9 +432,9 @@ public: /** @brief Set the Hash table of Task's colors. */ void setBinColorTablePtr(KsPlot::ColorTable *ct) {_binColors = ct;} - void fillCPUGraph(int cpu); + void fillCPUGraph(int sd, int cpu); - void fillTaskGraph(int pid); + void fillTaskGraph(int sd, int pid); void draw(float s = 1); From patchwork Mon Jan 4 17:47:16 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11997185 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0CEA7C433E6 for ; Mon, 4 Jan 2021 17:49:30 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id DE4D020700 for ; Mon, 4 Jan 2021 17:49:29 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727008AbhADRt3 (ORCPT ); Mon, 4 Jan 2021 12:49:29 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34362 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726626AbhADRt3 (ORCPT ); Mon, 4 Jan 2021 12:49:29 -0500 Received: from mail-ej1-x635.google.com (mail-ej1-x635.google.com [IPv6:2a00:1450:4864:20::635]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id BF093C06138B for ; Mon, 4 Jan 2021 09:48:14 -0800 (PST) Received: by mail-ej1-x635.google.com with SMTP id ce23so37889148ejb.8 for ; Mon, 04 Jan 2021 09:48:14 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=2H4nudzpSsWEB0/dzAjsoV+CDB73TMcPs68ddOwHN34=; b=d7ZJPs9xTSWcRLuOY7ssFDaujmYXj7jFPdIo27dZKrG9Pp3JHxgk/9tbxm9ryUkHep MVKxc/0IU5t8S5oa3ZN67EaC//VSbWswLXafVcGWm3T9REb+DdJPT5gHDaLTIF65RaO2 FtcbuM0unuGXuKu6ENPxJfBekHzPykKILGDxQvtfSrN02IkfyTXTwvNQdO3xVAIeNZIw HpU7gAOBBm2EyFsFV/uVSYz5vLu2ItY1d8GrKykxpiGtDsg++Q00SjqnUioknRei7ooU OLvFb77w+jqcb0a7puyAUc5uFidHusWeeQreFutXq+8bFCmb5IX6XYnxQmlYsdP6JX3Y oIZg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=2H4nudzpSsWEB0/dzAjsoV+CDB73TMcPs68ddOwHN34=; b=Ry1u3OafK1dmVkQTc31BKfpxRSxkA/a86DQvD8ptMqlN9EHXXqBIBNLeph3+VYmpeF RoWnKFsfuPRQDmtmceLwISn5XZZxqtpsWcGsVh2RY/fCCQVRg355tZx0mJqdbJJMs9jq BZ10yM+pKuQ3J/j87x0m4sjaaLGi01/o8EIe7UZIkK1YbAH8xA0YttPX8WK64fjjDZmn 5cA/QiGy/3XsBWiZ339yYAO9p+Osjl6EqflM3e7HSQkA56ZVAK3DjvrhRafkNaY0igyF Y4DjsmemLWHn1eOcx2IXXOf919BfwLUo1zhdgB8nPRd8VYmVJj5GFdodr+UUPrfSkVYU Oj5A== X-Gm-Message-State: AOAM531a28rDeHh86btCvZERLf0RRQRykE1aOyGW5wlFHuuJI62Q9V20 1Jhc/Pgmn6H8EVO5ccKzG8o= X-Google-Smtp-Source: ABdhPJyYrnDZ9nzdLdnfsVKT7yEH3pduUsEDjo6Amjiq+yJkccRaLVWHAKskV9NIHZ0GQbrHiqLQ9w== X-Received: by 2002:a17:906:74d9:: with SMTP id z25mr67044053ejl.217.1609782493568; Mon, 04 Jan 2021 09:48:13 -0800 (PST) Received: from localhost.localdomain ([95.87.199.238]) by smtp.gmail.com with ESMTPSA id l14sm44107750edq.35.2021.01.04.09.48.12 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 04 Jan 2021 09:48:13 -0800 (PST) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v8 36/44] kernel-shark: Add getStreamColorTable() Date: Mon, 4 Jan 2021 19:47:16 +0200 Message-Id: <20210104174724.70404-37-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210104174724.70404-1-y.karadz@gmail.com> References: <20210104174724.70404-1-y.karadz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org This method provides a hash table that maps the stream Ids to rainbow-like colors. Signed-off-by: Yordan Karadzhov (VMware) --- src/KsPlotTools.cpp | 50 +++++++++++++++++++++++++++++++++++++++++++++ src/KsPlotTools.hpp | 4 ++++ 2 files changed, 54 insertions(+) diff --git a/src/KsPlotTools.cpp b/src/KsPlotTools.cpp index 113a6c0..400a3e2 100644 --- a/src/KsPlotTools.cpp +++ b/src/KsPlotTools.cpp @@ -97,6 +97,23 @@ void Color::setRainbowColor(int n) set(r, g, b); } +/** Alpha blending with white background. */ +void Color::blend(float alpha) +{ + if (alpha < 0 || alpha > 1.) + return; + + auto lamBld = [alpha](int val) { + return val * alpha + (1 - alpha) * 255; + }; + + int r = lamBld(this->r()); + int g = lamBld(this->g()); + int b = lamBld(this->b()); + + set(r, g, b); +} + /** * @brief Create a Hash table of Rainbow colors. The sorted Pid values are * mapped to the palette of Rainbow colors. @@ -182,6 +199,39 @@ ColorTable getCPUColorTable() return colors; } +/** + * @brief Create a Hash table of Rainbow colors. The Steam Ids are + * mapped to the palette of Rainbow colors. + * + * @returns ColorTable instance. + */ +ColorTable getStreamColorTable() +{ + kshark_context *kshark_ctx(nullptr); + ColorTable colors; + Color color; + float alpha(.35); + int *streamIds; + + if (!kshark_instance(&kshark_ctx)) + return colors; + + streamIds = kshark_all_streams(kshark_ctx); + for (int i = 0; i < kshark_ctx->n_streams; ++i) { + /* + * Starting from index 6 provides better functioning of the + * color scheme slider. + */ + color.setRainbowColor(i + 6); + color.blend(alpha); + colors[streamIds[i]] = color; + } + + free(streamIds); + + return colors; +} + /** * @brief Search the Hash table of Rainbow colors for a particular key (pid). * diff --git a/src/KsPlotTools.hpp b/src/KsPlotTools.hpp index 6e66373..dd6c77f 100644 --- a/src/KsPlotTools.hpp +++ b/src/KsPlotTools.hpp @@ -47,6 +47,8 @@ public: void setRainbowColor(int n); + void blend(float alpha); + /** * @brief Get the C struct defining the RGB color. */ @@ -78,6 +80,8 @@ ColorTable getTaskColorTable(); ColorTable getCPUColorTable(); +ColorTable getStreamColorTable(); + Color getColor(ColorTable *colors, int pid); /** Represents an abstract graphical element. */ From patchwork Mon Jan 4 17:47:17 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11997187 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 38728C43381 for ; Mon, 4 Jan 2021 17:49:30 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 124E920700 for ; Mon, 4 Jan 2021 17:49:30 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726626AbhADRt3 (ORCPT ); Mon, 4 Jan 2021 12:49:29 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34360 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726939AbhADRt3 (ORCPT ); Mon, 4 Jan 2021 12:49:29 -0500 Received: from mail-ej1-x635.google.com (mail-ej1-x635.google.com [IPv6:2a00:1450:4864:20::635]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 895D7C06138C for ; Mon, 4 Jan 2021 09:48:15 -0800 (PST) Received: by mail-ej1-x635.google.com with SMTP id n26so37886136eju.6 for ; Mon, 04 Jan 2021 09:48:15 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=5Br8DNfdS6ZBCWS5r5dxkv3R/dbDG0DQTli280a8a4U=; b=sGj69qu6RrWIF5/+bcTZqJq4wvn8qPGBB0v6nCZ2lyxFKYtTwT2IBGumg38mF727NK bCyQT5jhvCxQXZ6k0U/bDUqXLDaBt1UxGgoavJ1Rf6sBc+LlaxtjnSYjtueEJZBvm44k ewA0ft3BWp/ae0lgDX2GdBR8saRGhNG1JFoKO/1O+3lwCakDm4vTvVezlRL53flX4pdW wblAjaiWlu992Jq4LkSHCEsK5qArGxq7zH3S9vDXPHHoaoyUYkWYN/eSiGj75eKuJN6p YBeuFCyt0qArMqFzbKWyat3YsNvvtcP93oAbqW+yQt7JdeQFQ0TrCM+Eq+MXmGC1wGYh OHcg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=5Br8DNfdS6ZBCWS5r5dxkv3R/dbDG0DQTli280a8a4U=; b=eZsFdML+2qyFtPdQ3qXYU47/uTqKJVDU0emre51s277vToDDX3euc+DY+MUW0kN5Xz 6VnZfz+SlbK71xwIwwYWh39CM5XcPr2bQTnF3S/3MmF3ZvQrlPemlSJy/6VZiwxdGg2q oD6lLRiGCTjRFj/bp/mhyxVq/g8z8YuOpy+GymYLeUzOurtPAT4epw149o8GryJ+xamT Kfz5NOebuLobX+LcHIu+jR03YZtpXrltcqThLtKcCMVjh7r8oPwL0dycQ3cm4mPrVjk9 K6wp2d3VpPw+vAyN/tlghtJbmwgMnO+/qkE/r+3pLZQfydH34gqug0FG4cqipiSdfLjC ErOQ== X-Gm-Message-State: AOAM533lJzIHPDxib7q8NSzXwAs6/g4auvkzXHqLd4Ug28r4/tEZcPpi BNG/6xJg46p4JrYRwWh5Yg8= X-Google-Smtp-Source: ABdhPJzL7gTfCBaX7vXkWgtzWCn5Hp2JwENkLkSw5iWd58lKuzDSaDilUA08WKn/lm2SKslBGk0jBg== X-Received: by 2002:a17:906:8594:: with SMTP id v20mr66921062ejx.470.1609782494368; Mon, 04 Jan 2021 09:48:14 -0800 (PST) Received: from localhost.localdomain ([95.87.199.238]) by smtp.gmail.com with ESMTPSA id l14sm44107750edq.35.2021.01.04.09.48.13 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 04 Jan 2021 09:48:13 -0800 (PST) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v8 37/44] kernel-shark: Redefine the args of KsPlot::getColor() Date: Mon, 4 Jan 2021 19:47:17 +0200 Message-Id: <20210104174724.70404-38-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210104174724.70404-1-y.karadz@gmail.com> References: <20210104174724.70404-1-y.karadz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org The function was originally designed to return the color that correspond to a given PID. However we now have 3 different color maps (for PID, CPU Id and Stream Id) and the same method can be used with each of the 3 maps. We also want to prevent the method from modifying the map itself. Signed-off-by: Yordan Karadzhov (VMware) --- src/KsPlotTools.cpp | 10 +++++----- src/KsPlotTools.hpp | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/KsPlotTools.cpp b/src/KsPlotTools.cpp index 400a3e2..6c3902a 100644 --- a/src/KsPlotTools.cpp +++ b/src/KsPlotTools.cpp @@ -233,17 +233,17 @@ ColorTable getStreamColorTable() } /** - * @brief Search the Hash table of Rainbow colors for a particular key (pid). + * @brief Search the Hash table of Rainbow colors for a particular key (Id). * * @param colors: Input location for the ColorTable instance. - * @param pid: the Process Id to search for. + * @param id: The Id to search for. * - * @returns The Rainbow color of the key "pid". If "pid" does not exist, the + * @returns The Rainbow color of the key "id". If "id" does not exist, the * returned color is Black. */ -Color getColor(ColorTable *colors, int pid) +Color getColor(const ColorTable *colors, int id) { - auto item = colors->find(pid); + auto item = colors->find(id); if (item != colors->end()) return item->second; diff --git a/src/KsPlotTools.hpp b/src/KsPlotTools.hpp index dd6c77f..7ffabd8 100644 --- a/src/KsPlotTools.hpp +++ b/src/KsPlotTools.hpp @@ -82,7 +82,7 @@ ColorTable getCPUColorTable(); ColorTable getStreamColorTable(); -Color getColor(ColorTable *colors, int pid); +Color getColor(const ColorTable *colors, int id); /** Represents an abstract graphical element. */ class PlotObject { From patchwork Mon Jan 4 17:47:18 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11997189 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5AEE6C433E0 for ; Mon, 4 Jan 2021 17:49:33 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 3524C206B2 for ; Mon, 4 Jan 2021 17:49:33 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726939AbhADRtd (ORCPT ); Mon, 4 Jan 2021 12:49:33 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34374 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727086AbhADRtc (ORCPT ); Mon, 4 Jan 2021 12:49:32 -0500 Received: from mail-ej1-x629.google.com (mail-ej1-x629.google.com [IPv6:2a00:1450:4864:20::629]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 753B8C06138D for ; Mon, 4 Jan 2021 09:48:16 -0800 (PST) Received: by mail-ej1-x629.google.com with SMTP id b9so37959375ejy.0 for ; Mon, 04 Jan 2021 09:48:16 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=T4A7E1BDscYXt41eolB0dZpFcPyC9alYcsymRqZXNtk=; b=g8O671zAm9lhVPIZhgeZRxmLNGOrf+UKpzCUrQHhLbNvJmJNd5XVgADGgv5QNc4Gsy u3A87XKkvkXiSwLQ31LTpeRNt2c3p/h0sUlL7Wwd6P7tHLwiYgtWxXkX71VYpUiqFgft RFUIhmEDCXNVA6mfoz5VgmjPG+TAZcAAlyF723lEXc5RzDFntKPDdF133LfsufjGZst7 Xas1B0OLaON3vjye2x8MN5jHlaF4xHW88hJJWfegqVAW/iDZTaiAHmrMR5Qv4WBRUml8 lBfeHrluW5BPErg3uFT67tYNDQBDokw0xj50xoLD31AMHZC1MjK47y8Z15nMlxxi1Qoa a8jQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=T4A7E1BDscYXt41eolB0dZpFcPyC9alYcsymRqZXNtk=; b=FEwvMgZgRp3rTgzmGrDSGeGabe1WjhVo8Tfc2rmh6NRHm+VCQTwr3KlT2AHKfxMfOf VMfDIQH88O5t8pf4Wr4WEnZu+39jJYKS6ZGwWR5I8cwQ5IE2cRJtrPUaCXs6yH1XYqm8 rG8QQinNI40LzJ5baQ8Ymg+g5FwfNH+qRD36dr+e7WUierTAm4nHfkMCk4oBTBc4hS9t nT31nNfJbshJ662c5Cq5FE2JXXlmumjtdB1bKZ4vjFhHNeT34QDydodvOxD35hYms+Oh Y2AjIIEkNeE+cGImh34YfBZvLWMkNyzLlHGbDsurpv9+MrLfLeDIiPhfwx0pgVg59U3C W+ng== X-Gm-Message-State: AOAM531vSuHydPpDJ2ik7+U8IpY1LZO+MO7g3AODTznWBxgfu6ClUPIa PXgjrCOqmfRS9RyJt4OIh1o= X-Google-Smtp-Source: ABdhPJyAvMqMt6igawczBDjk7aZ/NkUPp7qYq8EaNO0wyGjOrOyAVbrYK2wdrmf/MNjQ2bMC53Dnrw== X-Received: by 2002:a17:906:59a:: with SMTP id 26mr66644705ejn.309.1609782495239; Mon, 04 Jan 2021 09:48:15 -0800 (PST) Received: from localhost.localdomain ([95.87.199.238]) by smtp.gmail.com with ESMTPSA id l14sm44107750edq.35.2021.01.04.09.48.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 04 Jan 2021 09:48:14 -0800 (PST) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v8 38/44] kernel-shark: Add TextBox class to KsPlot namespace Date: Mon, 4 Jan 2021 19:47:18 +0200 Message-Id: <20210104174724.70404-39-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210104174724.70404-1-y.karadz@gmail.com> References: <20210104174724.70404-1-y.karadz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org This class represents a text that is printed/drawn inside a colorful frame. Signed-off-by: Yordan Karadzhov (VMware) --- src/KsPlotTools.cpp | 99 +++++++++++++++++++++++++++++++++++++++++++++ src/KsPlotTools.hpp | 40 ++++++++++++++++++ 2 files changed, 139 insertions(+) diff --git a/src/KsPlotTools.cpp b/src/KsPlotTools.cpp index 6c3902a..ef15cde 100644 --- a/src/KsPlotTools.cpp +++ b/src/KsPlotTools.cpp @@ -524,6 +524,105 @@ void Polygon::_draw(const Color &col, float size) const col.color_c_ptr(), size); } +/** The created object will print/draw only the text without the frame. */ +TextBox::TextBox() +: _text(""), + _font(nullptr) +{ + setPos(Point(0, 0)); + _box._visible = false; +} + + +/** The created object will print/draw only the text without the frame. */ +TextBox::TextBox(ksplot_font *f) +: _text(""), + _font(f) +{ + setPos(Point(0, 0)); + _box._visible = false; +} + +/** The created object will print/draw only the text without the frame. */ +TextBox::TextBox(ksplot_font *f, const std::string &text, const Point &pos) +: _text(text), + _font(f) +{ + setPos(pos); + _box._visible = false; +} + +/** The created object will print/draw only the text without the frame. */ +TextBox::TextBox(ksplot_font *f, const std::string &text, const Color &col, + const Point &pos) +: _text(text), + _font(f) +{ + _color = col; + setPos(pos); + _box._visible = false; +} + +/** The created object will print/draw the text and the frame. */ +TextBox::TextBox(ksplot_font *f, const std::string &text, const Color &col, + const Point &pos, int l, int h) +: _text(text), + _font(f) +{ + setPos(pos); + setBoxAppearance(col, l, h); +} + +/** + * @brief Set the position of the bottom-left corner of the frame. + * + * @param p: The coordinates of the bottom-left corner. + */ +void TextBox::setPos(const Point &p) +{ + _box.setPoint(0, p); +} + +/** + * @brief Set the color and the dimensions of the frame. + * + * @param col: The color of the frame. + * @param l: The length of the frame. + * @param h: The height of the frame. + */ +void TextBox::setBoxAppearance(const Color &col, int l, int h) +{ + _box.setFill(true); + _box._color = col; + _box._visible = true; + + if (h <= 0 && _font) + h = _font->height; + + _box.setPoint(1, _box.getPointX(0), _box.getPointY(0) - h); + _box.setPoint(2, _box.getPointX(0) + l, _box.getPointY(0) - h); + _box.setPoint(3, _box.getPointX(0) + l, _box.getPointY(0)); +} + +void TextBox::_draw(const Color &col, float size) const +{ + _box.draw(); + if (!_font || _text.empty()) + return; + + if (_box._visible ) { + int bShift = (_box.getPointY(0) - _box.getPointY(1) - _font->height) / 2; + ksplot_print_text(_font, NULL, + _box.getPointX(0) + _font->height / 4, + _box.getPointY(0) - _font->base - bShift, + _text.c_str()); + } else { + ksplot_print_text(_font, col.color_c_ptr(), + _box.getPointX(0) + _font->height / 4, + _box.getPointY(0) - _font->base, + _text.c_str()); + } +} /** * @brief Create a default Mark. diff --git a/src/KsPlotTools.hpp b/src/KsPlotTools.hpp index 7ffabd8..b270a56 100644 --- a/src/KsPlotTools.hpp +++ b/src/KsPlotTools.hpp @@ -307,6 +307,46 @@ public: virtual ~Rectangle() {} }; +/** + * This class represents a text that is printed/drawn inside a colorful frame. + */ +class TextBox : public PlotObject { +public: + TextBox(); + + TextBox(ksplot_font *f); + + TextBox(ksplot_font *f, const std::string &text, const Point &pos); + + TextBox(ksplot_font *f, const std::string &text, const Color &col, + const Point &pos); + + TextBox(ksplot_font *f, const std::string &text, const Color &col, + const Point &pos, int l, int h = -1); + + /** Destroy the TextBox object. Keep this destructor virtual. */ + virtual ~TextBox() {} + + /** Set the font to be used. */ + void setFont(ksplot_font *f) {_font = f;} + + /** Set the text. */ + void setText(const std::string &t) {_text = t;} + + void setPos(const Point &p); + + void setBoxAppearance(const Color &col, int l, int h); + +private: + void _draw(const Color &, float size = 1.) const override; + + std::string _text; + + ksplot_font *_font; + + Rectangle _box; +}; + /** * This class represents the graphical element of the KernelShark GUI marker. */ From patchwork Mon Jan 4 17:47:19 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11997191 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id C1D3CC433E9 for ; Mon, 4 Jan 2021 17:49:34 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 93033206B2 for ; Mon, 4 Jan 2021 17:49:34 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727318AbhADRte (ORCPT ); Mon, 4 Jan 2021 12:49:34 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34378 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727086AbhADRtd (ORCPT ); Mon, 4 Jan 2021 12:49:33 -0500 Received: from mail-ed1-x534.google.com (mail-ed1-x534.google.com [IPv6:2a00:1450:4864:20::534]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 61764C06138E for ; Mon, 4 Jan 2021 09:48:17 -0800 (PST) Received: by mail-ed1-x534.google.com with SMTP id cw27so28239873edb.5 for ; Mon, 04 Jan 2021 09:48:17 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=0JvNJho6nmQ6wRAwlTsWSUBLgvX8jyU10P1W/zdKzQs=; b=aXBmoeLVpNIu0BL2o3ahXfnGymdpqHbglgM6j5OFBseYzoPY1TjhfZbsgOtEl1rsGe zwMc364bdCq+4dFmpHgdl3cBQrbHOHpoxRnb0VM4eUe0ZfQMNwwjAb6grC4gd3a3dqny CoF7qN0ZBSmGeSQA2EvBLK3p8xFADkONfq8bNgIy92j/VBmpAY/RBVI3eed9LJWV2RXQ 4i6K0Xsom7ak31X7TzVQOWJjuZICioGUpsk769W8hMEJLffdVG6aNAk6lX0tjFDn7y9q r5QZx8W6DtiTceOZYuwfpIWdTgH+puGVmE8tK5Jchb5t7QSsOCDbxEsMk7x4RGCiKI8G lGRw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=0JvNJho6nmQ6wRAwlTsWSUBLgvX8jyU10P1W/zdKzQs=; b=VoKQfzbskf44v/UgxndEcmGNp6tnmrvHXsa3Mb56Vax6IToW35A8EANlA3upywAiLy lfXp/XeyayfVFzs+9nxNpP3HAJzf8v3Q8yN06c26yYJEnVmvRMpmXN+oAQICERN5DtXm zyRR1NHyDV8bM2pohXCldA1WS56OVk0DjoBxxWEEr5Ocw5hLuyMra8ad7JLj3OtknRm9 66XAqYOIOZ/YxJPjqXY4vCNIzDpnG5vxuhtukicZktOOjC7vQGl4zRORW45SVUZOwhch HAA61pCdAmdKHkzSpUqwEdYOm/RTAcS0xG0hKFQilWDFEs6DUMtV2YyR4Bl/iwv15ia3 Y52A== X-Gm-Message-State: AOAM532VZpvrCsmO/Ek/N9NzWRKR31LZcRt/cONKaB3bEgAP4HpsjAJa /1i+BFcXb1EY1zcjoBzURSdkTxqvMbIJtA== X-Google-Smtp-Source: ABdhPJxsng9O9EDakoyqAr8hA6j3rFR37+HWmtiydv47oJgy0F8n1zt/7T0/FYyd5PpLl4bVV7/LUQ== X-Received: by 2002:a05:6402:22e1:: with SMTP id dn1mr73301304edb.347.1609782496150; Mon, 04 Jan 2021 09:48:16 -0800 (PST) Received: from localhost.localdomain ([95.87.199.238]) by smtp.gmail.com with ESMTPSA id l14sm44107750edq.35.2021.01.04.09.48.15 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 04 Jan 2021 09:48:15 -0800 (PST) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v8 39/44] kernel-shark: Consistent method naming in KsPlotTools Date: Mon, 4 Jan 2021 19:47:19 +0200 Message-Id: <20210104174724.70404-40-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210104174724.70404-1-y.karadz@gmail.com> References: <20210104174724.70404-1-y.karadz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org Remove "get" from the name of the methods used to retrieve data fields in KsPlotTools. This makes the naming consistent with the style used by Qt. Signed-off-by: Yordan Karadzhov (VMware) --- src/KsPlotTools.cpp | 30 +++++++++++++++--------------- src/KsPlotTools.hpp | 34 +++++++++++++++++----------------- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/src/KsPlotTools.cpp b/src/KsPlotTools.cpp index ef15cde..4bf08ef 100644 --- a/src/KsPlotTools.cpp +++ b/src/KsPlotTools.cpp @@ -120,7 +120,7 @@ void Color::blend(float alpha) * * @returns ColorTable instance. */ -ColorTable getTaskColorTable() +ColorTable taskColorTable() { struct kshark_context *kshark_ctx(nullptr); int nTasks(0), pid, *pids, i(0), *streamIds; @@ -173,7 +173,7 @@ ColorTable getTaskColorTable() * * @returns ColorTable instance. */ -ColorTable getCPUColorTable() +ColorTable CPUColorTable() { kshark_context *kshark_ctx(nullptr); int nCPUs, nCPUMax(0), *streamIds; @@ -205,7 +205,7 @@ ColorTable getCPUColorTable() * * @returns ColorTable instance. */ -ColorTable getStreamColorTable() +ColorTable streamColorTable() { kshark_context *kshark_ctx(nullptr); ColorTable colors; @@ -360,7 +360,7 @@ void Shape::setPoint(size_t i, const Point &p) * @brief Get the point "i". If the point does not exist, the function returns * nullptr. */ -const ksplot_point *Shape::getPoint(size_t i) const +const ksplot_point *Shape::point(size_t i) const { if (i < _nPoints) return &_points[i]; @@ -394,7 +394,7 @@ void Shape::setPointY(size_t i, int y) { * @brief Get the horizontal coordinate of the point "i". If the point does * not exist, the function returns 0. */ -int Shape::getPointX(size_t i) const { +int Shape::pointX(size_t i) const { if (i < _nPoints) return _points[i].x; @@ -405,7 +405,7 @@ int Shape::getPointX(size_t i) const { * @brief Get the vertical coordinate of the point "i". If the point does * not exist, the function returns 0. */ -int Shape::getPointY(size_t i) const { +int Shape::pointY(size_t i) const { if (i < _nPoints) return _points[i].y; @@ -599,9 +599,9 @@ void TextBox::setBoxAppearance(const Color &col, int l, int h) if (h <= 0 && _font) h = _font->height; - _box.setPoint(1, _box.getPointX(0), _box.getPointY(0) - h); - _box.setPoint(2, _box.getPointX(0) + l, _box.getPointY(0) - h); - _box.setPoint(3, _box.getPointX(0) + l, _box.getPointY(0)); + _box.setPoint(1, _box.pointX(0), _box.pointY(0) - h); + _box.setPoint(2, _box.pointX(0) + l, _box.pointY(0) - h); + _box.setPoint(3, _box.pointX(0) + l, _box.pointY(0)); } void TextBox::_draw(const Color &col, float size) const @@ -611,15 +611,15 @@ void TextBox::_draw(const Color &col, float size) const return; if (_box._visible ) { - int bShift = (_box.getPointY(0) - _box.getPointY(1) - _font->height) / 2; + int bShift = (_box.pointY(0) - _box.pointY(1) - _font->height) / 2; ksplot_print_text(_font, NULL, - _box.getPointX(0) + _font->height / 4, - _box.getPointY(0) - _font->base - bShift, + _box.pointX(0) + _font->height / 4, + _box.pointY(0) - _font->base - bShift, _text.c_str()); } else { ksplot_print_text(_font, col.color_c_ptr(), - _box.getPointX(0) + _font->height / 4, - _box.getPointY(0) - _font->base, + _box.pointX(0) + _font->height / 4, + _box.pointY(0) - _font->base, _text.c_str()); } } @@ -808,7 +808,7 @@ void Graph::_initBins() /** * Get the number of bins. */ -int Graph::size() +int Graph::size() const { return _size; } diff --git a/src/KsPlotTools.hpp b/src/KsPlotTools.hpp index b270a56..287e0fb 100644 --- a/src/KsPlotTools.hpp +++ b/src/KsPlotTools.hpp @@ -64,7 +64,7 @@ public: * @brief Get the frequency value used to generate the Rainbow * palette. */ - static float getRainbowFrequency() {return _frequency;} + static float rainbowFrequency() {return _frequency;} private: ksplot_color _col_c; @@ -76,11 +76,11 @@ private: /** Hash table of colors. */ typedef std::unordered_map ColorTable; -ColorTable getTaskColorTable(); +ColorTable taskColorTable(); -ColorTable getCPUColorTable(); +ColorTable CPUColorTable(); -ColorTable getStreamColorTable(); +ColorTable streamColorTable(); Color getColor(const ColorTable *colors, int id); @@ -143,15 +143,15 @@ public: void setPoint(size_t i, const Point &p); - const ksplot_point *getPoint(size_t i) const; + const ksplot_point *point(size_t i) const; void setPointX(size_t i, int x); void setPointY(size_t i, int y); - int getPointX(size_t i) const; + int pointX(size_t i) const; - int getPointY(size_t i) const; + int pointY(size_t i) const; /** * @brief Get the number of point used to define the polygon. @@ -179,10 +179,10 @@ public: virtual ~Point() {} /** @brief Get the horizontal coordinate of the point. */ - int x() const {return getPointX(0);} + int x() const {return pointX(0);} /** @brief Get the vertical coordinate of the point. */ - int y() const {return getPointY(0);} + int y() const {return pointY(0);} /** @brief Set the horizontal coordinate of the point. */ void setX(int x) {setPointX(0, x);} @@ -201,7 +201,7 @@ public: /** * @brief Get the C struct defining the point. */ - const ksplot_point *point_c_ptr() const {return getPoint(0);} + const ksplot_point *point_c_ptr() const {return point(0);} private: void _draw(const Color &col, float size = 1.) const override; @@ -235,7 +235,7 @@ public: void setA(int x, int y) { setPoint(0, x, y);} /** @brief Get the first finishing point of the line. */ - const ksplot_point *getA() const {return getPoint(0);} + const ksplot_point *a() const {return point(0);} /** * @brief Set the coordinats of the second finishing point of the @@ -247,7 +247,7 @@ public: void setB(int x, int y) {setPoint(1, x, y);} /** @brief Get the second finishing point of the line. */ - const ksplot_point *getB() const {return getPoint(1);} + const ksplot_point *b() const {return point(1);} private: void _draw(const Color &col, float size = 1.) const override; @@ -408,7 +408,7 @@ public: void drawVal(float size = 2.); /** Get the height (module) of the line, representing the Bin. */ - int mod() {return _val.y() - _base.y();} + int mod() const {return _val.y() - _base.y();} /** @brief Set the vertical coordinate of the "val" Point. */ void setVal(int v) {_val.setY(_base.y() - v); } @@ -458,7 +458,7 @@ public: /* Keep this destructor virtual. */ virtual ~Graph(); - int size(); + int size() const; void setModelPtr(kshark_trace_histo *histo); @@ -485,12 +485,12 @@ public: void setBase(int b); /** @brief Get the vertical coordinate of the Graph's base. */ - int getBase() const {return _bins[0]._base.y();} + int base() const {return _bins[0]._base.y();} void setHeight(int h); /** @brief Get the vertical size (height) of the Graph. */ - int getHeight() const {return _height;} + int height() const {return _height;} void setBinValue(int bin, int val); @@ -504,7 +504,7 @@ public: const Color &col, uint8_t m); /** @brief Get a particular bin. */ - const Bin &getBin(int bin) const {return _bins[bin];} + const Bin &bin(int bin) const {return _bins[bin];} void setHMargin(int hMargin); From patchwork Mon Jan 4 17:47:20 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11997193 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id E21E5C433E6 for ; Mon, 4 Jan 2021 17:49:34 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id C101E20700 for ; Mon, 4 Jan 2021 17:49:34 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727086AbhADRte (ORCPT ); Mon, 4 Jan 2021 12:49:34 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34384 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727182AbhADRte (ORCPT ); Mon, 4 Jan 2021 12:49:34 -0500 Received: from mail-ed1-x533.google.com (mail-ed1-x533.google.com [IPv6:2a00:1450:4864:20::533]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 6E9D2C061574 for ; Mon, 4 Jan 2021 09:48:18 -0800 (PST) Received: by mail-ed1-x533.google.com with SMTP id b2so28272712edm.3 for ; Mon, 04 Jan 2021 09:48:18 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=zX3Jx/rqMTuIRQADtuT1k2P3h10g6nJEdPWVUEWe854=; b=tVmzTa0KHGnGTfUxfVrROkucxIC3LWHuidckooxoTY2KD1EL+J3t6Q8+rqDyqhOMXR TQdvFlQY3cAjv/7bqgUbGLbFNtOATxQ2UzUzPxMz5IYAHJrQAN2Eu8+mNePjD5ELwknS lVl10tKVpGECEG7hjLzcoP+w+jikKUsJQYYzm3rq106gOTyrFuW+4K5H17RqWyn0t1oI fyqR4m+ySCK4Ed2M5fLqb4fSbpkqKvXXJaFuDpYhWSbl7ELTA9rEpkd4uAejAjF8IhC3 VK5hipvwEWqP+Veplsa+bSzBaNQ0x+FKkP3YAU3DO2tmGIFQOFfGIow6k02Np1XUaPQn 6wvQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=zX3Jx/rqMTuIRQADtuT1k2P3h10g6nJEdPWVUEWe854=; b=iJ09bzp71h+toBPdTLG+LHq+kPqRpSRvJ0Hp2pc/plU7h5zCSy4e0g+itKUYkfZYRy yKyQR6403AL75Mrc9Zqi/P0lOIlRCvFiD83A9hhXd0aN4HTcSDYCkkqqI0IIiEQqbui7 //C4DyI3cpXjamN0h0NA11HlbAhqi930Byfer2QOWp176HRxRRNmzcuJ9wVhykPrRrTw rjoZVDMP4FJBApdfDM5pObWfgv/HxHskX2K/RQvES9WZ8O7x2R/8zAXBvum38F8BVXIV 0zvJ6cZ8cLQzpTMxXOSklYYYibuweqDZJM6SznGQK57HkPMPT7xSIk2xvJoKD4SiUlf8 Mhug== X-Gm-Message-State: AOAM532laoOsR68z4uD+BDkButzojfnoM4wJESkclHrTSxNUGc7sincm jgt/LZcs+SBACrdO+KiUp8A= X-Google-Smtp-Source: ABdhPJzNbfT7uo9S7UB3oIDl7SuUukpRg8PWeJVBkntLuuNWtaPnei78AP0AzRQB035tay2f0zdPzw== X-Received: by 2002:aa7:dcd0:: with SMTP id w16mr72439201edu.229.1609782497187; Mon, 04 Jan 2021 09:48:17 -0800 (PST) Received: from localhost.localdomain ([95.87.199.238]) by smtp.gmail.com with ESMTPSA id l14sm44107750edq.35.2021.01.04.09.48.16 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 04 Jan 2021 09:48:16 -0800 (PST) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v8 40/44] kernel-shark: Make the label part of the graph Date: Mon, 4 Jan 2021 19:47:20 +0200 Message-Id: <20210104174724.70404-41-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210104174724.70404-1-y.karadz@gmail.com> References: <20210104174724.70404-1-y.karadz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org The capability to draw text with OpenGL is used. With this functionality we will no longer need to create special Qt widgets containing the text of the labels and carefully align those widgets next to the OpenGL plotting area. This will greatly simplify the internel structure of the TraceGraph widget. Signed-off-by: Yordan Karadzhov (VMware) --- src/KsPlotTools.cpp | 34 +++++++++++++++++++++++++--------- src/KsPlotTools.hpp | 18 +++++++++++++++--- 2 files changed, 40 insertions(+), 12 deletions(-) diff --git a/src/KsPlotTools.cpp b/src/KsPlotTools.cpp index 4bf08ef..d952f5e 100644 --- a/src/KsPlotTools.cpp +++ b/src/KsPlotTools.cpp @@ -759,6 +759,7 @@ Graph::Graph() _collectionPtr(nullptr), _binColors(nullptr), _ensembleColors(nullptr), + _label(), _zeroSuppress(false) {} @@ -777,6 +778,7 @@ Graph::Graph(kshark_trace_histo *histo, KsPlot::ColorTable *bct, KsPlot::ColorTa _collectionPtr(nullptr), _binColors(bct), _ensembleColors(ect), + _label(), _zeroSuppress(false) { if (!_bins) { @@ -795,10 +797,15 @@ Graph::~Graph() delete[] _bins; } +int Graph::_firstBinOffset() +{ + return _labelSize + 2 * _hMargin; +} + void Graph::_initBins() { for (int i = 0; i < _size; ++i) { - _bins[i]._base.setX(i + _hMargin); + _bins[i]._base.setX(i + _firstBinOffset()); _bins[i]._base.setY(0); _bins[i]._val.setX(_bins[i]._base.x()); _bins[i]._val.setY(_bins[i]._base.y()); @@ -852,6 +859,8 @@ void Graph::setBase(int b) if (b == _bins[0]._base.y()) // Nothing to do. return; + _label.setBoxAppearance(_label._color, _labelSize, height()); + for (int i = 0; i < _size; ++i) { mod = _bins[i].mod(); _bins[i]._base.setY(b); @@ -870,24 +879,29 @@ void Graph::setHeight(int h) } /** - * @brief Set the size of the white space added on both sides of the Graph. + * @brief Set the color and the dimensions of the graph's label. * - * @param hMargin: the size of the white space in pixels. + * @param f: The font to be used to draw the labels. + * @param col: The color of the ComboGraph's label. + * @param lSize: the size of the graph's label in pixels. + * @param hMargin: the size of the white margine space in pixels. */ -void Graph::setHMargin(int hMargin) +void Graph::setLabelAppearance(ksplot_font *f, Color col, int lSize, int hMargin) { if (!_size) return; - if (hMargin == _bins[0]._base.x()) // Nothing to do. - return; + _labelSize = lSize; + _hMargin = hMargin; + + _label.setPos({_hMargin, base()}); + _label.setFont(f); + _label.setBoxAppearance(col, lSize, height()); for (int i = 0; i < _size; ++i) { - _bins[i]._base.setX(i + hMargin); + _bins[i]._base.setX(i + _firstBinOffset()); _bins[i]._val.setX(_bins[i]._base.x()); } - - _hMargin = hMargin; } /** @@ -1312,6 +1326,8 @@ void Graph::draw(float size) int lastPid(-1), b(0), boxH(_height * .3); Rectangle taskBox; + _label.draw(); + /* * Start by drawing a line between the base points of the first and * the last bin. diff --git a/src/KsPlotTools.hpp b/src/KsPlotTools.hpp index 287e0fb..8f530b0 100644 --- a/src/KsPlotTools.hpp +++ b/src/KsPlotTools.hpp @@ -506,7 +506,11 @@ public: /** @brief Get a particular bin. */ const Bin &bin(int bin) const {return _bins[bin];} - void setHMargin(int hMargin); + /** Set the text of the graph's label. */ + void setLabelText(std::string text) {_label.setText(text);} + + void setLabelAppearance(ksplot_font *f, Color col, + int lSize, int hMargin); /** * Check if this graph is Zero Suppressed. Zero Suppressed means that @@ -521,7 +525,7 @@ public: */ void setZeroSuppressed(bool zs) {_zeroSuppress = zs;} -private: +protected: /** Pointer to the model descriptor object. */ kshark_trace_histo *_histoPtr; @@ -537,6 +541,9 @@ private: */ int _hMargin; + /** The horizontal size of the Graph's label. */ + int _labelSize; + /** The vertical size (height) of the Graph. */ int _height; @@ -549,9 +556,14 @@ private: /** Hash table of ensemble's colors. */ ColorTable *_ensembleColors; +private: + TextBox _label; + bool _zeroSuppress; - void _initBins(); + void _initBins(); + + int _firstBinOffset(); }; }; // KsPlot From patchwork Mon Jan 4 17:47:21 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11997199 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 14348C43381 for ; Mon, 4 Jan 2021 17:49:35 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id E66D3206B2 for ; Mon, 4 Jan 2021 17:49:34 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727182AbhADRte (ORCPT ); Mon, 4 Jan 2021 12:49:34 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34376 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727091AbhADRte (ORCPT ); Mon, 4 Jan 2021 12:49:34 -0500 Received: from mail-ed1-x533.google.com (mail-ed1-x533.google.com [IPv6:2a00:1450:4864:20::533]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 93536C061793 for ; Mon, 4 Jan 2021 09:48:19 -0800 (PST) Received: by mail-ed1-x533.google.com with SMTP id h16so28204785edt.7 for ; Mon, 04 Jan 2021 09:48:19 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=vMtoru4ysgIyI4OZqBtUfzLnZQxv1b5TDuVhnuc4b6I=; b=eCkJFwhyohhn2pXYgz2qzYQRUeDMvO141Bmf3/2hNWLj268LZqRvhwJNpXjiuuhf29 F366NDibXcdiG1annnrsDoHvD8VA+Z3blELRWb+pdcZZ70VDj+iMoskoaFvKvLNGy6tM pn/X53vsSv/fQdTszsPAK/OOVbos2BnY5Ryo76AzB2MKboSjv360aMZeBQJoIU9eqSDo kTZ8atpu0gANLfhHlNcd2U8uaZO/a0nJCJC9SRbr5Gj4KVmV/u1M3GN008Chj8Euzbni ktS1Y1smiHQIAfsBAp9kkDJ3UDO6wqe8EqGiEtS4P5ZLH+czbDTSfgwEvppTCeMe9Kcc zPoA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=vMtoru4ysgIyI4OZqBtUfzLnZQxv1b5TDuVhnuc4b6I=; b=amrFX1dJuN+AvpkMVei0SS42IcQizcbC2tubOHZaDbeZ3SYz4Qk3RKlJWKr9NgLFaJ neoUiYPID9CksXo6I7fldHNRyIj/+SHYIVSoUd86JEHdt0d9gsPauguKnnV9xHAhm3dV vuB6lus3wq0iahAq4+myxlerFA8i5b6q+l5ONnGEvflMd0NrlfdjHwLDpHh8aoHwXuow 878/oDZjXC3rwP5qZWPbBUkQOGVQ2/uX3MOvhrgYYSlcI5lrKP1NmwCp46d0xNorw6xa kIEF3trcfvxqOMPJ3c/QRI1PfSm6vo6H9kDTxCb+tJDbEF9W71Xq4hUZ8b7F5qNTBRrK 88Vw== X-Gm-Message-State: AOAM531fnPLAp2IAj5v0Yc/WVbEJYCZVi15p/oJ//JmCASS4n3f0ph+4 0NnQ7ggvV5g/5j7vaGrqq/JxL3fKBD0bVw== X-Google-Smtp-Source: ABdhPJzm4CDRg2v0LHYUWAfBY2CiwxFMwLb/lcmClHa0FgvNdr46jiSTpxjWcbkU+ECIGBua2pRiqQ== X-Received: by 2002:a05:6402:1045:: with SMTP id e5mr72478437edu.40.1609782498335; Mon, 04 Jan 2021 09:48:18 -0800 (PST) Received: from localhost.localdomain ([95.87.199.238]) by smtp.gmail.com with ESMTPSA id l14sm44107750edq.35.2021.01.04.09.48.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 04 Jan 2021 09:48:17 -0800 (PST) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v8 41/44] kernel-shark: Remove the hard-coded Idle PID Date: Mon, 4 Jan 2021 19:47:21 +0200 Message-Id: <20210104174724.70404-42-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210104174724.70404-1-y.karadz@gmail.com> References: <20210104174724.70404-1-y.karadz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org KernelShark plots differently all events coming from Idle tasks. This is done in order to provide more intuitive visualization of the Idleness of the system. So far the process Id of the Idle task was assumed to be Zero. However this is true only for Linux systems. In this patch we make the PID of the Idle task configurable. Signed-off-by: Yordan Karadzhov (VMware) --- src/KsPlotTools.cpp | 48 +++++++++++++++++++++++++++++++++------------ src/KsPlotTools.hpp | 22 +++++++++------------ 2 files changed, 44 insertions(+), 26 deletions(-) diff --git a/src/KsPlotTools.cpp b/src/KsPlotTools.cpp index d952f5e..529f737 100644 --- a/src/KsPlotTools.cpp +++ b/src/KsPlotTools.cpp @@ -755,12 +755,13 @@ Graph::Graph() : _histoPtr(nullptr), _bins(nullptr), _size(0), - _hMargin(30), _collectionPtr(nullptr), _binColors(nullptr), _ensembleColors(nullptr), _label(), - _zeroSuppress(false) + _idleSuppress(false), + _idlePid(0), + _drawBase(true) {} /** @@ -774,12 +775,13 @@ Graph::Graph(kshark_trace_histo *histo, KsPlot::ColorTable *bct, KsPlot::ColorTa : _histoPtr(histo), _bins(new(std::nothrow) Bin[histo->n_bins]), _size(histo->n_bins), - _hMargin(30), _collectionPtr(nullptr), _binColors(bct), _ensembleColors(ect), _label(), - _zeroSuppress(false) + _idleSuppress(false), + _idlePid(0), + _drawBase(true) { if (!_bins) { _size = 0; @@ -904,6 +906,20 @@ void Graph::setLabelAppearance(ksplot_font *f, Color col, int lSize, int hMargin } } +/** + * @brief Set Idle Suppression. If True, the bins containing Idle task records + * are not grouped together. + * + * @param is: If True, Idle is suppressed. + * @param ip: The process Id of the Idle task. If Idle is not suppressed, this + * value has no effect. + */ +void Graph::setIdleSuppressed(bool is, int ip) +{ + _idleSuppress = is; + _idlePid = ip; +} + /** * @brief Set the value of a given bin. * @@ -916,7 +932,7 @@ void Graph::setBinValue(int bin, int val) } /** - * @brief Set the Process Id (Front and Back) a given bin. + * @brief Set the Process Id (Front and Back) of a given bin. * * @param bin: Bin Id. * @param pidF: The Process Id detected at the from (first in time) edge of @@ -1328,22 +1344,28 @@ void Graph::draw(float size) _label.draw(); - /* - * Start by drawing a line between the base points of the first and - * the last bin. - */ - drawLine(_bins[0]._base, _bins[_size - 1]._base, {}, size); + if (_drawBase) { + /* + * Start by drawing a line between the base points of the first and + * the last bin. + */ + drawLine(_bins[0]._base, _bins[_size - 1]._base, {}, size); + } /* Draw as vartical lines all bins containing data. */ for (int i = 0; i < _size; ++i) - if (_bins[i]._idFront >= 0 || _bins[i]._idBack >= 0) + if (_bins[i]._idFront >= 0 || _bins[i]._idBack >= 0 || + _bins[i]._idFront == _idlePid || _bins[i]._idBack ==_idlePid) if (_bins[i]._visMask & KS_EVENT_VIEW_FILTER_MASK) { _bins[i]._size = size; _bins[i].draw(); } auto lamCheckEnsblVal = [this] (int v) { - return v > 0 || (v == 0 && !this->_zeroSuppress); + if (_idleSuppress && v == _idlePid) + return false; + + return v >= 0; }; /* @@ -1424,4 +1446,4 @@ void Graph::draw(float size) } } -}; // KsPlot +} // KsPlot diff --git a/src/KsPlotTools.hpp b/src/KsPlotTools.hpp index 8f530b0..5ce8f6f 100644 --- a/src/KsPlotTools.hpp +++ b/src/KsPlotTools.hpp @@ -449,7 +449,7 @@ public: */ Graph(const Graph &) = delete; - /* Disable moving. Same as copying.*/ + /* Disable moving. Same as copying. */ Graph(Graph &&) = delete; Graph(kshark_trace_histo *histo, KsPlot::ColorTable *bct, @@ -512,18 +512,10 @@ public: void setLabelAppearance(ksplot_font *f, Color col, int lSize, int hMargin); - /** - * Check if this graph is Zero Suppressed. Zero Suppressed means that - * bins having Id value = 0 (Idle task records) are not grouped - * together. - */ - bool zeroSuppressed(bool zs) {return _zeroSuppress;} + void setIdleSuppressed(bool is, int ip = 0); - /** - * Set Zero Suppression. If True, the bins having Id value = 0 (Idle - * task records) are not grouped together. - */ - void setZeroSuppressed(bool zs) {_zeroSuppress = zs;} + /** Draw the base line of the graph or not. */ + void setDrawBase(bool b) {_drawBase = b;} protected: /** Pointer to the model descriptor object. */ @@ -559,7 +551,11 @@ protected: private: TextBox _label; - bool _zeroSuppress; + bool _idleSuppress; + + int _idlePid; + + bool _drawBase; void _initBins(); From patchwork Mon Jan 4 17:47:22 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11997195 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 34350C433E0 for ; Mon, 4 Jan 2021 17:49:35 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 1042E20700 for ; Mon, 4 Jan 2021 17:49:35 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727091AbhADRte (ORCPT ); Mon, 4 Jan 2021 12:49:34 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34380 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727217AbhADRte (ORCPT ); Mon, 4 Jan 2021 12:49:34 -0500 Received: from mail-ed1-x52c.google.com (mail-ed1-x52c.google.com [IPv6:2a00:1450:4864:20::52c]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 846FEC06138F for ; Mon, 4 Jan 2021 09:48:20 -0800 (PST) Received: by mail-ed1-x52c.google.com with SMTP id y24so28198359edt.10 for ; Mon, 04 Jan 2021 09:48:20 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=SyimfIUcyOxEzAGqudBgzM4qdeXWlI+NU+A9xhnpx+8=; b=QfkTXR/F4Q/YX1Vhh/tt8zQQc/hLklfU3y/qrpgFU7HZJMzwJpQFUfT9NjW53eTvbe ZDkQcpPG8TOVerXOG2NrwmiCkVT5Nk9olIaNa1EswCIFgeZlstNsLIC67zUTlZlEPTuo Kds7+AbA6mB5+Av+G1dE4ExCkPClQRsV6BdMIrOdFoYGCWx8EjXeZMypuE9/FrtxOPHq t4VC6kaZMhZ8TeWc2EibhzNQQjkCVbrirLcRPOdpNbhSAIZLw154NSLT+wuRVxn3ElK6 y65QTJkdiYsSsJldaoR7cjzCwQg2rR/VjrnoQroMS1XB0V7FylUM7s+FTaRF4Iks60fY dpRw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=SyimfIUcyOxEzAGqudBgzM4qdeXWlI+NU+A9xhnpx+8=; b=bJpDFyIfQtI/WohHVYf8f9az155+yBZNYbDtCbC35k8AZYg5i15PYHG29DaVYC7/Q3 l3zZxTksSscLa2dr5YTRLiP7FsMytx/VvfuqDMjs5yB+8WsWt+ob2/kgibRkP2Tocs2V 7cLAuiUTUCeI0RJCbeFc0gebtCDzqaocKOpw23WdAMTy2baTultr/6/xp1XvoUMRwL9S xMNu/rDp0akUvM0bzAHAweyLFpcoX1TMgIWR8P4AiODW147c0rlA+ggizHKka6Iwpluv EjJetewkWt421P5BMSiUpATVp0Eiui6untVTBC7yPYuGCAcNm4YVZtusRy23jmaIaTbo 62Ew== X-Gm-Message-State: AOAM532XD4X/nJz8oNN6BaIFSUxWAW7l9xwS7wWpPRI0jMJQZp/RNtBK ZaHn3YVGvFNGlzI9zK6W8eP4zae9vsFB2g== X-Google-Smtp-Source: ABdhPJxwFWJwIu03DuGHVivXdP51LcEVtMG/Njo0SBffklRpcRN2zCo1WZF0VMH8LGjE+sc3xAFdQw== X-Received: by 2002:a50:ed04:: with SMTP id j4mr73342894eds.84.1609782499319; Mon, 04 Jan 2021 09:48:19 -0800 (PST) Received: from localhost.localdomain ([95.87.199.238]) by smtp.gmail.com with ESMTPSA id l14sm44107750edq.35.2021.01.04.09.48.18 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 04 Jan 2021 09:48:18 -0800 (PST) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v8 42/44] kernel-shark: Add class Polyline to KsPlot namespace Date: Mon, 4 Jan 2021 19:47:22 +0200 Message-Id: <20210104174724.70404-43-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210104174724.70404-1-y.karadz@gmail.com> References: <20210104174724.70404-1-y.karadz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org This is a simple plotting class that represents a poly-line. Signed-off-by: Yordan Karadzhov (VMware) --- src/KsPlotTools.cpp | 14 ++++++++++++++ src/KsPlotTools.hpp | 16 ++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/src/KsPlotTools.cpp b/src/KsPlotTools.cpp index 529f737..9f98386 100644 --- a/src/KsPlotTools.cpp +++ b/src/KsPlotTools.cpp @@ -503,6 +503,20 @@ void Line::_draw(const Color &col, float size) const col.color_c_ptr(), size); } +/** + * @brief Create a default polyline. All points are initialized at (0, 0). + * + * @param n: The number of points connected by the polyline. + */ +Polyline::Polyline(size_t n) +: Shape(n) +{} + +void Polyline::_draw(const Color &col, float size) const +{ + ksplot_draw_polyline(_points, _nPoints, col.color_c_ptr(), size); +} + /** * @brief Create a default polygon. All points are initialized at (0, 0). * diff --git a/src/KsPlotTools.hpp b/src/KsPlotTools.hpp index 5ce8f6f..4a7ca0a 100644 --- a/src/KsPlotTools.hpp +++ b/src/KsPlotTools.hpp @@ -253,6 +253,22 @@ private: void _draw(const Color &col, float size = 1.) const override; }; +/** This class represents a polyline. */ +class Polyline : public Shape { +public: + Polyline(size_t n); + + /** + * @brief Destroy the polyline object. Keep this destructor virtual. + */ + virtual ~Polyline() {} + +private: + Polyline() = delete; + + void _draw(const Color &, float size = 1.) const override; +}; + /** This class represents a polygon. */ class Polygon : public Shape { public: From patchwork Mon Jan 4 17:47:23 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11997197 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 61FDCC4332D for ; Mon, 4 Jan 2021 17:49:35 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 43AF9206B2 for ; Mon, 4 Jan 2021 17:49:35 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727217AbhADRtf (ORCPT ); Mon, 4 Jan 2021 12:49:35 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34382 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727310AbhADRte (ORCPT ); Mon, 4 Jan 2021 12:49:34 -0500 Received: from mail-ej1-x629.google.com (mail-ej1-x629.google.com [IPv6:2a00:1450:4864:20::629]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 674C1C061342 for ; Mon, 4 Jan 2021 09:48:21 -0800 (PST) Received: by mail-ej1-x629.google.com with SMTP id q22so37960323eja.2 for ; Mon, 04 Jan 2021 09:48:21 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=ZmP1cMwJams9Qph5p5JmU94mt6kQfjBZBdjETGroLAw=; b=TOnwION65do4Lfzc507R671yOfo1LWEELVayH4KC75UvC1ZLswsEWDG7mqtiefSh6t x+Xw2a2uB6gMleAl2mAExpq+458ET52udWDsmz4iOZWdhLAK3BrcePSdLrlErZmW0FQv xeyY2G2/F8An+cYUGV4qcJTvkYafnWdTY0As7X1TUcfusJ+ulQShdfL6A26qdn+eazv1 RB7qAP5be0rK11/R3ujEXMnHoaDGiT8r05L8Xn8KXkOVnTvG8IIEwfwVyGPBEIe35MpM kgObyEmqaPT0PJ+YeIUgOVFr8TN6SKfYntAUB1Tl0sMMYdJxk1qk0d/KgmAX+F5bPbOY SoOA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=ZmP1cMwJams9Qph5p5JmU94mt6kQfjBZBdjETGroLAw=; b=D0nLke4/SaNtL5/RjYD4wAg5ZE7u5+BE2slGUdOqXxb26unQ3kEbD8/hIzYNUuRr7t ZuLSZm4NDV4HXb1Vgy00OZWpIG8I3IVk6+ABd9Va9YHVn8jT8cFhCoWmuK8ICRKKS37L 3LbDgUhIO4o0hfnA5BansJMz46Nz3psdqscJ0j+wx8IhcdsXk5n5KfkHO53X3eMY7bsc Mdrl7t3Nefkz4jpPX2C34TBGUC3pjo+U7Fqr6St/HIiTic6gMBWlDU9+85X8WMvQgLWN P4gjXVis51zwUIjFqvY7nbnZCOFNR9vYgCSZO5GRbPlxPZQwymzKNTrp5xzqQjDAbQGF +LFg== X-Gm-Message-State: AOAM5318X2ta9VkRlaC9UUsxkc6iWF2xlDIzMB8DWQ9FdP0fULrw4VhP n1Z+bxhLoXB+PtowL5fVT/M= X-Google-Smtp-Source: ABdhPJwz+l4B4Hr9LUqphY9CnXe+twdJrclh7GXnweTvca/ejtPffXrSBrarv/HneAvzaTGcSUXhsg== X-Received: by 2002:a17:906:8255:: with SMTP id f21mr66514370ejx.265.1609782500215; Mon, 04 Jan 2021 09:48:20 -0800 (PST) Received: from localhost.localdomain ([95.87.199.238]) by smtp.gmail.com with ESMTPSA id l14sm44107750edq.35.2021.01.04.09.48.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 04 Jan 2021 09:48:19 -0800 (PST) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v8 43/44] kernel-shark: Add VirtBridge and VirtGap classes Date: Mon, 4 Jan 2021 19:47:23 +0200 Message-Id: <20210104174724.70404-44-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210104174724.70404-1-y.karadz@gmail.com> References: <20210104174724.70404-1-y.karadz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org The two simple plotting classes will be used to visualise the guest->host data correlations. Signed-off-by: Yordan Karadzhov (VMware) --- src/KsPlotTools.cpp | 22 +++++++++++++++++++++ src/KsPlotTools.hpp | 47 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/src/KsPlotTools.cpp b/src/KsPlotTools.cpp index 9f98386..8d38009 100644 --- a/src/KsPlotTools.cpp +++ b/src/KsPlotTools.cpp @@ -1460,4 +1460,26 @@ void Graph::draw(float size) } } +void VirtGap::_draw(const Color &col, float size) const +{ + if (_entryPoint.x() - _exitPoint.x() < 4) + return; + + Point p0(_exitPoint.x() + _size, _exitPoint.y()); + Point p1(_exitPoint.x() + _size, _exitPoint.y() - _height); + Point p2(_entryPoint.x() - _size , _entryPoint.y()); + Point p3(_entryPoint.x() - _size , _entryPoint.y() - _height); + + Rectangle g; + + g.setPoint(0, p0); + g.setPoint(1, p1); + g.setPoint(2, p2); + g.setPoint(3, p3); + + g._color = {255, 255, 255}; // The virt. gap is always white. + g.setFill(false); + g.draw(); +} + } // KsPlot diff --git a/src/KsPlotTools.hpp b/src/KsPlotTools.hpp index 4a7ca0a..b9b93f3 100644 --- a/src/KsPlotTools.hpp +++ b/src/KsPlotTools.hpp @@ -578,6 +578,53 @@ private: int _firstBinOffset(); }; +/** + * This class represents the graphical element visualizing how the execution + * goes from the host to the guest and back. + */ +class VirtBridge : public Polyline { +public: + /** Create a default VirtBridge. */ + VirtBridge() : Polyline(4) {} + + /* Keep this destructor virtual. */ + virtual ~VirtBridge() {} + + /** Set the coordinates of the EntryHost point of the VirtBridge. */ + void setEntryHost(int x, int y) {setPoint(0, x, y);} + + /** Set the coordinates of the EntryGuest point of the VirtBridge. */ + void setEntryGuest(int x, int y) {setPoint(1, x, y);} + + /** Set the coordinates of the ExitGuest point of the VirtBridge. */ + void setExitGuest(int x, int y) {setPoint(2, x, y);} + + /** Set the coordinates of the ExitHost point of the VirtBridge. */ + void setExitHost(int x, int y) {setPoint(3, x, y);} +}; + +/** + * This class represents the graphical element visualizing the time interval + * in the guest during which the execution has been returned to the host. + */ +class VirtGap : public Shape { +public: + /** Create a VirtGap with height "h". */ + VirtGap(int h) :_height(h) {} + + /** The point where the execution exits the VM. */ + Point _exitPoint; + + /** The point where the execution enters the VM. */ + Point _entryPoint; + +private: + /** The vertical size (height) of the graphical element. */ + int _height; + + void _draw(const Color &col, float size) const; +}; + }; // KsPlot #endif /* _KS_PLOT_TOOLS_H */ From patchwork Mon Jan 4 17:47:24 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11997215 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-15.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 987D9C433E9 for ; Mon, 4 Jan 2021 17:49:44 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 6C35620770 for ; Mon, 4 Jan 2021 17:49:44 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727415AbhADRto (ORCPT ); Mon, 4 Jan 2021 12:49:44 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34356 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727525AbhADRtn (ORCPT ); Mon, 4 Jan 2021 12:49:43 -0500 Received: from mail-ed1-x52a.google.com (mail-ed1-x52a.google.com [IPv6:2a00:1450:4864:20::52a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5372CC061794 for ; Mon, 4 Jan 2021 09:48:22 -0800 (PST) Received: by mail-ed1-x52a.google.com with SMTP id c7so28237134edv.6 for ; Mon, 04 Jan 2021 09:48:22 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=jgl455XwHK5OdxmBO7abn5gsz+5Hs22napmQo7sYKTw=; b=H4bpt9NJk0Mtv29c0+eVdub/4/vlrdVbs5Vg26cf2F22GIordb+Wh37r6/SDo8YijF ic5JdCGbcqozSDt62fdO0D3JsjWgBiwY/sctlgWD75/Mk6ccs/SlUz8GwOkoSCwq++cW JFfITGFVg2S3+Tgrqg8c3zEeHF5eCNH0GZGct+smZ6ynC/Iehj3uwSzGCQNhr7jFgCdN Jv+9jt9UDpboLC4xwK/Kk6bgUH54Mr/aAarvSxymB+pDiKj/xuFiUk8rqhW4JiF7Sy2n z5RGPoYA3Tnryx7Yc9IpZlV3DdrE/4bC2y+Owy6Kk96rmh6041GD/09wZXd34Xt1rx5H Jr0A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=jgl455XwHK5OdxmBO7abn5gsz+5Hs22napmQo7sYKTw=; b=QqGzIJPQt/2g2XvM6ALdngpaWjaJ8JmE49amrambktEjDiz2RY2PRnjOCxFcvjA8j8 u3QHKbxpRIOLF6rN6MeuWjUWbPRCow2Kwj0eVG8Si8ipwfgeyse2GIAPE/mwbjl9bZwQ bWkr42dQxjZOROHm/cdvH/9h0Y5yU94NdUBt0dGt6nuDhGyPDqPE47l3lZVp5b81QXCo eT0hCfD4BLFIOZOX0UOz5wXojdsmdva17BhoN1cjjU8TBCmIhD+xQ5s+irF8jFiZhhHh Kvc7l1H36YhjGAtRDPGGXPDG6YHNlLmWbhw9LsXd6xsXczRH1FjnPQ0K8WbJb7tLffay 8cng== X-Gm-Message-State: AOAM531PSd5Sw3DnJoAj4kIO/PGytU+wAEfocDPNZIyygX/bDrDscrtx w5ZUw6starf7CUs43guvyZQ= X-Google-Smtp-Source: ABdhPJwprN6LxrOCNzpMuEqQHcxWmrBQRqaAY4yJ4Dokb35LN7vr7r23KBTahia/Yv0LzdoD+gX5Ug== X-Received: by 2002:a50:ac86:: with SMTP id x6mr20058616edc.197.1609782501057; Mon, 04 Jan 2021 09:48:21 -0800 (PST) Received: from localhost.localdomain ([95.87.199.238]) by smtp.gmail.com with ESMTPSA id l14sm44107750edq.35.2021.01.04.09.48.20 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 04 Jan 2021 09:48:20 -0800 (PST) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v8 44/44] kernel-shark: Add double click interface to PlotObject Date: Mon, 4 Jan 2021 19:47:24 +0200 Message-Id: <20210104174724.70404-45-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210104174724.70404-1-y.karadz@gmail.com> References: <20210104174724.70404-1-y.karadz@gmail.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org The interface consists of two virtual methods that have to be implemented by the inheriting classes. The first implements the double-click action of the object while the second one calculates the distance between the object and the exact position of the mouse click. The second method is used by the GUI to decide if the double-click action has to be executed or not. The patch contains a simple method for retrieving the geometrical center of a Shape. This helper method can be useful when implementing the calculation of the distance in the inheriting classes. Signed-off-by: Yordan Karadzhov (VMware) --- src/KsPlotTools.cpp | 31 +++++++++++++++++++++++++++++++ src/KsPlotTools.hpp | 14 ++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/src/KsPlotTools.cpp b/src/KsPlotTools.cpp index 8d38009..ac9c5b2 100644 --- a/src/KsPlotTools.cpp +++ b/src/KsPlotTools.cpp @@ -251,6 +251,21 @@ Color getColor(const ColorTable *colors, int id) return {}; } +/** + * @brief A method to be implemented in the inheriting class. It calculates + * the distance between the position of the click and the shape. This + * distance is used by the GUI to decide if the corresponding + * "Double click" action of the shape must be executed. The default + * implementation returns infinity. + * + * @param x: The X coordinate of the click. + * @param y: The Y coordinate of the click. + */ +double PlotObject::distance(int x, int y) const +{ + return std::numeric_limits::max(); +} + /** * @brief Create a default Shape. */ @@ -298,6 +313,22 @@ Shape::~Shape() { delete[] _points; } +/** @brief Get the coordinates of the geometrical center of this shape. */ +ksplot_point Shape::center() const +{ + ksplot_point c = {0, 0}; + + for (size_t i = 0; i < _nPoints; ++i) { + c.x += _points[i].x; + c.y += _points[i].y; + } + + c.x /= _nPoints; + c.y /= _nPoints; + + return c; +} + /** Assignment operator. */ void Shape::operator=(const Shape &s) { diff --git a/src/KsPlotTools.hpp b/src/KsPlotTools.hpp index b9b93f3..c993181 100644 --- a/src/KsPlotTools.hpp +++ b/src/KsPlotTools.hpp @@ -103,6 +103,16 @@ public: _draw(_color, _size); } + /** + * Generic action to be executed when the objects is double clicked. + */ + void doubleClick() const { + if (_visible) + _doubleClick(); + } + + virtual double distance(int x, int y) const; + /** Is this object visible. */ bool _visible; @@ -114,6 +124,8 @@ public: private: virtual void _draw(const Color &col, float s) const = 0; + + virtual void _doubleClick() const {} }; /** List of graphical element. */ @@ -135,6 +147,8 @@ public: /* Keep this destructor virtual. */ virtual ~Shape(); + ksplot_point center() const; + void operator=(const Shape &s); void setPoint(size_t i, int x, int y);