From patchwork Mon Oct 12 13:35:04 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11832799 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id A1A17109B for ; Mon, 12 Oct 2020 14:05:03 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 8522C2080D for ; Mon, 12 Oct 2020 14:05:03 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="EukK+8ha" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2390964AbgJLOFC (ORCPT ); Mon, 12 Oct 2020 10:05:02 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58564 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1730940AbgJLNf7 (ORCPT ); Mon, 12 Oct 2020 09:35:59 -0400 Received: from mail-wr1-x443.google.com (mail-wr1-x443.google.com [IPv6:2a00:1450:4864:20::443]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C2F33C0613D0 for ; Mon, 12 Oct 2020 06:35:58 -0700 (PDT) Received: by mail-wr1-x443.google.com with SMTP id i1so13104791wro.1 for ; Mon, 12 Oct 2020 06:35:58 -0700 (PDT) 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=avAYxea68r9wL9yBzVPbIuHqaqJjz0+4vW6MfyEUjlU=; b=EukK+8haEby0dx9hfOToeodZFubkvmbwaq7T6FcBISkCtQaL6gz2ZlAGCRqc8lGs/c JK3aYzHHA+vHoIjogo0YbT2PO+lbLXR+UE55JTEhQ4J/52GrFgQak2dyzoFl9sg3y2IM KQco4idYgHG4NU5J05KTWG2mfQtEXBlm9xU0MedNEAmqhyxo7emSDwliOz+2emdHydbp W7mF2eDAVGMI/gFDC7klB9x68bwMzf2K2kf10n7LeNUoydCTFhCp8dBGMnZ5roJ8c9EU EVi94ofzfPGCwIhMsV2nOWVJgczKVs8VuyKwdp4dcjzfK90OB01hG89E2jUWiDtZRlLQ p7gw== 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=avAYxea68r9wL9yBzVPbIuHqaqJjz0+4vW6MfyEUjlU=; b=KB7LAvMIpandtgcHWl3LLiSaL5pQGD7iahJEs3XeAOZ2NH8p2rElK+gXT0mfB5HRyJ zRzBTzZ+Fm+S3JNlq4k3EgftYxLA2TJubunY/2g7wvS1jFCwBEHcr+rwQPXEEgcLipGr bO04hQM1QTvzu8QckPU7daoyONf+qPeYrqBVpmhldgRyiu4ywQFf2M9k0I1ThAurhQke IFz1zxPNNPMplbdO6FUueHs9SqQEI2uI+uUR0Ncm/Gs20A5RCBbV+Xh1cLTciwaVAvIo ClouKmiyFJQgxoIHQfS1UlCwXA+STmXJdiVzeR2GppZOqpgAWRJ4HI6rQF9YSQHH+VQ2 7yxg== X-Gm-Message-State: AOAM530TAoPsHZrE/IqW0CmJiFS10ya4fkcm1o6Yk/N2OQwB9HT9ZC+J 2BaH1WxyUdjqfxvSwLaOhtF4MNrXP8w= X-Google-Smtp-Source: ABdhPJzZ9URFS+eG8FAtLo6Oj2ynONX4LHlWgPxfJHGtxVJX+FUJ3JtBrGGHhatcauFj7liFf7Oklg== X-Received: by 2002:a5d:40d2:: with SMTP id b18mr10538147wrq.155.1602509757518; Mon, 12 Oct 2020 06:35:57 -0700 (PDT) Received: from localhost.localdomain ([84.40.93.41]) by smtp.gmail.com with ESMTPSA id k5sm23145388wmb.19.2020.10.12.06.35.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Oct 2020 06:35:57 -0700 (PDT) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v2 01/20] kernel-shark: Start introducing KernelShark 2.0 Date: Mon, 12 Oct 2020 16:35:04 +0300 Message-Id: <20201012133523.469040-2-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20201012133523.469040-1-y.karadz@gmail.com> References: <20201012133523.469040-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 implies 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 c95249e9..ccca95c1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,12 +26,12 @@ include(${KS_DIR}/build/FindJSONC.cmake) 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 79726ff6..e398c0ca 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 032f3057..f6ed8978 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 457c1007..2e092b2e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,9 +2,9 @@ message("\n src ...") message(STATUS "libkshark") add_library(kshark SHARED libkshark.c - libkshark-model.c +# libkshark-model.c libkshark-plugin.c - libkshark-configio.c +# libkshark-configio.c libkshark-collection.c) target_link_libraries(kshark ${TRACECMD_LIBRARY} @@ -99,7 +99,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 Oct 12 13:35:05 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11832797 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id DE4A0109B for ; Mon, 12 Oct 2020 14:04:58 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id C21902074D for ; Mon, 12 Oct 2020 14:04:58 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="kQoxNHv9" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2389326AbgJLOE5 (ORCPT ); Mon, 12 Oct 2020 10:04:57 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58572 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1730739AbgJLNgB (ORCPT ); Mon, 12 Oct 2020 09:36:01 -0400 Received: from mail-wm1-x32a.google.com (mail-wm1-x32a.google.com [IPv6:2a00:1450:4864:20::32a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 38B16C0613D0 for ; Mon, 12 Oct 2020 06:36:01 -0700 (PDT) Received: by mail-wm1-x32a.google.com with SMTP id e23so10177423wme.2 for ; Mon, 12 Oct 2020 06:36:01 -0700 (PDT) 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=x9O0IeQDBm05Vx1oL137gxy8HhEf9zbyDs7+3Vndszg=; b=kQoxNHv9H3smTFPwY0m34+bDhJqjJX4r9WAQjipW93hla9OWaLVcoFjNYlOW55U9Dk BF/t2BL41wDoIJ4ir//KcKVMUPY5IXAKvIEuWZchKCgL3zPkGPKwe8bmC1ckWVT2tJe+ jcnWwS50LzqXcTvg4O3glWLsiIpKoyG32CruZ6yS2rGsdZF8CWJe5J7w5OxBMiThBaTX IYuTXIsDiOQHEUv8PlC1EpLOQBbVvYrMcf9i5SInZGkl+E9ayeXiUanaA3OknpEiRI/Y UCOccIYyN/b0KBYeB5kmn4BXtb5zT3gcYIcW+AQf3D16YvJPYb/OEt/7R014SdKht9EA 3nMA== 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=x9O0IeQDBm05Vx1oL137gxy8HhEf9zbyDs7+3Vndszg=; b=RqWMp3FOW3/jxFxb3fvaPZdFN6c43sb6coZVWmvlhPOpZC37ksKBQX13gdoKCGtpsq +Nb0qvHHKXzK9mTzAi0ZRAHouS+6qtVf1xYZTwFymVxxV6tMe8mMLqCv1f3PD05VrbMA KoQD3gNw0z7mCXMEQCHuUFkHTXHJ9/XeHFzqvICkIpdmN/Y7dIHUpTRsaNyJA06qcqhA jkIFEbKpWZvtujn+HFMHa7LwlXsf/VWmJ8i1R5z9TJfbMQRq0JuioiwqgLr91nMp2WYj 9sZ4SFqP1lh4CESb5+WNslr2GIL90gcqLTbD3KyKZS1P+hAJGbfDSwDHI84dyUHMKTZz CAfQ== X-Gm-Message-State: AOAM532lzCqJCi9vM+z+oWAD2Y7XHx8LoJ8OmPvcIa+59DP2ns4JxyfB iByKF98N535X3V/u2RduVQMe/OdGn30= X-Google-Smtp-Source: ABdhPJwtLRTFMqgrXM/IvcCKGCBQVr+kV+YF7+qu94pe/8dIiHzvTAtGFNsV3dP3SZUPmdAskmv3iA== X-Received: by 2002:a1c:4d01:: with SMTP id o1mr11322213wmh.39.1602509759798; Mon, 12 Oct 2020 06:35:59 -0700 (PDT) Received: from localhost.localdomain ([84.40.93.41]) by smtp.gmail.com with ESMTPSA id k5sm23145388wmb.19.2020.10.12.06.35.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Oct 2020 06:35:59 -0700 (PDT) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v2 02/20] kernel-shark: Use only signed types in kshark_entry Date: Mon, 12 Oct 2020 16:35:05 +0300 Message-Id: <20201012133523.469040-3-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20201012133523.469040-1-y.karadz@gmail.com> References: <20201012133523.469040-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 0d6c50d1..9eecc2d0 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 Oct 12 13:35:06 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11832795 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id ABFD2139F for ; Mon, 12 Oct 2020 14:04:49 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 8693921D81 for ; Mon, 12 Oct 2020 14:04:49 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="qOkq6rE3" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2390919AbgJLOEr (ORCPT ); Mon, 12 Oct 2020 10:04:47 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58584 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1730953AbgJLNgE (ORCPT ); Mon, 12 Oct 2020 09:36:04 -0400 Received: from mail-wm1-x343.google.com (mail-wm1-x343.google.com [IPv6:2a00:1450:4864:20::343]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 0F634C0613D1 for ; Mon, 12 Oct 2020 06:36:04 -0700 (PDT) Received: by mail-wm1-x343.google.com with SMTP id j136so17576511wmj.2 for ; Mon, 12 Oct 2020 06:36:03 -0700 (PDT) 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=eoqAnrsjvwy1ZKKAHaVz6ED3inWdsijpE+tgXuw5Q/o=; b=qOkq6rE3vIPxUzAuQ5W/Q5wEvNwTqc9lT+HCUG8P9hJ8lAyCP4M0DKoYrf/zA6JcVo YFUOJSd+bJvwMlXi+epah64513TcwqiyC0PNvQZyHg1Btoohp0W4d1cRj/ONvDcWnENw oCsAqD9kTEW10OY9/1ph0zFXbCaoEdBrg3FCJ+FKU5VbEI+99J7bf4d2HH0Z95GKILD4 FXf7Ff2uAAFhJpiKc21RvmhTHGLaGM2VrxyMn36l2RbANIrjiqA/Ps+eef7L/M0UOV2W qswvW4PRxQzlrR0zXe/ZJSr1zdUPdIt6X6NdQ21ayPuKR8Li2BV70MRqYz0UceNyl65E uFDg== 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=eoqAnrsjvwy1ZKKAHaVz6ED3inWdsijpE+tgXuw5Q/o=; b=YpLUAe9vueyqkyers1GFmZZaSm/REg3yD0MTnOt0DCV7Aw7d3+whA2JKlAd+HYZHqx 5F3nZGii+HjGM0sdaVgUbOPfb9eHqaKDeLVY1bH+Qemyf0FiBtH0HizbQYMkVLj19amc +qAODCrafYrvmtDQuOtywFSLYUaddhWHBkHd86ePtYai1ShVfqrGeHnp46SL2GKacsgm l5EFjrJmyT6WXW05QRrgoO6DNxVbiDjjiQHSBUAlKrshfZr9TtgCcbpkI9h3uCqHUBha W3gY8pXQEYbdJ5k2fX9WN3M4cB9C4S87KaQr8bevKcVzqUMqXKBOz7iDZlDNRKaYmXoN kS1A== X-Gm-Message-State: AOAM5303Qg0SZ5ndV36Z1JN1xLj3OfW8atsJ+2tftYaQLHMaL/BowlJo +ed0HeVzH/q8CAb+6GFwvt0= X-Google-Smtp-Source: ABdhPJx5aqLNYGYyBhYH+i90siRDZWzuwPGizDfm5A9xsXdV8/0e93WRI0D2RgNE+3xK0rQz/mXepA== X-Received: by 2002:a05:600c:21d9:: with SMTP id x25mr10412027wmj.145.1602509762666; Mon, 12 Oct 2020 06:36:02 -0700 (PDT) Received: from localhost.localdomain ([84.40.93.41]) by smtp.gmail.com with ESMTPSA id k5sm23145388wmb.19.2020.10.12.06.36.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Oct 2020 06:36:02 -0700 (PDT) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v2 03/20] kernel-shark: Introduce libkshark-hash Date: Mon, 12 Oct 2020 16:35:06 +0300 Message-Id: <20201012133523.469040-4-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20201012133523.469040-1-y.karadz@gmail.com> References: <20201012133523.469040-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 2e092b2e..39c4dcf0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,6 +2,7 @@ message("\n src ...") 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 00000000..89c021ba --- /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 9eecc2d0..0182cf6d 100644 --- a/src/libkshark.h +++ b/src/libkshark.h @@ -72,6 +72,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 Oct 12 13:35:07 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11832793 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 03BED109B for ; Mon, 12 Oct 2020 14:04:47 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id D678C2074D for ; Mon, 12 Oct 2020 14:04:46 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="hLnwIwgg" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1730983AbgJLOEp (ORCPT ); Mon, 12 Oct 2020 10:04:45 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58590 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1730967AbgJLNgG (ORCPT ); Mon, 12 Oct 2020 09:36:06 -0400 Received: from mail-wr1-x444.google.com (mail-wr1-x444.google.com [IPv6:2a00:1450:4864:20::444]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 04E7BC0613D0 for ; Mon, 12 Oct 2020 06:36:06 -0700 (PDT) Received: by mail-wr1-x444.google.com with SMTP id s9so7131824wro.8 for ; Mon, 12 Oct 2020 06:36:05 -0700 (PDT) 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=OwtSLj7I8f/64psYLH3fd6P0TG/AuHXoRS4W45a7JkQ=; b=hLnwIwggxCUQYZ4PL7ghA9XRxEuQRPJf/yrg9uF52RmGxiHD72qx+esM5U3fS+crYp aqP57n/ekc0b/iZMdvDFs+Qn1Kz8egJgWS3Fhod4epXeEUSpbySUk04qNoCT/x83e81Q cAH7M58TpDt9HMQbkM+gOHsVGT3yPV7oFA9rVf7J++ea05U/XEwq63bZPuICMtOYNw0A 2zKQF14dCy8A/QZ8tYSgCHKGyZ2I9EVl8qCFYL4fFsAH9vnGP6tYohi6GrHIs7u4UlMQ rbJ0cnV25mgEvnxXD2IDRlcVVdOXfnNdt/Pqga5T356NaqbIBmiGWzSz5g2yBCsL8FUm T1Wg== 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=OwtSLj7I8f/64psYLH3fd6P0TG/AuHXoRS4W45a7JkQ=; b=tYXeYbp3uVwmFkSLsgIvg0cuFlreshWrIZlc3F2RHsFSHzJEB1OwaMTg59NrYxa6tM Nt0qy2dY5RJ4WuxRuG7WppSWC8m/EuviP69SQu/6hp8yc1gXPYPdv535cYdAQ0xwz/q7 OZxV1M75yrA+QLZa5+Balaycg7vQbPxrLIJjD+yF7BQOhjfAeTDSepexjbYZ80NrcwWb iLgT1u3iM+ZbqUAccroEl0pH5udfEwzYhyDmCYekRTf5nfj2fLDZ8WcArO1d6u763jbU 9Y5EMz1udY/iHKJuoRkkP9AlG2lWvaRQ7blwuhhKXpP08/9DkWqfupZsKMMUFniaCvkS xEZA== X-Gm-Message-State: AOAM531awF/xBDLIidp/AiBVQpxKGv0Pyx2dXJrcxPqf0BQ+kl6WyMFD Cbc7Pjtyals4mEUDryeGhfE= X-Google-Smtp-Source: ABdhPJy11UuZNmj92KbpOdqF4A73MxYUpron6wuQDF6CYj9C5+KcLGjKJ9cJTvQ7xixB16BnnzZ/ow== X-Received: by 2002:adf:cd0c:: with SMTP id w12mr29529069wrm.305.1602509764598; Mon, 12 Oct 2020 06:36:04 -0700 (PDT) Received: from localhost.localdomain ([84.40.93.41]) by smtp.gmail.com with ESMTPSA id k5sm23145388wmb.19.2020.10.12.06.36.03 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Oct 2020 06:36:04 -0700 (PDT) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v2 04/20] kernel-shark: Introduce Data streams Date: Mon, 12 Oct 2020 16:35:07 +0300 Message-Id: <20201012133523.469040-5-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20201012133523.469040-1-y.karadz@gmail.com> References: <20201012133523.469040-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 | 201 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) diff --git a/src/libkshark.h b/src/libkshark.h index 0182cf6d..1165c512 100644 --- a/src/libkshark.h +++ b/src/libkshark.h @@ -118,6 +118,207 @@ 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 to initialize the interface of the data stream. */ +typedef int (*interface_init_func) (struct kshark_data_stream *, + const char *); + +/** A function type to be used to initialize the interface of the data stream. */ +typedef int (*interface_close_func) (struct kshark_data_stream *, + const char *); + +/** 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_INVALIDE_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 **cpu_array, + int32_t **pid_array, + int32_t **event_array, + int64_t **offset_array, + int64_t **ts_array); + +/** Data format identifier. */ +typedef enum kshark_data_format { + /** A data of unknown type. */ + KS_INVALIDE_DATA, + + /** Ftrace data. */ + KS_TEP_DATA, + + /** VMware SchedTrace data. */ + KS_VMW_ST_DATA, +} kshark_data_format; + +/** + * Structure representing the interface of methods used to operate over + * the data from a given stream. + */ +struct kshark_data_stream_interface { + /** 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 Latency of the entry. */ + stream_get_str_func get_latency; + + /** Method used to retrieve the Info string of the entry. */ + stream_get_str_func get_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_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; +}; + +/** Structure representing a stream of trace data. */ +struct kshark_data_stream { + /** Data stream identifier. */ + uint8_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. */ + enum kshark_data_format format; + + /** + * The interface of methods used to operate over the data from a given + * stream. + */ + struct kshark_data_stream_interface 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 Oct 12 13:35:08 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11832791 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 5688E109B for ; Mon, 12 Oct 2020 14:04:38 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 3A084206F4 for ; Mon, 12 Oct 2020 14:04:38 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="peN2l/0Q" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2388261AbgJLNgK (ORCPT ); Mon, 12 Oct 2020 09:36:10 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58598 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2388143AbgJLNgJ (ORCPT ); Mon, 12 Oct 2020 09:36:09 -0400 Received: from mail-wm1-x343.google.com (mail-wm1-x343.google.com [IPv6:2a00:1450:4864:20::343]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5BD3EC0613D0 for ; Mon, 12 Oct 2020 06:36:09 -0700 (PDT) Received: by mail-wm1-x343.google.com with SMTP id f21so17185622wml.3 for ; Mon, 12 Oct 2020 06:36:09 -0700 (PDT) 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=v9LG53zTXZaEA60v6ax0/6GIy63pdMNWl+hS8o3zo4k=; b=peN2l/0QLUM8jdD2M4D35kZbK4D4JjqQ6eHhfty7kQZ4fMvQ/OGIFd0Xlc4YcNRNYj jAtD4S1POzZbx1+zpt8Y/iElWaWtEHo2gYckRcuDJ+NsiTQZcY3oBH70oeH4bCkisr9A B7yFoROezH86jwit9IptJgFApMK+Ct8Sk8bz6umIRxNwTjLIcaguDsG3aRr3u9nZOiWr b1XU0qeNEIXUSj4+/luXKeFNA59bgUJ2eOWek2JC3LK3OMqwtbsCBr0WMXuOlBU6d8A/ iEvv7MCd+tC91KoUCnO63c7rNmdv3H77iyiG4Dtuu8oBJ8Ozi+hdngNsukshg5CdF7fS holQ== 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=v9LG53zTXZaEA60v6ax0/6GIy63pdMNWl+hS8o3zo4k=; b=EhDdhFl8BkA5jIkiNTiMD7TVH6vOVOQZaFETq74eG3YhPQXSjwNDY6ZGRSdN/lUQHS 1MOlTDiBKblnW1qH2ZYoCRKxj37B4NoLJMBalSmAPYYM8lJB73IcXQiYWhoKYTz6nTuQ LgfWoRkMub/w8l9189DQ4tysBxHg5i/GYopPq/5E4C5wEza5NeTET3Z8+OiIB6y16RvQ JMQCWZsKJQQo7EyNVUthixP3q6XtqznM8cIDYTbLigXoruMJEiZ94qduFLUoji60+i5b 9P6pNNeIGYw5zChW1HwzJTNU+GQF9SVGHRRWksCsZ7QpPidCmaA5ZYuHfJITwFpNQ6O0 FgqQ== X-Gm-Message-State: AOAM5324bbiSqel/NbAadvvSjh4M3WfYEqaqJ7ogj/2Y/vCTrmBZcW/F BT9/jmGwknhvnyIibl9faXU= X-Google-Smtp-Source: ABdhPJwUtXgwtgMyuLjK31crhzBE4CQLkt6+Er8hg6mmZSHihaY+1GossHf4zv5mKG5+bJL+2k6rug== X-Received: by 2002:a1c:2cc4:: with SMTP id s187mr10650598wms.36.1602509768096; Mon, 12 Oct 2020 06:36:08 -0700 (PDT) Received: from localhost.localdomain ([84.40.93.41]) by smtp.gmail.com with ESMTPSA id k5sm23145388wmb.19.2020.10.12.06.36.07 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Oct 2020 06:36:07 -0700 (PDT) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v2 05/20] kernel-shark: Add stream_id to kshark_entry Date: Mon, 12 Oct 2020 16:35:08 +0300 Message-Id: <20201012133523.469040-6-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20201012133523.469040-1-y.karadz@gmail.com> References: <20201012133523.469040-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 data fields to kshark_entry in order to make room for the unique identifier of the Data stream this entry belongs to. Signed-off-by: Yordan Karadzhov (VMware) --- src/libkshark.c | 2 +- src/libkshark.h | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/libkshark.c b/src/libkshark.c index 52aacd30..92e2450c 100644 --- a/src/libkshark.c +++ b/src/libkshark.c @@ -489,7 +489,7 @@ static inline void unset_event_filter_flag(struct kshark_context *kshark_ctx, e->visible &= ~event_mask; } -static void set_all_visible(uint16_t *v) { +static void set_all_visible(uint8_t *v) { /* Keep the original value of the PLUGIN_UNTOUCHED bit flag. */ *v |= 0xFF & ~KS_PLUGIN_UNTOUCHED_MASK; } diff --git a/src/libkshark.h b/src/libkshark.h index 1165c512..fe0ba7f2 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) */ /** @@ -49,7 +49,10 @@ struct kshark_entry { * kshark_filter_masks to check the level of visibility/invisibility * of the entry. */ - uint16_t visible; + uint8_t visible; + + /** Data stream identifier. */ + int8_t stream_id; /** The CPU core of the record. */ int16_t cpu; @@ -57,7 +60,7 @@ struct kshark_entry { /** The PID of the task the record was generated. */ int32_t pid; - /** Unique Id ot the trace event type. */ + /** Unique Id of the trace event type. */ int32_t event_id; /** The offset into the trace file, used to find the record. */ @@ -319,6 +322,9 @@ struct kshark_data_stream { struct kshark_data_stream_interface interface; }; +/** Hard-coded maximum number of data stream. */ +#define KS_MAX_NUM_STREAMS 127 + /** 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 Oct 12 13:35:09 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11832787 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id C782E139F for ; Mon, 12 Oct 2020 14:04:32 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id AA94B2080D for ; Mon, 12 Oct 2020 14:04:32 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="FHoplBPl" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2390937AbgJLOEc (ORCPT ); Mon, 12 Oct 2020 10:04:32 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58610 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2388608AbgJLNgN (ORCPT ); Mon, 12 Oct 2020 09:36:13 -0400 Received: from mail-wr1-x444.google.com (mail-wr1-x444.google.com [IPv6:2a00:1450:4864:20::444]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5A509C0613D0 for ; Mon, 12 Oct 2020 06:36:13 -0700 (PDT) Received: by mail-wr1-x444.google.com with SMTP id i1so13105742wro.1 for ; Mon, 12 Oct 2020 06:36:13 -0700 (PDT) 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=HlgI//eTg53lPEE454NCseMs22UW33H7XR+I0F9LnBk=; b=FHoplBPlL0u5RFLMGfZzidgMerxLkxwfgr8khiEgFLaGFHYaEvGwG1uUFfjvJ2KeeW W4UHmM3W5gzWV1gK0sHYgTdpFSqclWCgMEJTpKckXN5os+nwFlhcIUpKmqBLcUSIdwBg ZWO/2vhK9wFi86ohBLiEp2ZmhivhBvL1DjmVuAxtGa3PorCHJsRTElcMBGGMN+wLty9I aK1fzrV8KcU34533iYlRkO3+iSYWc2Djvlzj3zXkrX2yu+DVul80iTeS2UizI5up1fmx moNxH/xfQWm4/cirFFbSu06gmG12ir1D8IVD1gMLl+C7aJK04Q+m7OTJBal8Yx3oqGtM 06rQ== 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=HlgI//eTg53lPEE454NCseMs22UW33H7XR+I0F9LnBk=; b=JHyEKG5xLUAR3FzdUj/yUTNgpU+VVV5pmFrDlX7W29i+Vha2M/YrGYr3fFikANgROd KMwmFukbwoE7jvwlSpwSlrir0ID49cCr0lRQkCIKNmTZtylhI0PaewnC5l+UjHcdErXz d+TvnMTtryutpf6+hCtmB95rfRXZNvnjHYZ3NCMM2Y+BEky4W71bMyfdC+GhBRgMl7YC CQourEQHEM3L5LUWTWrZPWFbm/Z96vytj1E1GSNi+CRaCddiY7hBiz8+3XqZ5zL0sGqu h6WYaQdZNMxz5yU+oHtF3TROk47n3pSQTtHA0p2wm5DiHWbZTRDbDBiopAWWblboH9Hy twaQ== X-Gm-Message-State: AOAM533otPftj5gHqyjuoLeyDLsVt7IhRsOPbcTkv0AjkyOF+3hC9BTi n3sXmiZBvatZaO8WsyTimWI= X-Google-Smtp-Source: ABdhPJzP12CkUEmOKhgMaTmEwoB3k100FFNv+f7e8rxkqmk6jZXsQNrZbhjbjqNbdP3KVwu/Tca2OQ== X-Received: by 2002:a5d:4f09:: with SMTP id c9mr28817913wru.427.1602509772105; Mon, 12 Oct 2020 06:36:12 -0700 (PDT) Received: from localhost.localdomain ([84.40.93.41]) by smtp.gmail.com with ESMTPSA id k5sm23145388wmb.19.2020.10.12.06.36.11 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Oct 2020 06:36:11 -0700 (PDT) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v2 06/20] kernel-shark: Rename static methods in libkshark Date: Mon, 12 Oct 2020 16:35:09 +0300 Message-Id: <20201012133523.469040-7-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20201012133523.469040-1-y.karadz@gmail.com> References: <20201012133523.469040-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 92e2450c..6a1258dc 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); 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); 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 Oct 12 13:35:10 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11832773 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id CAE76139F for ; Mon, 12 Oct 2020 14:04:19 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id A6A782074D for ; Mon, 12 Oct 2020 14:04:19 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="bi6Cg99A" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2388719AbgJLOEE (ORCPT ); Mon, 12 Oct 2020 10:04:04 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58622 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727505AbgJLNgR (ORCPT ); Mon, 12 Oct 2020 09:36:17 -0400 Received: from mail-wm1-x344.google.com (mail-wm1-x344.google.com [IPv6:2a00:1450:4864:20::344]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 43A19C0613D1 for ; Mon, 12 Oct 2020 06:36:17 -0700 (PDT) Received: by mail-wm1-x344.google.com with SMTP id q5so17568053wmq.0 for ; Mon, 12 Oct 2020 06:36:17 -0700 (PDT) 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=QRGFyWD96P2o2F6V4CgAA5/3rTGFYG0pQQpuKrLitJM=; b=bi6Cg99AxznDPHmxbeYqLycb10Kgy2870AKz8davTF7S36KEHnykTpIBfJIFWVjIn5 IxqXV+uMKBbskJ0b/Npmtos+qV51h1dIZHwUx9BFWtA+sQCeXBlDdBCwU/XCyXe1NHPS eMeNQ4Qyjl7HyL7QYSgQp8+ca8bKsmEBRFQvnOUTY+Sema+KwQt9dbd6rWHGHDp8NxL0 tsSQqLR91kGlzpdErmI1YeMECOT04POLoU4oZCHz8hoWhrdbY6Yo+VSOfIYNf4TsKzUg Jdymrr/ByBFqFhYV7a1BZchRmuPtw76Twq+nin+Gp3P/TM+6Yx3HfmJOxPrh6r/kty7w tEjQ== 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=QRGFyWD96P2o2F6V4CgAA5/3rTGFYG0pQQpuKrLitJM=; b=bgxupNpEY9BFx6KrGulihhD6mVBa7cCZVySPsRWY2kGjaUCX6tm5zEhdLaMUmWSFcV Lk1VnINqV1JWtbHsWuKrWdHJFDIK8J57XoXULGpBUlJ+MmUshdhSszA57czEQwHnyMRI 2ZMvkMpJ3LfTbymJDUE1OKosbZmRrwBiC+86njTtWP/3xMd7fa46ot8EXoqbs/sSuXi8 njSqa+s6xTsgJjo6bD/bioEGIATULY6AJBI1KdNMQ+WwlLlOY/Pt2ZDMlVkIDGADJKK5 BTs0l3JEAu99u3LLKGiZHE4tz+mysKGw4n8lxzPyhddQFr52eSfDrFALy+xUCealGrny YO7A== X-Gm-Message-State: AOAM530zt6sddiQSU7WtVnvqh9RTniZEs1iPUdwOeZKdK2QDfAkpOQ8I lNDb4MgbXRAZlplDMo6fn/Q= X-Google-Smtp-Source: ABdhPJz6V/8ZWXifhwTbxnhxfyqPXRAQLnTdtq5EUspteq+2QX53vw4fgqb5h6JF6cIzHgsZTYmraw== X-Received: by 2002:a1c:e3c6:: with SMTP id a189mr10552525wmh.167.1602509775843; Mon, 12 Oct 2020 06:36:15 -0700 (PDT) Received: from localhost.localdomain ([84.40.93.41]) by smtp.gmail.com with ESMTPSA id k5sm23145388wmb.19.2020.10.12.06.36.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Oct 2020 06:36:15 -0700 (PDT) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v2 07/20] kernel-shark: Add basic methods for Data streams Date: Mon, 12 Oct 2020 16:35:10 +0300 Message-Id: <20201012133523.469040-8-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20201012133523.469040-1-y.karadz@gmail.com> References: <20201012133523.469040-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 | 197 ++++++++++++++++++++++++++++++++++++++++++++++++ src/libkshark.h | 171 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 368 insertions(+) diff --git a/src/libkshark.c b/src/libkshark.c index 6a1258dc..ffeb66a7 100644 --- a/src/libkshark.c +++ b/src/libkshark.c @@ -166,6 +166,153 @@ 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); +} + +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; + } + + stream->format = KS_INVALIDE_DATA; + + return stream; + + fail: + fprintf(stderr, "Failed to allocate memory for data stream.\n"); + if (stream) + kshark_stream_free(stream); + + return NULL; +} + +/** + * @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; + + if (kshark_ctx->n_streams == KS_MAX_NUM_STREAMS) + return -EMFILE; + + stream = kshark_stream_alloc(); + stream->stream_id = kshark_ctx->n_streams; + + if (pthread_mutex_init(&stream->input_mutex, NULL) != 0) { + kshark_stream_free(stream); + return -EAGAIN; + } + + kshark_ctx->stream[kshark_ctx->n_streams++] = stream; + + return stream->stream_id; +} + +/** + * @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 < KS_MAX_NUM_STREAMS) + 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 = malloc(kshark_ctx->n_streams * (sizeof(*ids))); + if (!ids) { + fprintf(stderr, + "Failed to allocate memory for stream array.\n"); + return NULL; + } + + for (i = 0; i < KS_MAX_NUM_STREAMS; ++i) + if (kshark_ctx->stream[i]) + ids[count++] = i; + + return ids; +} + /** * @brief Close the trace data file and free the trace data handle. * @@ -252,6 +399,56 @@ 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_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; + + e.visible = KS_PLUGIN_UNTOUCHED_MASK; + e.pid = pid; + + return stream->interface.get_task(stream, &e); +} + +/** + * @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_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; + + e.visible = KS_PLUGIN_UNTOUCHED_MASK; + e.event_id = event_id; + + return stream->interface.get_event_name(stream, &e); +} + 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 fe0ba7f2..e299d067 100644 --- a/src/libkshark.h +++ b/src/libkshark.h @@ -340,6 +340,12 @@ struct kshark_task_list { /** 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; + /** Input handle for the trace data file. */ struct tracecmd_input *handle; @@ -397,6 +403,16 @@ 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); + +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); @@ -416,6 +432,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); @@ -432,6 +452,157 @@ void kshark_convert_nano(uint64_t time, uint64_t *sec, uint64_t *usec); char* kshark_dump_entry(const struct kshark_entry *entry); +static inline int kshark_get_pid(const struct kshark_entry *entry) +{ + struct kshark_data_stream *stream = + kshark_get_stream_from_entry(entry); + + if (!stream) + return -1; + + return stream->interface.get_pid(stream, entry); +} + +static inline int kshark_get_event_id(const struct kshark_entry *entry) +{ + struct kshark_data_stream *stream = + kshark_get_stream_from_entry(entry); + + if (!stream) + return -1; + + return stream->interface.get_event_id(stream, entry); +} + +static inline int *kshark_get_all_event_ids(struct kshark_data_stream *stream) +{ + return stream->interface.get_all_event_ids(stream); +} + +static inline char *kshark_get_event_name(const struct kshark_entry *entry) +{ + struct kshark_data_stream *stream = + kshark_get_stream_from_entry(entry); + + if (!stream) + return NULL; + + return stream->interface.get_event_name(stream, entry); +} + +static inline char *kshark_get_task(const struct kshark_entry *entry) +{ + struct kshark_data_stream *stream = + kshark_get_stream_from_entry(entry); + + if (!stream) + return NULL; + + return stream->interface.get_task(stream, entry); +} + +static inline char *kshark_get_latency(const struct kshark_entry *entry) +{ + struct kshark_data_stream *stream = + kshark_get_stream_from_entry(entry); + + if (!stream) + return NULL; + + return stream->interface.get_latency(stream, entry); +} + +static inline char *kshark_get_info(const struct kshark_entry *entry) +{ + struct kshark_data_stream *stream = + kshark_get_stream_from_entry(entry); + + if (!stream) + return NULL; + + return stream->interface.get_info(stream, entry); +} + +static inline int kshark_read_event_field(const struct kshark_entry *entry, + const char* field, int64_t *val) +{ + struct kshark_data_stream *stream = + kshark_get_stream_from_entry(entry); + + if (!stream) + return -1; + + return stream->interface.read_event_field_int64(stream, entry, + field, val); +} + +/** + * @brief Load the content of the trace data file asociated with a given + * Data stream identifie 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. + */ +static inline ssize_t kshark_load_entries(struct kshark_context *kshark_ctx, + int sd, + struct kshark_entry ***data_rows) +{ + struct kshark_data_stream *stream; + + stream = kshark_get_data_stream(kshark_ctx, sd); + if (!stream) + return -EBADF; + + return stream->interface.load_entries(stream, kshark_ctx, data_rows); +} + +/** + * @brief Load the content of the trace data file asociated with a given + * Data stream identifie 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 _array: Output location for the 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. + */ +static inline ssize_t kshark_load_matrix(struct kshark_context *kshark_ctx, + int sd, + int16_t **cpu_array, + int32_t **pid_array, + int32_t **event_array, + int64_t **offset_array, + int64_t **ts_array) +{ + struct kshark_data_stream *stream; + + stream = kshark_get_data_stream(kshark_ctx, sd); + if (!stream) + return -EBADF; + + return stream->interface.load_matrix(stream, kshark_ctx, cpu_array, + pid_array, + event_array, + offset_array, + ts_array); +} + /** * Custom entry info function type. To be user for dumping info for custom * KernelShark entryes. From patchwork Mon Oct 12 13:35:11 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11832693 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 7852716BC for ; Mon, 12 Oct 2020 13:36:35 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 598DA21BE5 for ; Mon, 12 Oct 2020 13:36:35 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="c7/YIsCZ" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1730566AbgJLNgd (ORCPT ); Mon, 12 Oct 2020 09:36:33 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58628 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2388681AbgJLNgT (ORCPT ); Mon, 12 Oct 2020 09:36:19 -0400 Received: from mail-wm1-x343.google.com (mail-wm1-x343.google.com [IPv6:2a00:1450:4864:20::343]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id DDCFBC0613D2 for ; Mon, 12 Oct 2020 06:36:18 -0700 (PDT) Received: by mail-wm1-x343.google.com with SMTP id k18so17573229wmj.5 for ; Mon, 12 Oct 2020 06:36:18 -0700 (PDT) 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=Vakxz4aqkl2nG75Bky/OE4taqN34RSdtokMEO/n9Hgg=; b=c7/YIsCZbL81SX1dSXZ21/jx47Pc2STm8dnT6N6XRVh9hRHe1MYPnpEkXMZw5TGiiZ ehhZht023dEcOLtk1vskfccEpac9BsJVaXk/YMrj/CKdrfvphKVCw6rCt/QPd4FJZChX lFeEbqvHOyHsKqak+ZH1czIHfjarNR1Nj/kgSPrlzPw+mspdIrLt8pPodIHk9DgqmPqp aSUJhi1DmWN+fR0B3VWYRWx3xnbF0z3txFCt8tIiPU/9s6OHu7RdxOT3Vx0ZKjEIvLPZ Fx4Nn23RMVU9nhCzo9ALjlRoMC39lmlnAgrXVncPX9FE9ns2Ud3kOiFMmVpKQ4qpmIRw OAxg== 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=Vakxz4aqkl2nG75Bky/OE4taqN34RSdtokMEO/n9Hgg=; b=QJtPRPlTQ8WoDc+AQp0rRUgbrn2t6hCt4i0vCf7uJutxA9NP1JcW4kIPzE/OZY4KTc 2lA+tAYMDN3QE+oq3hB7akRHxhqhRRwof7tBScjIm3wP15/tTSHD/+NdT9oKOM19ISBS oVsKyhEB/sWd9ZXCizdYePxT2IkvUVIJNsGqhAxUlbXadOeAwYDDpBG9ToMyfDu6tFJ2 OCB4YfyCsGkysBSNi7GchGeJ1IIy38ECPF971MmT+B5i+losh1mUzIDq8ZsS4PGqr6CV L0v/eLHMzNa7eTk7G8FiNS4dIT9QPoPTwf+ADSikiEZ2IK3Ohrx/CWx5LbUKSRML5uB2 FyIQ== X-Gm-Message-State: AOAM5306eVDxV4A38OG5zaELIg37MBEZAQ+zWxCQYtclVnZv5xCS1nCs 1PKDcpc5UeIj7x4+2WL2MHk= X-Google-Smtp-Source: ABdhPJxfAgmpRRjuLFDm9IIWz/nTedOdqcW0S/TeB0fndLRBliBLMagY2l+9pIJch04BWqQxf1mXvw== X-Received: by 2002:a1c:4904:: with SMTP id w4mr10300534wma.99.1602509777566; Mon, 12 Oct 2020 06:36:17 -0700 (PDT) Received: from localhost.localdomain ([84.40.93.41]) by smtp.gmail.com with ESMTPSA id k5sm23145388wmb.19.2020.10.12.06.36.16 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Oct 2020 06:36:17 -0700 (PDT) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v2 08/20] kernel-shark: Housekeeping before implementing stream interface Date: Mon, 12 Oct 2020 16:35:11 +0300 Message-Id: <20201012133523.469040-9-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20201012133523.469040-1-y.karadz@gmail.com> References: <20201012133523.469040-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 | 21 +++++++++ 2 files changed, 40 insertions(+), 102 deletions(-) diff --git a/src/libkshark.c b/src/libkshark.c index ffeb66a7..56721717 100644 --- a/src/libkshark.c +++ b/src/libkshark.c @@ -671,21 +671,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(uint8_t *v) { /* Keep the original value of the PLUGIN_UNTOUCHED bit flag. */ *v |= 0xFF & ~KS_PLUGIN_UNTOUCHED_MASK; @@ -1161,11 +1146,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 **cpu_array, + int32_t **pid_array, + int32_t **event_array, + int64_t **offset_array, + int64_t **ts_array) { if (offset_array) { *offset_array = calloc(n_rows, sizeof(**offset_array)); @@ -1212,88 +1211,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 = tracecmd_cpus(kshark_ctx->handle); - - 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 e299d067..f26fad7c 100644 --- a/src/libkshark.h +++ b/src/libkshark.h @@ -689,6 +689,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); @@ -1067,6 +1082,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 **cpu_array, + int32_t **pid_array, + int32_t **event_array, + int64_t **offset_array, + int64_t **ts_array); + #ifdef __cplusplus } #endif From patchwork Mon Oct 12 13:35:12 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11832769 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 48558139F for ; Mon, 12 Oct 2020 14:04:00 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 13767208B8 for ; Mon, 12 Oct 2020 14:04:00 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="E9GxBnJc" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2390876AbgJLOD7 (ORCPT ); Mon, 12 Oct 2020 10:03:59 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58646 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1730092AbgJLNgY (ORCPT ); Mon, 12 Oct 2020 09:36:24 -0400 Received: from mail-wr1-x443.google.com (mail-wr1-x443.google.com [IPv6:2a00:1450:4864:20::443]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1B8F0C0613D0 for ; Mon, 12 Oct 2020 06:36:24 -0700 (PDT) Received: by mail-wr1-x443.google.com with SMTP id g12so19215260wrp.10 for ; Mon, 12 Oct 2020 06:36:24 -0700 (PDT) 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=iSvPNSvY1rc27cfoldeTUIyAHo6w1VQDUQGUzaZxf3w=; b=E9GxBnJcS84V0P76c4eJiS5w10UnpP9IeK9q5fo/uqSxHnvOaf6jHtKftzr3CEKfFs B66BwOa9pnRSur82gZV83iCW85BBTybZlwmpr6z0RkgwsaIw+QIw79Hb7XjZx9pV62x3 Rxd9lIoNSZXvo6l8HdRzVH9TJU0MbpaCdCjXN0JAnVFoD3mgcIeF9jannBa48lNJw44j EbA3AhGHhy6JOBBLEAT8LmesRIj/85nKldhSpu47vH3yxCgC3NikeZoizsMD1ViwDv59 /GY1isdIM+vCxbWqXk++VTGUBubrfMfUQfjNX4k/5XJSllYeGUEJyEBUpkhV8sOyaR/Y qi1Q== 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=iSvPNSvY1rc27cfoldeTUIyAHo6w1VQDUQGUzaZxf3w=; b=Xymp/8+M9BTmuCdUOthVtO2nvYm9h9Xg1sSVoFEtM7T4MmCJDYPigDSYYCd+Il1jMf 9VyVDZiM7XyRPbUDMZ0v1YTx2eXIgRf6MdfGf6iIYXIwzFjiupOL7xVFumkJjcOEWHAF 2icIUS8jEriEZdfQGKc0GsaFnO2qko2KtPewX2skFhtsqjGpoT48x+SSsVR64sijasi8 py/HMALHjKpnZGUYDuhj9O71WdrpAHlpD6gjkfnh/tgMzeKg+TFdC9JXGX5E8ucp6me6 bvcoGThkOxIbmCZ/6UsD7TdMzrwsFYlZ3nydWkXS1ZVLqjr0jWU2cN0kQX1ykCCluTAb 1grQ== X-Gm-Message-State: AOAM532kGskI6Cr6e+o6CcJWZBO+KKgoRI+ZoA94rZAdAel0p+vYfh7r 9Mshf+vEAczash/mYAjakz4= X-Google-Smtp-Source: ABdhPJysotGPoNv7fQOHf+zdSU4mcESQ5pbxC/sePT1BgSci6w7J8LgAIqokLsAY1wVF3/lzUMYbNg== X-Received: by 2002:adf:ecc8:: with SMTP id s8mr17325819wro.328.1602509782316; Mon, 12 Oct 2020 06:36:22 -0700 (PDT) Received: from localhost.localdomain ([84.40.93.41]) by smtp.gmail.com with ESMTPSA id k5sm23145388wmb.19.2020.10.12.06.36.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Oct 2020 06:36:21 -0700 (PDT) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v2 09/20] kernel-shark: Add stream interface for trace-cmd data Date: Mon, 12 Oct 2020 16:35:12 +0300 Message-Id: <20201012133523.469040-10-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20201012133523.469040-1-y.karadz@gmail.com> References: <20201012133523.469040-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 | 1391 +++++++++++++++++++++++++++++++++++++++ src/libkshark-tepdata.h | 51 ++ 3 files changed, 1443 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 39c4dcf0..2bdab1a5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -5,6 +5,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 00000000..30c383c8 --- /dev/null +++ b/src/libkshark-tepdata.c @@ -0,0 +1,1391 @@ +// 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; +}; + +/** Get the Page event object used to parse the page. */ +struct tep_handle *kshark_get_tep(struct kshark_data_stream *stream) +{ + if (stream->format != KS_TEP_DATA) + return NULL; + + struct tepdata_handle *tep_handle = stream->interface.handle; + 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) +{ + if (stream->format != KS_TEP_DATA) + return NULL; + + struct tepdata_handle *tep_handle = stream->interface.handle; + return tep_handle->input; +} + +static inline struct tep_event_filter * +get_adv_filter(struct kshark_data_stream *stream) +{ + if (stream->format != KS_TEP_DATA) + return NULL; + + struct tepdata_handle *tep_handle = stream->interface.handle; + return tep_handle->advanced_event_filter; +} + +static int get_sched_switch_id(struct kshark_data_stream *stream) +{ + if (stream->format != KS_TEP_DATA) + return -EINVAL; + + struct tepdata_handle *tep_handle = stream->interface.handle; + return tep_handle->sched_switch_event_id; +} + +static struct tep_format_field *get_sched_next(struct kshark_data_stream *stream) +{ + if (stream->format != KS_TEP_DATA) + return NULL; + + struct tepdata_handle *tep_handle = stream->interface.handle; + return tep_handle->sched_switch_next_field; +} + +static struct tep_format_field *get_sched_comm(struct kshark_data_stream *stream) +{ + if (stream->format != KS_TEP_DATA) + return NULL; + + struct tepdata_handle *tep_handle = stream->interface.handle; + 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) +{ + /* 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_get_tep(stream), 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_get_tep(stream), 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) + 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 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; + + 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)); + } + + 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); + + 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; + } + 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 **cpu_array, + int32_t **pid_array, + int32_t **event_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, cpu_array, + pid_array, + event_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 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); + + 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 tep_event *event; + char *buffer; + + int event_id = stream->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); + + 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) +{ + int pid = stream->interface.get_pid(stream, entry); + const char *task; + char *buffer; + + task = tep_data_comm_from_pid(kshark_get_tep(stream), pid); + if (asprintf(&buffer, "%s", task) <= 0) + return NULL; + + return buffer; +} + +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); + + 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); + + 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); + evt_ids = malloc(stream->n_events * sizeof(*evt_ids)); + + 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)); + + fields = tep_event_common_fields(event); + for (field = *fields; field; field = field->next) + if (asprintf(&buffer[i++], "%s", field->name) <= 0) + goto fail; + + free(fields); + + fields = tep_event_fields(event); + 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_context *kshark_ctx = NULL; + int n = 0; + + if (!kshark_instance(&kshark_ctx) || !init_thread_seq()) + return NULL; + + if (entry->event_id >= 0) { + if (kshark_get_tep(stream)) { + task = stream->interface.get_task(stream, entry); + latency = stream->interface.get_latency(stream, entry); + event = stream->interface.get_event_name(stream, entry); + info = stream->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, + stream->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, + stream->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_INVALIDE_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_INVALIDE_FIELD; + + if (mask & evt_field->flags) + return KS_INVALIDE_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); + free_record(record); + + return ret; +} + +/** Initialize all methods used by a stream of FTRACE data. */ +static void kshark_tep_init_methods(struct kshark_data_stream *stream) +{ + stream->interface.get_pid = tepdata_get_pid; + stream->interface.get_task = tepdata_get_task; + stream->interface.get_event_id = tepdata_get_event_id; + stream->interface.get_event_name = tepdata_get_event_name; + stream->interface.get_latency = tepdata_get_latency; + stream->interface.get_info = tepdata_get_info; + stream->interface.find_event_id = tepdata_find_event_id; + stream->interface.get_all_event_ids = tepdata_get_event_ids; + stream->interface.dump_entry = tepdata_dump_entry; + stream->interface.get_all_field_names = tepdata_get_field_names; + stream->interface.get_event_field_type = tepdata_get_field_type; + stream->interface.read_record_field_int64 = tepdata_read_record_field; + stream->interface.read_event_field_int64 = tepdata_read_event_field; + stream->interface.load_entries = tepdata_load_entries; + stream->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 || peer_stream->format != KS_TEP_DATA) + 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 tepdata_handle *tep_handle; + struct tep_event *event; + + tep_handle = calloc(1, sizeof(*tep_handle)); + if (!tep_handle) + goto fail; + + tep_handle->input = input; + tep_handle->tep = tracecmd_get_pevent(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(stream); + + stream->interface.handle = tep_handle; + + return 0; + + fail: + free(tep_handle); + stream->interface.handle = NULL; + return -EFAULT; +} + +/** Initialize the FTRACE data input (from file). */ +int kshark_tep_init_input(struct kshark_data_stream *stream, + const char *file) +{ + 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(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("top"); + + 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 tepdata_handle *tep_handle; + + tep_handle = calloc(1, sizeof(*tep_handle)); + if (!tep_handle) + return -EFAULT; + + tep_handle->tep = tracefs_local_events(tracefs_get_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); + stream->format = KS_TEP_DATA; + if (asprintf(&stream->file, "Local system") <= 0) + goto fail; + + stream->interface.handle = tep_handle; + kshark_tep_init_methods(stream); + + return 0; + + fail: + free(tep_handle); + stream->interface.handle = NULL; + return -EFAULT; +} + +/** Method used to close a stream of FTRACE data. */ +void kshark_tep_close_interface(struct kshark_data_stream *stream) +{ + struct tepdata_handle *tep_handle = stream->interface.handle; + + 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); + stream->interface.handle = NULL; +} + +/** 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_plugins() +{ + return tracefs_tracers(tracefs_get_tracing_dir()); +} diff --git a/src/libkshark-tepdata.h b/src/libkshark-tepdata.h new file mode 100644 index 00000000..a2bd211e --- /dev/null +++ b/src/libkshark-tepdata.h @@ -0,0 +1,51 @@ +/* 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 + +int kshark_tep_init_input(struct kshark_data_stream *stream, + const char *file); + +int kshark_tep_init_local(struct kshark_data_stream *stream); + +void 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_plugins(); + +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 Oct 12 13:35:13 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11832767 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 21B35139F for ; Mon, 12 Oct 2020 14:03:59 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id F366220757 for ; Mon, 12 Oct 2020 14:03:58 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="vVBzSED+" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2390726AbgJLOD5 (ORCPT ); Mon, 12 Oct 2020 10:03:57 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58652 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729890AbgJLNg0 (ORCPT ); Mon, 12 Oct 2020 09:36:26 -0400 Received: from mail-wr1-x433.google.com (mail-wr1-x433.google.com [IPv6:2a00:1450:4864:20::433]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id F1ED9C0613D1 for ; Mon, 12 Oct 2020 06:36:25 -0700 (PDT) Received: by mail-wr1-x433.google.com with SMTP id h7so19258999wre.4 for ; Mon, 12 Oct 2020 06:36:25 -0700 (PDT) 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=N0Wd8BcluIYXvLARGdrnMei3EImBodImmoRYuknwtno=; b=vVBzSED+TJcFRDAiv5GgFoC2rI3rvws9jtJ86LUiQfBrbShZjUPjdBnbMXBR7EUE95 JcLQBqBl2arv+ekzmGUEu3x3ml+asRUuLHYwfzEx70UH0DaN6MMESRXAsMjHpo9wiI6S JLrxolGJ+MZ4+AFXaQ5d35uA/1jO3kB29Tq+XV6YdmUaS/MSjPMtZ3BP4RcjaRJrCIcN Bfl1ec70eC6axVO1qLAeV1dCSK6H5n80sIK41N4r/urJwS+IavBeXzx56IXq/c3s+Zjc XSUrzJ0kv87fy6pFc8G2uzdW1Mo9qBO0Cyd8Al/KQMIBVXL1wAZDbKyw7HiR2Vl/svOE l2FQ== 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=N0Wd8BcluIYXvLARGdrnMei3EImBodImmoRYuknwtno=; b=Aub7T4T414nNxBM4NA6L20Q5HQSSZlIm6p9ao74/JPUH60v0wUAuJHpzjQMSs13LB5 pTkOG3m2icB1Vzynfa51gCwZ7FS8hRuZuzg5JU83I2TKOcvMS8EKG3c/QMx9HLWTkAt+ +CSgxkxJo1o++LMHnPBkl8uWj+LnfOMKQXhEASsWfCwmwe+VLUpLOEi0k0REVQSm2N02 Gg6SuBMl73LPJH0Rv2b9GTJwjkk3aI1ufam3vKVgwDG/L0tVU0Wr8EI2G/SACZSoGg8f MYJO2U5paWuYvXIwS8ZEAU2meg/ofkfBcbcNLr2IaKh9uQ9XU4Gu8ZtpLHh5/QkVsDWr 8Cmg== X-Gm-Message-State: AOAM531QYI9zXdD62HmpOr9ItFul+RpSbNUGTB4sK0Jn2ODQHWHNO9un WhS+bLTz0Je7oj6UiwEE/CaNrYbapd0= X-Google-Smtp-Source: ABdhPJxaZkf+dLHGdplpf37aN3jOoh3+lf4vo7QnDnXXGAUj0LQidOUMCUPidvDBGyECurp/Naqmcw== X-Received: by 2002:adf:d841:: with SMTP id k1mr30098345wrl.227.1602509784516; Mon, 12 Oct 2020 06:36:24 -0700 (PDT) Received: from localhost.localdomain ([84.40.93.41]) by smtp.gmail.com with ESMTPSA id k5sm23145388wmb.19.2020.10.12.06.36.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Oct 2020 06:36:24 -0700 (PDT) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v2 10/20] kernel-shark: Start using data streams Date: Mon, 12 Oct 2020 16:35:13 +0300 Message-Id: <20201012133523.469040-11-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20201012133523.469040-1-y.karadz@gmail.com> References: <20201012133523.469040-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 | 21 ++- src/libkshark.c | 336 ++++++++++++++++++-------------------------- src/libkshark.h | 24 +++- 3 files changed, 164 insertions(+), 217 deletions(-) diff --git a/examples/dataload.c b/examples/dataload.c index 15c5de05..c13acbcb 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); @@ -82,7 +79,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 56721717..6eff53e5 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_MAX_NUM_STREAMS, + sizeof(*kshark_ctx->stream)); + kshark_ctx->event_handlers = NULL; kshark_ctx->collections = NULL; kshark_ctx->plugins = NULL; @@ -108,62 +112,28 @@ 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); - - handle = tracecmd_open(file); - if (!handle) - return false; - - if (pthread_mutex_init(&kshark_ctx->input_mutex, NULL) != 0) { - tracecmd_close(handle); - return false; - } - - kshark_ctx->handle = handle; - kshark_ctx->pevent = tracecmd_get_pevent(handle); + int sd, rt; - kshark_ctx->advanced_event_filter = - tep_filter_alloc(kshark_ctx->pevent); + sd = kshark_add_stream(kshark_ctx); + if (sd < 0) + return sd; - /* - * 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"); + rt = kshark_stream_open(kshark_ctx->stream[sd], file); + if (rt < 0) + return rt; - return true; + return sd; } static void kshark_stream_free(struct kshark_data_stream *stream) @@ -253,6 +223,56 @@ int kshark_add_stream(struct kshark_context *kshark_ctx) return stream->stream_id; } +static bool is_tep(const char *filename) +{ + /* + * TODO: This is very naive. Implement more appropriate check. Ideally + * it should be part of the trace-cmd library. + */ + char *ext = strrchr(filename, '.'); + return ext && strcmp(ext, ".dat") == 0; +} + +static void set_format(struct kshark_context *kshark_ctx, + struct kshark_data_stream *stream, + const char *filename) +{ + stream->format = KS_INVALIDE_DATA; + + if (is_tep(filename)) { + stream->format = KS_TEP_DATA; + return; + } +} + +/** + * @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); + set_format(kshark_ctx, stream, file); + + switch (stream->format) { + case KS_TEP_DATA: + return kshark_tep_init_input(stream, file); + + default: + return -ENODATA; + } +} + /** * @brief Get the Data stream object having given Id. * @@ -313,45 +333,76 @@ int *kshark_all_streams(struct kshark_context *kshark_ctx) return ids; } +static void kshark_stream_close(struct kshark_data_stream *stream) +{ + struct kshark_context *kshark_ctx = NULL; + + if (!stream || !kshark_instance(&kshark_ctx)) + return; + + /* + * 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); + + switch (stream->format) { + case KS_TEP_DATA: + kshark_tep_close_interface(stream); + break; + + default: + break; + } + + pthread_mutex_destroy(&stream->input_mutex); +} + /** * @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. */ -void kshark_close(struct kshark_context *kshark_ctx) +void kshark_close(struct kshark_context *kshark_ctx, int sd) { - if (!kshark_ctx || !kshark_ctx->handle) + struct kshark_data_stream *stream; + + stream = kshark_get_data_stream(kshark_ctx, sd); + if (!stream) return; - /* - * 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; - } + kshark_stream_close(stream); + kshark_stream_free(stream); + kshark_ctx->stream[sd] = NULL; + kshark_ctx->n_streams--; +} + +/** + * @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; - - tracecmd_close(kshark_ctx->handle); - kshark_ctx->handle = NULL; - kshark_ctx->pevent = NULL; + n_streams = kshark_ctx->n_streams; + for (i = 0; i < n_streams; ++i) + kshark_close(kshark_ctx, stream_ids[i]); - pthread_mutex_destroy(&kshark_ctx->input_mutex); + free(stream_ids); } /** @@ -359,7 +410,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. */ @@ -373,25 +424,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); + kshark_close_all(kshark_ctx); - tracecmd_filter_id_hash_free(kshark_ctx->show_event_filter); - tracecmd_filter_id_hash_free(kshark_ctx->hide_event_filter); + free(kshark_ctx->stream); - tracecmd_filter_id_hash_free(kshark_ctx->show_cpu_filter); - tracecmd_filter_id_hash_free(kshark_ctx->hide_cpu_filter); - - 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; @@ -490,57 +528,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, @@ -1552,75 +1557,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); - } - - 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 f26fad7c..ef48d94b 100644 --- a/src/libkshark.h +++ b/src/libkshark.h @@ -401,7 +401,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); @@ -426,9 +428,12 @@ size_t kshark_load_data_matrix(struct kshark_context *kshark_ctx, uint16_t **pid_array, int **event_array); -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); + +void 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); @@ -450,8 +455,6 @@ 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); - static inline int kshark_get_pid(const struct kshark_entry *entry) { struct kshark_data_stream *stream = @@ -536,6 +539,17 @@ static inline int kshark_read_event_field(const struct kshark_entry *entry, field, val); } +static inline char *kshark_dump_entry(const struct kshark_entry *entry) +{ + struct kshark_data_stream *stream = + kshark_get_stream_from_entry(entry); + + if (!stream) + return NULL; + + return stream->interface.dump_entry(stream, entry); +} + /** * @brief Load the content of the trace data file asociated with a given * Data stream identifie into an array of kshark_entries. From patchwork Mon Oct 12 13:35:14 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11832771 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id B25DE109B for ; Mon, 12 Oct 2020 14:04:04 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 6AA1F2080D for ; Mon, 12 Oct 2020 14:04:04 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="ucXP910q" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2390874AbgJLOD6 (ORCPT ); Mon, 12 Oct 2020 10:03:58 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58660 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1730520AbgJLNgb (ORCPT ); Mon, 12 Oct 2020 09:36:31 -0400 Received: from mail-wr1-x436.google.com (mail-wr1-x436.google.com [IPv6:2a00:1450:4864:20::436]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7627BC0613D2 for ; Mon, 12 Oct 2020 06:36:28 -0700 (PDT) Received: by mail-wr1-x436.google.com with SMTP id e18so19213248wrw.9 for ; Mon, 12 Oct 2020 06:36:28 -0700 (PDT) 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=eVMccU+LWENSynKb+x3Hkzr8j4uPK2gAme6zDwP6Sus=; b=ucXP910qRAvzX5zBZRaaPG37IOdXNlz/3xWUrIkWbsUN82L9rb4wwMU+UVepaubCma tJHm1Men2rvLRozGoCnbKj+/41QJ1HgWKtVLtjDYaTRY5EcLvRV8Tg21NQam6/dObHyD 0XVhC4ihRwH0gQZKRyPh/Xo39KtbqeBccIGKsdVR9ZFJZaA2TgNEFRQ5bkNk0riGA93z 5rTMOkm6qod7RJSz7cFeUmv7tVqvDP/r/HjVc7/iZ+5x4hueN9Q8RLElD9rIHk82YEJJ bdrwWeqgRMMRvp5EUG2ZpLsjCPRXhpkK7a8Jj6o9Cq2g6pN3dO38oR5M197gqWgXdzT/ 4g6A== 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=eVMccU+LWENSynKb+x3Hkzr8j4uPK2gAme6zDwP6Sus=; b=cLKiqSqPvS80TRBrNnnFU1U5ySYuOP7dSfmmuZjGo1LMkv5F/KXfOTDzP8BPDK+cfH 49UnZYtln+hO3zX4X3jDCvbA0CcZp6bqofnZiPEmx4zScZc/6Dvq45SIpXNiKpKrbE3O p/4CoJdzLQ0ZxkKBuG8ZYIcmWMpABtdiQW7mqEgyW//M4wEElKcXxN9NYMthSqC5Nae4 3wuaHuKVhpTdhy04VkFDaN32aAg+kLH89IovJtE9A3hDGRaGN5T62OTlClDliF/fpSaO ZO0h77e3DqxskWWY6Y2Fq1iOnfUwGqYOBkqpZt28u+kxdys9iS+lPb6VTkNpfpyJPVla r15Q== X-Gm-Message-State: AOAM533OKGncsGxjys/5hfaA7XRyuoBc1cF0AqXOb6Ku1nchofEofYcr +Wlkdj4W/oJ8iFZTqUBzUG8= X-Google-Smtp-Source: ABdhPJxs6v+Ag5Hr6DQ3GA5gAFIocDZIPcsxZ4LT1B73f6RG9bP0vhq9du6flNx9UOrdDXeDTXYVzg== X-Received: by 2002:a5d:5092:: with SMTP id a18mr26327114wrt.323.1602509786843; Mon, 12 Oct 2020 06:36:26 -0700 (PDT) Received: from localhost.localdomain ([84.40.93.41]) by smtp.gmail.com with ESMTPSA id k5sm23145388wmb.19.2020.10.12.06.36.25 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Oct 2020 06:36:26 -0700 (PDT) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v2 11/20] kernel-shark: Remove dead code Date: Mon, 12 Oct 2020 16:35:14 +0300 Message-Id: <20201012133523.469040-12-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20201012133523.469040-1-y.karadz@gmail.com> References: <20201012133523.469040-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 | 807 +----------------------------------------------- src/libkshark.h | 77 +---- 2 files changed, 2 insertions(+), 882 deletions(-) diff --git a/src/libkshark.c b/src/libkshark.c index 6eff53e5..e363395a 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) @@ -62,14 +60,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 @@ -106,9 +96,6 @@ bool kshark_instance(struct kshark_context **kshark_ctx) } } - if (!init_thread_seq()) - return false; - return true; } @@ -487,42 +474,6 @@ char *kshark_event_from_id(int sd, int event_id) return stream->interface.get_event_name(stream, &e); } -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. @@ -751,400 +702,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) - 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 = tracecmd_cpus(kshark_ctx->handle); - 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; - } - free_record(rec); - break; - } /* REC_ENTRY */ - } - - task = kshark_add_task(kshark_ctx, pid); - if (!task) { - 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 = tracecmd_cpus(kshark_ctx->handle); - - 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 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 = tracecmd_cpus(kshark_ctx->handle); - - 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) @@ -1216,294 +773,6 @@ bool kshark_data_matrix_alloc(size_t n_rows, int16_t **cpu_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); - 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); - 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); - 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); - - 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. @@ -1520,44 +789,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. * @@ -1594,42 +825,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 ef48d94b..54c71a77 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-cmd/trace-filter-hash.h" -#include "traceevent/event-parse.h" -#include "tracefs/tracefs.h" // KernelShark #include "libkshark-plugin.h" @@ -325,19 +323,6 @@ struct kshark_data_stream { /** Hard-coded maximum number of data stream. */ #define KS_MAX_NUM_STREAMS 127 -/** 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 a kshark session. */ struct kshark_context { /** Array of data stream descriptors. */ @@ -346,15 +331,6 @@ struct kshark_context { /** The number of data streams. */ int n_streams; - /** 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; @@ -415,19 +391,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); - -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 sd, int **pids); @@ -441,18 +404,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); static inline int kshark_get_pid(const struct kshark_entry *entry) @@ -617,18 +568,6 @@ static inline ssize_t kshark_load_matrix(struct kshark_context *kshark_ctx, 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 { /** @@ -751,10 +690,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); @@ -1039,16 +974,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 Oct 12 13:35:15 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11832765 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id F1599109B for ; Mon, 12 Oct 2020 14:03:56 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id BCC082074D for ; Mon, 12 Oct 2020 14:03:56 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="B97Srs23" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2390529AbgJLODr (ORCPT ); Mon, 12 Oct 2020 10:03:47 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58666 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1730073AbgJLNgb (ORCPT ); Mon, 12 Oct 2020 09:36:31 -0400 Received: from mail-wr1-x432.google.com (mail-wr1-x432.google.com [IPv6:2a00:1450:4864:20::432]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7969AC0613D5 for ; Mon, 12 Oct 2020 06:36:30 -0700 (PDT) Received: by mail-wr1-x432.google.com with SMTP id s9so7133471wro.8 for ; Mon, 12 Oct 2020 06:36:30 -0700 (PDT) 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=Fw/4Sl1YAcEgbLMsbXfizIaxLHyVjzD93gaTl8TYCrU=; b=B97Srs23E8nFUsYg+KkcOFyLfG3azab28F2iCnBsKlnjrqE1ejP9sTHaWGU+7DO8Qf PafV4JC1K1NS7l1ZC4iHspFyYVgNORXQXCrdRRjgEkWA1ONR0iTbXovfWdRvlPK5n57g 7vD+QmctxVrNBKvjM4F8dKt1yQfSJrhYPA/8h1SUIEMQHSJddy4Oqx+jvTrTp413Gxo9 XxWGTGBd78vWX8THop6Fk8hIr6DQ/YOWGQCwA+LGCqKdLl4O5C7adWQh43McZamNf+Lk aZr80sQYo8RUMdIzXVNjeLmPXJcSYw/O+cB2XEt3RCUqQScDd8ETCboDPkhn+4+//xOb zZug== 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=Fw/4Sl1YAcEgbLMsbXfizIaxLHyVjzD93gaTl8TYCrU=; b=fedxrjKAzwuR7NeljV28q4SZPZkIj/16xUdfyknWyqgCpEoOC/ZvO1ON8gzA8LssLV 8Drphd+I9LOtCvCOgj29QQUUtwj71lb6IXC0GxidCf5GtTTgX9Sz246wfHiM073Jz8zF xc8Mx/XKOlYVmptTRq6pezcEBYKJ+eeOaQQwlwxhak6/H5HXrSxTC40wkQI1KYtAJBxa LXS2PxnLpSkZ6gG6n06OvJoXwaoBqUNJneDayD3kStlY3uv48lWCatfNfC3picKfXn6N SwNEAqMF1Snu3T/01aPHlMHTmrnzT+rCUozYpz2OlGuy/9v4m/w0D9vlPEER72TSF4OW /hMg== X-Gm-Message-State: AOAM5308M3zhvNRH6CLvmlSj3WRZQiD4ER8sMtASpv79eMpZrEKlxQf5 eLeiJiBLC+KH4PDVVHmUKgZnWr7625g= X-Google-Smtp-Source: ABdhPJwhRXZA9xvcKZQHmnadOx5xIY248r0D0BCwVhFnuleRuI0hsb9ZgHtPpH41c0guEdZxYkDxzQ== X-Received: by 2002:a5d:4cd1:: with SMTP id c17mr17927683wrt.109.1602509788720; Mon, 12 Oct 2020 06:36:28 -0700 (PDT) Received: from localhost.localdomain ([84.40.93.41]) by smtp.gmail.com with ESMTPSA id k5sm23145388wmb.19.2020.10.12.06.36.27 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Oct 2020 06:36:28 -0700 (PDT) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v2 12/20] kernel-shark: Redesign the plugin interface Date: Mon, 12 Oct 2020 16:35:15 +0300 Message-Id: <20201012133523.469040-13-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20201012133523.469040-1-y.karadz@gmail.com> References: <20201012133523.469040-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 | 597 ++++++++++++++++++++++++++++++++++------- src/libkshark-plugin.h | 279 ++++++++++++++----- src/libkshark.c | 56 +++- src/libkshark.h | 25 +- 4 files changed, 791 insertions(+), 166 deletions(-) diff --git a/src/libkshark-plugin.c b/src/libkshark-plugin.c index 4b21392a..8e5299cf 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 @@ -27,23 +26,36 @@ #include "libkshark-plugin.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 +67,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 +80,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,23 +106,20 @@ 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, +void kshark_unregister_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 **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); @@ -126,9 +134,71 @@ 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_proc_handler *last; + + while (handlers) { + last = handlers; + handlers = handlers->next; + free(last); + } +} + +/** + * @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_event_handler *last; + struct kshark_draw_handler *last; while (handlers) { last = handlers; @@ -139,96 +209,182 @@ void kshark_free_event_handler_list(struct kshark_event_handler *handlers) /** * @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; + 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); - plugin->close = dlsym(plugin->handle, - KSHARK_PLUGIN_DEINITIALIZER_NAME); + close_func = dlsym(plugin->handle, + KSHARK_PLOT_PLUGIN_DEINITIALIZER_NAME); - if (!plugin->init || !plugin->close) + if (!!(long)init_func && !!(long)close_func) { + plugin->process_interface = + calloc(1, sizeof(*plugin->process_interface)); + + if (!plugin->process_interface) + goto fail; + + plugin->process_interface->name = strdup(plugin->name); + plugin->process_interface->init = init_func; + plugin->process_interface->close = close_func; + } else if (!!(long)init_func || !!(long)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); + + if (!!(long)init_func && + !!(long)close_func && + !!(long)check_func) { + plugin->readout_interface = + calloc(1, sizeof(*plugin->readout_interface)); + + if (!plugin->readout_interface) + goto fail; + + plugin->readout_interface->name = strdup(plugin->name); + plugin->readout_interface->init = init_func; + plugin->readout_interface->close = close_func; + plugin->readout_interface->check_data = check_func; + + kshark_register_input(kshark_ctx, plugin->readout_interface); + } else if (!!(long)init_func || + !!(long)close_func || + !!(long)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); - return EFAULT; + return NULL; +} + +/** 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 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 +404,303 @@ 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; + + 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; +} + +/** + * @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 plugins: A list of plugins to search in. + * @param lib: The plugin object file to load. * - * @param kshark_ctx: Input location for context pointer. + * @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. + * + * @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 disables. + * 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) { + 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 { + 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 b3cf1c62..3143b018 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,47 @@ 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_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) + +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 +69,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 +100,15 @@ enum kshark_plugin_actions { * plugins. */ KSHARK_PLUGIN_CLOSE, - - /** - * Task draw action. This action identifier is used by the plugin draw - * function. - */ - KSHARK_PLUGIN_TASK_DRAW, - - /** - * CPU draw action. This action identifier is used by the plugin draw - * function. - */ - KSHARK_PLUGIN_CPU_DRAW, }; -/** - * Plugin Event handler structure, defining the properties of the required - * kshark_entry. - */ -struct kshark_event_handler { - /** Pointer to the next Plugin Event handler. */ - struct kshark_event_handler *next; +/** No event identifier associated with the plugin. */ +#define KS_PLUGIN_NO_EVENT (-ENODEV) - /** Unique Id ot the trace event type. */ - int id; +/** Plugin's Trace event processing handler structure. */ +struct kshark_event_proc_handler { + /** Pointer to the next Plugin Event handler. */ + struct kshark_event_proc_handler *next; /** * Event action function. This action can be used to modify the content @@ -120,6 +116,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); + +void 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 +146,66 @@ 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 data check function for the plugin. + */ +typedef int (*kshark_check_data_func)(const char *filename); - /** The plugin object file to load. */ - char *file; +/** + * A function type to be used when defining plugin's configuration/control + * function. + */ +typedef void *(*kshark_plugin_ctrl_func)(void *); + + +/** Plugable Data Readout Interface (dri). */ +struct kshark_dri { + /** A short name for this data input. */ + char *name; + + /** Data format identifier. */ + int format; + + /** 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; +}; - /** Plugin Event handler. */ - void *handle; +/** 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 +214,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 e363395a..bd2e4cc0 100644 --- a/src/libkshark.c +++ b/src/libkshark.c @@ -16,6 +16,7 @@ #include #include #include +#include // KernelShark #include "libkshark.h" @@ -34,7 +35,6 @@ static bool kshark_default_context(struct kshark_context **context) kshark_ctx->stream = calloc(KS_MAX_NUM_STREAMS, sizeof(*kshark_ctx->stream)); - kshark_ctx->event_handlers = NULL; kshark_ctx->collections = NULL; kshark_ctx->plugins = NULL; @@ -224,12 +224,22 @@ static void set_format(struct kshark_context *kshark_ctx, struct kshark_data_stream *stream, const char *filename) { + struct kshark_dri_list *input; + stream->format = KS_INVALIDE_DATA; if (is_tep(filename)) { stream->format = KS_TEP_DATA; return; } + + for (input = kshark_ctx->inputs; input; input = input->next) { + stream->format = input->interface->check_data(filename); + if (stream->format != KS_INVALIDE_DATA) { + input->interface->format = stream->format; + return; + } + } } /** @@ -244,6 +254,7 @@ static void set_format(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; @@ -256,6 +267,11 @@ int kshark_stream_open(struct kshark_data_stream *stream, const char *file) return kshark_tep_init_input(stream, file); default: + for (input = kshark_ctx->inputs; input; input = input->next) { + if (stream->format == input->interface->format) + return input->interface->init(stream); + } + return -ENODATA; } } @@ -323,6 +339,7 @@ int *kshark_all_streams(struct kshark_context *kshark_ctx) static void 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; @@ -344,6 +361,11 @@ static void kshark_stream_close(struct kshark_data_stream *stream) break; default: + for (input = kshark_ctx->inputs; input; input = input->next) { + if (stream->format == input->interface->format) + input->interface->close(stream); + } + break; } @@ -364,6 +386,13 @@ void kshark_close(struct kshark_context *kshark_ctx, int sd) if (!stream) return; + /* 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); + } + kshark_stream_close(stream); kshark_stream_free(stream); kshark_ctx->stream[sd] = NULL; @@ -418,6 +447,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; @@ -702,6 +733,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 54c71a77..a73539f4 100644 --- a/src/libkshark.h +++ b/src/libkshark.h @@ -313,6 +313,18 @@ struct kshark_data_stream { /** The type of the data. */ enum kshark_data_format format; + /** 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. @@ -368,11 +380,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); @@ -665,6 +683,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 Oct 12 13:35:16 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11832761 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id ED566139F for ; Mon, 12 Oct 2020 14:03:45 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id B53FE20838 for ; Mon, 12 Oct 2020 14:03:45 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="HMYEMiPx" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2390858AbgJLOD3 (ORCPT ); Mon, 12 Oct 2020 10:03:29 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58674 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1730567AbgJLNgd (ORCPT ); Mon, 12 Oct 2020 09:36:33 -0400 Received: from mail-wm1-x32e.google.com (mail-wm1-x32e.google.com [IPv6:2a00:1450:4864:20::32e]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id DB9F4C0613D6 for ; Mon, 12 Oct 2020 06:36:32 -0700 (PDT) Received: by mail-wm1-x32e.google.com with SMTP id 13so17211315wmf.0 for ; Mon, 12 Oct 2020 06:36:32 -0700 (PDT) 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=CTJX00qPgrrAZTxudylLqB7eDCCdJRTQMfy/6Cxef9A=; b=HMYEMiPxwMWoCd7kGeNzR9h65PfY+kDKHJESuyDeD+2orGFCYUNQqIqywLoieGu30t kX1BmSfuXQ6ws3uijTlI6kwrE4Lkr+sw9U8JmMPjx5x18w5isyqgVcLEPoufmx2BG6Eo MgiiT/2VBPfwK5R5tcw6w7W5SrkZlMxjqTN6XmFkfDWuGP98NJkqPzYsgyIGrYube2Lp A4d8wdnki8Od4ExyN8uEjFMZkL/dvKBfoM+zzlPQuWRRaDo4peum3CsmA2abVIsyc1v9 vVw18bLB0rjOeR8vVWRNd6xCEKuBC8P5JblgWAMokR2AZLVKqBU59+3wfpL53ZOVySS3 HOLg== 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=CTJX00qPgrrAZTxudylLqB7eDCCdJRTQMfy/6Cxef9A=; b=Ol8uIpQ0sMy254R/5j7mlDlioClUqNiSkH6cnR6qTq/s8F7y89O4WUAlK8+PTpStNA AnExjXBV/7KXl16xh0nHxRXr9GmH78VP1ssfBSfKB5o92fYgouQ9DqI9awzub5tPVMZy W9vl+JiTQOnqixoW+/qJGFXqP1/NFkNFaJ9Kr8482gUpSawVOIGqJ6VB4q7sc9GhQNx4 dVo6ed4NtqzYoOrmhiKdEyZN+h298DmuwFVead9ZITL5lOZxVylWw164jGLmop1ArWz1 H3e00qICGLQwtUGzG2Ox3NIt75hfk22IrLPz434cTHyRbkErOJlnXXNZO2i1SOC4bOJc 8tdA== X-Gm-Message-State: AOAM530DDI/VWBhL4KOBaNz98VIXS1sigOGKC+mGGk1Qa3/UCM/RPqf5 Emjf8CvqcpyEsgd1d5gQ9g8= X-Google-Smtp-Source: ABdhPJzOiaPifwbxcnLH2Ao6WRzf58lzxUj2E0ZB93QCYdVRz7jMp9Lp7NSyHGQK90ZX6mMRm8gxnA== X-Received: by 2002:a1c:9e0e:: with SMTP id h14mr10820273wme.18.1602509790771; Mon, 12 Oct 2020 06:36:30 -0700 (PDT) Received: from localhost.localdomain ([84.40.93.41]) by smtp.gmail.com with ESMTPSA id k5sm23145388wmb.19.2020.10.12.06.36.29 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Oct 2020 06:36:30 -0700 (PDT) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v2 13/20] kernel-shark: Complete the stream integration Date: Mon, 12 Oct 2020 16:35:16 +0300 Message-Id: <20201012133523.469040-14-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20201012133523.469040-1-y.karadz@gmail.com> References: <20201012133523.469040-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 | 3 +- src/libkshark-collection.c | 121 +++++++--- src/libkshark-tepdata.c | 352 +++++++++++++++++++++++++++++ src/libkshark-tepdata.h | 49 ++++ src/libkshark.c | 447 +++++++++++++++++++++++++------------ src/libkshark.h | 133 ++++++----- 8 files changed, 920 insertions(+), 260 deletions(-) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index f6ed8978..8d40e42c 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 bebc1813..38afab81 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 2bdab1a5..74e12a28 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,7 +7,8 @@ add_library(kshark SHARED libkshark.c libkshark-plugin.c libkshark-tepdata.c # libkshark-configio.c - libkshark-collection.c) + libkshark-collection.c + libkshark-tepdata.c) target_link_libraries(kshark ${TRACECMD_LIBRARY} ${TRACEFS_LIBRARY} diff --git a/src/libkshark-collection.c b/src/libkshark-collection.c index 66cdbca8..915983b9 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 30c383c8..d9d57843 100644 --- a/src/libkshark-tepdata.c +++ b/src/libkshark-tepdata.c @@ -318,6 +318,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) @@ -1160,6 +1163,43 @@ 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) +{ + int i, n_tep_plugins = sizeof(tep_plugin_names) / sizeof (const char *); + struct kshark_plugin_list *plugin; + struct kshark_data_stream *stream; + + 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 @@ -1210,6 +1250,172 @@ static int kshark_tep_stream_init(struct kshark_data_stream *stream, return -EFAULT; } +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 = malloc(n * sizeof(char *)); + + for (i = 0; i < n; ++i) + buffer_names[i] = + strdup(tracecmd_buffer_instance_name(top_input, i)); + + *n_buffers = n; + return buffer_names; +} + +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); + buffer_stream->format = KS_TEP_DATA; +} + +/** + * @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); + + 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); + + 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); + + ret = kshark_tep_stream_init(buffer_stream, buffer_input); + if (ret != 0) + return -EFAULT; + } + + return n_buffers; +} + /** Initialize the FTRACE data input (from file). */ int kshark_tep_init_input(struct kshark_data_stream *stream, const char *file) @@ -1389,3 +1595,149 @@ char **kshark_tracecmd_local_plugins() { return tracefs_tracers(tracefs_get_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 || guest_stream->format != KS_TEP_DATA) + 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 || peer_stream->format != KS_TEP_DATA) + 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 && + strcmp(stream->name, "top") == 0) + 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 a2bd211e..b6c9439e 100644 --- a/src/libkshark-tepdata.h +++ b/src/libkshark-tepdata.h @@ -41,9 +41,58 @@ void kshark_tep_filter_reset(struct kshark_data_stream *stream); char **kshark_tracecmd_local_plugins(); +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); + #ifdef __cplusplus } #endif diff --git a/src/libkshark.c b/src/libkshark.c index bd2e4cc0..6ce7b6ba 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 @@ -36,16 +38,6 @@ static bool kshark_default_context(struct kshark_context **context) sizeof(*kshark_ctx->stream)); 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; @@ -386,6 +378,12 @@ void kshark_close(struct kshark_context *kshark_ctx, int sd) if (!stream) return; + /* + * 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); @@ -530,103 +528,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_context *kshark_ctx, int pid) +static bool kshark_show_task(struct kshark_data_stream *stream, int pid) { - return filter_find(kshark_ctx->show_task_filter, pid, true) && - filter_find(kshark_ctx->hide_task_filter, pid, false); + return filter_find(stream->show_task_filter, pid, true) && + filter_find(stream->hide_task_filter, pid, false); } -static bool kshark_show_event(struct kshark_context *kshark_ctx, int pid) +static bool kshark_show_event(struct kshark_data_stream *stream, int pid) { - return filter_find(kshark_ctx->show_event_filter, pid, true) && - filter_find(kshark_ctx->hide_event_filter, pid, false); + return filter_find(stream->show_event_filter, pid, true) && + filter_find(stream->hide_event_filter, pid, false); } -static bool kshark_show_cpu(struct kshark_context *kshark_ctx, int cpu) +static bool kshark_show_cpu(struct kshark_data_stream *stream, int cpu) { - return filter_find(kshark_ctx->show_cpu_filter, cpu, true) && - filter_find(kshark_ctx->hide_cpu_filter, cpu, false); + return filter_find(stream->show_cpu_filter, cpu, true) && + filter_find(stream->hide_cpu_filter, cpu, false); +} + +static struct kshark_hash_id *get_filter(struct kshark_context *kshark_ctx, + int sd, + enum kshark_filter_type filter_id) +{ + struct kshark_data_stream *stream; + + stream = kshark_get_data_stream(kshark_ctx, sd); + if (!stream) + return NULL; + + return kshark_get_filter(stream, filter_id); } /** - * @brief Add an Id value to the filster specified by "filter_id". + * @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) +{ + 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 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); } /** @@ -636,7 +665,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; } @@ -645,17 +674,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(uint8_t *v) { @@ -663,56 +724,100 @@ static void set_all_visible(uint8_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 (stream->format == KS_TEP_DATA && + 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_data_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); } /** @@ -729,6 +834,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); } @@ -852,15 +958,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; @@ -884,15 +990,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; @@ -903,20 +1010,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. @@ -925,8 +1092,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. @@ -937,7 +1105,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)); @@ -952,7 +1120,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; @@ -1006,7 +1175,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 a73539f4..7239f56e 100644 --- a/src/libkshark.h +++ b/src/libkshark.h @@ -24,10 +24,6 @@ extern "C" { #endif -// trace-cmd -#include "trace-cmd/trace-cmd.h" -#include "trace-cmd/trace-filter-hash.h" - // KernelShark #include "libkshark-plugin.h" @@ -331,7 +327,6 @@ struct kshark_data_stream { */ struct kshark_data_stream_interface interface; }; - /** Hard-coded maximum number of data stream. */ #define KS_MAX_NUM_STREAMS 127 @@ -343,27 +338,6 @@ struct kshark_context { /** The number of data streams. */ int n_streams; - /** 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 @@ -371,12 +345,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; @@ -651,14 +619,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) @@ -675,9 +651,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, @@ -688,10 +671,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, }; @@ -707,15 +690,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. @@ -733,7 +727,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 @@ -755,10 +749,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; @@ -772,7 +769,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); @@ -789,8 +786,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 @@ -807,11 +804,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 @@ -834,26 +837,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 * @@ -995,17 +1002,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); From patchwork Mon Oct 12 13:35:17 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11832757 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id F25AC139F for ; Mon, 12 Oct 2020 14:03:24 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id CCD232080D for ; Mon, 12 Oct 2020 14:03:24 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="KiZI3BNt" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2390849AbgJLODN (ORCPT ); Mon, 12 Oct 2020 10:03:13 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58774 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1730986AbgJLNhM (ORCPT ); Mon, 12 Oct 2020 09:37:12 -0400 Received: from mail-wr1-x42a.google.com (mail-wr1-x42a.google.com [IPv6:2a00:1450:4864:20::42a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 4568EC0613D7 for ; Mon, 12 Oct 2020 06:36:34 -0700 (PDT) Received: by mail-wr1-x42a.google.com with SMTP id n15so19248080wrq.2 for ; Mon, 12 Oct 2020 06:36:34 -0700 (PDT) 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=PZVK5ur0QLZJDpDbIdcvUN+KV9jiO+1gHCwDpEfkeJI=; b=KiZI3BNtwAxiPErVU7GGfVztqvHcGu8tUCBrWFC68/IhUG5ZIPPxPk9pLewpKdYh02 osakk0ut6Bwfk6lMC9bXKAG8IDv90y+PO7V16kNdHNNpvXKJrRgylXDEkfgWSpC7N3+m JOlb2fi3AHAuyYqaXpEfNDYIwEmol7H0eKZ1RdEB8YVRWml09F8GhuFJKKJoWO3Fhpso NitWDfsR0VcTMpUPzma1Ryj9xGP3bkzIrpSVdyN2NY7kXGWmeNXVpzNHpFvY4qfXJOoJ TP/ozUdW3aPN5SW/Qm/svxqTu8lWrCLZsJLJyU+sK6GPsxKhb2S2vnoWCDJzPKws6ITf 6U7A== 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=PZVK5ur0QLZJDpDbIdcvUN+KV9jiO+1gHCwDpEfkeJI=; b=DN42HvZU1fRy9eAjNz40FJOkvpQfE2zZ44S9aUakP/Nrt1czck5xiFWJQamxd8X25f G9VIBoEYP7DIcJutBzWw68C2npTpZqfdzT3pLqRhuTgmaLGUM++Oi74EJEuSBzTJVhD5 yBhY1grr94ko9N0BQx7fdXILZ+cgWnW2n+Vcrt++2+AgWWb1uZU9zxEYQG/ciPe+9lfd w5eDPPF1KsiknuzyeHjSOiIyPN9ToBS21LgNSf03tZtGyg+Iszb1KQdC1uZxTqtW98nF GyUDzChjrBTOzHiM/2K4vxsT5c8sDxdHs9AQp9+hujIZxSc28df897HS+JUAWyIpVsmc LnAw== X-Gm-Message-State: AOAM530c6gm1HcbwbKD7Wx8+h/xSYw0T7awq95g8Cs97LnbYg3PeSlXb RPeDx6xstq82nQiatolnItE= X-Google-Smtp-Source: ABdhPJw5LYDqCMd4BVgymmC5gYo6TskIkuDfI9J9FGym2RrdnHR0SXb54Kw0+Irf9W8W5auEOMR7VQ== X-Received: by 2002:adf:f88a:: with SMTP id u10mr2346647wrp.1.1602509792776; Mon, 12 Oct 2020 06:36:32 -0700 (PDT) Received: from localhost.localdomain ([84.40.93.41]) by smtp.gmail.com with ESMTPSA id k5sm23145388wmb.19.2020.10.12.06.36.31 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Oct 2020 06:36:31 -0700 (PDT) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v2 14/20] kernel-shark: Provide merging of multiple data streams Date: Mon, 12 Oct 2020 16:35:17 +0300 Message-Id: <20201012133523.469040-15-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20201012133523.469040-1-y.karadz@gmail.com> References: <20201012133523.469040-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 firs 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 | 60 +++++++++ src/libkshark.c | 255 +++++++++++++++++++++++++++++++++++++ src/libkshark.h | 47 +++++++ 4 files changed, 366 insertions(+) create mode 100644 examples/multibufferload.c diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 8d40e42c..831eee24 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 00000000..70b2733a --- /dev/null +++ b/examples/multibufferload.c @@ -0,0 +1,60 @@ +#include +#include + +#include "libkshark.h" +#include "libkshark-tepdata.h" + +const char *default_file = "trace.dat"; + +void put_entry(struct kshark_entry *e) +{ + char *entry_str = kshark_dump_entry(e); + puts(entry_str); + free(entry_str); +} + +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) + put_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 6ce7b6ba..6f99dd6c 100644 --- a/src/libkshark.c +++ b/src/libkshark.c @@ -1258,3 +1258,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.cpu_array, + &merged_data.pid_array, + &merged_data.event_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 7239f56e..fbe1b2ac 100644 --- a/src/libkshark.h +++ b/src/libkshark.h @@ -1059,12 +1059,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 **cpu_array, int32_t **pid_array, int32_t **event_array, int64_t **offset_array, int64_t **ts_array); +/** Structure representing a data set made of data columns (arrays). */ +struct kshark_matrix_data_set { + /** CPU Id column. */ + int16_t *cpu_array; + + /** PID column. */ + int32_t *pid_array; + + /** Event Id column. */ + int32_t *event_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 Oct 12 13:35:18 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11832755 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 41778109B for ; Mon, 12 Oct 2020 14:03:14 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 186E12087E for ; Mon, 12 Oct 2020 14:03:14 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="hQlOICZ8" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2390853AbgJLODN (ORCPT ); Mon, 12 Oct 2020 10:03:13 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58776 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1730983AbgJLNhM (ORCPT ); Mon, 12 Oct 2020 09:37:12 -0400 Received: from mail-wr1-x42f.google.com (mail-wr1-x42f.google.com [IPv6:2a00:1450:4864:20::42f]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id AA777C0613D8 for ; Mon, 12 Oct 2020 06:36:36 -0700 (PDT) Received: by mail-wr1-x42f.google.com with SMTP id e17so19200281wru.12 for ; Mon, 12 Oct 2020 06:36:36 -0700 (PDT) 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=YHx5em2dTsUrJvVxQcbxhFIVzD8i8lTk54DJMq18OPM=; b=hQlOICZ86ePSkOYLaqfEkYpfyjmdWoafsa1x9yNxhvpODmqKZL9ccx6O5bgc1DG2Dp GIlrg9JJ0okGcS2lApDlgfP2mBFG0l0Ixuk8s4jCRP/k8L+7IfCvJVV1megWkjkrIpEB 5VyF4COUQcF9rUvceJsIWzZ8xVp9lxUTM796BNWQLd0yE0x+I4RK0zkS42t6ifuPUOtU K2Iq/Ku3vlbd2kYhYMVTknP4c5Hiu56N7NrgM708cL1ac7KhJWNy0Waz2mVhvS30vBXW ivGgmJ34MLfrcRWNfzmhD4csGpVpWO71z2/lN8qiYW2HrVVRsttws5xLV0+tzkXf3diP Ctbw== 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=YHx5em2dTsUrJvVxQcbxhFIVzD8i8lTk54DJMq18OPM=; b=Vcx2dHzVLgvVuhj21XjKEIN2GQCuTbpbNWxsGF/5Nu9f47uNlDtJby1KIrnaQwp7R3 +lCoz2z4OGfX0KZTacA6di554U/SARgDLAUPIF4G6NwGX3Wm9+6h2cqzfi7HIMjVbmpX e6y6vJWcOsHRUdhaJ/9Vt09Uur1kAn+pT10QnpHk+Ii0/KMzr/6H0+yqjuNTUo13+lFf 4NZL881MRK/MX/cy8h8GD7f+hNDyo/1nTitGaquih6TJHEBlEeSQbpn5QtmFduMCMusg QloO1eJaqUNpysDgO+thB6wcaXl+ekCOL8B3lbbpOK1peQXVrfWXe6whmPwcSky3GFpm XsVg== X-Gm-Message-State: AOAM530AmgRBbmpYCcjVQbSezjMPGN5mdsSPM3E2lUa2GbVCd8/O7JY8 FVc+2K3WRbQ2T+VNq64j5TFjD2xU7UM= X-Google-Smtp-Source: ABdhPJwz0IuGoGT2mXYRQtaCsZ6W9Q4NKAGdguKjpKnL3mIoNGWswk/3RgMaVc1STXvK0zvTKihClQ== X-Received: by 2002:adf:8290:: with SMTP id 16mr31807664wrc.103.1602509795112; Mon, 12 Oct 2020 06:36:35 -0700 (PDT) Received: from localhost.localdomain ([84.40.93.41]) by smtp.gmail.com with ESMTPSA id k5sm23145388wmb.19.2020.10.12.06.36.33 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Oct 2020 06:36:34 -0700 (PDT) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v2 15/20] kernel-shark: Integrate the stream definitions with data model Date: Mon, 12 Oct 2020 16:35:18 +0300 Message-Id: <20201012133523.469040-16-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20201012133523.469040-1-y.karadz@gmail.com> References: <20201012133523.469040-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 831eee24..2f6acea2 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 b177b088..568072d8 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 74e12a28..72eb8fd2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,7 +3,7 @@ message("\n src ...") 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 babac2a2..97cff314 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 47793b11..87e252eb 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 Oct 12 13:35:19 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11832701 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id BF66D92C for ; Mon, 12 Oct 2020 13:37:40 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 9F6652222E for ; Mon, 12 Oct 2020 13:37:40 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="nRblHlQp" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731066AbgJLNh2 (ORCPT ); Mon, 12 Oct 2020 09:37:28 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58778 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1730985AbgJLNhM (ORCPT ); Mon, 12 Oct 2020 09:37:12 -0400 Received: from mail-wr1-x441.google.com (mail-wr1-x441.google.com [IPv6:2a00:1450:4864:20::441]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E2E00C0613D9 for ; Mon, 12 Oct 2020 06:36:37 -0700 (PDT) Received: by mail-wr1-x441.google.com with SMTP id h7so19259717wre.4 for ; Mon, 12 Oct 2020 06:36:37 -0700 (PDT) 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=iIx7+grTv/751ft5w1VwxSU0mvVww6kVQ6CUeDcj3aM=; b=nRblHlQpbiYrdUgpQKDlR+FFt+yx0dPc/2jZSxn3ihnuToHb17VZiyrN0WaPDNVX01 U9J+vILbZ3AQtNRHXHqrERL7JMxOH50pR6CK4BgEiu5QP1hhW8FvB6B8om51mIqevxll 50sNe5rbVIVUzqOrTBqbiLCzytWhUDIYAaqPTtWqL0KKDURYQyhoEFT1DSFMePEkUnwO zrT1NDsGQupdaPWTG5fxWXTmQ1HOY1ZE/QyDLD0pqvCOb7KSk4DfifMEmd9K9UZhM5BT 3+xrNvLPRPtIdKDLJvMq7O+z3RcPHD2OGOuezbovFyRCrZz1yEYFVlKy6YpE8DR7uNYK 5nBg== 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=iIx7+grTv/751ft5w1VwxSU0mvVww6kVQ6CUeDcj3aM=; b=oYxMg+qCk/N8uZJVqHKT1Gq5sgwbfLg28FZRI7eykSmXNnQixrbQWcrAbiRGHUFvi2 e/8Wie3mLZCyuI5rLobF5O0YkVY6X3AD4AS/leOM6hRG8lod8mTxW4vQqLYT/uLptt0D pOaO5h73jpRthAU02Dpt8JIMRNYd+E38JAOr13c0LdtKpZEaA1544pzR6rWYpRLRAg8A xavmMX6DfBLhbewnd4r5xAhdYklFVT7A98Z+/YVC7SXbN/Sl78FttobrP5WLqlSMIJWK d+jewbIeuN7z1KYsJuVHEh39AP424rdu3Hb4WfUEXVhPrwnHJrQlqlA6TP5i6O38Nh8A 02cQ== X-Gm-Message-State: AOAM533vBhYvQI2b9MriZMqCzQwvBo1p1UBrfpAi1OGbJxxSluT0F9+L RxVg8P2kt42/ozxx8oyji1g= X-Google-Smtp-Source: ABdhPJxBVheYGcgQ+7Nwxm70J/Pr9r05QeyIbyQ9yG2qJ1xMfqwh5VEPvOYMjldtQ7oUP2aj7AAzqA== X-Received: by 2002:adf:b30c:: with SMTP id j12mr15254116wrd.82.1602509796658; Mon, 12 Oct 2020 06:36:36 -0700 (PDT) Received: from localhost.localdomain ([84.40.93.41]) by smtp.gmail.com with ESMTPSA id k5sm23145388wmb.19.2020.10.12.06.36.35 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Oct 2020 06:36:36 -0700 (PDT) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v2 16/20] kernel-shark: Use only signed types for model defs Date: Mon, 12 Oct 2020 16:35:19 +0300 Message-Id: <20201012133523.469040-17-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20201012133523.469040-1-y.karadz@gmail.com> References: <20201012133523.469040-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 97cff314..f3864bfd 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 87e252eb..b8624809 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 Oct 12 13:35:20 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11832699 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 8FBB8139F for ; Mon, 12 Oct 2020 13:37:30 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 6CDFC22244 for ; Mon, 12 Oct 2020 13:37:30 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="AO4CFrAQ" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731072AbgJLNh3 (ORCPT ); Mon, 12 Oct 2020 09:37:29 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58780 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1730989AbgJLNhM (ORCPT ); Mon, 12 Oct 2020 09:37:12 -0400 Received: from mail-wm1-x342.google.com (mail-wm1-x342.google.com [IPv6:2a00:1450:4864:20::342]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id BC80AC0613DA for ; Mon, 12 Oct 2020 06:36:39 -0700 (PDT) Received: by mail-wm1-x342.google.com with SMTP id 13so17211638wmf.0 for ; Mon, 12 Oct 2020 06:36:39 -0700 (PDT) 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=N7pkMWcXJFtNywsHJwHLLOPKrqZCvPFvqytyCVXU6To=; b=AO4CFrAQPM/4NjVAULdIRxWMC5/ZTQaZzmbrSFXs/fiNya5NS6OEpPA3N7xtG80Tm9 Z2Qr0RaaaIP2ofwJvxgzSMZjLAdaz83LyPs7PJNbBgYIxtiaGP3htxw0LthGYsVCIpb0 L7YDYQnqv8ZjkkKhbZEPuHDi/3j10ZfkrDs6xJwDVQWRVwLUNVJDtlPI4cC8BeF3RSgq /NOLXSx/IQNUQpIbHh4PdLmmlTCPZRh3YmEL71f03xWX7E1FteadSpoOQL2EvTS/w9kd 7lxDXZgT4u8NuE+mgG5wlg3WqcCQHe9OuLLwHJ82gbSk+/xdTM11ZSEdCmjVAB0BmJaU AyBw== 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=N7pkMWcXJFtNywsHJwHLLOPKrqZCvPFvqytyCVXU6To=; b=S0hK6fmkPocvs/YIwH1UyE9Ms+CDkvhhVT7DGcD94zoj2cMElvq+0P46zgI8+bbXmQ JcjFxr0CSGJc+IaqYLQ2gKksJvgiHLlgfP0UjbZ/UHygyiXdNXdS5NmsfE6WzXdaLfbG uS726pB/8Qtf6gENSe/VivrnsCoZy5GpL494iOXrwmPtqjyry5W7A88CnpQUBrNIbUb4 4yRDs6vjEYqEF76Kye9MkgLotyg29slnSG86JS5NT8F8n5L8yF8zxX6dqdvTtIryrIJv OzF7GvIzJBRgAV4KReHiIY2h56VzsYzf/sWDA203j1yEdjcuiv4CY2SXyGHfOysND/yS FXSg== X-Gm-Message-State: AOAM531uja7hxwM8n4MHqxN1AZTURuzc4SOOIZrOlQGGdQV0XOaWFj91 ASzgQNxnEoaMUdvJpkABZ18= X-Google-Smtp-Source: ABdhPJyNqPzn13oOFSIMOsSZwa8KegoShY4Sb+r1kdFYuxKb0yT3HwUU/cNiIlOrWPddRNiIrrNOiA== X-Received: by 2002:a05:600c:21d9:: with SMTP id x25mr10414094wmj.145.1602509798504; Mon, 12 Oct 2020 06:36:38 -0700 (PDT) Received: from localhost.localdomain ([84.40.93.41]) by smtp.gmail.com with ESMTPSA id k5sm23145388wmb.19.2020.10.12.06.36.37 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Oct 2020 06:36:37 -0700 (PDT) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v2 17/20] kernel-shark: Add ksmodel_get_bin() Date: Mon, 12 Oct 2020 16:35:20 +0300 Message-Id: <20201012133523.469040-18-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20201012133523.469040-1-y.karadz@gmail.com> References: <20201012133523.469040-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 f3864bfd..2301b069 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 b8624809..8989ee0e 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 Oct 12 13:35:21 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11832697 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id A22C292C for ; Mon, 12 Oct 2020 13:37:28 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 84E9E208B8 for ; Mon, 12 Oct 2020 13:37:28 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="bfmjEOsY" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731054AbgJLNh0 (ORCPT ); Mon, 12 Oct 2020 09:37:26 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58786 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1730992AbgJLNhO (ORCPT ); Mon, 12 Oct 2020 09:37:14 -0400 Received: from mail-wm1-x342.google.com (mail-wm1-x342.google.com [IPv6:2a00:1450:4864:20::342]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 52B9CC0613DB for ; Mon, 12 Oct 2020 06:36:41 -0700 (PDT) Received: by mail-wm1-x342.google.com with SMTP id e23so10179411wme.2 for ; Mon, 12 Oct 2020 06:36:41 -0700 (PDT) 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=rX9XfHhXf9sK0NoFo9P7mnliYZ0IH4YeOB5MqGmFCZI=; b=bfmjEOsY0HtsjlcKsTjmSaqNHQ8oIHPY53PEN3hWBBcVCvcboqhpLjI1t/vmTtRNVi MHSb7K8kbA+tV/x/MQLbWXOniwJHOMtCDXJlDULxEV9oIpzaKoBycbr0s0oJ+TgNE083 AW0kThI1aawcJqysXFV15j0ToBWnli/SkxPHXo1YqUuclmK2iQHVadJP+c7kWDmgJqnq mJL3omuCDDq55Muv494pjW0lFQxG9Li9cuWY84FI3YCjbrsyqstFSkKm19Wes7vyqFiD TS20S1+XStEkCzF6XtEpOCEHanuCsz16bESY1rkXbdV1JlS2cQERcZoW3PWiBa0rO07P UBHg== 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=rX9XfHhXf9sK0NoFo9P7mnliYZ0IH4YeOB5MqGmFCZI=; b=KAoKWpGM9DlNG0rdFa2cx5WXRZbkPflMshCrnczgBbSa1OidpS/ft7UyG8GekEDMwZ OqVRcn1Otw2XmVdFzjvSH2oQ45GA9cKPrgd7BApGdqKPeguR9hDcZ3ZihdU+Wr+9ek3T oixMNkALR8B3j/7PQa7PDoK+ydnQMKlFnlKs9EpNBOb1JZq0GFTb5vxgtToAS75nW5k1 VyN8MzEfXwEkiYQGsx3iDlNwcg4PQ8cD4vZRriY0dLnJ+mICQ+3c1e7PKUfYvn5MNzg+ 7Fdcq8/ajr75scyyiwpNfly312J/XrdE5xWnSCUaszRRSTQJ8L8wJmgBdg5B/zlejjoz iXJA== X-Gm-Message-State: AOAM53045nMnlTXLHLKgBoHtLcd12mXPfJWwmu5J29aW2ijQBhJ5yV1g 3WDfnl0caeYt1Ua9cV9o+Ds= X-Google-Smtp-Source: ABdhPJx9Fs0XCnkxl/uzxQiekCGu7WDvjrkAYCD4sDLZkxmtf+3KDUemItyQoUoZnDKETj0i41n3CA== X-Received: by 2002:a1c:1dc3:: with SMTP id d186mr10412074wmd.182.1602509800085; Mon, 12 Oct 2020 06:36:40 -0700 (PDT) Received: from localhost.localdomain ([84.40.93.41]) by smtp.gmail.com with ESMTPSA id k5sm23145388wmb.19.2020.10.12.06.36.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Oct 2020 06:36:39 -0700 (PDT) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v2 18/20] kernel-shark: Protect ksmodel_set_in_range_bining() Date: Mon, 12 Oct 2020 16:35:21 +0300 Message-Id: <20201012133523.469040-19-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20201012133523.469040-1-y.karadz@gmail.com> References: <20201012133523.469040-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 2301b069..44f829d6 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 Oct 12 13:35:22 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11832703 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 1593C16BC for ; Mon, 12 Oct 2020 13:37:41 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id E5B0522247 for ; Mon, 12 Oct 2020 13:37:40 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="UOHpDeYm" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1730992AbgJLNh1 (ORCPT ); Mon, 12 Oct 2020 09:37:27 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58788 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729934AbgJLNhO (ORCPT ); Mon, 12 Oct 2020 09:37:14 -0400 Received: from mail-wr1-x444.google.com (mail-wr1-x444.google.com [IPv6:2a00:1450:4864:20::444]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E036CC0613DC for ; Mon, 12 Oct 2020 06:36:42 -0700 (PDT) Received: by mail-wr1-x444.google.com with SMTP id x7so10627979wrl.3 for ; Mon, 12 Oct 2020 06:36:42 -0700 (PDT) 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=0ZiYvgJmwEaAbc61G30SdrrBhl//EoKenKIaWmqCYPg=; b=UOHpDeYmchR+ZPDTkvzgyy0vSrhEDG1ZfqKT3l7lS4Gq9nvBfYcSwbIrQYBdHT54TI 1HUtc3PS4ZTSocnRU55mFUzzwjaxiyd4E5Y/hwq8vlIuBpbsiYzPLNJkFcf8La+S9W96 AtPoIyU1Eq7B3Cuji9D46pBAnw0SIJchBBQuoDQ4LTafA0cBROy8+CSviuOd6NPntcna laBqdRUFV/OuJJdkN9E5OtT0yq8BpOhIx+meKKk1LYFNh24vZL8KhF0tFT2/4zkYDhAQ ERsMSJafEA/5vd63rgEY25f6P6rnLt9CEeRTw8QBTlw0qugNxhsh7MUnA64yqOa3Sh6c smPA== 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=0ZiYvgJmwEaAbc61G30SdrrBhl//EoKenKIaWmqCYPg=; b=Pnrq585Md7hYGaWyVC4DwYuYZLjF5/Gl7euEDo+u34/A+5qb3By88lE3G4P4KSk1pp 9wWb2nqo5S4O+L0InQONocgdgaBqqt7Tgsl/uLuSAFp1vTFmaygAux8fDkTFYSTdo9nE uhUYqcUUVag606TGjqdPj4OmsdfnQcRy+ra0p3LElfCY2dWUkPHZvm09cAls5LaW6o1N Vfrak26myjjy+3z/qu8cgcpOAIRsHZwik4ISj6kapDpvrV2z4ZIb6D84cwS9dnGiwF01 Qf4Pwx2NPnr7xWSrci7mudELRJYWxCjO86t8YNmIUqbcGptk9ILUsHnHfl5UpGmyUXUD yBIQ== X-Gm-Message-State: AOAM530+QeE0z2ACN6KOYEnV1rHXUW8vy6VYLCz0k6Ojg67wF1qFpFx6 VN7hR09s4CEmMzWZHU2nWNu+GmYJjrY= X-Google-Smtp-Source: ABdhPJx4zwhaRhg0Ft5pF5b63b7NUu2oAvY2/ZxsXNbKv/H+mfCzJ9gMFK4W1JhTXF4EWSMCurKYSA== X-Received: by 2002:a5d:5092:: with SMTP id a18mr26328164wrt.323.1602509801592; Mon, 12 Oct 2020 06:36:41 -0700 (PDT) Received: from localhost.localdomain ([84.40.93.41]) by smtp.gmail.com with ESMTPSA id k5sm23145388wmb.19.2020.10.12.06.36.40 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Oct 2020 06:36:41 -0700 (PDT) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v2 19/20] kernel-shark: Add methods for time calibration Date: Mon, 12 Oct 2020 16:35:22 +0300 Message-Id: <20201012133523.469040-20-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20201012133523.469040-1-y.karadz@gmail.com> References: <20201012133523.469040-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 d9d57843..31cb33d5 100644 --- a/src/libkshark-tepdata.c +++ b/src/libkshark-tepdata.c @@ -296,6 +296,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; @@ -316,6 +319,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. */ @@ -481,8 +490,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 6f99dd6c..33476ed0 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); @@ -862,6 +863,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) @@ -1259,6 +1291,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 fbe1b2ac..dc30665b 100644 --- a/src/libkshark.h +++ b/src/libkshark.h @@ -115,6 +115,12 @@ void kshark_hash_id_free(struct kshark_hash_id *hash); int *kshark_hash_ids(struct kshark_hash_id *hash); +/** + * 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 to initialize the interface of the data stream. */ @@ -315,6 +321,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; @@ -669,6 +684,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. */ @@ -1059,6 +1080,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 Oct 12 13:35:23 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 11832705 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id D3F83139F for ; Mon, 12 Oct 2020 13:37:47 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 7EF8822246 for ; Mon, 12 Oct 2020 13:37:47 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="AXZogNDf" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2388334AbgJLNhm (ORCPT ); Mon, 12 Oct 2020 09:37:42 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58790 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1730993AbgJLNhO (ORCPT ); Mon, 12 Oct 2020 09:37:14 -0400 Received: from mail-wr1-x442.google.com (mail-wr1-x442.google.com [IPv6:2a00:1450:4864:20::442]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 8A11DC0613DD for ; Mon, 12 Oct 2020 06:36:45 -0700 (PDT) Received: by mail-wr1-x442.google.com with SMTP id n15so19248768wrq.2 for ; Mon, 12 Oct 2020 06:36:45 -0700 (PDT) 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=jdb/VhyzyRx6iWReDW+upLSi83hm6pmUpZkAMAixMf8=; b=AXZogNDfZqd6RLpJ9fGFJvSXCp4g9eFRFHQUnfFbadXCczJVAslrFwdGwfo9HskLNc uQp9Pa0VsMv8LeurcXxBYTRQlkQJVhB6iW6cQ0w6M0GKw34i30vUNbn/rK/mcGZhM/P2 63p1qKft0X2jspyjGPc7/rPw8AWUFNE+lZ8HjVpEK3UcJMaVyjhmBxOIxc+xno6nFoh/ 700PUBTB8ic2diEhzI0uZvB2ydYA4hLwL1ACfhtefD+9pFjwl2XDU/Exy5l7CVBAawB+ g7zdJMAPZARKk7a9PW711W0eBb/DQJWOLGRBKi1f6gM+Qj4188OK+XylHFjR2UlHqYGB xSTg== 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=jdb/VhyzyRx6iWReDW+upLSi83hm6pmUpZkAMAixMf8=; b=hIfyC3zm3FwNNQebnRHmOntc0MCgncgGaEV3ChHhhn9kYTtFHd4WhSkxLXaEl22xDK UuHe0eKuL2ELaI7Vjy1VyLNWRKDzRkfzHzkM26Ik1GxiYZ4NbHk7absAJAA5Mm9bViGB rFQ1ulC9jp639Mh9S6EM3/5Pc9K4p/T+LGEVDjYPL8YfIzuJJSyaEFgA9cJaM5cZDocO PFBun0BQqVmS4PxmE2JBzpGI5+9uIV0Wd93nBZe7PpY44878eTMRkzj/lHfQDwozudD2 vAoqWVFdQWIM4l9hDC8H85k+mt5gNOV8UouwdDB9CR9bJosZHWThm/r7py8i6BVwM20K c3Fw== X-Gm-Message-State: AOAM531hOCRDdlUOvCnDtcDD0rZdQyEWLha4C/gK+7olETE3g4YzEdT+ tALxcol3gtW9vq2XXo9MkiY= X-Google-Smtp-Source: ABdhPJw4yVklUNYMA8rWR12qIxBC6A5bZwzFHe5DqO9Wlj5t2NzizP1rFH1TwSGSHolp4aTub9qdWQ== X-Received: by 2002:a5d:640d:: with SMTP id z13mr28896231wru.28.1602509803475; Mon, 12 Oct 2020 06:36:43 -0700 (PDT) Received: from localhost.localdomain ([84.40.93.41]) by smtp.gmail.com with ESMTPSA id k5sm23145388wmb.19.2020.10.12.06.36.42 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Oct 2020 06:36:42 -0700 (PDT) From: "Yordan Karadzhov (VMware)" To: rostedt@goodmis.org Cc: linux-trace-devel@vger.kernel.org, "Yordan Karadzhov (VMware)" Subject: [PATCH v2 20/20] kernel-shark: Integrate streams with libkshark-configio Date: Mon, 12 Oct 2020 16:35:23 +0300 Message-Id: <20201012133523.469040-21-y.karadz@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20201012133523.469040-1-y.karadz@gmail.com> References: <20201012133523.469040-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 | 1173 ++++++++++++++++++++++++++++++++------ src/libkshark.h | 73 ++- 5 files changed, 1060 insertions(+), 216 deletions(-) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 2f6acea2..b8bc79a4 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 faa116a1..9710d533 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 72eb8fd2..14a6ab69 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -6,7 +6,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 libkshark-tepdata.c) diff --git a/src/libkshark-configio.c b/src/libkshark-configio.c index cb7ca54b..0528e5ec 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) { @@ -331,7 +333,7 @@ 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); return false; } @@ -348,6 +350,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 +376,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 +447,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 +462,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 +495,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 +515,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 +525,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 +556,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,11 +565,13 @@ 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; } @@ -532,27 +585,378 @@ 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, "top") == 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; + + 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; - return file; + 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 +997,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 +1091,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; - jevent = jsystem = jname = NULL; + filter = kshark_get_filter(stream, filter_type); + if (!filter) + return false; + + jname = NULL; /* * If this Json document already contains a description of the filter, @@ -707,7 +1114,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 +1123,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 +1145,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 +1155,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 +1181,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 +1211,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 = stream->interface.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 +1227,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 +1263,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 +1277,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 +1321,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 +1337,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 +1363,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 +1387,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 +1403,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 +1418,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 +1426,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 +1458,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 +1469,22 @@ 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 (!*conf) *conf = kshark_filter_config_new(KS_CONFIG_JSON); @@ -1106,7 +1493,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 +1503,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 +1528,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 +1541,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 +1550,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 +1560,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 +1568,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 +1592,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 +1628,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 +1677,61 @@ 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", + conf->format); + return false; + } +} + +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", @@ -1306,11 +1740,79 @@ bool kshark_import_user_mask(struct kshark_context *kshark_ctx, } } +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 +1820,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 +1837,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 +1858,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 +1866,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 +1883,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 +1910,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 +1927,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 +1946,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 +1954,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 +1982,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 +1990,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 +2016,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 +2024,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 +2050,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 +2058,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 +2067,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 +2083,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 +2091,273 @@ 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_data_stream *stream = + kshark_get_data_stream(kshark_ctx, sd); + struct kshark_config_doc *file_conf, *filter_conf, *sd_conf, *plg_conf; + struct kshark_config_doc *dstream_conf; + + /* 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); + file_conf = kshark_export_trace_file(stream->file, stream->name, format); + plg_conf = kshark_export_stream_plugins(stream, format); + + if (!sd_conf || !dstream_conf || !filter_conf || !file_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 dc30665b..8800bf26 100644 --- a/src/libkshark.h +++ b/src/libkshark.h @@ -974,6 +974,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); @@ -985,9 +991,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); @@ -1003,24 +1015,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, @@ -1047,31 +1077,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);