linux-trace-devel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 00/27] Complete the KernelShark v2 transformation
@ 2021-02-11 10:31 Yordan Karadzhov (VMware)
  2021-02-11 10:31 ` [PATCH v2 01/27] kernel-shark: Add get_stream_object() Yordan Karadzhov (VMware)
                   ` (27 more replies)
  0 siblings, 28 replies; 29+ messages in thread
From: Yordan Karadzhov (VMware) @ 2021-02-11 10:31 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

With this patch-set we complete the destructive part of the transformation
of KernelShark towards v2 and the GUI is finally fully functional again.

v2 changes:
  * Fixing the bug in patch
    "kernel-shark: Update KsDualMarker and KsGLWidget",
    causing the error "Unable to find FreeSans font".

  * New patches:
    - kernel-shark: Search for font with Cmake at pre-build
    - kernel-shark: Add pkg-config configuration for libkshark
    - kernel-shark: Install libkshark-tepdata.h


Yordan Karadzhov (VMware) (27):
  kernel-shark: Add get_stream_object()
  kernel-shark: Do proper reset in kshark_close_all()
  kernel-shark: Restore the counting of event handlers
  kernel-shark: Fix a misleading comment
  kernel-shark: Count the number of readout interfaces
  kernel-shark: Update KsUtils
  kernel-shark: Update KsModels and KsSearchFSM
  kernel-shark: Add trace data files for CI testing
  kernel-shark: Add plugin tests
  kernel-shark: Add model tests
  kernel-shark: Update KsWidgetsLib
  kernel-shark: Add combo point to Mark
  kernel-shark: Add new methods to KsPlot::Mark
  kernel-shark: Update the plotting example
  kernel-shark: Search for font with Cmake at pre-build
  kernel-shark: Update KsDualMarker and KsGLWidget
  kernel-shark: Update KsTraceGraph and KsQuickContextMenu
  kernel-shark: Update KsTraceViewer
  kernel-shark: Update KsAdvFilteringDialog
  kernel-shark: Update KsCaptureDialog
  kernel-shark: Update KsSession
  kernel-shark: Update MissedEvents plugin
  kernel-shark: Update KsMainWindow and kernelshark.cpp
  kernel-shark: Clickable sched_event plugin shapes
  kernel-shark: Show Task plots from command lime
  kernel-shark: Add pkg-config configuration for libkshark
  kernel-shark: Install libkshark-tepdata.h

 .github/workflows/main.yml    |    9 +
 CMakeLists.txt                |   41 +-
 README                        |    1 +
 build/cmake_clean.sh          |    4 +-
 build/deff.h.cmake            |    9 +
 build/libkshark.pc.cmake      |   10 +
 examples/CMakeLists.txt       |   30 +-
 examples/dataplot.cpp         |   88 ++-
 examples/widgetdemo.cpp       |   65 +-
 src/CMakeLists.txt            |   15 +-
 src/KsAdvFilteringDialog.cpp  |  200 ++++--
 src/KsAdvFilteringDialog.hpp  |   16 +-
 src/KsCaptureDialog.cpp       |   90 +--
 src/KsCaptureDialog.hpp       |    4 +-
 src/KsDualMarker.cpp          |   23 +-
 src/KsDualMarker.hpp          |   16 +-
 src/KsGLWidget.cpp            |  696 +++++++++++++-------
 src/KsGLWidget.hpp            |  187 ++++--
 src/KsMainWindow.cpp          |  786 ++++++++++++++--------
 src/KsMainWindow.hpp          |  108 ++-
 src/KsModels.cpp              |  112 +++-
 src/KsModels.hpp              |   28 +-
 src/KsPlotTools.cpp           |   23 +-
 src/KsPlotTools.hpp           |   27 +-
 src/KsPlugins.hpp             |    5 +
 src/KsQuickContextMenu.cpp    |  151 +++--
 src/KsQuickContextMenu.hpp    |   26 +-
 src/KsSearchFSM.cpp           |   12 +-
 src/KsSession.cpp             |  379 +++++++----
 src/KsSession.hpp             |   33 +-
 src/KsTraceGraph.cpp          |  426 ++++++------
 src/KsTraceGraph.hpp          |   48 +-
 src/KsTraceViewer.cpp         |   57 +-
 src/KsTraceViewer.hpp         |   13 +-
 src/KsUtils.cpp               | 1157 +++++++++++++++++++++++----------
 src/KsUtils.hpp               |  183 ++++--
 src/KsWidgetsLib.cpp          |  695 +++++++++++++++++---
 src/KsWidgetsLib.hpp          |  331 +++++++++-
 src/kernelshark.cpp           |   64 +-
 src/libkshark-plugin.c        |   25 +-
 src/libkshark.c               |   28 +-
 src/plugins/CMakeLists.txt    |   36 +-
 src/plugins/MissedEvents.cpp  |  105 ++-
 src/plugins/SchedEvents.cpp   |   58 +-
 src/plugins/missed_events.c   |   25 +-
 src/plugins/missed_events.h   |    4 +-
 src/plugins/sched_events.c    |    7 +
 src/plugins/sched_events.h    |    3 +
 tests/CMakeLists.txt          |   42 +-
 tests/get_test_data.sh        |   21 +
 tests/libkshark-gui-tests.cpp |  313 +++++++++
 tests/libkshark-tests.cpp     |  317 ++++++++-
 tests/test-input.c            |  134 ++++
 tests/test-input_ctrl.c       |  140 ++++
 tests/test-plugin_dpi.c       |   26 +
 tests/test-plugin_dpi_ctrl.c  |   32 +
 tests/test-plugin_dpi_err.c   |   26 +
 57 files changed, 5608 insertions(+), 1902 deletions(-)
 create mode 100644 build/libkshark.pc.cmake
 create mode 100755 tests/get_test_data.sh
 create mode 100644 tests/libkshark-gui-tests.cpp
 create mode 100644 tests/test-input.c
 create mode 100644 tests/test-input_ctrl.c
 create mode 100644 tests/test-plugin_dpi.c
 create mode 100644 tests/test-plugin_dpi_ctrl.c
 create mode 100644 tests/test-plugin_dpi_err.c

-- 
2.25.1


^ permalink raw reply	[flat|nested] 29+ messages in thread

* [PATCH v2 01/27] kernel-shark: Add get_stream_object()
  2021-02-11 10:31 [PATCH v2 00/27] Complete the KernelShark v2 transformation Yordan Karadzhov (VMware)
@ 2021-02-11 10:31 ` Yordan Karadzhov (VMware)
  2021-02-11 10:31 ` [PATCH v2 02/27] kernel-shark: Do proper reset in kshark_close_all() Yordan Karadzhov (VMware)
                   ` (26 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Yordan Karadzhov (VMware) @ 2021-02-11 10:31 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

In a previous patch we modified kshark_get_data_stream() to return
NULL in the case when the kshark_data_stream object does not own
a data processing interface. This was motivated by the idea that
kshark_get_data_stream() is supposed to be used to access only
successfully loaded data streams, which is not possible in the case
when the data processing interface is not initialized. However, this
creates a contradiction with the definition of the public method
kshark_add_stream(). This method adds a stream without initializing
its data processing interface. In this patch we add a static function
that returns the Data stream object, regardless of the existence of
the interface and we use this static function in all cases when the
existence of the interface is not guaranteed.

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 src/libkshark.c | 15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/src/libkshark.c b/src/libkshark.c
index a54fdd5..c57ca01 100644
--- a/src/libkshark.c
+++ b/src/libkshark.c
@@ -361,6 +361,17 @@ kshark_get_data_stream(struct kshark_context *kshark_ctx, int sd)
 	return NULL;
 }
 
+static struct kshark_data_stream *
+get_stream_object(struct kshark_context *kshark_ctx, int sd)
+{
+	if (sd >= 0 && sd <= kshark_ctx->stream_info.max_stream_id)
+		if (kshark_ctx->stream[sd] &&
+		    kshark_is_valid_stream(kshark_ctx->stream[sd]))
+			return kshark_ctx->stream[sd];
+
+	return NULL;
+}
+
 /**
  * @brief Get the Data stream object corresponding to a given entry
  *
@@ -443,7 +454,7 @@ int kshark_close(struct kshark_context *kshark_ctx, int sd)
 	struct kshark_data_stream *stream;
 	int ret;
 
-	stream = kshark_get_data_stream(kshark_ctx, sd);
+	stream = get_stream_object(kshark_ctx, sd);
 	if (!stream)
 		return -EFAULT;
 
@@ -1182,7 +1193,7 @@ bool kshark_filter_is_set(struct kshark_context *kshark_ctx, int sd)
 {
 	struct kshark_data_stream *stream;
 
-	stream = kshark_get_data_stream(kshark_ctx, sd);
+	stream = get_stream_object(kshark_ctx, sd);
 	if (!stream)
 		return false;
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v2 02/27] kernel-shark: Do proper reset in kshark_close_all()
  2021-02-11 10:31 [PATCH v2 00/27] Complete the KernelShark v2 transformation Yordan Karadzhov (VMware)
  2021-02-11 10:31 ` [PATCH v2 01/27] kernel-shark: Add get_stream_object() Yordan Karadzhov (VMware)
@ 2021-02-11 10:31 ` Yordan Karadzhov (VMware)
  2021-02-11 10:31 ` [PATCH v2 03/27] kernel-shark: Restore the counting of event handlers Yordan Karadzhov (VMware)
                   ` (25 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Yordan Karadzhov (VMware) @ 2021-02-11 10:31 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

The function is supposed to close all Data streams and reset the
session context object (kshark_ctx). However, this is not happening
completely, because after closing the individual streams, the context
still contains the history of all closed stream Ids, recorded inside
the array of data stream descriptors. In this patch we add a proper
resetting of this array and its info descriptor.

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 src/libkshark.c | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/src/libkshark.c b/src/libkshark.c
index c57ca01..dc58dcf 100644
--- a/src/libkshark.c
+++ b/src/libkshark.c
@@ -37,6 +37,7 @@ static bool kshark_default_context(struct kshark_context **context)
 	kshark_ctx->stream = calloc(KS_DEFAULT_NUM_STREAMS,
 				    sizeof(*kshark_ctx->stream));
 	kshark_ctx->stream_info.array_size = KS_DEFAULT_NUM_STREAMS;
+	kshark_ctx->stream_info.next_free_stream_id = 0;
 	kshark_ctx->stream_info.max_stream_id = -1;
 
 	/* Will free kshark_context_handler. */
@@ -484,10 +485,22 @@ int kshark_close(struct kshark_context *kshark_ctx, int sd)
  */
 void kshark_close_all(struct kshark_context *kshark_ctx)
 {
+	size_t mem_reset_size;
 	int i;
 
+	if (kshark_ctx->stream_info.max_stream_id < 0)
+		return;
+
 	for (i = 0; i <= kshark_ctx->stream_info.max_stream_id; ++i)
 		kshark_close(kshark_ctx, i);
+
+	/* Reset the array of data stream descriptors. */
+	mem_reset_size = (kshark_ctx->stream_info.max_stream_id + 1 ) *
+			 sizeof(*kshark_ctx->stream);
+	memset(kshark_ctx->stream, 0, mem_reset_size);
+
+	kshark_ctx->stream_info.next_free_stream_id = 0;
+	kshark_ctx->stream_info.max_stream_id = -1;
 }
 
 /**
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v2 03/27] kernel-shark: Restore the counting of event handlers
  2021-02-11 10:31 [PATCH v2 00/27] Complete the KernelShark v2 transformation Yordan Karadzhov (VMware)
  2021-02-11 10:31 ` [PATCH v2 01/27] kernel-shark: Add get_stream_object() Yordan Karadzhov (VMware)
  2021-02-11 10:31 ` [PATCH v2 02/27] kernel-shark: Do proper reset in kshark_close_all() Yordan Karadzhov (VMware)
@ 2021-02-11 10:31 ` Yordan Karadzhov (VMware)
  2021-02-11 10:31 ` [PATCH v2 04/27] kernel-shark: Fix a misleading comment Yordan Karadzhov (VMware)
                   ` (24 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Yordan Karadzhov (VMware) @ 2021-02-11 10:31 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

The number of the event handlers, successfully registered by the
plugins is supposed to be counted and returned by the functions
kshark_handle_dpi() and kshark_handle_all_dpis(). Although this is
not used by the GUI, we want the API to support this feature. In
this patch we fix a regression that was introduced during the
transformation of the API for KernelShark v2.0.

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 src/libkshark-plugin.c | 20 ++++++++++++--------
 1 file changed, 12 insertions(+), 8 deletions(-)

diff --git a/src/libkshark-plugin.c b/src/libkshark-plugin.c
index 74a5862..b85d0e9 100644
--- a/src/libkshark-plugin.c
+++ b/src/libkshark-plugin.c
@@ -457,7 +457,6 @@ kshark_register_input(struct kshark_context *kshark_ctx,
 	input->interface = plugin;
 	input->next = kshark_ctx->inputs;
 	kshark_ctx->inputs = input;
-
 	return input;
 
  conflict:
@@ -635,7 +634,7 @@ void kshark_unregister_plugin_from_stream(struct kshark_data_stream *stream,
 	}
 }
 
-static void plugin_init(struct kshark_data_stream *stream,
+static int plugin_init(struct kshark_data_stream *stream,
 			struct kshark_dpi_list *plugin)
 {
 	int handler_count = plugin->interface->init(stream);
@@ -660,13 +659,18 @@ static void plugin_init(struct kshark_data_stream *stream,
 		plugin->status |= KSHARK_PLUGIN_FAILED;
 		plugin->status &= ~KSHARK_PLUGIN_LOADED;
 	}
+
+	return handler_count;
 }
 
-static void plugin_close(struct kshark_data_stream *stream,
+static int plugin_close(struct kshark_data_stream *stream,
 			 struct kshark_dpi_list *plugin)
 {
-	plugin->interface->close(stream);
+	int handler_count = plugin->interface->close(stream);
+
 	plugin->status &= ~KSHARK_PLUGIN_LOADED;
+
+	return handler_count;
 }
 
 /**
@@ -689,24 +693,24 @@ int kshark_handle_dpi(struct kshark_data_stream *stream,
 	switch (task_id) {
 	case KSHARK_PLUGIN_INIT:
 		if (plugin->status & KSHARK_PLUGIN_ENABLED)
-			plugin_init(stream, plugin);
+			handler_count += plugin_init(stream, plugin);
 
 		break;
 
 	case KSHARK_PLUGIN_UPDATE:
 		if (plugin->status & KSHARK_PLUGIN_LOADED)
-			plugin_close(stream, plugin);
+			handler_count -= plugin_close(stream, plugin);
 
 		plugin->status &= ~KSHARK_PLUGIN_FAILED;
 
 		if (plugin->status & KSHARK_PLUGIN_ENABLED)
-			plugin_init(stream, plugin);
+			handler_count += plugin_init(stream, plugin);
 
 		break;
 
 	case KSHARK_PLUGIN_CLOSE:
 		if (plugin->status & KSHARK_PLUGIN_LOADED)
-			plugin_close(stream, plugin);
+			handler_count -= plugin_close(stream, plugin);
 
 		plugin->status &= ~KSHARK_PLUGIN_FAILED;
 		break;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v2 04/27] kernel-shark: Fix a misleading comment
  2021-02-11 10:31 [PATCH v2 00/27] Complete the KernelShark v2 transformation Yordan Karadzhov (VMware)
                   ` (2 preceding siblings ...)
  2021-02-11 10:31 ` [PATCH v2 03/27] kernel-shark: Restore the counting of event handlers Yordan Karadzhov (VMware)
@ 2021-02-11 10:31 ` Yordan Karadzhov (VMware)
  2021-02-11 10:31 ` [PATCH v2 05/27] kernel-shark: Count the number of readout interfaces Yordan Karadzhov (VMware)
                   ` (23 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Yordan Karadzhov (VMware) @ 2021-02-11 10:31 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 src/libkshark-plugin.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/libkshark-plugin.c b/src/libkshark-plugin.c
index b85d0e9..bc68186 100644
--- a/src/libkshark-plugin.c
+++ b/src/libkshark-plugin.c
@@ -577,7 +577,8 @@ kshark_register_plugin_to_stream(struct kshark_data_stream *stream,
 			 * The plugin has been registered already. Check if it
 			 * is initialized and if this is the case, close the
 			 * existing initialization. This way we guarantee a
-			 * clean new initialization from.
+			 * clean new initialization from kshark_handle_dpi()
+			 * or kshark_handle_all_dpis().
 			 */
 			if (plugin_list->status & KSHARK_PLUGIN_LOADED)
 				kshark_handle_dpi(stream, plugin_list,
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v2 05/27] kernel-shark: Count the number of readout interfaces
  2021-02-11 10:31 [PATCH v2 00/27] Complete the KernelShark v2 transformation Yordan Karadzhov (VMware)
                   ` (3 preceding siblings ...)
  2021-02-11 10:31 ` [PATCH v2 04/27] kernel-shark: Fix a misleading comment Yordan Karadzhov (VMware)
@ 2021-02-11 10:31 ` Yordan Karadzhov (VMware)
  2021-02-11 10:31 ` [PATCH v2 06/27] kernel-shark: Update KsUtils Yordan Karadzhov (VMware)
                   ` (22 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Yordan Karadzhov (VMware) @ 2021-02-11 10:31 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

The number of readout interfaces, successfully registered by
the plugins is supposed to be counted and stored in the session
context object. Although this is not used by the GUI, we want
the API to support this feature.

Sgned-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 src/libkshark-plugin.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/libkshark-plugin.c b/src/libkshark-plugin.c
index bc68186..ebd2579 100644
--- a/src/libkshark-plugin.c
+++ b/src/libkshark-plugin.c
@@ -457,6 +457,7 @@ kshark_register_input(struct kshark_context *kshark_ctx,
 	input->interface = plugin;
 	input->next = kshark_ctx->inputs;
 	kshark_ctx->inputs = input;
+	kshark_ctx->n_inputs++;
 	return input;
 
  conflict:
@@ -489,6 +490,7 @@ void kshark_unregister_input(struct kshark_context *kshark_ctx,
 			*last = this_input->next;
 
 			free(this_input);
+			kshark_ctx->n_inputs--;
 
 			return;
 		}
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v2 06/27] kernel-shark: Update KsUtils
  2021-02-11 10:31 [PATCH v2 00/27] Complete the KernelShark v2 transformation Yordan Karadzhov (VMware)
                   ` (4 preceding siblings ...)
  2021-02-11 10:31 ` [PATCH v2 05/27] kernel-shark: Count the number of readout interfaces Yordan Karadzhov (VMware)
@ 2021-02-11 10:31 ` Yordan Karadzhov (VMware)
  2021-02-11 10:31 ` [PATCH v2 07/27] kernel-shark: Update KsModels and KsSearchFSM Yordan Karadzhov (VMware)
                   ` (21 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Yordan Karadzhov (VMware) @ 2021-02-11 10:31 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

The compilation of KsUtils is re-enabled and all functionalities
provided in it are made compatible with the new version of the
C API of libkshark (KernelShark 2.0).

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 CMakeLists.txt            |   10 +-
 build/deff.h.cmake        |    3 +
 src/CMakeLists.txt        |   73 +--
 src/KsUtils.cpp           | 1154 +++++++++++++++++++++++++------------
 src/KsUtils.hpp           |  181 ++++--
 tests/CMakeLists.txt      |    2 +-
 tests/libkshark-tests.cpp |    1 +
 7 files changed, 969 insertions(+), 455 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index dd62091..e013916 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -49,12 +49,18 @@ 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})")
 
+    if(Qt5Widgets_VERSION VERSION_LESS "5.11")
+
+        set(QT_VERSION_LESS_5_11 TRUE)
+
+    endif()
+
 endif (Qt5Widgets_FOUND)
 
 find_package (Boost COMPONENTS unit_test_framework)
diff --git a/build/deff.h.cmake b/build/deff.h.cmake
index 868ffec..5584574 100644
--- a/build/deff.h.cmake
+++ b/build/deff.h.cmake
@@ -26,6 +26,9 @@
 /** GLUT has been found. */
 #cmakedefine GLUT_FOUND
 
+/** Qt - old version detected. */
+#cmakedefine QT_VERSION_LESS_5_11
+
 /** Semicolon-separated list of plugin names. */
 #define KS_BUILTIN_PLUGINS "@PLUGINS@"
 
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 980e802..5c9fe17 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -66,34 +66,34 @@ endif (OPENGL_FOUND)
 if (Qt5Widgets_FOUND AND Qt5Network_FOUND)
 
     message(STATUS "libkshark-gui")
-    set (ks-guiLib_hdr  KsUtils.hpp
-                        KsModels.hpp
-                        KsGLWidget.hpp
-                        KsSearchFSM.hpp
-                        KsDualMarker.hpp
-                        KsWidgetsLib.hpp
-                        KsTraceGraph.hpp
-                        KsTraceViewer.hpp
-                        KsMainWindow.hpp
-                        KsCaptureDialog.hpp
-                        KsQuickContextMenu.hpp
-                        KsAdvFilteringDialog.hpp)
+    set (ks-guiLib_hdr  KsUtils.hpp)
+#                         KsModels.hpp
+#                         KsGLWidget.hpp
+#                         KsSearchFSM.hpp
+#                         KsDualMarker.hpp
+#                         KsWidgetsLib.hpp
+#                         KsTraceGraph.hpp
+#                         KsTraceViewer.hpp
+#                         KsMainWindow.hpp
+#                         KsCaptureDialog.hpp
+#                         KsQuickContextMenu.hpp
+#                         KsAdvFilteringDialog.hpp)
 
     QT5_WRAP_CPP(ks-guiLib_hdr_moc ${ks-guiLib_hdr})
 
-    add_library(kshark-gui  SHARED  ${ks-guiLib_hdr_moc}    KsUtils.cpp
-                                                            KsModels.cpp
-                                                            KsSession.cpp
-                                                            KsGLWidget.cpp
-                                                            KsSearchFSM.cpp
-                                                            KsDualMarker.cpp
-                                                            KsWidgetsLib.cpp
-                                                            KsTraceGraph.cpp
-                                                            KsTraceViewer.cpp
-                                                            KsMainWindow.cpp
-                                                            KsCaptureDialog.cpp
-                                                            KsQuickContextMenu.cpp
-                                                            KsAdvFilteringDialog.cpp)
+    add_library(kshark-gui  SHARED  ${ks-guiLib_hdr_moc}    KsUtils.cpp)
+#                                                             KsModels.cpp
+#                                                             KsSession.cpp
+#                                                             KsGLWidget.cpp
+#                                                             KsSearchFSM.cpp
+#                                                             KsDualMarker.cpp
+#                                                             KsWidgetsLib.cpp
+#                                                             KsTraceGraph.cpp
+#                                                             KsTraceViewer.cpp
+#                                                             KsMainWindow.cpp
+#                                                             KsCaptureDialog.cpp
+#                                                             KsQuickContextMenu.cpp
+#                                                             KsAdvFilteringDialog.cpp)
 
     target_link_libraries(kshark-gui kshark-plot
                                      Qt5::Widgets
@@ -102,19 +102,20 @@ if (Qt5Widgets_FOUND AND Qt5Network_FOUND)
 
     set_target_properties(kshark-gui PROPERTIES  SUFFIX ".so.${KS_VERSION_STRING}")
 
-    message(STATUS ${KS_APP_NAME})
-    add_executable(${KS_APP_NAME}          kernelshark.cpp)
-    target_link_libraries(${KS_APP_NAME}   kshark-gui)
+#     message(STATUS ${KS_APP_NAME})
+#     add_executable(${KS_APP_NAME}          kernelshark.cpp)
+#     target_link_libraries(${KS_APP_NAME}   kshark-gui)
 
-    message(STATUS "kshark-record")
-    add_executable(kshark-record        kshark-record.cpp)
-    target_link_libraries(kshark-record kshark-gui)
+#     message(STATUS "kshark-record")
+#     add_executable(kshark-record        kshark-record.cpp)
+#     target_link_libraries(kshark-record kshark-gui)
+
+#     install(TARGETS ${KS_APP_NAME} kshark-record kshark-gui
+#             RUNTIME DESTINATION       ${_INSTALL_PREFIX}/bin/
+#                 COMPONENT                 kernelshark
+#             LIBRARY DESTINATION       ${_LIBDIR}
+#                 COMPONENT                 kernelshark)
 
-    install(TARGETS ${KS_APP_NAME} kshark-record kshark-gui
-            RUNTIME DESTINATION       ${_INSTALL_PREFIX}/bin/
-                COMPONENT                 kernelshark
-            LIBRARY DESTINATION       ${_LIBDIR}
-                COMPONENT                 kernelshark)
 
     install(FILES "${KS_DIR}/${KS_APP_NAME}.desktop"
             DESTINATION ${_INSTALL_PREFIX}/share/applications/
diff --git a/src/KsUtils.cpp b/src/KsUtils.cpp
index 24f7178..36f9b25 100644
--- a/src/KsUtils.cpp
+++ b/src/KsUtils.cpp
@@ -10,90 +10,244 @@
  */
 
 // KernelShark
+#include "libkshark-plugin.h"
+#include "libkshark-tepdata.h"
 #include "KsUtils.hpp"
-#include "KsWidgetsLib.hpp"
 
 namespace KsUtils {
 
-/** @brief Get a sorted vector of CPU Ids. */
-QVector<int> getCPUList()
+/**
+ * @brief Get a sorted vector of CPU Ids associated with a given Data stream.
+ *
+ * @param sd: Data stream identifier.
+ *
+ * @returns Vector of CPU Ids on success or an empty vector on failure.
+ */
+QVector<int> getCPUList(int sd)
 {
 	kshark_context *kshark_ctx(nullptr);
-	int nCPUs;
+	kshark_data_stream *stream;
 
 	if (!kshark_instance(&kshark_ctx))
 		return {};
 
-	nCPUs = tep_get_cpus(kshark_ctx->pevent);
-	QVector<int> allCPUs = QVector<int>(nCPUs);
+	stream = kshark_get_data_stream(kshark_ctx, sd);
+	if (!stream)
+		return {};
+
+	QVector<int> allCPUs = QVector<int>(stream->n_cpus);
 	std::iota(allCPUs.begin(), allCPUs.end(), 0);
 
 	return allCPUs;
 }
 
-/** @brief Get a sorted vector of Task's Pids. */
-QVector<int> getPidList()
+/**
+ * @brief Get a sorteg vector of Task's PIDs associated with a given Data
+ *	  stream.
+ *
+ * @param sd: Data stream identifier.
+ *
+ * @returns Vector of PIDs on success or an empty vector on failure.
+ */
+QVector<int> getPidList(int sd)
 {
 	kshark_context *kshark_ctx(nullptr);
-	int nTasks, *tempPids;
-	QVector<int> pids;
+	int nTasks, *ids;
 
 	if (!kshark_instance(&kshark_ctx))
-		return pids;
+		return {};
 
-	nTasks = kshark_get_task_pids(kshark_ctx, &tempPids);
-	for (int r = 0; r < nTasks; ++r) {
-		pids.append(tempPids[r]);
-	}
+	nTasks = kshark_get_task_pids(kshark_ctx, sd, &ids);
 
-	free(tempPids);
+	QVector<int> pids(nTasks);
+	for (int i = 0; i < nTasks; ++i)
+		pids[i] = ids[i];
 
-	std::sort(pids.begin(), pids.end());
+	free(ids);
 
 	return pids;
 }
 
 /**
- * @brief Get a sorted vector of Event Ids.
+ * @brief Get a vector of all Event Ids associated with a given Data stream.
+ *
+ * @param sd: Data stream identifier.
+ *
+ * @returns Vector of Event Ids on success or an empty vector on failure.
  */
-QVector<int> getEventIdList(tep_event_sort_type sortType)
+QVector<int> getEventIdList(int sd)
 {
 	kshark_context *kshark_ctx(nullptr);
-	tep_event **events;
-	int nEvts;
+	kshark_data_stream *stream;
+	int *ids;
 
 	if (!kshark_instance(&kshark_ctx))
 		return {};
 
-	nEvts = tep_get_events_count(kshark_ctx->pevent);
-	events = tep_list_events(kshark_ctx->pevent, sortType);
+	stream = kshark_get_data_stream(kshark_ctx, sd);
+	if (!stream)
+		return {};
+
+	ids = kshark_get_all_event_ids(stream);
+	if (!ids)
+		return {};
 
-	QVector<int> allEvts(nEvts);
-	for (int i = 0; i < nEvts; ++i)
-		allEvts[i] = events[i]->id;
+	QVector<int> evts(stream->n_events);
+	for (int i = 0; i < stream->n_events; ++i)
+		evts[i] = ids[i];
 
-	return allEvts;
+	free(ids);
+
+	return evts;
 }
 
-/** @brief Get a sorted vector of Id values of a filter. */
-QVector<int> getFilterIds(tracecmd_filter_id *filter)
+/**
+ * @brief Retrieve the unique Id of the event.
+ *
+ * @param sd: Data stream identifier.
+ * @param eventName: The name of the event.
+ *
+ * @returns Event Id on success or a negative errno code on failure.
+ */
+int getEventId(int sd, const QString &eventName)
 {
+	const std::string buff = eventName.toStdString();
 	kshark_context *kshark_ctx(nullptr);
-	int *cpuFilter, n;
-	QVector<int> v;
+	kshark_data_stream *stream;
 
 	if (!kshark_instance(&kshark_ctx))
-		return v;
+		return -EFAULT;
+
+	stream = kshark_get_data_stream(kshark_ctx, sd);
+	if (!stream)
+		return -ENODEV;
+
+	return kshark_find_event_id(stream, buff.c_str());
+}
+
+static kshark_entry probeEntry(int sd, int eventId)
+{
+	kshark_entry e;
 
-	cpuFilter = tracecmd_filter_ids(filter);
-	n = filter->count;
+	e.stream_id = sd;
+	e.event_id = eventId;
+	e.visible = 0xff;
+
+	return e;
+}
+
+/**
+ * @brief Retrieve the name of the event.
+ *
+ * @param sd: Data stream identifier.
+ * @param eventId: The unique Id of the event.
+ *
+ * @returns Event name on success or "Unknown" on failure.
+ */
+QString getEventName(int sd, int eventId)
+{
+	kshark_entry entry = probeEntry(sd, eventId);
+	QString ret("Unknown");
+	char *event;
+
+	event = kshark_get_event_name(&entry);
+	if (event) {
+		 ret = QString(event);
+		 free(event);
+	}
+
+	return QString(ret);
+}
+
+/**
+ * @brief Get the namse of all data fields associated with a given trace event.
+ *
+ * @param sd: Data stream identifier.
+ * @param eventId: The unique Id of the event.
+ *
+ * @returns List of fieldsnames on success or an empty list on failure.
+ */
+QStringList getEventFieldsList(int sd, int eventId)
+{
+	kshark_entry entry = probeEntry(sd, eventId);
+	QStringList fieldList;
+	char **eventFields;
+	int nFields;
+
+	nFields = kshark_get_all_event_field_names(&entry, &eventFields);
+	if (nFields <= 0)
+		return {};
+
+	for (int i = 0; i < nFields; ++i) {
+		fieldList << eventFields[i];
+		free(eventFields[i]);
+	}
+
+	free(eventFields);
+
+	return fieldList;
+}
+
+/**
+ * @brief Retrieve the type of a given data field associatedwith a given trace
+ *	  event.
+ *
+ * @param sd: Data stream identifier.
+ * @param eventId: The unique Id of the event.
+ * @param fieldName: The name of the data field.
+ *
+ * @returns Field format identifier.
+ */
+kshark_event_field_format getEventFieldType(int sd, int eventId,
+					    const QString &fieldName)
+{
+	const std::string buff = fieldName.toStdString();
+	kshark_entry entry = probeEntry(sd, eventId);
+
+	return kshark_get_event_field_type(&entry, buff.c_str());
+}
+
+/**
+ * @brief Get all Data stream Ids.
+ *
+ * @param kshark_ctx: Input location for context pointer.
+ *
+ * @returns Vector of Data stream Ids.
+ */
+QVector<int> getStreamIdList(kshark_context *kshark_ctx)
+{
+	int *ids = kshark_all_streams(kshark_ctx);
+	QVector<int> streamIds(kshark_ctx->n_streams);
+
+	for (int i = 0; i < kshark_ctx->n_streams; ++i)
+		streamIds[i] = ids[i];
+
+	free(ids);
+
+	return streamIds;
+}
+
+/**
+ * @brief Get a sorted vector of Id values of a filter.
+ *
+ * @param filter: Input location for the filter object.
+ */
+QVector<int> getFilterIds(kshark_hash_id *filter)
+{
+	kshark_context *kshark_ctx(nullptr);
+	int *ids, n = filter->count;
+
+	if (!kshark_instance(&kshark_ctx))
+		return {};
+
+	ids = kshark_hash_ids(filter);
+	QVector<int> filterIds(n);
 	for (int i = 0; i < n; ++i)
-		v.append(cpuFilter[i]);
+		filterIds[i] = ids[i];
 
-	std::sort(v.begin(), v.end());
+	free(ids);
 
-	free(cpuFilter);
-	return v;
+	return filterIds;
 }
 
 /**
@@ -134,7 +288,6 @@ void graphFilterSync(bool state)
 	}
 }
 
-
 /**
  * @brief Add a checkbox to a menu.
  *
@@ -164,15 +317,46 @@ QCheckBox *addCheckBoxToMenu(QMenu *menu, QString name)
  *
  * @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" and
  * 	    the entry is visibility in Graph. Otherwise false.
  */
 bool matchCPUVisible(struct kshark_context *kshark_ctx,
-		     struct kshark_entry *e, int cpu)
+		     struct kshark_entry *e, int sd, int *cpu)
 {
-	return (e->cpu == cpu && (e->visible & KS_GRAPH_VIEW_FILTER_MASK));
+	return (e->cpu == *cpu &&
+		e->stream_id == sd &&
+		(e->visible & KS_GRAPH_VIEW_FILTER_MASK));
+}
+
+/**
+ * @brief Get an elided version of the string that will fit within a label.
+ *
+ * @param label: Pointer to the label object.
+ * @param text: The text to be elided.
+ * @param mode: Parameter specifies whether the text is elided on the left,
+ *		in the middle, or on the right.
+ * @param labelWidth: The desired width of the label.
+ */
+void setElidedText(QLabel* label, QString text,
+		   enum Qt::TextElideMode mode,
+		   int labelWidth)
+{
+	QFontMetrics metrix(label->font());
+	QString elidedText;
+	int textWidth;
+
+	textWidth = labelWidth - FONT_WIDTH * 3;
+	elidedText = metrix.elidedText(text, Qt::ElideRight, textWidth);
+
+	while(labelWidth < STRING_WIDTH(elidedText) + FONT_WIDTH * 5) {
+		textWidth -= FONT_WIDTH * 3;
+		elidedText = metrix.elidedText(text, mode, textWidth);
+	}
+
+	label->setText(elidedText);
 }
 
 /**
@@ -268,36 +452,8 @@ QStringList getFiles(QWidget *parent,
 }
 
 /**
- * @brief Open a standard Qt getFileName dialog and return the name of the
- *	  selected file. Only one file can be selected.
- */
-QString getSaveFile(QWidget *parent,
-		    const QString &windowName,
-		    const QString &filter,
-		    const QString &extension,
-		    QString &lastFilePath)
-{
-	QString fileName = getFileDialog(parent,
-					 windowName,
-					 filter,
-					 lastFilePath,
-					 true);
-
-	if (!fileName.isEmpty() && !fileName.endsWith(extension)) {
-		fileName += extension;
-
-		if (QFileInfo(fileName).exists()) {
-			if (!KsWidgetsLib::fileExistsDialog(fileName))
-				fileName.clear();
-		}
-	}
-
-	return fileName;
-}
-
-/**
- * Separate the command line arguments inside the string taking into account
- * possible shell quoting and new lines.
+ * @brief Separate the command line arguments inside the string taking into
+ *	  account possible shell quoting and new lines.
  */
 QStringList splitArguments(QString cmd)
 {
@@ -336,7 +492,10 @@ QStringList splitArguments(QString cmd)
 	return argv;
 }
 
-/** Parse a string containing Ids. The string can be of the form "1 4-7 9". */
+/**
+ * @brief Parse a string containing Ids. The string can be of the form
+ *	  "1,4-7,9".
+ */
 QVector<int> parseIdList(QString v_str)
 {
 	QStringList list = v_str.split(",", QString::SkipEmptyParts);
@@ -360,6 +519,63 @@ QVector<int> parseIdList(QString v_str)
 	return v;
 }
 
+/**
+ * @brief Split the ststem name from the actual name of the event itself.
+ *
+ * @param sd: Data stream identifier.
+ * @param eventId: Identifier of the Event.
+ */
+QStringList getTepEvtName(int sd, int eventId)
+{
+	QString name(kshark_event_from_id(sd, eventId));
+
+	return name.split('/');
+}
+
+/**
+ * @brief Get a string to be used as a standard name of a task graph.
+ *
+ * @param sd: Graph's Data stream identifier.
+ * @param pid: Graph's progress Id.
+ */
+QString taskPlotName(int sd, int pid)
+{
+	kshark_context *kshark_ctx(nullptr);
+	kshark_data_stream *stream;
+	QString name;
+
+	if (!kshark_instance(&kshark_ctx))
+		return {};
+
+	stream = kshark_get_data_stream(kshark_ctx, sd);
+	if (!stream)
+		return {};
+
+	name = kshark_comm_from_pid(sd, pid);
+	name += "-";
+	name += QString("%1").arg(pid);
+
+	return name;
+}
+
+/**
+ * @brief Get a description of the stream showing its data file and buffer
+ *	  name.
+ *
+ * @param stream: Input location for a Trace data stream pointer.
+ */
+QString streamDescription(kshark_data_stream *stream)
+{
+	QString descr(stream->file);
+	QString buffName(stream->name);
+	if (!buffName.isEmpty() && !kshark_tep_is_top_stream(stream)) {
+		descr += ":";
+		descr += stream->name;
+	}
+
+	return descr;
+}
+
 }; // KsUtils
 
 /** A stream operator for converting QColor into KsPlot::Color. */
@@ -370,10 +586,17 @@ KsPlot::Color& operator <<(KsPlot::Color &thisColor, const QColor &c)
 	return thisColor;
 }
 
+/** A stream operator for converting KsPlot::Color into QColor. */
+QColor& operator <<(QColor &thisColor, const KsPlot::Color &c)
+{
+	thisColor.setRgb(c.r(), c.g(), c.b());
+
+	return thisColor;
+}
+
 /** Create a default (empty) KsDataStore. */
 KsDataStore::KsDataStore(QWidget *parent)
 : QObject(parent),
-  _tep(nullptr),
   _rows(nullptr),
   _dataSize(0)
 {}
@@ -382,29 +605,127 @@ KsDataStore::KsDataStore(QWidget *parent)
 KsDataStore::~KsDataStore()
 {}
 
+int KsDataStore::_openDataFile(kshark_context *kshark_ctx,
+				const QString &file)
+{
+	int sd = kshark_open(kshark_ctx, file.toStdString().c_str());
+	if (sd < 0) {
+		qCritical() << "ERROR:" << sd << "while opening file " << file;
+		return sd;
+	}
+
+	if (kshark_is_tep(kshark_ctx->stream[sd])) {
+		kshark_tep_init_all_buffers(kshark_ctx, sd);
+		for (int i = 0; i < kshark_ctx->n_streams; ++i)
+			kshark_tep_handle_plugins(kshark_ctx, i);
+	}
+
+	return sd;
+}
+
+void KsDataStore::_addPluginsToStream(kshark_context *kshark_ctx, int sd,
+				      QVector<kshark_dpi *> plugins)
+{
+	kshark_data_stream *stream;
+
+	stream = kshark_get_data_stream(kshark_ctx, sd);
+	if (!stream)
+		return;
+
+	for (auto const &p: plugins) {
+		struct kshark_dpi_list *plugin;
+
+		plugin = kshark_register_plugin_to_stream(stream, p, true);
+		kshark_handle_dpi(stream, plugin, KSHARK_PLUGIN_INIT);
+	}
+}
+
 /** Load trace data for file. */
-void KsDataStore::loadDataFile(const QString &file)
+int KsDataStore::loadDataFile(const QString &file,
+			       QVector<kshark_dpi *> plugins)
 {
 	kshark_context *kshark_ctx(nullptr);
+	int i, sd, n_streams;
 
 	if (!kshark_instance(&kshark_ctx))
-		return;
+		return -EFAULT;
 
 	clear();
 
-	if (!kshark_open(kshark_ctx, file.toStdString().c_str())) {
-		qCritical() << "ERROR Loading file " << file;
-		return;
+	sd = _openDataFile(kshark_ctx, file);
+	if (sd != 0)
+		return sd;
+
+	/*
+	 * The file may contain multiple buffers so we can have multiple
+	 * streams loaded.
+	 */
+	n_streams = kshark_ctx->n_streams;
+	for (i = 0; i < n_streams; ++i)
+		_addPluginsToStream(kshark_ctx, i, plugins);
+
+	_dataSize = kshark_load_all_entries(kshark_ctx, &_rows);
+	if (_dataSize <= 0) {
+		kshark_close_all(kshark_ctx);
+		return _dataSize;
 	}
 
-	_tep = kshark_ctx->pevent;
+	registerCPUCollections();
 
-	if (kshark_ctx->event_handlers == nullptr)
-		kshark_handle_plugins(kshark_ctx, KSHARK_PLUGIN_INIT);
-	else
-		kshark_handle_plugins(kshark_ctx, KSHARK_PLUGIN_UPDATE);
+	return sd;
+}
+
+/**
+ * @brief Append a trace data file to the data-set that is already loaded.
+ *	  The clock of the new data will be calibrated in order to be
+ *	  compatible with the clock of the prior data.
+ *
+ * @param file: Trace data file, to be append to the already loaded data.
+ * @param offset: The offset in time of the Data stream to be appended.
+ */
+int KsDataStore::appendDataFile(const QString &file, int64_t offset)
+{
+	kshark_context *kshark_ctx(nullptr);
+	struct kshark_entry **mergedRows;
+	ssize_t nLoaded = _dataSize;
+	int i, sd;
+
+	if (!kshark_instance(&kshark_ctx))
+		return -EFAULT;
+
+	unregisterCPUCollections();
+
+	sd = _openDataFile(kshark_ctx, file);
+	if (sd < 0)
+		return sd;
+
+	for (i = sd; i < kshark_ctx->n_streams; ++i) {
+		kshark_ctx->stream[sd]->calib = kshark_offset_calib;
+		kshark_ctx->stream[sd]->calib_array =
+			(int64_t *) calloc(1, sizeof(int64_t));
+		kshark_ctx->stream[sd]->calib_array[0] = offset;
+		kshark_ctx->stream[sd]->calib_array_size = 1;
+	}
+
+	_dataSize = kshark_append_all_entries(kshark_ctx, _rows, nLoaded, sd,
+					      &mergedRows);
+
+	if (_dataSize <= 0 || _dataSize == nLoaded) {
+		QErrorMessage *em = new QErrorMessage();
+		em->showMessage(QString("File %1 contains no data.").arg(file));
+		em->exec();
+
+		for (i = sd; i < kshark_ctx->n_streams; ++i)
+			kshark_close(kshark_ctx, i);
+
+		return _dataSize;
+	}
+
+	_rows = mergedRows;
+
+	registerCPUCollections();
 
-	_dataSize = kshark_load_data_entries(kshark_ctx, &_rows);
+	return sd;
 }
 
 void KsDataStore::_freeData()
@@ -430,8 +751,14 @@ void KsDataStore::reload()
 
 	_freeData();
 
-	_dataSize = kshark_load_data_entries(kshark_ctx, &_rows);
-	_tep = kshark_ctx->pevent;
+	if (kshark_ctx->n_streams == 0)
+		return;
+
+	unregisterCPUCollections();
+
+	_dataSize = kshark_load_all_entries(kshark_ctx, &_rows);
+
+	registerCPUCollections();
 
 	emit updateWidgets(this);
 }
@@ -441,11 +768,12 @@ void KsDataStore::clear()
 {
 	kshark_context *kshark_ctx(nullptr);
 
-	_freeData();
-	_tep = nullptr;
+	if (!kshark_instance(&kshark_ctx))
+		return;
 
-	if (kshark_instance(&kshark_ctx) && kshark_ctx->handle)
-		kshark_close(kshark_ctx);
+	_freeData();
+	unregisterCPUCollections();
+	kshark_close_all(kshark_ctx);
 }
 
 /** Update the visibility of the entries (filter). */
@@ -456,93 +784,113 @@ void KsDataStore::update()
 	if (!kshark_instance(&kshark_ctx))
 		return;
 
-	_unregisterCPUCollections();
+	unregisterCPUCollections();
 
-	if (kshark_filter_is_set(kshark_ctx)) {
-		kshark_filter_entries(kshark_ctx, _rows, _dataSize);
-		emit updateWidgets(this);
-	}
+	kshark_filter_all_entries(kshark_ctx, _rows, _dataSize);
 
 	registerCPUCollections();
+
+	emit updateWidgets(this);
 }
 
 /** Register a collection of visible entries for each CPU. */
 void KsDataStore::registerCPUCollections()
 {
 	kshark_context *kshark_ctx(nullptr);
+	int *streamIds, nCPUs, sd;
 
-	if (!kshark_instance(&kshark_ctx) ||
-	    !kshark_filter_is_set(kshark_ctx))
+	if (!kshark_instance(&kshark_ctx))
 		return;
 
-	int nCPUs = tep_get_cpus(_tep);
-	for (int cpu = 0; cpu < nCPUs; ++cpu) {
-		kshark_register_data_collection(kshark_ctx,
-						_rows, _dataSize,
-						KsUtils::matchCPUVisible,
-						cpu,
-						0);
+	streamIds = kshark_all_streams(kshark_ctx);
+	for (int i = 0; i < kshark_ctx->n_streams; ++i) {
+		sd = streamIds[i];
+
+		nCPUs = kshark_ctx->stream[sd]->n_cpus;
+		for (int cpu = 0; cpu < nCPUs; ++cpu) {
+			kshark_register_data_collection(kshark_ctx,
+							_rows, _dataSize,
+							KsUtils::matchCPUVisible,
+							sd, &cpu, 1,
+							0);
+		}
 	}
+
+	free(streamIds);
 }
 
-void KsDataStore::_unregisterCPUCollections()
+/** Unregister all CPU collections. */
+void KsDataStore::unregisterCPUCollections()
 {
 	kshark_context *kshark_ctx(nullptr);
+	int *streamIds, nCPUs, sd;
 
 	if (!kshark_instance(&kshark_ctx))
 		return;
 
-	int nCPUs = tep_get_cpus(_tep);
-	for (int cpu = 0; cpu < nCPUs; ++cpu) {
-		kshark_unregister_data_collection(&kshark_ctx->collections,
-						  KsUtils::matchCPUVisible,
-						  cpu);
+	streamIds = kshark_all_streams(kshark_ctx);
+	for (int i = 0; i < kshark_ctx->n_streams; ++i) {
+		sd = streamIds[i];
+
+		nCPUs = kshark_ctx->stream[sd]->n_cpus;
+		for (int cpu = 0; cpu < nCPUs; ++cpu) {
+			kshark_unregister_data_collection(&kshark_ctx->collections,
+							  KsUtils::matchCPUVisible,
+							  sd, &cpu, 1);
+		}
 	}
+
+	free(streamIds);
 }
 
-void KsDataStore::_applyIdFilter(int filterId, QVector<int> vec)
+void KsDataStore::_applyIdFilter(int filterId, QVector<int> vec, int sd)
 {
 	kshark_context *kshark_ctx(nullptr);
+	kshark_data_stream *stream;
 
 	if (!kshark_instance(&kshark_ctx))
 		return;
 
+	stream = kshark_get_data_stream(kshark_ctx, sd);
+	if (!stream)
+		return;
+
 	switch (filterId) {
 		case KS_SHOW_EVENT_FILTER:
 		case KS_HIDE_EVENT_FILTER:
-			kshark_filter_clear(kshark_ctx, KS_SHOW_EVENT_FILTER);
-			kshark_filter_clear(kshark_ctx, KS_HIDE_EVENT_FILTER);
+			kshark_filter_clear(kshark_ctx, sd, KS_SHOW_EVENT_FILTER);
+			kshark_filter_clear(kshark_ctx, sd, KS_HIDE_EVENT_FILTER);
 			break;
 		case KS_SHOW_TASK_FILTER:
 		case KS_HIDE_TASK_FILTER:
-			kshark_filter_clear(kshark_ctx, KS_SHOW_TASK_FILTER);
-			kshark_filter_clear(kshark_ctx, KS_HIDE_TASK_FILTER);
+			kshark_filter_clear(kshark_ctx, sd, KS_SHOW_TASK_FILTER);
+			kshark_filter_clear(kshark_ctx, sd, KS_HIDE_TASK_FILTER);
 			break;
 		case KS_SHOW_CPU_FILTER:
 		case KS_HIDE_CPU_FILTER:
-			kshark_filter_clear(kshark_ctx, KS_SHOW_CPU_FILTER);
-			kshark_filter_clear(kshark_ctx, KS_HIDE_CPU_FILTER);
+			kshark_filter_clear(kshark_ctx, sd, KS_SHOW_CPU_FILTER);
+			kshark_filter_clear(kshark_ctx, sd, KS_HIDE_CPU_FILTER);
 			break;
 		default:
 			return;
 	}
 
 	for (auto &&val: vec)
-		kshark_filter_add_id(kshark_ctx, filterId, val);
+		kshark_filter_add_id(kshark_ctx, sd, filterId, val);
 
-	if (!_tep)
+	if (!kshark_ctx->n_streams)
 		return;
 
-	_unregisterCPUCollections();
+	unregisterCPUCollections();
 
 	/*
-	 * If the advanced event filter is set, the data has to be reloaded,
+	 * If the advanced event filter is set the data has to be reloaded,
 	 * because the advanced filter uses tep_records.
 	 */
-	if (kshark_ctx->advanced_event_filter->filters)
+	if (kshark_is_tep(stream) && kshark_tep_filter_is_set(stream))
 		reload();
 	else
-		kshark_filter_entries(kshark_ctx, _rows, _dataSize);
+		kshark_filter_stream_entries(kshark_ctx, sd, _rows, _dataSize);
 
 	registerCPUCollections();
 
@@ -550,381 +898,475 @@ void KsDataStore::_applyIdFilter(int filterId, QVector<int> vec)
 }
 
 /** Apply Show Task filter. */
-void KsDataStore::applyPosTaskFilter(QVector<int> vec)
+void KsDataStore::applyPosTaskFilter(int sd, QVector<int> vec)
 {
-	_applyIdFilter(KS_SHOW_TASK_FILTER, vec);
+	_applyIdFilter(KS_SHOW_TASK_FILTER, vec, sd);
 }
 
 /** Apply Hide Task filter. */
-void KsDataStore::applyNegTaskFilter(QVector<int> vec)
+void KsDataStore::applyNegTaskFilter(int sd, QVector<int> vec)
 {
-	_applyIdFilter(KS_HIDE_TASK_FILTER, vec);
+	_applyIdFilter(KS_HIDE_TASK_FILTER, vec, sd);
 }
 
 /** Apply Show Event filter. */
-void KsDataStore::applyPosEventFilter(QVector<int> vec)
+void KsDataStore::applyPosEventFilter(int sd, QVector<int> vec)
 {
-	_applyIdFilter(KS_SHOW_EVENT_FILTER, vec);
+	_applyIdFilter(KS_SHOW_EVENT_FILTER, vec, sd);
 }
 
 /** Apply Hide Event filter. */
-void KsDataStore::applyNegEventFilter(QVector<int> vec)
+void KsDataStore::applyNegEventFilter(int sd, QVector<int> vec)
 {
-	_applyIdFilter(KS_HIDE_EVENT_FILTER, vec);
+	_applyIdFilter(KS_HIDE_EVENT_FILTER, vec, sd);
 }
 
 /** Apply Show CPU filter. */
-void KsDataStore::applyPosCPUFilter(QVector<int> vec)
+void KsDataStore::applyPosCPUFilter(int sd, QVector<int> vec)
 {
-	_applyIdFilter(KS_SHOW_CPU_FILTER, vec);
+	_applyIdFilter(KS_SHOW_CPU_FILTER, vec, sd);
 }
 
 /** Apply Hide CPU filter. */
-void KsDataStore::applyNegCPUFilter(QVector<int> vec)
+void KsDataStore::applyNegCPUFilter(int sd, QVector<int> vec)
 {
-	_applyIdFilter(KS_HIDE_CPU_FILTER, vec);
+	_applyIdFilter(KS_HIDE_CPU_FILTER, vec, sd);
 }
 
 /** Disable all filters. */
 void KsDataStore::clearAllFilters()
 {
 	kshark_context *kshark_ctx(nullptr);
+	int *streamIds, sd;
 
-	if (!kshark_instance(&kshark_ctx) || !_tep)
+	if (!kshark_instance(&kshark_ctx) || !kshark_ctx->n_streams)
 		return;
 
-	_unregisterCPUCollections();
+	unregisterCPUCollections();
+
+	streamIds = kshark_all_streams(kshark_ctx);
+	for (int i = 0; i < kshark_ctx->n_streams; ++i) {
+		sd = streamIds[i];
+
+		kshark_filter_clear(kshark_ctx, sd, KS_SHOW_TASK_FILTER);
+		kshark_filter_clear(kshark_ctx, sd, KS_HIDE_TASK_FILTER);
+		kshark_filter_clear(kshark_ctx, sd, KS_SHOW_EVENT_FILTER);
+		kshark_filter_clear(kshark_ctx, sd, KS_HIDE_EVENT_FILTER);
+		kshark_filter_clear(kshark_ctx, sd, KS_SHOW_CPU_FILTER);
+		kshark_filter_clear(kshark_ctx, sd, KS_HIDE_CPU_FILTER);
+
+		if (kshark_is_tep(kshark_ctx->stream[sd]))
+			kshark_tep_filter_reset(kshark_ctx->stream[sd]);
+	}
 
-	kshark_filter_clear(kshark_ctx, KS_SHOW_TASK_FILTER);
-	kshark_filter_clear(kshark_ctx, KS_HIDE_TASK_FILTER);
-	kshark_filter_clear(kshark_ctx, KS_SHOW_EVENT_FILTER);
-	kshark_filter_clear(kshark_ctx, KS_HIDE_EVENT_FILTER);
-	kshark_filter_clear(kshark_ctx, KS_SHOW_CPU_FILTER);
-	kshark_filter_clear(kshark_ctx, KS_HIDE_CPU_FILTER);
+	free(streamIds);
 
-	tep_filter_reset(kshark_ctx->advanced_event_filter);
 	kshark_clear_all_filters(kshark_ctx, _rows, _dataSize);
+	registerCPUCollections();
 
 	emit updateWidgets(this);
 }
 
 /**
- * @brief Create Plugin Manager. Use list of plugins declared in the
- *	  CMake-generated header file.
+ * @brief Apply constant offset to the timestamps of all entries from a given
+ *	  Data stream.
+ *
+ * @param sd: Data stream identifier.
+ * @param offset: The constant offset to be added (in nanosecond).
  */
-KsPluginManager::KsPluginManager(QWidget *parent)
-: QObject(parent)
+void KsDataStore::setClockOffset(int sd, int64_t offset)
 {
 	kshark_context *kshark_ctx(nullptr);
-	_parsePluginList();
 
 	if (!kshark_instance(&kshark_ctx))
 		return;
 
-	registerFromList(kshark_ctx);
+	if (!kshark_get_data_stream(kshark_ctx, sd))
+		return;
+
+	unregisterCPUCollections();
+	kshark_set_clock_offset(kshark_ctx, _rows, _dataSize, sd, offset);
+	registerCPUCollections();
 }
 
-/** Parse the plugin list declared in the CMake-generated header file. */
-void KsPluginManager::_parsePluginList()
+/**
+ * @brief Create Plugin Manager. Use the list of plugins declared in the
+ *	  CMake-generated header file.
+ */
+KsPluginManager::KsPluginManager(QWidget *parent)
+: QObject(parent)
 {
-	_ksPluginList = KsUtils::getPluginList();
-	int nPlugins = _ksPluginList.count();
+	_loadPluginList(KsUtils::getPluginList());
+}
 
-	_registeredKsPlugins.resize(nPlugins);
+QVector<kshark_plugin_list *>
+KsPluginManager::_loadPluginList(const QStringList &plugins)
+{
+	kshark_context *kshark_ctx(nullptr);
+	QVector<kshark_plugin_list *> vec;
+	kshark_plugin_list *plugin;
+	std::string name, lib;
+	int nPlugins;
+
+	if (!kshark_instance(&kshark_ctx))
+		return vec;
+
+	nPlugins = plugins.count();
 	for (int i = 0; i < nPlugins; ++i) {
-		if (_ksPluginList[i].contains(" default", Qt::CaseInsensitive)) {
-			_ksPluginList[i].remove(" default", Qt::CaseInsensitive);
-			_registeredKsPlugins[i] = true;
+		if (plugins[i].endsWith(".so")) {
+			lib = plugins[i].toStdString();
+			name = _pluginNameFromLib(plugins[i]);
 		} else {
-			_registeredKsPlugins[i] = false;
+			lib = _pluginLibFromName(plugins[i]);
+			name = plugins[i].toStdString();
+		}
+
+		plugin = kshark_find_plugin(kshark_ctx->plugins,
+					    lib.c_str());
+
+		if (!plugin) {
+			plugin = kshark_register_plugin(kshark_ctx,
+							name.c_str(),
+							lib.c_str());
+
+			if (plugin)
+				vec.append(plugin);
 		}
 	}
+
+	return vec;
 }
 
 /**
- * Register the plugins by using the information in "_ksPluginList" and
- * "_registeredKsPlugins".
+ * @brief Get a list of all plugins registered to a given Data stream.
+ *
+ * @param sd: Data stream identifier.
+ * @return List of plugin names.
  */
-void KsPluginManager::registerFromList(kshark_context *kshark_ctx)
+QStringList KsPluginManager::getStreamPluginList(int sd) const
 {
-	auto lamRegBuiltIn = [&kshark_ctx, this](const QString &plugin)
-	{
-		char *lib;
-		int n;
-
-		lib = _pluginLibFromName(plugin, n);
-		if (n <= 0)
-			return;
+	kshark_context *kshark_ctx(nullptr);
+	kshark_data_stream *stream;
+	kshark_dpi_list *plugin;
+	QStringList list;
 
-		kshark_register_plugin(kshark_ctx, lib);
-		free(lib);
-	};
+	if (!kshark_instance(&kshark_ctx))
+		return {};
 
-	auto lamRegUser = [&kshark_ctx](const QString &plugin)
-	{
-		std::string lib = plugin.toStdString();
-		kshark_register_plugin(kshark_ctx, lib.c_str());
-	};
+	stream = kshark_get_data_stream(kshark_ctx, sd);
+	if (!stream)
+		return {};
 
-	_forEachInList(_ksPluginList,
-		       _registeredKsPlugins,
-		       lamRegBuiltIn);
+	plugin = stream->plugins;
+	while (plugin) {
+		list.append(plugin->interface->name);
+		plugin = plugin->next;
+	}
 
-	_forEachInList(_userPluginList,
-		       _registeredUserPlugins,
-		       lamRegUser);
+	return list;
 }
 
 /**
- * Unegister the plugins by using the information in "_ksPluginList" and
- * "_registeredKsPlugins".
+ * @brief Get a list of all plugins registered to a given Data stream.
+ *
+ * @param sd: Data stream identifier.
  */
-void KsPluginManager::unregisterFromList(kshark_context *kshark_ctx)
+QVector<int> KsPluginManager::getActivePlugins(int sd) const
 {
-	auto lamUregBuiltIn = [&kshark_ctx, this](const QString &plugin)
-	{
-		char *lib;
-		int n;
+	kshark_context *kshark_ctx(nullptr);
+	kshark_data_stream *stream;
+	kshark_dpi_list *plugin;
+	QVector<int> vec;
+	int i(0);
 
-		lib = _pluginLibFromName(plugin, n);
-		if (n <= 0)
-			return;
+	if (!kshark_instance(&kshark_ctx))
+		return {};
 
-		kshark_unregister_plugin(kshark_ctx, lib);
-		free(lib);
-	};
+	stream = kshark_get_data_stream(kshark_ctx, sd);
+	if (!stream)
+		return {};
 
-	auto lamUregUser = [&kshark_ctx](const QString &plugin)
-	{
-		std::string lib = plugin.toStdString();
-		kshark_unregister_plugin(kshark_ctx, lib.c_str());
-	};
+	plugin = stream->plugins;
 
-	_forEachInList(_ksPluginList,
-		       _registeredKsPlugins,
-			lamUregBuiltIn);
+	while (plugin) {
+		vec.append(plugin->status & KSHARK_PLUGIN_ENABLED);
+		plugin = plugin->next;
+		i++;
+	}
 
-	_forEachInList(_userPluginList,
-		       _registeredUserPlugins,
-			lamUregUser);
+	return vec;
 }
 
-char *KsPluginManager::_pluginLibFromName(const QString &plugin, int &n)
+/**
+ * @brief Get the indexes of all plugins registered to a given stream and
+ *	  having given status.
+ */
+QVector<int> KsPluginManager::getPluginsByStatus(int sd, int status) const
 {
-	QString appPath = QCoreApplication::applicationDirPath();
-	QString libPath = appPath + "/../../kernel-shark/lib";
-	std::string pluginStr = plugin.toStdString();
-	char *lib;
+	kshark_context *kshark_ctx(nullptr);
+	kshark_data_stream *stream;
+	kshark_dpi_list *plugin;
+	QVector<int> vec;
+	int i(0);
 
-	libPath = QDir::cleanPath(libPath);
-	if (!KsUtils::isInstalled() && QDir(libPath).exists()) {
-		std::string pathStr = libPath.toStdString();
-		n = asprintf(&lib, "%s/plugin-%s.so",
-			     pathStr.c_str(), pluginStr.c_str());
-	} else {
-		n = asprintf(&lib, "%s/plugin-%s.so",
-			     KS_PLUGIN_INSTALL_PREFIX, pluginStr.c_str());
+	if (!kshark_instance(&kshark_ctx))
+		return {};
+
+	stream = kshark_get_data_stream(kshark_ctx, sd);
+	if (!stream)
+		return {};
+
+	plugin = stream->plugins;
+
+	while (plugin) {
+		if (plugin->status & status)
+			vec.append(i);
+
+		plugin = plugin->next;
+		i++;
 	}
 
-	return lib;
+	return vec;
 }
 
 /**
- * @brief Register a Plugin.
- *
- * @param plugin: provide here the name of the plugin (as in the CMake-generated
- *		  header file) of a name of the plugin's library file (.so).
+ * @brief Loop over the registered plugins and register all plugin-defined
+ *	  menus (if any).
  */
-void KsPluginManager::registerPlugin(const QString &plugin)
+void KsPluginManager::registerPluginMenues()
 {
 	kshark_context *kshark_ctx(nullptr);
-	char *lib;
-	int n;
+	kshark_plugin_list *plugin;
 
 	if (!kshark_instance(&kshark_ctx))
 		return;
 
-	for (int i = 0; i < _ksPluginList.count(); ++i) {
-		if (_ksPluginList[i] == plugin) {
-			/*
-			 * The argument is the name of the plugin. From the
-			 * name get the library .so file.
-			 */
-			lib = _pluginLibFromName(plugin, n);
-			if (n > 0) {
-				kshark_register_plugin(kshark_ctx, lib);
-				_registeredKsPlugins[i] = true;
-				free(lib);
+	for (plugin = kshark_ctx->plugins; plugin; plugin = plugin->next)
+		if (plugin->handle && plugin->ctrl_interface) {
+			void *dialogPtr = plugin->ctrl_interface(parent());
+			if (dialogPtr) {
+				QWidget *dialog =
+					static_cast<QWidget *>(dialogPtr);
+				_pluginDialogs.append(dialog);
 			}
+		}
+}
 
-			return;
+std::string KsPluginManager::_pluginLibFromName(const QString &plugin)
+{
+	QString appPath = QCoreApplication::applicationDirPath();
+	QString libPath = appPath + "/../lib";
+	std::string lib;
 
-		} else if (plugin.contains("/lib/plugin-" + _ksPluginList[i],
-					   Qt::CaseInsensitive)) {
-			/*
-			 * The argument is the name of the library .so file.
-			 */
-			n = asprintf(&lib, "%s", plugin.toStdString().c_str());
-			if (n > 0) {
-				kshark_register_plugin(kshark_ctx, lib);
-				_registeredKsPlugins[i] = true;
-				free(lib);
-			}
+	auto lamFileName = [&] () {
+		return std::string("/plugin-" + plugin.toStdString() + ".so");
+	};
 
-			return;
-		}
-	}
+	libPath = QDir::cleanPath(libPath);
+	if (!KsUtils::isInstalled() && QDir(libPath).exists())
+		lib = libPath.toStdString() + lamFileName();
+	else
+		lib = std::string(KS_PLUGIN_INSTALL_PREFIX) + lamFileName();
 
-	/* No plugin with this name in the list. Try to add it anyway. */
-	if (plugin.endsWith(".so") && QFileInfo::exists(plugin)) {
-		kshark_register_plugin(kshark_ctx,
-				       plugin.toStdString().c_str());
+	return lib;
+}
 
-		_userPluginList.append(plugin);
-		_registeredUserPlugins.append(true);
-	} else {
-		qCritical() << "ERROR: " << plugin << "cannot be registered!";
-	}
+std::string KsPluginManager::_pluginNameFromLib(const QString &plugin)
+{
+	QString name = plugin.section('/', -1);
+	name.remove("plugin-");
+	name.remove(".so");
+
+	return name.toStdString();
 }
 
-/** @brief Unregister a Built in KernelShark plugin.
- *<br>
- * WARNING: Do not use this function to unregister User plugins.
- * Instead use directly kshark_unregister_plugin().
+/**
+ * @brief Register a list pf plugins
  *
- * @param plugin: provide here the name of the plugin (as in the CMake-generated
- *		  header file) or a name of the plugin's library file (.so).
+ * @param pluginNames: Provide here the names of the plugin (as in the
+ *		       CMake-generated header file) or the names of the
+ *		       plugin's library files (.so including path).
+ * 		       The names must be comma separated.
+ */
+void KsPluginManager::registerPlugins(const QString &pluginNames)
+{
+	_userPlugins.append(_loadPluginList(pluginNames.split(',')));
+}
+
+/**
+ * @brief Unregister a list pf plugins.
  *
+ * @param pluginNames: Provide here a comma separated list of plugin names (as
+ *		       in the CMake-generated header file).
  */
-void KsPluginManager::unregisterPlugin(const QString &plugin)
+void KsPluginManager::unregisterPlugins(const QString &pluginNames)
 {
 	kshark_context *kshark_ctx(nullptr);
-	char *lib;
-	int n;
+	kshark_plugin_list *plugin;
+	kshark_data_stream *stream;
+	int *streamArray;
 
 	if (!kshark_instance(&kshark_ctx))
 		return;
 
-	for (int i = 0; i < _ksPluginList.count(); ++i) {
-		if (_ksPluginList[i] == plugin) {
-			/*
-			 * The argument is the name of the plugin. From the
-			 * name get the library .so file.
-			 */
-			lib = _pluginLibFromName(plugin, n);
-			if (n > 0) {
-				kshark_unregister_plugin(kshark_ctx, lib);
-				_registeredKsPlugins[i] = false;
-				free(lib);
-			}
-
-			return;
-		} else if (plugin.contains("/lib/plugin-" +
-			                   _ksPluginList[i], Qt::CaseInsensitive)) {
-			/*
-			 * The argument is the name of the library .so file.
-			 */
-			n = asprintf(&lib, "%s", plugin.toStdString().c_str());
-			if (n > 0) {
-				kshark_unregister_plugin(kshark_ctx, lib);
-				_registeredKsPlugins[i] = false;
-				free(lib);
-			}
+	for (auto const &name: pluginNames.split(',')) {
+		plugin = kshark_find_plugin_by_name(kshark_ctx->plugins,
+						    name.toStdString().c_str());
 
-			return;
+		streamArray = kshark_all_streams(kshark_ctx);
+		for  (int i = 0; i < kshark_ctx->n_streams; ++i) {
+			stream = kshark_get_data_stream(kshark_ctx,
+							streamArray[i]);
+			kshark_unregister_plugin_from_stream(stream,
+							     plugin->process_interface);
 		}
+
+		kshark_unregister_plugin(kshark_ctx,
+					 name.toStdString().c_str(),
+					 plugin->file);
 	}
 }
 
-/** @brief Add to the list and initialize user-provided plugins. All other
- *	   previously loaded plugins will be reinitialized and the data will be
- *	   reloaded.
- *
- * @param fileNames: the library files (.so) of the plugins.
-*/
-void KsPluginManager::addPlugins(const QStringList &fileNames)
+void KsPluginManager::_pluginToStream(const QString &pluginName,
+				      QVector<int> streamIds,
+				      bool reg)
 {
 	kshark_context *kshark_ctx(nullptr);
+	kshark_plugin_list *plugin;
+	kshark_data_stream *stream;
 
 	if (!kshark_instance(&kshark_ctx))
 		return;
 
-	kshark_handle_plugins(kshark_ctx, KSHARK_PLUGIN_CLOSE);
+	plugin = kshark_find_plugin_by_name(kshark_ctx->plugins,
+					    pluginName.toStdString().c_str());
 
-	for (auto const &p: fileNames)
-		registerPlugin(p);
+	if (!plugin || !plugin->process_interface)
+		return;
 
-	kshark_handle_plugins(kshark_ctx, KSHARK_PLUGIN_INIT);
+	for (auto const &sd: streamIds) {
+		stream = kshark_get_data_stream(kshark_ctx, sd);
+		if (!stream)
+			continue;
+
+		if (reg)
+			kshark_register_plugin_to_stream(stream,
+							 plugin->process_interface,
+							 true);
+		else
+			kshark_unregister_plugin_from_stream(stream,
+							     plugin->process_interface);
+
+		kshark_handle_all_dpis(stream, KSHARK_PLUGIN_UPDATE);
+	}
 
 	emit dataReload();
 }
 
-/** Unload all plugins. */
-void KsPluginManager::unloadAll()
+/**
+ * @brief Register a given plugin to given Data streams.
+ *
+ * @param pluginName: The name of the plugin to register.
+ * @param streamIds: Vector of Data stream identifiers.
+ */
+void KsPluginManager::registerPluginToStream(const QString &pluginName,
+					     QVector<int> streamIds)
+{
+	_pluginToStream(pluginName, streamIds, true);
+}
+
+/**
+ * @brief Unregister a given plugin from given Data streams.
+ *
+ * @param pluginName: The name of the plugin to unregister.
+ * @param streamIds: Vector of Data stream identifiers.
+ */
+void KsPluginManager::unregisterPluginFromStream(const QString &pluginName,
+						 QVector<int> streamIds)
+{
+	_pluginToStream(pluginName, streamIds, false);
+}
+
+/** @brief Add to the list and initialize user-provided plugins. All other
+ *	   previously loaded plugins will be reinitialized and the data will be
+ *	   reloaded.
+ *
+ * @param fileNames: The library files (.so) of the plugins.
+ * @param streamIds: Vector of Data stream identifiers. If the vector is empty
+ * 		     the plugins will be registered to all Data streams.
+ * 		     Otherwise the plugins will be registered to the listed
+ * 		     streams.
+*/
+void KsPluginManager::addPlugins(const QStringList &fileNames,
+				 QVector<int> streamIds)
 {
+	QVector<kshark_plugin_list *> plugins;
 	kshark_context *kshark_ctx(nullptr);
+	kshark_data_stream *stream;
 
 	if (!kshark_instance(&kshark_ctx))
 		return;
 
-	kshark_handle_plugins(kshark_ctx, KSHARK_PLUGIN_CLOSE);
-	kshark_free_plugin_list(kshark_ctx->plugins);
-	kshark_ctx->plugins = nullptr;
-	kshark_free_event_handler_list(kshark_ctx->event_handlers);
+	plugins = _loadPluginList(fileNames);
+	_userPlugins.append(plugins);
 
-	unregisterFromList(kshark_ctx);
+	if (streamIds.isEmpty())
+		streamIds = KsUtils::getStreamIdList(kshark_ctx);
+
+	for (auto const &sd: streamIds) {
+		stream = kshark_get_data_stream(kshark_ctx, sd);
+
+		for (auto const &p: plugins) {
+			if (p->process_interface)
+				kshark_register_plugin_to_stream(stream,
+								 p->process_interface,
+								 true);
+		}
+
+		kshark_handle_all_dpis(stream, KSHARK_PLUGIN_UPDATE);
+	}
 }
 
-/** @brief Update (change) the Plugins.
+/** @brief Update (change) the plugins for a given Data stream.
  *
- * @param pluginIds: The indexes of the plugins to be loaded.
+ * @param sd: Data stream identifier.
+ * @param pluginStates: A vector of plugin's states (0 or 1) telling which
+ *			plugins to be loaded.
  */
-void KsPluginManager::updatePlugins(QVector<int> pluginIds)
+void KsPluginManager::updatePlugins(int sd, QVector<int> pluginStates)
 {
 	kshark_context *kshark_ctx(nullptr);
+	kshark_data_stream *stream;
+	kshark_dpi_list *plugin;
+	QVector<int> vec;
+	int i(0);
 
 	if (!kshark_instance(&kshark_ctx))
 		return;
 
-	auto register_plugins = [&] (QVector<int> ids)
-	{
-		int nKsPlugins = _registeredKsPlugins.count();
-
-		/* First clear all registered plugins. */
-		for (auto &p: _registeredKsPlugins)
-			p = false;
-		for (auto &p: _registeredUserPlugins)
-			p = false;
-
-		/* The vector contains the indexes of those to register. */
-		for (auto const &p: ids) {
-			if (p < nKsPlugins)
-				_registeredKsPlugins[p] = true;
-			else
-				_registeredUserPlugins[p - nKsPlugins] = true;
-		}
-		registerFromList(kshark_ctx);
-	};
+	stream = kshark_get_data_stream(kshark_ctx, sd);
+	if (!stream)
+		return;
 
-	if (!kshark_ctx->pevent) {
-		kshark_free_plugin_list(kshark_ctx->plugins);
-		kshark_ctx->plugins = nullptr;
+	plugin = stream->plugins;
+	while (plugin) {
+		if (pluginStates[i++])
+			plugin->status |= KSHARK_PLUGIN_ENABLED;
+		else
+			plugin->status &= ~KSHARK_PLUGIN_ENABLED;
 
-		/*
-		 * No data is loaded. For the moment, just register the
-		 * plugins. Handling of the plugins will be done after
-		 * we load a data file.
-		 */
-		register_plugins(pluginIds);
-		return;
+		plugin = plugin->next;
 	}
 
-	/* Clean up all old plugins first. */
-	unloadAll();
-
-	/* Now load. */
-	register_plugins(pluginIds);
-	kshark_handle_plugins(kshark_ctx, KSHARK_PLUGIN_INIT);
+	kshark_handle_all_dpis(stream, KSHARK_PLUGIN_UPDATE);
+}
 
-	emit dataReload();
+/**
+ * @brief Destroy all Plugin dialogs.
+ */
+void KsPluginManager::deletePluginDialogs()
+{
+	/** Delete all register plugin dialogs. */
+	for (auto &pd: _pluginDialogs)
+		delete pd;
 }
diff --git a/src/KsUtils.hpp b/src/KsUtils.hpp
index 2772b84..0d2c9c3 100644
--- a/src/KsUtils.hpp
+++ b/src/KsUtils.hpp
@@ -45,7 +45,15 @@ static auto stringWidth = [](QString s)
 	QFont font;
 	QFontMetrics fm(font);
 
+#ifdef QT_VERSION_LESS_5_11
+
+	return fm.width(s);
+
+#else
+
 	return fm.horizontalAdvance(s);
+
+#endif // QT_VERSION_LESS_5_11
 };
 
 //! @endcond
@@ -54,13 +62,13 @@ static auto stringWidth = [](QString s)
 #define FONT_HEIGHT		fontHeight()
 
 /** Macro providing the width of the font in pixels. */
-#define FONT_WIDTH 		stringWidth("4")
+#define FONT_WIDTH 		(stringWidth("KernelShark") / 11)
 
 /** Macro providing the width of a string in pixels. */
 #define STRING_WIDTH(s)		stringWidth(s)
 
 /** Macro providing the height of the KernelShark graphs in pixels. */
-#define KS_GRAPH_HEIGHT	(FONT_HEIGHT*2)
+#define KS_GRAPH_HEIGHT		(FONT_HEIGHT * 2)
 
 //! @cond Doxygen_Suppress
 
@@ -82,16 +90,27 @@ std::chrono::high_resolution_clock::now() - t0).count()
 
 namespace KsUtils {
 
-QVector<int> getCPUList();
+QVector<int> getCPUList(int sd);
+
+QVector<int> getPidList(int sd);
+
+QVector<int> getEventIdList(int sd);
+
+int getEventId(int sd, const QString &eventName);
+
+QString getEventName(int sd, int eventId);
+
+QStringList getEventFieldsList(int sd, int eventId);
 
-QVector<int> getPidList();
+kshark_event_field_format getEventFieldType(int sd, int eventId,
+					    const QString &fieldName);
 
-QVector<int> getEventIdList(tep_event_sort_type sortType=TEP_EVENT_SORT_ID);
+QVector<int> getStreamIdList(kshark_context *kshark_ctx);
 
-QVector<int> getFilterIds(tracecmd_filter_id *filter);
+QVector<int> getFilterIds(kshark_hash_id *filter);
 
-/** @brief Geat the list of plugins. */
-inline QStringList getPluginList() {return plugins.split(";");}
+/** @brief Geat the list of plugins provided by the package. */
+inline QStringList getPluginList() {return QString(KS_BUILTIN_PLUGINS).split(";");}
 
 void listFilterSync(bool state);
 
@@ -113,8 +132,8 @@ inline QString Ts2String(int64_t ts, int prec)
 	return QString::number(ts * 1e-9, 'f', prec);
 }
 
-bool matchCPUVisible(struct kshark_context *kshark_ctx,
-			      struct kshark_entry *e, int cpu);
+bool matchCPUVisible(kshark_context *kshark_ctx,
+		     kshark_entry *e, int sd, int *cpu);
 
 bool isInstalled();
 
@@ -134,10 +153,33 @@ QString getSaveFile(QWidget *parent,
 		    const QString &extension,
 		    QString &lastFilePath);
 
+void setElidedText(QLabel* label, QString text,
+		   enum Qt::TextElideMode mode,
+		   int labelWidth);
+
 QStringList splitArguments(QString cmd);
 
 QVector<int> parseIdList(QString v_str);
 
+QStringList getTepEvtName(int sd, int eventId);
+
+/** Get a string to be used as a standard name of a CPU graph. */
+inline QString cpuPlotName(int cpu) {return QString("CPU %1").arg(cpu);}
+
+QString taskPlotName(int sd, int pid);
+
+/** Get the total number of Data streams. */
+inline int getNStreams()
+{
+	kshark_context *kshark_ctx(nullptr);
+
+	if (!kshark_instance(&kshark_ctx))
+		return -1;
+	return kshark_ctx->n_streams;
+}
+
+QString streamDescription(kshark_data_stream *stream);
+
 }; // KsUtils
 
 /** Identifier of the Dual Marker active state. */
@@ -158,39 +200,49 @@ public:
 
 	~KsDataStore();
 
-	void loadDataFile(const QString &file);
+	int loadDataFile(const QString &file,
+			 QVector<kshark_dpi *> plugins);
+
+	int appendDataFile(const QString &file, int64_t shift);
 
 	void clear();
 
-	/** Get the trace event parser. */
-	tep_handle *tep() const {return _tep;}
+	/** Get the trace data array. */
+	kshark_entry **rows() const {return _rows;}
 
-	/** Get the trace data array.. */
-	struct kshark_entry **rows() const {return _rows;}
+	/** Get a reference of the trace data array. */
+	kshark_entry ***rows_r() {return &_rows;}
 
 	/** Get the size of the data array. */
 	ssize_t size() const {return _dataSize;}
 
+	/** Set the size of the data (number of entries). */
+	void setSize(ssize_t s) {_dataSize = s;}
+
 	void reload();
 
 	void update();
 
 	void registerCPUCollections();
 
-	void applyPosTaskFilter(QVector<int>);
+	void unregisterCPUCollections();
+
+	void applyPosTaskFilter(int sd, QVector<int> vec);
 
-	void applyNegTaskFilter(QVector<int>);
+	void applyNegTaskFilter(int sd, QVector<int> vec);
 
-	void applyPosEventFilter(QVector<int>);
+	void applyPosEventFilter(int sd, QVector<int> vec);
 
-	void applyNegEventFilter(QVector<int>);
+	void applyNegEventFilter(int sd, QVector<int> vec);
 
-	void applyPosCPUFilter(QVector<int>);
+	void applyPosCPUFilter(int sd, QVector<int> vec);
 
-	void applyNegCPUFilter(QVector<int>);
+	void applyNegCPUFilter(int sd, QVector<int> vec);
 
 	void clearAllFilters();
 
+	void setClockOffset(int sd, int64_t offset);
+
 signals:
 	/**
 	 * This signal is emitted when the data has changed and the View
@@ -199,75 +251,84 @@ signals:
 	void updateWidgets(KsDataStore *);
 
 private:
-	/** Page event used to parse the page. */
-	tep_handle		*_tep;
-
 	/** Trace data array. */
-	struct kshark_entry	**_rows;
+	kshark_entry		**_rows;
 
 	/** The size of the data array. */
 	ssize_t			_dataSize;
 
+	int _openDataFile(kshark_context *kshark_ctx, const QString &file);
+
 	void _freeData();
-	void _unregisterCPUCollections();
-	void _applyIdFilter(int filterId, QVector<int> vec);
+
+	void _applyIdFilter(int filterId, QVector<int> vec, int sd);
+
+	void _addPluginsToStream(kshark_context *kshark_ctx, int sd,
+				 QVector<kshark_dpi *> plugins);
 };
 
-/** A Plugin Manage class. */
+/** A Plugin Manager class. */
 class KsPluginManager : public QObject
 {
 	Q_OBJECT
 public:
 	explicit KsPluginManager(QWidget *parent = nullptr);
 
-	/** A list of available built-in plugins. */
-	QStringList	_ksPluginList;
+	QStringList getStreamPluginList(int sd) const;
+
+	QVector<int> getActivePlugins(int sd) const;
 
-	/** A list of registered built-in plugins. */
-	QVector<bool>	_registeredKsPlugins;
+	QVector<int> getPluginsByStatus(int sd, int status) const;
 
-	/** A list of available user plugins. */
-	QStringList	_userPluginList;
+	/** Get a list of all plugins added by the user. */
+	const QVector<kshark_plugin_list *>
+	getUserPlugins() const {return _userPlugins;}
 
-	/** A list of registered user plugins. */
-	QVector<bool>	_registeredUserPlugins;
+	void registerPluginMenues();
 
-	void registerFromList(kshark_context *kshark_ctx);
-	void unregisterFromList(kshark_context *kshark_ctx);
+	void updatePlugins(int sd, QVector<int> pluginStates);
 
-	void registerPlugin(const QString &plugin);
-	void unregisterPlugin(const QString &plugin);
+	void addPlugins(const QStringList &fileNames, QVector<int> streams);
 
-	void addPlugins(const QStringList &fileNames);
+	void registerPlugins(const QString &pluginNames);
 
-	void unloadAll();
+	void unregisterPlugins(const QString &pluginNames);
 
-	void updatePlugins(QVector<int> pluginId);
+	void registerPluginToStream(const QString &pluginName,
+				    QVector<int> streamId);
+
+	void unregisterPluginFromStream(const QString &pluginName,
+					QVector<int> streamId);
+
+	void deletePluginDialogs();
+
+	/** Append to the list of User plugin. */
+	void addUserPluginToList(kshark_plugin_list *p) {_userPlugins.append(p);}
 
 signals:
 	/** This signal is emitted when a plugin is loaded or unloaded. */
 	void dataReload();
 
 private:
-	void _parsePluginList();
-
-	char *_pluginLibFromName(const QString &plugin, int &n);
-
-	template <class T>
-	void _forEachInList(const QStringList &pl,
-			    const QVector<bool> &reg,
-			    T action)
-	{
-		int nPlugins;
-		nPlugins = pl.count();
-		for (int i = 0; i < nPlugins; ++i) {
-			if (reg[i]) {
-				action(pl[i]);
-			}
-		}
-	}
+	QVector<kshark_plugin_list *>	_userPlugins;
+
+	/** Plugin dialogs. */
+	QVector<QWidget *>		_pluginDialogs;
+
+	QVector<kshark_plugin_list *>
+	_loadPluginList(const QStringList &plugins);
+
+	std::string _pluginLibFromName(const QString &plugin);
+
+	std::string _pluginNameFromLib(const QString &plugin);
+
+	void _pluginToStream(const QString &pluginName,
+			     QVector<int> streamId,
+			     bool reg);
 };
 
 KsPlot::Color& operator <<(KsPlot::Color &thisColor, const QColor &c);
 
+QColor& operator <<(QColor &thisColor, const KsPlot::Color &c);
+
 #endif
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index a1e3085..17b586e 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -6,7 +6,7 @@ set(EXECUTABLE_OUTPUT_PATH           ${KS_TEST_DIR})
 add_executable(kshark-tests          libkshark-tests.cpp)
 target_include_directories(kshark-tests PRIVATE ${Boost_INCLUDE_DIRS})
 target_compile_definitions(kshark-tests PRIVATE "BOOST_TEST_DYN_LINK=1")
-target_link_libraries(kshark-tests   kshark
+target_link_libraries(kshark-tests   kshark-gui
                                      ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY})
 
 add_test(NAME              "libkshark_tests"
diff --git a/tests/libkshark-tests.cpp b/tests/libkshark-tests.cpp
index 780a3fa..eb5cb1f 100644
--- a/tests/libkshark-tests.cpp
+++ b/tests/libkshark-tests.cpp
@@ -46,6 +46,7 @@ BOOST_AUTO_TEST_CASE(add_remove_streams)
 	BOOST_CHECK_EQUAL(sd, -ENODEV);
 
 	kshark_close_all(kshark_ctx);
+	kshark_free(kshark_ctx);
 }
 
 #define ARRAY_DEFAULT_SIZE	1000
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v2 07/27] kernel-shark: Update KsModels and KsSearchFSM
  2021-02-11 10:31 [PATCH v2 00/27] Complete the KernelShark v2 transformation Yordan Karadzhov (VMware)
                   ` (5 preceding siblings ...)
  2021-02-11 10:31 ` [PATCH v2 06/27] kernel-shark: Update KsUtils Yordan Karadzhov (VMware)
@ 2021-02-11 10:31 ` Yordan Karadzhov (VMware)
  2021-02-11 10:31 ` [PATCH v2 08/27] kernel-shark: Add trace data files for CI testing Yordan Karadzhov (VMware)
                   ` (20 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Yordan Karadzhov (VMware) @ 2021-02-11 10:31 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

The compilation of KsModels.cpp and KsSearchFSM.cpp is re-enabled
and all functionalities are made compatible with the new version
of the C API of libkshark (KernelShark 2.0). The two source files
are updated in a single patch because of their interdependence.

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 src/CMakeLists.txt  |  12 ++---
 src/KsModels.cpp    | 112 +++++++++++++++++++++++++++++++++++++-------
 src/KsModels.hpp    |  28 +++++++----
 src/KsSearchFSM.cpp |  12 ++++-
 4 files changed, 132 insertions(+), 32 deletions(-)

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 5c9fe17..21d5b85 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -66,10 +66,10 @@ endif (OPENGL_FOUND)
 if (Qt5Widgets_FOUND AND Qt5Network_FOUND)
 
     message(STATUS "libkshark-gui")
-    set (ks-guiLib_hdr  KsUtils.hpp)
-#                         KsModels.hpp
+    set (ks-guiLib_hdr  KsUtils.hpp
+                        KsModels.hpp
 #                         KsGLWidget.hpp
-#                         KsSearchFSM.hpp
+                        KsSearchFSM.hpp)
 #                         KsDualMarker.hpp
 #                         KsWidgetsLib.hpp
 #                         KsTraceGraph.hpp
@@ -81,11 +81,11 @@ if (Qt5Widgets_FOUND AND Qt5Network_FOUND)
 
     QT5_WRAP_CPP(ks-guiLib_hdr_moc ${ks-guiLib_hdr})
 
-    add_library(kshark-gui  SHARED  ${ks-guiLib_hdr_moc}    KsUtils.cpp)
-#                                                             KsModels.cpp
+    add_library(kshark-gui  SHARED  ${ks-guiLib_hdr_moc}    KsUtils.cpp
+                                                            KsModels.cpp
 #                                                             KsSession.cpp
 #                                                             KsGLWidget.cpp
-#                                                             KsSearchFSM.cpp
+                                                            KsSearchFSM.cpp)
 #                                                             KsDualMarker.cpp
 #                                                             KsWidgetsLib.cpp
 #                                                             KsTraceGraph.cpp
diff --git a/src/KsModels.cpp b/src/KsModels.cpp
index 51a7b79..df8373e 100644
--- a/src/KsModels.cpp
+++ b/src/KsModels.cpp
@@ -227,16 +227,54 @@ QList<int> KsFilterProxyModel::searchThread(int column,
 	return matchList;
 }
 
+int KsFilterProxyModel::mapRowFromSource(int r) const
+{
+	/*
+	 * This works because the row number is shown in column
+	 * TRACE_VIEW_COL_INDEX (or TRACE_VIEW_COL_INDEX - 1 in the case when
+	 * the Stream Id column is hidden).
+	 */
+	int column = KsViewModel::TRACE_VIEW_COL_INDEX;
+
+	if(_source->singleStream())
+		column--;
+
+	return this->data(this->index(r, column)).toInt();
+}
+
 /** Create default (empty) KsViewModel object. */
 KsViewModel::KsViewModel(QObject *parent)
 : QAbstractTableModel(parent),
   _data(nullptr),
   _nRows(0),
-  _header({"#", "CPU", "Time Stamp", "Task", "PID",
-	   "Latency", "Event", "Info"}),
   _markA(KS_NO_ROW_SELECTED),
-  _markB(KS_NO_ROW_SELECTED)
-{}
+  _markB(KS_NO_ROW_SELECTED),
+  _singleStream(true)
+{
+	_updateHeader();
+}
+
+/** Update the list of table headers. */
+void KsViewModel::_updateHeader()
+{
+	beginRemoveColumns(QModelIndex(), 0, _header.count());
+	endRemoveColumns();
+
+	_header.clear();
+
+	if (KsUtils::getNStreams() > 1) {
+		_header << " >> ";
+		_singleStream = false;
+	} else {
+		_singleStream = true;
+	}
+
+	_header << "#" << "CPU" << "Time Stamp" << "Task" << "PID"
+		<< "Latency" << "Event" << "Info";
+
+	beginInsertColumns(QModelIndex(), 0, _header.count() - 1);
+	endInsertColumns();
+}
 
 /**
  * Get the data stored under the given role for the item referred to by
@@ -246,10 +284,10 @@ KsViewModel::KsViewModel(QObject *parent)
 QVariant KsViewModel::data(const QModelIndex &index, int role) const
 {
 	if (role == Qt::ForegroundRole) {
-		if (index.row() == _markA)
+		if (index.row() == _markA && index.column() != 0)
 			return QVariant::fromValue(QColor(Qt::white));
 
-		if (index.row() == _markB)
+		if (index.row() == _markB && index.column() != 0)
 			return QVariant::fromValue(QColor(Qt::white));
 	}
 
@@ -259,6 +297,15 @@ QVariant KsViewModel::data(const QModelIndex &index, int role) const
 
 		if (index.row() == _markB)
 			return QVariant::fromValue(QColor(_colorMarkB));
+
+		if (index.column() == TRACE_VIEW_COL_STREAM &&
+		    !_singleStream) {
+			int sd = _data[index.row()]->stream_id;
+			QColor col;
+			col << KsPlot::getColor(&_streamColors, sd);
+
+			return QVariant::fromValue(col);
+		}
 	}
 
 	if (role == Qt::DisplayRole)
@@ -270,9 +317,26 @@ QVariant KsViewModel::data(const QModelIndex &index, int role) const
 /** Get the string data stored in a given cell of the table. */
 QString KsViewModel::getValueStr(int column, int row) const
 {
+	char *buffer;
 	int pid;
 
+	/*
+	 * If only one Data stream (file) is loaded, the first column
+	 * (TRACE_VIEW_COL_STREAM) is not shown.
+	 */
+	if(_singleStream)
+		column++;
+
+	auto lanMakeString = [&buffer] () {
+		QString str(buffer);
+		free(buffer);
+		return str;
+	};
+
 	switch (column) {
+		case TRACE_VIEW_COL_STREAM :
+			return QString("%1").arg(_data[row]->stream_id);
+
 		case TRACE_VIEW_COL_INDEX :
 			return QString("%1").arg(row);
 
@@ -283,20 +347,24 @@ QString KsViewModel::getValueStr(int column, int row) const
 			return KsUtils::Ts2String(_data[row]->ts, 6);
 
 		case TRACE_VIEW_COL_COMM:
-			return kshark_get_task_easy(_data[row]);
+			buffer = kshark_get_task(_data[row]);
+			return lanMakeString();
 
 		case TRACE_VIEW_COL_PID:
-			pid = kshark_get_pid_easy(_data[row]);
+			pid = kshark_get_pid(_data[row]);
 			return QString("%1").arg(pid);
 
-		case TRACE_VIEW_COL_LAT:
-			return kshark_get_latency_easy(_data[row]);
+		case TRACE_VIEW_COL_AUX:
+			buffer = kshark_get_aux_info(_data[row]);
+			return lanMakeString();
 
 		case TRACE_VIEW_COL_EVENT:
-			return kshark_get_event_name_easy(_data[row]);
+			buffer = kshark_get_event_name(_data[row]);
+			return lanMakeString();
 
 		case TRACE_VIEW_COL_INFO :
-			return kshark_get_info_easy(_data[row]);
+			buffer = kshark_get_info(_data[row]);
+			return lanMakeString();
 
 		default:
 			return {};
@@ -333,8 +401,10 @@ void KsViewModel::fill(KsDataStore *data)
 
 	_data = data->rows();
 	_nRows = data->size();
+	_streamColors = KsPlot::streamColorTable();
 
 	endInsertRows();
+	_updateHeader();
 }
 
 /** @brief Select a row in the table.
@@ -375,6 +445,14 @@ void KsViewModel::update(KsDataStore *data)
 	fill(data);
 }
 
+/** Update the color scheme used by the model. */
+void KsViewModel::loadColors()
+{
+	beginResetModel();
+	_streamColors = KsPlot::streamColorTable();
+	endResetModel();
+}
+
 /** @brief Search the content of the table for a data satisfying an abstract
  *	   condition.
  *
@@ -420,12 +498,12 @@ KsGraphModel::~KsGraphModel()
 /**
  * @brief Provide the Visualization model with data. Calculate the current
  *	  state of the model.
- *
- * @param entries: Input location for the trace data.
- * @param n: Number of bins.
  */
-void KsGraphModel::fill(kshark_entry **entries, size_t n)
+void KsGraphModel::fill(KsDataStore *data)
 {
+	kshark_entry **entries = data->rows();
+	size_t n = data->size();
+
 	if (n == 0)
 		return;
 
@@ -435,7 +513,7 @@ void KsGraphModel::fill(kshark_entry **entries, size_t n)
 		ksmodel_set_bining(&_histo,
 				   KS_DEFAULT_NBUNS,
 				   entries[0]->ts,
-				   entries[n-1]->ts);
+				   entries[n - 1]->ts);
 
 	ksmodel_fill(&_histo, entries, n);
 
diff --git a/src/KsModels.hpp b/src/KsModels.hpp
index d360ad6..3a6d3f1 100644
--- a/src/KsModels.hpp
+++ b/src/KsModels.hpp
@@ -26,6 +26,7 @@
 // KernelShark
 #include "libkshark.h"
 #include "libkshark-model.h"
+#include "KsPlotTools.hpp"
 #include "KsSearchFSM.hpp"
 
 /** A negative row index, to be used for deselecting the Passive Marker. */
@@ -45,7 +46,7 @@ public:
 	explicit KsViewModel(QObject *parent = nullptr);
 
 	/** Set the colors of the two markers. */
-	void setColors(const QColor &colA, const QColor &colB) {
+	void setMarkerColors(const QColor &colA, const QColor &colB) {
 		_colorMarkA = colA;
 		_colorMarkB = colB;
 	};
@@ -91,8 +92,16 @@ public:
 		      search_condition_func cond,
 		      QList<size_t> *matchList);
 
+	void loadColors();
+
+	/** Returns True is only one Data stream is open. */
+	bool singleStream() const {return _singleStream;}
+
 	/** Table columns Identifiers. */
 	enum {
+		/** Identifier of the Data stream. */
+		TRACE_VIEW_COL_STREAM,
+
 		/** Identifier of the Index column. */
 		TRACE_VIEW_COL_INDEX,
 
@@ -109,7 +118,7 @@ public:
 		TRACE_VIEW_COL_PID,
 
 		/** Identifier of the Latency Id column. */
-		TRACE_VIEW_COL_LAT,
+		TRACE_VIEW_COL_AUX,
 
 		/** Identifier of the Event name Id column. */
 		TRACE_VIEW_COL_EVENT,
@@ -122,6 +131,8 @@ public:
 	};
 
 private:
+	void _updateHeader();
+
 	/** Trace data array. */
 	kshark_entry		**_data;
 
@@ -142,6 +153,11 @@ private:
 
 	/** The color of the row selected by marker B. */
 	QColor	_colorMarkB;
+
+	/** True if only one Data stream is open. */
+	bool	_singleStream;
+
+	KsPlot::ColorTable	_streamColors;
 };
 
 /**
@@ -192,11 +208,7 @@ public:
 	 * Use the "row" index in the Proxy model to retrieve the "row" index
 	 * in the source model.
 	 */
-	int mapRowFromSource(int r) const
-	{
-		/*This works because the row number is shown in column "0". */
-		return this->data(this->index(r, 0)).toInt();
-	}
+	int mapRowFromSource(int r) const;
 
 	/** Get the source model. */
 	KsViewModel *source() {return _source;}
@@ -272,7 +284,7 @@ public:
 	/** Get the kshark_trace_histo object. */
 	kshark_trace_histo *histo() {return &_histo;}
 
-	void fill(kshark_entry **entries, size_t n);
+	void fill(KsDataStore *data);
 
 	void shiftForward(size_t n);
 
diff --git a/src/KsSearchFSM.cpp b/src/KsSearchFSM.cpp
index 6a93ca7..a5f3682 100644
--- a/src/KsSearchFSM.cpp
+++ b/src/KsSearchFSM.cpp
@@ -136,6 +136,16 @@ void KsSearchFSM ::_lockSearchPanel(bool lock)
 /** Act according to the provided input. */
 void NotDone::handleInput(KsSearchFSM* sm, sm_input_t input)
 {
+	int column = sm->column();
+	if (sm->_columnComboBox.findText(">>", Qt::MatchContains) < 0) {
+		/*
+		 * If only one Data stream (file) is loaded, the ">>" column
+		 * (TRACE_VIEW_COL_STREAM) is not shown. The column index has
+		 * to be corrected.
+		 */
+		++column;
+	}
+
 	switch(input) {
 	case sm_input_t::Start:
 		sm->_lastRowSearched = -1;
@@ -166,7 +176,7 @@ void Paused::handleInput(KsSearchFSM* sm, sm_input_t input)
 		sm->searchRestartVisible(false);
 
 		if (sm->column() != KsViewModel::TRACE_VIEW_COL_INFO &&
-		    sm->column() != KsViewModel::TRACE_VIEW_COL_LAT)
+		    sm->column() != KsViewModel::TRACE_VIEW_COL_AUX)
 			sm->_searchCountLabel.setText("");
 
 		sm->changeState(std::shared_ptr<InProgress>(new InProgress));
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v2 08/27] kernel-shark: Add trace data files for CI testing
  2021-02-11 10:31 [PATCH v2 00/27] Complete the KernelShark v2 transformation Yordan Karadzhov (VMware)
                   ` (6 preceding siblings ...)
  2021-02-11 10:31 ` [PATCH v2 07/27] kernel-shark: Update KsModels and KsSearchFSM Yordan Karadzhov (VMware)
@ 2021-02-11 10:31 ` Yordan Karadzhov (VMware)
  2021-02-11 10:31 ` [PATCH v2 09/27] kernel-shark: Add plugin tests Yordan Karadzhov (VMware)
                   ` (19 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Yordan Karadzhov (VMware) @ 2021-02-11 10:31 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

The patch adds a procedure to download two trace data files that
will be used by the CI tests. The files are obtained from a dedicated
github repository. The tests cases using the trace data files will be
added in following patches.

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 .github/workflows/main.yml |  8 ++++++++
 CMakeLists.txt             |  3 ++-
 build/cmake_clean.sh       |  1 +
 tests/CMakeLists.txt       |  6 +++++-
 tests/get_test_data.sh     | 21 +++++++++++++++++++++
 5 files changed, 37 insertions(+), 2 deletions(-)
 create mode 100755 tests/get_test_data.sh

diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index a35f003..2cce624 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -35,6 +35,7 @@ jobs:
         make
         sudo make install
         sudo make install_libs
+
     - name: Create Build Environment
       # Some projects don't allow in-source building, so create a separate build directory
       # We'll use this as our working directory for all subsequent commands
@@ -61,3 +62,10 @@ jobs:
       shell: bash
       # Execute tests defined by the CMake configuration.
       run: ctest -C $BUILD_TYPE
+
+    - name: Upload Artifacts
+      if: ${{ always() }}
+      uses: actions/upload-artifact@v2
+      with:
+        name: artifacts-download
+        path: ${{runner.workspace}}/build/Testing/
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e013916..26fb7ae 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -123,8 +123,9 @@ add_subdirectory(${KS_DIR}/examples)
 
 if (Boost_FOUND)
 
+    set(KS_TEST_DIR                      "${KS_DIR}/tests")
     enable_testing()
-    add_subdirectory(${KS_DIR}/tests)
+    add_subdirectory(${KS_TEST_DIR})
 
 endif()
 
diff --git a/build/cmake_clean.sh b/build/cmake_clean.sh
index f70b545..b534014 100755
--- a/build/cmake_clean.sh
+++ b/build/cmake_clean.sh
@@ -9,6 +9,7 @@ rm -rf src/
 rm -rf examples/
 rm -rf tests/
 rm -rf Testing/
+rm -f ../tests/*.dat
 rm -f ../lib/*
 rm ../kernelshark.desktop
 rm ../org.freedesktop.kshark-record.policy
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 17b586e..0847414 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -1,6 +1,5 @@
 message("\n tests ...")
 
-set(KS_TEST_DIR                      "${KS_DIR}/tests")
 set(EXECUTABLE_OUTPUT_PATH           ${KS_TEST_DIR})
 
 add_executable(kshark-tests          libkshark-tests.cpp)
@@ -9,6 +8,11 @@ target_compile_definitions(kshark-tests PRIVATE "BOOST_TEST_DYN_LINK=1")
 target_link_libraries(kshark-tests   kshark-gui
                                      ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY})
 
+add_test(NAME              "get_test_data"
+         COMMAND           ${KS_TEST_DIR}/get_test_data.sh
+         WORKING_DIRECTORY ${KS_TEST_DIR})
+
+message(STATUS "libkshark-tests")
 add_test(NAME              "libkshark_tests"
          COMMAND           ${KS_TEST_DIR}/kshark-tests --log_format=HRF
          WORKING_DIRECTORY ${KS_TEST_DIR})
diff --git a/tests/get_test_data.sh b/tests/get_test_data.sh
new file mode 100755
index 0000000..b935d11
--- /dev/null
+++ b/tests/get_test_data.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+TEST_DIR=`dirname $0`
+TEST_DATA_DIR=kernel-shark_testdata
+TEST_DATA_REPO=https://github.com/yordan-karadzhov/${TEST_DATA_DIR}.git
+
+rm -fv ${TEST_DIR}/*.dat
+
+if [ -d "${TEST_DATA_DIR}" ]; then
+    rm -rf ${TEST_DATA_DIR}
+fi
+
+git clone ${TEST_DATA_REPO}
+
+if [ ! -d "${TEST_DATA_DIR}" ]; then
+    exit false
+fi
+
+rm -f ${TEST_DATA_DIR}/LICENSE
+cp -v ${TEST_DATA_DIR}/* ${TEST_DIR}/
+rm -rf ${TEST_DATA_DIR}
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v2 09/27] kernel-shark: Add plugin tests
  2021-02-11 10:31 [PATCH v2 00/27] Complete the KernelShark v2 transformation Yordan Karadzhov (VMware)
                   ` (7 preceding siblings ...)
  2021-02-11 10:31 ` [PATCH v2 08/27] kernel-shark: Add trace data files for CI testing Yordan Karadzhov (VMware)
@ 2021-02-11 10:31 ` Yordan Karadzhov (VMware)
  2021-02-11 10:31 ` [PATCH v2 10/27] kernel-shark: Add model tests Yordan Karadzhov (VMware)
                   ` (18 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Yordan Karadzhov (VMware) @ 2021-02-11 10:31 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

We add a number of dummy plugins and we test the plugin-related part
of the C API.

We also add few simple test cases of the functionalities provided in
KSUtils.

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 CMakeLists.txt                |  11 +-
 build/cmake_clean.sh          |   2 +-
 build/deff.h.cmake            |   3 +
 src/CMakeLists.txt            |   4 +-
 tests/CMakeLists.txt          |  34 +++-
 tests/libkshark-gui-tests.cpp | 227 +++++++++++++++++++++++++
 tests/libkshark-tests.cpp     | 309 +++++++++++++++++++++++++++++++++-
 tests/test-input.c            | 134 +++++++++++++++
 tests/test-input_ctrl.c       | 140 +++++++++++++++
 tests/test-plugin_dpi.c       |  26 +++
 tests/test-plugin_dpi_ctrl.c  |  32 ++++
 tests/test-plugin_dpi_err.c   |  26 +++
 12 files changed, 934 insertions(+), 14 deletions(-)
 create mode 100644 tests/libkshark-gui-tests.cpp
 create mode 100644 tests/test-input.c
 create mode 100644 tests/test-input_ctrl.c
 create mode 100644 tests/test-plugin_dpi.c
 create mode 100644 tests/test-plugin_dpi_ctrl.c
 create mode 100644 tests/test-plugin_dpi_err.c

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 26fb7ae..b9b947e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -144,10 +144,13 @@ if (_DOXYGEN_DOC AND DOXYGEN_FOUND)
 
 endif ()
 
-configure_file( ${KS_DIR}/build/ks.desktop.cmake
-                ${KS_DIR}/${KS_APP_NAME}.desktop)
+configure_file(${KS_DIR}/build/deff.h.cmake
+               ${KS_DIR}/src/KsCmakeDef.hpp)
 
-configure_file( ${KS_DIR}/build/org.freedesktop.kshark-record.policy.cmake
-                ${KS_DIR}/org.freedesktop.kshark-record.policy)
+configure_file(${KS_DIR}/build/ks.desktop.cmake
+               ${KS_DIR}/${KS_APP_NAME}.desktop)
+
+configure_file(${KS_DIR}/build/org.freedesktop.kshark-record.policy.cmake
+               ${KS_DIR}/org.freedesktop.kshark-record.policy)
 
 message("")
diff --git a/build/cmake_clean.sh b/build/cmake_clean.sh
index b534014..2ca1136 100755
--- a/build/cmake_clean.sh
+++ b/build/cmake_clean.sh
@@ -3,7 +3,7 @@ rm CMakeCache.txt
 rm cmake_install.cmake
 rm Makefile
 rm CTestTestfile.cmake
-rm DartConfiguration.tcl
+rm -f DartConfiguration.tcl
 rm -rf CMakeFiles/
 rm -rf src/
 rm -rf examples/
diff --git a/build/deff.h.cmake b/build/deff.h.cmake
index 5584574..423a2fd 100644
--- a/build/deff.h.cmake
+++ b/build/deff.h.cmake
@@ -29,6 +29,9 @@
 /** Qt - old version detected. */
 #cmakedefine QT_VERSION_LESS_5_11
 
+/** Location of the KernelShark tests. */
+#cmakedefine KS_TEST_DIR "@KS_TEST_DIR@"
+
 /** Semicolon-separated list of plugin names. */
 #define KS_BUILTIN_PLUGINS "@PLUGINS@"
 
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 21d5b85..b308403 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -137,8 +137,6 @@ if (Qt5Widgets_FOUND AND Qt5Network_FOUND)
 endif (Qt5Widgets_FOUND AND Qt5Network_FOUND)
 
 add_subdirectory(plugins)
+set(PLUGINS ${PLUGINS} PARENT_SCOPE)
 
 find_program(DO_AS_ROOT pkexec)
-
-configure_file( ${KS_DIR}/build/deff.h.cmake
-                ${KS_DIR}/src/KsCmakeDef.hpp)
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 0847414..28f711b 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -1,18 +1,50 @@
 message("\n tests ...")
 
 set(EXECUTABLE_OUTPUT_PATH           ${KS_TEST_DIR})
+set(LIBRARY_OUTPUT_PATH              ${KS_TEST_DIR})
 
 add_executable(kshark-tests          libkshark-tests.cpp)
 target_include_directories(kshark-tests PRIVATE ${Boost_INCLUDE_DIRS})
 target_compile_definitions(kshark-tests PRIVATE "BOOST_TEST_DYN_LINK=1")
-target_link_libraries(kshark-tests   kshark-gui
+target_link_libraries(kshark-tests   kshark
                                      ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY})
 
 add_test(NAME              "get_test_data"
          COMMAND           ${KS_TEST_DIR}/get_test_data.sh
          WORKING_DIRECTORY ${KS_TEST_DIR})
 
+add_library(dummy_dpi             SHARED  test-plugin_dpi.c)
+set_target_properties(dummy_dpi   PROPERTIES PREFIX "plugin-")
+target_link_libraries(dummy_dpi   kshark)
+
+add_library(dummy_dpi_ctrl             SHARED  test-plugin_dpi_ctrl.c)
+set_target_properties(dummy_dpi_ctrl   PROPERTIES PREFIX "plugin-")
+target_link_libraries(dummy_dpi_ctrl   kshark)
+
+add_library(dummy_dpi_err             SHARED  test-plugin_dpi_err.c)
+set_target_properties(dummy_dpi_err   PROPERTIES PREFIX "plugin-")
+target_link_libraries(dummy_dpi_err   kshark)
+
+add_library(dummy_input             SHARED  test-input.c)
+set_target_properties(dummy_input   PROPERTIES PREFIX "input-")
+target_link_libraries(dummy_input   kshark)
+
+add_library(dummy_input_ctrl             SHARED  test-input_ctrl.c)
+set_target_properties(dummy_input_ctrl   PROPERTIES PREFIX "input-")
+target_link_libraries(dummy_input_ctrl   kshark)
+
 message(STATUS "libkshark-tests")
 add_test(NAME              "libkshark_tests"
          COMMAND           ${KS_TEST_DIR}/kshark-tests --log_format=HRF
          WORKING_DIRECTORY ${KS_TEST_DIR})
+
+add_executable(kshark-gui-tests          libkshark-gui-tests.cpp)
+target_include_directories(kshark-gui-tests PRIVATE ${Boost_INCLUDE_DIRS})
+target_compile_definitions(kshark-gui-tests PRIVATE "BOOST_TEST_DYN_LINK=1")
+target_link_libraries(kshark-gui-tests   kshark-gui
+                                     ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY})
+
+message(STATUS "libkshark-gui_tests")
+add_test(NAME              "libkshark-gui_tests"
+         COMMAND           ${KS_TEST_DIR}/kshark-gui-tests --log_format=HRF
+         WORKING_DIRECTORY ${KS_TEST_DIR})
diff --git a/tests/libkshark-gui-tests.cpp b/tests/libkshark-gui-tests.cpp
new file mode 100644
index 0000000..de6eb30
--- /dev/null
+++ b/tests/libkshark-gui-tests.cpp
@@ -0,0 +1,227 @@
+// SPDX-License-Identifier: LGPL-2.1
+
+/*
+ * Copyright (C) 2020 VMware Inc, Yordan Karadzhov (VMware) <y.karadz@gmail.com>
+ */
+
+// Boost
+#define BOOST_TEST_MODULE KernelSharkTests
+#include <boost/test/unit_test.hpp>
+
+// KernelShark
+#include "libkshark.h"
+#include "libkshark-plugin.h"
+#include "KsUtils.hpp"
+
+using namespace KsUtils;
+
+#define N_RECORDS_TEST1		1530
+
+BOOST_AUTO_TEST_CASE(KsUtils_datatest)
+{
+	kshark_context *kshark_ctx{nullptr};
+	kshark_entry **data{nullptr};
+	std::string file(KS_TEST_DIR);
+	ssize_t n_rows;
+	int sd, ss_id;
+
+	BOOST_REQUIRE(kshark_instance(&kshark_ctx));
+	file += "/trace_test1.dat";
+	sd = kshark_open(kshark_ctx, file.c_str());
+	BOOST_CHECK_EQUAL(sd, 0);
+
+	n_rows = kshark_load_entries(kshark_ctx, sd, &data);
+	BOOST_CHECK_EQUAL(n_rows, N_RECORDS_TEST1);
+
+	auto cpus = getCPUList(sd);
+	BOOST_CHECK_EQUAL(cpus.size(), 8);
+	for (int i = 0; i < cpus.size(); ++i)
+		BOOST_CHECK_EQUAL(cpus[i], i);
+
+	auto pids = getPidList(sd);
+	BOOST_CHECK_EQUAL(pids.size(), 46);
+	BOOST_CHECK_EQUAL(pids[0], 0);
+	for (int i = 1; i < pids.size(); ++i)
+		BOOST_CHECK(pids[i] > pids[i - 1]);
+
+	auto evts = getEventIdList(sd);
+	BOOST_CHECK_EQUAL(evts.size(), 40);
+	BOOST_CHECK_EQUAL(evts[34], 323);
+
+	ss_id = getEventId(sd, "sched/sched_switch");
+	BOOST_CHECK_EQUAL(ss_id, 323);
+
+	QString name = getEventName(sd, 323);
+	BOOST_CHECK(name == QString("sched/sched_switch"));
+	name = getEventName(sd, 999);
+	BOOST_CHECK(name == QString("Unknown"));
+
+	auto fields = getEventFieldsList(sd, ss_id);
+	BOOST_CHECK_EQUAL(fields.size(), 11);
+	BOOST_CHECK(fields[10] == QString("next_prio"));
+
+	BOOST_CHECK_EQUAL(getEventFieldType(sd, ss_id, "next_prio"),
+			  KS_INTEGER_FIELD);
+
+	BOOST_CHECK_EQUAL(getEventFieldType(sd, ss_id, "next_comm"),
+			  KS_INVALID_FIELD);
+
+	for (ssize_t r = 0; r < n_rows; ++r)
+		free(data[r]);
+	free(data);
+
+	kshark_close(kshark_ctx, sd);
+	kshark_free(kshark_ctx);
+}
+
+BOOST_AUTO_TEST_CASE(KsUtils_setFilterSync)
+{
+	struct kshark_context *kshark_ctx{nullptr};
+
+	BOOST_REQUIRE(kshark_instance(&kshark_ctx));
+	kshark_ctx->filter_mask = KS_TEXT_VIEW_FILTER_MASK |
+				  KS_GRAPH_VIEW_FILTER_MASK |
+				  KS_EVENT_VIEW_FILTER_MASK;
+
+	BOOST_CHECK_EQUAL(kshark_ctx->filter_mask, 0x7);
+
+	listFilterSync(false);
+	BOOST_CHECK_EQUAL(kshark_ctx->filter_mask & KS_TEXT_VIEW_FILTER_MASK, 0);
+	BOOST_CHECK_EQUAL(kshark_ctx->filter_mask & KS_GRAPH_VIEW_FILTER_MASK,
+			  KS_GRAPH_VIEW_FILTER_MASK);
+	BOOST_CHECK_EQUAL(kshark_ctx->filter_mask & KS_EVENT_VIEW_FILTER_MASK,
+			  KS_EVENT_VIEW_FILTER_MASK);
+	listFilterSync(true);
+	BOOST_CHECK_EQUAL(kshark_ctx->filter_mask, 0x7);
+
+	graphFilterSync(false);
+	BOOST_CHECK_EQUAL(kshark_ctx->filter_mask & KS_TEXT_VIEW_FILTER_MASK,
+			  KS_TEXT_VIEW_FILTER_MASK);
+	BOOST_CHECK_EQUAL(kshark_ctx->filter_mask & KS_GRAPH_VIEW_FILTER_MASK, 0);
+	BOOST_CHECK_EQUAL(kshark_ctx->filter_mask & KS_EVENT_VIEW_FILTER_MASK, 0);
+	graphFilterSync(true);
+	BOOST_CHECK_EQUAL(kshark_ctx->filter_mask, 0x7);
+
+	kshark_free(kshark_ctx);
+}
+
+BOOST_AUTO_TEST_CASE(KsUtils_parseIds)
+{
+	QVector<int> ids_test = parseIdList("1,33,4-6,3,55-57");
+	QVector<int> ids = {1, 33, 4, 5, 6, 3, 55, 56, 57};
+	BOOST_CHECK(ids == ids_test);
+}
+
+#define N_RECORDS_TEST2		73945
+BOOST_AUTO_TEST_CASE(KsUtils_KsDataStore)
+{
+	int64_t ts_last(0);
+	KsDataStore data;
+	int sd;
+
+	BOOST_CHECK_EQUAL(data.size(), 0);
+	BOOST_CHECK_EQUAL(data.rows(), nullptr);
+
+	sd = data.loadDataFile(QString(KS_TEST_DIR) + "/trace_test1.dat", {});
+	BOOST_CHECK_EQUAL(sd, 0);
+	BOOST_CHECK_EQUAL(data.size(), N_RECORDS_TEST1);
+	BOOST_CHECK(data.rows() != nullptr);
+
+	sd = data.appendDataFile(QString(KS_TEST_DIR) + "/trace_test2.dat", {});
+	BOOST_CHECK_EQUAL(sd, 1);
+	BOOST_CHECK_EQUAL(data.size(), N_RECORDS_TEST1 + N_RECORDS_TEST2);
+
+	kshark_entry **rows = data.rows();
+	for (ssize_t i = 0; i < data.size(); ++i) {
+		BOOST_CHECK(rows[i]->ts >= ts_last);
+		ts_last = rows[i]->ts;
+	}
+
+	data.clear();
+	BOOST_CHECK_EQUAL(data.size(), 0);
+	BOOST_CHECK_EQUAL(data.rows(), nullptr);
+}
+
+BOOST_AUTO_TEST_CASE(KsUtils_getPluginList)
+{
+	QStringList plugins{"sched_events"};
+
+	BOOST_CHECK(getPluginList() == plugins);
+}
+
+#define PLUGIN_1_LIB	"/plugin-dummy_dpi.so"
+#define PLUGIN_2_LIB	"/plugin-dummy_dpi_ctrl.so"
+#define INPUT_A_LIB	"/input-dummy_input.so"
+
+QString path(KS_TEST_DIR);
+
+BOOST_AUTO_TEST_CASE(KsUtils_KsPluginManager)
+{
+	struct kshark_context *kshark_ctx = NULL;
+	int sd, argc{0};
+	QCoreApplication a(argc, nullptr);
+
+	KsPluginManager pm;
+	pm.registerPlugins(path + INPUT_A_LIB);
+
+	kshark_instance(&kshark_ctx);
+	BOOST_CHECK_EQUAL(kshark_ctx->n_inputs, 1);
+	BOOST_CHECK(kshark_ctx->inputs != nullptr);
+
+	sd = kshark_add_stream(kshark_ctx);
+	BOOST_CHECK_EQUAL(sd, 0);
+	kshark_ctx->stream[sd]->interface =
+		malloc(1);
+
+	sd = kshark_add_stream(kshark_ctx);
+	BOOST_CHECK_EQUAL(sd, 1);
+	kshark_ctx->stream[sd]->interface = malloc(1);
+
+	pm.registerPluginToStream("sched_events",
+				  getStreamIdList(kshark_ctx));
+
+	QStringList list = pm.getStreamPluginList(sd);
+	BOOST_CHECK_EQUAL(list.count(), 1);
+	BOOST_CHECK(list[0] == "sched_events");
+
+	QString testPlugins = path + PLUGIN_1_LIB + ",";
+	testPlugins += path + PLUGIN_2_LIB;
+	pm.registerPlugins(testPlugins);
+	auto listTest = pm.getUserPlugins();
+	BOOST_CHECK_EQUAL(listTest.count(), 3);
+	list = pm.getStreamPluginList(sd);
+
+	for (auto const &p: listTest)
+		pm.registerPluginToStream(p->name, {sd});
+
+	list = pm.getStreamPluginList(sd);
+	BOOST_CHECK_EQUAL(list.count(), 3);
+
+	BOOST_CHECK(list == QStringList({"dummy_dpi_ctrl",
+					 "dummy_dpi",
+					 "sched_events"}));
+
+	auto active = pm.getActivePlugins(sd);
+	BOOST_CHECK(pm.getActivePlugins(sd) == QVector<int>({1, 1, 1}));
+
+	auto enabled = pm.getPluginsByStatus(sd, KSHARK_PLUGIN_ENABLED);
+	BOOST_CHECK(enabled == QVector<int>({0, 1, 2}));
+	auto loaded = pm.getPluginsByStatus(sd, KSHARK_PLUGIN_LOADED);
+	BOOST_CHECK(loaded == QVector<int>({0, 1}));
+	auto failed = pm.getPluginsByStatus(sd, KSHARK_PLUGIN_FAILED);
+	BOOST_CHECK(failed == QVector<int>({2}));
+
+	active[1] = 0;
+	pm.updatePlugins(sd, active);
+	BOOST_CHECK(active == pm.getActivePlugins(sd));
+
+	enabled = pm.getPluginsByStatus(sd, KSHARK_PLUGIN_ENABLED);
+	BOOST_CHECK(enabled == QVector<int>({0, 2}));
+	loaded = pm.getPluginsByStatus(sd, KSHARK_PLUGIN_LOADED);
+	BOOST_CHECK(loaded == QVector<int>({0}));
+	failed = pm.getPluginsByStatus(sd, KSHARK_PLUGIN_FAILED);
+	BOOST_CHECK(failed == QVector<int>({2}));
+
+	kshark_free(kshark_ctx);
+	a.exit();
+}
diff --git a/tests/libkshark-tests.cpp b/tests/libkshark-tests.cpp
index eb5cb1f..a22c1e5 100644
--- a/tests/libkshark-tests.cpp
+++ b/tests/libkshark-tests.cpp
@@ -11,6 +11,7 @@
 // KernelShark
 #include "libkshark.h"
 #include "libkshark-plugin.h"
+#include "KsCmakeDef.hpp"
 
 #define N_TEST_STREAMS	1000
 
@@ -19,7 +20,7 @@ BOOST_AUTO_TEST_CASE(add_remove_streams)
 	struct kshark_context *kshark_ctx = NULL;
 	int sd, free = 0, i;
 
-	kshark_instance(&kshark_ctx);
+	BOOST_REQUIRE(kshark_instance(&kshark_ctx));
 
 	for (i = 0; i < N_TEST_STREAMS; ++i) {
 		sd = kshark_add_stream(kshark_ctx);
@@ -45,10 +46,46 @@ BOOST_AUTO_TEST_CASE(add_remove_streams)
 	BOOST_CHECK_EQUAL(kshark_ctx->stream_info.array_size, INT16_MAX + 1);
 	BOOST_CHECK_EQUAL(sd, -ENODEV);
 
-	kshark_close_all(kshark_ctx);
 	kshark_free(kshark_ctx);
 }
 
+BOOST_AUTO_TEST_CASE(get_stream)
+{
+	kshark_context *kshark_ctx(nullptr);
+	kshark_data_stream *stream;
+	int sd;
+
+	BOOST_REQUIRE(kshark_instance(&kshark_ctx));
+	sd = kshark_add_stream(kshark_ctx);
+	stream = kshark_get_data_stream(kshark_ctx, sd);
+	BOOST_CHECK_EQUAL(stream, nullptr);
+
+	kshark_ctx->stream[sd]->interface = malloc(1);
+	stream = kshark_get_data_stream(kshark_ctx, sd);
+	BOOST_CHECK(stream != nullptr);
+
+	kshark_free(kshark_ctx);
+}
+
+BOOST_AUTO_TEST_CASE(close_all)
+{
+	struct kshark_context *kshark_ctx(nullptr);
+	int sd, i;
+
+	BOOST_REQUIRE(kshark_instance(&kshark_ctx));
+	for (i = 0; i < N_TEST_STREAMS; ++i) {
+		sd = kshark_add_stream(kshark_ctx);
+		BOOST_CHECK_EQUAL(sd, i);
+	}
+
+	kshark_close_all(kshark_ctx);
+	BOOST_CHECK_EQUAL(kshark_ctx->n_streams, 0);
+	BOOST_CHECK_EQUAL(kshark_ctx->stream_info.next_free_stream_id, 0);
+	BOOST_CHECK_EQUAL(kshark_ctx->stream_info.max_stream_id, -1);
+	for (i = 0; i < kshark_ctx->stream_info.array_size; ++i)
+		BOOST_CHECK_EQUAL(kshark_ctx->stream[i], nullptr);
+}
+
 #define ARRAY_DEFAULT_SIZE	1000
 BOOST_AUTO_TEST_CASE(doule_size_macro)
 {
@@ -90,7 +127,7 @@ BOOST_AUTO_TEST_CASE(fill_data_container)
 	kshark_data_container_sort(data);
 	BOOST_CHECK_EQUAL(data->capacity, N_VALUES);
 	for (i = 0; i < N_VALUES; ++i) {
-		BOOST_REQUIRE(data->data[i]->entry->ts >= ts_last);
+		BOOST_CHECK(data->data[i]->entry->ts >= ts_last);
 		BOOST_CHECK_EQUAL(data->data[i]->entry->ts,
 				  10 - data->data[i]->field);
 
@@ -100,8 +137,8 @@ BOOST_AUTO_TEST_CASE(fill_data_container)
 	i = kshark_find_entry_field_by_time(MAX_TS / 2, data->data,
 					    0, N_VALUES - 1);
 
-	BOOST_REQUIRE(data->data[i - 1]->entry->ts < MAX_TS / 2);
-	BOOST_REQUIRE(data->data[i]->entry->ts >= MAX_TS / 2);
+	BOOST_CHECK(data->data[i - 1]->entry->ts < MAX_TS / 2);
+	BOOST_CHECK(data->data[i]->entry->ts >= MAX_TS / 2);
 
 	kshark_free_data_container(data);
 }
@@ -136,3 +173,265 @@ BOOST_AUTO_TEST_CASE(init_close_plugin)
 
 	__close(-1);
 }
+
+#define PLUGIN_1_LIB	"/plugin-dummy_dpi.so"
+#define PLUGIN_1_NAME	"dummy_dpi"
+
+#define PLUGIN_2_LIB	"/plugin-dummy_dpi_ctrl.so"
+#define PLUGIN_2_NAME	"dummy_dpi_ctrl"
+
+#define INPUT_A_LIB	"/input-dummy_input.so"
+#define INPUT_A_NAME	"dummy_input"
+
+#define INPUT_B_LIB	"/input-dummy_input_ctrl.so"
+#define INPUT_B_NAME	"dummy_input_ctrl"
+
+std::string path(KS_TEST_DIR);
+
+BOOST_AUTO_TEST_CASE(register_plugin)
+{
+	kshark_plugin_list *p1, *p2, *i1, *i2, *x1, *x2;
+	kshark_generic_stream_interface *interface;
+	kshark_context *kshark_ctx(nullptr);
+	kshark_data_stream *stream;
+	std::string plugin;
+	int sd;
+
+	BOOST_REQUIRE(kshark_instance(&kshark_ctx));
+	BOOST_REQUIRE(kshark_ctx->plugins == nullptr);
+	BOOST_REQUIRE(kshark_ctx->inputs == nullptr);
+	BOOST_CHECK_EQUAL(kshark_ctx->n_plugins, 0);
+
+	plugin = path + PLUGIN_1_LIB;
+	p1 = kshark_register_plugin(kshark_ctx, PLUGIN_1_NAME, plugin.c_str());
+	BOOST_CHECK_EQUAL(kshark_ctx->n_plugins, 1);
+	BOOST_CHECK(kshark_ctx->plugins != nullptr);
+	BOOST_CHECK_EQUAL(kshark_ctx->plugins->next, nullptr);
+	BOOST_CHECK_EQUAL(kshark_ctx->plugins, p1);
+	BOOST_CHECK(p1 != nullptr);
+	BOOST_CHECK(p1->process_interface != nullptr);
+	BOOST_CHECK(p1->handle != nullptr);
+	BOOST_CHECK_EQUAL(strcmp(p1->file, plugin.c_str()), 0);
+	BOOST_CHECK_EQUAL(strcmp(p1->name, PLUGIN_1_NAME), 0);
+
+	BOOST_CHECK_EQUAL(p1->ctrl_interface, nullptr);
+	BOOST_CHECK_EQUAL(p1->readout_interface, nullptr);
+
+	plugin = path + PLUGIN_2_LIB;
+	p2 = kshark_register_plugin(kshark_ctx, PLUGIN_2_NAME, plugin.c_str());
+	BOOST_CHECK_EQUAL(kshark_ctx->n_plugins, 2);
+	BOOST_CHECK_EQUAL(kshark_ctx->plugins, p2);
+	BOOST_CHECK_EQUAL(kshark_ctx->plugins->next, p1);
+	BOOST_CHECK(p2 != nullptr);
+	BOOST_CHECK(p2->process_interface != nullptr);
+	BOOST_CHECK(p2->handle != nullptr);
+	BOOST_CHECK_EQUAL(strcmp(p2->file, plugin.c_str()), 0);
+	BOOST_CHECK_EQUAL(strcmp(p2->name, PLUGIN_2_NAME), 0);
+	BOOST_CHECK(p2->ctrl_interface != nullptr);
+
+	BOOST_CHECK_EQUAL(p2->readout_interface, nullptr);
+
+	plugin = path + INPUT_A_LIB;
+	i1 = kshark_register_plugin(kshark_ctx, INPUT_A_NAME, plugin.c_str());
+	BOOST_CHECK(i1 != nullptr);
+	BOOST_CHECK_EQUAL(kshark_ctx->n_plugins, 3);
+	BOOST_CHECK_EQUAL(kshark_ctx->n_inputs, 1);
+	BOOST_CHECK(kshark_ctx->inputs != nullptr);
+	BOOST_CHECK(i1->readout_interface != nullptr);
+	BOOST_CHECK(i1->handle != nullptr);
+	BOOST_CHECK_EQUAL(strcmp(i1->file, plugin.c_str()), 0);
+	BOOST_CHECK_EQUAL(strcmp(i1->name, INPUT_A_NAME), 0);
+
+	BOOST_CHECK_EQUAL(i1->ctrl_interface, nullptr);
+	BOOST_CHECK_EQUAL(i1->process_interface, nullptr);
+
+	plugin = path + INPUT_B_LIB;
+	i2 = kshark_register_plugin(kshark_ctx, INPUT_B_NAME, plugin.c_str());
+	BOOST_CHECK(i2 != nullptr);
+	BOOST_CHECK_EQUAL(kshark_ctx->n_plugins, 4);
+	BOOST_CHECK_EQUAL(kshark_ctx->n_inputs, 2);
+	BOOST_CHECK(i2->readout_interface != nullptr);
+	BOOST_CHECK(i2->handle != nullptr);
+	BOOST_CHECK(strcmp(i2->file, plugin.c_str()) == 0);
+	BOOST_CHECK(strcmp(i2->name, INPUT_B_NAME) == 0);
+	BOOST_CHECK(i2->ctrl_interface != nullptr);
+
+	BOOST_CHECK_EQUAL(i2->process_interface, nullptr);
+
+	x1 = kshark_find_plugin_by_name(kshark_ctx->plugins, PLUGIN_2_NAME);
+	BOOST_CHECK_EQUAL(x1, p2);
+
+	plugin = path + PLUGIN_2_LIB;
+	x2 = kshark_find_plugin(kshark_ctx->plugins, plugin.c_str());
+
+	BOOST_CHECK_EQUAL(x2, p2);
+
+	sd = kshark_add_stream(kshark_ctx);
+	interface =
+		(kshark_generic_stream_interface *) malloc(sizeof(*interface));
+	kshark_ctx->stream[sd]->interface = interface;
+	BOOST_CHECK_EQUAL(sd, 0);
+
+	stream = kshark_get_data_stream(kshark_ctx, sd);
+	BOOST_CHECK(stream != nullptr);
+
+	BOOST_CHECK_EQUAL(stream->plugins, nullptr);
+	kshark_register_plugin_to_stream(stream,
+					 p1->process_interface,
+					 true);
+	BOOST_CHECK_EQUAL(stream->n_plugins, 1);
+	BOOST_CHECK_EQUAL(stream->plugins->interface, p1->process_interface);
+	BOOST_CHECK_EQUAL(stream->plugins->next, nullptr);
+
+	kshark_register_plugin_to_stream(stream,
+					 p2->process_interface,
+					 true);
+	BOOST_CHECK_EQUAL(stream->n_plugins, 2);
+	BOOST_CHECK_EQUAL(stream->plugins->interface, p2->process_interface);
+	BOOST_CHECK_EQUAL(stream->plugins->next->interface, p1->process_interface);
+
+	kshark_unregister_plugin_from_stream(stream, p1->process_interface);
+	BOOST_CHECK_EQUAL(stream->n_plugins, 1);
+	BOOST_CHECK_EQUAL(stream->plugins->interface, p2->process_interface);
+	BOOST_CHECK_EQUAL(stream->plugins->next, nullptr);
+
+	kshark_free(kshark_ctx);
+}
+
+#define PLUGIN_ERR_LIB	"/plugin-dummy_dpi_err.so"
+#define PLUGIN_ERR_NAME	"dummy_dpi_err"
+
+BOOST_AUTO_TEST_CASE(handle_plugin)
+{
+	kshark_dpi_list *dpi1, *dpi2, *dpi_err;
+	kshark_plugin_list *p1, *p2, *p_err;
+	kshark_context *kshark_ctx(nullptr);
+	kshark_data_stream *stream;
+	std::string plugin;
+	int sd, ret;
+
+	BOOST_REQUIRE(kshark_instance(&kshark_ctx));
+	BOOST_CHECK_EQUAL(kshark_ctx->plugins, nullptr);
+	BOOST_CHECK_EQUAL(kshark_ctx->n_plugins, 0);
+
+	plugin = path + PLUGIN_1_LIB;
+	p1 = kshark_register_plugin(kshark_ctx, PLUGIN_1_NAME, plugin.c_str());
+
+	plugin = path + PLUGIN_2_LIB;
+	p2 = kshark_register_plugin(kshark_ctx, PLUGIN_2_NAME, plugin.c_str());
+	BOOST_CHECK(kshark_ctx->plugins != nullptr);
+	BOOST_CHECK_EQUAL(kshark_ctx->n_plugins, 2);
+
+	sd = kshark_add_stream(kshark_ctx);
+	kshark_ctx->stream[sd]->interface = malloc(1);
+	stream = kshark_get_data_stream(kshark_ctx, sd);
+	BOOST_CHECK(stream != nullptr);
+
+	dpi1 = kshark_register_plugin_to_stream(stream,
+						p1->process_interface,
+						true);
+	BOOST_CHECK_EQUAL(dpi1->status, KSHARK_PLUGIN_ENABLED);
+
+	dpi2 = kshark_register_plugin_to_stream(stream,
+						p2->process_interface,
+						false);
+	BOOST_CHECK_EQUAL(dpi2->status, 0);
+
+	ret = kshark_handle_dpi(stream, dpi1, KSHARK_PLUGIN_INIT);
+	BOOST_CHECK_EQUAL(ret, 1);
+	BOOST_CHECK_EQUAL(dpi1->status,
+			  KSHARK_PLUGIN_LOADED | KSHARK_PLUGIN_ENABLED);
+
+	ret = kshark_handle_dpi(stream, dpi2, KSHARK_PLUGIN_INIT);
+	BOOST_CHECK_EQUAL(ret, 0);
+	BOOST_CHECK_EQUAL(dpi2->status, 0);
+
+	dpi2->status |= KSHARK_PLUGIN_ENABLED;
+	ret = kshark_handle_dpi(stream, dpi2, KSHARK_PLUGIN_INIT);
+	BOOST_CHECK_EQUAL(ret, 2);
+	BOOST_CHECK_EQUAL(dpi1->status,
+			  KSHARK_PLUGIN_LOADED | KSHARK_PLUGIN_ENABLED);
+
+	ret = kshark_handle_all_dpis(stream, KSHARK_PLUGIN_UPDATE);
+	BOOST_CHECK_EQUAL(ret, 0);
+	BOOST_CHECK_EQUAL(dpi1->status,
+			  KSHARK_PLUGIN_LOADED | KSHARK_PLUGIN_ENABLED);
+	BOOST_CHECK_EQUAL(dpi2->status,
+			  KSHARK_PLUGIN_LOADED | KSHARK_PLUGIN_ENABLED);
+
+	plugin = path + PLUGIN_ERR_LIB;
+	p_err = kshark_register_plugin(kshark_ctx, PLUGIN_ERR_NAME,
+				       plugin.c_str());
+	BOOST_CHECK_EQUAL(kshark_ctx->n_plugins, 3);
+	dpi_err = kshark_register_plugin_to_stream(stream,
+						   p_err->process_interface,
+						   true);
+	BOOST_CHECK_EQUAL(ret, 0);
+	ret = kshark_handle_dpi(stream, dpi_err, KSHARK_PLUGIN_INIT);
+	BOOST_CHECK_EQUAL(dpi_err->status,
+			  KSHARK_PLUGIN_FAILED | KSHARK_PLUGIN_ENABLED);
+	BOOST_CHECK_EQUAL(ret, 0);
+	ret = kshark_handle_dpi(stream, dpi_err, KSHARK_PLUGIN_CLOSE);
+	BOOST_CHECK_EQUAL(ret, 0);
+	BOOST_CHECK_EQUAL(dpi_err->status, KSHARK_PLUGIN_ENABLED);
+
+	ret = kshark_handle_all_dpis(stream, KSHARK_PLUGIN_CLOSE);
+	BOOST_CHECK_EQUAL(ret, -3);
+
+	kshark_free(kshark_ctx);
+}
+
+#define FAKE_DATA_FILE_A	"test.ta"
+#define FAKE_DATA_A_SIZE	200
+
+#define FAKE_DATA_FILE_B	"test.tb"
+#define FAKE_DATA_B_SIZE	100
+
+BOOST_AUTO_TEST_CASE(readout_plugins)
+{
+	kshark_context *kshark_ctx(nullptr);
+	kshark_entry **entries{nullptr};
+	kshark_data_stream *stream;
+	std::string plugin, data;
+	int sd, i, n_entries;
+	int64_t ts_last(0);
+
+	BOOST_REQUIRE(kshark_instance(&kshark_ctx));
+
+	plugin = path + INPUT_A_LIB;
+	kshark_register_plugin(kshark_ctx, INPUT_A_NAME, plugin.c_str());
+	plugin = path + INPUT_B_LIB;
+	kshark_register_plugin(kshark_ctx, INPUT_B_NAME, plugin.c_str());
+
+	data = FAKE_DATA_FILE_A;
+	sd = kshark_open(kshark_ctx, data.c_str());
+	BOOST_CHECK_EQUAL(sd, 0);
+
+	stream = kshark_get_data_stream(kshark_ctx, sd);
+	BOOST_CHECK(stream != nullptr);
+	BOOST_CHECK(stream->interface != nullptr);
+	BOOST_CHECK_EQUAL(strcmp(stream->data_format, "format_a"), 0);
+
+	data = FAKE_DATA_FILE_B;
+	sd = kshark_open(kshark_ctx, data.c_str());
+	BOOST_CHECK_EQUAL(sd, 1);
+
+	stream = kshark_get_data_stream(kshark_ctx, sd);
+	BOOST_CHECK(stream != nullptr);
+	BOOST_CHECK(stream->interface != nullptr);
+	BOOST_CHECK_EQUAL(strcmp(stream->data_format, "format_b"), 0);
+
+	n_entries = kshark_load_all_entries(kshark_ctx, &entries);
+	BOOST_CHECK_EQUAL(n_entries, FAKE_DATA_A_SIZE + FAKE_DATA_B_SIZE);
+
+	for (i = 0; i < n_entries; ++i) {
+		BOOST_CHECK(ts_last <= entries[i]->ts);
+		ts_last = entries[i]->ts;
+	}
+
+	for (i = 0; i < n_entries; ++i)
+		free(entries[i]);
+	free(entries);
+
+	kshark_free(kshark_ctx);
+}
diff --git a/tests/test-input.c b/tests/test-input.c
new file mode 100644
index 0000000..31620b9
--- /dev/null
+++ b/tests/test-input.c
@@ -0,0 +1,134 @@
+
+// C
+#ifndef _GNU_SOURCE
+/** Use GNU C Library. */
+#define _GNU_SOURCE
+#endif // _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+// KernelShark
+#include "libkshark.h"
+#include "libkshark-plugin.h"
+
+static ssize_t load_entries(struct kshark_data_stream *stream,
+			    struct kshark_context *kshark_ctx,
+			    struct kshark_entry ***data_rows)
+{
+	struct kshark_entry **rows;
+	ssize_t total = 200, i;
+
+	rows = calloc(total, sizeof(struct kshark_entry *));
+	for (i = 0; i < total; ++i) {
+		rows[i] = calloc(1, sizeof(struct kshark_entry));
+		rows[i]->ts = 1000000 + i * 10000;
+		rows[i]->stream_id = stream->stream_id;
+		rows[i]->event_id = i % 5;
+		rows[i]->pid = 10 + i % 2;
+		rows[i]->cpu = i % 2;
+		rows[i]->visible = 0xff;
+	}
+
+	*data_rows = rows;
+	return total;
+}
+
+static char *dump_entry(struct kshark_data_stream *stream,
+			const struct kshark_entry *entry)
+{
+	char *entry_str;
+	int ret;
+
+	ret = asprintf(&entry_str, "e: time=%li evt=%i s_id=%i", entry->ts,
+								 entry->event_id,
+								 entry->stream_id);
+
+	if (ret <= 0)
+		return NULL;
+
+	return entry_str;
+}
+
+static const char *format_name = "format_a";
+
+const char *KSHARK_INPUT_FORMAT()
+{
+	return format_name;
+}
+
+bool KSHARK_INPUT_CHECK(const char *file, char **format)
+{
+	char *ext = strrchr(file, '.');
+
+	if (ext && strcmp(ext, ".ta") == 0)
+		return true;
+
+	return false;
+}
+
+static const int get_pid(struct kshark_data_stream *stream,
+			 const struct kshark_entry *entry)
+{
+	return entry->pid;
+}
+
+static char *get_task(struct kshark_data_stream *stream,
+		      const struct kshark_entry *entry)
+{
+	char *entry_str;
+	int ret;
+
+	ret = asprintf(&entry_str, "test_a/test");
+
+	if (ret <= 0)
+		return NULL;
+
+	return entry_str;
+}
+
+static char *get_event_name(struct kshark_data_stream *stream,
+			    const struct kshark_entry *entry)
+{
+	char *evt_str;
+	int ret;
+
+	ret = asprintf(&evt_str, "test_a/event-%i", entry->event_id);
+
+	if (ret <= 0)
+		return NULL;
+
+	return evt_str;
+}
+
+int KSHARK_INPUT_INITIALIZER(struct kshark_data_stream *stream)
+{
+	struct kshark_generic_stream_interface *interface;
+
+	stream->interface = interface = calloc(1, sizeof(*interface));
+	if (!interface)
+		return -ENOMEM;
+
+	interface->type = KS_GENERIC_DATA_INTERFACE;
+
+	stream->n_cpus = 2;
+	stream->n_events = 5;
+	stream->idle_pid = 0;
+
+	kshark_hash_id_add(stream->tasks, 10);
+	kshark_hash_id_add(stream->tasks, 11);
+
+	interface->get_pid = get_pid;
+	interface->get_task = get_task;
+	interface->get_event_name = get_event_name;
+
+	interface->dump_entry = dump_entry;
+	interface->load_entries = load_entries;
+
+	return 0;
+}
+
+void KSHARK_INPUT_DEINITIALIZER(struct kshark_data_stream *stream)
+{}
diff --git a/tests/test-input_ctrl.c b/tests/test-input_ctrl.c
new file mode 100644
index 0000000..3dcc92e
--- /dev/null
+++ b/tests/test-input_ctrl.c
@@ -0,0 +1,140 @@
+
+// C
+#ifndef _GNU_SOURCE
+/** Use GNU C Library. */
+#define _GNU_SOURCE
+#endif // _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+// KernelShark
+#include "libkshark.h"
+#include "libkshark-plugin.h"
+
+static ssize_t load_entries(struct kshark_data_stream *stream,
+			    struct kshark_context *kshark_ctx,
+			    struct kshark_entry ***data_rows)
+{
+	struct kshark_entry **rows;
+	ssize_t total = 100, i;
+
+	rows = calloc(total, sizeof(struct kshark_entry *));
+	for (i = 0; i < total; ++i) {
+		rows[i] = calloc(1, sizeof(struct kshark_entry));
+		rows[i]->ts = 1000 + i * 15000;
+		rows[i]->stream_id = stream->stream_id;
+		rows[i]->event_id = i % 3;
+		rows[i]->pid = 20;
+		rows[i]->visible = 0xff;
+	}
+
+	rows[i-1]->pid = 0;
+
+	*data_rows = rows;
+	return total;
+}
+
+static char *dump_entry(struct kshark_data_stream *stream,
+			const struct kshark_entry *entry)
+{
+	char *entry_str;
+	int ret;
+
+	ret = asprintf(&entry_str, "e: time=%li evt=%i s_id=%i", entry->ts,
+								 entry->event_id,
+								 entry->stream_id);
+
+	if (ret <= 0)
+		return NULL;
+
+	return entry_str;
+}
+
+static const char *format_name = "format_b";
+// static const char *format_name = "tep data";
+
+const char *KSHARK_INPUT_FORMAT()
+{
+	return format_name;
+}
+
+bool KSHARK_INPUT_CHECK(const char *file, char **format)
+{
+	char *ext = strrchr(file, '.');
+
+	if (ext && strcmp(ext, ".tb") == 0)
+		return true;
+
+	return false;
+}
+
+static const int get_pid(struct kshark_data_stream *stream,
+			 const struct kshark_entry *entry)
+{
+	return entry->pid;
+}
+
+static char *get_task(struct kshark_data_stream *stream,
+		      const struct kshark_entry *entry)
+{
+	char *entry_str;
+	int ret;
+
+	ret = asprintf(&entry_str, "test_b/test");
+
+	if (ret <= 0)
+		return NULL;
+
+	return entry_str;
+}
+
+static char *get_event_name(struct kshark_data_stream *stream,
+			    const struct kshark_entry *entry)
+{
+	char *evt_str;
+	int ret;
+
+	ret = asprintf(&evt_str, "test_b/event-%i", entry->event_id);
+
+	if (ret <= 0)
+		return NULL;
+
+	return evt_str;
+}
+
+int KSHARK_INPUT_INITIALIZER(struct kshark_data_stream *stream)
+{
+	struct kshark_generic_stream_interface *interface;
+
+	stream->interface = interface = calloc(1, sizeof(*interface));
+	if (!interface)
+		return -ENOMEM;
+
+	interface->type = KS_GENERIC_DATA_INTERFACE;
+
+	stream->n_cpus = 1;
+	stream->n_events = 3;
+	stream->idle_pid = 0;
+
+	kshark_hash_id_add(stream->tasks, 20);
+
+	interface->get_pid = get_pid;
+	interface->get_task = get_task;
+	interface->get_event_name = get_event_name;
+	interface->dump_entry = dump_entry;
+	interface->load_entries = load_entries;
+
+	return 0;
+}
+
+void KSHARK_INPUT_DEINITIALIZER(struct kshark_data_stream *stream)
+{}
+
+/** Initialize the control interface of the plugin. */
+void *KSHARK_MENU_PLUGIN_INITIALIZER(void *ptr)
+{
+	return NULL;
+}
diff --git a/tests/test-plugin_dpi.c b/tests/test-plugin_dpi.c
new file mode 100644
index 0000000..82f94f3
--- /dev/null
+++ b/tests/test-plugin_dpi.c
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: LGPL-2.1
+
+/*
+ * Copyright (C) 2021 VMware Inc, Yordan Karadzhov <ykaradzhov@vmware.com>
+ */
+
+// C
+#include <stdio.h>
+
+// KernelShark
+#include "libkshark.h"
+#include "libkshark-plugin.h"
+
+/** Load this plugin. */
+int KSHARK_PLOT_PLUGIN_INITIALIZER(struct kshark_data_stream *stream)
+{
+	printf("--> plugin1\n");
+	return 1;
+}
+
+/** Unload this plugin. */
+int KSHARK_PLOT_PLUGIN_DEINITIALIZER(struct kshark_data_stream *stream)
+{
+	printf("<-- plugin1\n");
+	return 1;
+}
diff --git a/tests/test-plugin_dpi_ctrl.c b/tests/test-plugin_dpi_ctrl.c
new file mode 100644
index 0000000..5fafd1d
--- /dev/null
+++ b/tests/test-plugin_dpi_ctrl.c
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: LGPL-2.1
+
+/*
+ * Copyright (C) 2021 VMware Inc, Yordan Karadzhov <ykaradzhov@vmware.com>
+ */
+
+// C
+#include <stdio.h>
+
+// KernelShark
+#include "libkshark.h"
+#include "libkshark-plugin.h"
+
+/** Load this plugin. */
+int KSHARK_PLOT_PLUGIN_INITIALIZER(struct kshark_data_stream *stream)
+{
+	printf("--> plugin2\n");
+	return 2;
+}
+
+/** Unload this plugin. */
+int KSHARK_PLOT_PLUGIN_DEINITIALIZER(struct kshark_data_stream *stream)
+{
+	printf("<-- plugin2\n");
+	return 2;
+}
+
+/** Initialize the control interface of the plugin. */
+void *KSHARK_MENU_PLUGIN_INITIALIZER(void *ptr)
+{
+	return NULL;
+}
diff --git a/tests/test-plugin_dpi_err.c b/tests/test-plugin_dpi_err.c
new file mode 100644
index 0000000..4148930
--- /dev/null
+++ b/tests/test-plugin_dpi_err.c
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: LGPL-2.1
+
+/*
+ * Copyright (C) 2021 VMware Inc, Yordan Karadzhov <ykaradzhov@vmware.com>
+ */
+
+// C
+#include <stdio.h>
+
+// KernelShark
+#include "libkshark.h"
+#include "libkshark-plugin.h"
+
+/** Load this plugin. */
+int KSHARK_PLOT_PLUGIN_INITIALIZER(struct kshark_data_stream *stream)
+{
+	printf("--> plugin_err\n");
+	return 0;
+}
+
+/** Unload this plugin. */
+int KSHARK_PLOT_PLUGIN_DEINITIALIZER(struct kshark_data_stream *stream)
+{
+	printf("<-- plugin_err\n");
+	return 0;
+}
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v2 10/27] kernel-shark: Add model tests
  2021-02-11 10:31 [PATCH v2 00/27] Complete the KernelShark v2 transformation Yordan Karadzhov (VMware)
                   ` (8 preceding siblings ...)
  2021-02-11 10:31 ` [PATCH v2 09/27] kernel-shark: Add plugin tests Yordan Karadzhov (VMware)
@ 2021-02-11 10:31 ` Yordan Karadzhov (VMware)
  2021-02-11 10:31 ` [PATCH v2 11/27] kernel-shark: Update KsWidgetsLib Yordan Karadzhov (VMware)
                   ` (17 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Yordan Karadzhov (VMware) @ 2021-02-11 10:31 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

We add few simple test cases of the KsModel functionalities.

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 tests/libkshark-gui-tests.cpp | 59 +++++++++++++++++++++++++++++++++++
 1 file changed, 59 insertions(+)

diff --git a/tests/libkshark-gui-tests.cpp b/tests/libkshark-gui-tests.cpp
index de6eb30..dedd69f 100644
--- a/tests/libkshark-gui-tests.cpp
+++ b/tests/libkshark-gui-tests.cpp
@@ -12,6 +12,8 @@
 #include "libkshark.h"
 #include "libkshark-plugin.h"
 #include "KsUtils.hpp"
+#include "KsModels.hpp"
+
 
 using namespace KsUtils;
 
@@ -225,3 +227,60 @@ BOOST_AUTO_TEST_CASE(KsUtils_KsPluginManager)
 	kshark_free(kshark_ctx);
 	a.exit();
 }
+
+BOOST_AUTO_TEST_CASE(ViewModel)
+{
+	QStringList header{"#", "CPU", "Time Stamp", "Task", "PID", "Latency", "Event", "Info"};
+	struct kshark_context *kshark_ctx(nullptr);
+	KsViewModel model;
+	KsDataStore data;
+
+	data.loadDataFile(QString(KS_TEST_DIR) + "/trace_test1.dat", {});
+	model.fill(&data);
+	BOOST_CHECK_EQUAL(model.rowCount({}), N_RECORDS_TEST1);
+	BOOST_CHECK_EQUAL(model.columnCount({}), 8);
+	BOOST_CHECK_EQUAL(model.singleStream(), true);
+	BOOST_CHECK(model.header() == header);
+
+	data.appendDataFile(QString(KS_TEST_DIR) + "/trace_test2.dat", {});
+	BOOST_REQUIRE(kshark_instance(&kshark_ctx));
+	BOOST_CHECK(getStreamIdList(kshark_ctx) == QVector<int>({0, 1}));
+
+	model.update(&data);
+	header = QStringList{" >> "} + header;
+
+	BOOST_CHECK_EQUAL(model.rowCount({}), N_RECORDS_TEST1 + N_RECORDS_TEST2);
+	BOOST_CHECK_EQUAL(model.columnCount({}), 9);
+	BOOST_CHECK_EQUAL(model.singleStream(), false);
+	BOOST_CHECK(model.header() == header);
+
+	BOOST_CHECK(model.getValueStr(0, 0) == "1");
+	BOOST_CHECK(model.getValueStr(4, 1) == "trace-cmd");
+	BOOST_CHECK(model.getValueStr(5, 2) == "29474");
+	BOOST_CHECK(model.getValueStr(7, 2) == "sched/sched_switch");
+
+	BOOST_CHECK(model.getValueStr(0, N_RECORDS_TEST1 + N_RECORDS_TEST2 - 1) == "0");
+	BOOST_CHECK(model.getValueStr(4, N_RECORDS_TEST1 + N_RECORDS_TEST2 - 1) == "<idle>");
+
+	model.reset();
+	BOOST_CHECK_EQUAL(model.rowCount({}), 0);
+}
+
+BOOST_AUTO_TEST_CASE(GraphModel)
+{
+	struct kshark_context *kshark_ctx(nullptr);
+	KsGraphModel model;
+	KsDataStore data;
+
+	data.loadDataFile(QString(KS_TEST_DIR) + "/trace_test1.dat", {});
+	BOOST_REQUIRE(kshark_instance(&kshark_ctx));
+	BOOST_CHECK(getStreamIdList(kshark_ctx) == QVector<int>({0}));
+
+	model.fill(&data);
+	BOOST_CHECK_EQUAL(model.rowCount({}), KS_DEFAULT_NBUNS);
+	BOOST_CHECK(abs(model.histo()->min - data.rows()[0]->ts) < model.histo()->bin_size);
+	BOOST_CHECK(abs(model.histo()->max - data.rows()[N_RECORDS_TEST1 - 1]->ts) < model.histo()->bin_size);
+
+	model.reset();
+	BOOST_CHECK_EQUAL(model.rowCount({}), 0);
+}
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v2 11/27] kernel-shark: Update KsWidgetsLib
  2021-02-11 10:31 [PATCH v2 00/27] Complete the KernelShark v2 transformation Yordan Karadzhov (VMware)
                   ` (9 preceding siblings ...)
  2021-02-11 10:31 ` [PATCH v2 10/27] kernel-shark: Add model tests Yordan Karadzhov (VMware)
@ 2021-02-11 10:31 ` Yordan Karadzhov (VMware)
  2021-02-11 10:31 ` [PATCH v2 12/27] kernel-shark: Add combo point to Mark Yordan Karadzhov (VMware)
                   ` (16 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Yordan Karadzhov (VMware) @ 2021-02-11 10:31 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

The compilation of  KsWidgetsLib is re-enabled and all functionalities
are made compatible with the new version of the C API of libkshark
(KernelShark 2.0). We re-enable the widgetdemo example as well.

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 examples/CMakeLists.txt |  24 +-
 examples/widgetdemo.cpp |  65 ++--
 src/CMakeLists.txt      |   8 +-
 src/KsUtils.cpp         |  29 ++
 src/KsWidgetsLib.cpp    | 695 +++++++++++++++++++++++++++++++++++-----
 src/KsWidgetsLib.hpp    | 331 +++++++++++++++++--
 6 files changed, 996 insertions(+), 156 deletions(-)

diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index b8bc79a..8360841 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -23,15 +23,15 @@ 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)
+
+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/examples/widgetdemo.cpp b/examples/widgetdemo.cpp
index 73049bf..0234d4b 100644
--- a/examples/widgetdemo.cpp
+++ b/examples/widgetdemo.cpp
@@ -24,6 +24,7 @@
 static char *input_file = nullptr;
 
 using namespace std;
+using namespace KsWidgetsLib;
 
 void usage(const char *prog)
 {
@@ -37,13 +38,11 @@ void usage(const char *prog)
 
 struct TaskPrint : public QObject
 {
-	tep_handle	*_pevent;
-
-	void print(QVector<int> pids)
+	void print(int sd, QVector<int> pids)
 	{
 		for (auto const &pid: pids)
 			cout << "task: "
-			     << tep_data_comm_from_pid(_pevent, pid)
+			     << kshark_comm_from_pid(sd, pid)
 			     << "  pid: " << pid << endl;
 	}
 };
@@ -51,11 +50,12 @@ struct TaskPrint : public QObject
 int main(int argc, char **argv)
 {
 	kshark_context *kshark_ctx(nullptr);
+	kshark_data_stream *stream;
 	QApplication a(argc, argv);
 	KsPluginManager plugins;
+	int c, i(0), sd(-1);
 	KsDataStore data;
 	size_t nRows(0);
-	int c;
 
 	if (!kshark_instance(&kshark_ctx))
 		return 1;
@@ -71,11 +71,11 @@ int main(int argc, char **argv)
 			break;
 
 		case 'p':
-			plugins.registerPlugin(QString(optarg));
+			plugins.registerPlugins(QString(optarg));
 			break;
 
 		case 'u':
-			plugins.unregisterPlugin(QString(optarg));
+			plugins.unregisterPlugins(QString(optarg));
 			break;
 
 		case 'h':
@@ -91,7 +91,7 @@ int main(int argc, char **argv)
 	}
 
 	if (input_file) {
-		data.loadDataFile(input_file);
+		sd = data.loadDataFile(input_file, {});
 		nRows = data.size();
 	} else {
 		cerr << "No input file is provided.\n";
@@ -99,54 +99,51 @@ int main(int argc, char **argv)
 
 	cout << nRows << " entries loaded\n";
 
-	auto lamPrintPl = [&]()
-	{
-		kshark_plugin_list *pl;
-		for (pl = kshark_ctx->plugins; pl; pl = pl->next)
-			cout << pl->file << endl;
-	};
+	if (!nRows)
+		return 1;
+
+	stream = kshark_get_data_stream(kshark_ctx, sd);
+	if (!stream)
+		return 1;
 
 	cout << "\n\n";
-	lamPrintPl();
+	for (kshark_plugin_list *pl = kshark_ctx->plugins; pl; pl = pl->next)
+			cout << pl->file << endl;
 	sleep(1);
 
-	QVector<bool> registeredPlugins;
 	QStringList pluginsList;
+	QVector<int> streamIds, enabledPlugins, failedPlugins;
 
-	pluginsList << plugins._ksPluginList
-		    << plugins._userPluginList;
-
-	registeredPlugins << plugins._registeredKsPlugins
-			  << plugins._registeredUserPlugins;
+	pluginsList = plugins.getStreamPluginList(sd);
+	enabledPlugins = plugins.getActivePlugins(sd);
+	failedPlugins = plugins.getPluginsByStatus(sd, KSHARK_PLUGIN_FAILED);
 
 	KsCheckBoxWidget *pluginCBD
-		= new KsPluginCheckBoxWidget(pluginsList);
-
-	pluginCBD->set(registeredPlugins);
+		= new KsPluginCheckBoxWidget(sd, pluginsList);
+	pluginCBD->set(enabledPlugins);
 
-	KsCheckBoxDialog *dialog1 = new KsCheckBoxDialog(pluginCBD);
+	KsCheckBoxDialog *dialog1 = new KsCheckBoxDialog({pluginCBD});
+	dialog1->applyStatus();
 	QObject::connect(dialog1,	&KsCheckBoxDialog::apply,
-			&plugins,	&KsPluginManager::updatePlugins);
+			 &plugins,	&KsPluginManager::updatePlugins);
 
 	dialog1->show();
 	a.exec();
 
 	cout << "\n\nYou selected\n";
-	lamPrintPl();
-	sleep(1);
+	enabledPlugins = plugins.getActivePlugins(sd);
+	for (auto const &p: pluginsList)
+		qInfo() << p << (bool) enabledPlugins[i++];
 
-	if (!nRows)
-		return 1;
+	sleep(1);
 
 	KsCheckBoxWidget *tasks_cbd =
-		new KsTasksCheckBoxWidget(data.tep(), true);
+		new KsTasksCheckBoxWidget(stream, true);
 
 	tasks_cbd->setDefault(false);
 
 	TaskPrint p;
-	p._pevent = data.tep();
-
-	KsCheckBoxDialog *dialog2 = new KsCheckBoxDialog(tasks_cbd);
+	KsCheckBoxDialog *dialog2 = new KsCheckBoxDialog({tasks_cbd});
 	QObject::connect(dialog2,	&KsCheckBoxDialog::apply,
 			 &p,		&TaskPrint::print);
 
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index b308403..140fed8 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -69,9 +69,9 @@ if (Qt5Widgets_FOUND AND Qt5Network_FOUND)
     set (ks-guiLib_hdr  KsUtils.hpp
                         KsModels.hpp
 #                         KsGLWidget.hpp
-                        KsSearchFSM.hpp)
+                        KsSearchFSM.hpp
 #                         KsDualMarker.hpp
-#                         KsWidgetsLib.hpp
+                        KsWidgetsLib.hpp)
 #                         KsTraceGraph.hpp
 #                         KsTraceViewer.hpp
 #                         KsMainWindow.hpp
@@ -85,9 +85,9 @@ if (Qt5Widgets_FOUND AND Qt5Network_FOUND)
                                                             KsModels.cpp
 #                                                             KsSession.cpp
 #                                                             KsGLWidget.cpp
-                                                            KsSearchFSM.cpp)
+                                                            KsSearchFSM.cpp
 #                                                             KsDualMarker.cpp
-#                                                             KsWidgetsLib.cpp
+                                                            KsWidgetsLib.cpp)
 #                                                             KsTraceGraph.cpp
 #                                                             KsTraceViewer.cpp
 #                                                             KsMainWindow.cpp
diff --git a/src/KsUtils.cpp b/src/KsUtils.cpp
index 36f9b25..27cda55 100644
--- a/src/KsUtils.cpp
+++ b/src/KsUtils.cpp
@@ -13,6 +13,7 @@
 #include "libkshark-plugin.h"
 #include "libkshark-tepdata.h"
 #include "KsUtils.hpp"
+#include "KsWidgetsLib.hpp"
 
 namespace KsUtils {
 
@@ -451,6 +452,34 @@ QStringList getFiles(QWidget *parent,
 	return getFilesDialog(parent, windowName, filter, lastFilePath);
 }
 
+/**
+ * @brief Open a standard Qt getFileName dialog and return the name of the
+ *	  selected file. Only one file can be selected.
+ */
+QString getSaveFile(QWidget *parent,
+		    const QString &windowName,
+		    const QString &filter,
+		    const QString &extension,
+		    QString &lastFilePath)
+{
+	QString fileName = getFileDialog(parent,
+					 windowName,
+					 filter,
+					 lastFilePath,
+					 true);
+
+	if (!fileName.isEmpty() && !fileName.endsWith(extension)) {
+		fileName += extension;
+
+		if (QFileInfo(fileName).exists()) {
+			if (!KsWidgetsLib::fileExistsDialog(fileName))
+				fileName.clear();
+		}
+	}
+
+	return fileName;
+}
+
 /**
  * @brief Separate the command line arguments inside the string taking into
  *	  account possible shell quoting and new lines.
diff --git a/src/KsWidgetsLib.cpp b/src/KsWidgetsLib.cpp
index a84aff3..4ec6033 100644
--- a/src/KsWidgetsLib.cpp
+++ b/src/KsWidgetsLib.cpp
@@ -9,13 +9,18 @@
  *  @brief   Defines small widgets and dialogues used by the KernelShark GUI.
  */
 
+// C
+#include <unistd.h>
+
 // KernelShark
-#include "libkshark.h"
-#include "KsUtils.hpp"
+#include "libkshark-tepdata.h"
 #include "KsCmakeDef.hpp"
 #include "KsPlotTools.hpp"
 #include "KsWidgetsLib.hpp"
 
+namespace KsWidgetsLib
+{
+
 /**
  * @brief Create KsProgressBar.
  *
@@ -25,11 +30,12 @@
 KsProgressBar::KsProgressBar(QString message, QWidget *parent)
 : QWidget(parent),
   _sb(this),
-  _pb(&_sb) {
-	resize(KS_BROGBAR_WIDTH, KS_BROGBAR_HEIGHT);
+  _pb(&_sb),
+  _notDone(false) {
 	setWindowTitle("KernelShark");
 	setLayout(new QVBoxLayout);
-
+	setFixedHeight(KS_PROGBAR_HEIGHT);
+	setFixedWidth(KS_PROGBAR_WIDTH);
 	_pb.setOrientation(Qt::Horizontal);
 	_pb.setTextVisible(false);
 	_pb.setRange(0, KS_PROGRESS_BAR_MAX);
@@ -45,6 +51,13 @@ KsProgressBar::KsProgressBar(QString message, QWidget *parent)
 	show();
 }
 
+/** Destroy the KsProgressBar object. */
+KsProgressBar::~KsProgressBar()
+{
+	_notDone = false;
+	usleep(10000);
+}
+
 /** @brief Set the state of the progressbar.
  *
  * @param i: A value ranging from 0 to KS_PROGRESS_BAR_MAX.
@@ -54,6 +67,101 @@ void KsProgressBar::setValue(int i) {
 	QApplication::processEvents();
 }
 
+/** Show continuous work. */
+void KsProgressBar::workInProgress()
+{
+	int progress, inc;
+	bool inv = false;
+
+	progress = inc = 5;
+	_notDone = true;
+	while (_notDone) {
+		if (progress > KS_PROGRESS_BAR_MAX ||
+		    progress <= 0) {
+			inc = -inc;
+			inv = !inv;
+			_pb.setInvertedAppearance(inv);
+		}
+
+		setValue(progress);
+		progress += inc;
+		usleep(30000);
+	}
+}
+
+/**
+ * @brief Create KsWorkInProgress.
+ *
+ * @param parent: The parent of this widget.
+ */
+KsWorkInProgress::KsWorkInProgress(QWidget *parent)
+: QWidget(parent),
+  _icon(this),
+  _message("work in progress", this)
+{
+	QIcon statusIcon = QIcon::fromTheme("dialog-warning");
+	_icon.setPixmap(statusIcon.pixmap(.8 * FONT_HEIGHT));
+}
+
+/**
+ * @brief Show the "work in progress" notification.
+ *
+ * @param w: Data Work identifier.
+ */
+void KsWorkInProgress::show(KsDataWork w)
+{
+	_works.insert(w);
+	if (_works.size() == 1) {
+		_icon.show();
+		_message.show();
+
+		if (w != KsDataWork::RenderGL &&
+		    w != KsDataWork::ResizeGL)
+			QApplication::processEvents();
+	}
+}
+
+/**
+ * @brief Hide the "work in progress" notification.
+ *
+ * @param w: Data Work identifier.
+ */
+void KsWorkInProgress::hide(KsDataWork w)
+{
+	_works.remove(w);
+	if (_works.isEmpty()) {
+		_icon.hide();
+		_message.hide();
+
+		if (w != KsDataWork::RenderGL &&
+		    w != KsDataWork::ResizeGL)
+			QApplication::processEvents();
+	}
+}
+
+/**
+ * @brief Returns True the "work in progress" notification is active.
+ *	  Otherwise False.
+ *
+ * @param w: Data Work identifier.
+ */
+bool KsWorkInProgress::isBusy(KsDataWork w) const
+{
+	if (w == KsDataWork::AnyWork)
+		return _works.isEmpty()? false : true;
+
+	return _works.contains(w)? true : false;
+}
+
+/** Add the KsWorkInProgress widget to a given Status Bar. */
+void KsWorkInProgress::addToStatusBar(QStatusBar *sb)
+{
+	sb->addPermanentWidget(&_icon);
+	sb->addPermanentWidget(&_message);
+	_icon.hide();
+	_message.hide();
+}
+
 /**
  * @brief Create KsMessageDialog.
  *
@@ -76,9 +184,6 @@ KsMessageDialog::KsMessageDialog(QString message, QWidget *parent)
 	this->setLayout(&_layout);
 }
 
-namespace KsWidgetsLib
-{
-
 /**
  * @brief Launch a File exists dialog. Use this function to ask the user
  * before overwriting an existing file.
@@ -103,38 +208,156 @@ bool fileExistsDialog(QString fileName)
 	return (msgBox.exec() == QMessageBox::Save);
 }
 
-}; // KsWidgetsLib
+/** Create KsTimeOffsetDialog. */
+KsTimeOffsetDialog::KsTimeOffsetDialog(QWidget *parent)
+{
+	kshark_context *kshark_ctx(nullptr);
+	QVector<int> streamIds;
+	QString streamName;
+	int64_t max_ofst;
+
+	auto lamApply = [&] (double val) {
+		int sd = _streamCombo.currentData().toInt();
+		emit apply(sd, val);
+		close();
+	};
+
+	if (!kshark_instance(&kshark_ctx))
+		return;
+
+	this->setLayout(new QVBoxLayout);
+
+	streamIds = KsUtils::getStreamIdList(kshark_ctx);
+	if (streamIds.size() > 1) {
+		for (auto const &sd: streamIds)
+			if (sd != 0) {
+				streamName = KsUtils::streamDescription(kshark_ctx->stream[sd]);
+				_streamCombo.addItem(streamName);
+			}
+
+		layout()->addWidget(&_streamCombo);
+	}
+
+	_input.setInputMode(QInputDialog::DoubleInput);
+	 max_ofst = (int64_t) 1 << 60;
+	_input.setDoubleRange(-max_ofst, max_ofst);
+	_input.setDoubleDecimals(3);
+	_input.setLabelText("Offset [usec]:");
+	_setDefault(_streamCombo.currentIndex());
+
+	layout()->addWidget(&_input);
+
+	connect(&_input,	&QInputDialog::doubleValueSelected,
+		lamApply);
+
+	connect(&_input,	&QDialog::rejected,
+		this,		&QWidget::close);
+
+	connect(&_streamCombo,	SIGNAL(currentIndexChanged(int)),
+		SLOT(_setDefault(int)));
+
+	show();
+}
+
+void KsTimeOffsetDialog::_setDefault(int index) {
+	int sd = _streamCombo.currentData().toInt();
+	kshark_context *kshark_ctx(nullptr);
+	struct kshark_data_stream *stream;
+	double offset;
+
+	if (!kshark_instance(&kshark_ctx))
+		return;
+
+	stream = kshark_get_data_stream(kshark_ctx, sd);
+	if (!stream)
+		return;
+
+	if (!stream->calib_array) {
+		stream->calib = kshark_offset_calib;
+		stream->calib_array =
+			(int64_t *) calloc(1, sizeof(int64_t));
+		stream->calib_array_size = 1;
+	}
+
+	offset = stream->calib_array[0] * 1e-3;
+	_input.setDoubleValue(offset);
+}
+
+/**
+ * @brief Static function that starts a KsTimeOffsetDialog and returns value
+ *	  selected by the user.
+ *
+ * @param dataFile: The name of the trace file to which the Time Offset will
+ *		    apply. To be shown by the dialog.
+ * @param ok: Output location to a success flag. True if the user has pressed
+ *	      "Apply".
+ */
+double KsTimeOffsetDialog::getValueNanoSec(QString dataFile, bool *ok)
+{
+	KsTimeOffsetDialog dialog;
+	int64_t ofst(0);
+	int sd(-1);
+
+	*ok = false;
+
+	auto lamGetOffset = [&] (int stream_id, double ms) {
+		ofst = ms * 1000;
+		sd = stream_id;
+		*ok = true;
+	};
+
+	connect(&dialog, &KsTimeOffsetDialog::apply, lamGetOffset);
+	dialog._streamCombo.hide();
+	dialog._input.setLabelText(dataFile + "\nOffset [usec]:");
+	dialog.exec();
+
+	return ofst;
+}
 
 /**
  * @brief Create KsCheckBoxWidget.
  *
+ * @param sd: Data stream identifier.
  * @param name: The name of this widget.
  * @param parent: The parent of this widget.
  */
-KsCheckBoxWidget::KsCheckBoxWidget(const QString &name, QWidget *parent)
+KsCheckBoxWidget::KsCheckBoxWidget(int sd, const QString &name,
+				   QWidget *parent)
 : QWidget(parent),
-  _tb(this),
-  _allCb("all", &_tb),
+  _userInput(false),
+  _sd(sd),
+  _allCb("all"),
   _cbWidget(this),
   _cbLayout(&_cbWidget),
   _topLayout(this),
+  _allCbAction(nullptr),
+  _streamLabel("", this),
   _name(name),
-  _nameLabel(name + ":  ",&_tb)
+  _nameLabel(name + ":  ")
 {
 	setWindowTitle(_name);
 	setMinimumHeight(SCREEN_HEIGHT / 2);
+	setMinimumWidth(FONT_WIDTH * 20);
+
+	auto lamCheckAll = [this] (bool s) {
+		_userInput = true;
+		_checkAll(s);
+	};
 
 	connect(&_allCb,	&QCheckBox::clicked,
-		this,		&KsCheckBoxWidget::_checkAll);
+				lamCheckAll);
 
 	_cbWidget.setLayout(&_cbLayout);
 
+	_setStream(sd);
+	if (!_streamLabel.text().isEmpty())
+		_topLayout.addWidget(&_streamLabel);
+
 	_tb.addWidget(&_nameLabel);
-	_tb.addWidget(&_allCb);
-	_topLayout.addWidget(&_tb);
+	_allCbAction = _tb.addWidget(&_allCb);
 
+	_topLayout.addWidget(&_tb);
 	_topLayout.addWidget(&_cbWidget);
-	_topLayout.setContentsMargins(0, 0, 0, 0);
 
 	setLayout(&_topLayout);
 	_allCb.setCheckState(Qt::Checked);
@@ -154,6 +377,27 @@ void KsCheckBoxWidget::setDefault(bool st)
 	_checkAll(state);
 }
 
+/** Set the stream Id of the widget. */
+void KsCheckBoxWidget::_setStream(int8_t sd)
+{
+	kshark_context *kshark_ctx(nullptr);
+	kshark_data_stream *stream;
+
+	if (!kshark_instance(&kshark_ctx))
+		return;
+
+	_sd = sd;
+	stream = kshark_get_data_stream(kshark_ctx, sd);
+	if (!stream)
+		return;
+
+	_streamName = KsUtils::streamDescription(stream);
+
+	KsUtils::setElidedText(&_streamLabel, _streamName,
+			       Qt::ElideLeft, width());
+	QApplication::processEvents();
+}
+
 /** Get a vector containing the indexes of all checked boxes. */
 QVector<int> KsCheckBoxWidget::getCheckedIds()
 {
@@ -167,12 +411,24 @@ QVector<int> KsCheckBoxWidget::getCheckedIds()
 	return vec;
 }
 
+/** Get a vector containing the state of all checkboxes. */
+QVector<int> KsCheckBoxWidget::getStates()
+{
+	int n = _id.size();
+	QVector<int> vec(n);
+
+	for (int i = 0; i < n; ++i)
+		vec[i] = !!_checkState(i);
+
+	return vec;
+}
+
 /**
  * @brief Set the state of the checkboxes.
  *
- * @param v: Vector containing the bool values for all checkboxes.
+ * @param v: Vector containing the state values for all checkboxes.
  */
-void KsCheckBoxWidget::set(QVector<bool> v)
+void KsCheckBoxWidget::set(QVector<int> v)
 {
 	Qt::CheckState state;
 	int nChecks;
@@ -215,18 +471,24 @@ void KsCheckBoxWidget::_checkAll(bool st)
 /**
  * @brief Create KsCheckBoxDialog.
  *
- * @param cbw: A KsCheckBoxWidget to be nested in this dialog.
+ * @param cbws: A vector of KsCheckBoxWidgets to be nested in this dialog.
  * @param parent: The parent of this widget.
  */
-KsCheckBoxDialog::KsCheckBoxDialog(KsCheckBoxWidget *cbw, QWidget *parent)
-: QDialog(parent), _checkBoxWidget(cbw),
+KsCheckBoxDialog::KsCheckBoxDialog(QVector<KsCheckBoxWidget *> cbws, QWidget *parent)
+: QDialog(parent),
+  _applyIds(true),
+  _checkBoxWidgets(cbws),
   _applyButton("Apply", this),
   _cancelButton("Cancel", this)
 {
 	int buttonWidth;
 
-	setWindowTitle(cbw->name());
-	_topLayout.addWidget(_checkBoxWidget);
+	if (!cbws.isEmpty())
+		setWindowTitle(cbws[0]->name());
+
+	for (auto const &w: _checkBoxWidgets)
+		_cbLayout.addWidget(w);
+	_topLayout.addLayout(&_cbLayout);
 
 	buttonWidth = STRING_WIDTH("--Cancel--");
 	_applyButton.setFixedWidth(buttonWidth);
@@ -256,16 +518,29 @@ KsCheckBoxDialog::KsCheckBoxDialog(KsCheckBoxWidget *cbw, QWidget *parent)
 
 void KsCheckBoxDialog::_applyPress()
 {
-	QVector<int> vec = _checkBoxWidget->getCheckedIds();
-	emit apply(vec);
+	QVector<int> vec;
 
 	/*
 	 * Disconnect _applyButton. This is done in order to protect
 	 * against multiple clicks.
 	 */
 	disconnect(_applyButtonConnection);
-}
 
+	_preApplyAction();
+
+	for (auto const &w: _checkBoxWidgets) {
+		if (!w->_userInput)
+			continue;
+
+		if (_applyIds)
+			vec = w->getCheckedIds();
+		else
+			vec = w->getStates();
+		emit apply(w->sd(), vec);
+	}
+
+	_postApplyAction();
+}
 
 /**
  * @brief Create KsCheckBoxTable.
@@ -356,12 +631,13 @@ void KsCheckBoxTable::_doubleClicked(int row, int col)
 /**
  * @brief Create KsCheckBoxTableWidget.
  *
+ * @param sd: Data stream identifier.
  * @param name: The name of this widget.
  * @param parent: The parent of this widget.
  */
-KsCheckBoxTableWidget::KsCheckBoxTableWidget(const QString &name,
+KsCheckBoxTableWidget::KsCheckBoxTableWidget(int sd, const QString &name,
 					     QWidget *parent)
-: KsCheckBoxWidget(name, parent),
+: KsCheckBoxWidget(sd, name, parent),
   _table(this)
 {
 	connect(&_table,	&KsCheckBoxTable::changeState,
@@ -409,6 +685,8 @@ void  KsCheckBoxTableWidget::_update(bool state)
 	/* If a Checkbox is being unchecked. Unchecked "all" as well. */
 	if (!state)
 		_allCb.setCheckState(Qt::Unchecked);
+
+	_userInput = true;
 }
 
 void KsCheckBoxTableWidget::_changeState(int row)
@@ -425,6 +703,8 @@ void KsCheckBoxTableWidget::_changeState(int row)
 			break;
 		}
 	}
+
+	_userInput = true;
 }
 
 static void update_r(QTreeWidgetItem *item, Qt::CheckState state)
@@ -511,16 +791,24 @@ void KsCheckBoxTree::mousePressEvent(QMouseEvent *event)
 /**
  * @brief Create KsCheckBoxTreeWidget.
  *
+ * @param sd: Data stream identifier.
  * @param name: The name of this widget.
  * @param parent: The parent of this widget.
  */
-KsCheckBoxTreeWidget::KsCheckBoxTreeWidget(const QString &name,
+KsCheckBoxTreeWidget::KsCheckBoxTreeWidget(int sd, const QString &name,
 					   QWidget *parent)
-: KsCheckBoxWidget(name, parent),
+: KsCheckBoxWidget(sd, name, parent),
   _tree(this)
 {
-	connect(&_tree,	&KsCheckBoxTree::verify,
-		this,	&KsCheckBoxTreeWidget::_verify);
+	connect(&_tree,		&KsCheckBoxTree::verify,
+		this,		&KsCheckBoxTreeWidget::_verify);
+
+	auto lamSetUserInput = [this] (QTreeWidgetItem *, int) {
+		_userInput = true;
+	};
+
+	connect(&_tree,		&QTreeWidget::itemClicked,
+				lamSetUserInput);
 }
 
 /** Initialize the KsCheckBoxTree and its layout. */
@@ -553,7 +841,7 @@ void KsCheckBoxTreeWidget::_adjustSize()
 		width = _tree.visualItemRect(_tree.topLevelItem(0)).width();
 	}
 
-	width += FONT_WIDTH*3 + style()->pixelMetric(QStyle::PM_ScrollBarExtent);
+	width += FONT_WIDTH * 3 + style()->pixelMetric(QStyle::PM_ScrollBarExtent);
 	_cbWidget.resize(width, _cbWidget.height());
 
 	for (int i = 0; i < n; ++i)
@@ -614,14 +902,13 @@ void KsCheckBoxTreeWidget::_verify()
 /**
  * @brief Create KsCPUCheckBoxWidget.
  *
- * @param tep: Trace event parseer.
+ * @param stream: Input location for a Data stream pointer.
  * @param parent: The parent of this widget.
  */
-KsCPUCheckBoxWidget::KsCPUCheckBoxWidget(struct tep_handle *tep,
-					 QWidget *parent)
-: KsCheckBoxTreeWidget("CPUs", parent)
+KsCPUCheckBoxWidget::KsCPUCheckBoxWidget(kshark_data_stream *stream, QWidget *parent)
+: KsCheckBoxTreeWidget(stream->stream_id, "CPUs", parent)
 {
-	int nCPUs(0), height(FONT_HEIGHT * 1.5);
+	int height(FONT_HEIGHT * 1.5);
 	KsPlot::ColorTable colors;
 	QString style;
 
@@ -630,19 +917,16 @@ KsCPUCheckBoxWidget::KsCPUCheckBoxWidget(struct tep_handle *tep,
 
 	_initTree();
 
-	if (tep)
-		nCPUs = tep_get_cpus(tep);
+	_id.resize(stream->n_cpus);
+	_cb.resize(stream->n_cpus);
+	colors = KsPlot::CPUColorTable();
 
-	_id.resize(nCPUs);
-	_cb.resize(nCPUs);
-	colors = KsPlot::getCPUColorTable();
-
-	for (int i = 0; i < nCPUs; ++i) {
+	for (int i = 0; i < stream->n_cpus; ++i) {
 		QTreeWidgetItem *cpuItem = new QTreeWidgetItem;
 		cpuItem->setText(0, "  ");
 		cpuItem->setText(1, QString("CPU %1").arg(i));
 		cpuItem->setCheckState(0, Qt::Checked);
-		cpuItem->setBackground(0, QColor(colors[i].r(),
+		cpuItem->setBackgroundColor(0, QColor(colors[i].r(),
 						      colors[i].g(),
 						      colors[i].b()));
 		_tree.addTopLevelItem(cpuItem);
@@ -656,36 +940,66 @@ KsCPUCheckBoxWidget::KsCPUCheckBoxWidget(struct tep_handle *tep,
 /**
  * @brief Create KsEventsCheckBoxWidget.
  *
- * @param tep: Trace event parseer.
+ * @param stream: Input location for a Data stream pointer.
  * @param parent: The parent of this widget.
  */
-KsEventsCheckBoxWidget::KsEventsCheckBoxWidget(struct tep_handle *tep,
+KsEventsCheckBoxWidget::KsEventsCheckBoxWidget(kshark_data_stream *stream,
 					       QWidget *parent)
-: KsCheckBoxTreeWidget("Events", parent)
+: KsCheckBoxTreeWidget(stream->stream_id, "Events", parent)
 {
-	QTreeWidgetItem *sysItem, *evtItem;
-	tep_event **events(nullptr);
-	QString sysName, evtName;
-	int nEvts(0), i(0);
+	QVector<int> eventIds = KsUtils::getEventIdList(stream->stream_id);
+
+	_initTree();
+	if(!stream->n_events || eventIds.isEmpty())
+		return;
+
+	_id.resize(stream->n_events);
+	_cb.resize(stream->n_events);
 
-	if (tep) {
-		nEvts = tep_get_events_count(tep);
-		events = tep_list_events(tep, TEP_EVENT_SORT_SYSTEM);
+	if (kshark_is_tep(stream))
+		_makeTepEventItems(stream, eventIds);
+	else
+		_makeItems(stream, eventIds);
+}
+
+void KsEventsCheckBoxWidget::_makeItems(kshark_data_stream *stream,
+					QVector<int> eventIds)
+{
+	QTreeWidgetItem *evtItem;
+	QString evtName;
+
+	for (int i = 0; i < stream->n_events; ++i) {
+		evtName = KsUtils::getEventName(stream->stream_id,
+						eventIds[i]);
+		evtItem = new QTreeWidgetItem;
+		evtItem->setText(0, evtName);
+		evtItem->setCheckState(0, Qt::Checked);
+		evtItem->setFlags(evtItem->flags() |
+				  Qt::ItemIsUserCheckable);
+		_tree.addTopLevelItem(evtItem);
+		_cb[i] = evtItem;
 	}
+}
 
-	_initTree();
-	_id.resize(nEvts);
-	_cb.resize(nEvts);
+void KsEventsCheckBoxWidget::_makeTepEventItems(kshark_data_stream *stream,
+						QVector<int> eventIds)
+{
+	QTreeWidgetItem *sysItem, *evtItem;
+	QString sysName, evtName;
+	QStringList name;
+	int i(0);
 
-	while (i < nEvts) {
-		sysName = events[i]->system;
+	while (i < stream->n_events) {
+		name = KsUtils::getTepEvtName(stream->stream_id,
+					      eventIds[i]);
+		sysName = name[0];
 		sysItem = new QTreeWidgetItem;
 		sysItem->setText(0, sysName);
 		sysItem->setCheckState(0, Qt::Checked);
 		_tree.addTopLevelItem(sysItem);
 
-		while (sysName == events[i]->system) {
-			evtName = events[i]->name;
+		while (sysName == name[0]) {
+			evtName = name[1];
 			evtItem = new QTreeWidgetItem;
 			evtItem->setText(0, evtName);
 			evtItem->setCheckState(0, Qt::Checked);
@@ -694,11 +1008,13 @@ KsEventsCheckBoxWidget::KsEventsCheckBoxWidget(struct tep_handle *tep,
 
 			sysItem->addChild(evtItem);
 
-			_id[i] = events[i]->id;
+			_id[i] = eventIds[i];
 			_cb[i] = evtItem;
-
-			if (++i == nEvts)
+			if (++i == stream->n_events)
 				break;
+
+			name = KsUtils::getTepEvtName(stream->stream_id,
+						      eventIds[i]);
 		}
 	}
 
@@ -726,7 +1042,7 @@ QStringList KsEventsCheckBoxWidget::getCheckedEvents(bool option)
 		optStr = "-e";
 
 	nSys = _tree.topLevelItemCount();
-	for (int t = 0; t < nSys; ++t) {
+	for(int t = 0; t < nSys; ++t) {
 		sysItem = _tree.topLevelItem(t);
 		if (sysItem->checkState(0) == Qt::Checked) {
 			list << optStr + sysItem->text(0);
@@ -763,48 +1079,48 @@ void KsEventsCheckBoxWidget::removeSystem(QString name) {
 /**
  * @brief Create KsTasksCheckBoxWidget.
  *
- * @param pevent: Page event used to parse the page.
+ * @param stream: Input location for a Data stream pointer.
  * @param cond: If True make a "Show Task" widget. Otherwise make "Hide Task".
  * @param parent: The parent of this widget.
  */
-KsTasksCheckBoxWidget::KsTasksCheckBoxWidget(struct tep_handle *pevent,
+KsTasksCheckBoxWidget::KsTasksCheckBoxWidget(kshark_data_stream *stream,
 					     bool cond, QWidget *parent)
-: KsCheckBoxTableWidget("Tasks", parent)
+: KsCheckBoxTableWidget(stream->stream_id, "Tasks", parent),
+  _cond(cond)
 {
-	kshark_context *kshark_ctx(nullptr);
 	QTableWidgetItem *pidItem, *comItem;
 	KsPlot::ColorTable colors;
 	QStringList headers;
+	kshark_entry entry;
 	const char *comm;
 	int nTasks, pid;
 
-	if (!kshark_instance(&kshark_ctx))
-		return;
-
 	if (_cond)
 		headers << "Show" << "Pid" << "Task";
 	else
 		headers << "Hide" << "Pid" << "Task";
 
-	_id = KsUtils::getPidList();
+	_id = KsUtils::getPidList(stream->stream_id);
 	nTasks = _id.count();
 	_initTable(headers, nTasks);
-	colors = KsPlot::getTaskColorTable();
-
+	colors = KsPlot::taskColorTable();
+	entry.stream_id = stream->stream_id;
+	entry.visible = 0xff;
 	for (int i = 0; i < nTasks; ++i) {
-		pid = _id[i];
-		pidItem	= new QTableWidgetItem(tr("%1").arg(pid));
+		entry.pid = pid = _id[i];
+		pidItem = new QTableWidgetItem(tr("%1").arg(pid));
 		_table.setItem(i, 1, pidItem);
 
-		comm = tep_data_comm_from_pid(kshark_ctx->pevent, pid);
+		comm = kshark_get_task(&entry);
+
 		comItem = new QTableWidgetItem(tr(comm));
 
-		pidItem->setBackground(QColor(colors[pid].r(),
+		pidItem->setBackgroundColor(QColor(colors[pid].r(),
 						   colors[pid].g(),
 						   colors[pid].b()));
 
 		if (_id[i] == 0)
-			pidItem->setForeground(Qt::white);
+			pidItem->setTextColor(Qt::white);
 
 		_table.setItem(i, 2, comItem);
 	}
@@ -815,12 +1131,13 @@ KsTasksCheckBoxWidget::KsTasksCheckBoxWidget(struct tep_handle *pevent,
 /**
  * @brief Create KsPluginCheckBoxWidget.
  *
+ * @param sd: Data stream identifier.
  * @param pluginList: A list of plugin names.
  * @param parent: The parent of this widget.
  */
-KsPluginCheckBoxWidget::KsPluginCheckBoxWidget(QStringList pluginList,
+KsPluginCheckBoxWidget::KsPluginCheckBoxWidget(int sd, QStringList pluginList,
 					       QWidget *parent)
-: KsCheckBoxTableWidget("Plugins", parent)
+: KsCheckBoxTableWidget(sd, "Manage plugins", parent)
 {
 	QTableWidgetItem *nameItem, *infoItem;
 	QStringList headers;
@@ -833,7 +1150,16 @@ KsPluginCheckBoxWidget::KsPluginCheckBoxWidget(QStringList pluginList,
 	_id.resize(nPlgins);
 
 	for (int i = 0; i < nPlgins; ++i) {
-		nameItem = new QTableWidgetItem(pluginList[i]);
+		if (pluginList[i] < 30) {
+			nameItem = new QTableWidgetItem(pluginList[i]);
+		} else {
+			QLabel l;
+			KsUtils::setElidedText(&l, pluginList[i],
+					       Qt::ElideLeft,
+					       FONT_WIDTH * 30);
+			nameItem = new QTableWidgetItem(l.text());
+		}
+
 		_table.setItem(i, 1, nameItem);
 		infoItem = new QTableWidgetItem(" -- ");
 		_table.setItem(i, 2, infoItem);
@@ -842,3 +1168,200 @@ KsPluginCheckBoxWidget::KsPluginCheckBoxWidget(QStringList pluginList,
 
 	_adjustSize();
 }
+
+/**
+ * @brief Set the "Info" field inside the table of the widget.
+ *
+ * @param row: The row number in the table.
+ * @param info: The "Info" string to be shown.
+ */
+void KsPluginCheckBoxWidget::setInfo(int row, QString info)
+{
+	QTableWidgetItem *infoItem = _table.item(row, 2);
+	infoItem->setText(info);
+}
+
+/**
+ * @brief Set the "Active" field inside the table of the widget.
+ *
+ * @param rows: The row numbers in the table.
+ * @param a: Are those plugins active.
+ */
+void KsPluginCheckBoxWidget::setActive(QVector<int> rows, bool a)
+{
+	for (auto const &r: rows) {
+		QTableWidgetItem *infoItem = _table.item(r, 2);
+		if (a) {
+			infoItem->setText("- Active");
+			infoItem->setForeground(QBrush(QColor(0, 220, 80)));
+		} else {
+			infoItem->setText("- Not Active");
+			infoItem->setForeground(QBrush(QColor(255, 50, 50)));
+		}
+	}
+}
+
+void KsPluginsCheckBoxDialog::_postApplyAction()
+{
+	emit _data->updateWidgets(_data);
+}
+
+/**
+ * @brief Create KsDStreamCheckBoxWidget.
+ *
+ * @param parent: The parent of this widget.
+ */
+KsDStreamCheckBoxWidget::KsDStreamCheckBoxWidget(QWidget *parent)
+: KsCheckBoxTableWidget(-1, "Select Data stream", parent)
+{
+	kshark_context *kshark_ctx(nullptr);
+	kshark_data_stream *stream;
+	QTableWidgetItem *nameItem;
+	QVector<int> streamIds;
+	QStringList headers;
+	int nStreams;
+
+	if (!kshark_instance(&kshark_ctx))
+		return;
+
+	headers << "Apply" << "To stream";
+	streamIds = KsUtils::getStreamIdList(kshark_ctx);
+	nStreams = streamIds.size();
+	_initTable(headers, nStreams);
+	_id.resize(nStreams);
+
+	for (int i = 0; i < nStreams; ++i) {
+		stream = kshark_ctx->stream[streamIds[i]];
+		QString name = KsUtils::streamDescription(stream);
+		if (name < 40) {
+			nameItem = new QTableWidgetItem(name);
+		} else {
+			QLabel l;
+			KsUtils::setElidedText(&l, name,
+					       Qt::ElideLeft,
+					       FONT_WIDTH * 40);
+			nameItem = new QTableWidgetItem(l.text());
+		}
+
+		_table.setItem(i, 1, nameItem);
+		_id[i] = stream->stream_id;
+	}
+
+	_adjustSize();
+}
+
+/**
+ * @brief Create KsEventFieldSelectWidget.
+ *
+ * @param parent: The parent of this widget.
+ */
+KsEventFieldSelectWidget::KsEventFieldSelectWidget(QWidget *parent)
+: QWidget(parent),
+  _streamLabel("Data stream", this),
+  _eventLabel("Event (type in for searching)", this),
+  _fieldLabel("Field", this)
+{
+	auto lamAddLine = [&] {
+		QFrame* line = new QFrame();
+		QSpacerItem *spacer = new QSpacerItem(1, FONT_HEIGHT / 2,
+						      QSizePolicy::Expanding,
+						      QSizePolicy::Minimum);
+		line->setFrameShape(QFrame::HLine);
+		line->setFrameShadow(QFrame::Sunken);
+		_topLayout.addSpacerItem(spacer);
+		_topLayout.addWidget(line);
+	};
+
+	_topLayout.addWidget(&_streamLabel);
+	_topLayout.addWidget(&_streamComboBox);
+
+	/*
+	 * Using the old Signal-Slot syntax because QComboBox::currentIndexChanged
+	 * has overloads.
+	 */
+	connect(&_streamComboBox,	SIGNAL(currentIndexChanged(const QString&)),
+		this,			SLOT(_streamChanged(const QString&)));
+
+	lamAddLine();
+
+	_topLayout.addWidget(&_eventLabel);
+	_topLayout.addWidget(&_eventComboBox);
+	_eventComboBox.setEditable(true);
+	_eventComboBox.view()->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+	_eventComboBox.setMaxVisibleItems(25);
+
+	/*
+	 * Using the old Signal-Slot syntax because QComboBox::currentIndexChanged
+	 * has overloads.
+	 */
+	connect(&_eventComboBox,	SIGNAL(currentIndexChanged(const QString&)),
+		this,			SLOT(_eventChanged(const QString&)));
+
+	lamAddLine();
+
+	_topLayout.addWidget(&_fieldLabel);
+	_topLayout.addWidget(&_fieldComboBox);
+
+	lamAddLine();
+
+	setLayout(&_topLayout);
+}
+
+/** Populate the Data stream selection combo box. */
+void KsEventFieldSelectWidget::setStreamCombo()
+{
+	kshark_context *kshark_ctx(NULL);
+	kshark_data_stream *stream;
+	QVector<int> streamIds;
+
+	if (!kshark_instance(&kshark_ctx))
+		return;
+
+	streamIds = KsUtils::getStreamIdList(kshark_ctx);
+	for (auto const &sd: streamIds) {
+		stream = kshark_ctx->stream[sd];
+		if (_streamComboBox.findData(sd) < 0)
+			_streamComboBox.addItem(KsUtils::streamDescription(stream), sd);
+	}
+}
+
+void KsEventFieldSelectWidget::_streamChanged(const QString &streamFile)
+{
+	int sd = _streamComboBox.currentData().toInt();
+	QVector<int> eventIds = KsUtils::getEventIdList(sd);
+	QStringList evtsList;
+
+	_eventComboBox.clear();
+
+	for (auto const &eid: eventIds)
+		evtsList << KsUtils::getEventName(sd, eid);
+
+	std::sort(evtsList.begin(), evtsList.end());
+	_eventComboBox.addItems(evtsList);
+}
+
+void KsEventFieldSelectWidget::_eventChanged(const QString &eventName)
+{
+	int sd = _streamComboBox.currentData().toInt();
+	int eventId = KsUtils::getEventId(sd, eventName);
+	QStringList fieldsList = KsUtils::getEventFieldsList(sd, eventId);
+
+	auto lamIsValide = [&] (const QString &f) {
+		return KsUtils::getEventFieldType(sd, eventId, f) ==
+		       KS_INVALID_FIELD;
+	};
+
+	_fieldComboBox.clear();
+
+	fieldsList.erase(std::remove_if(fieldsList.begin(), fieldsList.end(),
+					lamIsValide), fieldsList.end());
+
+	if (fieldsList.isEmpty())
+		return;
+
+	std::sort(fieldsList.begin(), fieldsList.end());
+
+	_fieldComboBox.addItems(fieldsList);
+}
+
+}; // KsWidgetsLib
diff --git a/src/KsWidgetsLib.hpp b/src/KsWidgetsLib.hpp
index 59e773d..428e8dd 100644
--- a/src/KsWidgetsLib.hpp
+++ b/src/KsWidgetsLib.hpp
@@ -15,6 +15,13 @@
 // Qt
 #include <QtWidgets>
 
+// KernelShark
+#include "libkshark.h"
+#include "KsUtils.hpp"
+
+namespace KsWidgetsLib
+{
+
 /**
  * The KsProgressBar class provides a visualization of the progress of a
  * running job.
@@ -30,17 +37,133 @@ class KsProgressBar : public QWidget
 public:
 	KsProgressBar(QString message, QWidget *parent = nullptr);
 
+	virtual ~KsProgressBar();
+
 	void setValue(int i);
+
+	void workInProgress();
+
+private:
+	bool	_notDone;
 };
 
 /** Defines the progress bar's maximum value. */
 #define KS_PROGRESS_BAR_MAX 200
 
 /** The height of the KsProgressBar widget. */
-#define KS_BROGBAR_HEIGHT (FONT_HEIGHT * 5)
+#define KS_PROGBAR_HEIGHT (FONT_HEIGHT * 5)
 
 /** The width of the KsProgressBar widget. */
-#define KS_BROGBAR_WIDTH  (FONT_WIDTH * 50)
+#define KS_PROGBAR_WIDTH  (FONT_WIDTH * 50)
+
+/** Data Work identifiers. */
+enum class KsDataWork
+{
+	AnyWork,
+	EditPlotList,
+	ZoomIn,
+	QuickZoomIn,
+	ZoomOut,
+	QuickZoomOut,
+	ScrollLeft,
+	ScrollRight,
+	JumpTo,
+	GraphUpdateGeom,
+	UpdatePlugins,
+	ResizeGL,
+	RenderGL,
+};
+
+/** Defines hash function needed by the QSet tempate container class. */
+inline uint qHash(KsDataWork key, uint seed)
+{
+	/*
+	 * Cast the enum class to uint and use the definition of qHash outside
+	 * the current scope.
+	 */
+	return ::qHash(static_cast<uint>(key), seed);
+}
+
+/**
+ * The KsWorkInProgress class provides a widget showing the
+ * "work in progress" notification.
+ */
+class KsWorkInProgress : public QWidget
+{
+public:
+	explicit KsWorkInProgress(QWidget *parent = nullptr);
+
+	void show(KsDataWork w);
+
+	void hide(KsDataWork w);
+
+	bool isBusy(KsDataWork w = KsDataWork::AnyWork) const;
+
+	void addToStatusBar(QStatusBar *sb);
+
+private:
+	QLabel	_icon, _message;
+
+	QSet<KsDataWork>	_works;
+};
+
+/**
+ * The KsDataWidget class provides a base widget that provides the capability
+ * to show the "work in progress" notification. The class must be inherited by
+ * the widgets performing heavy data processing operations.
+ */
+class KsDataWidget : public QWidget
+{
+public:
+	/**
+	 * @brief Create KsDataWidget.
+	 *
+	 * @param parent: The parent of this widget.
+	 */
+	explicit KsDataWidget(QWidget *parent = nullptr)
+	: QWidget(parent), _workInProgress(nullptr) {}
+
+	/** Set a pointer to the KsWorkInProgress widget. */
+	const KsWorkInProgress *wipPtr(KsWorkInProgress *wip) const
+	{
+		return _workInProgress;
+	}
+
+	/** Set the pointer to the KsWorkInProgress widget. */
+	void setWipPtr(KsWorkInProgress *wip)
+	{
+		_workInProgress = wip;
+	}
+
+	/**
+	 * Call this function when a given work is about to start in order to
+	 * show the "work in progress" notification.
+	 */
+	void startOfWork(KsDataWork w)
+	{
+		if (_workInProgress)
+			_workInProgress->show(w);
+	}
+
+	/**
+	 * Call this function when a given work is done in order to hide the
+	 * "work in progress" notification.
+	 */
+	void endOfWork(KsDataWork w)
+	{
+		if (_workInProgress)
+			_workInProgress->hide(w);
+	}
+
+	/** Check if the GUI is busy processing data. */
+	bool isBusy(KsDataWork w = KsDataWork::AnyWork) const
+	{
+		return _workInProgress ? _workInProgress->isBusy(w) : false;
+	}
+
+private:
+	KsWorkInProgress	*_workInProgress;
+};
 
 /**
  * The KsMessageDialog class provides a widget showing a message and having
@@ -66,12 +189,32 @@ public:
 /** The width of the KsMessageDialog widget. */
 #define KS_MSG_DIALOG_WIDTH  (SCREEN_WIDTH / 10)
 
-namespace KsWidgetsLib
+bool fileExistsDialog(QString fileName);
+
+/**
+ * The KsTimeOffsetDialog class provides a dialog used to enter the value of
+ * the time offset between two Data streams.
+ */
+class KsTimeOffsetDialog : public QDialog
 {
+	Q_OBJECT
+public:
+	explicit KsTimeOffsetDialog(QWidget *parent = nullptr);
 
-bool fileExistsDialog(QString fileName);
+	static double getValueNanoSec(QString dataFile, bool *ok);
 
-}; // KsWidgetsLib
+signals:
+	/** Signal emitted when the "Apply" button is pressed. */
+	void apply(int sd, double val);
+
+private:
+	QInputDialog	_input;
+
+	QComboBox	_streamCombo;
+
+private slots:
+	void _setDefault(int index);
+};
 
 /**
  * The KsCheckBoxWidget class is the base class of all CheckBox widget used
@@ -81,7 +224,7 @@ class KsCheckBoxWidget : public QWidget
 {
 	Q_OBJECT
 public:
-	KsCheckBoxWidget(const QString &name = "",
+	KsCheckBoxWidget(int sd, const QString &name = "",
 			 QWidget *parent = nullptr);
 
 	/** Get the name of the widget. */
@@ -95,9 +238,12 @@ public:
 		return false;
 	}
 
+	/** The "all" checkboxe to be visible or not. */
+	void setVisibleCbAll(bool v) {_allCbAction->setVisible(v);}
+
 	void setDefault(bool);
 
-	void set(QVector<bool> v);
+	void set(QVector<int> v);
 
 	/**
 	 * Get a vector containing all Ids (can be PID CPU Ids etc.) managed
@@ -107,10 +253,35 @@ public:
 
 	QVector<int> getCheckedIds();
 
+	QVector<int> getStates();
+
+	/**
+	 * Get the identifier of the Data stream for which the selection
+	 * applies.
+	 */
+	int sd() const {return _sd;}
+
+	/**
+	 * Reimplemented event handler used to update the geometry of the widget on
+	 * resize events.
+	 */
+	void resizeEvent(QResizeEvent* event)
+	{
+		KsUtils::setElidedText(&_streamLabel, _streamName,
+				       Qt::ElideLeft, width());
+		QApplication::processEvents();
+	}
+
+	/** The user provided an input. The widget has been modified. */
+	bool _userInput;
+
 private:
 	QToolBar _tb;
 
 protected:
+	/** Identifier of the Data stream for which the selection applies. */
+	int		_sd;
+
 	/** The "all" checkboxe. */
 	QCheckBox	_allCb;
 
@@ -126,13 +297,25 @@ protected:
 	/** The top level layout of this widget. */
 	QVBoxLayout	_topLayout;
 
+private:
+	QAction		*_allCbAction;
+
+	/**
+	 * The name of this Data stream. Typically this will be the name of
+	 * the data file.
+	 */
+	QString		_streamName;
+	/**
+	 * A label to show the name of the Data stream for which the selection
+	 * applies. */
+	QLabel		_streamLabel;
+
 	/** The name of this widget. */
 	QString		_name;
 
 	/** A label to show the name of the widget. */
 	QLabel		_nameLabel;
 
-private:
 	virtual void _setCheckState(int i, Qt::CheckState st) = 0;
 
 	virtual Qt::CheckState _checkState(int i) const = 0;
@@ -140,6 +323,8 @@ private:
 	virtual void _verify() {};
 
 	void _checkAll(bool);
+
+	void _setStream(int8_t sd);
 };
 
 /**
@@ -152,26 +337,65 @@ class KsCheckBoxDialog : public QDialog
 public:
 	KsCheckBoxDialog() = delete;
 
-	KsCheckBoxDialog(KsCheckBoxWidget *cbw, QWidget *parent = nullptr);
+	KsCheckBoxDialog(QVector<KsCheckBoxWidget *> cbws,
+			 QWidget *parent = nullptr);
+
+	/**
+	 * The "apply" signal will emit a vector containing the Ids of all
+	 * checked checkboxe.
+	 */
+	void applyIds(bool v = true) {_applyIds = v;}
+
+	/**
+	 * The "apply" signal will emit a vector containing the statuse of all
+	 * checkboxe.
+	 */
+	void applyStatus(bool v = true) {_applyIds = !v;}
 
 signals:
 	/** Signal emitted when the "Apply" button is pressed. */
-	void apply(QVector<int>);
+	void apply(int sd, QVector<int>);
 
 private:
 	void _applyPress();
 
-	QVBoxLayout		_topLayout;
+	virtual void _preApplyAction() {}
+
+	virtual void _postApplyAction() {}
+
+	bool _applyIds;
+
+	QVBoxLayout			_topLayout;
 
-	QHBoxLayout		_buttonLayout;
+	QHBoxLayout			_cbLayout, _buttonLayout;
 
-	KsCheckBoxWidget	*_checkBoxWidget;
+	QVector<KsCheckBoxWidget *>	_checkBoxWidgets;
 
-	QPushButton		_applyButton, _cancelButton;
+	QPushButton			_applyButton, _cancelButton;
 
 	QMetaObject::Connection		_applyButtonConnection;
 };
 
+/**
+ * The KsPluginsCheckBoxDialog provides dialog for selecting plugins.
+ * used by KernelShark. The class is used to override _postApplyAction().
+ */
+class KsPluginsCheckBoxDialog : public KsCheckBoxDialog
+{
+public:
+	KsPluginsCheckBoxDialog() = delete;
+
+	/** Create KsPluginsCheckBoxDialog. */
+	KsPluginsCheckBoxDialog(QVector<KsCheckBoxWidget *> cbws,
+				KsDataStore *d, QWidget *parent = nullptr)
+	: KsCheckBoxDialog(cbws, parent), _data(d) {}
+
+private:
+	virtual void _postApplyAction() override;
+
+	KsDataStore	*_data;
+};
+
 /** The KsCheckBoxTable class provides a table of checkboxes. */
 class KsCheckBoxTable : public QTableWidget
 {
@@ -205,9 +429,16 @@ class KsCheckBoxTableWidget : public KsCheckBoxWidget
 {
 	Q_OBJECT
 public:
-	KsCheckBoxTableWidget(const QString &name = "",
+	KsCheckBoxTableWidget(int sd, const QString &name = "",
 			      QWidget *parent = nullptr);
 
+	/** Only one checkboxe at the time can be checked. */
+	void setSingleSelection()
+	{
+		_table.setSelectionMode(QAbstractItemView::SingleSelection);
+		setVisibleCbAll(false);
+	}
+
 protected:
 	void _adjustSize();
 
@@ -266,9 +497,16 @@ class KsCheckBoxTreeWidget : public KsCheckBoxWidget
 public:
 	KsCheckBoxTreeWidget() = delete;
 
-	KsCheckBoxTreeWidget(const QString &name = "",
+	KsCheckBoxTreeWidget(int sd, const QString &name = "",
 			     QWidget *parent = nullptr);
 
+	/** Only one checkboxe at the time can be checked. */
+	void setSingleSelection()
+	{
+		_tree.setSelectionMode(QAbstractItemView::SingleSelection);
+		setVisibleCbAll(false);
+	}
+
 protected:
 	void _adjustSize();
 
@@ -304,7 +542,7 @@ struct KsCPUCheckBoxWidget : public KsCheckBoxTreeWidget
 {
 	KsCPUCheckBoxWidget() = delete;
 
-	KsCPUCheckBoxWidget(struct tep_handle *pe,
+	KsCPUCheckBoxWidget(kshark_data_stream *stream,
 			    QWidget *parent = nullptr);
 };
 
@@ -316,7 +554,7 @@ struct KsTasksCheckBoxWidget : public KsCheckBoxTableWidget
 {
 	KsTasksCheckBoxWidget() = delete;
 
-	KsTasksCheckBoxWidget(struct tep_handle *pe,
+	KsTasksCheckBoxWidget(kshark_data_stream *stream,
 			      bool cond = true,
 			      QWidget *parent = nullptr);
 
@@ -336,12 +574,17 @@ struct KsEventsCheckBoxWidget : public KsCheckBoxTreeWidget
 {
 	KsEventsCheckBoxWidget() = delete;
 
-	KsEventsCheckBoxWidget(struct tep_handle *pe,
+	KsEventsCheckBoxWidget(kshark_data_stream *stream,
 			       QWidget *parent = nullptr);
 
 	QStringList getCheckedEvents(bool option);
 
 	void removeSystem(QString name);
+
+private:
+	void _makeItems(kshark_data_stream *stream, QVector<int> eventIds);
+
+	void _makeTepEventItems(kshark_data_stream *stream, QVector<int> eventIds);
 };
 
 /**
@@ -351,8 +594,56 @@ struct KsPluginCheckBoxWidget : public KsCheckBoxTableWidget
 {
 	KsPluginCheckBoxWidget() = delete;
 
-	KsPluginCheckBoxWidget(QStringList pluginList,
+	KsPluginCheckBoxWidget(int sd, QStringList pluginList,
 			       QWidget *parent = nullptr);
+
+	void setInfo(int row, QString info);
+
+	void setActive(QVector<int> rows, bool a);
 };
 
+/**
+ * The KsDStreamCheckBoxWidget class provides a widget for selecting Data streams.
+ */
+struct KsDStreamCheckBoxWidget : public KsCheckBoxTableWidget
+{
+	explicit KsDStreamCheckBoxWidget(QWidget *parent = nullptr);
+};
+
+/**
+ * The KsEventFieldSelectWidget class provides a widget for selecting a data
+ * field of the trace event.
+ */
+class KsEventFieldSelectWidget : public QWidget
+{
+	Q_OBJECT
+public:
+	explicit KsEventFieldSelectWidget(QWidget *parent = nullptr);
+
+	/** Get the currently selected stream Id. */
+	int streamId() const {return _streamComboBox.currentData().toInt();}
+
+	/** Get the currently selected event name. */
+	QString eventName() const {return _eventComboBox.currentText();}
+
+	/** Get the currently selected field name. */
+	QString fieldName() const {return _fieldComboBox.currentText();}
+
+	void setStreamCombo();
+
+private slots:
+	void _streamChanged(const QString &stream);
+
+	void _eventChanged(const QString &event);
+
+private:
+	QVBoxLayout	_topLayout;
+
+	QComboBox	_streamComboBox, _eventComboBox, _fieldComboBox;
+
+	QLabel		_streamLabel, _eventLabel, _fieldLabel;
+};
+
+}; // KsWidgetsLib
+
 #endif
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v2 12/27] kernel-shark: Add combo point to Mark
  2021-02-11 10:31 [PATCH v2 00/27] Complete the KernelShark v2 transformation Yordan Karadzhov (VMware)
                   ` (10 preceding siblings ...)
  2021-02-11 10:31 ` [PATCH v2 11/27] kernel-shark: Update KsWidgetsLib Yordan Karadzhov (VMware)
@ 2021-02-11 10:31 ` Yordan Karadzhov (VMware)
  2021-02-11 10:31 ` [PATCH v2 13/27] kernel-shark: Add new methods to KsPlot::Mark Yordan Karadzhov (VMware)
                   ` (15 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Yordan Karadzhov (VMware) @ 2021-02-11 10:31 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

KsPlot::Mark uses colored point to highlight the selected trace event
on CPU and Task plots. Here we add a combo point that will highlight
the selected event in Combo plots, like "Virtual Combo" for example.
Those Combo plots will be introduced at the level of the GUI code in
successive patches.

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 src/KsPlotTools.cpp | 23 ++++++++++++++++++++++-
 src/KsPlotTools.hpp | 15 ++++++++++++++-
 2 files changed, 36 insertions(+), 2 deletions(-)

diff --git a/src/KsPlotTools.cpp b/src/KsPlotTools.cpp
index ac9c5b2..225dc34 100644
--- a/src/KsPlotTools.cpp
+++ b/src/KsPlotTools.cpp
@@ -701,7 +701,7 @@ void Mark::_draw(const Color &col, float size) const
 void Mark::setDPR(int dpr)
 {
 	_size = 1.5 * dpr;
-	_task._size = _cpu._size = 1.5 + 4.0 * dpr;
+	_task._size = _cpu._size = _combo._size = 1.5 + 4.0 * dpr;
 }
 
 /**
@@ -715,6 +715,7 @@ void Mark::setX(int x)
 	_b.setX(x);
 	_cpu.setX(x);
 	_task.setX(x);
+	_combo.setX(x);
 }
 
 /**
@@ -769,6 +770,26 @@ void Mark::setTaskVisible(bool v)
 	_task._visible = v;
 }
 
+/**
+ * @brief Set the visiblity of the Mark's Combo point.
+ *
+ * @param v: If True, the Task point will be visible.
+ */
+void Mark::setComboVisible(bool v)
+{
+	_combo._visible = v;
+}
+
+/**
+ * @brief Set the Y coordinates (vertical) of the Mark's Combo points.
+ *
+ * @param yCombo: Y coordinate of the Mark's Task point.
+ */
+void Mark::setComboY(int yCombo)
+{
+	_combo.setY(yCombo);
+}
+
 /**
  * @brief Create a default Bin.
  */
diff --git a/src/KsPlotTools.hpp b/src/KsPlotTools.hpp
index c993181..a9a5ba8 100644
--- a/src/KsPlotTools.hpp
+++ b/src/KsPlotTools.hpp
@@ -406,6 +406,16 @@ public:
 	/** If True, the Mark will be plotted as a dashed line. */
 	void setDashed(bool d) {_dashed = d;}
 
+	/** Get the Y coordinate of the Mark's Combo point. */
+	int comboY() const {return _combo.y();}
+
+	void setComboY(int yCombo);
+
+	/** Is the Combo point visible. */
+	bool comboIsVisible() const {return _combo._visible;}
+
+	void setComboVisible(bool v);
+
 private:
 	void _draw(const Color &col, float size = 1.) const override;
 
@@ -421,7 +431,10 @@ private:
 	/** A point indicating the position of the Mark in a Task graph. */
 	Point _task;
 
-	/* If True, plot the Mark as a dashed line. */
+	/** A point indicating the position of the Mark in a Combo graph. */
+	Point _combo;
+
+	/** If True, plot the Mark as a dashed line. */
 	bool _dashed;
 };
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v2 13/27] kernel-shark: Add new methods to KsPlot::Mark
  2021-02-11 10:31 [PATCH v2 00/27] Complete the KernelShark v2 transformation Yordan Karadzhov (VMware)
                   ` (11 preceding siblings ...)
  2021-02-11 10:31 ` [PATCH v2 12/27] kernel-shark: Add combo point to Mark Yordan Karadzhov (VMware)
@ 2021-02-11 10:31 ` Yordan Karadzhov (VMware)
  2021-02-11 10:31 ` [PATCH v2 14/27] kernel-shark: Update the plotting example Yordan Karadzhov (VMware)
                   ` (14 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Yordan Karadzhov (VMware) @ 2021-02-11 10:31 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

The new "get" methods are needed by the logic of the GUI.

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 src/KsPlotTools.hpp | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/src/KsPlotTools.hpp b/src/KsPlotTools.hpp
index a9a5ba8..1725926 100644
--- a/src/KsPlotTools.hpp
+++ b/src/KsPlotTools.hpp
@@ -395,12 +395,24 @@ public:
 
 	void setY(int yA, int yB);
 
+	/** Get the Y coordinate of the Mark's CPU point. */
+	int cpuY() const {return _cpu.y();}
+
 	void setCPUY(int yCPU);
 
+	/** Is the CPU point visible. */
+	bool cpuIsVisible() const {return _cpu._visible;}
+
 	void setCPUVisible(bool v);
 
+	/** Get the Y coordinate of the Mark's Task point. */
+	int taskY() const {return _task.y();}
+
 	void setTaskY(int yTask);
 
+	/** Is the Task point visible. */
+	bool taskIsVisible() const {return _task._visible;}
+
 	void setTaskVisible(bool v);
 
 	/** If True, the Mark will be plotted as a dashed line. */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v2 14/27] kernel-shark: Update the plotting example
  2021-02-11 10:31 [PATCH v2 00/27] Complete the KernelShark v2 transformation Yordan Karadzhov (VMware)
                   ` (12 preceding siblings ...)
  2021-02-11 10:31 ` [PATCH v2 13/27] kernel-shark: Add new methods to KsPlot::Mark Yordan Karadzhov (VMware)
@ 2021-02-11 10:31 ` Yordan Karadzhov (VMware)
  2021-02-11 10:31 ` [PATCH v2 15/27] kernel-shark: Search for font with Cmake at pre-build Yordan Karadzhov (VMware)
                   ` (13 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Yordan Karadzhov (VMware) @ 2021-02-11 10:31 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

The compilation of the plotting example is re-enabled and it is
made compatible with the new version of the C API of libkshark
(KernelShark 2.0).

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 examples/CMakeLists.txt |  6 +--
 examples/dataplot.cpp   | 88 ++++++++++++++++++++++++++++-------------
 2 files changed, 63 insertions(+), 31 deletions(-)

diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index 8360841..e6af5f2 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -20,9 +20,9 @@ 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)
+message(STATUS "dataplot")
+add_executable(dplot          dataplot.cpp)
+target_link_libraries(dplot   kshark-plot)
 
 if (Qt5Widgets_FOUND)
 
diff --git a/examples/dataplot.cpp b/examples/dataplot.cpp
index 94841e7..b3ff29f 100644
--- a/examples/dataplot.cpp
+++ b/examples/dataplot.cpp
@@ -10,6 +10,7 @@
 // C++
 #include <vector>
 #include <iostream>
+#include <sstream>
 
 // OpenGL
 #include <GL/freeglut.h>
@@ -20,25 +21,29 @@
 
 using namespace std;
 
-#define GRAPH_HEIGHT	40   // width of the graph in pixels
-#define GRAPH_H_MARGIN	50   // size of the white space surrounding the graph
-#define WINDOW_WIDTH	800  // width of the screen window in pixels
-#define WINDOW_HEIGHT	480  // height of the scrren window in pixels
+#define GRAPH_HEIGHT		40   // width of the graph in pixels
+#define GRAPH_H_MARGIN		15   // size of the white space surrounding
+				     // the graph
+#define GRAPH_LABEL_WIDTH	80   // width of the graph's label in pixels
+#define WINDOW_WIDTH		800  // width of the screen window in pixels
+#define WINDOW_HEIGHT		480  // height of the scrren window in pixels
 
 #define default_file (char*)"trace.dat"
 
 struct kshark_trace_histo	histo;
 vector<KsPlot::Graph *>		graphs;
+struct ksplot_font		font;
+int stream_id;
 
 void usage(const char *prog)
 {
 	cout << "Usage: " << prog << endl;
-	cout << "  -h	Display this help message\n";
+	cout << "  -h	Display this help message.\n";
 	cout << "  -s	Draw shapes. This demonstrates how to draw simple "
 	     << "geom. shapes.\n";
 	cout << "  -i	<file>	Input file and draw animated graphs.\n";
 	cout << "  No args.	Import " << default_file
-	     << "and draw animated graphs.\n";
+	     << " and draw animated graphs.\n";
 }
 
 /* An example function drawing something. */
@@ -57,6 +62,11 @@ void drawShapes()
 	t._color = {100, 200, 50};
 	t.draw();
 
+	/* Print/draw "Hello Kernel!". */
+	KsPlot::Color col = {50, 150, 255};
+	KsPlot::TextBox tb(&font, "Hello Kernel!", col, {250, 70}, 250);
+	tb.draw();
+
 	KsPlot::Rectangle r;
 	KsPlot::Point d(400, 200), e(400, 300), f(500, 300), g(500, 200);
 	r.setPoint(0, d);
@@ -78,13 +88,14 @@ void drawShapes()
 /* An example function demonstrating Zoom In and Zoom Out. */
 void play()
 {
-	KsPlot::ColorTable taskColors = KsPlot::getTaskColorTable();
-	KsPlot::ColorTable cpuColors = KsPlot::getCPUColorTable();
+	KsPlot::ColorTable taskColors = KsPlot::taskColorTable();
+	KsPlot::ColorTable cpuColors = KsPlot::CPUColorTable();
 	vector<KsPlot::Graph *>::iterator it;
+	KsPlot::Graph *graph;
 	vector<int> CPUs, Tasks;
 	bool zoomIn(true);
 	int base;
-	size_t i;
+	size_t i(1);
 
 	CPUs = {3, 4, 6};
 	Tasks = {}; // Add valid pids here, if you want task plots.
@@ -92,35 +103,47 @@ void play()
 	auto lamAddGraph = [&] (KsPlot::Graph *g) {
 		/* Set the dimensions of the Graph. */
 		g->setHeight(GRAPH_HEIGHT);
-		g->setHMargin(GRAPH_H_MARGIN);
 
 		/*
 		 * Set the Y coordinate of the Graph's base.
 		 * Remember that the "Y" coordinate is inverted.
 		 */
-		base = 1.7 * GRAPH_HEIGHT * (i + 1);
+		base = 1.7 * GRAPH_HEIGHT * (i++);
 		g->setBase(base);
 
+		g->setLabelAppearance(&font, {160, 255, 255}, GRAPH_LABEL_WIDTH,
+							      GRAPH_H_MARGIN);
+
 		/* Add the Graph. */
 		graphs.push_back(g);
 	};
 
-	for (i = 0; i < CPUs.size(); ++i)
-		lamAddGraph(new KsPlot::Graph(&histo, &taskColors,
-						      &taskColors));
+	for (auto const &cpu: CPUs) {
+		std::stringstream ss;
+		ss << "CPU " << cpu;
+
+		graph = new KsPlot::Graph(&histo, &taskColors, &taskColors);
+		graph->setLabelText(ss.str());
+		lamAddGraph(graph);
+	}
+
+	for (auto const &pid: Tasks) {
+		std::stringstream ss;
+		ss << "PID " << pid;
 
-	for (;i < CPUs.size() + Tasks.size(); ++i)
-		lamAddGraph(new KsPlot::Graph(&histo, &taskColors,
-						      &cpuColors));
+		graph = new KsPlot::Graph(&histo, &taskColors, &cpuColors);
+		graph->setLabelText(ss.str());
+		lamAddGraph(graph);
+	}
 
 	for (i = 1; i < 1000; ++i) {
 		it = graphs.begin();
 
 		for (int const &cpu: CPUs)
-			(*it++)->fillCPUGraph(cpu);
+			(*it++)->fillCPUGraph(stream_id, cpu);
 
 		for (int const &pid: Tasks)
-			(*it++)->fillTaskGraph(pid);
+			(*it++)->fillTaskGraph(stream_id, pid);
 
 		/* Clear the screen. */
 		glClear(GL_COLOR_BUFFER_BIT);
@@ -146,7 +169,8 @@ int main(int argc, char **argv)
 	struct kshark_context *kshark_ctx(nullptr);
 	struct kshark_entry **data(nullptr);
 	static char *input_file(nullptr);
-	bool status, shapes(false);
+	bool shapes(false);
+	char *font_file;
 	size_t r, nRows;
 	int c, nBins;
 
@@ -166,11 +190,20 @@ int main(int argc, char **argv)
 		}
 	}
 
+	font_file = ksplot_find_font_file("FreeMono", "FreeMonoBold");
+	if (!font_file)
+		return 1;
+
 	auto lamDraw = [&] (void (*func)(void)) {
-		/* Initialize OpenGL/Glut. */
+		/* Initialize Glut. */
 		glutInit(&argc, argv);
 		ksplot_make_scene(WINDOW_WIDTH, WINDOW_HEIGHT);
+
+		/* Initialize OpenGL. */
 		ksplot_init_opengl(1);
+		ksplot_resize_opengl(WINDOW_WIDTH, WINDOW_HEIGHT);
+
+		ksplot_init_font(&font, 18, font_file);
 
 		/* Display something. */
 		glutDisplayFunc(func);
@@ -191,8 +224,8 @@ int main(int argc, char **argv)
 	if (!input_file)
 		input_file = default_file;
 
-	status = kshark_open(kshark_ctx, input_file);
-	if (!status) {
+	stream_id = kshark_open(kshark_ctx, input_file);
+	if (stream_id < 0) {
 		kshark_free(kshark_ctx);
 		usage(argv[0]);
 		cerr << "\nFailed to open file " << input_file << endl;
@@ -201,12 +234,12 @@ int main(int argc, char **argv)
 	}
 
 	/* Load the content of the file into an array of entries. */
-	nRows = kshark_load_data_entries(kshark_ctx, &data);
+	nRows = kshark_load_entries(kshark_ctx, stream_id, &data);
 
 	/* Initialize the Visualization Model. */
 	ksmodel_init(&histo);
 
-	nBins = WINDOW_WIDTH - 2 * GRAPH_HEIGHT;
+	nBins = WINDOW_WIDTH - GRAPH_LABEL_WIDTH - 3 * GRAPH_H_MARGIN;
 	ksmodel_set_bining(&histo, nBins, data[0]->ts,
 					  data[nRows - 1]->ts);
 
@@ -216,6 +249,8 @@ int main(int argc, char **argv)
 	/* Play animated Graph. */
 	lamDraw(play);
 
+	free(font_file);
+
 	/* Free the memory. */
 	for (auto &g: graphs)
 		delete g;
@@ -227,9 +262,6 @@ int main(int argc, char **argv)
 	/* Reset (clear) the model. */
 	ksmodel_clear(&histo);
 
-	/* Close the file. */
-	kshark_close(kshark_ctx);
-
 	/* Close the session. */
 	kshark_free(kshark_ctx);
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v2 15/27] kernel-shark: Search for font with Cmake at pre-build
  2021-02-11 10:31 [PATCH v2 00/27] Complete the KernelShark v2 transformation Yordan Karadzhov (VMware)
                   ` (13 preceding siblings ...)
  2021-02-11 10:31 ` [PATCH v2 14/27] kernel-shark: Update the plotting example Yordan Karadzhov (VMware)
@ 2021-02-11 10:31 ` Yordan Karadzhov (VMware)
  2021-02-11 10:31 ` [PATCH v2 16/27] kernel-shark: Update KsDualMarker and KsGLWidget Yordan Karadzhov (VMware)
                   ` (12 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Yordan Karadzhov (VMware) @ 2021-02-11 10:31 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

Being able to load the Truetype font is compulsory for the functioning
of the visualization, hence the font must be considered as critical
external dependency. Add a search for the font file next to the search
of the other third party dependencies and have this file added as
definition in the Cmake-generated header file. This way we do not need
to search for the font every time when we open KernelShark.

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 .github/workflows/main.yml |  1 +
 CMakeLists.txt             | 14 ++++++++++++++
 README                     |  1 +
 build/deff.h.cmake         |  3 +++
 examples/CMakeLists.txt    |  4 ++--
 src/CMakeLists.txt         |  4 ++--
 tests/CMakeLists.txt       | 22 +++++++++++++---------
 tests/libkshark-tests.cpp  |  9 +++++++++
 8 files changed, 45 insertions(+), 13 deletions(-)

diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 2cce624..4125354 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -19,6 +19,7 @@ jobs:
         sudo apt-get update
         sudo apt-get install build-essential git cmake libjson-c-dev -y
         sudo apt-get install freeglut3-dev libxmu-dev libxi-dev -y
+        sudo apt-get install fonts-freefont-ttf
         sudo apt-get install qtbase5-dev libboost-all-dev -y
         git clone https://git.kernel.org/pub/scm/libs/libtrace/libtraceevent.git/
         cd libtraceevent
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b9b947e..8ca33fd 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -49,6 +49,20 @@ set(OpenGL_GL_PREFERENCE LEGACY)
 find_package(OpenGL)
 find_package(GLUT)
 
+set(KS_FONT FreeSans)
+execute_process(COMMAND  bash "-c" "fc-list '${KS_FONT}' |grep ${KS_FONT}.ttf | cut -d':' -f 1 -z"
+                OUTPUT_VARIABLE TT_FONT_FILE)
+
+if (TT_FONT_FILE)
+
+  message(STATUS "Found font: ${TT_FONT_FILE}")
+
+else (TT_FONT_FILE)
+
+  message(WARNING "\nCould not find font ${KS_FONT}!\n")
+
+endif (TT_FONT_FILE)
+
 find_package(Qt5Widgets 5.7.1)
 find_package(Qt5Network)
 if (Qt5Widgets_FOUND)
diff --git a/README b/README
index d7efd96..a7e66df 100644
--- a/README
+++ b/README
@@ -12,6 +12,7 @@ KernelShark has the following external dependencies:
 1. In order to install the packages on Ubuntu do the following:
     sudo apt-get install build-essential git cmake libjson-c-dev -y
     sudo apt-get install freeglut3-dev libxmu-dev libxi-dev -y
+    sudo apt-get install fonts-freefont-ttf
     sudo apt-get install qtbase5-dev -y
 
 1.1 I you want to be able to generate Doxygen documentation:
diff --git a/build/deff.h.cmake b/build/deff.h.cmake
index 423a2fd..06cbf16 100644
--- a/build/deff.h.cmake
+++ b/build/deff.h.cmake
@@ -26,6 +26,9 @@
 /** GLUT has been found. */
 #cmakedefine GLUT_FOUND
 
+/** Truetype font file. */
+#cmakedefine TT_FONT_FILE "@TT_FONT_FILE@"
+
 /** Qt - old version detected. */
 #cmakedefine QT_VERSION_LESS_5_11
 
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index e6af5f2..bc17635 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -24,7 +24,7 @@ message(STATUS "dataplot")
 add_executable(dplot          dataplot.cpp)
 target_link_libraries(dplot   kshark-plot)
 
-if (Qt5Widgets_FOUND)
+if (Qt5Widgets_FOUND AND TT_FONT_FILE)
 
     message(STATUS "widgetdemo")
     add_executable(widgetdemo          widgetdemo.cpp)
@@ -34,4 +34,4 @@ if (Qt5Widgets_FOUND)
     add_executable(cmd_split           cmd_split.cpp)
     target_link_libraries(cmd_split    kshark-gui)
 
-endif (Qt5Widgets_FOUND)
+endif (Qt5Widgets_FOUND AND TT_FONT_FILE)
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 140fed8..6c02d82 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -63,7 +63,7 @@ if (OPENGL_FOUND)
 
 endif (OPENGL_FOUND)
 
-if (Qt5Widgets_FOUND AND Qt5Network_FOUND)
+if (Qt5Widgets_FOUND AND Qt5Network_FOUND AND TT_FONT_FILE)
 
     message(STATUS "libkshark-gui")
     set (ks-guiLib_hdr  KsUtils.hpp
@@ -134,7 +134,7 @@ if (Qt5Widgets_FOUND AND Qt5Network_FOUND)
             DESTINATION ${_INSTALL_PREFIX}/bin/
                 COMPONENT                 kernelshark)
 
-endif (Qt5Widgets_FOUND AND Qt5Network_FOUND)
+endif (Qt5Widgets_FOUND AND Qt5Network_FOUND AND TT_FONT_FILE)
 
 add_subdirectory(plugins)
 set(PLUGINS ${PLUGINS} PARENT_SCOPE)
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 28f711b..1814c72 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -38,13 +38,17 @@ add_test(NAME              "libkshark_tests"
          COMMAND           ${KS_TEST_DIR}/kshark-tests --log_format=HRF
          WORKING_DIRECTORY ${KS_TEST_DIR})
 
-add_executable(kshark-gui-tests          libkshark-gui-tests.cpp)
-target_include_directories(kshark-gui-tests PRIVATE ${Boost_INCLUDE_DIRS})
-target_compile_definitions(kshark-gui-tests PRIVATE "BOOST_TEST_DYN_LINK=1")
-target_link_libraries(kshark-gui-tests   kshark-gui
-                                     ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY})
+if (Qt5Widgets_FOUND AND TT_FONT_FILE)
 
-message(STATUS "libkshark-gui_tests")
-add_test(NAME              "libkshark-gui_tests"
-         COMMAND           ${KS_TEST_DIR}/kshark-gui-tests --log_format=HRF
-         WORKING_DIRECTORY ${KS_TEST_DIR})
+    add_executable(kshark-gui-tests          libkshark-gui-tests.cpp)
+    target_include_directories(kshark-gui-tests PRIVATE ${Boost_INCLUDE_DIRS})
+    target_compile_definitions(kshark-gui-tests PRIVATE "BOOST_TEST_DYN_LINK=1")
+    target_link_libraries(kshark-gui-tests   kshark-gui
+                          ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY})
+
+    message(STATUS "libkshark-gui_tests")
+    add_test(NAME              "libkshark-gui_tests"
+             COMMAND           ${KS_TEST_DIR}/kshark-gui-tests --log_format=HRF
+             WORKING_DIRECTORY ${KS_TEST_DIR})
+
+endif (Qt5Widgets_FOUND AND TT_FONT_FILE)
diff --git a/tests/libkshark-tests.cpp b/tests/libkshark-tests.cpp
index a22c1e5..5f7c88a 100644
--- a/tests/libkshark-tests.cpp
+++ b/tests/libkshark-tests.cpp
@@ -435,3 +435,12 @@ BOOST_AUTO_TEST_CASE(readout_plugins)
 
 	kshark_free(kshark_ctx);
 }
+
+BOOST_AUTO_TEST_CASE(check_font_found)
+{
+#ifdef TT_FONT_FILE
+BOOST_REQUIRE(true);
+#else
+BOOST_REQUIRE(false);
+#endif
+}
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v2 16/27] kernel-shark: Update KsDualMarker and KsGLWidget
  2021-02-11 10:31 [PATCH v2 00/27] Complete the KernelShark v2 transformation Yordan Karadzhov (VMware)
                   ` (14 preceding siblings ...)
  2021-02-11 10:31 ` [PATCH v2 15/27] kernel-shark: Search for font with Cmake at pre-build Yordan Karadzhov (VMware)
@ 2021-02-11 10:31 ` Yordan Karadzhov (VMware)
  2021-02-11 10:31 ` [PATCH v2 17/27] kernel-shark: Update KsTraceGraph and KsQuickContextMenu Yordan Karadzhov (VMware)
                   ` (11 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Yordan Karadzhov (VMware) @ 2021-02-11 10:31 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

The compilation of KsDualMarker.cpp and KsGLWidget.cpp is re-enabled
and all functionalities are made compatible with the new version of
the C API of libkshark (KernelShark 2.0). The two source files are
updated in a single patch because of their interdependence.

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 src/CMakeLists.txt   |   8 +-
 src/KsDualMarker.cpp |  23 +-
 src/KsDualMarker.hpp |  16 +-
 src/KsGLWidget.cpp   | 696 +++++++++++++++++++++++++++++--------------
 src/KsGLWidget.hpp   | 187 +++++++++---
 5 files changed, 629 insertions(+), 301 deletions(-)

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 6c02d82..3dc7213 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -68,9 +68,9 @@ if (Qt5Widgets_FOUND AND Qt5Network_FOUND AND TT_FONT_FILE)
     message(STATUS "libkshark-gui")
     set (ks-guiLib_hdr  KsUtils.hpp
                         KsModels.hpp
-#                         KsGLWidget.hpp
+                        KsGLWidget.hpp
                         KsSearchFSM.hpp
-#                         KsDualMarker.hpp
+                        KsDualMarker.hpp
                         KsWidgetsLib.hpp)
 #                         KsTraceGraph.hpp
 #                         KsTraceViewer.hpp
@@ -84,9 +84,9 @@ if (Qt5Widgets_FOUND AND Qt5Network_FOUND AND TT_FONT_FILE)
     add_library(kshark-gui  SHARED  ${ks-guiLib_hdr_moc}    KsUtils.cpp
                                                             KsModels.cpp
 #                                                             KsSession.cpp
-#                                                             KsGLWidget.cpp
+                                                            KsGLWidget.cpp
                                                             KsSearchFSM.cpp
-#                                                             KsDualMarker.cpp
+                                                            KsDualMarker.cpp
                                                             KsWidgetsLib.cpp)
 #                                                             KsTraceGraph.cpp
 #                                                             KsTraceViewer.cpp
diff --git a/src/KsDualMarker.cpp b/src/KsDualMarker.cpp
index 90c5373..9fb68e7 100644
--- a/src/KsDualMarker.cpp
+++ b/src/KsDualMarker.cpp
@@ -59,10 +59,8 @@ void KsGraphMark::reset()
 {
 	_isSet = false;
 	_bin = -1;
-	_cpu = -1;
-	_task = -1;
 	_pos = 0;
-
+	_sd = 0;
 	_mark._visible = false;
 }
 
@@ -72,17 +70,17 @@ void KsGraphMark::reset()
  * @param data: Input location for the Data Store object.
  * @param histo: Input location for the model descriptor.
  * @param pos: The index inside the data array this marker will points to.
- * @param cpuGraph: The index of the CPU Graph this marker points to.
- * @param taskGraph: The index of the Task Graph this marker points to.
+ * @param sd: Data stream identifier.
  */
 bool KsGraphMark::set(const KsDataStore &data,
 		      kshark_trace_histo *histo,
-		      size_t pos, int cpuGraph, int taskGraph)
+		      ssize_t pos, int sd)
 {
 	uint8_t visFlags;
 
 	_isSet = true;
 	_pos = pos;
+	_sd = sd;
 	_ts = data.rows()[_pos]->ts;
 	visFlags = data.rows()[_pos]->visible;
 
@@ -92,9 +90,6 @@ bool KsGraphMark::set(const KsDataStore &data,
 	else
 		_mark.setDashed(true);
 
-	_cpu = cpuGraph;
-	_task = taskGraph;
-
 	if (_ts > histo->max || _ts < histo->min) {
 		_bin = -1;
 		_mark._visible = false;
@@ -119,7 +114,7 @@ bool KsGraphMark::update(const KsDataStore &data, kshark_trace_histo *histo)
 	if (!_isSet)
 		return false;
 
-	return set(data, histo, this->_pos, this->_cpu, this->_task);
+	return set(data, histo, this->_pos, this->_sd);
 }
 
 /** Unset the Marker and make it invisible. */
@@ -151,8 +146,8 @@ KsDualMarkerSM::KsDualMarkerSM(QWidget *parent)
 {
 	QString styleSheetA, styleSheetB;
 
-	_buttonA.setFixedWidth(STRING_WIDTH(" Marker A "));
-	_buttonB.setFixedWidth(STRING_WIDTH(" Marker B "));
+	_buttonA.setFixedWidth(STRING_WIDTH(" Marker A ") + FONT_WIDTH);
+	_buttonB.setFixedWidth(STRING_WIDTH(" Marker B ") + FONT_WIDTH);
 
 	for (auto const &l: {&_labelMA, &_labelMB, &_labelDelta}) {
 		l->setFrameStyle(QFrame::Panel | QFrame::Sunken);
@@ -318,10 +313,10 @@ void KsDualMarkerSM::updateMarkers(const KsDataStore &data,
 				   KsGLWidget *glw)
 {
 	if(_markA.update(data, glw->model()->histo()))
-		glw->setMark(&_markA);
+		glw->setMarkPoints(data, &_markA);
 
 	if(_markB.update(data, glw->model()->histo()))
-		glw->setMark(&_markB);
+		glw->setMarkPoints(data, &_markB);
 
 	updateLabels();
 }
diff --git a/src/KsDualMarker.hpp b/src/KsDualMarker.hpp
index 597bddb..0dcaf93 100644
--- a/src/KsDualMarker.hpp
+++ b/src/KsDualMarker.hpp
@@ -66,9 +66,7 @@ public:
 
 	bool set(const KsDataStore &data,
 		 kshark_trace_histo *histo,
-		 size_t pos,
-		 int cpuGraph,
-		 int taskGraph);
+		 ssize_t pos, int sd);
 
 	bool update(const KsDataStore &data, kshark_trace_histo *histo);
 
@@ -83,24 +81,20 @@ public:
 
 	void remove();
 
-public:
 	/** Is this marker set. */
 	bool		_isSet;
 
 	/** The number of the bin this marker points to. */
 	int		_bin;
 
-	/** The index of the CPU Graph this marker points to. */
-	int		_cpu;
-
-	/** The  index of the Task Graph this marker points to. */
-	int		_task;
+	/** Data stream identifier of the Graph this marker points to. */
+	int		_sd;
 
 	/** The index inside the data array this marker points to. */
-	size_t		_pos;
+	ssize_t		_pos;
 
 	/** The timestamp of the marker. */
-	uint64_t	_ts;
+	int64_t		_ts;
 
 	/** The RGB color of the marker. */
 	QColor		_color;
diff --git a/src/KsGLWidget.cpp b/src/KsGLWidget.cpp
index 78ded33..9f8e43b 100644
--- a/src/KsGLWidget.cpp
+++ b/src/KsGLWidget.cpp
@@ -14,16 +14,35 @@
 #include <GL/gl.h>
 
 // KernelShark
+#include "libkshark-plugin.h"
 #include "KsGLWidget.hpp"
 #include "KsUtils.hpp"
 #include "KsPlugins.hpp"
-#include "KsDualMarker.hpp"
+
+/** A stream operator for converting vector of integers into KsPlotEntry. */
+KsPlotEntry &operator <<(KsPlotEntry &plot, QVector<int> &v)
+{
+	plot._streamId = v.takeFirst();
+	plot._type = v.takeFirst();
+	plot._id = v.takeFirst();
+
+	return plot;
+}
+
+/** A stream operator for converting KsPlotEntry into vector of integers. */
+void operator >>(const KsPlotEntry &plot, QVector<int> &v)
+{
+	v.append(plot._streamId);
+	v.append(plot._type);
+	v.append(plot._id);
+}
 
 /** Create a default (empty) OpenGL widget. */
 KsGLWidget::KsGLWidget(QWidget *parent)
 : QOpenGLWidget(parent),
-  _hMargin(20),
-  _vMargin(30),
+  _labelSize(100),
+  _hMargin(15),
+  _vMargin(25),
   _vSpacing(20),
   _mState(nullptr),
   _data(nullptr),
@@ -40,10 +59,28 @@ KsGLWidget::KsGLWidget(QWidget *parent)
 	connect(&_model, SIGNAL(modelReset()), this, SLOT(update()));
 }
 
+void KsGLWidget::_freeGraphs()
+{
+	for (auto &stream: _graphs) {
+		for (auto &g: stream)
+			delete g;
+		stream.resize(0);
+	}
+}
+
+void KsGLWidget::freePluginShapes()
+{
+	while (!_shapes.empty()) {
+		auto s = _shapes.front();
+		_shapes.pop_front();
+		delete s;
+	}
+}
+
 KsGLWidget::~KsGLWidget()
 {
-	for (auto &g: _graphs)
-		delete g;
+	_freeGraphs();
+	freePluginShapes();
 }
 
 /** Reimplemented function used to set up all required OpenGL resources. */
@@ -51,6 +88,11 @@ void KsGLWidget::initializeGL()
 {
 	_dpr = QApplication::desktop()->devicePixelRatio();
 	ksplot_init_opengl(_dpr);
+
+	ksplot_init_font(&_font, 15, TT_FONT_FILE);
+
+	_labelSize = _getMaxLabelSize() + FONT_WIDTH * 2;
+	update();
 }
 
 /**
@@ -67,7 +109,9 @@ void KsGLWidget::resizeGL(int w, int h)
 	 * From the size of the widget, calculate the number of bins.
 	 * One bin will correspond to one pixel.
 	 */
-	int nBins = width() - _hMargin * 2;
+	int nBins = width() - _bin0Offset() - _hMargin;
+	if (nBins <= 0)
+		return;
 
 	/*
 	 * Reload the data. The range of the histogram is the same
@@ -78,7 +122,7 @@ void KsGLWidget::resizeGL(int w, int h)
 			   _model.histo()->min,
 			   _model.histo()->max);
 
-	_model.fill(_data->rows(), _data->size());
+	_model.fill(_data);
 }
 
 /** Reimplemented function used to plot trace graphs. */
@@ -91,24 +135,23 @@ void KsGLWidget::paintGL()
 	if (isEmpty())
 		return;
 
+	render();
+
 	/* Draw the time axis. */
 	_drawAxisX(size);
 
-	/* Process and draw all graphs by using the built-in logic. */
-	_makeGraphs(_cpuList, _taskList);
-	for (auto const &g: _graphs)
-		g->draw(size);
+	for (auto const &stream: _graphs)
+		for (auto const &g: stream)
+			g->draw(size);
 
-	/* Process and draw all plugin-specific shapes. */
-	_makePluginShapes(_cpuList, _taskList);
-	while (!_shapes.empty()) {
-		auto s = _shapes.front();
-		_shapes.pop_front();
+	for (auto const &s: _shapes) {
+		if (!s)
+			continue;
 
-		s->_size = size;
-		s->draw();
+		if (s->_size < 0)
+			s->_size = size + abs(s->_size + 1);
 
-		delete s;
+		s->draw();
 	}
 
 	/*
@@ -120,22 +163,25 @@ void KsGLWidget::paintGL()
 	_mState->activeMarker().draw();
 }
 
+/** Process and draw all graphs. */
+void KsGLWidget::render()
+{
+	/* Process and draw all graphs by using the built-in logic. */
+	_makeGraphs();
+
+	/* Process and draw all plugin-specific shapes. */
+	_makePluginShapes();
+};
+
 /** Reset (empty) the widget. */
 void KsGLWidget::reset()
 {
-	_cpuList = {};
-	_taskList = {};
+	_streamPlots.clear();
+	_comboPlots.clear();
 	_data = nullptr;
 	_model.reset();
 }
 
-/** Check if the widget is empty (not showing anything). */
-bool KsGLWidget::isEmpty() const {
-	return !_data ||
-	       !_data->size() ||
-	       (!_cpuList.size() && !_taskList.size());
-}
-
 /** Reimplemented event handler used to receive mouse press events. */
 void KsGLWidget::mousePressEvent(QMouseEvent *event)
 {
@@ -146,7 +192,7 @@ void KsGLWidget::mousePressEvent(QMouseEvent *event)
 }
 
 int KsGLWidget::_getLastTask(struct kshark_trace_histo *histo,
-			     int bin, int cpu)
+			     int bin, int sd, int cpu)
 {
 	kshark_context *kshark_ctx(nullptr);
 	kshark_entry_collection *col;
@@ -157,15 +203,17 @@ int KsGLWidget::_getLastTask(struct kshark_trace_histo *histo,
 
 	col = kshark_find_data_collection(kshark_ctx->collections,
 					  KsUtils::matchCPUVisible,
-					  cpu);
+					  sd, &cpu, 1);
 
 	for (int b = bin; b >= 0; --b) {
-		pid = ksmodel_get_pid_back(histo, b, cpu, false, col, nullptr);
+		pid = ksmodel_get_pid_back(histo, b, sd, cpu,
+					   false, col, nullptr);
 		if (pid >= 0)
 			return pid;
 	}
 
 	return ksmodel_get_pid_back(histo, LOWER_OVERFLOW_BIN,
+					   sd,
 					   cpu,
 					   false,
 					   col,
@@ -173,7 +221,7 @@ int KsGLWidget::_getLastTask(struct kshark_trace_histo *histo,
 }
 
 int KsGLWidget::_getLastCPU(struct kshark_trace_histo *histo,
-			     int bin, int pid)
+			    int bin, int sd, int pid)
 {
 	kshark_context *kshark_ctx(nullptr);
 	kshark_entry_collection *col;
@@ -184,15 +232,17 @@ int KsGLWidget::_getLastCPU(struct kshark_trace_histo *histo,
 
 	col = kshark_find_data_collection(kshark_ctx->collections,
 					  kshark_match_pid,
-					  pid);
+					  sd, &pid, 1);
 
 	for (int b = bin; b >= 0; --b) {
-		cpu = ksmodel_get_cpu_back(histo, b, pid, false, col, nullptr);
+		cpu = ksmodel_get_cpu_back(histo, b, sd, pid,
+					   false, col, nullptr);
 		if (cpu >= 0)
 			return cpu;
 	}
 
 	return ksmodel_get_cpu_back(histo, LOWER_OVERFLOW_BIN,
+					   sd,
 					   pid,
 					   false,
 					   col,
@@ -203,7 +253,7 @@ int KsGLWidget::_getLastCPU(struct kshark_trace_histo *histo,
 /** Reimplemented event handler used to receive mouse move events. */
 void KsGLWidget::mouseMoveEvent(QMouseEvent *event)
 {
-	int bin, cpu, pid;
+	int bin, sd, cpu, pid;
 	size_t row;
 	bool ret;
 
@@ -213,23 +263,22 @@ void KsGLWidget::mouseMoveEvent(QMouseEvent *event)
 	if (_rubberBand.isVisible())
 		_rangeBoundStretched(_posInRange(event->pos().x()));
 
-	bin = event->pos().x() - _hMargin;
-	cpu = getPlotCPU(event->pos());
-	pid = getPlotPid(event->pos());
+	bin = event->pos().x() - _bin0Offset();
+	getPlotInfo(event->pos(), &sd, &cpu, &pid);
 
-	ret = _find(bin, cpu, pid, 5, false, &row);
+	ret = _find(bin, sd, cpu, pid, 5, false, &row);
 	if (ret) {
 		emit found(row);
 	} else {
 		if (cpu >= 0) {
-			pid = _getLastTask(_model.histo(), bin, cpu);
+			pid = _getLastTask(_model.histo(), bin, sd, cpu);
 		}
 
 		if (pid > 0) {
-			cpu = _getLastCPU(_model.histo(), bin, pid);
+			cpu = _getLastCPU(_model.histo(), bin, sd, pid);
 		}
 
-		emit notFound(ksmodel_bin_ts(_model.histo(), bin), cpu, pid);
+		emit notFound(ksmodel_bin_ts(_model.histo(), bin), sd, cpu, pid);
 	}
 }
 
@@ -243,11 +292,11 @@ void KsGLWidget::mouseReleaseEvent(QMouseEvent *event)
 		size_t posMouseRel = _posInRange(event->pos().x());
 		int min, max;
 		if (_posMousePress < posMouseRel) {
-			min = _posMousePress - _hMargin;
-			max = posMouseRel - _hMargin;
+			min = _posMousePress - _bin0Offset();
+			max = posMouseRel - _bin0Offset();
 		} else {
-			max = _posMousePress - _hMargin;
-			min = posMouseRel - _hMargin;
+			max = _posMousePress - _bin0Offset();
+			min = posMouseRel - _bin0Offset();
 		}
 
 		_rangeChanged(min, max);
@@ -257,7 +306,21 @@ void KsGLWidget::mouseReleaseEvent(QMouseEvent *event)
 /** Reimplemented event handler used to receive mouse double click events. */
 void KsGLWidget::mouseDoubleClickEvent(QMouseEvent *event)
 {
-	if (event->button() == Qt::LeftButton)
+	KsPlot::PlotObject *pluginClicked(nullptr);
+	double distance, distanceMin = FONT_HEIGHT;
+
+	for (auto const &s: _shapes) {
+		distance = s->distance(event->pos().x(), event->pos().y());
+		if (distance < distanceMin) {
+			distanceMin = distance;
+			pluginClicked = s;
+		}
+	}
+
+	if (pluginClicked)
+		pluginClicked->doubleClick();
+
+	else if (event->button() == Qt::LeftButton)
 		_findAndSelect(event);
 }
 
@@ -266,7 +329,8 @@ void KsGLWidget::wheelEvent(QWheelEvent * event)
 {
 	int zoomFocus;
 
-	if (isEmpty())
+	if (QApplication::keyboardModifiers() != Qt::ControlModifier ||
+	    isEmpty())
 		return;
 
 	if (_mState->activeMarker()._isSet &&
@@ -281,7 +345,7 @@ void KsGLWidget::wheelEvent(QWheelEvent * event)
 		 * Use the position of the mouse as a focus point for the
 		 * zoom.
 		 */
-		zoomFocus = event->pos().x() - _hMargin;
+		zoomFocus = event->pos().x() - _bin0Offset();
 	}
 
 	if (event->delta() > 0) {
@@ -353,40 +417,53 @@ void KsGLWidget::keyReleaseEvent(QKeyEvent *event)
  */
 void KsGLWidget::loadData(KsDataStore *data)
 {
+	kshark_context *kshark_ctx(nullptr);
+	QVector<int> streamIds, plotVec;
 	uint64_t tMin, tMax;
 	int nCPUs, nBins;
 
+	if (!kshark_instance(&kshark_ctx) || !kshark_ctx->n_streams)
+		return;
+
+	loadColors();
+
 	_data = data;
+	_model.reset();
+	_streamPlots.clear();
+
+	/*
+	 * Make default CPU and Task lists. All CPUs from all Data streams will
+	 * be plotted. No tasks will be plotted.
+	 */
+	streamIds = KsUtils::getStreamIdList(kshark_ctx);
+	for (auto const &sd: streamIds) {
+		nCPUs = kshark_ctx->stream[sd]->n_cpus;
+		plotVec.clear();
+
+		/* If the number of CPUs is too big show only the first 16. */
+		if (nCPUs > KS_MAX_START_PLOTS / kshark_ctx->n_streams)
+			nCPUs = KS_MAX_START_PLOTS / kshark_ctx->n_streams;
+
+		for (int i = 0; i < nCPUs; ++i)
+			plotVec.append(i);
+
+		_streamPlots[sd]._cpuList = plotVec;
+		_streamPlots[sd]._taskList = {};
+	}
 
 	/*
 	 * From the size of the widget, calculate the number of bins.
 	 * One bin will correspond to one pixel.
 	 */
-	nBins = width() - _hMargin * 2;
-
-	_model.reset();
+	nBins = width() - _bin0Offset() - _hMargin;
+	if (nBins < 0)
+		nBins = 0;
 
 	/* Now load the entire set of trace data. */
 	tMin = _data->rows()[0]->ts;
 	tMax = _data->rows()[_data->size() - 1]->ts;
 	ksmodel_set_bining(_model.histo(), nBins, tMin, tMax);
-	_model.fill(_data->rows(), _data->size());
-
-	/* Make a default CPU list. All CPUs (or the first N_max) will be plotted. */
-	_cpuList = {};
-
-	nCPUs = tep_get_cpus(_data->tep());
-	if (nCPUs > KS_MAX_START_PLOTS)
-		nCPUs = KS_MAX_START_PLOTS;
-
-	for (int i = 0; i < nCPUs; ++i)
-		_cpuList.append(i);
-
-	/* Make a default task list. No tasks will be plotted. */
-	_taskList = {};
-
-	loadColors();
-	_makeGraphs(_cpuList, _taskList);
+	_model.fill(_data);
 }
 
 /**
@@ -396,96 +473,78 @@ void KsGLWidget::loadData(KsDataStore *data)
 void KsGLWidget::loadColors()
 {
 	_pidColors.clear();
-	_pidColors = KsPlot::getTaskColorTable();
+	_pidColors = KsPlot::taskColorTable();
 	_cpuColors.clear();
-	_cpuColors = KsPlot::getCPUColorTable();
+	_cpuColors = KsPlot::CPUColorTable();
+	_streamColors.clear();
+	_streamColors = KsPlot::streamColorTable();
 }
 
 /**
  * Position the graphical elements of the marker according to the current
  * position of the graphs inside the GL widget.
  */
-void KsGLWidget::setMark(KsGraphMark *mark)
+void KsGLWidget::setMarkPoints(const KsDataStore &data, KsGraphMark *mark)
 {
-	mark->_mark.setDPR(_dpr);
-	mark->_mark.setX(mark->_bin + _hMargin);
-	mark->_mark.setY(_vMargin / 2 + 2, height() - _vMargin);
-
-	if (mark->_cpu >= 0) {
-		mark->_mark.setCPUY(_graphs[mark->_cpu]->getBase());
-		mark->_mark.setCPUVisible(true);
-	} else {
-		mark->_mark.setCPUVisible(false);
-	}
+	const kshark_entry *e = data.rows()[mark->_pos];
+	int sd = e->stream_id;
 
-	if (mark->_task >= 0) {
-		mark->_mark.setTaskY(_graphs[mark->_task]->getBase());
-		mark->_mark.setTaskVisible(true);
-	} else {
-		mark->_mark.setTaskVisible(false);
-	}
-}
+	mark->_mark.setDPR(_dpr);
+	mark->_mark.setX(mark->_bin + _bin0Offset());
+	mark->_mark.setY(_vMargin * 3 / 2 + 2, height() - _vMargin / 4);
 
-/**
- * @brief Check if a given KernelShark entry is ploted.
- *
- * @param e: Input location for the KernelShark entry.
- * @param graphCPU: Output location for index of the CPU graph to which this
- *		    entry belongs. If such a graph does not exist the outputted
- *		    value is "-1".
- * @param graphTask: Output location for index of the Task graph to which this
- *		     entry belongs. If such a graph does not exist the
- *		     outputted value is "-1".
- */
-void KsGLWidget::findGraphIds(const kshark_entry &e,
-			      int *graphCPU,
-			      int *graphTask)
-{
-	int graph(0);
-	bool cpuFound(false), taskFound(false);
+	mark->_mark.setCPUVisible(false);
+	mark->_mark.setTaskVisible(false);
+	mark->_mark.setComboVisible(false);
 
-	/*
-	 * Loop over all CPU graphs and try to find the one that
-	 * contains the entry.
-	 */
-	for (auto const &c: _cpuList) {
-		if (c == e.cpu) {
-			cpuFound = true;
-			break;
+	for (int i = 0; i < _streamPlots[sd]._cpuList.count(); ++i) {
+		if (_streamPlots[sd]._cpuList[i] == e->cpu) {
+			mark->_mark.setCPUY(_streamPlots[sd]._cpuGraphs[i]->base());
+			mark->_mark.setCPUVisible(true);
 		}
-		++graph;
 	}
 
-	if (cpuFound)
-		*graphCPU = graph;
-	else
-		*graphCPU = -1;
-
-	/*
-	 * Loop over all Task graphs and try to find the one that
-	 * contains the entry.
-	 */
-	graph = _cpuList.count();
-	for (auto const &p: _taskList) {
-		if (p == e.pid) {
-			taskFound = true;
-			break;
+	for (int i = 0; i < _streamPlots[sd]._taskList.count(); ++i) {
+		if (_streamPlots[sd]._taskList[i] == e->pid) {
+			mark->_mark.setTaskY(_streamPlots[sd]._taskGraphs[i]->base());
+			mark->_mark.setTaskVisible(true);
 		}
-		++graph;
 	}
 
-	if (taskFound)
-		*graphTask = graph;
-	else
-		*graphTask = -1;
+	for (auto const &c: _comboPlots)
+		for (auto const &p: c) {
+			if (p._streamId != e->stream_id)
+				continue;
+
+			if (p._type & KSHARK_CPU_DRAW &&
+			    p._id == e->cpu) {
+				mark->_mark.setComboY(p.base());
+				mark->_mark.setComboVisible(true);
+			} else if (p._type & KSHARK_TASK_DRAW &&
+				   p._id == e->pid) {
+				mark->_mark.setComboY(p.base());
+				mark->_mark.setComboVisible(true);
+			}
+		}
 }
 
 void KsGLWidget::_drawAxisX(float size)
 {
-	KsPlot::Point a0(_hMargin, _vMargin / 4), a1(_hMargin, _vMargin / 2);
-	KsPlot::Point b0(width() / 2, _vMargin / 4), b1(width() / 2, _vMargin / 2);
-	KsPlot::Point c0(width() - _hMargin, _vMargin / 4),
-			 c1(width() - _hMargin, _vMargin / 2);
+	int64_t model_min = model()->histo()->min;
+	int64_t model_max = model()->histo()->max;
+	uint64_t sec, usec, tsMid;
+	char *tMin, *tMid, *tMax;
+	int mid = (width() - _bin0Offset() - _hMargin) / 2;
+	int y_1 = _vMargin * 5 / 4;
+	int y_2 = _vMargin * 6 / 4;
+	int count;
+
+	KsPlot::Point a0(_bin0Offset(), y_1);
+	KsPlot::Point a1(_bin0Offset(), y_2);
+	KsPlot::Point b0(_bin0Offset() + mid, y_1);
+	KsPlot::Point b1(_bin0Offset() + mid, y_2);
+	KsPlot::Point c0(width() - _hMargin, y_1);
+	KsPlot::Point c1(width() - _hMargin, y_2);
 
 	a0._size = c0._size = _dpr;
 
@@ -495,109 +554,265 @@ void KsGLWidget::_drawAxisX(float size)
 	KsPlot::drawLine(b0, b1, {}, size);
 	KsPlot::drawLine(c0, c1, {}, size);
 	KsPlot::drawLine(a0, c0, {}, size);
+
+	if (model_min < 0)
+		model_min = 0;
+
+	kshark_convert_nano(model_min, &sec, &usec);
+	count = asprintf(&tMin,"%" PRIu64 ".%06" PRIu64 "", sec, usec);
+	if (count <= 0)
+		return;
+
+	tsMid = (model_min + model_max) / 2;
+	kshark_convert_nano(tsMid, &sec, &usec);
+	count = asprintf(&tMid, "%" PRIu64 ".%06" PRIu64 "", sec, usec);
+	if (count <= 0)
+		return;
+
+	kshark_convert_nano(model_max, &sec, &usec);
+	count = asprintf(&tMax, "%" PRIu64 ".%06" PRIu64 "", sec, usec);
+	if (count <= 0)
+		return;
+
+	ksplot_print_text(&_font, nullptr,
+			  a0.x(),
+			  a0.y() - _hMargin / 2,
+			  tMin);
+
+	ksplot_print_text(&_font, nullptr,
+			  b0.x() - _font.char_width * count / 2,
+			  b0.y() - _hMargin / 2,
+			  tMid);
+
+	ksplot_print_text(&_font, nullptr,
+			  c0.x() - _font.char_width * count,
+			  c0.y() - _hMargin / 2,
+			  tMax);
+
+	free(tMin);
+	free(tMid);
+	free(tMax);
 }
 
-void KsGLWidget::_makeGraphs(QVector<int> cpuList, QVector<int> taskList)
+int KsGLWidget::_getMaxLabelSize()
 {
+	int size, max(0);
+
+	for (auto it = _streamPlots.begin(); it != _streamPlots.end(); ++it) {
+		int sd = it.key();
+		for (auto const &pid: it.value()._taskList) {
+			size = _font.char_width *
+			       KsUtils::taskPlotName(sd, pid).count();
+			max = (size > max) ? size : max;
+		}
+
+		for (auto const &cpu: it.value()._cpuList) {
+			size = _font.char_width * KsUtils::cpuPlotName(cpu).count();
+			max = (size > max) ? size : max;
+		}
+	}
+
+	for (auto &c: _comboPlots)
+		for (auto const &p: c) {
+			if (p._type & KSHARK_TASK_DRAW) {
+				size = _font.char_width *
+					KsUtils::taskPlotName(p._streamId, p._id).count();
+
+				max = (size > max) ? size : max;
+			} else if (p._type & KSHARK_CPU_DRAW) {
+				size = _font.char_width *
+				       KsUtils::cpuPlotName(p._id).count();
+
+				max = (size > max) ? size : max;
+			}
+		}
+
+	return max;
+}
+
+void KsGLWidget::_makeGraphs()
+{
+	int base(_vMargin * 2 + KS_GRAPH_HEIGHT), sd;
+	KsPlot::Graph *g;
+
 	/* The very first thing to do is to clean up. */
-	for (auto &g: _graphs)
-		delete g;
-	_graphs.resize(0);
+	_freeGraphs();
 
 	if (!_data || !_data->size())
 		return;
 
-	auto lamAddGraph = [&](KsPlot::Graph *graph) {
-		/*
-		* Calculate the base level of the CPU graph inside the widget.
-		* Remember that the "Y" coordinate is inverted.
-		*/
+	_labelSize = _getMaxLabelSize() + FONT_WIDTH * 2;
+
+	auto lamAddGraph = [&](int sd, KsPlot::Graph *graph, int vSpace=0) {
 		if (!graph)
-			return;
+			return graph;
 
-		int base = _vMargin +
-			   _vSpacing * _graphs.count() +
-			   KS_GRAPH_HEIGHT * (_graphs.count() + 1);
+		KsPlot::Color color = {255, 255, 255}; // White
+		/*
+		 * Calculate the base level of the CPU graph inside the widget.
+		 * Remember that the "Y" coordinate is inverted.
+		 */
 
 		graph->setBase(base);
-		_graphs.append(graph);
+
+		/*
+		 * If we have multiple Data streams use the color of the stream
+		 * for the label of the graph.
+		 */
+		if (KsUtils::getNStreams() > 1)
+			color = KsPlot::getColor(&_streamColors, sd);
+
+		graph->setLabelAppearance(&_font,
+					  color,
+					  _labelSize,
+					  _hMargin);
+
+		_graphs[sd].append(graph);
+		base += graph->height() + vSpace;
+
+		return graph;
 	};
 
-	/* Create CPU graphs according to the cpuList. */
-	for (auto const &cpu: cpuList)
-		lamAddGraph(_newCPUGraph(cpu));
+	for (auto it = _streamPlots.begin(); it != _streamPlots.end(); ++it) {
+		sd = it.key();
+		/* Create CPU graphs according to the cpuList. */
+		it.value()._cpuGraphs = {};
+		for (auto const &cpu: it.value()._cpuList) {
+			g = lamAddGraph(sd, _newCPUGraph(sd, cpu), _vSpacing);
+			it.value()._cpuGraphs.append(g);
+		}
+
+		/* Create Task graphs according to the taskList. */
+		it.value()._taskGraphs = {};
+		for (auto const &pid: it.value()._taskList) {
+			g = lamAddGraph(sd, _newTaskGraph(sd, pid), _vSpacing);
+			it.value()._taskGraphs.append(g);
+		}
+	}
 
-	/* Create Task graphs taskList to the taskList. */
-	for (auto const &pid: taskList)
-		lamAddGraph(_newTaskGraph(pid));
+	for (auto &c: _comboPlots) {
+		int n = c.count();
+		for (int i = 0; i < n; ++i) {
+			sd = c[i]._streamId;
+			if (c[i]._type & KSHARK_TASK_DRAW) {
+				c[i]._graph = lamAddGraph(sd, _newTaskGraph(sd, c[i]._id));
+			} else if (c[i]._type & KSHARK_CPU_DRAW) {
+				c[i]._graph = lamAddGraph(sd, _newCPUGraph(sd, c[i]._id));
+			} else {
+				c[i]._graph = nullptr;
+			}
+
+			if (c[i]._graph && i < n - 1)
+				c[i]._graph->setDrawBase(false);
+		}
+
+		base += _vSpacing;
+	}
 }
 
-void KsGLWidget::_makePluginShapes(QVector<int> cpuList, QVector<int> taskList)
+void KsGLWidget::_makePluginShapes()
 {
 	kshark_context *kshark_ctx(nullptr);
-	kshark_event_handler *evt_handlers;
+	kshark_draw_handler *draw_handlers;
+	struct kshark_data_stream *stream;
 	KsCppArgV cppArgv;
+	int sd;
 
 	if (!kshark_instance(&kshark_ctx))
 		return;
 
+	/* The very first thing to do is to clean up. */
+	freePluginShapes();
+
 	cppArgv._histo = _model.histo();
 	cppArgv._shapes = &_shapes;
 
-	for (int g = 0; g < cpuList.count(); ++g) {
-		cppArgv._graph = _graphs[g];
-		evt_handlers = kshark_ctx->event_handlers;
-		while (evt_handlers) {
-			evt_handlers->draw_func(cppArgv.toC(),
-						cpuList[g],
-						KSHARK_PLUGIN_CPU_DRAW);
+	for (auto it = _streamPlots.constBegin(); it != _streamPlots.constEnd(); ++it) {
+		sd = it.key();
+		stream = kshark_get_data_stream(kshark_ctx, sd);
+		if (!stream)
+			continue;
+
+		for (int g = 0; g < it.value()._cpuList.count(); ++g) {
+			cppArgv._graph = it.value()._cpuGraphs[g];
+			draw_handlers = stream->draw_handlers;
+			while (draw_handlers) {
+				draw_handlers->draw_func(cppArgv.toC(),
+							sd,
+							it.value()._cpuList[g],
+							KSHARK_CPU_DRAW);
+
+				draw_handlers = draw_handlers->next;
+			}
+		}
 
-			evt_handlers = evt_handlers->next;
+		for (int g = 0; g < it.value()._taskList.count(); ++g) {
+			cppArgv._graph = it.value()._taskGraphs[g];
+			draw_handlers = stream->draw_handlers;
+			while (draw_handlers) {
+				draw_handlers->draw_func(cppArgv.toC(),
+							sd,
+							it.value()._taskList[g],
+							KSHARK_TASK_DRAW);
+
+				draw_handlers = draw_handlers->next;
+			}
 		}
 	}
 
-	for (int g = 0; g < taskList.count(); ++g) {
-		cppArgv._graph = _graphs[cpuList.count() + g];
-		evt_handlers = kshark_ctx->event_handlers;
-		while (evt_handlers) {
-			evt_handlers->draw_func(cppArgv.toC(),
-						taskList[g],
-						KSHARK_PLUGIN_TASK_DRAW);
-
-			evt_handlers = evt_handlers->next;
+	for (auto const &c: _comboPlots) {
+		for (auto const &p: c) {
+			stream = kshark_get_data_stream(kshark_ctx, p._streamId);
+			draw_handlers = stream->draw_handlers;
+			cppArgv._graph = p._graph;
+			while (draw_handlers) {
+				draw_handlers->draw_func(cppArgv.toC(),
+							 p._streamId,
+							 p._id,
+							 p._type);
+
+				draw_handlers = draw_handlers->next;
+			}
 		}
 	}
 }
 
-KsPlot::Graph *KsGLWidget::_newCPUGraph(int cpu)
+KsPlot::Graph *KsGLWidget::_newCPUGraph(int sd, int cpu)
 {
+	QString name;
 	/* The CPU graph needs to know only the colors of the tasks. */
 	KsPlot::Graph *graph = new KsPlot::Graph(_model.histo(),
 						 &_pidColors,
 						 &_pidColors);
-	graph->setZeroSuppressed(true);
 
 	kshark_context *kshark_ctx(nullptr);
+	kshark_data_stream *stream;
 	kshark_entry_collection *col;
 
 	if (!kshark_instance(&kshark_ctx))
 		return nullptr;
 
-	graph->setHMargin(_hMargin);
+	stream = kshark_get_data_stream(kshark_ctx, sd);
+	if (!stream)
+		return nullptr;
+
+	graph->setIdleSuppressed(true, stream->idle_pid);
 	graph->setHeight(KS_GRAPH_HEIGHT);
+	graph->setLabelText(KsUtils::cpuPlotName(cpu).toStdString());
 
 	col = kshark_find_data_collection(kshark_ctx->collections,
 					  KsUtils::matchCPUVisible,
-					  cpu);
+					  sd, &cpu, 1);
 
 	graph->setDataCollectionPtr(col);
-	graph->fillCPUGraph(cpu);
+	graph->fillCPUGraph(sd, cpu);
 
 	return graph;
 }
 
-KsPlot::Graph *KsGLWidget::_newTaskGraph(int pid)
+KsPlot::Graph *KsGLWidget::_newTaskGraph(int sd, int pid)
 {
+	QString name;
 	/*
 	 * The Task graph needs to know the colors of the tasks and the colors
 	 * of the CPUs.
@@ -607,15 +822,21 @@ KsPlot::Graph *KsGLWidget::_newTaskGraph(int pid)
 						 &_cpuColors);
 	kshark_context *kshark_ctx(nullptr);
 	kshark_entry_collection *col;
+	kshark_data_stream *stream;
 
 	if (!kshark_instance(&kshark_ctx))
 		return nullptr;
 
-	graph->setHMargin(_hMargin);
+	stream = kshark_get_data_stream(kshark_ctx, sd);
+	if (!stream)
+		return nullptr;
+
 	graph->setHeight(KS_GRAPH_HEIGHT);
+	graph->setLabelText(KsUtils::taskPlotName(sd, pid).toStdString());
 
 	col = kshark_find_data_collection(kshark_ctx->collections,
-					  kshark_match_pid, pid);
+					  kshark_match_pid, sd, &pid, 1);
+
 	if (!col) {
 		/*
 		 * If a data collection for this task does not exist,
@@ -624,7 +845,8 @@ KsPlot::Graph *KsGLWidget::_newTaskGraph(int pid)
 		col = kshark_register_data_collection(kshark_ctx,
 						      _data->rows(),
 						      _data->size(),
-						      kshark_match_pid, pid,
+						      kshark_match_pid,
+						      sd, &pid, 1,
 						      25);
 	}
 
@@ -647,7 +869,7 @@ KsPlot::Graph *KsGLWidget::_newTaskGraph(int pid)
 	}
 
 	graph->setDataCollectionPtr(col);
-	graph->fillTaskGraph(pid);
+	graph->fillTaskGraph(sd, pid);
 
 	return graph;
 }
@@ -667,18 +889,19 @@ KsPlot::Graph *KsGLWidget::_newTaskGraph(int pid)
 bool KsGLWidget::find(const QPoint &point, int variance, bool joined,
 		      size_t *index)
 {
+	int bin, sd, cpu, pid;
+
 	/*
 	 * Get the bin, pid and cpu numbers.
 	 * Remember that one bin corresponds to one pixel.
 	 */
-	int bin = point.x() - _hMargin;
-	int cpu = getPlotCPU(point);
-	int pid = getPlotPid(point);
+	bin = point.x() - _bin0Offset();
+	getPlotInfo(point, &sd, &cpu, &pid);
 
-	return _find(bin, cpu, pid, variance, joined, index);
+	return _find(bin, sd, cpu, pid, variance, joined, index);
 }
 
-int KsGLWidget::_getNextCPU(int pid, int bin)
+int KsGLWidget::_getNextCPU(int bin, int sd, int pid)
 {
 	kshark_context *kshark_ctx(nullptr);
 	kshark_entry_collection *col;
@@ -689,12 +912,12 @@ int KsGLWidget::_getNextCPU(int pid, int bin)
 
 	col = kshark_find_data_collection(kshark_ctx->collections,
 					  kshark_match_pid,
-					  pid);
+					  sd, &pid, 1);
 	if (!col)
 		return KS_EMPTY_BIN;
 
 	for (int i = bin; i < _model.histo()->n_bins; ++i) {
-		cpu = ksmodel_get_cpu_front(_model.histo(), i, pid,
+		cpu = ksmodel_get_cpu_front(_model.histo(), i, sd, pid,
 					    false, col, nullptr);
 		if (cpu >= 0)
 			return cpu;
@@ -703,7 +926,7 @@ int KsGLWidget::_getNextCPU(int pid, int bin)
 	return KS_EMPTY_BIN;
 }
 
-bool KsGLWidget::_find(int bin, int cpu, int pid,
+bool KsGLWidget::_find(int bin, int sd, int cpu, int pid,
 		       int variance, bool joined, size_t *row)
 {
 	int hSize = _model.histo()->n_bins;
@@ -721,7 +944,7 @@ bool KsGLWidget::_find(int bin, int cpu, int pid,
 	auto lamGetEntryByCPU = [&](int b) {
 		/* Get the first data entry in this bin. */
 		found = ksmodel_first_index_at_cpu(_model.histo(),
-							   b, cpu);
+						   b, sd, cpu);
 		if (found < 0) {
 			/*
 			 * The bin is empty or the entire connect of the bin
@@ -737,7 +960,7 @@ bool KsGLWidget::_find(int bin, int cpu, int pid,
 	auto lamGetEntryByPid = [&](int b) {
 		/* Get the first data entry in this bin. */
 		found = ksmodel_first_index_at_pid(_model.histo(),
-							   b, pid);
+						   b, sd, pid);
 		if (found < 0) {
 			/*
 			 * The bin is empty or the entire connect of the bin
@@ -803,7 +1026,7 @@ bool KsGLWidget::_find(int bin, int cpu, int pid,
 		 * for an entry on the next CPU used by this task.
 		 */
 		if (!ret && joined) {
-			cpu = _getNextCPU(pid, bin);
+			cpu = _getNextCPU(sd, bin, pid);
 			ret = lamFindEntryByCPU(bin);
 		}
 
@@ -901,7 +1124,7 @@ void KsGLWidget::_rangeChanged(int binMin, int binMax)
 
 	/* Recalculate the model and update the markers. */
 	ksmodel_set_bining(_model.histo(), nBins, min, max);
-	_model.fill(_data->rows(), _data->size());
+	_model.fill(_data);
 	_mState->updateMarkers(*_data, this);
 
 	/*
@@ -932,8 +1155,8 @@ void KsGLWidget::_rangeChanged(int binMin, int binMax)
 int KsGLWidget::_posInRange(int x)
 {
 	int posX;
-	if (x < _hMargin)
-		posX = _hMargin;
+	if (x < _bin0Offset())
+		posX = _bin0Offset();
 	else if (x > (width() - _hMargin))
 		posX = width() - _hMargin;
 	else
@@ -942,35 +1165,62 @@ int KsGLWidget::_posInRange(int x)
 	return posX;
 }
 
-/** Get the CPU Id of the Graph plotted at given position. */
-int KsGLWidget::getPlotCPU(const QPoint &point)
+/**
+ * @brief Get information about the graph plotted at given position (under the mouse).
+ *
+ * @param point: The position to be inspected.
+ * @param sd: Output location for the Data stream Identifier of the graph.
+ * @param cpu: Output location for the CPU Id of the graph, or -1 if this is
+ *	       a Task graph.
+ * @param pid: Output location for the Process Id of the graph, or -1 if this is
+ *	       a CPU graph.
+ */
+bool KsGLWidget::getPlotInfo(const QPoint &point, int *sd, int *cpu, int *pid)
 {
-	int cpuId, y = point.y();
+	int base, n;
 
-	if (_cpuList.count() == 0)
-		return -1;
+	*sd = *cpu = *pid = -1;
 
-	cpuId = (y - _vMargin + _vSpacing / 2) / (_vSpacing + KS_GRAPH_HEIGHT);
-	if (cpuId < 0 || cpuId >= _cpuList.count())
-		return -1;
+	for (auto it = _streamPlots.constBegin(); it != _streamPlots.constEnd(); ++it) {
+		n = it.value()._cpuList.count();
+		for (int i = 0; i < n; ++i) {
+			base = it.value()._cpuGraphs[i]->base();
+			if (base - KS_GRAPH_HEIGHT < point.y() &&
+			    point.y() < base) {
+				*sd = it.key();
+				*cpu = it.value()._cpuList[i];
 
-	return _cpuList[cpuId];
-}
+				return true;
+			}
+		}
 
-/** Get the CPU Id of the Graph plotted at given position. */
-int KsGLWidget::getPlotPid(const QPoint &point)
-{
-	int pidId, y = point.y();
+		n = it.value()._taskList.count();
+		for (int i = 0; i < n; ++i) {
+			base = it.value()._taskGraphs[i]->base();
+			if (base - KS_GRAPH_HEIGHT < point.y() &&
+			    point.y() < base) {
+				*sd = it.key();
+				*pid = it.value()._taskList[i];
 
-	if (_taskList.count() == 0)
-		return -1;
+				return true;
+			}
+		}
+	}
 
-	pidId = (y - _vMargin -
-		     _cpuList.count()*(KS_GRAPH_HEIGHT + _vSpacing) +
-		     _vSpacing / 2) / (_vSpacing + KS_GRAPH_HEIGHT);
+	for (auto const &c: _comboPlots) {
+		for (auto const &p: c) {
+			base = p.base();
+			if (base - KS_GRAPH_HEIGHT < point.y() && point.y() < base) {
+				*sd = p._streamId;
+				if (p._type & KSHARK_CPU_DRAW)
+					*cpu = p._id;
+				else if (p._type & KSHARK_TASK_DRAW)
+					*pid = p._id;
 
-	if (pidId < 0 || pidId >= _taskList.count())
-		return -1;
+				return true;
+			}
+		}
+	}
 
-	return _taskList[pidId];
+	return false;
 }
diff --git a/src/KsGLWidget.hpp b/src/KsGLWidget.hpp
index c6fd787..629ae37 100644
--- a/src/KsGLWidget.hpp
+++ b/src/KsGLWidget.hpp
@@ -17,10 +17,51 @@
 
 // KernelShark
 #include "KsUtils.hpp"
+#include "KsWidgetsLib.hpp"
 #include "KsPlotTools.hpp"
 #include "KsModels.hpp"
 #include "KsDualMarker.hpp"
 
+/** Structure describing all graphs to be plotted for a given Data stream. */
+struct KsPerStreamPlots {
+	/** CPUs to be plotted. */
+	QVector<int>			_cpuList;
+
+	/** "Y" coordinates of the bases of all CPU plots for this stream. */
+	QVector<KsPlot::Graph *>	_cpuGraphs;
+
+	/** Tasks to be plotted. */
+	QVector<int>			_taskList;
+
+	/** "Y" coordinates of the bases of all CPU plots for this stream. */
+	QVector<KsPlot::Graph *>	_taskGraphs;
+};
+
+/** Structure describing a plot. */
+struct KsPlotEntry {
+	/** The Data stream identifier of the plot. */
+	int	_streamId;
+
+	/** Plotting action identifier (Task or CPU plot). */
+	int	_type;
+
+	/** Identifier of the plot (can be PID or CPU number). */
+	int	_id;
+
+	/** Graph pointer. */
+	KsPlot::Graph	*_graph;
+
+	/** "Y" coordinates of the bases of the plot. */
+	int base() const {return _graph->base();}
+};
+
+KsPlotEntry &operator <<(KsPlotEntry &plot, QVector<int> &v);
+
+void operator >>(const KsPlotEntry &plot, QVector<int> &v);
+
+/** Vector of KsPlotEntry used to describe a Combo plot. */
+typedef QVector<KsPlotEntry>	KsComboPlot;
+
 /**
  * The KsGLWidget class provides a widget for rendering OpenGL graphics used
  * to plot trace graphs.
@@ -39,9 +80,9 @@ public:
 
 	void paintGL() override;
 
-	void reset();
+	void render();
 
-	bool isEmpty() const;
+	void reset();
 
 	/** Reprocess all graphs. */
 	void update() {resizeGL(width(), height());}
@@ -64,24 +105,6 @@ public:
 
 	void loadColors();
 
-	/**
-	 * Reimplementing the event handler of the focus event, in order to
-	 * avoid the update (redrawing) of the graphs every time when the
-	 * widget grabs the focus of the keyboard. This is done because we do
-	 * not need to redraw, while on the other hand on large data-sets,
-	 * redrawing can take a lot of time.
-	 */
-	void focusInEvent(QFocusEvent* e) override {}
-
-	/**
-	 * Reimplementing the event handler of the focus event, in order to
-	 * avoid the update (redrawing) of the graphs every time when the
-	 * widget releases the focus of the keyboard. This is done because we
-	 * do not need to redraw, while on the other hand on large data-sets,
-	 * redrawing can take a lot of time.
-	 */
-	void focusOutEvent(QFocusEvent* e) override {}
-
 	/**
 	 * Provide the widget with a pointer to the Dual Marker state machine
 	 * object.
@@ -91,19 +114,68 @@ public:
 	/** Get the KsGraphModel object. */
 	KsGraphModel *model() {return &_model;}
 
-	/** Get the number of CPU graphs. */
-	int cpuGraphCount() const {return _cpuList.count();}
+	/** Get the number of CPU graphs for a given Data stream. */
+	int cpuGraphCount(int sd) const
+	{
+		auto it = _streamPlots.find(sd);
+		if (it != _streamPlots.end())
+			return it.value()._cpuList.count();
+		return 0;
+	}
+
+	/** Get the number of Task graphs for a given Data stream. */
+	int taskGraphCount(int sd) const
+	{
+		auto it = _streamPlots.find(sd);
+		if (it != _streamPlots.end())
+			return it.value()._taskList.count();
+		return 0;
+	}
 
-	/** Get the number of Task graphs. */
-	int taskGraphCount() const {return _taskList.count();}
+	/** Get the total number of graphs for a given Data stream. */
+	int graphCount(int sd) const
+	{
+		auto it = _streamPlots.find(sd);
+		if (it != _streamPlots.end())
+			return it.value()._taskList.count() +
+			       it.value()._cpuList.count();
+		return 0;
+	}
 
-	/** Get the total number of graphs. */
-	int graphCount() const {return _cpuList.count() + _taskList.count();}
+	/**
+	 * Get the total number of graphs for all Data stream. The Combo plots
+	 * are not counted.
+	 */
+	int totGraphCount() const
+	{
+		int count(0);
+		for (auto const &s: _streamPlots)
+			count += s._taskList.count() +
+				 s._cpuList.count();
+		return count;
+	}
+
+	/** Get the number of plots in Combos. */
+	int comboGraphCount() const {
+		int count(0);
+		for (auto const &c: _comboPlots)
+			count += c.count();
+		return count;
+	}
+
+	/** Check if the widget is empty (not showing anything). */
+	bool isEmpty() const
+	{
+		return !_data || !_data->size() ||
+		       (!totGraphCount() && !comboGraphCount());
+	}
 
 	/** Get the height of the widget. */
 	int height() const
 	{
-		return graphCount() * (KS_GRAPH_HEIGHT + _vSpacing) +
+		return totGraphCount() * (KS_GRAPH_HEIGHT + _vSpacing) +
+		       comboGraphCount() * KS_GRAPH_HEIGHT +
+		       _comboPlots.count() * _vSpacing +
 		       _vMargin * 2;
 	}
 
@@ -119,24 +191,27 @@ public:
 	/** Get the size of the vertical spaceing between the graphs. */
 	int vSpacing()	const {return _vSpacing;}
 
-	void setMark(KsGraphMark *mark);
-
-	void findGraphIds(const kshark_entry &e,
-			  int *graphCPU,
-			  int *graphTask);
+	void setMarkPoints(const KsDataStore &data, KsGraphMark *mark);
 
 	bool find(const QPoint &point, int variance, bool joined,
 		  size_t *index);
 
-	int getPlotCPU(const QPoint &point);
+	bool getPlotInfo(const QPoint &point, int *sd, int *cpu, int *pid);
 
-	int getPlotPid(const QPoint &point);
+	/** CPUs and Tasks graphs (per data stream) to be plotted. */
+	QMap<int, KsPerStreamPlots>	_streamPlots;
 
-	/** CPUs to be plotted. */
-	QVector<int>	_cpuList;
+	/** Combo graphs to be plotted. */
+	QVector<KsComboPlot>		_comboPlots;
 
-	/** Tasks to be plotted. */
-	QVector<int>	_taskList;
+	/** Set the pointer to the WorkInProgress widget. */
+	void setWipPtr(KsWidgetsLib::KsWorkInProgress *wip)
+	{
+		_workInProgress = wip;
+	}
+
+	/** Free the list of plugin-defined shapes. */
+	void freePluginShapes();
 
 signals:
 	/**
@@ -149,7 +224,7 @@ signals:
 	 * This signal is emitted when the mouse moves but there is no visible
 	 * KernelShark entry under the cursor.
 	 */
-	void notFound(uint64_t ts, int cpu, int pid);
+	void notFound(uint64_t ts, int sd, int cpu, int pid);
 
 	/** This signal is emitted when the Plus key is pressed. */
 	void zoomIn();
@@ -182,7 +257,7 @@ signals:
 	void updateView(size_t pos, bool mark);
 
 private:
-	QVector<KsPlot::Graph*>	_graphs;
+	QMap<int, QVector<KsPlot::Graph *>>	_graphs;
 
 	KsPlot::PlotObjList	_shapes;
 
@@ -190,7 +265,11 @@ private:
 
 	KsPlot::ColorTable	_cpuColors;
 
-	int		_hMargin, _vMargin;
+	KsPlot::ColorTable	_streamColors;
+
+	KsWidgetsLib::KsWorkInProgress	*_workInProgress;
+
+	int	_labelSize, _hMargin, _vMargin;
 
 	unsigned int	_vSpacing;
 
@@ -210,15 +289,21 @@ private:
 
 	int 		_dpr;
 
+	ksplot_font	_font;
+
+	void _freeGraphs();
+
 	void _drawAxisX(float size);
 
-	void _makeGraphs(QVector<int> cpuMask, QVector<int> taskMask);
+	int _getMaxLabelSize();
+
+	void _makeGraphs();
 
-	KsPlot::Graph *_newCPUGraph(int cpu);
+	KsPlot::Graph *_newCPUGraph(int sd, int cpu);
 
-	KsPlot::Graph *_newTaskGraph(int pid);
+	KsPlot::Graph *_newTaskGraph(int sd, int pid);
 
-	void _makePluginShapes(QVector<int> cpuMask, QVector<int> taskMask);
+	void _makePluginShapes();
 
 	int _posInRange(int x);
 
@@ -230,16 +315,20 @@ private:
 
 	bool _findAndSelect(QMouseEvent *event);
 
-	bool _find(int bin, int cpu, int pid,
+	bool _find(int bin, int sd, int cpu, int pid,
 		   int variance, bool joined, size_t *row);
 
-	int _getNextCPU(int pid, int bin);
+	int _getNextCPU(int bin, int sd, int pid);
 
-	int _getLastTask(struct kshark_trace_histo *histo, int bin, int cpu);
+	int _getLastTask(struct kshark_trace_histo *histo,
+			 int bin, int sd, int cpu);
 
-	int _getLastCPU(struct kshark_trace_histo *histo, int bin, int pid);
+	int _getLastCPU(struct kshark_trace_histo *histo,
+			int bin, int sd, int pid);
 
 	void _deselect();
+
+	int _bin0Offset() {return _labelSize + 2 * _hMargin;}
 };
 
 #endif
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v2 17/27] kernel-shark: Update KsTraceGraph and KsQuickContextMenu
  2021-02-11 10:31 [PATCH v2 00/27] Complete the KernelShark v2 transformation Yordan Karadzhov (VMware)
                   ` (15 preceding siblings ...)
  2021-02-11 10:31 ` [PATCH v2 16/27] kernel-shark: Update KsDualMarker and KsGLWidget Yordan Karadzhov (VMware)
@ 2021-02-11 10:31 ` Yordan Karadzhov (VMware)
  2021-02-11 10:31 ` [PATCH v2 18/27] kernel-shark: Update KsTraceViewer Yordan Karadzhov (VMware)
                   ` (10 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Yordan Karadzhov (VMware) @ 2021-02-11 10:31 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

The compilation of KsTraceGraph.cpp and KsQuickContextMenu.cpp is
re-enabled and all functionalities are made compatible with the new
version of the C API of libkshark (KernelShark 2.0). The two source
files are updated in a single patch because of their interdependence.

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 src/CMakeLists.txt         |  12 +-
 src/KsQuickContextMenu.cpp | 151 +++++++++----
 src/KsQuickContextMenu.hpp |  26 ++-
 src/KsTraceGraph.cpp       | 426 ++++++++++++++++++-------------------
 src/KsTraceGraph.hpp       |  43 ++--
 5 files changed, 351 insertions(+), 307 deletions(-)

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 3dc7213..ab74d5a 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -71,12 +71,12 @@ if (Qt5Widgets_FOUND AND Qt5Network_FOUND AND TT_FONT_FILE)
                         KsGLWidget.hpp
                         KsSearchFSM.hpp
                         KsDualMarker.hpp
-                        KsWidgetsLib.hpp)
-#                         KsTraceGraph.hpp
+                        KsWidgetsLib.hpp
+                        KsTraceGraph.hpp
 #                         KsTraceViewer.hpp
 #                         KsMainWindow.hpp
 #                         KsCaptureDialog.hpp
-#                         KsQuickContextMenu.hpp
+                        KsQuickContextMenu.hpp)
 #                         KsAdvFilteringDialog.hpp)
 
     QT5_WRAP_CPP(ks-guiLib_hdr_moc ${ks-guiLib_hdr})
@@ -87,12 +87,12 @@ if (Qt5Widgets_FOUND AND Qt5Network_FOUND AND TT_FONT_FILE)
                                                             KsGLWidget.cpp
                                                             KsSearchFSM.cpp
                                                             KsDualMarker.cpp
-                                                            KsWidgetsLib.cpp)
-#                                                             KsTraceGraph.cpp
+                                                            KsWidgetsLib.cpp
+                                                            KsTraceGraph.cpp
 #                                                             KsTraceViewer.cpp
 #                                                             KsMainWindow.cpp
 #                                                             KsCaptureDialog.cpp
-#                                                             KsQuickContextMenu.cpp
+                                                            KsQuickContextMenu.cpp)
 #                                                             KsAdvFilteringDialog.cpp)
 
     target_link_libraries(kshark-gui kshark-plot
diff --git a/src/KsQuickContextMenu.cpp b/src/KsQuickContextMenu.cpp
index a84444e..6fa242d 100644
--- a/src/KsQuickContextMenu.cpp
+++ b/src/KsQuickContextMenu.cpp
@@ -39,17 +39,19 @@ KsQuickMarkerMenu::KsQuickMarkerMenu(KsDualMarkerSM *dm, QWidget *parent)
 /**
  * @brief Create KsQuickContextMenu.
  *
+ * @param dm: The State machine of the Dual marker.
  * @param data: Input location for the KsDataStore object.
  * @param row: The index of the entry used to initialize the menu.
- * @param dm: The State machine of the Dual marker.
  * @param parent: The parent of this widget.
  */
-KsQuickContextMenu::KsQuickContextMenu(KsDataStore *data, size_t row,
-				       KsDualMarkerSM *dm,
+KsQuickContextMenu::KsQuickContextMenu(KsDualMarkerSM *dm,
+				       KsDataStore *data, size_t row,
 				       QWidget *parent)
 : KsQuickMarkerMenu(dm, parent),
   _data(data),
   _row(row),
+  _rawTime(this),
+  _rawEvent(this),
   _graphSyncCBox(nullptr),
   _listSyncCBox(nullptr),
   _hideTaskAction(this),
@@ -65,16 +67,50 @@ KsQuickContextMenu::KsQuickContextMenu(KsDataStore *data, size_t row,
   _clearAllFilters(this)
 {
 	typedef void (KsQuickContextMenu::*mfp)();
-	QString taskName, parentName, descr;
+	QString time, taskName, parentName, descr;
+	kshark_entry *entry = _data->rows()[_row];
+	kshark_context *kshark_ctx(nullptr);
+	kshark_data_stream *stream;
+	QStringList eventFields;
+	int pid, cpu, sd, ret;
 	KsTraceGraph *graphs;
-	int pid, cpu;
+	int64_t fieldVal;
 
 	if (!parent || !_data)
 		return;
 
-	taskName = kshark_get_task_easy(_data->rows()[_row]);
-	pid = kshark_get_pid_easy(_data->rows()[_row]);
-	cpu = _data->rows()[_row]->cpu;
+	if (!kshark_instance(&kshark_ctx))
+		return;
+
+	taskName = kshark_get_task(entry);
+	pid = kshark_get_pid(entry);
+	cpu = entry->cpu;
+	sd = entry->stream_id;
+
+	stream = kshark_get_data_stream(kshark_ctx, sd);
+	if (!stream)
+		return;
+
+	QString evtData("\t"), val;
+	eventFields =  KsUtils::getEventFieldsList(entry->stream_id,
+						   entry->event_id);
+
+	for (auto const &field: eventFields) {
+		std::string buff = field.toStdString();
+		ret = kshark_read_event_field_int(entry, buff.c_str(), &fieldVal);
+		if (ret < 0)
+			continue;
+
+		evtData += field + ":  " + val.setNum(fieldVal) + "\n\t";
+	}
+
+	addSection("Raw event");
+	time = QString("\ttime:  %1 [ns]").arg(entry->ts);
+
+	_rawTime.setDefaultWidget(new QLabel(time));
+	addAction(&_rawTime);
+	_rawEvent.setDefaultWidget(new QLabel(evtData));
+	addAction(&_rawEvent);
 
 	auto lamAddAction = [this, &descr] (QAction *action, mfp mf) {
 		action->setText(descr);
@@ -104,12 +140,12 @@ KsQuickContextMenu::KsQuickContextMenu(KsDataStore *data, size_t row,
 	lamAddAction(&_hideTaskAction, &KsQuickContextMenu::_hideTask);
 
 	descr = "Show event [";
-	descr += kshark_get_event_name_easy(_data->rows()[_row]);
+	descr += kshark_get_event_name(entry);
 	descr += "] only";
 	lamAddAction(&_showEventAction, &KsQuickContextMenu::_showEvent);
 
 	descr = "Hide event [";
-	descr += kshark_get_event_name_easy(_data->rows()[_row]);
+	descr += kshark_get_event_name(entry);
 	descr += "]";
 	lamAddAction(&_hideEventAction, &KsQuickContextMenu::_hideEvent);
 
@@ -118,7 +154,7 @@ KsQuickContextMenu::KsQuickContextMenu(KsDataStore *data, size_t row,
 		lamAddAction(&_showCPUAction, &KsQuickContextMenu::_showCPU);
 	}
 
-	descr = QString("Hide CPU [%1]").arg(_data->rows()[_row]->cpu);
+	descr = QString("Hide CPU [%1]").arg(entry->cpu);
 	lamAddAction(&_hideCPUAction, &KsQuickContextMenu::_hideCPU);
 
 	descr = "Clear all filters";
@@ -138,7 +174,7 @@ KsQuickContextMenu::KsQuickContextMenu(KsDataStore *data, size_t row,
 
 	if (parentName == "KsTraceGraph" &&
 	    (graphs = dynamic_cast<KsTraceGraph *>(parent))) {
-		if (graphs->glPtr()->_taskList.contains(pid)) {
+		if (graphs->glPtr()->_streamPlots[sd]._taskList.contains(pid)) {
 			descr = "Remove [";
 			descr += taskName;
 			descr += "-";
@@ -156,7 +192,7 @@ KsQuickContextMenu::KsQuickContextMenu(KsDataStore *data, size_t row,
 				     &KsQuickContextMenu::_addTaskPlot);
 		}
 
-		if (graphs->glPtr()->_cpuList.contains(cpu)) {
+		if (graphs->glPtr()->_streamPlots[sd]._cpuList.contains(cpu)) {
 			descr = "Remove [CPU ";
 			descr += QString("%1").arg(cpu);
 			descr += "] plot";
@@ -174,66 +210,86 @@ KsQuickContextMenu::KsQuickContextMenu(KsDataStore *data, size_t row,
 
 void KsQuickContextMenu::_hideTask()
 {
-	int pid = kshark_get_pid_easy(_data->rows()[_row]);
+	int pid = kshark_get_pid(_data->rows()[_row]);
+	int sd = _data->rows()[_row]->stream_id;
 	kshark_context *kshark_ctx(nullptr);
+	kshark_data_stream *stream;
 	QVector<int> vec;
 
 	if (!kshark_instance(&kshark_ctx))
 		return;
 
-	vec =_getFilterVector(kshark_ctx->hide_task_filter, pid);
-	_data->applyNegTaskFilter(vec);
+	stream = kshark_get_data_stream(kshark_ctx, sd);
+	if (!stream)
+		return;
+
+	vec =_getFilterVector(stream->hide_task_filter, pid);
+	_data->applyNegTaskFilter(sd, vec);
 }
 
 void KsQuickContextMenu::_showTask()
 {
-	int pid = kshark_get_pid_easy(_data->rows()[_row]);
-
-	_data->applyPosTaskFilter(QVector<int>(1, pid));
+	int pid = kshark_get_pid(_data->rows()[_row]);
+	int sd = _data->rows()[_row]->stream_id;
+	_data->applyPosTaskFilter(sd, QVector<int>(1, pid));
 }
 
 void KsQuickContextMenu::_hideEvent()
 {
-	int eventId = kshark_get_event_id_easy(_data->rows()[_row]);
+	int eventId = kshark_get_event_id(_data->rows()[_row]);
+	int sd = _data->rows()[_row]->stream_id;
 	kshark_context *kshark_ctx(nullptr);
+	kshark_data_stream *stream;
 	QVector<int> vec;
 
 	if (!kshark_instance(&kshark_ctx))
 		return;
 
-	vec =_getFilterVector(kshark_ctx->hide_event_filter, eventId);
-	_data->applyNegEventFilter(vec);
+	stream = kshark_get_data_stream(kshark_ctx, sd);
+	if (!stream)
+		return;
+
+	vec =_getFilterVector(stream->hide_event_filter, eventId);
+	_data->applyNegEventFilter(sd, vec);
 }
 
 void KsQuickContextMenu::_showEvent()
 {
-	int eventId = kshark_get_event_id_easy(_data->rows()[_row]);
+	int eventId = kshark_get_event_id(_data->rows()[_row]);
+	int sd = _data->rows()[_row]->stream_id;
 
-	_data->applyPosEventFilter(QVector<int>(1, eventId));
+	_data->applyPosEventFilter(sd, QVector<int>(1, eventId));
 }
 
 void KsQuickContextMenu::_showCPU()
 {
 	int cpu = _data->rows()[_row]->cpu;
+	int sd = _data->rows()[_row]->stream_id;
 
-	_data->applyPosCPUFilter(QVector<int>(1, cpu));
+	_data->applyPosCPUFilter(sd, QVector<int>(1, cpu));
 }
 
 void KsQuickContextMenu::_hideCPU()
 {
+	int sd = _data->rows()[_row]->stream_id;
 	kshark_context *kshark_ctx(nullptr);
+	kshark_data_stream *stream;
 	QVector<int> vec;
 
 	if (!kshark_instance(&kshark_ctx))
 		return;
 
-	vec =_getFilterVector(kshark_ctx->hide_cpu_filter,
+	stream = kshark_get_data_stream(kshark_ctx, sd);
+	if (!stream)
+		return;
+
+	vec =_getFilterVector(stream->hide_cpu_filter,
 			      _data->rows()[_row]->cpu);
 
-	_data->applyNegCPUFilter(vec);
+	_data->applyNegCPUFilter(sd, vec);
 }
 
-QVector<int> KsQuickContextMenu::_getFilterVector(tracecmd_filter_id *filter, int newId)
+QVector<int> KsQuickContextMenu::_getFilterVector(kshark_hash_id *filter, int newId)
 {
 	QVector<int> vec = KsUtils::getFilterIds(filter);
 	if (!vec.contains(newId))
@@ -244,38 +300,42 @@ QVector<int> KsQuickContextMenu::_getFilterVector(tracecmd_filter_id *filter, in
 
 void KsQuickContextMenu::_addTaskPlot()
 {
-	int pid = kshark_get_pid_easy(_data->rows()[_row]);
+	int pid = kshark_get_pid(_data->rows()[_row]);
+	int sd = _data->rows()[_row]->stream_id;
 
-	emit addTaskPlot(pid);
+	emit addTaskPlot(sd, pid);
 }
 
 void KsQuickContextMenu::_addCPUPlot()
 {
-	emit addCPUPlot(_data->rows()[_row]->cpu);
+	emit addCPUPlot(_data->rows()[_row]->stream_id, _data->rows()[_row]->cpu);
 }
 
 void KsQuickContextMenu::_removeTaskPlot()
 {
-	int pid = kshark_get_pid_easy(_data->rows()[_row]);
+	int pid = kshark_get_pid(_data->rows()[_row]);
+	int sd = _data->rows()[_row]->stream_id;
 
-	emit removeTaskPlot(pid);
+	emit removeTaskPlot(sd, pid);
 }
 
 void KsQuickContextMenu::_removeCPUPlot()
 {
-	emit removeCPUPlot(_data->rows()[_row]->cpu);
+	emit removeCPUPlot(_data->rows()[_row]->stream_id, _data->rows()[_row]->cpu);
 }
 
 /**
  * @brief Create KsRmPlotContextMenu.
  *
  * @param dm: The State machine of the Dual marker.
+ * @param sd: Data stream identifier.
  * @param parent: The parent of this widget.
  */
-KsRmPlotContextMenu::KsRmPlotContextMenu(KsDualMarkerSM *dm,
+KsRmPlotContextMenu::KsRmPlotContextMenu(KsDualMarkerSM *dm, int sd,
 					 QWidget *parent)
 : KsQuickMarkerMenu(dm, parent),
-  _removePlotAction(this)
+  _removePlotAction(this),
+  _sd(sd)
 {
 	addSection("Plots");
 
@@ -289,12 +349,13 @@ KsRmPlotContextMenu::KsRmPlotContextMenu(KsDualMarkerSM *dm,
  * @brief Create KsRmCPUPlotMenu.
  *
  * @param dm: The State machine of the Dual marker.
+ * @param sd: Data stream identifier.
  * @param cpu : CPU Id.
  * @param parent: The parent of this widget.
  */
-KsRmCPUPlotMenu::KsRmCPUPlotMenu(KsDualMarkerSM *dm, int cpu,
+KsRmCPUPlotMenu::KsRmCPUPlotMenu(KsDualMarkerSM *dm, int sd, int cpu,
 				 QWidget *parent)
-: KsRmPlotContextMenu(dm, parent)
+: KsRmPlotContextMenu(dm, sd, parent)
 {
 	_removePlotAction.setText(QString("Remove [CPU %1]").arg(cpu));
 }
@@ -303,20 +364,18 @@ KsRmCPUPlotMenu::KsRmCPUPlotMenu(KsDualMarkerSM *dm, int cpu,
  * @brief Create KsRmTaskPlotMenu.
  *
  * @param dm: The State machine of the Dual marker.
+ * @param sd: Data stream identifier.
  * @param pid: Process Id.
  * @param parent: The parent of this widget.
  */
-KsRmTaskPlotMenu::KsRmTaskPlotMenu(KsDualMarkerSM *dm, int pid,
+KsRmTaskPlotMenu::KsRmTaskPlotMenu(KsDualMarkerSM *dm, int sd, int pid,
 				   QWidget *parent)
-: KsRmPlotContextMenu(dm, parent)
+: KsRmPlotContextMenu(dm, sd, parent)
 {
-	kshark_context *kshark_ctx(nullptr);
-	QString descr("Remove [ ");
-
-	if (!kshark_instance(&kshark_ctx))
-		return;
+	QString descr;
 
-	descr += tep_data_comm_from_pid(kshark_ctx->pevent, pid);
+	descr = "Remove [ ";
+	descr += kshark_comm_from_pid(sd, pid);
 	descr += "-";
 	descr += QString("%1").arg(pid);
 	descr += "] plot";
diff --git a/src/KsQuickContextMenu.hpp b/src/KsQuickContextMenu.hpp
index df8a65b..ca0b341 100644
--- a/src/KsQuickContextMenu.hpp
+++ b/src/KsQuickContextMenu.hpp
@@ -45,22 +45,22 @@ class KsQuickContextMenu : public KsQuickMarkerMenu {
 public:
 	KsQuickContextMenu() = delete;
 
-	KsQuickContextMenu(KsDataStore *data, size_t row,
-			   KsDualMarkerSM *dm,
+	KsQuickContextMenu(KsDualMarkerSM *dm,
+			   KsDataStore *data, size_t row,
 			   QWidget *parent = nullptr);
 
 signals:
 	/** Signal to add a task plot. */
-	void addTaskPlot(int);
+	void addTaskPlot(int sd, int pid);
 
 	/** Signal to add a CPU plot. */
-	void addCPUPlot(int);
+	void addCPUPlot(int sd, int cpu);
 
 	/** Signal to remove a task plot. */
-	void removeTaskPlot(int);
+	void removeTaskPlot(int sd, int pid);
 
 	/** Signal to remove a CPU plot. */
-	void removeCPUPlot(int);
+	void removeCPUPlot(int sd, int cpu);
 
 private:
 	void _hideTask();
@@ -83,7 +83,7 @@ private:
 
 	void _removeTaskPlot();
 
-	QVector<int> _getFilterVector(tracecmd_filter_id *filter, int newId);
+	QVector<int> _getFilterVector(kshark_hash_id *filter, int newId);
 
 	void _clearFilters() {_data->clearAllFilters();}
 
@@ -91,6 +91,8 @@ private:
 
 	size_t		_row;
 
+	QWidgetAction	_rawTime, _rawEvent;
+
 	QCheckBox	*_graphSyncCBox, *_listSyncCBox;
 
 	QAction _hideTaskAction, _showTaskAction;
@@ -118,7 +120,8 @@ class KsRmPlotContextMenu : public KsQuickMarkerMenu {
 public:
 	KsRmPlotContextMenu() = delete;
 
-	KsRmPlotContextMenu(KsDualMarkerSM *dm, QWidget *parent = nullptr);
+	KsRmPlotContextMenu(KsDualMarkerSM *dm, int sd,
+			    QWidget *parent = nullptr);
 
 signals:
 	/** Signal to remove a plot. */
@@ -127,13 +130,16 @@ signals:
 protected:
 	/** Menu action. */
 	QAction _removePlotAction;
+
+	/** Data stream identifier. */
+	int _sd;
 };
 
 /**
  * The KsQuickMarkerMenu class provides CPU Plot remove menus.
  */
 struct KsRmCPUPlotMenu : public KsRmPlotContextMenu {
-	KsRmCPUPlotMenu(KsDualMarkerSM *dm, int cpu,
+	KsRmCPUPlotMenu(KsDualMarkerSM *dm, int sd, int cpu,
 			QWidget *parent = nullptr);
 };
 
@@ -141,7 +147,7 @@ struct KsRmCPUPlotMenu : public KsRmPlotContextMenu {
  * The KsQuickMarkerMenu class provides Task Plot remove menus.
  */
 struct KsRmTaskPlotMenu : public KsRmPlotContextMenu {
-	KsRmTaskPlotMenu(KsDualMarkerSM *dm, int pid,
+	KsRmTaskPlotMenu(KsDualMarkerSM *dm, int sd, int pid,
 			 QWidget *parent = nullptr);
 };
 
diff --git a/src/KsTraceGraph.cpp b/src/KsTraceGraph.cpp
index 7b656c0..fd9cfac 100644
--- a/src/KsTraceGraph.cpp
+++ b/src/KsTraceGraph.cpp
@@ -17,7 +17,7 @@
 
 /** Create a default (empty) Trace graph widget. */
 KsTraceGraph::KsTraceGraph(QWidget *parent)
-: QWidget(parent),
+: KsWidgetsLib::KsDataWidget(parent),
   _pointerBar(this),
   _navigationBar(this),
   _zoomInButton("+", this),
@@ -34,13 +34,7 @@ KsTraceGraph::KsTraceGraph(QWidget *parent)
   _labelI4("", this),
   _labelI5("", this),
   _scrollArea(this),
-  _drawWindow(&_scrollArea),
-  _legendWindow(&_drawWindow),
-  _legendAxisX(&_drawWindow),
-  _labelXMin("", &_legendAxisX),
-  _labelXMid("", &_legendAxisX),
-  _labelXMax("", &_legendAxisX),
-  _glWindow(&_drawWindow),
+  _glWindow(&_scrollArea),
   _mState(nullptr),
   _data(nullptr),
   _keyPressed(false)
@@ -62,7 +56,7 @@ KsTraceGraph::KsTraceGraph(QWidget *parent)
 
 	_pointerBar.addWidget(&_labelP1);
 	_labelP2.setFrameStyle(QFrame::Panel | QFrame::Sunken);
-	_labelP2.setStyleSheet("QLabel { background-color : white; color: black}");
+	_labelP2.setStyleSheet("QLabel {background-color : white; color: black}");
 	_labelP2.setTextInteractionFlags(Qt::TextSelectableByMouse);
 	_labelP2.setFixedWidth(FONT_WIDTH * 16);
 	_pointerBar.addWidget(&_labelP2);
@@ -84,31 +78,7 @@ KsTraceGraph::KsTraceGraph(QWidget *parent)
 	_pointerBar.addSeparator();
 	_pointerBar.addWidget(&_labelI5);
 
-	_legendAxisX.setFixedHeight(FONT_HEIGHT * 1.5);
-	_legendAxisX.setLayout(new QHBoxLayout);
-	_legendAxisX.layout()->setSpacing(0);
-	_legendAxisX.layout()->setContentsMargins(0, 0, FONT_WIDTH, 0);
-
-	_labelXMin.setAlignment(Qt::AlignLeft);
-	_labelXMid.setAlignment(Qt::AlignHCenter);
-	_labelXMax.setAlignment(Qt::AlignRight);
-
-	_legendAxisX.layout()->addWidget(&_labelXMin);
-	_legendAxisX.layout()->addWidget(&_labelXMid);
-	_legendAxisX.layout()->addWidget(&_labelXMax);
-	_legendAxisX.setStyleSheet("QLabel { background-color : white; color: black}");
-
-	_drawWindow.setMinimumSize(100, 100);
-	_drawWindow.setStyleSheet("QWidget {background-color : white;}");
-
-	_drawLayout.setContentsMargins(0, 0, 0, 0);
-	_drawLayout.setSpacing(0);
-	_drawLayout.addWidget(&_legendAxisX, 0, 1);
-	_drawLayout.addWidget(&_legendWindow, 1, 0);
-	_drawLayout.addWidget(&_glWindow, 1, 1);
-	_drawWindow.setLayout(&_drawLayout);
-
-	_drawWindow.installEventFilter(this);
+	_glWindow.installEventFilter(this);
 
 	connect(&_glWindow,	&KsGLWidget::select,
 		this,		&KsTraceGraph::markEntry);
@@ -134,15 +104,12 @@ KsTraceGraph::KsTraceGraph(QWidget *parent)
 	connect(&_glWindow,	&KsGLWidget::stopUpdating,
 		this,		&KsTraceGraph::_stopUpdating);
 
-	connect(_glWindow.model(),	&KsGraphModel::modelReset,
-		this,			&KsTraceGraph::_updateTimeLegends);
-
 	_glWindow.setContextMenuPolicy(Qt::CustomContextMenu);
 	connect(&_glWindow,	&QWidget::customContextMenuRequested,
 		this,		&KsTraceGraph::_onCustomContextMenu);
 
 	_scrollArea.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
-	_scrollArea.setWidget(&_drawWindow);
+	_scrollArea.setWidget(&_glWindow);
 
 	lamMakeNavButton(&_scrollLeftButton);
 	connect(&_scrollLeftButton,	&QPushButton::pressed,
@@ -187,7 +154,6 @@ void KsTraceGraph::loadData(KsDataStore *data)
 {
 	_data = data;
 	_glWindow.loadData(data);
-	_updateGraphLegends();
 	updateGeom();
 }
 
@@ -211,14 +177,10 @@ void KsTraceGraph::reset()
 		l1->setText("");
 
 	_selfUpdate();
-	for (auto l2: {&_labelXMin, &_labelXMid, &_labelXMax})
-		l2->setText("");
 }
 
 void KsTraceGraph::_selfUpdate()
 {
-	_updateGraphLegends();
-	_updateTimeLegends();
 	_markerReDraw();
 	_glWindow.model()->update();
 	updateGeom();
@@ -226,12 +188,20 @@ void KsTraceGraph::_selfUpdate()
 
 void KsTraceGraph::_zoomIn()
 {
-	_updateGraphs(GraphActions::ZoomIn);
+	KsWidgetsLib::KsDataWork action = KsWidgetsLib::KsDataWork::ZoomIn;
+
+	startOfWork(action);
+	_updateGraphs(action);
+	endOfWork(action);
 }
 
 void KsTraceGraph::_zoomOut()
 {
-	_updateGraphs(GraphActions::ZoomOut);
+	KsWidgetsLib::KsDataWork action = KsWidgetsLib::KsDataWork::ZoomOut;
+
+	startOfWork(action);
+	_updateGraphs(action);
+	endOfWork(action);
 }
 
 void KsTraceGraph::_quickZoomIn()
@@ -239,6 +209,8 @@ void KsTraceGraph::_quickZoomIn()
 	if (_glWindow.isEmpty())
 		return;
 
+	startOfWork(KsWidgetsLib::KsDataWork::QuickZoomIn);
+
 	/* Bin size will be 100 ns. */
 	_glWindow.model()->quickZoomIn(100);
 	if (_mState->activeMarker()._isSet &&
@@ -249,7 +221,10 @@ void KsTraceGraph::_quickZoomIn()
 		 */
 		uint64_t ts = _mState->activeMarker()._ts;
 		_glWindow.model()->jumpTo(ts);
+		_glWindow.render();
 	}
+
+	endOfWork(KsWidgetsLib::KsDataWork::QuickZoomIn);
 }
 
 void KsTraceGraph::_quickZoomOut()
@@ -257,17 +232,28 @@ void KsTraceGraph::_quickZoomOut()
 	if (_glWindow.isEmpty())
 		return;
 
+	startOfWork(KsWidgetsLib::KsDataWork::QuickZoomOut);
 	_glWindow.model()->quickZoomOut();
+	_glWindow.render();
+	endOfWork(KsWidgetsLib::KsDataWork::QuickZoomOut);
 }
 
 void KsTraceGraph::_scrollLeft()
 {
-	_updateGraphs(GraphActions::ScrollLeft);
+	KsWidgetsLib::KsDataWork action = KsWidgetsLib::KsDataWork::ScrollLeft;
+
+	startOfWork(action);
+	_updateGraphs(action);
+	endOfWork(action);
 }
 
 void KsTraceGraph::_scrollRight()
 {
-	_updateGraphs(GraphActions::ScrollRight);
+	KsWidgetsLib::KsDataWork action = KsWidgetsLib::KsDataWork::ScrollRight;
+
+	startOfWork(action);
+	_updateGraphs(action);
+	endOfWork(action);
 }
 
 void KsTraceGraph::_stopUpdating()
@@ -292,10 +278,18 @@ QString KsTraceGraph::_t2str(uint64_t sec, uint64_t usec) {
 	return QString::number(sec) + "." + usecStr;
 }
 
-void KsTraceGraph::_resetPointer(uint64_t ts, int cpu, int pid)
+void KsTraceGraph::_resetPointer(int64_t ts, int sd, int cpu, int pid)
 {
+	kshark_entry entry;
 	uint64_t sec, usec;
 
+	entry.cpu = cpu;
+	entry.pid = pid;
+	entry.stream_id = sd;
+
+	if (ts < 0)
+		ts = 0;
+
 	kshark_convert_nano(ts, &sec, &usec);
 	_labelP2.setText(_t2str(sec, usec));
 
@@ -305,7 +299,7 @@ void KsTraceGraph::_resetPointer(uint64_t ts, int cpu, int pid)
 		if (!kshark_instance(&kshark_ctx))
 			return;
 
-		QString comm(tep_data_comm_from_pid(kshark_ctx->pevent, pid));
+		QString comm(kshark_get_task(&entry));
 		comm.append("-");
 		comm.append(QString("%1").arg(pid));
 		_labelI1.setText(comm);
@@ -323,23 +317,30 @@ void KsTraceGraph::_resetPointer(uint64_t ts, int cpu, int pid)
 void KsTraceGraph::_setPointerInfo(size_t i)
 {
 	kshark_entry *e = _data->rows()[i];
-	QString event(kshark_get_event_name_easy(e));
-	QString lat(kshark_get_latency_easy(e));
-	QString info(kshark_get_info_easy(e));
-	QString comm(kshark_get_task_easy(e));
-	int labelWidth, width;
-	QString elidedText;
+	auto lanMakeString = [] (char *buffer) {
+		QString str(buffer);
+		free(buffer);
+		return str;
+	};
+
+	QString event(lanMakeString(kshark_get_event_name(e)));
+	QString aux(lanMakeString(kshark_get_aux_info(e)));
+	QString info(lanMakeString(kshark_get_info(e)));
+	QString comm(lanMakeString(kshark_get_task(e)));
+	QString pointer, elidedText;
+	int labelWidth;
 	uint64_t sec, usec;
 
 	kshark_convert_nano(e->ts, &sec, &usec);
-	_labelP2.setText(_t2str(sec, usec));
+	pointer.sprintf("%" PRIu64 ".%06" PRIu64 "", sec, usec);
+	_labelP2.setText(pointer);
 
 	comm.append("-");
-	comm.append(QString("%1").arg(kshark_get_pid_easy(e)));
+	comm.append(QString("%1").arg(kshark_get_pid(e)));
 
 	_labelI1.setText(comm);
 	_labelI2.setText(QString("CPU %1").arg(e->cpu));
-	_labelI3.setText(lat);
+	_labelI3.setText(aux);
 	_labelI4.setText(event);
 	_labelI5.setText(info);
 	QCoreApplication::processEvents();
@@ -353,16 +354,7 @@ void KsTraceGraph::_setPointerInfo(size_t i)
 	 * The Info string is too long and cannot be displayed on the toolbar.
 	 * Try to fit the text in the available space.
 	 */
-	QFontMetrics metrix(_labelI5.font());
-	width = labelWidth - FONT_WIDTH * 3;
-	elidedText = metrix.elidedText(info, Qt::ElideRight, width);
-
-	while(labelWidth < STRING_WIDTH(elidedText) + FONT_WIDTH * 5) {
-		width -= FONT_WIDTH * 3;
-		elidedText = metrix.elidedText(info, Qt::ElideRight, width);
-	}
-
-	_labelI5.setText(elidedText);
+	KsUtils::setElidedText(&_labelI5, info, Qt::ElideRight, labelWidth);
 	_labelI5.setVisible(true);
 	QCoreApplication::processEvents();
 }
@@ -374,127 +366,188 @@ void KsTraceGraph::_setPointerInfo(size_t i)
  */
 void KsTraceGraph::markEntry(size_t row)
 {
-	int graph, cpuGrId, taskGrId;
-
-	_glWindow.findGraphIds(*_data->rows()[row], &cpuGrId, &taskGrId);
-
-	/*
-	 * If a Task graph has been found, this Task graph will be
-	 * visible. If no Task graph has been found, make visible
-	 * the corresponding CPU graph.
-	 */
-	if (taskGrId >= 0)
-		graph = taskGrId;
-	else
-		graph = cpuGrId;
-
-	_scrollArea.ensureVisible(0,
-				  _legendAxisX.height() +
-				  _glWindow.vMargin() +
-				  KS_GRAPH_HEIGHT / 2 +
-				  graph*(KS_GRAPH_HEIGHT + _glWindow.vSpacing()),
-				  50,
-				  KS_GRAPH_HEIGHT / 2 + _glWindow.vSpacing() / 2);
+	int yPosVis(-1);
 
 	_glWindow.model()->jumpTo(_data->rows()[row]->ts);
-	_mState->activeMarker().set(*_data,
-				    _glWindow.model()->histo(),
-				    row, cpuGrId, taskGrId);
+	_mState->activeMarker().set(*_data, _glWindow.model()->histo(),
+				    row, _data->rows()[row]->stream_id);
 
 	_mState->updateMarkers(*_data, &_glWindow);
+
+	/*
+	 * If a Combo graph has been found, this Combo graph will be visible.
+	 * Else the Task graph will be shown. If no Combo and no Task graph
+	 * has been found, make visible the corresponding CPU graph.
+	 */
+	if (_mState->activeMarker()._mark.comboIsVisible())
+		yPosVis = _mState->activeMarker()._mark.comboY();
+	else if (_mState->activeMarker()._mark.taskIsVisible())
+		yPosVis = _mState->activeMarker()._mark.taskY();
+	else if (_mState->activeMarker()._mark.cpuIsVisible())
+		yPosVis = _mState->activeMarker()._mark.cpuY();
+
+	if (yPosVis > 0)
+		_scrollArea.ensureVisible(0, yPosVis);
 }
 
 void KsTraceGraph::_markerReDraw()
 {
-	int cpuGrId, taskGrId;
 	size_t row;
 
 	if (_mState->markerA()._isSet) {
 		row = _mState->markerA()._pos;
-		_glWindow.findGraphIds(*_data->rows()[row], &cpuGrId, &taskGrId);
-		_mState->markerA().set(*_data,
-				       _glWindow.model()->histo(),
-				       row, cpuGrId, taskGrId);
+		_mState->markerA().set(*_data, _glWindow.model()->histo(),
+				       row, _data->rows()[row]->stream_id);
 	}
 
 	if (_mState->markerB()._isSet) {
 		row = _mState->markerB()._pos;
-		_glWindow.findGraphIds(*_data->rows()[row], &cpuGrId, &taskGrId);
-		_mState->markerB().set(*_data,
-				       _glWindow.model()->histo(),
-				       row, cpuGrId, taskGrId);
+		_mState->markerB().set(*_data, _glWindow.model()->histo(),
+				       row, _data->rows()[row]->stream_id);
 	}
 }
 
 /**
  * @brief Redreaw all CPU graphs.
  *
+ * @param sd: Data stream identifier.
  * @param v: CPU ids to be plotted.
  */
-void KsTraceGraph::cpuReDraw(QVector<int> v)
+void KsTraceGraph::cpuReDraw(int sd, QVector<int> v)
 {
-	_glWindow._cpuList = v;
+	startOfWork(KsWidgetsLib::KsDataWork::EditPlotList);
+	if (_glWindow._streamPlots.contains(sd))
+		_glWindow._streamPlots[sd]._cpuList = v;
+
 	_selfUpdate();
+	endOfWork(KsWidgetsLib::KsDataWork::EditPlotList);
 }
 
 /**
  * @brief Redreaw all Task graphs.
  *
+ * @param sd: Data stream identifier.
  * @param v: Process ids of the tasks to be plotted.
  */
-void KsTraceGraph::taskReDraw(QVector<int> v)
+void KsTraceGraph::taskReDraw(int sd, QVector<int> v)
+{
+	startOfWork(KsWidgetsLib::KsDataWork::EditPlotList);
+	if (_glWindow._streamPlots.contains(sd))
+		_glWindow._streamPlots[sd]._taskList = v;
+
+	_selfUpdate();
+	endOfWork(KsWidgetsLib::KsDataWork::EditPlotList);
+}
+
+/**
+ * @brief Redreaw all virtCombo graphs.
+ *
+ * @param nCombos: Numver of Combo plots.
+ * @param v: Descriptor of the Combo to be plotted.
+ */
+void KsTraceGraph::comboReDraw(int nCombos, QVector<int> v)
 {
-	_glWindow._taskList = v;
+	KsComboPlot combo;
+
+	startOfWork(KsWidgetsLib::KsDataWork::EditPlotList);
+
+	_glWindow._comboPlots.clear();
+
+	for (int i = 0; i < nCombos; ++i) {
+		combo.resize(v.takeFirst());
+		for (auto &p: combo)
+			p << v;
+
+		_glWindow._comboPlots.append(combo);
+	}
+
 	_selfUpdate();
+	endOfWork(KsWidgetsLib::KsDataWork::EditPlotList);
 }
 
 /** Add (and plot) a CPU graph to the existing list of CPU graphs. */
-void KsTraceGraph::addCPUPlot(int cpu)
+void KsTraceGraph::addCPUPlot(int sd, int cpu)
 {
-	if (_glWindow._cpuList.contains(cpu))
+	startOfWork(KsWidgetsLib::KsDataWork::EditPlotList);
+	QVector<int> &list = _glWindow._streamPlots[sd]._cpuList;
+	if (list.contains(cpu))
 		return;
 
-	_glWindow._cpuList.append(cpu);
-	std::sort(_glWindow._cpuList.begin(), _glWindow._cpuList.end());
+	list.append(cpu);
+	std::sort(list.begin(), list.end());
+
 	_selfUpdate();
+	endOfWork(KsWidgetsLib::KsDataWork::EditPlotList);
 }
 
 /** Add (and plot) a Task graph to the existing list of Task graphs. */
-void KsTraceGraph::addTaskPlot(int pid)
+void KsTraceGraph::addTaskPlot(int sd, int pid)
 {
-	if (_glWindow._taskList.contains(pid))
+	startOfWork(KsWidgetsLib::KsDataWork::EditPlotList);
+	QVector<int> &list = _glWindow._streamPlots[sd]._taskList;
+	if (list.contains(pid))
 		return;
 
-	_glWindow._taskList.append(pid);
-	std::sort(_glWindow._taskList.begin(), _glWindow._taskList.end());
+	list.append(pid);
+	std::sort(list.begin(), list.end());
+
 	_selfUpdate();
+	endOfWork(KsWidgetsLib::KsDataWork::EditPlotList);
 }
 
 /** Remove a CPU graph from the existing list of CPU graphs. */
-void KsTraceGraph::removeCPUPlot(int cpu)
+void KsTraceGraph::removeCPUPlot(int sd, int cpu)
 {
-	if (!_glWindow._cpuList.contains(cpu))
+	startOfWork(KsWidgetsLib::KsDataWork::EditPlotList);
+	if (!_glWindow._streamPlots[sd]._cpuList.contains(cpu))
 		return;
 
-	_glWindow._cpuList.removeAll(cpu);
+	_glWindow._streamPlots[sd]._cpuList.removeAll(cpu);
 	_selfUpdate();
+	endOfWork(KsWidgetsLib::KsDataWork::EditPlotList);
 }
 
 /** Remove a Task graph from the existing list of Task graphs. */
-void KsTraceGraph::removeTaskPlot(int pid)
+void KsTraceGraph::removeTaskPlot(int sd, int pid)
 {
-	if (!_glWindow._taskList.contains(pid))
+	startOfWork(KsWidgetsLib::KsDataWork::EditPlotList);
+	if (!_glWindow._streamPlots[sd]._taskList.contains(pid))
 		return;
 
-	_glWindow._taskList.removeAll(pid);
+	_glWindow._streamPlots[sd]._taskList.removeAll(pid);
 	_selfUpdate();
+	endOfWork(KsWidgetsLib::KsDataWork::EditPlotList);
 }
 
 /** Update the content of all graphs. */
 void KsTraceGraph::update(KsDataStore *data)
 {
-	_glWindow.model()->update(data);
+	kshark_context *kshark_ctx(nullptr);
+	QVector<int> streamIds;
+
+	if (!kshark_instance(&kshark_ctx))
+		return;
+
+	streamIds = KsUtils::getStreamIdList(kshark_ctx);
+	for (auto const &sd: streamIds)
+		for (auto &pid: _glWindow._streamPlots[sd]._taskList) {
+			kshark_unregister_data_collection(&kshark_ctx->collections,
+							  kshark_match_pid,
+							  sd, &pid, 1);
+		}
+
 	_selfUpdate();
+
+	streamIds = KsUtils::getStreamIdList(kshark_ctx);
+	for (auto const &sd: streamIds)
+		for (auto &pid: _glWindow._streamPlots[sd]._taskList) {
+			kshark_register_data_collection(kshark_ctx,
+							data->rows(),
+							data->size(),
+							kshark_match_pid,
+							sd, &pid, 1,
+							25);
+		}
 }
 
 /** Update the geometry of the widget. */
@@ -519,19 +572,14 @@ void KsTraceGraph::updateGeom()
 	 * of the scroll bar.
 	 */
 	dwWidth = _scrollArea.width();
-	if (_glWindow.height() + _legendAxisX.height() > _scrollArea.height())
+	if (_glWindow.height() > _scrollArea.height())
 		dwWidth -=
 			qApp->style()->pixelMetric(QStyle::PM_ScrollBarExtent);
 
-	/*
-	 * Set the height of the Draw window according to the number of
-	 * plotted graphs.
-	 */
-	_drawWindow.resize(dwWidth,
-			   _glWindow.height() + _legendAxisX.height());
+	_glWindow.resize(dwWidth, _glWindow.height());
 
 	/* Set the minimum height of the Graph widget. */
-	hMin = _drawWindow.height() +
+	hMin = _glWindow.height() +
 	       _pointerBar.height() +
 	       _navigationBar.height() +
 	       _layout.contentsMargins().top() +
@@ -546,7 +594,7 @@ void KsTraceGraph::updateGeom()
 	 * Now use the height of the Draw Window to fix the maximum height
 	 * of the Graph widget.
 	 */
-	setMaximumHeight(_drawWindow.height() +
+	setMaximumHeight(_glWindow.height() +
 			 _pointerBar.height() +
 			 _navigationBar.height() +
 			 _layout.spacing() * 2 +
@@ -560,75 +608,6 @@ void KsTraceGraph::updateGeom()
 	_glWindow.update();
 }
 
-void KsTraceGraph::_updateGraphLegends()
-{
-	QString graphLegends, graphName;
-	QVBoxLayout *layout;
-	int width = 0;
-
-	if (_legendWindow.layout()) {
-		/*
-		 * Remove and delete the existing layout of the legend window.
-		 */
-		QLayoutItem *child;
-		while ((child = _legendWindow.layout()->takeAt(0)) != 0) {
-			delete child->widget();
-			delete child;
-		}
-
-		delete _legendWindow.layout();
-	}
-
-	layout = new QVBoxLayout;
-	layout->setContentsMargins(FONT_WIDTH, 0, 0, 0);
-	layout->setSpacing(_glWindow.vSpacing());
-	layout->setAlignment(Qt::AlignTop);
-	layout->addSpacing(_glWindow.vMargin());
-
-	auto lamMakeName = [&]() {
-		QLabel *name = new QLabel(graphName);
-
-		if (width < STRING_WIDTH(graphName))
-			width = STRING_WIDTH(graphName);
-
-		name->setAlignment(Qt::AlignBottom);
-		name->setStyleSheet("QLabel {background-color : white; color : black}");
-		name->setFixedHeight(KS_GRAPH_HEIGHT);
-		layout->addWidget(name);
-	};
-
-	for (auto const &cpu: _glWindow._cpuList) {
-		graphName = QString("CPU %1").arg(cpu);
-		lamMakeName();
-	}
-
-	for (auto const &pid: _glWindow._taskList) {
-		graphName = QString(tep_data_comm_from_pid(_data->tep(),
-							   pid));
-		graphName.append(QString("-%1").arg(pid));
-		lamMakeName();
-	}
-
-	_legendWindow.setLayout(layout);
-	_legendWindow.setMaximumWidth(width + FONT_WIDTH);
-}
-
-void KsTraceGraph::_updateTimeLegends()
-{
-	uint64_t sec, usec, tsMid;
-
-	kshark_convert_nano(_glWindow.model()->histo()->min, &sec, &usec);
-	_labelXMin.setText(_t2str(sec, usec));
-
-	tsMid = (_glWindow.model()->histo()->min +
-		 _glWindow.model()->histo()->max) / 2;
-	kshark_convert_nano(tsMid, &sec, &usec);
-	_labelXMid.setText(_t2str(sec, usec));
-
-	kshark_convert_nano(_glWindow.model()->histo()->max, &sec, &usec);
-	_labelXMax.setText(_t2str(sec, usec));
-}
-
 /**
  * Reimplemented event handler used to update the geometry of the widget on
  * resize events.
@@ -646,16 +625,25 @@ void KsTraceGraph::resizeEvent(QResizeEvent* event)
  */
 bool KsTraceGraph::eventFilter(QObject* obj, QEvent* evt)
 {
-	if (obj == &_drawWindow && evt->type() == QEvent::Enter)
+	/* Desable all mouse events for the OpenGL wiget when busy. */
+	if (obj == &_glWindow && this->isBusy() &&
+	    (evt->type() == QEvent::MouseButtonDblClick ||
+	     evt->type() == QEvent::MouseButtonPress ||
+	     evt->type() == QEvent::MouseButtonRelease ||
+	     evt->type() == QEvent::MouseMove)
+	)
+		return true;
+
+	if (obj == &_glWindow && evt->type() == QEvent::Enter)
 		_glWindow.setFocus();
 
-	if (obj == &_drawWindow && evt->type() == QEvent::Leave)
+	if (obj == &_glWindow && evt->type() == QEvent::Leave)
 		_glWindow.clearFocus();
 
 	return QWidget::eventFilter(obj, evt);
 }
 
-void KsTraceGraph::_updateGraphs(GraphActions action)
+void KsTraceGraph::_updateGraphs(KsWidgetsLib::KsDataWork action)
 {
 	double k;
 	int bin;
@@ -673,7 +661,7 @@ void KsTraceGraph::_updateGraphs(GraphActions action)
 	k = .01;
 	while (_keyPressed) {
 		switch (action) {
-		case GraphActions::ZoomIn:
+		case KsWidgetsLib::KsDataWork::ZoomIn:
 			if (_mState->activeMarker()._isSet &&
 			    _mState->activeMarker().isVisible()) {
 				/*
@@ -692,7 +680,7 @@ void KsTraceGraph::_updateGraphs(GraphActions action)
 
 			break;
 
-		case GraphActions::ZoomOut:
+		case KsWidgetsLib::KsDataWork::ZoomOut:
 			if (_mState->activeMarker()._isSet &&
 			    _mState->activeMarker().isVisible()) {
 				/*
@@ -711,13 +699,16 @@ void KsTraceGraph::_updateGraphs(GraphActions action)
 
 			break;
 
-		case GraphActions::ScrollLeft:
+		case KsWidgetsLib::KsDataWork::ScrollLeft:
 			_glWindow.model()->shiftBackward(10);
 			break;
 
-		case GraphActions::ScrollRight:
+		case KsWidgetsLib::KsDataWork::ScrollRight:
 			_glWindow.model()->shiftForward(10);
 			break;
+
+		default:
+			return;
 		}
 
 		/*
@@ -729,7 +720,7 @@ void KsTraceGraph::_updateGraphs(GraphActions action)
 			k  *= 1.02;
 
 		_mState->updateMarkers(*_data, &_glWindow);
-		_updateTimeLegends();
+		_glWindow.render();
 		QCoreApplication::processEvents();
 	}
 }
@@ -737,7 +728,7 @@ void KsTraceGraph::_updateGraphs(GraphActions action)
 void KsTraceGraph::_onCustomContextMenu(const QPoint &point)
 {
 	KsQuickMarkerMenu *menu(nullptr);
-	int cpu, pid;
+	int sd, cpu, pid;
 	size_t row;
 	bool found;
 
@@ -745,8 +736,8 @@ void KsTraceGraph::_onCustomContextMenu(const QPoint &point)
 	if (found) {
 		/* KernelShark entry has been found under the cursor. */
 		KsQuickContextMenu *entryMenu;
-		menu = entryMenu = new KsQuickContextMenu(_data, row,
-							  _mState, this);
+		menu = entryMenu = new KsQuickContextMenu(_mState, _data, row,
+							  this);
 
 		connect(entryMenu,	&KsQuickContextMenu::addTaskPlot,
 			this,		&KsTraceGraph::addTaskPlot);
@@ -760,34 +751,35 @@ void KsTraceGraph::_onCustomContextMenu(const QPoint &point)
 		connect(entryMenu,	&KsQuickContextMenu::removeCPUPlot,
 			this,		&KsTraceGraph::removeCPUPlot);
 	} else {
-		cpu = _glWindow.getPlotCPU(point);
+		if (!_glWindow.getPlotInfo(point, &sd, &cpu, &pid))
+			return;
+
 		if (cpu >= 0) {
 			/*
 			 * This is a CPU plot, but we do not have an entry
 			 * under the cursor.
 			 */
 			KsRmCPUPlotMenu *rmMenu;
-			menu = rmMenu = new KsRmCPUPlotMenu(_mState, cpu, this);
+			menu = rmMenu = new KsRmCPUPlotMenu(_mState, sd, cpu, this);
 
-			auto lamRmPlot = [&cpu, this] () {
-				removeCPUPlot(cpu);
+			auto lamRmPlot = [&sd, &cpu, this] () {
+				removeCPUPlot(sd, cpu);
 			};
 
 			connect(rmMenu, &KsRmPlotContextMenu::removePlot,
 				lamRmPlot);
 		}
 
-		pid = _glWindow.getPlotPid(point);
 		if (pid >= 0) {
 			/*
 			 * This is a Task plot, but we do not have an entry
 			 * under the cursor.
 			 */
 			KsRmTaskPlotMenu *rmMenu;
-			menu = rmMenu = new KsRmTaskPlotMenu(_mState, pid, this);
+			menu = rmMenu = new KsRmTaskPlotMenu(_mState, sd, pid, this);
 
-			auto lamRmPlot = [&pid, this] () {
-				removeTaskPlot(pid);
+			auto lamRmPlot = [&sd, &pid, this] () {
+				removeTaskPlot(sd, pid);
 			};
 
 			connect(rmMenu, &KsRmPlotContextMenu::removePlot,
diff --git a/src/KsTraceGraph.hpp b/src/KsTraceGraph.hpp
index 0eeef14..0e31b88 100644
--- a/src/KsTraceGraph.hpp
+++ b/src/KsTraceGraph.hpp
@@ -12,6 +12,7 @@
 #define _KS_TRACEGRAPH_H
 
 // KernelShark
+#include "KsWidgetsLib.hpp"
 #include "KsGLWidget.hpp"
 
 /**
@@ -35,7 +36,7 @@ public:
  * The KsTraceViewer class provides a widget for interactive visualization of
  * trace data shown as time-series.
  */
-class KsTraceGraph : public QWidget
+class KsTraceGraph : public KsWidgetsLib::KsDataWidget
 {
 	Q_OBJECT
 public:
@@ -52,17 +53,19 @@ public:
 
 	void markEntry(size_t);
 
-	void cpuReDraw(QVector<int>);
+	void cpuReDraw(int sd, QVector<int> cpus);
 
-	void taskReDraw(QVector<int>);
+	void taskReDraw(int sd, QVector<int> pids);
 
-	void addCPUPlot(int);
+	void comboReDraw(int sd, QVector<int> v);
 
-	void addTaskPlot(int);
+	void addCPUPlot(int sd, int cpu);
 
-	void removeCPUPlot(int);
+	void addTaskPlot(int sd, int pid);
 
-	void removeTaskPlot(int);
+	void removeCPUPlot(int sd, int cpu);
+
+	void removeTaskPlot(int sd, int pid);
 
 	void update(KsDataStore *data);
 
@@ -96,34 +99,24 @@ private:
 
 	void _stopUpdating();
 
-	void _resetPointer(uint64_t ts, int cpu, int pid);
+	void _resetPointer(int64_t ts, int sd, int cpu, int pid);
 
 	void _setPointerInfo(size_t);
 
-	void _updateTimeLegends();
-
-	void _updateGraphLegends();
-
 	void _selfUpdate();
 
 	void _markerReDraw();
 
-	QString _t2str(uint64_t sec, uint64_t usec);
-
-	enum class GraphActions {
-		ZoomIn,
-		ZoomOut,
-		ScrollLeft,
-		ScrollRight
-	};
-
-	void _updateGraphs(GraphActions action);
+	void _updateGraphs(KsWidgetsLib::KsDataWork action);
 
 	void _onCustomContextMenu(const QPoint &point);
 
+	QString _t2str(uint64_t sec, uint64_t usec);
+
 	QToolBar	_pointerBar, _navigationBar;
 
 	QPushButton	_zoomInButton, _quickZoomInButton;
+
 	QPushButton	_zoomOutButton, _quickZoomOutButton;
 
 	QPushButton	_scrollLeftButton, _scrollRightButton;
@@ -133,14 +126,8 @@ private:
 
 	KsGraphScrollArea	_scrollArea;
 
-	QWidget		_drawWindow, _legendWindow, _legendAxisX;
-
-	QLabel		_labelXMin, _labelXMid, _labelXMax;
-
 	KsGLWidget	_glWindow;
 
-	QGridLayout	_drawLayout;
-
 	QVBoxLayout	_layout;
 
 	KsDualMarkerSM	*_mState;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v2 18/27] kernel-shark: Update KsTraceViewer
  2021-02-11 10:31 [PATCH v2 00/27] Complete the KernelShark v2 transformation Yordan Karadzhov (VMware)
                   ` (16 preceding siblings ...)
  2021-02-11 10:31 ` [PATCH v2 17/27] kernel-shark: Update KsTraceGraph and KsQuickContextMenu Yordan Karadzhov (VMware)
@ 2021-02-11 10:31 ` Yordan Karadzhov (VMware)
  2021-02-11 10:31 ` [PATCH v2 19/27] kernel-shark: Update KsAdvFilteringDialog Yordan Karadzhov (VMware)
                   ` (9 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Yordan Karadzhov (VMware) @ 2021-02-11 10:31 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

The compilation of KsTraceViewer.cpp is re-enabled and all
functionalities are made compatible with the new version of
the C API of libkshark (KernelShark 2.0).

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 src/CMakeLists.txt    |  4 +--
 src/KsTraceGraph.hpp  |  5 +++-
 src/KsTraceViewer.cpp | 57 +++++++++++++++++++++++++++++--------------
 src/KsTraceViewer.hpp | 13 +++++++---
 4 files changed, 54 insertions(+), 25 deletions(-)

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index ab74d5a..e09deb7 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -73,7 +73,7 @@ if (Qt5Widgets_FOUND AND Qt5Network_FOUND AND TT_FONT_FILE)
                         KsDualMarker.hpp
                         KsWidgetsLib.hpp
                         KsTraceGraph.hpp
-#                         KsTraceViewer.hpp
+                        KsTraceViewer.hpp
 #                         KsMainWindow.hpp
 #                         KsCaptureDialog.hpp
                         KsQuickContextMenu.hpp)
@@ -89,7 +89,7 @@ if (Qt5Widgets_FOUND AND Qt5Network_FOUND AND TT_FONT_FILE)
                                                             KsDualMarker.cpp
                                                             KsWidgetsLib.cpp
                                                             KsTraceGraph.cpp
-#                                                             KsTraceViewer.cpp
+                                                            KsTraceViewer.cpp
 #                                                             KsMainWindow.cpp
 #                                                             KsCaptureDialog.cpp
                                                             KsQuickContextMenu.cpp)
diff --git a/src/KsTraceGraph.hpp b/src/KsTraceGraph.hpp
index 0e31b88..6e83f21 100644
--- a/src/KsTraceGraph.hpp
+++ b/src/KsTraceGraph.hpp
@@ -29,7 +29,10 @@ public:
 	 * Reimplemented handler for mouse wheel events. All mouse wheel
 	 * events will be ignored.
 	 */
-	void wheelEvent(QWheelEvent *evt) {evt->ignore();}
+	void wheelEvent(QWheelEvent *evt) {
+		if (QApplication::keyboardModifiers() != Qt::ControlModifier)
+			QScrollArea::wheelEvent(evt);
+	}
 };
 
 /**
diff --git a/src/KsTraceViewer.cpp b/src/KsTraceViewer.cpp
index 0e0e3d4..afd5a85 100644
--- a/src/KsTraceViewer.cpp
+++ b/src/KsTraceViewer.cpp
@@ -9,7 +9,7 @@
  *  @brief   KernelShark Trace Viewer widget.
  */
 
-// C++11
+// C++
 #include <thread>
 #include <future>
 #include <queue>
@@ -50,11 +50,10 @@ void KsTableView::scrollTo(const QModelIndex &index, ScrollHint hint)
 
 /** Create a default (empty) Trace viewer widget. */
 KsTraceViewer::KsTraceViewer(QWidget *parent)
-: QWidget(parent),
+: KsWidgetsLib::KsDataWidget(parent),
   _view(this),
   _model(this),
   _proxyModel(this),
-  _tableHeader(_model.header()),
   _toolbar(this),
   _labelSearch("Search: Column", this),
   _labelGrFollows("Graph follows  ", this),
@@ -72,7 +71,7 @@ KsTraceViewer::KsTraceViewer(QWidget *parent)
 
 	/* On the toolbar make two Combo boxes for the search settings. */
 	_toolbar.addWidget(&_labelSearch);
-	_searchFSM._columnComboBox.addItems(_tableHeader);
+	_searchFSM._columnComboBox.addItems(_model.header());
 
 	/*
 	 * Using the old Signal-Slot syntax because
@@ -163,6 +162,9 @@ void KsTraceViewer::loadData(KsDataStore *data)
 	_model.fill(data);
 	this->_resizeToContents();
 
+	_searchFSM._columnComboBox.clear();
+	_searchFSM._columnComboBox.addItems(_model.header());
+
 	this->setMinimumHeight(SCREEN_HEIGHT / 5);
 }
 
@@ -172,8 +174,8 @@ void KsTraceViewer::setMarkerSM(KsDualMarkerSM *m)
 	QString styleSheetA, styleSheetB;
 
 	_mState = m;
-	_model.setColors(_mState->markerA()._color,
-			 _mState->markerB()._color);
+	_model.setMarkerColors(_mState->markerA()._color,
+			       _mState->markerB()._color);
 
 	/*
 	 * Assign a property to State A of the Dual marker state machine. When
@@ -234,6 +236,7 @@ void KsTraceViewer::update(KsDataStore *data)
 	_data = data;
 	if (_mState->activeMarker()._isSet)
 		showRow(_mState->activeMarker()._pos, true);
+	_resizeToContents();
 }
 
 void KsTraceViewer::_onCustomContextMenu(const QPoint &point)
@@ -246,7 +249,7 @@ void KsTraceViewer::_onCustomContextMenu(const QPoint &point)
 		 * of the row number in the source model.
 		 */
 		size_t row = _proxyModel.mapRowFromSource(i.row());
-		KsQuickContextMenu menu(_data, row, _mState, this);
+		KsQuickContextMenu menu(_mState, _data, row, this);
 
 		/*
 		 * Note that this slot was connected to the
@@ -290,7 +293,7 @@ void KsTraceViewer::_graphFollowsChanged(int state)
 
 	_graphFollows = (bool) state;
 	if (_graphFollows && row != KS_NO_ROW_SELECTED)
-		emit select(row); // Send a signal to the Graph widget.
+		emit select(*_it); // Send a signal to the Graph widget.
 }
 
 void KsTraceViewer::_search()
@@ -459,7 +462,7 @@ void KsTraceViewer::clearSelection()
 /** Switch the Dual marker. */
 void KsTraceViewer::markSwitch()
 {
-	int row;
+	ssize_t row;
 
 	/* The state of the Dual marker has changed. Get the new active marker. */
 	DualMarkerState state = _mState->getState();
@@ -490,7 +493,7 @@ void KsTraceViewer::markSwitch()
 		 * The index in the source model is used to retrieve the value
 		 * of the row number in the proxy model.
 		 */
-		size_t row =_mState->getMarker(state)._pos;
+		row =_mState->getMarker(state)._pos;
 
 		QModelIndex index =
 			_proxyModel.mapFromSource(_model.index(row, 0));
@@ -525,7 +528,7 @@ void KsTraceViewer::markSwitch()
  */
 void KsTraceViewer::resizeEvent(QResizeEvent* event)
 {
-	int nColumns = _tableHeader.count();
+	int nColumns = _model.header().count();
 	int tableSize(0), viewSize, freeSpace;
 
 	_resizeToContents();
@@ -564,7 +567,7 @@ void KsTraceViewer::keyReleaseEvent(QKeyEvent *event)
 
 void KsTraceViewer::_resizeToContents()
 {
-	int rows, columnSize, markRow = selectedRow();
+	int col, rows, columnSize, markRow = selectedRow();
 
 	_view.setVisible(false);
 	_view.resizeColumnsToContents();
@@ -579,13 +582,22 @@ void KsTraceViewer::_resizeToContents()
 		_view.clearSelection();
 
 	/*
-	 * Because of some unknown reason the first column doesn't get
+	 * Because of some unknown reason some of the columns doesn't get
 	 * resized properly by the code above. We will resize this
 	 * column by hand.
 	 */
+	col = KsViewModel::TRACE_VIEW_COL_STREAM;
+	columnSize = STRING_WIDTH(_model.header()[col]) + FONT_WIDTH;
+	_view.setColumnWidth(col, columnSize);
+
+	col = KsViewModel::TRACE_VIEW_COL_CPU;
+	columnSize = STRING_WIDTH(_model.header()[col]) + FONT_WIDTH * 2;
+	_view.setColumnWidth(col, columnSize);
+
+	col = KsViewModel::TRACE_VIEW_COL_INDEX;
 	rows = _model.rowCount({});
 	columnSize = STRING_WIDTH(QString("%1").arg(rows)) + FONT_WIDTH;
-	_view.setColumnWidth(0, columnSize);
+	_view.setColumnWidth(col, columnSize);
 }
 
 //! @cond Doxygen_Suppress
@@ -598,7 +610,16 @@ size_t KsTraceViewer::_searchItems()
 {
 	int column = _searchFSM._columnComboBox.currentIndex();
 	QString searchText = _searchFSM._searchLineEdit.text();
-	int count, dataRow;
+	int count, dataRow, columnIndex = column;
+
+	if (_model.singleStream()) {
+		/*
+		 * If only one Data stream (file) is loaded, the first column
+		 * (TRACE_VIEW_COL_STREAM) is not shown. The column index has
+		 * to be corrected.
+		 */
+		++columnIndex;
+	}
 
 	if (searchText.isEmpty()) {
 		/*
@@ -620,8 +641,8 @@ size_t KsTraceViewer::_searchItems()
 	} else {
 		_searchFSM.handleInput(sm_input_t::Start);
 
-		if (column == KsViewModel::TRACE_VIEW_COL_INFO ||
-		    column == KsViewModel::TRACE_VIEW_COL_LAT)
+		if (columnIndex == KsViewModel::TRACE_VIEW_COL_INFO ||
+		    columnIndex == KsViewModel::TRACE_VIEW_COL_AUX)
 			_searchItemsST();
 		else
 			_searchItemsMT();
@@ -761,7 +782,7 @@ void KsTraceViewer::_searchItemsMT()
 		};
 
 		for (int i = 0; i < mapList.size(); ++i)
-			if ( mapList[i].count()) {
+			if (mapList[i].count()) {
 				queue.push(std::make_pair(i, mapList[i].front()));
 				mapList[i].pop_front();
 			}
diff --git a/src/KsTraceViewer.hpp b/src/KsTraceViewer.hpp
index 6080d0d..f3979a6 100644
--- a/src/KsTraceViewer.hpp
+++ b/src/KsTraceViewer.hpp
@@ -20,6 +20,7 @@
 #include "KsModels.hpp"
 #include "KsSearchFSM.hpp"
 #include "KsDualMarker.hpp"
+#include "KsWidgetsLib.hpp"
 
 /**
  * Table View class, needed in order to reimplemented the handler for mouse
@@ -42,7 +43,7 @@ public:
  * The KsTraceViewer class provides a widget for browsing in the trace data
  * shown in a text form.
  */
-class KsTraceViewer : public QWidget
+class KsTraceViewer : public KsWidgetsLib::KsDataWidget
 {
 	Q_OBJECT
 public:
@@ -74,6 +75,12 @@ public:
 
 	void update(KsDataStore *data);
 
+	/** Update the color scheme used by the model. */
+	void loadColors()
+	{
+		_model.loadColors();
+	}
+
 signals:
 	/** Signal emitted when new row is selected. */
 	void select(size_t);
@@ -82,7 +89,7 @@ signals:
 	 * This signal is used to re-emitted the addTaskPlot signal of the
 	 * KsQuickContextMenu.
 	 */
-	void addTaskPlot(int pid);
+	void addTaskPlot(int sd, int pid);
 
 	/**
 	 * This signal is used to re-emitted the deselect signal of the
@@ -99,8 +106,6 @@ private:
 
 	KsFilterProxyModel	_proxyModel;
 
-	QStringList	_tableHeader;
-
 	QToolBar	_toolbar;
 
 	QLabel		_labelSearch, _labelGrFollows;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v2 19/27] kernel-shark: Update KsAdvFilteringDialog
  2021-02-11 10:31 [PATCH v2 00/27] Complete the KernelShark v2 transformation Yordan Karadzhov (VMware)
                   ` (17 preceding siblings ...)
  2021-02-11 10:31 ` [PATCH v2 18/27] kernel-shark: Update KsTraceViewer Yordan Karadzhov (VMware)
@ 2021-02-11 10:31 ` Yordan Karadzhov (VMware)
  2021-02-11 10:31 ` [PATCH v2 20/27] kernel-shark: Update KsCaptureDialog Yordan Karadzhov (VMware)
                   ` (8 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Yordan Karadzhov (VMware) @ 2021-02-11 10:31 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

The compilation ofKsAdvFilteringDialog .cpp is re-enabled and all
functionalities are made compatible with the new version of the
C API of libkshark (KernelShark 2.0).

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 src/CMakeLists.txt           |   8 +-
 src/KsAdvFilteringDialog.cpp | 200 ++++++++++++++++++++++-------------
 src/KsAdvFilteringDialog.hpp |  16 ++-
 3 files changed, 144 insertions(+), 80 deletions(-)

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index e09deb7..6bb94d6 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -76,8 +76,8 @@ if (Qt5Widgets_FOUND AND Qt5Network_FOUND AND TT_FONT_FILE)
                         KsTraceViewer.hpp
 #                         KsMainWindow.hpp
 #                         KsCaptureDialog.hpp
-                        KsQuickContextMenu.hpp)
-#                         KsAdvFilteringDialog.hpp)
+                        KsQuickContextMenu.hpp
+                        KsAdvFilteringDialog.hpp)
 
     QT5_WRAP_CPP(ks-guiLib_hdr_moc ${ks-guiLib_hdr})
 
@@ -92,8 +92,8 @@ if (Qt5Widgets_FOUND AND Qt5Network_FOUND AND TT_FONT_FILE)
                                                             KsTraceViewer.cpp
 #                                                             KsMainWindow.cpp
 #                                                             KsCaptureDialog.cpp
-                                                            KsQuickContextMenu.cpp)
-#                                                             KsAdvFilteringDialog.cpp)
+                                                            KsQuickContextMenu.cpp
+                                                            KsAdvFilteringDialog.cpp)
 
     target_link_libraries(kshark-gui kshark-plot
                                      Qt5::Widgets
diff --git a/src/KsAdvFilteringDialog.cpp b/src/KsAdvFilteringDialog.cpp
index 8dc53bb..2fdb624 100644
--- a/src/KsAdvFilteringDialog.cpp
+++ b/src/KsAdvFilteringDialog.cpp
@@ -9,10 +9,16 @@
  *  @brief   GUI Dialog for Advanced filtering settings.
  */
 
+// trace-cmd
+#include "trace-cmd/trace-cmd.h"
+
 // KernelShark
-#include "KsAdvFilteringDialog.hpp"
 #include "libkshark.h"
+#include "libkshark-tepdata.h"
 #include "KsUtils.hpp"
+#include "KsAdvFilteringDialog.hpp"
+
+using namespace KsWidgetsLib;
 
 /** Create dialog for Advanced Filtering. */
 KsAdvFilteringDialog::KsAdvFilteringDialog(QWidget *parent)
@@ -71,6 +77,9 @@ KsAdvFilteringDialog::KsAdvFilteringDialog(QWidget *parent)
 
 	lamAddLine();
 
+	_topLayout.addWidget(&_streamComboBox);
+	_getFtraceStreams(kshark_ctx);
+
 	_getFilters(kshark_ctx);
 
 	if (_filters.count()) {
@@ -149,23 +158,38 @@ KsAdvFilteringDialog::KsAdvFilteringDialog(QWidget *parent)
 		this,			&QWidget::close);
 }
 
-void KsAdvFilteringDialog::_setSystemCombo(struct kshark_context *kshark_ctx)
+kshark_data_stream *
+KsAdvFilteringDialog::_getCurrentStream(kshark_context *kshark_ctx)
 {
+	int sd = _streamComboBox.currentData().toInt();
+
+	return kshark_get_data_stream(kshark_ctx, sd);
+}
+
+void KsAdvFilteringDialog::_setSystemCombo(kshark_context *kshark_ctx)
+{
+	kshark_data_stream *stream;
+	QVector<int> eventIds;
 	QStringList sysList;
-	tep_event **events;
-	int i(0), nEvts(0);
+	int i(0);
 
-	if (kshark_ctx->pevent) {
-		nEvts = tep_get_events_count(kshark_ctx->pevent);
-		events = tep_list_events(kshark_ctx->pevent,
-					 TEP_EVENT_SORT_SYSTEM);
-	}
+	stream = _getCurrentStream(kshark_ctx);
+	if (!stream || !kshark_is_tep(stream))
+		return;
+
+	eventIds = KsUtils::getEventIdList(stream->stream_id);
+
+	auto lamGetSysName = [&stream] (int eventId) {
+		QStringList name = KsUtils::getTepEvtName(stream->stream_id,
+							  eventId);
+		return name[0];
+	};
 
-	while (i < nEvts) {
-		QString sysName(events[i]->system);
+	while (i < stream->n_events) {
+		QString sysName = lamGetSysName(eventIds[i]);
 		sysList << sysName;
-		while (sysName == events[i]->system) {
-			if (++i == nEvts)
+		while (sysName == lamGetSysName(eventIds[i])) {
+			if (++i == stream->n_events)
 				break;
 		}
 	}
@@ -202,24 +226,48 @@ QStringList KsAdvFilteringDialog::_operators()
 	return OpsList;
 }
 
-void KsAdvFilteringDialog::_getFilters(struct kshark_context *kshark_ctx)
+void KsAdvFilteringDialog::_getFtraceStreams(kshark_context *kshark_ctx)
 {
-	tep_event **events;
-	char *str;
+	kshark_data_stream *stream;
+	QVector<int> streamIds;
+
+	_streamComboBox.clear();
+	streamIds = KsUtils::getStreamIdList(kshark_ctx);
+	for (auto const &sd: streamIds) {
+		stream = kshark_ctx->stream[sd];
+		if (kshark_is_tep(stream))
+			_streamComboBox.addItem(KsUtils::streamDescription(stream), sd);
+	}
 
-	events = tep_list_events(kshark_ctx->pevent, TEP_EVENT_SORT_SYSTEM);
+	if (!_streamComboBox.count())
+		_streamComboBox.addItem("No FTRACE data loaded", -1);
+}
 
-	for (int i = 0; events[i]; i++) {
-		str = tep_filter_make_string(kshark_ctx->advanced_event_filter,
-					     events[i]->id);
-		if (!str)
+void KsAdvFilteringDialog::_getFilters(kshark_context *kshark_ctx)
+{
+	kshark_data_stream *stream;
+	QVector<int> eventIds;
+	QStringList eventName;
+	char *filterStr;
+
+	stream = _getCurrentStream(kshark_ctx);
+	if (!stream || !kshark_is_tep(stream))
+		return;
+
+	eventIds = KsUtils::getEventIdList(stream->stream_id);
+	for (int i = 0; i < stream->n_events; ++i) {
+		eventName = KsUtils::getTepEvtName(stream->stream_id, eventIds[i]);
+		filterStr = kshark_tep_filter_make_string(stream, eventIds[i]);
+		if (!filterStr)
 			continue;
 
-		_filters.insert(events[i]->id,
-				QString("%1/%2:%3").arg(events[i]->system,
-							  events[i]->name, str));
+		_filters.insert(eventIds[i],
+				QString("%1:%2/%3:%4").arg(QString::number(stream->stream_id),
+							   eventName[0],
+							   eventName[1],
+							   filterStr));
 
-		free(str);
+		free(filterStr);
 	}
 }
 
@@ -232,7 +280,7 @@ void KsAdvFilteringDialog::_makeFilterTable(struct kshark_context *kshark_ctx)
 
 	_table = new KsCheckBoxTable(this);
 	_table->setSelectionMode(QAbstractItemView::SingleSelection);
-	headers << "Delete" << "Event" << " Id" << "Filter";
+	headers << "Delete" << "Stream" << "Event" << " Id" << "Filter";
 	_table->init(headers, _filters.count());
 
 	for(auto f : _filters.keys()) {
@@ -241,11 +289,14 @@ void KsAdvFilteringDialog::_makeFilterTable(struct kshark_context *kshark_ctx)
 		i1 = new QTableWidgetItem(thisFilter[0]);
 		_table->setItem(count, 1, i1);
 
+		i1 = new QTableWidgetItem(thisFilter[1]);
+		_table->setItem(count, 2, i1);
+
 		i2 = new QTableWidgetItem(tr("%1").arg(f));
-		_table->setItem(count, 2, i2);
+		_table->setItem(count, 3, i2);
 
-		i3 = new QTableWidgetItem(thisFilter[1]);
-		_table->setItem(count, 3, i3);
+		i3 = new QTableWidgetItem(thisFilter[2]);
+		_table->setItem(count, 4, i3);
 
 		++count;
 	}
@@ -275,20 +326,25 @@ void KsAdvFilteringDialog::_help()
 void KsAdvFilteringDialog::_systemChanged(const QString &sysName)
 {
 	kshark_context *kshark_ctx(NULL);
-	QStringList evtsList;
-	tep_event **events;
-	int i, nEvts;
+	kshark_data_stream *stream;
+	QStringList evtsList, name;
+	QVector<int> eventIds;
+	int i;
 
-	_eventComboBox.clear();
-	if (!kshark_instance(&kshark_ctx) || !kshark_ctx->pevent)
+	if (!kshark_instance(&kshark_ctx))
 		return;
 
-	nEvts = tep_get_events_count(kshark_ctx->pevent);
-	events = tep_list_events(kshark_ctx->pevent, TEP_EVENT_SORT_SYSTEM);
+	_eventComboBox.clear();
+
+	stream = _getCurrentStream(kshark_ctx);
+	if (!stream || !kshark_is_tep(stream))
+		return;
 
-	for (i = 0; i < nEvts; ++i) {
-		if (sysName == events[i]->system)
-			evtsList << events[i]->name;
+	eventIds = KsUtils::getEventIdList(stream->stream_id);
+	for (i = 0; i < stream->n_events; ++i) {
+		name = KsUtils::getTepEvtName(stream->stream_id, eventIds[i]);
+		if (sysName == name[0])
+			evtsList << name[1];
 	}
 
 	std::sort(evtsList.begin(), evtsList.end());
@@ -300,15 +356,20 @@ void KsAdvFilteringDialog::_systemChanged(const QString &sysName)
 }
 
 QStringList
-KsAdvFilteringDialog::_getEventFormatFields(struct tep_event *event)
+KsAdvFilteringDialog::_getEventFields(int eventId)
 {
-	tep_format_field *field, **fields = tep_event_fields(event);
+	kshark_context *kshark_ctx(NULL);
+	kshark_data_stream *stream;
 	QStringList fieldList;
 
-	for (field = *fields; field; field = field->next)
-		fieldList << field->name;
+	if (!kshark_instance(&kshark_ctx))
+		return {};
+
+	stream = _getCurrentStream(kshark_ctx);
+	if (!stream || !kshark_is_tep(stream))
+		return {};
 
-	free(fields);
+	fieldList = KsUtils::getEventFieldsList(stream->stream_id, eventId);
 
 	std::sort(fieldList.begin(), fieldList.end());
 	return fieldList;
@@ -317,22 +378,24 @@ KsAdvFilteringDialog::_getEventFormatFields(struct tep_event *event)
 void KsAdvFilteringDialog::_eventChanged(const QString &evtName)
 {
 	QString sysName = _systemComboBox.currentText();
+	QStringList fieldList, eventName;
 	kshark_context *kshark_ctx(NULL);
-	QStringList fieldList;
-	tep_event **events;
-	int nEvts;
+	kshark_data_stream *stream;
+	QVector<int> eventIds;
 
 	_fieldComboBox.clear();
-	if (!kshark_instance(&kshark_ctx) || !kshark_ctx->pevent)
+	if (!kshark_instance(&kshark_ctx))
 		return;
 
-	nEvts = tep_get_events_count(kshark_ctx->pevent);
-	events = tep_list_events(kshark_ctx->pevent, TEP_EVENT_SORT_SYSTEM);
+	stream = _getCurrentStream(kshark_ctx);
+	if (!stream || !kshark_is_tep(stream))
+		return;
 
-	for (int i = 0; i < nEvts; ++i) {
-		if (evtName == events[i]->name &&
-		    sysName == events[i]->system) {
-			fieldList = _getEventFormatFields(events[i]);
+	eventIds = KsUtils::getEventIdList(stream->stream_id);
+	for (int i = 0; i < stream->n_events; ++i) {
+		eventName = KsUtils::getTepEvtName(stream->stream_id, eventIds[i]);
+		if (sysName == eventName[0] && evtName == eventName[1]) {
+			fieldList = _getEventFields(eventIds[i]);
 			_fieldComboBox.addItems(fieldList);
 
 			return;
@@ -384,20 +447,28 @@ void KsAdvFilteringDialog::_applyPress()
 {
 	QMapIterator<int, QString> f(_filters);
 	kshark_context *kshark_ctx(NULL);
+	kshark_data_stream *stream;
 	const char *text;
-	tep_errno ret;
 	char *filter;
 	int i(0);
 
 	if (!kshark_instance(&kshark_ctx))
 		return;
 
+	stream = _getCurrentStream(kshark_ctx);
+	if (!stream || !kshark_is_tep(stream))
+		return;
+
 	while (f.hasNext()) {
 		f.next();
 		if (_table->_cb[i]->checkState() == Qt::Checked) {
-			tep_filter_remove_event(kshark_ctx->advanced_event_filter,
-						f.key());
+			kshark_data_stream *filter_stream;
+			int sd = f.value().split(":").at(0).toInt();
+
+			filter_stream = kshark_get_data_stream(kshark_ctx, sd);
+			kshark_tep_filter_remove_event(filter_stream, f.key());
 		}
+
 		++i;
 	}
 
@@ -419,20 +490,7 @@ void KsAdvFilteringDialog::_applyPress()
 	filter = (char*) malloc(strlen(text) + 1);
 	strcpy(filter, text);
 
-	ret = tep_filter_add_filter_str(kshark_ctx->advanced_event_filter,
-					   filter);
-
-	if (ret < 0) {
-		char error_str[200];
-
-		tep_strerror(kshark_ctx->pevent, ret, error_str,
-						       sizeof(error_str));
-
-		fprintf(stderr, "filter failed due to: %s\n", error_str);
-		free(filter);
-
-		return;
-	}
+	kshark_tep_add_filter_str(stream, filter);
 
 	free(filter);
 
diff --git a/src/KsAdvFilteringDialog.hpp b/src/KsAdvFilteringDialog.hpp
index 2a534d0..a1db90b 100644
--- a/src/KsAdvFilteringDialog.hpp
+++ b/src/KsAdvFilteringDialog.hpp
@@ -36,7 +36,7 @@ private:
 
 	QMap<int, QString>	_filters;
 
-	KsCheckBoxTable		*_table;
+	KsWidgetsLib::KsCheckBoxTable	*_table;
 
 	QVBoxLayout	_topLayout;
 
@@ -46,6 +46,8 @@ private:
 
 	QLabel		_descrLabel, _sysEvLabel, _opsLabel, _fieldLabel;
 
+	QComboBox	_streamComboBox;
+
 	QComboBox	_systemComboBox, _eventComboBox;
 
 	QComboBox	_opsComboBox, _fieldComboBox;
@@ -74,13 +76,17 @@ private:
 
 	QStringList _operators();
 
-	void _getFilters(struct kshark_context *kshark_ctx);
+	void _getFtraceStreams(kshark_context *kshark_ctx);
+
+	void _getFilters(kshark_context *kshark_ctx);
+
+	void _makeFilterTable(kshark_context *kshark_ctx);
 
-	void _makeFilterTable(struct kshark_context *kshark_ctx);
+	QStringList _getEventFields(int eventId);
 
-	QStringList _getEventFormatFields(struct tep_event *event);
+	void _setSystemCombo(kshark_context *kshark_ctx);
 
-	void _setSystemCombo(struct kshark_context *kshark_ctx);
+	kshark_data_stream *_getCurrentStream(kshark_context *kshark_ctx);
 
 private slots:
 	void _systemChanged(const QString&);
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v2 20/27] kernel-shark: Update KsCaptureDialog
  2021-02-11 10:31 [PATCH v2 00/27] Complete the KernelShark v2 transformation Yordan Karadzhov (VMware)
                   ` (18 preceding siblings ...)
  2021-02-11 10:31 ` [PATCH v2 19/27] kernel-shark: Update KsAdvFilteringDialog Yordan Karadzhov (VMware)
@ 2021-02-11 10:31 ` Yordan Karadzhov (VMware)
  2021-02-11 10:31 ` [PATCH v2 21/27] kernel-shark: Update KsSession Yordan Karadzhov (VMware)
                   ` (7 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Yordan Karadzhov (VMware) @ 2021-02-11 10:31 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

The compilation of KsCaptureDialog.cpp and kshark-record.cpp is
re-enabled and all functionalities are made compatible with the new
version of the C API of libkshark (KernelShark 2.0).

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 src/CMakeLists.txt      | 10 ++---
 src/KsCaptureDialog.cpp | 90 ++++++++++++++++++++++-------------------
 src/KsCaptureDialog.hpp |  4 +-
 3 files changed, 56 insertions(+), 48 deletions(-)

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 6bb94d6..28f44fe 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -75,7 +75,7 @@ if (Qt5Widgets_FOUND AND Qt5Network_FOUND AND TT_FONT_FILE)
                         KsTraceGraph.hpp
                         KsTraceViewer.hpp
 #                         KsMainWindow.hpp
-#                         KsCaptureDialog.hpp
+                        KsCaptureDialog.hpp
                         KsQuickContextMenu.hpp
                         KsAdvFilteringDialog.hpp)
 
@@ -91,7 +91,7 @@ if (Qt5Widgets_FOUND AND Qt5Network_FOUND AND TT_FONT_FILE)
                                                             KsTraceGraph.cpp
                                                             KsTraceViewer.cpp
 #                                                             KsMainWindow.cpp
-#                                                             KsCaptureDialog.cpp
+                                                            KsCaptureDialog.cpp
                                                             KsQuickContextMenu.cpp
                                                             KsAdvFilteringDialog.cpp)
 
@@ -106,9 +106,9 @@ if (Qt5Widgets_FOUND AND Qt5Network_FOUND AND TT_FONT_FILE)
 #     add_executable(${KS_APP_NAME}          kernelshark.cpp)
 #     target_link_libraries(${KS_APP_NAME}   kshark-gui)
 
-#     message(STATUS "kshark-record")
-#     add_executable(kshark-record        kshark-record.cpp)
-#     target_link_libraries(kshark-record kshark-gui)
+    message(STATUS "kshark-record")
+    add_executable(kshark-record        kshark-record.cpp)
+    target_link_libraries(kshark-record kshark-gui)
 
 #     install(TARGETS ${KS_APP_NAME} kshark-record kshark-gui
 #             RUNTIME DESTINATION       ${_INSTALL_PREFIX}/bin/
diff --git a/src/KsCaptureDialog.cpp b/src/KsCaptureDialog.cpp
index 7253ab6..ca4c2bc 100644
--- a/src/KsCaptureDialog.cpp
+++ b/src/KsCaptureDialog.cpp
@@ -14,6 +14,7 @@
 
 // KernelShark
 #include "libkshark.h"
+#include "libkshark-tepdata.h"
 #include "KsUtils.hpp"
 #include "KsCmakeDef.hpp"
 #include "KsCaptureDialog.hpp"
@@ -24,11 +25,6 @@ extern "C" {
   #include <sys/types.h>
 }
 
-static inline tep_handle *local_events()
-{
-	return tracefs_local_events(tracefs_tracing_dir());
-}
-
 /**
  * @brief Create KsCommandLineEdit.
  *
@@ -48,11 +44,28 @@ QSize KsCommandLineEdit::sizeHint() const
 	return {FONT_WIDTH * 30, FONT_HEIGHT * 3};
 }
 
+static kshark_data_stream *_initLocalStream()
+{
+	kshark_context *kshark_ctx(nullptr);
+	kshark_data_stream *stream;
+	int sd;
+
+	if (!kshark_instance(&kshark_ctx))
+		return nullptr;
+
+	sd = kshark_add_stream(kshark_ctx);
+	stream = kshark_ctx->stream[sd];
+
+	kshark_tep_init_local(stream);
+
+	return stream;
+}
+
 /** @brief Create KsCaptureControl widget. */
 KsCaptureControl::KsCaptureControl(QWidget *parent)
 : QWidget(parent),
-  _localTEP(local_events()),
-  _eventsWidget(_localTEP, this),
+  _stream(_initLocalStream()),
+  _eventsWidget(_stream),
   _pluginsLabel("Plugin: ", this),
   _outputLabel("Output file: ", this),
   _commandLabel("Command: ", this),
@@ -80,7 +93,7 @@ KsCaptureControl::KsCaptureControl(QWidget *parent)
 		_topLayout.addWidget(line);
 	};
 
-	if (pluginList.count() == 0 || !_localTEP) {
+	if (pluginList.count() == 0 || !kshark_get_tep(_stream)) {
 		/*
 		 * No plugins or events have been found. Most likely this is
 		 * because the process has no Root privileges or because
@@ -88,7 +101,7 @@ KsCaptureControl::KsCaptureControl(QWidget *parent)
 		 */
 		QString message("Error: No events or plugins found.\n");
 
-		if (!_localTEP)
+		if (!kshark_get_tep(_stream))
 			message += "Cannot find or mount tracing directory.\n";
 
 		// geteuid() returns 0 if running as effective id of root
@@ -202,23 +215,19 @@ QStringList KsCaptureControl::getArgs()
 QStringList KsCaptureControl::_getPlugins()
 {
 	QStringList pluginList;
-	char **all_plugins;
+	char **all_tracers;
 
-	all_plugins = tracefs_tracers(tracefs_tracing_dir());
+	all_tracers = kshark_tracecmd_local_plugins();
 
-	if (!all_plugins)
+	if (!all_tracers)
 		return pluginList;
 
-	for (int i = 0; all_plugins[i]; ++i) {
-		/*
-		 * TODO plugin selection here.
-		 * printf("plugin %i %s\n", i, all_plugins[i]);
-		 */
-		pluginList << all_plugins[i];
-		free(all_plugins[i]);
+	for (int i = 0; all_tracers[i]; ++i) {
+		pluginList << all_tracers[i];
+		free(all_tracers[i]);
 	}
 
-	free (all_plugins);
+	free(all_tracers);
 	std::sort(pluginList.begin(), pluginList.end());
 
 	return pluginList;
@@ -226,20 +235,17 @@ QStringList KsCaptureControl::_getPlugins()
 
 void KsCaptureControl::_importSettings()
 {
-	int nEvts = tep_get_events_count(_localTEP), nIds;
+	QVector<int> event_ids = _eventsWidget.getIds();
+	QVector<int> status(_stream->n_events, 0);
 	kshark_config_doc *conf, *jevents, *temp;
-	QVector<bool> v(nEvts, false);
-	tracecmd_filter_id *eventHash;
-	QVector<int> eventIds;
+	kshark_hash_id *eventHash;
 	QString fileName;
+	int nIds;
 
 	auto lamImportError = [this] () {
 		emit print("ERROR: Unable to load the configuration file.\n");
 	};
 
-	/** Get all available events. */
-	eventIds = KsUtils::getEventIdList(TEP_EVENT_SORT_SYSTEM);
-
 	/* Get the configuration document. */
 	fileName = KsUtils::getFile(this, "Import from Filter",
 				    "Kernel Shark Config files (*.json);;",
@@ -267,8 +273,9 @@ void KsCaptureControl::_importSettings()
 		return;
 	}
 
-	eventHash = tracecmd_filter_id_hash_alloc();
-	nIds = kshark_import_event_filter(_localTEP, eventHash, "Events", jevents);
+	eventHash = kshark_get_filter(_stream, KS_SHOW_EVENT_FILTER);
+	nIds = kshark_import_event_filter(_stream, KS_SHOW_EVENT_FILTER,
+					  "Events", jevents);
 	if (nIds < 0) {
 		QString err("WARNING: ");
 		err += "Some of the imported events are not available on this system.\n";
@@ -276,13 +283,12 @@ void KsCaptureControl::_importSettings()
 		emit print(err);
 	}
 
-	for (int i = 0; i < nEvts; ++i) {
-		if (tracecmd_filter_id_find(eventHash, eventIds[i]))
-			v[i] = true;
+	for (int i = 0; i < _stream->n_events; ++i) {
+		if (kshark_hash_id_find(eventHash, event_ids[i]))
+			status[i] = true;
 	}
 
-	_eventsWidget.set(v);
-	tracecmd_filter_id_hash_free(eventHash);
+	_eventsWidget.set(status);
 
 	/** Get all available plugins. */
 	temp = kshark_string_config_alloc();
@@ -294,9 +300,9 @@ void KsCaptureControl::_importSettings()
 		if (pluginIndex >= 0) {
 			_pluginsComboBox.setCurrentText(KS_C_STR_CAST(temp->conf_doc));
 		} else {
-			QString err("WARNING: The traceer plugin \"");
+			QString err("WARNING: The tracer plugin \"");
 			err += plugin;
-			err += "\" is not available on this machine\n";
+			err += "\" is not available on this machine!\n";
 			emit print(err);
 		}
 	}
@@ -329,18 +335,20 @@ void KsCaptureControl::_exportSettings()
 	events = kshark_filter_config_new(KS_CONFIG_JSON);
 
 	/*
-	 * Use the tracecmd_filter_id to save all selected events in the
+	 * Use the kshark_hash_id to save all selected events in the
 	 * configuration file.
 	 */
 	ids = _eventsWidget.getCheckedIds();
-	tracecmd_filter_id *eventHash = tracecmd_filter_id_hash_alloc();
+	kshark_hash_id *eventHash = kshark_get_filter(_stream,
+						      KS_SHOW_EVENT_FILTER);
+
 	for (auto const &id: ids)
-		tracecmd_filter_id_add(eventHash, id);
+		kshark_hash_id_add(eventHash, id);
 
-	kshark_export_event_filter(_localTEP, eventHash, "Events", events);
+	kshark_export_event_filter(_stream, KS_SHOW_EVENT_FILTER, "Events", events);
 	kshark_config_doc_add(conf, "Events", events);
 
-	tracecmd_filter_id_hash_free(eventHash);
+	kshark_hash_id_free(eventHash);
 
 	/* Save the plugin. */
 	plugin = _pluginsComboBox.currentText();
diff --git a/src/KsCaptureDialog.hpp b/src/KsCaptureDialog.hpp
index 3fd3d8d..0c85101 100644
--- a/src/KsCaptureDialog.hpp
+++ b/src/KsCaptureDialog.hpp
@@ -61,9 +61,9 @@ signals:
 	void print(const QString &message);
 
 private:
-	tep_handle		*_localTEP;
+	kshark_data_stream	*_stream;
 
-	KsEventsCheckBoxWidget	_eventsWidget;
+	KsWidgetsLib::KsEventsCheckBoxWidget	_eventsWidget;
 
 	QVBoxLayout	_topLayout;
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v2 21/27] kernel-shark: Update KsSession
  2021-02-11 10:31 [PATCH v2 00/27] Complete the KernelShark v2 transformation Yordan Karadzhov (VMware)
                   ` (19 preceding siblings ...)
  2021-02-11 10:31 ` [PATCH v2 20/27] kernel-shark: Update KsCaptureDialog Yordan Karadzhov (VMware)
@ 2021-02-11 10:31 ` Yordan Karadzhov (VMware)
  2021-02-11 10:32 ` [PATCH v2 22/27] kernel-shark: Update MissedEvents plugin Yordan Karadzhov (VMware)
                   ` (6 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Yordan Karadzhov (VMware) @ 2021-02-11 10:31 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

The compilation KsSession.cpp is re-enabled and all functionalities
are made compatible with the new version of the C API of libkshark
(KernelShark 2.0).

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 src/CMakeLists.txt |   2 +-
 src/KsSession.cpp  | 415 +++++++++++++++++++++++++++------------------
 src/KsSession.hpp  |  37 ++--
 3 files changed, 269 insertions(+), 185 deletions(-)

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 28f44fe..5ea34ed 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -83,7 +83,7 @@ if (Qt5Widgets_FOUND AND Qt5Network_FOUND AND TT_FONT_FILE)
 
     add_library(kshark-gui  SHARED  ${ks-guiLib_hdr_moc}    KsUtils.cpp
                                                             KsModels.cpp
-#                                                             KsSession.cpp
+                                                            KsSession.cpp
                                                             KsGLWidget.cpp
                                                             KsSearchFSM.cpp
                                                             KsDualMarker.cpp
diff --git a/src/KsSession.cpp b/src/KsSession.cpp
index a581bbf..786aa3e 100644
--- a/src/KsSession.cpp
+++ b/src/KsSession.cpp
@@ -11,14 +11,13 @@
 
 // KernelShark
 #include "libkshark.h"
+#include "libkshark-tepdata.h"
 #include "KsSession.hpp"
-#include "KsMainWindow.hpp"
 
 /** Create a KsSession object. */
 KsSession::KsSession()
 {
-	_config = kshark_config_new("kshark.config.session",
-				      KS_CONFIG_JSON);
+	_config = kshark_session_config_new(KS_CONFIG_JSON);
 }
 
 /** Destroy a KsSession object. */
@@ -79,10 +78,11 @@ void KsSession::loadVisModel(KsGraphModel *model)
 }
 
 /** Save the trace data file. */
-void KsSession::saveDataFile(QString fileName)
+void KsSession::saveDataFile(QString fileName, QString dataSetName)
 {
 	kshark_config_doc *file =
 		kshark_export_trace_file(fileName.toStdString().c_str(),
+					 dataSetName.toStdString().c_str(),
 					 KS_CONFIG_JSON);
 
 	kshark_config_doc_add(_config, "Data", file);
@@ -92,54 +92,51 @@ void KsSession::saveDataFile(QString fileName)
 QString KsSession::getDataFile(kshark_context *kshark_ctx)
 {
 	kshark_config_doc *file = kshark_config_alloc(KS_CONFIG_JSON);
-	const char *file_str;
+	int sd;
 
 	if (!kshark_config_doc_get(_config, "Data", file))
 		return QString();
 
-	file_str = kshark_import_trace_file(kshark_ctx, file);
-	if (file_str)
-		return QString(file_str);
+	sd = kshark_import_trace_file(kshark_ctx, file);
+	if (sd)
+		return QString(kshark_ctx->stream[sd]->file);
 
 	return QString();
 }
 
 /**
- * @brief Save the configuration of the filters.
+ * @brief Save the configuration information for all load Data streams.
  *
  * @param kshark_ctx: Input location for context pointer.
  */
-void KsSession::saveFilters(kshark_context *kshark_ctx)
+void KsSession::saveDataStreams(kshark_context *kshark_ctx)
 {
-	kshark_config_doc *filters =
-		kshark_export_all_filters(kshark_ctx, KS_CONFIG_JSON);
-
-	kshark_config_doc_add(_config, "Filters", filters);
+	kshark_export_all_dstreams(kshark_ctx, &_config);
 }
 
 /**
- * @brief Load the configuration of the filters and filter the data.
+ * @brief Load Data streams.
  *
  * @param kshark_ctx: Input location for context pointer.
- * @param data: Input location for KsDataStore object;
+ * @param data:  Input location for KsDataStore object;
  */
-void KsSession::loadFilters(kshark_context *kshark_ctx, KsDataStore *data)
+void KsSession::loadDataStreams(kshark_context *kshark_ctx,
+				KsDataStore *data)
 {
-	kshark_config_doc *filters = kshark_config_alloc(KS_CONFIG_JSON);
-
-	if (!kshark_config_doc_get(_config, "Filters", filters))
-		return;
+	ssize_t dataSize;
 
-	kshark_import_all_filters(kshark_ctx, filters);
+	data->unregisterCPUCollections();
 
-	if (kshark_ctx->advanced_event_filter->filters)
-		data->reload();
-	else
-		kshark_filter_entries(kshark_ctx, data->rows(), data->size());
+	dataSize = kshark_import_all_dstreams(kshark_ctx,
+					      _config,
+					      data->rows_r());
+	if (dataSize < 0) {
+		data->clear();
+		return;
+	}
 
+	data->setSize(dataSize);
 	data->registerCPUCollections();
-
-	emit data->updateWidgets(data);
 }
 
 /**
@@ -152,7 +149,7 @@ void KsSession::saveTable(const KsTraceViewer &view) {
 	int64_t r = view.getTopRow();
 
 	topRow->conf_doc = json_object_new_int64(r);
-	kshark_config_doc_add(_config, "ViewTop",topRow);
+	kshark_config_doc_add(_config, "ViewTop", topRow);
 }
 
 /**
@@ -196,39 +193,6 @@ void KsSession::saveMainWindowSize(const QMainWindow &window)
 	kshark_config_doc_add(_config, "MainWindow", windowConf);
 }
 
-/**
- * @brief Load the KernelShark Main window size.
- *
- * @param window: Input location for the KsMainWindow widget.
- */
-void KsSession::loadMainWindowSize(KsMainWindow *window)
-{
-	kshark_config_doc *windowConf = kshark_config_alloc(KS_CONFIG_JSON);
-	json_object *jwindow, *jwidth, *jheight;
-	int width, height;
-
-	if (!kshark_config_doc_get(_config, "MainWindow", windowConf))
-		return;
-
-	if (_config->format == KS_CONFIG_JSON) {
-		jwindow = KS_JSON_CAST(windowConf->conf_doc);
-		if (json_object_get_type(jwindow) == json_type_string &&
-		    QString(json_object_get_string(jwindow)) == "FullScreen") {
-			window->setFullScreenMode(true);
-			return;
-		}
-
-		jwidth = json_object_array_get_idx(jwindow, 0);
-		jheight = json_object_array_get_idx(jwindow, 1);
-
-		width = json_object_get_int(jwidth);
-		height = json_object_get_int(jheight);
-
-		window->setFullScreenMode(false);
-		window->resize(width, height);
-	}
-}
-
 /**
  * @brief Save the state of the Main window spliter.
  *
@@ -269,6 +233,7 @@ void KsSession::loadSplitterSize(QSplitter *splitter)
 
 		graphSize = json_object_get_int(jgraphsize);
 		viewSize = json_object_get_int(jviewsize);
+
 		if (graphSize == 0 && viewSize == 0) {
 			/* 0/0 spliter ratio is undefined. Make it 1/1. */
 			viewSize = graphSize = 1;
@@ -282,7 +247,7 @@ void KsSession::loadSplitterSize(QSplitter *splitter)
 /** @brief Save the Color scheme used. */
 void KsSession::saveColorScheme() {
 	kshark_config_doc *colSch = kshark_config_alloc(KS_CONFIG_JSON);
-	double s = KsPlot::Color::getRainbowFrequency();
+	double s = KsPlot::Color::rainbowFrequency();
 
 	colSch->conf_doc = json_object_new_double(s);
 	kshark_config_doc_add(_config, "ColorScheme", colSch);
@@ -307,97 +272,235 @@ float KsSession::getColorScheme() {
 /**
  * @brief Save the list of the graphs plotted.
  *
- * @param glw: Input location for the KsGLWidget widget.
+ * @param kshark_ctx: Input location for context pointer.
+ * @param graphs: Input location for the KsTraceGraph widget..
  */
-void KsSession::saveGraphs(const KsGLWidget &glw)
+void KsSession::saveGraphs(kshark_context *kshark_ctx,
+			   KsTraceGraph &graphs)
 {
-	_saveCPUPlots(glw._cpuList);
-	_saveTaskPlots(glw._taskList);
+	QVector<int> streamIds = KsUtils::getStreamIdList(kshark_ctx);
+	for (auto const &sd: streamIds) {
+		_saveCPUPlots(sd, graphs.glPtr());
+		_saveTaskPlots(sd, graphs.glPtr());
+	}
+
+	_saveComboPlots(graphs.glPtr());
 }
 
 /**
  * @brief Load the list of the graphs and plot.
  *
+ * @param kshark_ctx: Input location for context pointer.
  * @param graphs: Input location for the KsTraceGraph widget.
  */
-void KsSession::loadGraphs(KsTraceGraph *graphs)
+void KsSession::loadGraphs(kshark_context *kshark_ctx,
+			   KsTraceGraph &graphs)
 {
-	graphs->cpuReDraw(_getCPUPlots());
-	graphs->taskReDraw(_getTaskPlots());
+	QVector<int> combos, streamIds;
+	int nCombos;
+
+	streamIds = KsUtils::getStreamIdList(kshark_ctx);
+	for (auto const &sd: streamIds) {
+		graphs.cpuReDraw(sd, _getCPUPlots(sd));
+		graphs.taskReDraw(sd, _getTaskPlots(sd));
+	}
+
+	combos = _getComboPlots(&nCombos);
+	if (nCombos > 0)
+		graphs.comboReDraw(nCombos, combos);
 }
 
-void KsSession::_saveCPUPlots(const QVector<int> &cpus)
+void KsSession::_savePlots(int sd, KsGLWidget *glw, bool cpu)
 {
-	kshark_config_doc *cpuPlts = kshark_config_alloc(KS_CONFIG_JSON);
-	json_object *jcpus = json_object_new_array();
+	kshark_config_doc *streamsConf = kshark_config_alloc(KS_CONFIG_JSON);
+	json_object *jallStreams, *jstream, *jstreamId, *jplots;
+	QVector<int> plotIds;
+	int nStreams;
 
-	for (int i = 0; i < cpus.count(); ++i) {
-		json_object *jcpu = json_object_new_int(cpus[i]);
-		json_object_array_put_idx(jcpus, i, jcpu);
+	if (cpu) {
+		plotIds = glw->_streamPlots[sd]._cpuList;
+	} else {
+		plotIds = glw->_streamPlots[sd]._taskList;
 	}
 
-	cpuPlts->conf_doc = jcpus;
-	kshark_config_doc_add(_config, "CPUPlots", cpuPlts);
+	if (!kshark_config_doc_get(_config, "data streams", streamsConf) ||
+	    streamsConf->format != KS_CONFIG_JSON)
+		return;
+
+	jallStreams = KS_JSON_CAST(streamsConf->conf_doc);
+	if (json_object_get_type(jallStreams) != json_type_array)
+		return;
+
+	nStreams = json_object_array_length(jallStreams);
+	for (int i = 0; i < nStreams; ++i) {
+		jstream = json_object_array_get_idx(jallStreams, i);
+		if (json_object_object_get_ex(jstream, "stream id", &jstreamId) &&
+		    json_object_get_int(jstreamId) == sd)
+			break;
+
+		jstream = nullptr;
+	}
+
+	free(streamsConf);
+	if (!jstream)
+		return;
+
+	jplots = json_object_new_array();
+	for (int i = 0; i < plotIds.count(); ++i) {
+		json_object *jcpu = json_object_new_int(plotIds[i]);
+		json_object_array_put_idx(jplots, i, jcpu);
+	}
+
+	if (cpu)
+		json_object_object_add(jstream, "CPUPlots", jplots);
+	else
+		json_object_object_add(jstream, "TaskPlots", jplots);
 }
 
-QVector<int> KsSession::_getCPUPlots()
+void KsSession::_saveCPUPlots(int sd, KsGLWidget *glw)
 {
-	kshark_config_doc *cpuPlts = kshark_config_alloc(KS_CONFIG_JSON);
-	json_object *jcpus;
-	QVector<int> cpus;
-	size_t length;
+	_savePlots(sd, glw, true);
+}
 
-	if (!kshark_config_doc_get(_config, "CPUPlots", cpuPlts))
-		return cpus;
+void KsSession::_saveTaskPlots(int sd, KsGLWidget *glw)
+{
+	_savePlots(sd, glw, false);
+}
 
-	if (_config->format == KS_CONFIG_JSON) {
-		jcpus = KS_JSON_CAST(cpuPlts->conf_doc);
-		length = json_object_array_length(jcpus);
-		for (size_t i = 0; i < length; ++i) {
-			int cpu = json_object_get_int(json_object_array_get_idx(jcpus,
-										i));
-			cpus.append(cpu);
+void KsSession::_saveComboPlots(KsGLWidget *glw)
+{
+	kshark_config_doc *combos = kshark_config_alloc(KS_CONFIG_JSON);
+	json_object *jcombos, *jplots, *jplt;
+	int var;
+
+	jcombos = json_object_new_array();
+	for (auto const &c: glw->_comboPlots) {
+		jplots = json_object_new_array();
+		for (auto const &p: c) {
+			jplt = json_object_new_array();
+			var = p._streamId;
+			json_object_array_put_idx(jplt, 0,
+						  json_object_new_int(var));
+
+			var = p._type;
+			json_object_array_put_idx(jplt, 1,
+						  json_object_new_int(var));
+
+			var = p._id;
+			json_object_array_put_idx(jplt, 2,
+						  json_object_new_int(var));
+
+			json_object_array_add(jplots, jplt);
 		}
+
+		json_object_array_add(jcombos, jplots);
 	}
 
-	return cpus;
+	combos->conf_doc = jcombos;
+	kshark_config_doc_add(_config, "ComboPlots", combos);
 }
 
-void KsSession::_saveTaskPlots(const QVector<int> &tasks)
+QVector<int> KsSession::_getPlots(int sd, bool cpu)
 {
-	kshark_config_doc *taskPlts = kshark_config_alloc(KS_CONFIG_JSON);
-	json_object *jtasks = json_object_new_array();
+	kshark_config_doc *streamsConf = kshark_config_alloc(KS_CONFIG_JSON);
+	json_object *jallStreams, *jstream, *jstreamId, *jplots;
+	int nStreams, nPlots, id;
+	const char *plotKey;
+	QVector<int> plots;
+
+	if (!kshark_config_doc_get(_config, "data streams", streamsConf) ||
+	    streamsConf->format != KS_CONFIG_JSON)
+		return plots;
+
+	jallStreams = KS_JSON_CAST(streamsConf->conf_doc);
+	if (json_object_get_type(jallStreams) != json_type_array)
+		return plots;
+
+	nStreams = json_object_array_length(jallStreams);
+	for (int i = 0; i < nStreams; ++i) {
+		jstream = json_object_array_get_idx(jallStreams, i);
+		if (json_object_object_get_ex(jstream, "stream id", &jstreamId) &&
+		    json_object_get_int(jstreamId) == sd)
+			break;
+
+		jstream = nullptr;
+	}
+
+	if (!jstream)
+		return plots;
+
+	if (cpu)
+		plotKey = "CPUPlots";
+	else
+		plotKey = "TaskPlots";
 
-	for (int i = 0; i < tasks.count(); ++i) {
-		json_object *jtask = json_object_new_int(tasks[i]);
-		json_object_array_put_idx(jtasks, i, jtask);
+	if (!json_object_object_get_ex(jstream, plotKey, &jplots) ||
+	    json_object_get_type(jplots) != json_type_array)
+		return plots;
+
+	nPlots = json_object_array_length(jplots);
+	for (int i = 0; i < nPlots; ++i) {
+		id = json_object_get_int(json_object_array_get_idx(jplots, i));
+		plots.append(id);
 	}
 
-	taskPlts->conf_doc = jtasks;
-	kshark_config_doc_add(_config, "TaskPlots", taskPlts);
+	return plots;
+}
+
+QVector<int> KsSession::_getCPUPlots(int sd)
+{
+	return _getPlots(sd, true);
+}
+
+QVector<int> KsSession::_getTaskPlots(int sd)
+{
+	return _getPlots(sd, false);
 }
 
-QVector<int> KsSession::_getTaskPlots()
+QVector<int> KsSession::_getComboPlots(int *n)
 {
-	kshark_config_doc *taskPlts = kshark_config_alloc(KS_CONFIG_JSON);
-	json_object *jtasks;
-	QVector<int> tasks;
-	size_t length;
+	kshark_config_doc *combos = kshark_config_alloc(KS_CONFIG_JSON);
+	int nCombos, nPlots, sd, type, id;
+	json_object *jcombos, *jplots, *jplt, *jvar;
+	QVector<int> vec;
 
-	if (!kshark_config_doc_get(_config, "TaskPlots", taskPlts))
-		return tasks;
+	*n = 0;
+	if (!kshark_config_doc_get(_config, "ComboPlots", combos))
+		return {};
 
 	if (_config->format == KS_CONFIG_JSON) {
-		jtasks = KS_JSON_CAST(taskPlts->conf_doc);
-		length = json_object_array_length(jtasks);
-		for (size_t i = 0; i < length; ++i) {
-			int pid = json_object_get_int(json_object_array_get_idx(jtasks,
-										i));
-			tasks.append(pid);
+		jcombos = KS_JSON_CAST(combos->conf_doc);
+		if (json_object_get_type(jcombos) != json_type_array)
+			return {};
+
+		*n = nCombos = json_object_array_length(jcombos);
+		for (int i = 0; i < nCombos; ++i) {
+			jplots = json_object_array_get_idx(jcombos, i);
+			if (json_object_get_type(jplots) != json_type_array)
+				return {};
+
+			nPlots =  json_object_array_length(jplots);
+			vec.append(nPlots);
+			for (int j = 0; j < nPlots; ++j) {
+				jplt = json_object_array_get_idx(jplots, j);
+				if (json_object_get_type(jplt) != json_type_array)
+					return {};
+
+				jvar = json_object_array_get_idx(jplt, 0);
+				sd = json_object_get_int(jvar);
+
+				jvar = json_object_array_get_idx(jplt, 1);
+				type = json_object_get_int(jvar);
+
+				jvar = json_object_array_get_idx(jplt, 2);
+				id = json_object_get_int(jvar);
+
+				vec.append({sd, type, id});
+			}
 		}
 	}
 
-	return tasks;
+	return vec;
 }
 
 /**
@@ -407,7 +510,7 @@ QVector<int> KsSession::_getTaskPlots()
  */
 void KsSession::saveDualMarker(KsDualMarkerSM *dm)
 {
-	struct kshark_config_doc *markers =
+	kshark_config_doc *markers =
 		kshark_config_new("kshark.config.markers", KS_CONFIG_JSON);
 	json_object *jd_mark = KS_JSON_CAST(markers->conf_doc);
 
@@ -450,7 +553,7 @@ void KsSession::saveDualMarker(KsDualMarkerSM *dm)
  */
 void KsSession::loadDualMarker(KsDualMarkerSM *dm, KsTraceGraph *graphs)
 {
-	size_t pos;
+	uint64_t pos;
 
 	dm->reset();
 	dm->setState(DualMarkerState::A);
@@ -476,7 +579,7 @@ void KsSession::loadDualMarker(KsDualMarkerSM *dm, KsTraceGraph *graphs)
 
 json_object *KsSession::_getMarkerJson()
 {
-	struct kshark_config_doc *markers =
+	kshark_config_doc *markers =
 		kshark_config_alloc(KS_CONFIG_JSON);
 
 	if (!kshark_config_doc_get(_config, "Markers", markers) ||
@@ -529,33 +632,23 @@ DualMarkerState KsSession::_getMarkerState()
  *
  * @param pm: Input location for the KsPluginManager object.
  */
-void KsSession::savePlugins(const KsPluginManager &pm)
+void KsSession::saveUserPlugins(const KsPluginManager &pm)
 {
-	struct kshark_config_doc *plugins =
+	kshark_config_doc *plugins =
 		kshark_config_new("kshark.config.plugins", KS_CONFIG_JSON);
 	json_object *jplugins = KS_JSON_CAST(plugins->conf_doc);
-	const QVector<bool> &registeredPlugins = pm._registeredKsPlugins;
-	const QStringList &pluginList = pm._ksPluginList;
-	int nPlugins = pluginList.length();
-	json_object *jlist, *jpl;
-	QByteArray array;
-	char* buffer;
-	bool active;
-
-	jlist = json_object_new_array();
-	for (int i = 0; i < nPlugins; ++i) {
-		array = pluginList[i].toLocal8Bit();
-		buffer = array.data();
-		jpl = json_object_new_array();
-		json_object_array_put_idx(jpl, 0, json_object_new_string(buffer));
-
-		active = registeredPlugins[i];
-		json_object_array_put_idx(jpl, 1, json_object_new_boolean(active));
-		json_object_array_put_idx(jlist, i, jpl);
+	json_object *jplg, *jlist = json_object_new_array();
+
+	for (auto const p: pm.getUserPlugins()) {
+		kshark_config_doc *lib =
+			kshark_export_plugin_file(p, KS_CONFIG_JSON);
+		jplg = KS_JSON_CAST(lib->conf_doc);
+		json_object_array_add(jlist, jplg);
+		free(lib);
 	}
 
-	json_object_object_add(jplugins, "Plugin List", jlist);
-	kshark_config_doc_add(_config, "Plugins", plugins);
+	json_object_object_add(jplugins, "obj. files", jlist);
+	kshark_config_doc_add(_config, "User Plugins", plugins);
 }
 
 /**
@@ -564,37 +657,21 @@ void KsSession::savePlugins(const KsPluginManager &pm)
  * @param kshark_ctx: Input location for context pointer.
  * @param pm: Input location for the KsPluginManager object.
  */
-void KsSession::loadPlugins(kshark_context *kshark_ctx, KsPluginManager *pm)
+void KsSession::loadUserPlugins(kshark_context *kshark_ctx, KsPluginManager *pm)
 {
-	kshark_config_doc *plugins = kshark_config_alloc(KS_CONFIG_JSON);
-	json_object *jplugins, *jlist, *jpl;
-	const char *pluginName;
-	QVector<int> pluginIds;
-	int length, index;
-	bool loaded;
-
-	if (!kshark_config_doc_get(_config, "Plugins", plugins) ||
-	    !kshark_type_check(plugins, "kshark.config.plugins"))
+	kshark_config_doc *plugins =
+		kshark_config_alloc(KS_CONFIG_JSON);
+	kshark_plugin_list *list, **last;
+
+	if (!kshark_config_doc_get(_config, "User Plugins", plugins))
 		return;
 
-	if (plugins->format == KS_CONFIG_JSON) {
-		jplugins = KS_JSON_CAST(plugins->conf_doc);
-		json_object_object_get_ex(jplugins, "Plugin List", &jlist);
-		if (!jlist ||
-	            json_object_get_type(jlist) != json_type_array ||
-		    !json_object_array_length(jlist))
-			return;
-
-		length = json_object_array_length(jlist);
-		for (int i = 0; i < length; ++i) {
-			jpl = json_object_array_get_idx(jlist, i);
-			pluginName = json_object_get_string(json_object_array_get_idx(jpl, 0));
-			index = pm->_ksPluginList.indexOf(pluginName);
-			loaded = json_object_get_boolean(json_object_array_get_idx(jpl, 1));
-			if (index >= 0 && loaded)
-				pluginIds.append(index);
-		}
-	}
+	/* Get the list of already loaded plugins. */
+	list = kshark_ctx->plugins;
+
+	kshark_import_all_plugins(kshark_ctx, plugins);
 
-	pm->updatePlugins(pluginIds);
+	/* Loop until the beginning of the old list. */
+	for (last = &kshark_ctx->plugins; *last != list; last = &(*last)->next)
+		pm->addUserPluginToList(*last);
 }
diff --git a/src/KsSession.hpp b/src/KsSession.hpp
index b07c810..e1b7fd4 100644
--- a/src/KsSession.hpp
+++ b/src/KsSession.hpp
@@ -20,8 +20,6 @@
 #include "KsTraceGraph.hpp"
 #include "KsTraceViewer.hpp"
 
-class KsMainWindow;
-
 /**
  * The KsSession class provides instruments for importing/exporting the state
  * of the different components of the GUI from/to Json documents. These
@@ -41,7 +39,7 @@ public:
 
 	void exportToFile(QString jfileName);
 
-	void saveDataFile(QString fileName);
+	void saveDataFile(QString fileName, QString dataSetName);
 
 	QString getDataFile(kshark_context *kshark_ctx);
 
@@ -49,18 +47,19 @@ public:
 
 	void loadVisModel(KsGraphModel *model);
 
-	void saveGraphs(const KsGLWidget &glw);
+	void saveGraphs(kshark_context *kshark_ctx,
+			KsTraceGraph &graphs);
 
-	void loadGraphs(KsTraceGraph *graphs);
+	void loadGraphs(kshark_context *kshark_ctx,
+			KsTraceGraph &graphs);
 
-	void saveFilters(kshark_context *kshark_ctx);
+	void saveDataStreams(kshark_context *kshark_ctx);
 
-	void loadFilters(kshark_context *kshark_ctx, KsDataStore *data);
+	void loadDataStreams(kshark_context *kshark_ctx,
+			     KsDataStore *data);
 
 	void saveMainWindowSize(const QMainWindow &window);
 
-	void loadMainWindowSize(KsMainWindow *window);
-
 	void saveSplitterSize(const QSplitter &splitter);
 
 	void loadSplitterSize(QSplitter *splitter);
@@ -69,9 +68,9 @@ public:
 
 	void loadDualMarker(KsDualMarkerSM *dmm, KsTraceGraph *graphs);
 
-	void savePlugins(const KsPluginManager &pm);
+	void saveUserPlugins(const KsPluginManager &pm);
 
-	void loadPlugins(kshark_context *kshark_ctx, KsPluginManager *pm);
+	void loadUserPlugins(kshark_context *kshark_ctx, KsPluginManager *pm);
 
 	void saveTable(const KsTraceViewer &view);
 
@@ -86,13 +85,21 @@ private:
 
 	json_object *_getMarkerJson();
 
-	void _saveCPUPlots(const QVector<int> &cpus);
+	void _savePlots(int sd, KsGLWidget *glw, bool cpu);
+
+	QVector<int> _getPlots(int sd, bool cpu);
+
+	void _saveCPUPlots(int sd, KsGLWidget *glw);
+
+	QVector<int> _getCPUPlots(int sd);
+
+	void _saveTaskPlots(int sd, KsGLWidget *glw);
 
-	QVector<int> _getCPUPlots();
+	QVector<int> _getTaskPlots(int sd);
 
-	void _saveTaskPlots(const QVector<int> &tasks);
+	void _saveComboPlots(KsGLWidget *glw);
 
-	QVector<int> _getTaskPlots();
+	QVector<int> _getComboPlots(int *nCombos);
 
 	bool _getMarker(const char* name, size_t *pos);
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v2 22/27] kernel-shark: Update MissedEvents plugin
  2021-02-11 10:31 [PATCH v2 00/27] Complete the KernelShark v2 transformation Yordan Karadzhov (VMware)
                   ` (20 preceding siblings ...)
  2021-02-11 10:31 ` [PATCH v2 21/27] kernel-shark: Update KsSession Yordan Karadzhov (VMware)
@ 2021-02-11 10:32 ` Yordan Karadzhov (VMware)
  2021-02-11 10:32 ` [PATCH v2 23/27] kernel-shark: Update KsMainWindow and kernelshark.cpp Yordan Karadzhov (VMware)
                   ` (5 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Yordan Karadzhov (VMware) @ 2021-02-11 10:32 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

General revision of the MissedEvents plugin that uses
the new generic methods for visualization of trace event.

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 src/plugins/CMakeLists.txt    |   6 +-
 src/plugins/MissedEvents.cpp  | 105 ++++++++++++++--------------------
 src/plugins/missed_events.c   |  25 ++++----
 src/plugins/missed_events.h   |   4 +-
 tests/libkshark-gui-tests.cpp |   4 +-
 5 files changed, 62 insertions(+), 82 deletions(-)

diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt
index 108bc5f..dc0c320 100644
--- a/src/plugins/CMakeLists.txt
+++ b/src/plugins/CMakeLists.txt
@@ -22,9 +22,9 @@ BUILD_PLUGIN(NAME sched_events
              SOURCE sched_events.c SchedEvents.cpp)
 list(APPEND PLUGIN_LIST "sched_events")
 
-# BUILD_PLUGIN(NAME missed_events
-#              SOURCE missed_events.c MissedEvents.cpp)
-# list(APPEND PLUGIN_LIST "missed_events")
+BUILD_PLUGIN(NAME missed_events
+             SOURCE missed_events.c MissedEvents.cpp)
+list(APPEND PLUGIN_LIST "missed_events")
 
 install(TARGETS ${PLUGIN_LIST}
         LIBRARY DESTINATION ${KS_PLUGIN_INSTALL_PREFIX}
diff --git a/src/plugins/MissedEvents.cpp b/src/plugins/MissedEvents.cpp
index 05dfcb5..cf0ed34 100644
--- a/src/plugins/MissedEvents.cpp
+++ b/src/plugins/MissedEvents.cpp
@@ -26,12 +26,8 @@ using namespace KsPlot;
  */
 class MissedEventsMark : public PlotObject {
 public:
-	/** Create a default Missed events marker. */
-	MissedEventsMark() : _base(0, 0), _height(0)
-	{
-		_color = {0, 0, 255};
-		_size = 2;
-	}
+	/** No default constructor. */
+	MissedEventsMark() = delete;
 
 	/**
 	 * @brief Create and position a Missed events marker.
@@ -40,17 +36,7 @@ public:
 	 * @param h: vertical size (height) of the marker.
 	 */
 	MissedEventsMark(const Point &p, int h)
-	: _base(p.x(), p.y()), _height(h)
-	{
-		_color = {0, 0, 255};
-		_size = 2;
-	}
-
-	/** Set the Base point of the marker. */
-	void setBase(const Point &p) {_base.set(p.x(), p.y());}
-
-	/** Set the vertical size (height) point of the marker. */
-	void setHeight(int h) {_height = h;}
+	: _base(p.x(), p.y()), _height(h) {}
 
 private:
 	/** Base point of the Mark's line. */
@@ -76,61 +62,38 @@ void MissedEventsMark::_draw(const Color &col, float size) const
 	rec.draw();
 }
 
+static PlotObject *makeShape(std::vector<const Graph *> graph,
+			     std::vector<int> bin,
+			     std::vector<kshark_data_field_int64 *>,
+			     Color col, float size)
+{
+	MissedEventsMark *mark = new MissedEventsMark(graph[0]->bin(bin[0])._base,
+						      graph[0]->height());
+	mark->_size = size;
+	mark->_color = col;
+
+	return mark;
+}
+
 //! @cond Doxygen_Suppress
 
 #define PLUGIN_MAX_ENTRIES		10000
 
-#define KS_TASK_COLLECTION_MARGIN	25
-
 //! @endcond
 
-static void pluginDraw(kshark_context *kshark_ctx,
-		       KsCppArgV *argvCpp,
-		       int val, int draw_action)
-{
-	int height = argvCpp->_graph->getHeight();
-	const kshark_entry *entry(nullptr);
-	MissedEventsMark *mark;
-	ssize_t index;
-
-	int nBins = argvCpp->_graph->size();
-	for (int bin = 0; bin < nBins; ++bin) {
-		if (draw_action == KSHARK_PLUGIN_TASK_DRAW)
-			entry = ksmodel_get_task_missed_events(argvCpp->_histo,
-							       bin, val,
-							       nullptr,
-							       &index);
-		if (draw_action == KSHARK_PLUGIN_CPU_DRAW)
-			entry = ksmodel_get_cpu_missed_events(argvCpp->_histo,
-							      bin, val,
-							      nullptr,
-							      &index);
-
-		if (entry) {
-			mark = new MissedEventsMark(argvCpp->_graph->getBin(bin)._base,
-						    height);
-
-			argvCpp->_shapes->push_front(mark);
-		}
-	}
-}
-
 /**
  * @brief Plugin's draw function.
  *
  * @param argv_c: A C pointer to be converted to KsCppArgV (C++ struct).
+ * @param sd: Data stream identifier.
  * @param val: Process or CPU Id value.
  * @param draw_action: Draw action identifier.
  */
 void draw_missed_events(kshark_cpp_argv *argv_c,
-			int val, int draw_action)
+			int sd, int val, int draw_action)
 {
-	kshark_context *kshark_ctx(NULL);
-
-	if (!kshark_instance(&kshark_ctx))
-		return;
-
 	KsCppArgV *argvCpp = KS_ARGV_TO_CPP(argv_c);
+	IsApplicableFunc checkEntry;
 
 	/*
 	 * Plotting the "Missed events" makes sense only in the case of a deep
@@ -141,9 +104,29 @@ void draw_missed_events(kshark_cpp_argv *argv_c,
 	if (argvCpp->_histo->tot_count > PLUGIN_MAX_ENTRIES)
 		return;
 
-	try {
-		pluginDraw(kshark_ctx, argvCpp, val, draw_action);
-	} catch (const std::exception &exc) {
-		std::cerr << "Exception in MissedEvents\n" << exc.what();
-	}
+	if (!(draw_action & KSHARK_CPU_DRAW) &&
+	    !(draw_action & KSHARK_TASK_DRAW))
+		return;
+
+	if (draw_action & KSHARK_CPU_DRAW)
+		checkEntry = [=] (kshark_data_container *, ssize_t bin) {
+			return !!ksmodel_get_cpu_missed_events(argvCpp->_histo,
+							       bin, sd, val,
+							       nullptr,
+							       nullptr);
+		};
+
+	else if (draw_action & KSHARK_TASK_DRAW)
+		checkEntry = [=] (kshark_data_container *, ssize_t bin) {
+			return !!ksmodel_get_task_missed_events(argvCpp->_histo,
+								bin, sd, val,
+								nullptr,
+								nullptr);
+		};
+
+	eventPlot(argvCpp,
+		  checkEntry,
+		  makeShape,
+		  {0, 0, 255}, // Blue
+		  -1);         // Default size
 }
diff --git a/src/plugins/missed_events.c b/src/plugins/missed_events.c
index cf652bf..8fe9780 100644
--- a/src/plugins/missed_events.c
+++ b/src/plugins/missed_events.c
@@ -10,32 +10,27 @@
  *	     ring buffer.
  */
 
+// C
+#include <stdio.h>
+
 // KernelShark
+#include "libkshark.h"
 #include "plugins/missed_events.h"
 
-static void nop_action(struct kshark_context *kshark_ctx,
-				struct tep_record *record,
-				struct kshark_entry *entry)
-{}
-
 /** Load this plugin. */
-int KSHARK_PLUGIN_INITIALIZER(struct kshark_context *kshark_ctx)
+int KSHARK_PLOT_PLUGIN_INITIALIZER(struct kshark_data_stream *stream)
 {
-	kshark_register_event_handler(&kshark_ctx->event_handlers,
-				      KS_EVENT_OVERFLOW,
-				      nop_action,
-				      draw_missed_events);
+	printf("--> missed_events init %i\n", stream->stream_id);
+	kshark_register_draw_handler(stream, draw_missed_events);
 
 	return 1;
 }
 
 /** Unload this plugin. */
-int KSHARK_PLUGIN_DEINITIALIZER(struct kshark_context *kshark_ctx)
+int KSHARK_PLOT_PLUGIN_DEINITIALIZER(struct kshark_data_stream *stream)
 {
-	kshark_unregister_event_handler(&kshark_ctx->event_handlers,
-					KS_EVENT_OVERFLOW,
-					nop_action,
-					draw_missed_events);
+	printf("<-- missed_events close %i\n", stream->stream_id);
+	kshark_unregister_draw_handler(stream, draw_missed_events);
 
 	return 1;
 }
diff --git a/src/plugins/missed_events.h b/src/plugins/missed_events.h
index e05d79a..10bc549 100644
--- a/src/plugins/missed_events.h
+++ b/src/plugins/missed_events.h
@@ -14,14 +14,14 @@
 #define _KS_PLUGIN_M_EVTS_H
 
 // KernelShark
-#include "libkshark.h"
+#include "libkshark-plugin.h"
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
 void draw_missed_events(struct kshark_cpp_argv *argv,
-			int pid, int draw_action);
+			int sd, int pid, int draw_action);
 
 #ifdef __cplusplus
 }
diff --git a/tests/libkshark-gui-tests.cpp b/tests/libkshark-gui-tests.cpp
index dedd69f..5a0ca01 100644
--- a/tests/libkshark-gui-tests.cpp
+++ b/tests/libkshark-gui-tests.cpp
@@ -146,7 +146,9 @@ BOOST_AUTO_TEST_CASE(KsUtils_KsDataStore)
 
 BOOST_AUTO_TEST_CASE(KsUtils_getPluginList)
 {
-	QStringList plugins{"sched_events"};
+	QStringList plugins{"sched_events",
+			    "missed_events"
+	};
 
 	BOOST_CHECK(getPluginList() == plugins);
 }
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v2 23/27] kernel-shark: Update KsMainWindow and kernelshark.cpp
  2021-02-11 10:31 [PATCH v2 00/27] Complete the KernelShark v2 transformation Yordan Karadzhov (VMware)
                   ` (21 preceding siblings ...)
  2021-02-11 10:32 ` [PATCH v2 22/27] kernel-shark: Update MissedEvents plugin Yordan Karadzhov (VMware)
@ 2021-02-11 10:32 ` Yordan Karadzhov (VMware)
  2021-02-11 10:32 ` [PATCH v2 24/27] kernel-shark: Clickable sched_event plugin shapes Yordan Karadzhov (VMware)
                   ` (4 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Yordan Karadzhov (VMware) @ 2021-02-11 10:32 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

The compilation of KsMainWindow.cpp and kernelshark.cpp is
re-enabled and all functionalities are made compatible with
the new version of the C API of libkshark (KernelShark 2.0).

This patch completes the destructive part of the transformation
and the GUI is finally fully functional again.

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 src/CMakeLists.txt   |  21 +-
 src/KsMainWindow.cpp | 786 +++++++++++++++++++++++++++++--------------
 src/KsMainWindow.hpp | 108 ++++--
 src/KsPlugins.hpp    |   5 +
 src/KsSession.cpp    |  34 ++
 src/KsSession.hpp    |   4 +
 src/kernelshark.cpp  |  49 +--
 7 files changed, 695 insertions(+), 312 deletions(-)

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 5ea34ed..4158901 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -74,7 +74,7 @@ if (Qt5Widgets_FOUND AND Qt5Network_FOUND AND TT_FONT_FILE)
                         KsWidgetsLib.hpp
                         KsTraceGraph.hpp
                         KsTraceViewer.hpp
-#                         KsMainWindow.hpp
+                        KsMainWindow.hpp
                         KsCaptureDialog.hpp
                         KsQuickContextMenu.hpp
                         KsAdvFilteringDialog.hpp)
@@ -90,7 +90,7 @@ if (Qt5Widgets_FOUND AND Qt5Network_FOUND AND TT_FONT_FILE)
                                                             KsWidgetsLib.cpp
                                                             KsTraceGraph.cpp
                                                             KsTraceViewer.cpp
-#                                                             KsMainWindow.cpp
+                                                            KsMainWindow.cpp
                                                             KsCaptureDialog.cpp
                                                             KsQuickContextMenu.cpp
                                                             KsAdvFilteringDialog.cpp)
@@ -102,20 +102,19 @@ if (Qt5Widgets_FOUND AND Qt5Network_FOUND AND TT_FONT_FILE)
 
     set_target_properties(kshark-gui PROPERTIES  SUFFIX ".so.${KS_VERSION_STRING}")
 
-#     message(STATUS ${KS_APP_NAME})
-#     add_executable(${KS_APP_NAME}          kernelshark.cpp)
-#     target_link_libraries(${KS_APP_NAME}   kshark-gui)
+    message(STATUS ${KS_APP_NAME})
+    add_executable(${KS_APP_NAME}          kernelshark.cpp)
+    target_link_libraries(${KS_APP_NAME}   kshark-gui)
 
     message(STATUS "kshark-record")
     add_executable(kshark-record        kshark-record.cpp)
     target_link_libraries(kshark-record kshark-gui)
 
-#     install(TARGETS ${KS_APP_NAME} kshark-record kshark-gui
-#             RUNTIME DESTINATION       ${_INSTALL_PREFIX}/bin/
-#                 COMPONENT                 kernelshark
-#             LIBRARY DESTINATION       ${_LIBDIR}
-#                 COMPONENT                 kernelshark)
-
+    install(TARGETS ${KS_APP_NAME} kshark-record kshark-gui
+            RUNTIME DESTINATION       ${_INSTALL_PREFIX}/bin/
+                COMPONENT                 kernelshark
+            LIBRARY DESTINATION       ${_LIBDIR}
+                COMPONENT                 kernelshark)
 
     install(FILES "${KS_DIR}/${KS_APP_NAME}.desktop"
             DESTINATION ${_INSTALL_PREFIX}/share/applications/
diff --git a/src/KsMainWindow.cpp b/src/KsMainWindow.cpp
index fc4e9a9..e830c5e 100644
--- a/src/KsMainWindow.cpp
+++ b/src/KsMainWindow.cpp
@@ -26,11 +26,13 @@
 
 // KernelShark
 #include "libkshark.h"
+#include "libkshark-tepdata.h"
 #include "KsCmakeDef.hpp"
 #include "KsMainWindow.hpp"
-#include "KsCaptureDialog.hpp"
 #include "KsAdvFilteringDialog.hpp"
 
+using namespace KsWidgetsLib;
+
 /** Create KernelShark Main window. */
 KsMainWindow::KsMainWindow(QWidget *parent)
 : QMainWindow(parent),
@@ -42,25 +44,25 @@ KsMainWindow::KsMainWindow(QWidget *parent)
   _plugins(this),
   _capture(this),
   _captureLocalServer(this),
-  _openAction("Open", this),
+  _openAction("Open Trace File", this),
+  _appendAction("Append Trace File", this),
   _restoreSessionAction("Restore Last Session", this),
   _importSessionAction("Import Session", this),
   _exportSessionAction("Export Session", this),
   _quitAction("Quit", this),
-  _importFilterAction("Import Filter", this),
-  _exportFilterAction("Export Filter", this),
   _graphFilterSyncCBox(nullptr),
   _listFilterSyncCBox(nullptr),
   _showEventsAction("Show events", this),
   _showTasksAction("Show tasks", this),
   _showCPUsAction("Show CPUs", this),
-  _advanceFilterAction("Advance Filtering", this),
+  _advanceFilterAction("TEP Advance Filtering", this),
   _clearAllFilters("Clear all filters", this),
   _cpuSelectAction("CPUs", this),
   _taskSelectAction("Tasks", this),
-  _managePluginsAction("Manage plugins", this),
+  _managePluginsAction("Manage Plotting plugins", this),
   _addPluginsAction("Add plugins", this),
   _captureAction("Record", this),
+  _addOffcetAction("Add Time Offset", this),
   _colorAction(this),
   _colSlider(this),
   _colorPhaseSlider(Qt::Horizontal, this),
@@ -69,12 +71,15 @@ KsMainWindow::KsMainWindow(QWidget *parent)
   _contentsAction("Contents", this),
   _bugReportAction("Report a bug", this),
   _deselectShortcut(this),
-  _settings(_getCacheDir() + "/setting.ini", QSettings::IniFormat)
+  _settings(_getCacheDir() + "/setting.ini", QSettings::IniFormat),
+  _workInProgress(this),
+  _updateSessionSize(true)
 {
 	setWindowTitle("Kernel Shark");
 	_createActions();
 	_createMenus();
 	_initCapture();
+	_plugins.registerPluginMenues();
 
 	if (geteuid() == 0)
 		_rootWarning();
@@ -82,6 +87,24 @@ KsMainWindow::KsMainWindow(QWidget *parent)
 	_splitter.addWidget(&_graph);
 	_splitter.addWidget(&_view);
 	setCentralWidget(&_splitter);
+
+	/*
+	 * Add Status bar. First of all remove the bottom margins of the table
+	 * so that the Status bar can nicely stick to it.
+	 */
+	QMargins m = _view.layout()->contentsMargins();
+	m.setBottom(0);
+	_view.layout()->setContentsMargins(m);
+
+	/* Now create the Status bar and the "Work In Progress" Widget. */
+	QStatusBar *sb = statusBar();
+	sb->setFixedHeight(1.2 * FONT_HEIGHT);
+	_workInProgress.addToStatusBar(sb);
+
+	_graph.setWipPtr(&_workInProgress);
+	_graph.glPtr()->setWipPtr(&_workInProgress);
+	_view.setWipPtr(&_workInProgress);
+
 	connect(&_splitter,	&QSplitter::splitterMoved,
 		this,		&KsMainWindow::_splitterMoved);
 
@@ -157,6 +180,7 @@ KsMainWindow::~KsMainWindow()
 	_settings.setValue("pluginPath", _lastPluginFilePath);
 
 	_data.clear();
+	_plugins.deletePluginDialogs();
 
 	/*
 	 * Do not show error messages if the "capture" process is still
@@ -168,14 +192,32 @@ KsMainWindow::~KsMainWindow()
 		_capture.waitForFinished();
 	}
 
+	/**
+	 * The list of shapes generated by the plugins may contain objects
+	 * defined inside the plugins. Make sure to delete these objects now,
+	 * because after closing the plugins, the destructors of the
+	 * plugins-defined objects will no longer be available.
+	 */
+	_graph.glPtr()->freePluginShapes();
+
 	if (kshark_instance(&kshark_ctx))
 		kshark_free(kshark_ctx);
 }
 
 /** Set the list ot CPU cores to be plotted. */
-void KsMainWindow::setCPUPlots(QVector<int> cpus)
+void KsMainWindow::setCPUPlots(int sd, QVector<int> cpus)
 {
-	int nCPUs = tep_get_cpus(_data.tep());
+	kshark_context *kshark_ctx(nullptr);
+	kshark_data_stream *stream;
+
+	if (!kshark_instance(&kshark_ctx))
+		return;
+
+	stream = kshark_get_data_stream(kshark_ctx, sd);
+	if (!stream)
+		return;
+
+	int nCPUs = stream->n_cpus;
 	auto lamCPUCheck = [=] (int cpu) {
 		if (cpu >= nCPUs) {
 			qWarning() << "Warning: No CPU" << cpu << "found in the data.";
@@ -188,13 +230,13 @@ void KsMainWindow::setCPUPlots(QVector<int> cpus)
 	cpus.erase(std::remove_if(cpus.begin(), cpus.end(), lamCPUCheck),
 		   cpus.end());
 
-	_graph.cpuReDraw(cpus);
+	_graph.cpuReDraw(sd, cpus);
 }
 
 /** Set the list ot tasks (pids) to be plotted. */
-void KsMainWindow::setTaskPlots(QVector<int> pids)
+void KsMainWindow::setTaskPlots(int sd, QVector<int> pids)
 {
-	QVector<int> allPids = KsUtils::getPidList();
+	QVector<int> allPids = KsUtils::getPidList(sd);
 	auto lamPidCheck = [=] (int pid) {
 		int i = allPids.indexOf(pid);
 		if (i < 0) {
@@ -208,7 +250,7 @@ void KsMainWindow::setTaskPlots(QVector<int> pids)
 	pids.erase(std::remove_if(pids.begin(), pids.end(), lamPidCheck),
 		   pids.end());
 
-	_graph.taskReDraw(pids);
+	_graph.taskReDraw(sd, pids);
 }
 
 /**
@@ -219,8 +261,10 @@ void KsMainWindow::resizeEvent(QResizeEvent* event)
 {
 	QMainWindow::resizeEvent(event);
 
-	_session.saveMainWindowSize(*this);
-	_session.saveSplitterSize(_splitter);
+	if (_updateSessionSize) {
+		_session.saveMainWindowSize(*this);
+		_session.saveSplitterSize(_splitter);
+	}
 }
 
 void KsMainWindow::_createActions()
@@ -233,6 +277,13 @@ void KsMainWindow::_createActions()
 	connect(&_openAction,	&QAction::triggered,
 		this,		&KsMainWindow::_open);
 
+	_appendAction.setIcon(QIcon::fromTheme("document-open"));
+	_appendAction.setShortcut(tr("Ctrl+A"));
+	_appendAction.setStatusTip("Append an existing data file");
+
+	connect(&_appendAction,	&QAction::triggered,
+		this,		&KsMainWindow::_append);
+
 	_restoreSessionAction.setIcon(QIcon::fromTheme("document-open-recent"));
 	connect(&_restoreSessionAction,	&QAction::triggered,
 		this,			&KsMainWindow::_restoreSession);
@@ -257,18 +308,6 @@ void KsMainWindow::_createActions()
 		this,		&KsMainWindow::close);
 
 	/* Filter menu */
-	_importFilterAction.setIcon(QIcon::fromTheme("document-send"));
-	_importFilterAction.setStatusTip("Load a filter");
-
-	connect(&_importFilterAction,	&QAction::triggered,
-		this,			&KsMainWindow::_importFilter);
-
-	_exportFilterAction.setIcon(QIcon::fromTheme("document-revert"));
-	_exportFilterAction.setStatusTip("Export a filter");
-
-	connect(&_exportFilterAction,	&QAction::triggered,
-		this,			&KsMainWindow::_exportFilter);
-
 	connect(&_showEventsAction,	&QAction::triggered,
 		this,			&KsMainWindow::_showEvents);
 
@@ -312,14 +351,24 @@ void KsMainWindow::_createActions()
 	connect(&_captureAction,	&QAction::triggered,
 		this,			&KsMainWindow::_record);
 
+	connect(&_addOffcetAction,	&QAction::triggered,
+		this,			&KsMainWindow::_offset);
+
 	_colorPhaseSlider.setMinimum(20);
 	_colorPhaseSlider.setMaximum(180);
-	_colorPhaseSlider.setValue(KsPlot::Color::getRainbowFrequency() * 100);
+	_colorPhaseSlider.setValue(KsPlot::Color::rainbowFrequency() * 100);
 	_colorPhaseSlider.setFixedWidth(FONT_WIDTH * 15);
 
 	connect(&_colorPhaseSlider,	&QSlider::valueChanged,
 		this,			&KsMainWindow::_setColorPhase);
 
+	/*
+	 * Updating the colors of the table can be slow. Do this only when
+	 * the slider is released.
+	 */
+	connect(&_colorPhaseSlider,	&QSlider::sliderReleased,
+		&_view,			&KsTraceViewer::loadColors);
+
 	_colSlider.setLayout(new QHBoxLayout);
 	_colSlider.layout()->addWidget(new QLabel("Color scheme", this));
 	_colSlider.layout()->addWidget(&_colorPhaseSlider);
@@ -357,6 +406,7 @@ void KsMainWindow::_createMenus()
 	/* File menu */
 	file = menuBar()->addMenu("File");
 	file->addAction(&_openAction);
+	file->addAction(&_appendAction);
 
 	sessions = file->addMenu("Sessions");
 	sessions->setIcon(QIcon::fromTheme("document-properties"));
@@ -365,15 +415,30 @@ void KsMainWindow::_createMenus()
 	sessions->addAction(&_exportSessionAction);
 	file->addAction(&_quitAction);
 
+	/*
+	 * Enable the "Append Trace File" menu only in the case of multiple
+	 * data streams.
+	 */
+	auto lamEnableAppendAction = [this] () {
+		kshark_context *kshark_ctx(nullptr);
+
+		if (!kshark_instance(&kshark_ctx))
+			return;
+
+		if (kshark_ctx->n_streams > 0)
+			_appendAction.setEnabled(true);
+		else
+			_appendAction.setEnabled(false);
+	};
+
+	connect(file,	&QMenu::aboutToShow, lamEnableAppendAction);
+
 	/* Filter menu */
 	filter = menuBar()->addMenu("Filter");
 
 	connect(filter, 		&QMenu::aboutToShow,
 		this,			&KsMainWindow::_updateFilterMenu);
 
-	filter->addAction(&_importFilterAction);
-	filter->addAction(&_exportFilterAction);
-
 	/*
 	 * Set the default filter mask. Filter will apply to both View and
 	 * Graph.
@@ -403,6 +468,29 @@ void KsMainWindow::_createMenus()
 	filter->addAction(&_advanceFilterAction);
 	filter->addAction(&_clearAllFilters);
 
+	/*
+	 * Enable the "TEP Advance Filtering" menu only in the case when TEP
+	 * data is loaded.
+	 */
+	auto lamEnableAdvFilterAction = [this] () {
+		kshark_context *kshark_ctx(nullptr);
+		QVector<int> streamIds;
+
+		if (!kshark_instance(&kshark_ctx))
+			return;
+
+		_advanceFilterAction.setEnabled(false);
+		streamIds = KsUtils::getStreamIdList(kshark_ctx);
+		for (auto const &sd: streamIds) {
+			if (kshark_is_tep(kshark_ctx->stream[sd])) {
+				_advanceFilterAction.setEnabled(true);
+				break;
+			}
+		}
+	};
+
+	connect(filter,	&QMenu::aboutToShow, lamEnableAdvFilterAction);
+
 	/* Plot menu */
 	plots = menuBar()->addMenu("Plots");
 	plots->addAction(&_cpuSelectAction);
@@ -410,12 +498,31 @@ void KsMainWindow::_createMenus()
 
 	/* Tools menu */
 	tools = menuBar()->addMenu("Tools");
-	tools->addAction(&_managePluginsAction);
-	tools->addAction(&_addPluginsAction);
-	tools->addAction(&_captureAction);
-	tools->addSeparator();
 	tools->addAction(&_colorAction);
 	tools->addAction(&_fullScreenModeAction);
+	tools->addSeparator();
+	tools->addAction(&_captureAction);
+	tools->addAction(&_managePluginsAction);
+	tools->addAction(&_addPluginsAction);
+	tools->addAction(&_addOffcetAction);
+
+	/*
+	 * Enable the "Add Time Offset" menu only in the case of multiple
+	 * data streams.
+	 */
+	auto lamEnableOffcetAction = [this] () {
+		kshark_context *kshark_ctx(nullptr);
+
+		if (!kshark_instance(&kshark_ctx))
+			return;
+
+		if (kshark_ctx->n_streams > 1)
+			_addOffcetAction.setEnabled(true);
+		else
+			_addOffcetAction.setEnabled(false);
+	};
+
+	connect(tools,	&QMenu::aboutToShow, lamEnableOffcetAction);
 
 	/* Help menu */
 	help = menuBar()->addMenu("Help");
@@ -424,6 +531,53 @@ void KsMainWindow::_createMenus()
 	help->addAction(&_bugReportAction);
 }
 
+/**
+ * @brief Add a plugin configuration/control menu.
+ *
+ * @param place: String describing the place to add the menu. For example
+ *		 "Tools/Plot Latency".
+ * @param func: Function that will launch of plugin control menus.
+ */
+void KsMainWindow::addPluginMenu(QString place, pluginActionFunc func)
+{
+	QStringList dialogPath = place.split("/");
+	QAction *pluginAction;
+
+	auto lamAddMenu = [this, func] () {
+		func(this);
+	};
+
+	for (auto &m:  menuBar()->findChildren<QMenu*>()) {
+		if(dialogPath[0] == m->menuAction()->text()) {
+			pluginAction = new QAction(dialogPath[1], this);
+			m->addAction(pluginAction);
+
+			connect(pluginAction,	&QAction::triggered,
+				lamAddMenu);
+		}
+	}
+}
+
+/** Select the kshark_entry having given index with a given maker. */
+void KsMainWindow::markEntry(ssize_t row, DualMarkerState st)
+{
+	if (row >= 0) {
+		_mState.setState(st);
+		_graph.markEntry(row);
+		_view.showRow(row, true);
+	}
+}
+
+/** Select given kshark_entry with a given maker. */
+void KsMainWindow::markEntry(const kshark_entry *e, DualMarkerState st)
+{
+	ssize_t row = kshark_find_entry_by_time(e->ts, _data.rows(),
+						0, _data.size() - 1);
+
+	markEntry(row, st);
+}
+
+
 void KsMainWindow::_open()
 {
 	QString fileName;
@@ -436,6 +590,17 @@ void KsMainWindow::_open()
 		loadDataFile(fileName);
 }
 
+void KsMainWindow::_append()
+{
+	QString fileName = KsUtils::getFile(this,
+					    "Append File",
+					    "trace-cmd files (*.dat);;Text files (*.txt);;All files (*)",
+					    _lastDataFilePath);
+
+	if (!fileName.isEmpty())
+		appendDataFile(fileName);
+}
+
 QString KsMainWindow::_getCacheDir()
 {
 	QString dir;
@@ -524,13 +689,13 @@ void KsMainWindow::_updateSession()
 	if (!kshark_instance(&kshark_ctx))
 		return;
 
-	_session.saveGraphs(*_graph.glPtr());
 	_session.saveVisModel(_graph.glPtr()->model()->histo());
-	_session.saveFilters(kshark_ctx);
+	_session.saveDataStreams(kshark_ctx);
+	_session.saveGraphs(kshark_ctx, _graph);
 	_session.saveDualMarker(&_mState);
 	_session.saveTable(_view);
 	_session.saveColorScheme();
-	_session.savePlugins(_plugins);
+	_session.saveUserPlugins(_plugins);
 }
 
 void KsMainWindow::_exportSession()
@@ -570,57 +735,6 @@ void KsMainWindow::_updateFilterMenu()
 		_filterSyncCBoxUpdate(kshark_ctx);
 }
 
-void KsMainWindow::_importFilter()
-{
-	kshark_context *kshark_ctx(nullptr);
-	kshark_config_doc *conf;
-	QString fileName;
-
-	if (!kshark_instance(&kshark_ctx) || _data.size() < 1)
-		return;
-
-	fileName = KsUtils::getFile(this, "Import Filter",
-				    "Kernel Shark Config files (*.json);;",
-				    _lastConfFilePath);
-
-	if (fileName.isEmpty())
-		return;
-
-	conf = kshark_open_config_file(fileName.toStdString().c_str(),
-				       "kshark.config.filter");
-	if (!conf)
-		return;
-
-	kshark_import_all_event_filters(kshark_ctx, conf);
-	kshark_free_config_doc(conf);
-
-	kshark_filter_entries(kshark_ctx, _data.rows(), _data.size());
-	_filterSyncCBoxUpdate(kshark_ctx);
-	emit _data.updateWidgets(&_data);
-}
-
-void KsMainWindow::_exportFilter()
-{
-	kshark_context *kshark_ctx(nullptr);
-	kshark_config_doc *conf(nullptr);
-	QString fileName;
-
-	if (!kshark_instance(&kshark_ctx))
-		return;
-
-	fileName = KsUtils::getSaveFile(this, "Export Filter",
-					"Kernel Shark Config files (*.json);;",
-					".json",
-					_lastConfFilePath);
-
-	if (fileName.isEmpty())
-		return;
-
-	kshark_export_all_event_filters(kshark_ctx, &conf);
-	kshark_save_config_file(fileName.toStdString().c_str(), conf);
-	kshark_free_config_doc(conf);
-}
-
 void KsMainWindow::_listFilterSync(int state)
 {
 	KsUtils::listFilterSync(state);
@@ -633,8 +747,8 @@ void KsMainWindow::_graphFilterSync(int state)
 	_data.update();
 }
 
-void KsMainWindow::_presetCBWidget(tracecmd_filter_id *showFilter,
-				   tracecmd_filter_id *hideFilter,
+void KsMainWindow::_presetCBWidget(kshark_hash_id *showFilter,
+				   kshark_hash_id *hideFilter,
 				   KsCheckBoxWidget *cbw)
 {
 	if (!kshark_this_filter_is_set(showFilter) &&
@@ -646,7 +760,7 @@ void KsMainWindow::_presetCBWidget(tracecmd_filter_id *showFilter,
 		cbw->setDefault(true);
 	} else {
 		QVector<int> ids = cbw->getIds();
-		QVector<bool>  status;
+		QVector<int>  status;
 		int n = ids.count();
 		bool show, hide;
 
@@ -655,12 +769,12 @@ void KsMainWindow::_presetCBWidget(tracecmd_filter_id *showFilter,
 			 * The "show only" filter is set. The default status
 			 * of all CheckBoxes will be "unchecked".
 			 */
-			status = QVector<bool>(n, false);
+			status = QVector<int>(n, false);
 			for (int i = 0; i < n; ++i) {
-				show = !!tracecmd_filter_id_find(showFilter,
+				show = !!kshark_hash_id_find(showFilter,
 							         ids[i]);
 
-				hide = !!tracecmd_filter_id_find(hideFilter,
+				hide = !!kshark_hash_id_find(hideFilter,
 							         ids[i]);
 
 				if (show && !hide) {
@@ -677,9 +791,9 @@ void KsMainWindow::_presetCBWidget(tracecmd_filter_id *showFilter,
 			 * Only the "do not show" filter is set. The default
 			 * status of all CheckBoxes will be "checked".
 			 */
-			status = QVector<bool>(n, true);
+			status = QVector<int>(n, true);
 			for (int i = 0; i < n; ++i) {
-				hide = !!tracecmd_filter_id_find(hideFilter,
+				hide = !!kshark_hash_id_find(hideFilter,
 							         ids[i]);
 
 				if (hide)
@@ -691,12 +805,12 @@ void KsMainWindow::_presetCBWidget(tracecmd_filter_id *showFilter,
 	}
 }
 
-void KsMainWindow::_applyFilter(QVector<int> all, QVector<int> show,
-				std::function<void(QVector<int>)> posFilter,
-				std::function<void(QVector<int>)> negFilter)
+void KsMainWindow::_applyFilter(int sd, QVector<int> all, QVector<int> show,
+				std::function<void(int, QVector<int>)> posFilter,
+				std::function<void(int, QVector<int>)> negFilter)
 {
-	if (show.count() < all.count() / 2) {
-		posFilter(show);
+	if (show.count() != 0 && show.count() < all.count() / 2) {
+		posFilter(sd, show);
 	} else {
 		/*
 		 * It is more efficiant to apply negative (do not show) filter.
@@ -719,38 +833,46 @@ void KsMainWindow::_applyFilter(QVector<int> all, QVector<int> show,
 				    show.begin(), show.end(),
 				    std::inserter(diff, diff.begin()));
 
-		negFilter(diff);
+		negFilter(sd, diff);
 	}
 }
 
 /* Quiet warnings over documenting simple structures */
 //! @cond Doxygen_Suppress
 
-#define LAMDA_FILTER(method) [=] (QVector<int> vec) {method(vec);}
+#define LAMBDA_FILTER(method) [=] (int sd, QVector<int> vec) {method(sd, vec);}
 
 //! @endcond
 
 void KsMainWindow::_showEvents()
 {
 	kshark_context *kshark_ctx(nullptr);
-	KsCheckBoxWidget *events_cb;
+	QVector<KsCheckBoxWidget *> cbws;
+	KsCheckBoxWidget *events_cbw;
 	KsCheckBoxDialog *dialog;
-	QVector<bool> v;
+	kshark_data_stream *stream;
+	QVector<int> streamIds;
 
 	if (!kshark_instance(&kshark_ctx))
 		return;
 
-	events_cb = new KsEventsCheckBoxWidget(_data.tep(), this);
-	dialog = new KsCheckBoxDialog(events_cb, this);
-	_presetCBWidget(kshark_ctx->show_event_filter,
-		        kshark_ctx->hide_event_filter,
-		        events_cb);
-
-	auto lamFilter = [=] (QVector<int> show) {
-		QVector<int> all = KsUtils::getEventIdList();
-		_applyFilter(all, show,
-			     LAMDA_FILTER(_data.applyPosEventFilter),
-			     LAMDA_FILTER(_data.applyNegEventFilter));
+	streamIds = KsUtils::getStreamIdList(kshark_ctx);
+	for (auto const &sd: streamIds) {
+		stream = kshark_ctx->stream[sd];
+		events_cbw = new KsEventsCheckBoxWidget(stream, this);
+		cbws.append(events_cbw);
+		_presetCBWidget(stream->show_event_filter,
+				stream->hide_event_filter,
+				events_cbw);
+	}
+
+	dialog = new KsCheckBoxDialog(cbws, this);
+
+	auto lamFilter = [=] (int sd, QVector<int> show) {
+		QVector<int> all = KsUtils::getEventIdList(sd);
+		_applyFilter(sd, all, show,
+			     LAMBDA_FILTER(_data.applyPosEventFilter),
+			     LAMBDA_FILTER(_data.applyNegEventFilter));
 	};
 
 	connect(dialog,		&KsCheckBoxDialog::apply, lamFilter);
@@ -761,24 +883,32 @@ void KsMainWindow::_showEvents()
 void KsMainWindow::_showTasks()
 {
 	kshark_context *kshark_ctx(nullptr);
-	KsCheckBoxWidget *tasks_cbd;
+	QVector<KsCheckBoxWidget *> cbws;
+	kshark_data_stream *stream;
+	KsCheckBoxWidget *tasks_cbw;
 	KsCheckBoxDialog *dialog;
-	QVector<bool> v;
+	QVector<int> streamIds;
 
 	if (!kshark_instance(&kshark_ctx))
 		return;
 
-	tasks_cbd = new KsTasksCheckBoxWidget(_data.tep(), true, this);
-	dialog = new KsCheckBoxDialog(tasks_cbd, this);
-	_presetCBWidget(kshark_ctx->show_task_filter,
-			kshark_ctx->hide_task_filter,
-			tasks_cbd);
-
-	auto lamFilter = [=] (QVector<int> show) {
-		QVector<int> all = KsUtils::getEventIdList();
-		_applyFilter(all, show,
-			     LAMDA_FILTER(_data.applyPosTaskFilter),
-			     LAMDA_FILTER(_data.applyNegTaskFilter));
+	streamIds = KsUtils::getStreamIdList(kshark_ctx);
+	for (auto const &sd: streamIds) {
+		stream = kshark_ctx->stream[sd];
+		tasks_cbw = new KsTasksCheckBoxWidget(stream, true, this);
+		cbws.append(tasks_cbw);
+		_presetCBWidget(stream->show_task_filter,
+				stream->hide_task_filter,
+				tasks_cbw);
+	}
+
+	dialog = new KsCheckBoxDialog(cbws, this);
+
+	auto lamFilter = [=] (int sd, QVector<int> show) {
+		QVector<int> all = KsUtils::getPidList(sd);
+		_applyFilter(sd, all, show,
+			     LAMBDA_FILTER(_data.applyPosTaskFilter),
+			     LAMBDA_FILTER(_data.applyNegTaskFilter));
 	};
 
 	connect(dialog,		&KsCheckBoxDialog::apply, lamFilter);
@@ -789,24 +919,32 @@ void KsMainWindow::_showTasks()
 void KsMainWindow::_showCPUs()
 {
 	kshark_context *kshark_ctx(nullptr);
-	KsCheckBoxWidget *cpu_cbd;
+	QVector<KsCheckBoxWidget *> cbws;
+	kshark_data_stream *stream;
+	KsCheckBoxWidget *cpus_cbw;
 	KsCheckBoxDialog *dialog;
-	QVector<bool> v;
+	QVector<int> streamIds;
 
 	if (!kshark_instance(&kshark_ctx))
 		return;
 
-	cpu_cbd = new KsCPUCheckBoxWidget(_data.tep(), this);
-	dialog = new KsCheckBoxDialog(cpu_cbd, this);
-	_presetCBWidget(kshark_ctx->show_cpu_filter,
-			kshark_ctx->hide_cpu_filter,
-			cpu_cbd);
-
-	auto lamFilter = [=] (QVector<int> show) {
-		QVector<int> all = KsUtils::getEventIdList();
-		_applyFilter(all, show,
-			     LAMDA_FILTER(_data.applyPosCPUFilter),
-			     LAMDA_FILTER(_data.applyNegCPUFilter));
+	streamIds = KsUtils::getStreamIdList(kshark_ctx);
+	for (auto const &sd: streamIds) {
+		stream = kshark_ctx->stream[sd];
+		cpus_cbw = new KsCPUCheckBoxWidget(stream, this);
+		cbws.append(cpus_cbw);
+		_presetCBWidget(stream->show_task_filter,
+				stream->hide_task_filter,
+				cpus_cbw);
+	}
+
+	dialog = new KsCheckBoxDialog(cbws, this);
+
+	auto lamFilter = [=] (int sd, QVector<int> show) {
+		QVector<int> all = KsUtils::getCPUList(sd);
+		_applyFilter(sd, all, show,
+			     LAMBDA_FILTER(_data.applyPosCPUFilter),
+			     LAMBDA_FILTER(_data.applyNegCPUFilter));
 	};
 
 	connect(dialog,		&KsCheckBoxDialog::apply, lamFilter);
@@ -818,18 +956,6 @@ void KsMainWindow::_advancedFiltering()
 {
 	KsAdvFilteringDialog *dialog;
 
-	if (!_data.tep()) {
-		QErrorMessage *em = new QErrorMessage(this);
-		QString text("Unable to open Advanced filtering dialog.");
-
-		text += " Tracing data has to be loaded first.";
-
-		em->showMessage(text, "advancedFiltering");
-		qCritical() << "ERROR: " << text;
-
-		return;
-	}
-
 	dialog = new KsAdvFilteringDialog(this);
 	connect(dialog,		&KsAdvFilteringDialog::dataReload,
 		&_data,		&KsDataStore::reload);
@@ -844,23 +970,37 @@ void KsMainWindow::_clearFilters()
 
 void KsMainWindow::_cpuSelect()
 {
-	KsCheckBoxWidget *cpus_cbd = new KsCPUCheckBoxWidget(_data.tep(), this);
-	KsCheckBoxDialog *dialog = new KsCheckBoxDialog(cpus_cbd, this);
+	kshark_context *kshark_ctx(nullptr);
+	QVector<KsCheckBoxWidget *> cbws;
+	kshark_data_stream *stream;
+	KsCheckBoxWidget *cpus_cbd;
+	KsCheckBoxDialog *dialog;
+	QVector<int> streamIds;
+	int nCPUs;
+
+	if (!kshark_instance(&kshark_ctx))
+		return;
 
-	if(_data.tep()) {
-		int nCPUs = tep_get_cpus(_data.tep());
-		if (nCPUs == _graph.glPtr()->cpuGraphCount()) {
+	streamIds = KsUtils::getStreamIdList(kshark_ctx);
+	for (auto const &sd: streamIds) {
+		stream = kshark_ctx->stream[sd];
+		cpus_cbd = new KsCPUCheckBoxWidget(stream, this);
+		cbws.append(cpus_cbd);
+
+		nCPUs = stream->n_cpus;
+		if (nCPUs == _graph.glPtr()->cpuGraphCount(sd)) {
 			cpus_cbd->setDefault(true);
 		} else {
-			QVector<bool> v(nCPUs, false);
-
-			for (auto const &cpu: _graph.glPtr()->_cpuList)
+			QVector<int> v(nCPUs, false);
+			for (auto const &cpu: _graph.glPtr()->_streamPlots[sd]._cpuList)
 				v[cpu] = true;
 
 			cpus_cbd->set(v);
 		}
 	}
 
+	dialog = new KsCheckBoxDialog(cbws, this);
+
 	connect(dialog,		&KsCheckBoxDialog::apply,
 		&_graph,	&KsTraceGraph::cpuReDraw);
 
@@ -869,29 +1009,46 @@ void KsMainWindow::_cpuSelect()
 
 void KsMainWindow::_taskSelect()
 {
-	KsCheckBoxWidget *tasks_cbd = new KsTasksCheckBoxWidget(_data.tep(),
-								true,
-								this);
-	KsCheckBoxDialog *dialog = new KsCheckBoxDialog(tasks_cbd, this);
-	QVector<int> pids = KsUtils::getPidList();
-	int nPids = pids.count();
-
-	if (nPids == _graph.glPtr()->taskGraphCount()) {
-		tasks_cbd->setDefault(true);
-	} else {
-		QVector<bool> v(nPids, false);
-		for (int i = 0; i < nPids; ++i) {
-			for (auto const &pid: _graph.glPtr()->_taskList) {
-				if (pids[i] == pid) {
-					v[i] = true;
-					break;
+	kshark_context *kshark_ctx(nullptr);
+	QVector<KsCheckBoxWidget *> cbws;
+	QVector<int> streamIds, pids;
+	kshark_data_stream *stream;
+	KsCheckBoxWidget *tasks_cbd;
+	KsCheckBoxDialog *dialog;
+	int nPids;
+
+	if (!kshark_instance(&kshark_ctx))
+		return;
+
+	streamIds = KsUtils::getStreamIdList(kshark_ctx);
+	for (auto const &sd: streamIds) {
+		stream = kshark_ctx->stream[sd];
+		tasks_cbd = new KsTasksCheckBoxWidget(stream, true, this);
+		cbws.append(tasks_cbd);
+
+		pids = KsUtils::getPidList(sd);
+		nPids = pids.count();
+		if (nPids == _graph.glPtr()->taskGraphCount(sd)) {
+			tasks_cbd->setDefault(true);
+		} else {
+			QVector<int> v(nPids, false);
+			for (int i = 0; i < nPids; ++i) {
+				QVector<int> plots =
+					_graph.glPtr()->_streamPlots[sd]._taskList;
+				for (auto const &pid: plots) {
+					if (pids[i] == pid) {
+						v[i] = true;
+						break;
+					}
 				}
 			}
-		}
 
-		tasks_cbd->set(v);
+			tasks_cbd->set(v);
+		}
 	}
 
+	dialog = new KsCheckBoxDialog(cbws, this);
+
 	connect(dialog,		&KsCheckBoxDialog::apply,
 		&_graph,	&KsTraceGraph::taskReDraw);
 
@@ -900,37 +1057,106 @@ void KsMainWindow::_taskSelect()
 
 void KsMainWindow::_pluginSelect()
 {
-	KsCheckBoxWidget *plugin_cbd;
+	QVector<int> streamIds, enabledPlugins, failedPlugins;
+	kshark_context *kshark_ctx(nullptr);
+	QVector<KsCheckBoxWidget *> cbws;
+	KsPluginCheckBoxWidget *plugin_cbw;
 	KsCheckBoxDialog *dialog;
-	QVector<bool> registeredPlugins;
-	QStringList plugins;
+	QStringList pluginList;
 
-	plugins << _plugins._ksPluginList << _plugins._userPluginList;
+	if (!kshark_instance(&kshark_ctx))
+		return;
+
+	if (kshark_ctx->n_streams == 0) {
+		QString err("Data has to be loaded first.");
+		QMessageBox msgBox;
+		msgBox.critical(nullptr, "Error", err);
+
+		return;
+	}
+
+	streamIds = KsUtils::getStreamIdList(kshark_ctx);
+	for (auto const &sd: streamIds) {
+		pluginList = _plugins.getStreamPluginList(sd);
+		enabledPlugins = _plugins.getActivePlugins(sd);
+		failedPlugins =
+			_plugins.getPluginsByStatus(sd, KSHARK_PLUGIN_FAILED);
 
-	registeredPlugins << _plugins._registeredKsPlugins
-			  << _plugins._registeredUserPlugins;
+		plugin_cbw = new KsPluginCheckBoxWidget(sd, pluginList, this);
+		plugin_cbw->set(enabledPlugins);
+		plugin_cbw->setActive(failedPlugins, false);
 
-	plugin_cbd = new KsPluginCheckBoxWidget(plugins, this);
-	plugin_cbd->set(registeredPlugins);
+		cbws.append(plugin_cbw);
+	}
 
-	dialog = new KsCheckBoxDialog(plugin_cbd, this);
+	dialog = new KsPluginsCheckBoxDialog(cbws, &_data, this);
+	dialog->applyStatus();
 
 	connect(dialog,		&KsCheckBoxDialog::apply,
-		&_plugins,	&KsPluginManager::updatePlugins);
+		this,		&KsMainWindow::_pluginUpdate);
 
 	dialog->show();
+
+	_graph.update(&_data);
+}
+
+void KsMainWindow::_pluginUpdate(int sd, QVector<int> pluginStates)
+{
+	kshark_context *kshark_ctx(nullptr);
+	QVector<int> streamIds;
+
+	if (!kshark_instance(&kshark_ctx))
+		return;
+
+	_plugins.updatePlugins(sd, pluginStates);
+	streamIds = KsUtils::getStreamIdList(kshark_ctx);
+	if (streamIds.size() && streamIds.last() == sd) {
+		/* This is the last stream. Reload the data. */
+		if (_data.size())
+			_data.reload();
+	}
 }
 
 void KsMainWindow::_pluginAdd()
 {
+	kshark_context *kshark_ctx(nullptr);
 	QStringList fileNames;
+	QVector<int> streams;
+
+	if (!kshark_instance(&kshark_ctx))
+		return;
+
+	fileNames = KsUtils::getFiles(this,
+				      "Add KernelShark plugins",
+				      "KernelShark Plugins (*.so);;",
+				      _lastPluginFilePath);
+
+	if (!fileNames.isEmpty()) {
+		if (kshark_ctx->n_streams > 1) {
+			KsDStreamCheckBoxWidget *stream_cbw;
+			QVector<KsCheckBoxWidget *> cbws;
+			KsCheckBoxDialog *dialog;
+
+			stream_cbw = new KsDStreamCheckBoxWidget();
+			cbws.append(stream_cbw);
+			dialog = new KsCheckBoxDialog(cbws, this);
 
-	fileNames = KsUtils::getFiles(this, "Add KernelShark plugins",
-				     "KernelShark Plugins (*.so);;",
-				     _lastPluginFilePath);
+			auto lamStreams = [&streams] (int, QVector<int> s) {
+				streams = s;
+			};
 
-	if (!fileNames.isEmpty())
-		_plugins.addPlugins(fileNames);
+			connect(dialog, &KsCheckBoxDialog::apply, lamStreams);
+			dialog->exec();
+		}
+
+		_graph.startOfWork(KsDataWork::UpdatePlugins);
+
+		_plugins.addPlugins(fileNames, streams);
+		if (_data.size())
+			_data.reload();
+
+		_graph.endOfWork(KsDataWork::UpdatePlugins);
+	}
 }
 
 void KsMainWindow::_record()
@@ -950,13 +1176,25 @@ void KsMainWindow::_record()
 		message += " ./cmake_clean.sh <br> cmake .. <br> make <br>";
 		message += " sudo make install";
 
-		_error(message, "recordCantStart", false, false);
+		_error(message, "recordCantStart", false);
 		return;
 	}
 
 	_capture.start();
 }
 
+void KsMainWindow::_offset()
+{
+	KsTimeOffsetDialog *dialog = new KsTimeOffsetDialog(this);
+
+	auto lamApplyOffset = [&] (int sd, double ms) {
+		_data.setClockOffset(sd, ms * 1000);
+		_graph.update(&_data);
+	};
+
+	connect(dialog, &KsTimeOffsetDialog::apply, lamApplyOffset);
+}
+
 void KsMainWindow::_setColorPhase(int f)
 {
 	KsPlot::Color::setRainbowFrequency(f / 100.);
@@ -1005,14 +1243,13 @@ void KsMainWindow::_bugReport()
 	QDesktopServices::openUrl(bugs);
 }
 
-/** Load trace data for file. */
-void KsMainWindow::loadDataFile(const QString& fileName)
+void KsMainWindow::_load(const QString& fileName, bool append)
 {
-	char buff[FILENAME_MAX];
 	QString pbLabel("Loading    ");
 	bool loadDone = false;
 	struct stat st;
-	int ret;
+	double shift;
+	int ret, sd;
 
 	ret = stat(fileName.toStdString().c_str(), &st);
 	if (ret != 0) {
@@ -1020,16 +1257,21 @@ void KsMainWindow::loadDataFile(const QString& fileName)
 
 		text.append(fileName);
 		text.append(".");
-		_error(text, "loadDataErr1", true, true);
+		_error(text, "loadDataErr1", true);
 
 		return;
 	}
 
 	qInfo() << "Loading " << fileName;
 
-	_mState.reset();
-	_view.reset();
-	_graph.reset();
+	if (append) {
+		bool ok;
+		shift = KsTimeOffsetDialog::getValueNanoSec(fileName, &ok);
+		if (ok)
+			shift *= 1000.;
+		else
+			shift = 0.;
+	}
 
 	if (fileName.size() < 40) {
 		pbLabel += fileName;
@@ -1039,14 +1281,34 @@ void KsMainWindow::loadDataFile(const QString& fileName)
 	}
 
 	setWindowTitle("Kernel Shark");
-	KsProgressBar pb(pbLabel);
+	KsWidgetsLib::KsProgressBar pb(pbLabel);
 	QApplication::processEvents();
 
-	auto lamLoadJob = [&](KsDataStore *d) {
-		d->loadDataFile(fileName);
+	_view.reset();
+	_graph.reset();
+
+	auto lamLoadJob = [&, this] () {
+		QVector<kshark_dpi *> v;
+		for (auto const p: _plugins.getUserPlugins()) {
+			if (p->process_interface)
+				v.append(p->process_interface);
+		}
+
+		sd = _data.loadDataFile(fileName, v);
 		loadDone = true;
 	};
-	std::thread tload(lamLoadJob, &_data);
+
+	auto lamAppendJob = [&, this] () {
+		sd = _data.appendDataFile(fileName, shift);
+		loadDone = true;
+	};
+
+	std::thread job;
+	if (append) {
+		job = std::thread(lamAppendJob);
+	} else {
+		job = std::thread(lamLoadJob);
+	}
 
 	for (int i = 0; i < 160; ++i) {
 		/*
@@ -1060,33 +1322,40 @@ void KsMainWindow::loadDataFile(const QString& fileName)
 		usleep(150000);
 	}
 
-	tload.join();
+	job.join();
 
-	if (_data.size() < 1) {
-		QString text("No data was loaded from file ");
+	if (sd < 0 || !_data.size()) {
+		QString text("File ");
 
-		text.append(fileName + ".");
-		_error(text, "loadDataErr2", true, true);
-
-		return;
+		text.append(fileName);
+		text.append(" contains no data.");
+		_error(text, "loadDataErr2", true);
 	}
 
-	pb.setValue(165);
 	_view.loadData(&_data);
+	pb.setValue(175);
 
-	pb.setValue(180);
 	_graph.loadData(&_data);
 	pb.setValue(195);
+}
+
+/** Load trace data for file. */
+void KsMainWindow::loadDataFile(const QString& fileName)
+{
+	_mState.reset();
+	_load(fileName, false);
 	setWindowTitle("Kernel Shark (" + fileName + ")");
+}
 
-	if (realpath(fileName.toStdString().c_str(), buff)) {
-		QString path(buff);
-		_session.saveDataFile(path);
-	}
+/** Append trace data for file. */
+void KsMainWindow::appendDataFile(const QString& fileName)
+{
+	_load(fileName, true);
 }
 
-void KsMainWindow::_error(const QString &mesg, const QString &errCode,
-			  bool resize, bool unloadPlugins)
+void KsMainWindow::_error(const QString &mesg,
+			  const QString &errCode,
+			  bool resize)
 {
 	QErrorMessage *em = new QErrorMessage(this);
 	QString text = mesg;
@@ -1095,13 +1364,10 @@ void KsMainWindow::_error(const QString &mesg, const QString &errCode,
 	if (resize)
 		_resizeEmpty();
 
-	if (unloadPlugins)
-		_plugins.unloadAll();
-
 	text.replace("<br>", "\n", Qt::CaseInsensitive);
 	html.replace("\n", "<br>", Qt::CaseInsensitive);
 
-	qCritical().noquote() << "ERROR: " << text;
+	qCritical().noquote() << "ERROR:" << text;
 	em->showMessage(html, errCode);
 	em->exec();
 }
@@ -1114,6 +1380,7 @@ void KsMainWindow::_error(const QString &mesg, const QString &errCode,
 void KsMainWindow::loadSession(const QString &fileName)
 {
 	kshark_context *kshark_ctx(nullptr);
+	bool loadDone = false;
 	struct stat st;
 	int ret;
 
@@ -1126,59 +1393,70 @@ void KsMainWindow::loadSession(const QString &fileName)
 
 		text.append(fileName);
 		text.append("\n");
-		_error(text, "loadSessErr0", true, true);
+		_error(text, "loadSessErr0", true);
 
 		return;
 	}
 
+	KsWidgetsLib::KsProgressBar pb("Loading session settings ...");
+	pb.setValue(10);
+
+	_updateSessionSize = false;
 	if (!_session.importFromFile(fileName)) {
 		QString text("Unable to open session description file ");
 
 		text.append(fileName);
 		text.append(".\n");
-		_error(text, "loadSessErr1", true, true);
+		_error(text, "loadSessErr1", true);
 
 		return;
 	}
 
-	_session.loadPlugins(kshark_ctx, &_plugins);
+	_view.reset();
+	_graph.reset();
+	_data.clear();
 
-	QString dataFile(_session.getDataFile(kshark_ctx));
-	if (dataFile.isEmpty()) {
-		QString text("Unable to open trace data file for session ");
+	_session.loadUserPlugins(kshark_ctx, &_plugins);
+	pb.setValue(20);
 
-		text.append(fileName);
-		text.append("\n");
-		_error(text, "loadSessErr1", true, true);
+	auto lamLoadJob = [&] (KsDataStore *d) {
+		_session.loadDataStreams(kshark_ctx, &_data);
+		loadDone = true;
+	};
 
-		return;
-	}
+	std::thread job = std::thread(lamLoadJob, &_data);
 
-	loadDataFile(dataFile);
-	if (!_data.tep()) {
-		_plugins.unloadAll();
-		return;
+	for (int i = 0; i < 150; ++i) {
+		/*
+		 * TODO: The way this progress bar gets updated here is a pure
+		 * cheat. See if this can be implemented better.
+		*/
+		if (loadDone)
+			break;
+
+		pb.setValue(i);
+		usleep(300000);
 	}
 
-	KsProgressBar pb("Loading session settings ...");
-	pb.setValue(10);
+	job.join();
 
-	_session.loadGraphs(&_graph);
-	pb.setValue(20);
+	_view.loadData(&_data);
+	pb.setValue(155);
 
-	_session.loadFilters(kshark_ctx, &_data);
+	_graph.loadData(&_data);
 	_filterSyncCBoxUpdate(kshark_ctx);
-	pb.setValue(130);
+	pb.setValue(175);
 
 	_session.loadSplitterSize(&_splitter);
 	_session.loadMainWindowSize(this);
-	this->show();
-	pb.setValue(140);
+	_updateSessionSize = true;
+	pb.setValue(180);
 
 	_session.loadDualMarker(&_mState, &_graph);
 	_session.loadVisModel(_graph.glPtr()->model());
 	_mState.updateMarkers(_data, _graph.glPtr());
-	pb.setValue(170);
+	_session.loadGraphs(kshark_ctx, _graph);
+	pb.setValue(190);
 
 	_session.loadTable(&_view);
 	_colorPhaseSlider.setValue(_session.getColorScheme() * 100);
@@ -1268,7 +1546,7 @@ void KsMainWindow::_captureErrorMessage(QProcess *capture)
 	message += capture->errorString();
 	message += "<br>Standard Error: ";
 	message += capture->readAllStandardError();
-	_error(message, "captureFinishedErr", false, false);
+	_error(message, "captureFinishedErr", false);
 }
 
 void KsMainWindow::_readSocket()
@@ -1280,7 +1558,7 @@ void KsMainWindow::_readSocket()
 	auto lamSocketError = [&](QString message)
 	{
 		message = "ERROR from Local Server: " + message;
-		_error(message, "readSocketErr", false, false);
+		_error(message, "readSocketErr", false);
 	};
 
 	socket = _captureLocalServer.nextPendingConnection();
diff --git a/src/KsMainWindow.hpp b/src/KsMainWindow.hpp
index 2fac107..952a2ad 100644
--- a/src/KsMainWindow.hpp
+++ b/src/KsMainWindow.hpp
@@ -20,6 +20,7 @@
 #include "KsTraceViewer.hpp"
 #include "KsTraceGraph.hpp"
 #include "KsWidgetsLib.hpp"
+#include "KsPlugins.hpp"
 #include "KsSession.hpp"
 #include "KsUtils.hpp"
 
@@ -36,35 +37,61 @@ public:
 
 	void loadDataFile(const QString &fileName);
 
+	void appendDataFile(const QString &fileName);
+
 	void loadSession(const QString &fileName);
 
 	QString lastSessionFile();
 
 	/**
-	 * @brief
+	 * @brief Register a list of plugins.
+	 *
+	 * @param plugins: Provide here the names of the plugin (as in the
+ *			   CMake-generated header file) or the names of the
+ *			   plugin's library files (.so including path).
+ * 			   The names must be comma separated.
+	 */
+	void registerPlugins(const QString &plugins)
+	{
+		_plugins.registerPlugins(plugins);
+	}
+
+	/**
+	 * @brief Unregister a list pf plugins.
+	 *
+	 * @param pluginNames: Provide here a comma separated list of plugin
+	 *		       names (as in the CMake-generated header file).
+	 */
+	void unregisterPlugins(const QString &pluginNames)
+	{
+		_plugins.unregisterPlugins(pluginNames);
+	}
+
+	/**
+	 * @brief Register a given plugin to given Data streams.
 	 *
-	 * @param plugin: can be the name of the plugin or the plugin's library
-	 * file (including absolute or relative path).
+	 * @param pluginName: The name of the plugin to register.
+	 * @param streamIds: Vector of Data stream identifiers.
 	 */
-	void registerPlugin(const QString &plugin)
+	void registerPluginToStream(const QString &pluginName, QVector<int> streamIds)
 	{
-		_plugins.registerPlugin(plugin);
+		_plugins.registerPluginToStream(pluginName, streamIds);
 	}
 
 	/**
-	 * @brief
+	 * @brief Unregister a given plugin from given Data streams.
 	 *
-	 * @param plugin: can be the name of the plugin or the plugin's library
-	 * file (including absolute path).
+	 * @param pluginName: The name of the plugin to unregister.
+	 * @param streamIds: Vector of Data stream identifiers.
 	 */
-	void unregisterPlugin(const QString &plugin)
+	void unregisterPluginFromStream(const QString &pluginName, QVector<int> streamIds)
 	{
-		_plugins.unregisterPlugin(plugin);
+		_plugins.unregisterPluginFromStream(pluginName, streamIds);
 	}
 
-	void setCPUPlots(QVector<int> cpus);
+	void setCPUPlots(int sd, QVector<int> cpus);
 
-	void setTaskPlots(QVector<int> pids);
+	void setTaskPlots(int sd, QVector<int> pids);
 
 	void resizeEvent(QResizeEvent* event);
 
@@ -74,6 +101,21 @@ public:
 			_changeScreenMode();
 	}
 
+	void addPluginMenu(QString place, pluginActionFunc action);
+
+	/** Get the KsTraceGraph object. */
+	KsTraceGraph *graphPtr() {return &_graph;}
+
+	/** Get the KsTraceViewer object. */
+	KsTraceViewer *viewPtr() {return &_view;}
+
+	/** Get the KsWorkInProgress object. */
+	KsWidgetsLib::KsWorkInProgress *wipPtr() {return &_workInProgress;}
+
+	void markEntry(ssize_t row, DualMarkerState st);
+
+	void markEntry(const kshark_entry *e, DualMarkerState st);
+
 private:
 	QSplitter	_splitter;
 
@@ -104,6 +146,8 @@ private:
 	// File menu.
 	QAction		_openAction;
 
+	QAction		_appendAction;
+
 	QAction		_restoreSessionAction;
 
 	QAction		_importSessionAction;
@@ -113,10 +157,6 @@ private:
 	QAction		_quitAction;
 
 	// Filter menu.
-	QAction		_importFilterAction;
-
-	QAction		_exportFilterAction;
-
 	QCheckBox	*_graphFilterSyncCBox;
 
 	QCheckBox	*_listFilterSyncCBox;
@@ -143,6 +183,8 @@ private:
 
 	QAction		_captureAction;
 
+	QAction		_addOffcetAction;
+
 	QWidgetAction	_colorAction;
 
 	QWidget		_colSlider;
@@ -166,29 +208,34 @@ private:
 
 	QMetaObject::Connection		_captureErrorConnection;
 
+	// Status bar.
+	KsWidgetsLib::KsWorkInProgress	_workInProgress;
+
+	bool	_updateSessionSize;
+
+	void _load(const QString& fileName, bool append);
+
 	void _open();
 
+	void _append();
+
 	void _restoreSession();
 
 	void _importSession();
 
 	void _exportSession();
 
-	void _importFilter();
-
-	void _exportFilter();
-
 	void _listFilterSync(int state);
 
 	void _graphFilterSync(int state);
 
-	void _presetCBWidget(tracecmd_filter_id *showFilter,
-			     tracecmd_filter_id *hideFilter,
-			     KsCheckBoxWidget *cbw);
+	void _presetCBWidget(kshark_hash_id *showFilter,
+			     kshark_hash_id *hideFilter,
+			     KsWidgetsLib::KsCheckBoxWidget *cbw);
 
-	void _applyFilter(QVector<int> all, QVector<int> show,
-			  std::function<void(QVector<int>)> posFilter,
-			  std::function<void(QVector<int>)> negFilter);
+	void _applyFilter(int sd, QVector<int> all, QVector<int> show,
+			  std::function<void(int, QVector<int>)> posFilter,
+			  std::function<void(int, QVector<int>)> negFilter);
 
 	void _showEvents();
 
@@ -206,10 +253,14 @@ private:
 
 	void _pluginSelect();
 
+	void _pluginUpdate(int sd, QVector<int> pluginStates);
+
 	void _pluginAdd();
 
 	void _record();
 
+	void _offset();
+
 	void _setColorPhase(int);
 
 	void _changeScreenMode();
@@ -240,8 +291,9 @@ private:
 
 	inline void _resizeEmpty() {resize(SCREEN_WIDTH * .5, FONT_HEIGHT * 3);}
 
-	void _error(const QString &text, const QString &errCode,
-		    bool resize, bool unloadPlugins);
+	void _error(const QString &text,
+		    const QString &errCode,
+		    bool resize);
 
 	void _deselectActive();
 
diff --git a/src/KsPlugins.hpp b/src/KsPlugins.hpp
index a19bb9d..d41d094 100644
--- a/src/KsPlugins.hpp
+++ b/src/KsPlugins.hpp
@@ -16,9 +16,14 @@
 #include <functional>
 
 // KernelShark
+#include "libkshark-plugin.h"
 #include "libkshark-model.h"
 #include "KsPlotTools.hpp"
 
+class KsMainWindow;
+/** Function type used for launching of plugin control menus. */
+typedef void (pluginActionFunc) (KsMainWindow *);
+
 /**
  * Structure representing the vector of C++ arguments of the drawing function
  * of a plugin.
diff --git a/src/KsSession.cpp b/src/KsSession.cpp
index 786aa3e..8d489f7 100644
--- a/src/KsSession.cpp
+++ b/src/KsSession.cpp
@@ -13,6 +13,7 @@
 #include "libkshark.h"
 #include "libkshark-tepdata.h"
 #include "KsSession.hpp"
+#include "KsMainWindow.hpp"
 
 /** Create a KsSession object. */
 KsSession::KsSession()
@@ -193,6 +194,39 @@ void KsSession::saveMainWindowSize(const QMainWindow &window)
 	kshark_config_doc_add(_config, "MainWindow", windowConf);
 }
 
+/**
+ * @brief Load the KernelShark Main window size.
+ *
+ * @param window: Input location for the KsMainWindow widget.
+ */
+void KsSession::loadMainWindowSize(KsMainWindow *window)
+{
+	kshark_config_doc *windowConf = kshark_config_alloc(KS_CONFIG_JSON);
+	json_object *jwindow, *jwidth, *jheight;
+	int width, height;
+
+	if (!kshark_config_doc_get(_config, "MainWindow", windowConf))
+		return;
+
+	if (_config->format == KS_CONFIG_JSON) {
+		jwindow = KS_JSON_CAST(windowConf->conf_doc);
+		if (json_object_get_type(jwindow) == json_type_string &&
+		    QString(json_object_get_string(jwindow)) == "FullScreen") {
+			window->setFullScreenMode(true);
+			return;
+		}
+
+		jwidth = json_object_array_get_idx(jwindow, 0);
+		jheight = json_object_array_get_idx(jwindow, 1);
+
+		width = json_object_get_int(jwidth);
+		height = json_object_get_int(jheight);
+
+		window->setFullScreenMode(false);
+		window->resize(width, height);
+	}
+}
+
 /**
  * @brief Save the state of the Main window spliter.
  *
diff --git a/src/KsSession.hpp b/src/KsSession.hpp
index e1b7fd4..fd45270 100644
--- a/src/KsSession.hpp
+++ b/src/KsSession.hpp
@@ -20,6 +20,8 @@
 #include "KsTraceGraph.hpp"
 #include "KsTraceViewer.hpp"
 
+class KsMainWindow;
+
 /**
  * The KsSession class provides instruments for importing/exporting the state
  * of the different components of the GUI from/to Json documents. These
@@ -60,6 +62,8 @@ public:
 
 	void saveMainWindowSize(const QMainWindow &window);
 
+	void loadMainWindowSize(KsMainWindow *window);
+
 	void saveSplitterSize(const QSplitter &splitter);
 
 	void loadSplitterSize(QSplitter *splitter);
diff --git a/src/kernelshark.cpp b/src/kernelshark.cpp
index 0de80af..41ffbe7 100644
--- a/src/kernelshark.cpp
+++ b/src/kernelshark.cpp
@@ -17,14 +17,16 @@
 
 #define default_input_file (char*)"trace.dat"
 
-static char *input_file;
+static char *prior_input_file;
+QStringList appInputFiles;
 
 void usage(const char *prog)
 {
 	printf("Usage: %s\n", prog);
 	printf("  -h	Display this help message\n");
 	printf("  -v	Display version and exit\n");
-	printf("  -i	input_file, default is %s\n", default_input_file);
+	printf("  -i	prior input file, default is %s\n", default_input_file);
+	printf("  -a	input file to append to the prior\n");
 	printf("  -p	register plugin, use plugin name, absolute or relative path\n");
 	printf("  -u	unregister plugin, use plugin name or absolute path\n");
 	printf("  -s	import a session\n");
@@ -45,16 +47,18 @@ static option longOptions[] = {
 
 int main(int argc, char **argv)
 {
-	QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
-	QApplication a(argc, argv);
-
 	QVector<int> cpuPlots, taskPlots;
 	bool fromSession = false;
 	int optionIndex = 0;
-	KsMainWindow ks;
 	int c;
 
-	while ((c = getopt_long(argc, argv, "hvi:p:u:s:l",
+	QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
+	QApplication a(argc, argv);
+
+	KsMainWindow ks;
+	ks.show();
+
+	while ((c = getopt_long(argc, argv, "hvi:a:p:u:s:l",
 					    longOptions,
 					    &optionIndex)) != -1) {
 		switch(c) {
@@ -75,15 +79,19 @@ int main(int argc, char **argv)
 			return 0;
 
 		case 'i':
-			input_file = optarg;
+			prior_input_file = optarg;
+			break;
+
+		case 'a':
+			appInputFiles << QString(optarg).split(" ", QString::SkipEmptyParts);
 			break;
 
 		case 'p':
-			ks.registerPlugin(QString(optarg));
+			ks.registerPlugins(QString(optarg));
 			break;
 
 		case 'u':
-			ks.unregisterPlugin(QString(optarg));
+			ks.unregisterPlugins(QString(optarg));
 			break;
 
 		case 's':
@@ -103,19 +111,22 @@ int main(int argc, char **argv)
 
 	if (!fromSession) {
 		if ((argc - optind) >= 1) {
-			if (input_file)
+			if (prior_input_file)
 				usage(argv[0]);
-			input_file = argv[optind];
+			prior_input_file = argv[optind];
 		}
 
-		if (!input_file) {
+		if (!prior_input_file) {
 			struct stat st;
 			if (stat(default_input_file, &st) == 0)
-				input_file = default_input_file;
+				prior_input_file = default_input_file;
 		}
 
-		if (input_file)
-			ks.loadDataFile(QString(input_file));
+		if (prior_input_file)
+			ks.loadDataFile(QString(prior_input_file));
+
+		for (auto const &f: appInputFiles)
+			ks.appendDataFile(f);
 	}
 
 	auto lamOrderIds = [] (QVector<int> &ids) {
@@ -126,10 +137,10 @@ int main(int argc, char **argv)
 	};
 
 	if (cpuPlots.count() || taskPlots.count()) {
-		ks.setCPUPlots(lamOrderIds(cpuPlots));
-		ks.setTaskPlots(lamOrderIds(taskPlots));
+		ks.setCPUPlots(0, lamOrderIds(cpuPlots));
+		ks.setTaskPlots(0, lamOrderIds(taskPlots));
 	}
 
-	ks.show();
+	ks.raise();
 	return a.exec();
 }
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v2 24/27] kernel-shark: Clickable sched_event plugin shapes
  2021-02-11 10:31 [PATCH v2 00/27] Complete the KernelShark v2 transformation Yordan Karadzhov (VMware)
                   ` (22 preceding siblings ...)
  2021-02-11 10:32 ` [PATCH v2 23/27] kernel-shark: Update KsMainWindow and kernelshark.cpp Yordan Karadzhov (VMware)
@ 2021-02-11 10:32 ` Yordan Karadzhov (VMware)
  2021-02-11 10:32 ` [PATCH v2 25/27] kernel-shark: Show Task plots from command lime Yordan Karadzhov (VMware)
                   ` (3 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Yordan Karadzhov (VMware) @ 2021-02-11 10:32 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

Here we make the latency boxes plotted by the sched_event plugin
clickable. When the box is clicked, the two events determining
the latency (sched_waking and sched_switch) are selected with the
markers. This is achieved by providing an implementation of the
corresponding interface of virtual functions in the definition
of the LatencyBox class. We also need to provide the plugin with
a pointer to the KsMainWindow object (the GUI itself) such that
the plugin can manipulate the markers.
---
 src/plugins/CMakeLists.txt  | 30 +++++++++++++++++--
 src/plugins/SchedEvents.cpp | 58 +++++++++++++++++++++++++++++++++++--
 src/plugins/sched_events.c  |  7 +++++
 src/plugins/sched_events.h  |  3 ++
 4 files changed, 93 insertions(+), 5 deletions(-)

diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt
index dc0c320..5e28368 100644
--- a/src/plugins/CMakeLists.txt
+++ b/src/plugins/CMakeLists.txt
@@ -17,10 +17,34 @@ function(BUILD_PLUGIN)
 
 endfunction()
 
+function(BUILD_GUI_PLUGIN)
+    set(options)
+    set(oneValueArgs NAME MOC)
+    set(multiValueArgs SOURCE)
+    cmake_parse_arguments(ADD_PLUGIN "${options}"
+                                     "${oneValueArgs}"
+                                     "${multiValueArgs}"
+                                     ${ARGN})
+
+    message(STATUS ${ADD_PLUGIN_NAME})
+
+    QT5_WRAP_CPP(plugin_moc ${ADD_PLUGIN_MOC})
+
+    add_library(${ADD_PLUGIN_NAME} SHARED ${plugin_moc} ${ADD_PLUGIN_SOURCE})
+    set_target_properties(${ADD_PLUGIN_NAME} PROPERTIES PREFIX "plugin-")
+    target_link_libraries(${ADD_PLUGIN_NAME} kshark kshark-gui)
+
+endfunction()
+
 set(PLUGIN_LIST "")
-BUILD_PLUGIN(NAME sched_events
-             SOURCE sched_events.c SchedEvents.cpp)
-list(APPEND PLUGIN_LIST "sched_events")
+
+if (Qt5Widgets_FOUND AND TT_FONT_FILE)
+
+    BUILD_GUI_PLUGIN(NAME sched_events
+                     SOURCE sched_events.c SchedEvents.cpp)
+    list(APPEND PLUGIN_LIST "sched_events")
+
+endif (Qt5Widgets_FOUND AND TT_FONT_FILE)
 
 BUILD_PLUGIN(NAME missed_events
              SOURCE missed_events.c MissedEvents.cpp)
diff --git a/src/plugins/SchedEvents.cpp b/src/plugins/SchedEvents.cpp
index c85a059..a81182e 100644
--- a/src/plugins/SchedEvents.cpp
+++ b/src/plugins/SchedEvents.cpp
@@ -20,15 +20,69 @@
 #include "plugins/sched_events.h"
 #include "KsPlotTools.hpp"
 #include "KsPlugins.hpp"
+#include "KsMainWindow.hpp"
 
 using namespace KsPlot;
 
+static KsMainWindow *ks_ptr;
+
+/**
+ * @brief Provide the plugin with a pointer to the KsMainWindow object (the GUI
+ * itself) such that the plugin can manipulate the GUI.
+ */
+void *plugin_set_gui_ptr(void *gui_ptr)
+{
+	ks_ptr = static_cast<KsMainWindow *>(gui_ptr);
+	return nullptr;
+}
+
+/**
+ * This class represents the graphical element visualizing the latency between
+ *  sched_waking and sched_switch events.
+ */
+class LatencyBox : public Rectangle
+{
+	/** On double click do. */
+	void _doubleClick() const override
+	{
+		ks_ptr->markEntry(_data[1]->entry, DualMarkerState::B);
+		ks_ptr->markEntry(_data[0]->entry, DualMarkerState::A);
+	}
+
+public:
+	/** The trace record data that corresponds to this LatencyBox. */
+	std::vector<kshark_data_field_int64 *>	_data;
+
+	/**
+	 * @brief Distance between the click and the shape. Used to decide if
+	 *	  the double click action must be executed.
+	 *
+	 * @param x: X coordinate of the click.
+	 * @param y: Y coordinate of the click.
+	 *
+	 * @returns If the click is inside the box, the distance is zero.
+	 *	    Otherwise infinity.
+	 */
+	double distance(int x, int y) const override
+	{
+		if (x < pointX(0) || x > pointX(2))
+			return std::numeric_limits<double>::max();
+
+		if (y < pointY(0) || y > pointY(1))
+			return std::numeric_limits<double>::max();
+
+		return 0;
+	}
+};
+
 static PlotObject *makeShape(std::vector<const Graph *> graph,
 			     std::vector<int> bins,
-			     std::vector<kshark_data_field_int64 *>,
+			     std::vector<kshark_data_field_int64 *> data,
 			     Color col, float size)
 {
-	Rectangle *rec = new KsPlot::Rectangle;
+	LatencyBox *rec = new LatencyBox;
+	rec->_data = data;
+
 	Point p0 = graph[0]->bin(bins[0])._base;
 	Point p1 = graph[0]->bin(bins[1])._base;
 	int height = graph[0]->height() * .3;
diff --git a/src/plugins/sched_events.c b/src/plugins/sched_events.c
index 1596880..ac4a7bf 100644
--- a/src/plugins/sched_events.c
+++ b/src/plugins/sched_events.c
@@ -216,3 +216,10 @@ int KSHARK_PLOT_PLUGIN_DEINITIALIZER(struct kshark_data_stream *stream)
 
 	return 1;
 }
+
+/** Initialize the control interface of the plugin. */
+void *KSHARK_MENU_PLUGIN_INITIALIZER(void *gui_ptr)
+{
+	printf("--> sched init menu\n");
+	return plugin_set_gui_ptr(gui_ptr);
+}
diff --git a/src/plugins/sched_events.h b/src/plugins/sched_events.h
index 4c57606..78cfda0 100644
--- a/src/plugins/sched_events.h
+++ b/src/plugins/sched_events.h
@@ -53,6 +53,7 @@ struct plugin_sched_context {
 	struct kshark_data_container	*sw_data;
 };
 
+/** A general purpose macro is used to define plugin context. */
 KS_DEFINE_PLUGIN_CONTEXT(struct plugin_sched_context);
 
 /** The type of the data field stored in the kshark_data_container object. */
@@ -65,6 +66,8 @@ int plugin_sched_get_prev_state(ks_num_field_t field);
 void plugin_draw(struct kshark_cpp_argv *argv, int sd, int pid,
 		 int draw_action);
 
+void *plugin_set_gui_ptr(void *gui_ptr);
+
 #ifdef __cplusplus
 }
 #endif
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v2 25/27] kernel-shark: Show Task plots from command lime
  2021-02-11 10:31 [PATCH v2 00/27] Complete the KernelShark v2 transformation Yordan Karadzhov (VMware)
                   ` (23 preceding siblings ...)
  2021-02-11 10:32 ` [PATCH v2 24/27] kernel-shark: Clickable sched_event plugin shapes Yordan Karadzhov (VMware)
@ 2021-02-11 10:32 ` Yordan Karadzhov (VMware)
  2021-02-11 10:32 ` [PATCH v2 26/27] kernel-shark: Add pkg-config configuration for libkshark Yordan Karadzhov (VMware)
                   ` (2 subsequent siblings)
  27 siblings, 0 replies; 29+ messages in thread
From: Yordan Karadzhov (VMware) @ 2021-02-11 10:32 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

We add a new command line option for starting KernelShark that allows
the user to select Task plots to be shown. The new option is similar
to "--pid" but the tasks can be specified by name.

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 src/KsUtils.cpp               | 32 ++++++++++++++++++++++++++++++++
 src/KsUtils.hpp               |  2 ++
 src/kernelshark.cpp           | 17 +++++++++++++----
 tests/libkshark-gui-tests.cpp | 25 +++++++++++++++++++++++++
 4 files changed, 72 insertions(+), 4 deletions(-)

diff --git a/src/KsUtils.cpp b/src/KsUtils.cpp
index 27cda55..ec53267 100644
--- a/src/KsUtils.cpp
+++ b/src/KsUtils.cpp
@@ -548,6 +548,38 @@ QVector<int> parseIdList(QString v_str)
 	return v;
 }
 
+/**
+ * @brief Convert a string containing task names into a list of PID numbers.
+ */
+QMap<int, QVector<int>> parseTaskList(QString v_str)
+{
+	QStringList taskList = v_str.split(",", QString::SkipEmptyParts);
+	QVector<int> streamIds, allPids;
+	kshark_context *kshark_ctx(nullptr);
+	QMap<int, QVector<int>> ret;
+	QString name;
+
+	if (!kshark_instance(&kshark_ctx))
+		return {};
+
+	streamIds = getStreamIdList(kshark_ctx);
+	for (auto const sd: streamIds) {
+		allPids = getPidList(sd);
+		for (auto const pid: allPids) {
+			name = kshark_comm_from_pid(sd, pid);
+			if (name.isEmpty())
+				continue;
+
+			for (auto const task: taskList) {
+				if(name == task)
+					ret[sd].append(pid);
+			}
+		}
+	}
+
+	return ret;
+}
+
 /**
  * @brief Split the ststem name from the actual name of the event itself.
  *
diff --git a/src/KsUtils.hpp b/src/KsUtils.hpp
index 0d2c9c3..cf209bc 100644
--- a/src/KsUtils.hpp
+++ b/src/KsUtils.hpp
@@ -161,6 +161,8 @@ QStringList splitArguments(QString cmd);
 
 QVector<int> parseIdList(QString v_str);
 
+QMap<int, QVector<int>> parseTaskList(QString v_str);
+
 QStringList getTepEvtName(int sd, int eventId);
 
 /** Get a string to be used as a standard name of a CPU graph. */
diff --git a/src/kernelshark.cpp b/src/kernelshark.cpp
index 41ffbe7..8ed4948 100644
--- a/src/kernelshark.cpp
+++ b/src/kernelshark.cpp
@@ -32,7 +32,8 @@ void usage(const char *prog)
 	printf("  -s	import a session\n");
 	printf("  -l	import the last session\n");
 	puts(" --cpu	show plots for CPU cores, default is \"show all\"");
-	puts(" --pid	show plots for tasks, default is \"do not show\"");
+	puts(" --pid	show plots for tasks (by PID), default is \"do not show\"");
+	puts(" --task	show plots for tasks (by name), default is \"do not show\"");
 	puts("\n example:");
 	puts("  kernelshark -i mytrace.dat --cpu 1,4-7 --pid 11 -p path/to/my/plugin/myplugin.so\n");
 }
@@ -42,6 +43,7 @@ static option longOptions[] = {
 	{"help", no_argument, nullptr, 'h'},
 	{"pid", required_argument, nullptr, KS_LONG_OPTS},
 	{"cpu", required_argument, nullptr, KS_LONG_OPTS},
+	{"task", required_argument, nullptr, KS_LONG_OPTS},
 	{nullptr, 0, nullptr, 0}
 };
 
@@ -50,6 +52,7 @@ int main(int argc, char **argv)
 	QVector<int> cpuPlots, taskPlots;
 	bool fromSession = false;
 	int optionIndex = 0;
+	QString taskList;
 	int c;
 
 	QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
@@ -67,7 +70,8 @@ int main(int argc, char **argv)
 				cpuPlots.append(KsUtils::parseIdList(QString(optarg)));
 			else if (strcmp(longOptions[optionIndex].name, "pid") == 0)
 				taskPlots.append(KsUtils::parseIdList(QString(optarg)));
-
+			else if (strcmp(longOptions[optionIndex].name, "task") == 0)
+				taskList = QString(optarg);
 			break;
 
 		case 'h':
@@ -136,9 +140,14 @@ int main(int argc, char **argv)
 		return ids;
 	};
 
-	if (cpuPlots.count() || taskPlots.count()) {
+	if (cpuPlots.count() || taskPlots.count() || taskList.size()) {
 		ks.setCPUPlots(0, lamOrderIds(cpuPlots));
-		ks.setTaskPlots(0, lamOrderIds(taskPlots));
+
+		auto pidMap = KsUtils::parseTaskList(taskList);
+		pidMap[0].append(taskPlots);
+		for (auto it = pidMap.begin(); it != pidMap.end(); ++it) {
+			ks.setTaskPlots(it.key(), lamOrderIds(it.value()));
+		}
 	}
 
 	ks.raise();
diff --git a/tests/libkshark-gui-tests.cpp b/tests/libkshark-gui-tests.cpp
index 5a0ca01..bc49194 100644
--- a/tests/libkshark-gui-tests.cpp
+++ b/tests/libkshark-gui-tests.cpp
@@ -286,3 +286,28 @@ BOOST_AUTO_TEST_CASE(GraphModel)
 	model.reset();
 	BOOST_CHECK_EQUAL(model.rowCount({}), 0);
 }
+
+BOOST_AUTO_TEST_CASE(KsUtils_parseTasks)
+{
+	QVector<int> pids{28121, 28137, 28141, 28199, 28201, 205666, 267481};
+	kshark_context *kshark_ctx{nullptr};
+	kshark_entry **data{nullptr};
+	std::string file(KS_TEST_DIR);
+	ssize_t n_rows;
+	int sd;
+
+	kshark_instance(&kshark_ctx);
+	file += "/trace_test1.dat";
+	sd = kshark_open(kshark_ctx, file.c_str());
+	n_rows = kshark_load_entries(kshark_ctx, sd, &data);
+
+	auto pids_test = parseTaskList("zoom,sleep");
+	BOOST_CHECK(pids == pids_test[0]);
+
+	for (ssize_t r = 0; r < n_rows; ++r)
+		free(data[r]);
+	free(data);
+
+	kshark_close(kshark_ctx, sd);
+	kshark_free(kshark_ctx);
+}
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v2 26/27] kernel-shark: Add pkg-config configuration for libkshark
  2021-02-11 10:31 [PATCH v2 00/27] Complete the KernelShark v2 transformation Yordan Karadzhov (VMware)
                   ` (24 preceding siblings ...)
  2021-02-11 10:32 ` [PATCH v2 25/27] kernel-shark: Show Task plots from command lime Yordan Karadzhov (VMware)
@ 2021-02-11 10:32 ` Yordan Karadzhov (VMware)
  2021-02-11 10:32 ` [PATCH v2 27/27] kernel-shark: Install libkshark-tepdata.h Yordan Karadzhov (VMware)
  2021-02-11 15:02 ` [PATCH v2 00/27] Complete the KernelShark v2 transformation Steven Rostedt
  27 siblings, 0 replies; 29+ messages in thread
From: Yordan Karadzhov (VMware) @ 2021-02-11 10:32 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

Add auto-generated pkg-config setup file to install for pkg-config.
To be used for building against libkshark.

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 CMakeLists.txt           |  3 +++
 build/cmake_clean.sh     |  1 +
 build/libkshark.pc.cmake | 10 ++++++++++
 src/CMakeLists.txt       |  6 ++++++
 4 files changed, 20 insertions(+)
 create mode 100644 build/libkshark.pc.cmake

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8ca33fd..efcccb1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -167,4 +167,7 @@ configure_file(${KS_DIR}/build/ks.desktop.cmake
 configure_file(${KS_DIR}/build/org.freedesktop.kshark-record.policy.cmake
                ${KS_DIR}/org.freedesktop.kshark-record.policy)
 
+configure_file(${KS_DIR}/build/libkshark.pc.cmake
+               ${KS_DIR}/libkshark.pc @ONLY)
+
 message("")
diff --git a/build/cmake_clean.sh b/build/cmake_clean.sh
index 2ca1136..d645c32 100755
--- a/build/cmake_clean.sh
+++ b/build/cmake_clean.sh
@@ -12,6 +12,7 @@ rm -rf Testing/
 rm -f ../tests/*.dat
 rm -f ../lib/*
 rm ../kernelshark.desktop
+rm ../libkshark.pc
 rm ../org.freedesktop.kshark-record.policy
 rm -f ../src/KsCmakeDef.hpp
 rm -f CMakeDoxyfile.in
diff --git a/build/libkshark.pc.cmake b/build/libkshark.pc.cmake
new file mode 100644
index 0000000..ad4ce34
--- /dev/null
+++ b/build/libkshark.pc.cmake
@@ -0,0 +1,10 @@
+prefix=@_INSTALL_PREFIX@
+libdir=@_LIBDIR@
+includedir=${prefix}/include/@KS_APP_NAME@
+
+Name: libkshark
+URL: https://git.kernel.org/pub/scm/utils/trace-cmd/kernel-shark.git/
+Description: Library for accessing ftrace file system
+Version: @KS_VERSION_STRING@
+Cflags: -I${includedir}
+Libs: -L${libdir} -lkshark
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 4158901..6a6eda1 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -133,6 +133,12 @@ if (Qt5Widgets_FOUND AND Qt5Network_FOUND AND TT_FONT_FILE)
             DESTINATION ${_INSTALL_PREFIX}/bin/
                 COMPONENT                 kernelshark)
 
+    execute_process(COMMAND  bash "-c" "pkg-config --variable pc_path pkg-config | cut -f 1 -d: -z"
+                    OUTPUT_VARIABLE PKG_CONGIG_DIR)
+    install(FILES "${KS_DIR}/libkshark.pc"
+            DESTINATION ${PKG_CONGIG_DIR}
+                COMPONENT                 libkshark-devel)
+
 endif (Qt5Widgets_FOUND AND Qt5Network_FOUND AND TT_FONT_FILE)
 
 add_subdirectory(plugins)
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* [PATCH v2 27/27] kernel-shark: Install libkshark-tepdata.h
  2021-02-11 10:31 [PATCH v2 00/27] Complete the KernelShark v2 transformation Yordan Karadzhov (VMware)
                   ` (25 preceding siblings ...)
  2021-02-11 10:32 ` [PATCH v2 26/27] kernel-shark: Add pkg-config configuration for libkshark Yordan Karadzhov (VMware)
@ 2021-02-11 10:32 ` Yordan Karadzhov (VMware)
  2021-02-11 15:02 ` [PATCH v2 00/27] Complete the KernelShark v2 transformation Steven Rostedt
  27 siblings, 0 replies; 29+ messages in thread
From: Yordan Karadzhov (VMware) @ 2021-02-11 10:32 UTC (permalink / raw)
  To: rostedt; +Cc: linux-trace-devel, Yordan Karadzhov (VMware)

The header wull be installed as part of the "libkshark-devel" component.
It can be useful for applications dealing with data from trace-cmd. In
particular, the header is needed whene building trace-cruncher.

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 src/CMakeLists.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 6a6eda1..1e86e9c 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -34,6 +34,7 @@ install(TARGETS kshark
 install(FILES "${KS_DIR}/src/libkshark.h"
               "${KS_DIR}/src/libkshark-model.h"
               "${KS_DIR}/src/libkshark-plugin.h"
+              "${KS_DIR}/src/libkshark-tepdata.h"
         DESTINATION ${KS_INCLUDS_DESTINATION}
             COMPONENT libkshark-devel)
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 29+ messages in thread

* Re: [PATCH v2 00/27] Complete the KernelShark v2 transformation
  2021-02-11 10:31 [PATCH v2 00/27] Complete the KernelShark v2 transformation Yordan Karadzhov (VMware)
                   ` (26 preceding siblings ...)
  2021-02-11 10:32 ` [PATCH v2 27/27] kernel-shark: Install libkshark-tepdata.h Yordan Karadzhov (VMware)
@ 2021-02-11 15:02 ` Steven Rostedt
  27 siblings, 0 replies; 29+ messages in thread
From: Steven Rostedt @ 2021-02-11 15:02 UTC (permalink / raw)
  To: Yordan Karadzhov (VMware); +Cc: linux-trace-devel

On Thu, 11 Feb 2021 12:31:38 +0200
"Yordan Karadzhov (VMware)" <y.karadz@gmail.com> wrote:

> With this patch-set we complete the destructive part of the transformation
> of KernelShark towards v2 and the GUI is finally fully functional again.
> 
> v2 changes:
>   * Fixing the bug in patch
>     "kernel-shark: Update KsDualMarker and KsGLWidget",
>     causing the error "Unable to find FreeSans font".
> 
>   * New patches:
>     - kernel-shark: Search for font with Cmake at pre-build
>     - kernel-shark: Add pkg-config configuration for libkshark
>     - kernel-shark: Install libkshark-tepdata.h
> 
> 
> Yordan Karadzhov (VMware) (27):
>   kernel-shark: Add get_stream_object()
>   kernel-shark: Do proper reset in kshark_close_all()
>   kernel-shark: Restore the counting of event handlers
>   kernel-shark: Fix a misleading comment
>   kernel-shark: Count the number of readout interfaces
>   kernel-shark: Update KsUtils
>   kernel-shark: Update KsModels and KsSearchFSM
>   kernel-shark: Add trace data files for CI testing
>   kernel-shark: Add plugin tests
>   kernel-shark: Add model tests
>   kernel-shark: Update KsWidgetsLib
>   kernel-shark: Add combo point to Mark
>   kernel-shark: Add new methods to KsPlot::Mark
>   kernel-shark: Update the plotting example
>   kernel-shark: Search for font with Cmake at pre-build
>   kernel-shark: Update KsDualMarker and KsGLWidget
>   kernel-shark: Update KsTraceGraph and KsQuickContextMenu
>   kernel-shark: Update KsTraceViewer
>   kernel-shark: Update KsAdvFilteringDialog
>   kernel-shark: Update KsCaptureDialog
>   kernel-shark: Update KsSession
>   kernel-shark: Update MissedEvents plugin
>   kernel-shark: Update KsMainWindow and kernelshark.cpp
>   kernel-shark: Clickable sched_event plugin shapes
>   kernel-shark: Show Task plots from command lime
>   kernel-shark: Add pkg-config configuration for libkshark
>   kernel-shark: Install libkshark-tepdata.h
> 
> 

Acked-by: Steven Rostedt (VMware) <rostedt@goodmis.org>

-- Steve

^ permalink raw reply	[flat|nested] 29+ messages in thread

end of thread, other threads:[~2021-02-11 15:29 UTC | newest]

Thread overview: 29+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-02-11 10:31 [PATCH v2 00/27] Complete the KernelShark v2 transformation Yordan Karadzhov (VMware)
2021-02-11 10:31 ` [PATCH v2 01/27] kernel-shark: Add get_stream_object() Yordan Karadzhov (VMware)
2021-02-11 10:31 ` [PATCH v2 02/27] kernel-shark: Do proper reset in kshark_close_all() Yordan Karadzhov (VMware)
2021-02-11 10:31 ` [PATCH v2 03/27] kernel-shark: Restore the counting of event handlers Yordan Karadzhov (VMware)
2021-02-11 10:31 ` [PATCH v2 04/27] kernel-shark: Fix a misleading comment Yordan Karadzhov (VMware)
2021-02-11 10:31 ` [PATCH v2 05/27] kernel-shark: Count the number of readout interfaces Yordan Karadzhov (VMware)
2021-02-11 10:31 ` [PATCH v2 06/27] kernel-shark: Update KsUtils Yordan Karadzhov (VMware)
2021-02-11 10:31 ` [PATCH v2 07/27] kernel-shark: Update KsModels and KsSearchFSM Yordan Karadzhov (VMware)
2021-02-11 10:31 ` [PATCH v2 08/27] kernel-shark: Add trace data files for CI testing Yordan Karadzhov (VMware)
2021-02-11 10:31 ` [PATCH v2 09/27] kernel-shark: Add plugin tests Yordan Karadzhov (VMware)
2021-02-11 10:31 ` [PATCH v2 10/27] kernel-shark: Add model tests Yordan Karadzhov (VMware)
2021-02-11 10:31 ` [PATCH v2 11/27] kernel-shark: Update KsWidgetsLib Yordan Karadzhov (VMware)
2021-02-11 10:31 ` [PATCH v2 12/27] kernel-shark: Add combo point to Mark Yordan Karadzhov (VMware)
2021-02-11 10:31 ` [PATCH v2 13/27] kernel-shark: Add new methods to KsPlot::Mark Yordan Karadzhov (VMware)
2021-02-11 10:31 ` [PATCH v2 14/27] kernel-shark: Update the plotting example Yordan Karadzhov (VMware)
2021-02-11 10:31 ` [PATCH v2 15/27] kernel-shark: Search for font with Cmake at pre-build Yordan Karadzhov (VMware)
2021-02-11 10:31 ` [PATCH v2 16/27] kernel-shark: Update KsDualMarker and KsGLWidget Yordan Karadzhov (VMware)
2021-02-11 10:31 ` [PATCH v2 17/27] kernel-shark: Update KsTraceGraph and KsQuickContextMenu Yordan Karadzhov (VMware)
2021-02-11 10:31 ` [PATCH v2 18/27] kernel-shark: Update KsTraceViewer Yordan Karadzhov (VMware)
2021-02-11 10:31 ` [PATCH v2 19/27] kernel-shark: Update KsAdvFilteringDialog Yordan Karadzhov (VMware)
2021-02-11 10:31 ` [PATCH v2 20/27] kernel-shark: Update KsCaptureDialog Yordan Karadzhov (VMware)
2021-02-11 10:31 ` [PATCH v2 21/27] kernel-shark: Update KsSession Yordan Karadzhov (VMware)
2021-02-11 10:32 ` [PATCH v2 22/27] kernel-shark: Update MissedEvents plugin Yordan Karadzhov (VMware)
2021-02-11 10:32 ` [PATCH v2 23/27] kernel-shark: Update KsMainWindow and kernelshark.cpp Yordan Karadzhov (VMware)
2021-02-11 10:32 ` [PATCH v2 24/27] kernel-shark: Clickable sched_event plugin shapes Yordan Karadzhov (VMware)
2021-02-11 10:32 ` [PATCH v2 25/27] kernel-shark: Show Task plots from command lime Yordan Karadzhov (VMware)
2021-02-11 10:32 ` [PATCH v2 26/27] kernel-shark: Add pkg-config configuration for libkshark Yordan Karadzhov (VMware)
2021-02-11 10:32 ` [PATCH v2 27/27] kernel-shark: Install libkshark-tepdata.h Yordan Karadzhov (VMware)
2021-02-11 15:02 ` [PATCH v2 00/27] Complete the KernelShark v2 transformation Steven Rostedt

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).