Linux-Trace-Devel Archive on lore.kernel.org
 help / color / Atom feed
* [PATCH 0/2] New plugins for KS 2.0
@ 2021-04-29 15:44 Yordan Karadzhov (VMware)
  2021-04-29 15:44 ` [PATCH 1/2] kernel-shark: Add EventFieldPlot plugin Yordan Karadzhov (VMware)
  2021-04-29 15:44 ` [PATCH 2/2] kernel-shark: Add LatencyPlot plugin Yordan Karadzhov (VMware)
  0 siblings, 2 replies; 4+ messages in thread
From: Yordan Karadzhov (VMware) @ 2021-04-29 15:44 UTC (permalink / raw)
  To: linux-trace-devel; +Cc: Yordan Karadzhov (VMware)

This patch applies on top of the
"(Not so) Minor fixes toward KS 2.0" v2 patch-set.

Yordan Karadzhov (VMware) (2):
  kernel-shark: Add EventFieldPlot plugin
  kernel-shark: Add LatencyPlot plugin

 src/plugins/CMakeLists.txt        |  10 +
 src/plugins/EventFieldDialog.cpp  | 183 ++++++++++++++++++
 src/plugins/EventFieldDialog.hpp  |  60 ++++++
 src/plugins/EventFieldPlot.cpp    | 109 +++++++++++
 src/plugins/LatencyPlot.cpp       | 305 ++++++++++++++++++++++++++++++
 src/plugins/LatencyPlotDialog.cpp | 184 ++++++++++++++++++
 src/plugins/LatencyPlotDialog.hpp |  59 ++++++
 src/plugins/event_field_plot.c    | 125 ++++++++++++
 src/plugins/event_field_plot.h    |  64 +++++++
 src/plugins/latency_plot.c        | 168 ++++++++++++++++
 src/plugins/latency_plot.h        |  62 ++++++
 tests/libkshark-gui-tests.cpp     |   2 +
 12 files changed, 1331 insertions(+)
 create mode 100644 src/plugins/EventFieldDialog.cpp
 create mode 100644 src/plugins/EventFieldDialog.hpp
 create mode 100644 src/plugins/EventFieldPlot.cpp
 create mode 100644 src/plugins/LatencyPlot.cpp
 create mode 100644 src/plugins/LatencyPlotDialog.cpp
 create mode 100644 src/plugins/LatencyPlotDialog.hpp
 create mode 100644 src/plugins/event_field_plot.c
 create mode 100644 src/plugins/event_field_plot.h
 create mode 100644 src/plugins/latency_plot.c
 create mode 100644 src/plugins/latency_plot.h

-- 
2.27.0


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

* [PATCH 1/2] kernel-shark: Add EventFieldPlot plugin
  2021-04-29 15:44 [PATCH 0/2] New plugins for KS 2.0 Yordan Karadzhov (VMware)
@ 2021-04-29 15:44 ` Yordan Karadzhov (VMware)
  2021-04-29 15:44 ` [PATCH 2/2] kernel-shark: Add LatencyPlot plugin Yordan Karadzhov (VMware)
  1 sibling, 0 replies; 4+ messages in thread
From: Yordan Karadzhov (VMware) @ 2021-04-29 15:44 UTC (permalink / raw)
  To: linux-trace-devel; +Cc: Yordan Karadzhov (VMware)

The plugin allows the user to visualize the recorded value of a given
data field from a given trace event. The core logic that implements
the processing of the data and the visualization itself is rather
simple. It is implemented in:
event_field_plot.h,
event_field_plot.c and
EventFieldPlot.cpp

The plugin also registers its own dialog, that allows the user to
select the event and field to be visualized. The widget of the dialog
gets implemented in:
EventFieldDialog.hpp and
EventFieldDialog.cpp

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 src/plugins/CMakeLists.txt       |   5 +
 src/plugins/EventFieldDialog.cpp | 183 +++++++++++++++++++++++++++++++
 src/plugins/EventFieldDialog.hpp |  60 ++++++++++
 src/plugins/EventFieldPlot.cpp   | 109 ++++++++++++++++++
 src/plugins/event_field_plot.c   | 125 +++++++++++++++++++++
 src/plugins/event_field_plot.h   |  64 +++++++++++
 tests/libkshark-gui-tests.cpp    |   1 +
 7 files changed, 547 insertions(+)
 create mode 100644 src/plugins/EventFieldDialog.cpp
 create mode 100644 src/plugins/EventFieldDialog.hpp
 create mode 100644 src/plugins/EventFieldPlot.cpp
 create mode 100644 src/plugins/event_field_plot.c
 create mode 100644 src/plugins/event_field_plot.h

diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt
index 5e28368..333f0da 100644
--- a/src/plugins/CMakeLists.txt
+++ b/src/plugins/CMakeLists.txt
@@ -44,6 +44,11 @@ if (Qt5Widgets_FOUND AND TT_FONT_FILE)
                      SOURCE sched_events.c SchedEvents.cpp)
     list(APPEND PLUGIN_LIST "sched_events")
 
+    BUILD_GUI_PLUGIN(NAME event_field_plot
+                     MOC EventFieldDialog.hpp
+                     SOURCE event_field_plot.c EventFieldDialog.cpp EventFieldPlot.cpp)
+    list(APPEND PLUGIN_LIST "event_field_plot")
+
 endif (Qt5Widgets_FOUND AND TT_FONT_FILE)
 
 BUILD_PLUGIN(NAME missed_events
diff --git a/src/plugins/EventFieldDialog.cpp b/src/plugins/EventFieldDialog.cpp
new file mode 100644
index 0000000..fbfe4cc
--- /dev/null
+++ b/src/plugins/EventFieldDialog.cpp
@@ -0,0 +1,183 @@
+// SPDX-License-Identifier: LGPL-2.1
+
+/*
+ * Copyright (C) 2020 VMware Inc, Yordan Karadzhov (VMware) <y.karadz@gmail.com>
+ */
+
+/**
+ *  @file    EventFieldDialog.cpp
+ *  @brief   Dialog class used by the EventFieldPlot plugin.
+ */
+
+// C++
+#include <iostream>
+#include <vector>
+
+// KernelShark
+#include "KsMainWindow.hpp"
+#include "EventFieldDialog.hpp"
+
+/** The name of the menu item used to start the dialog of the plugin. */
+#define DIALOG_NAME "Plot Event Field"
+
+/** Create plugin dialog widget. */
+KsEFPDialog::KsEFPDialog(QWidget *parent)
+: QDialog(parent),
+  _selectLabel("Show", this),
+  _applyButton("Apply", this),
+  _resetButton("Reset", this),
+  _cancelButton("Cancel", this)
+{
+	setWindowTitle(DIALOG_NAME);
+
+	_topLayout.addWidget(&_efsWidget);
+
+	_topLayout.addWidget(&_selectLabel);
+	_setSelectCombo();
+	_topLayout.addWidget(&_selectComboBox);
+
+	_buttonLayout.addWidget(&_applyButton);
+	_applyButton.setAutoDefault(false);
+
+	_buttonLayout.addWidget(&_resetButton);
+	_resetButton.setAutoDefault(false);
+
+	_buttonLayout.addWidget(&_cancelButton);
+	_cancelButton.setAutoDefault(false);
+
+	_buttonLayout.setAlignment(Qt::AlignLeft);
+	_topLayout.addLayout(&_buttonLayout);
+
+	connect(&_applyButton,	&QPushButton::pressed,
+		this,		&KsEFPDialog::_apply);
+
+	connect(&_applyButton,	&QPushButton::pressed,
+		this,		&QWidget::close);
+
+	connect(&_resetButton,	&QPushButton::pressed,
+		this,		&KsEFPDialog::_reset);
+
+	connect(&_resetButton,	&QPushButton::pressed,
+		this,		&QWidget::close);
+
+	connect(&_cancelButton,	&QPushButton::pressed,
+		this,		&QWidget::close);
+
+	setLayout(&_topLayout);
+}
+
+void KsEFPDialog::_setSelectCombo()
+{
+	_selectComboBox.clear();
+	_selectComboBox.addItem("max. value", 0);
+	_selectComboBox.addItem("min. value", 1);
+}
+
+/** Select the plotting criteria. */
+void KsEFPDialog::selectCondition(plugin_efp_context *plugin_ctx)
+{
+	/* In the combo box "max" is 0 and "min" is 1. */
+	plugin_ctx->show_max = !_selectComboBox.currentData().toInt();
+}
+
+/** Update the dialog, using the current settings of the plugin. */
+void KsEFPDialog::update()
+{
+	_efsWidget.setStreamCombo();
+}
+
+static KsEFPDialog *efp_dialog(nullptr);
+
+static int plugin_get_stream_id()
+{
+	return efp_dialog->_efsWidget.streamId();
+}
+
+/** Use the Event name selected by the user to update the plugin's context. */
+__hidden void plugin_set_event_name(plugin_efp_context *plugin_ctx)
+{
+	QString buff = efp_dialog->_efsWidget.eventName();
+	char *event;
+
+	if (asprintf(&event, "%s", buff.toStdString().c_str()) >= 0) {
+		plugin_ctx->event_name = event;
+		return;
+	}
+
+	plugin_ctx->event_name = NULL;
+}
+
+/** Use the Field name selected by the user to update the plugin's context. */
+__hidden void plugin_set_field_name(plugin_efp_context *plugin_ctx)
+{
+	QString buff = efp_dialog->_efsWidget.fieldName();
+	char *field;
+
+	if (asprintf(&field, "%s", buff.toStdString().c_str()) >= 0) {
+		plugin_ctx->field_name = field;
+		return;
+	}
+
+	plugin_ctx->field_name = NULL;
+}
+
+/** Use the condition selected by the user to update the plugin's context. */
+__hidden void plugin_set_select_condition(plugin_efp_context *plugin_ctx)
+{
+	efp_dialog->selectCondition(plugin_ctx);
+}
+
+void KsEFPDialog::_apply()
+{
+	auto work = KsWidgetsLib::KsDataWork::UpdatePlugins;
+
+	/* The plugin needs to process the data and this may take time
+	 * on large datasets. Show a "Work In Process" warning.
+	 */
+	_gui_ptr->wipPtr()->show(work);
+	_gui_ptr->registerPluginToStream("event_field_plot",
+					 {plugin_get_stream_id()});
+	_gui_ptr->wipPtr()->hide(work);
+}
+
+void KsEFPDialog::_reset()
+{
+	auto work = KsWidgetsLib::KsDataWork::UpdatePlugins;
+	kshark_context *kshark_ctx(nullptr);
+	QVector<int> streamIds;
+
+	if (!kshark_instance(&kshark_ctx))
+		return;
+
+	streamIds = KsUtils::getStreamIdList(kshark_ctx);
+
+	/*
+	 * The plugin needs to process the data and this may take time
+	 * on large datasets. Show a "Work In Process" warning.
+	 */
+	_gui_ptr->wipPtr()->show(work);
+	_gui_ptr->unregisterPluginFromStream("event_field_plot",
+					     streamIds);
+	_gui_ptr->wipPtr()->hide(work);
+}
+
+static void showDialog(KsMainWindow *ks)
+{
+	efp_dialog->update();
+	efp_dialog->show();
+}
+
+/** Add the dialog of the plugin to the KernelShark menus. */
+__hidden void *plugin_efp_add_menu(void *ks_ptr)
+{
+	if (!efp_dialog) {
+		efp_dialog = new KsEFPDialog();
+		efp_dialog->_gui_ptr = static_cast<KsMainWindow *>(ks_ptr);
+	}
+
+	QString menu("Tools/");
+	menu += DIALOG_NAME;
+	efp_dialog->_gui_ptr->addPluginMenu(menu, showDialog);
+
+	return efp_dialog;
+}
diff --git a/src/plugins/EventFieldDialog.hpp b/src/plugins/EventFieldDialog.hpp
new file mode 100644
index 0000000..46339b2
--- /dev/null
+++ b/src/plugins/EventFieldDialog.hpp
@@ -0,0 +1,60 @@
+/* SPDX-License-Identifier: LGPL-2.1 */
+
+/*
+ * Copyright (C) 2020 VMware Inc, Yordan Karadzhov <ykaradzhov@vmware.com>
+ */
+
+/**
+ *  @file    EventFieldDialog.hpp
+ *  @brief   Dialog class used by the EventFieldPlot plugin.
+ */
+
+#ifndef _KS_EFP_DIALOG_H
+#define _KS_EFP_DIALOG_H
+
+// KernelShark
+#include "plugins/event_field_plot.h"
+#include "KsWidgetsLib.hpp"
+
+class KsMainWindow;
+
+/**
+ * The KsEFPDialog class provides a widget for selecting Trace event field to
+ * be visualized.
+ */
+
+class KsEFPDialog : public QDialog
+{
+	Q_OBJECT
+public:
+	explicit KsEFPDialog(QWidget *parent = nullptr);
+
+	void update();
+
+	void selectCondition(plugin_efp_context *plugin_ctx);
+
+	/** Widget for selecting Treace event. */
+	KsWidgetsLib::KsEventFieldSelectWidget	_efsWidget;
+
+	/** KernelShark GUI (main window) object. */
+	KsMainWindow	*_gui_ptr;
+
+private:
+	QVBoxLayout	_topLayout;
+
+	QHBoxLayout	_buttonLayout;
+
+	QComboBox	_selectComboBox;
+
+	QLabel		_selectLabel;
+
+	QPushButton	_applyButton, _resetButton, _cancelButton;
+
+	void _setSelectCombo();
+
+	void _apply();
+
+	void _reset();
+};
+
+#endif
diff --git a/src/plugins/EventFieldPlot.cpp b/src/plugins/EventFieldPlot.cpp
new file mode 100644
index 0000000..1938d62
--- /dev/null
+++ b/src/plugins/EventFieldPlot.cpp
@@ -0,0 +1,109 @@
+// SPDX-License-Identifier: LGPL-2.1
+
+/*
+ * Copyright (C) 2020 VMware Inc, Yordan Karadzhov (VMware) <y.karadz@gmail.com>
+ */
+
+/**
+ *  @file    EventFieldPlot.cpp
+ *  @brief   Plugin for visualizing a given data field of a trace event.
+ */
+
+// C++
+#include <vector>
+
+// KernelShark
+#include "plugins/event_field_plot.h"
+#include "KsPlotTools.hpp"
+#include "KsPlugins.hpp"
+
+using namespace KsPlot;
+
+/**
+ * @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: Can be CPU Id or Process Id.
+ * @param draw_action: Draw action identifier.
+ */
+__hidden void draw_event_field(kshark_cpp_argv *argv_c,
+			       int sd, int val, int draw_action)
+{
+	KsCppArgV *argvCpp = KS_ARGV_TO_CPP(argv_c);
+	Graph *graph = argvCpp->_graph;
+	plugin_efp_context *plugin_ctx;
+	IsApplicableFunc checkEntry;
+	int binSize(0), s0, s1;
+	int64_t norm;
+
+	if (!(draw_action & KSHARK_CPU_DRAW) &&
+	    !(draw_action & KSHARK_TASK_DRAW))
+		return;
+
+	plugin_ctx = __get_context(sd);
+	if (!plugin_ctx)
+		return;
+
+	/* Get the size of the graph's bins. */
+	for (int i = 0; i < graph->size(); ++i)
+		if (graph->bin(i).mod()) {
+			binSize = graph->bin(i)._size;
+			break;
+		}
+
+	s0 = graph->height() / 3;
+	s1 = graph->height() / 5;
+
+	norm = plugin_ctx->field_max - plugin_ctx->field_min;
+	/* Avoid division by zero. */
+	if (norm == 0)
+		++norm;
+
+	auto lamMakeShape = [=] (std::vector<const Graph *> graph,
+				 std::vector<int> bin,
+				 std::vector<kshark_data_field_int64 *> data,
+				 Color, float) {
+		int x, y, mod(binSize);
+		Color c;
+
+		x = graph[0]->bin(bin[0])._val.x();
+		y = graph[0]->bin(bin[0])._val.y() - s0;
+
+		if (plugin_ctx->show_max)
+			mod += s1 * (data[0]->field - plugin_ctx->field_min) / norm;
+		else
+			mod += s1 * (plugin_ctx->field_max - data[0]->field) / norm;
+
+		Point p0(x, y + mod), p1(x, y - mod);
+		Line *l = new Line(p0, p1);
+		c.setRainbowColor(mod - 1);
+		l->_size = binSize + 1;
+		l->_color = c;
+
+		return l;
+	};
+
+	if (draw_action & KSHARK_CPU_DRAW)
+		checkEntry = [=] (kshark_data_container *d, ssize_t i) {
+			return d->data[i]->entry->cpu == val;
+		};
+
+	else if (draw_action & KSHARK_TASK_DRAW)
+		checkEntry = [=] (kshark_data_container *d, ssize_t i) {
+			return d->data[i]->entry->pid == val;
+		};
+
+	if (plugin_ctx->show_max)
+		eventFieldPlotMax(argvCpp,
+				  plugin_ctx->data, checkEntry,
+				  lamMakeShape,
+				  {}, // Undefined color
+				  0); // Undefined size
+	else
+		eventFieldPlotMin(argvCpp,
+				  plugin_ctx->data, checkEntry,
+				  lamMakeShape,
+				  {}, // Undefined color
+				  0); // Undefined size
+}
diff --git a/src/plugins/event_field_plot.c b/src/plugins/event_field_plot.c
new file mode 100644
index 0000000..08b453f
--- /dev/null
+++ b/src/plugins/event_field_plot.c
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: LGPL-2.1
+
+/*
+ * Copyright (C) 2020 VMware Inc, Yordan Karadzhov (VMware) <y.karadz@gmail.com>
+ */
+
+/**
+ *  @file    event_field_plot.c
+ *  @brief   Plugin for visualizing a given data field of a trace event.
+ */
+
+// C
+#include <stdio.h>
+#include <assert.h>
+#include <limits.h>
+
+// KernelShark
+#include "plugins/event_field_plot.h"
+
+static void efp_free_context(struct plugin_efp_context *plugin_ctx)
+{
+	if (!plugin_ctx)
+		return;
+
+	free(plugin_ctx->event_name);
+	free(plugin_ctx->field_name);
+	kshark_free_data_container(plugin_ctx->data);
+}
+
+/** A general purpose macro is used to define plugin context. */
+KS_DEFINE_PLUGIN_CONTEXT(struct plugin_efp_context, efp_free_context);
+
+static bool plugin_efp_init_context(struct kshark_data_stream *stream,
+				    struct plugin_efp_context *plugin_ctx)
+{
+	plugin_set_event_name(plugin_ctx);
+	plugin_set_field_name(plugin_ctx);
+	plugin_set_select_condition(plugin_ctx);
+
+	plugin_ctx->field_max = INT64_MIN;
+	plugin_ctx->field_min = INT64_MAX;
+
+	plugin_ctx->event_id =
+		kshark_find_event_id(stream, plugin_ctx->event_name);
+
+	if (plugin_ctx->event_id < 0) {
+		fprintf(stderr, "Event %s not found in stream %s:%s\n",
+			plugin_ctx->event_name, stream->file, stream->name);
+		return false;
+	}
+
+	plugin_ctx->data = kshark_init_data_container();
+	if (!plugin_ctx->data)
+		return false;
+
+	return true;
+}
+
+static void plugin_get_field(struct kshark_data_stream *stream, void *rec,
+			     struct kshark_entry *entry)
+{
+	struct plugin_efp_context *plugin_ctx;
+	int64_t val;
+
+	plugin_ctx = __get_context(stream->stream_id);
+	if (!plugin_ctx)
+		return;
+
+	kshark_read_record_field_int(stream, rec,
+				     plugin_ctx->field_name,
+				     &val);
+
+	kshark_data_container_append(plugin_ctx->data, entry, val);
+
+	if (val > plugin_ctx->field_max)
+		plugin_ctx->field_max = val;
+
+	if (val < plugin_ctx->field_min)
+		plugin_ctx->field_min = val;
+}
+
+/** Load this plugin. */
+int KSHARK_PLOT_PLUGIN_INITIALIZER(struct kshark_data_stream *stream)
+{
+	struct plugin_efp_context *plugin_ctx = __init(stream->stream_id);
+
+	if (!plugin_ctx || !plugin_efp_init_context(stream, plugin_ctx)) {
+		__close(stream->stream_id);
+		return 0;
+	}
+
+	kshark_register_event_handler(stream,
+				      plugin_ctx->event_id,
+				      plugin_get_field);
+
+	kshark_register_draw_handler(stream, draw_event_field);
+
+	return 1;
+}
+
+/** Unload this plugin. */
+int KSHARK_PLOT_PLUGIN_DEINITIALIZER(struct kshark_data_stream *stream)
+{
+	struct plugin_efp_context *plugin_ctx = __get_context(stream->stream_id);
+	int ret = 0;
+
+	if (plugin_ctx) {
+		kshark_unregister_event_handler(stream,
+						plugin_ctx->event_id,
+						plugin_get_field);
+
+		kshark_unregister_draw_handler(stream, draw_event_field);
+		ret = 1;
+	}
+
+	__close(stream->stream_id);
+
+	return ret;
+}
+
+/** Initialize the control interface of the plugin. */
+void *KSHARK_MENU_PLUGIN_INITIALIZER(void *gui_ptr)
+{
+	return plugin_efp_add_menu(gui_ptr);
+}
diff --git a/src/plugins/event_field_plot.h b/src/plugins/event_field_plot.h
new file mode 100644
index 0000000..43bbac2
--- /dev/null
+++ b/src/plugins/event_field_plot.h
@@ -0,0 +1,64 @@
+/* SPDX-License-Identifier: LGPL-2.1 */
+
+/*
+ * Copyright (C) 2020 VMware Inc, Yordan Karadzhov (VMware) <y.karadz@gmail.com>
+ */
+
+/**
+ *  @file    event_field_plot.h
+ *  @brief   Plugin for visualizing a given data field of a trace event.
+ */
+
+#ifndef _KS_PLUGIN_EVENT_FIELD_H
+#define _KS_PLUGIN_EVENT_FIELD_H
+
+// KernelShark
+#include "libkshark.h"
+#include "libkshark-plugin.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Structure representing a plugin-specific context. */
+struct plugin_efp_context {
+	/** Trace event name. */
+	char 		*event_name;
+
+	/** Event field name. */
+	char 		*field_name;
+
+	/** The max value of the field in the data. */
+	int64_t		field_max;
+
+	/** The min value of the field in the data. */
+	int64_t		field_min;
+
+	/** Trace event identifier. */
+	int		event_id;
+
+	/** If true, highlight the max field value. Else highlight the min. */
+	bool		show_max;
+
+	/** Container object to store the trace event field's data. */
+	struct kshark_data_container	*data;
+};
+
+KS_DECLARE_PLUGIN_CONTEXT_METHODS(struct plugin_efp_context)
+
+void draw_event_field(struct kshark_cpp_argv *argv_c,
+		      int sd, int pid, int draw_action);
+
+void *plugin_efp_add_menu(void *gui_ptr);
+
+void plugin_set_event_name(struct plugin_efp_context *plugin_ctx);
+
+void plugin_set_field_name(struct plugin_efp_context *plugin_ctx);
+
+void plugin_set_select_condition(struct plugin_efp_context *plugin_ctx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/tests/libkshark-gui-tests.cpp b/tests/libkshark-gui-tests.cpp
index bc49194..a023c39 100644
--- a/tests/libkshark-gui-tests.cpp
+++ b/tests/libkshark-gui-tests.cpp
@@ -147,6 +147,7 @@ BOOST_AUTO_TEST_CASE(KsUtils_KsDataStore)
 BOOST_AUTO_TEST_CASE(KsUtils_getPluginList)
 {
 	QStringList plugins{"sched_events",
+			    "event_field_plot",
 			    "missed_events"
 	};
 
-- 
2.27.0


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

* [PATCH 2/2] kernel-shark: Add LatencyPlot plugin
  2021-04-29 15:44 [PATCH 0/2] New plugins for KS 2.0 Yordan Karadzhov (VMware)
  2021-04-29 15:44 ` [PATCH 1/2] kernel-shark: Add EventFieldPlot plugin Yordan Karadzhov (VMware)
@ 2021-04-29 15:44 ` Yordan Karadzhov (VMware)
  2021-05-06 19:33   ` Steven Rostedt
  1 sibling, 1 reply; 4+ messages in thread
From: Yordan Karadzhov (VMware) @ 2021-04-29 15:44 UTC (permalink / raw)
  To: linux-trace-devel; +Cc: Yordan Karadzhov (VMware)

The plugin allows the user to visualize the latency between two
events under the condition that the values of given data fields
in the two events are identical (for example having the same PID).
The core logic that implements the processing of the data and
the visualization itself is implemented in:

src/plugins/latency_plot.h,
src/plugins/latency_plot.c and
src/plugins/LatencyPlot.cpp

The plugin also registers its own dialog, that allows the user to
select the events (and the matching field) to be visualized. The
widget of the dialog gets implemented in:

src/plugins/LatencyPlotDialog.hpp
src/plugins/LatencyPlotDialog.cpp

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 src/plugins/CMakeLists.txt        |   5 +
 src/plugins/LatencyPlot.cpp       | 305 ++++++++++++++++++++++++++++++
 src/plugins/LatencyPlotDialog.cpp | 184 ++++++++++++++++++
 src/plugins/LatencyPlotDialog.hpp |  59 ++++++
 src/plugins/latency_plot.c        | 168 ++++++++++++++++
 src/plugins/latency_plot.h        |  62 ++++++
 tests/libkshark-gui-tests.cpp     |   1 +
 7 files changed, 784 insertions(+)
 create mode 100644 src/plugins/LatencyPlot.cpp
 create mode 100644 src/plugins/LatencyPlotDialog.cpp
 create mode 100644 src/plugins/LatencyPlotDialog.hpp
 create mode 100644 src/plugins/latency_plot.c
 create mode 100644 src/plugins/latency_plot.h

diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt
index 333f0da..2906dd4 100644
--- a/src/plugins/CMakeLists.txt
+++ b/src/plugins/CMakeLists.txt
@@ -49,6 +49,11 @@ if (Qt5Widgets_FOUND AND TT_FONT_FILE)
                      SOURCE event_field_plot.c EventFieldDialog.cpp EventFieldPlot.cpp)
     list(APPEND PLUGIN_LIST "event_field_plot")
 
+    BUILD_GUI_PLUGIN(NAME latency_plot
+                     MOC LatencyPlotDialog.hpp
+                     SOURCE latency_plot.c LatencyPlot.cpp LatencyPlotDialog.cpp)
+    list(APPEND PLUGIN_LIST "latency_plot")
+
 endif (Qt5Widgets_FOUND AND TT_FONT_FILE)
 
 BUILD_PLUGIN(NAME missed_events
diff --git a/src/plugins/LatencyPlot.cpp b/src/plugins/LatencyPlot.cpp
new file mode 100644
index 0000000..7dd0af0
--- /dev/null
+++ b/src/plugins/LatencyPlot.cpp
@@ -0,0 +1,305 @@
+// SPDX-License-Identifier: LGPL-2.1
+
+/*
+ * Copyright (C) 2020 VMware Inc, Yordan Karadzhov (VMware) <y.karadz@gmail.com>
+ */
+
+/**
+ *  @file    LatencyPlot.cpp
+ *  @brief   Plugin for visualizing the latency between two trace events.
+ */
+
+// C
+#include <math.h>
+
+// C++
+#include <unordered_map>
+#include <iostream>
+
+// KernelShark
+#include "plugins/latency_plot.h"
+#include "KsPlotTools.hpp"
+#include "KsPlugins.hpp"
+
+/** A pair of events defining the latency. */
+typedef std::pair<kshark_entry *, kshark_entry *> LatencyPair;
+
+/** Hash table of latency pairs. */
+typedef std::unordered_multimap<int, LatencyPair> LatencyHashTable;
+
+/** Hash table storing the latency pairs per CPU.*/
+LatencyHashTable latencyCPUMap;
+
+/** Hash table storing the latency pairs per Task.*/
+LatencyHashTable latencyTaskMap;
+
+/**
+ * Macro used to forward the arguments and construct the pair directly into
+ * a hash table without unnecessary copies (or moves).
+ */
+#define LATENCY_EMPLACE(map, key ,eA, eB) \
+	map.emplace(std::piecewise_construct, \
+		    std::forward_as_tuple(key), \
+		    std::forward_as_tuple(eA, eB)); \
+
+using namespace KsPlot;
+
+/*
+ * A second pass over the data is used to populate the hash tables of latency
+ * pairs.
+ */
+static void secondPass(plugin_latency_context *plugin_ctx)
+{
+	kshark_data_field_int64	**dataA = plugin_ctx->data[0]->data;
+	kshark_data_field_int64	**dataB = plugin_ctx->data[1]->data;
+
+	size_t nEvtAs = plugin_ctx->data[0]->size;
+	size_t nEvtBs = plugin_ctx->data[1]->size;
+	int64_t timeA, timeANext, valFieldA;
+	size_t iB(0);
+
+	/*
+	 * The order of the events in the container is the same as in the raw
+	 * data in the file. This means the data is not sorted in time.
+	 */
+	kshark_data_container_sort(plugin_ctx->data[0]);
+	kshark_data_container_sort(plugin_ctx->data[1]);
+
+	latencyCPUMap.clear();
+	latencyTaskMap.clear();
+
+	for (size_t iA = 0; iA < nEvtAs; ++iA) {
+		timeA = dataA[iA]->entry->ts;
+		valFieldA = dataA[iA]->field;
+
+		/*
+		 * Find the time of the next "A event" having the same field
+		 * value.
+		 */
+		timeANext = INT64_MAX;
+		for (size_t i = iA + 1; i <nEvtAs; ++i) {
+			if (dataA[i]->field == valFieldA) {
+				timeANext = dataA[i]->entry->ts;
+				break;
+			}
+		}
+
+		for (size_t i = iB; i < nEvtBs; ++i) {
+			if (dataB[i]->entry->ts < timeA) {
+				/*
+				 * We only care about the "B evenys" that are
+				 * after (in time) the current "A event".
+				 * Skip these "B events", when you search to
+				 * pair the next "A event".
+				 */
+				++iB;
+				continue;
+			}
+
+			if (dataB[i]->entry->ts > timeANext) {
+				/*
+				 * We already bypassed in time the next
+				 * "A event" having the same field value.
+				 */
+				break;
+			}
+
+			if (dataB[i]->field == valFieldA) {
+				int64_t delta = dataB[i]->entry->ts - timeA;
+
+				if (delta > plugin_ctx->max_latency)
+					plugin_ctx->max_latency = delta;
+
+				/*
+				 * Store this pair of events in the hash
+				 * tables. Use the CPU Id as a key.
+				 */
+				LATENCY_EMPLACE(latencyCPUMap,
+						dataB[i]->entry->cpu,
+						dataA[iA]->entry,
+						dataB[i]->entry)
+
+				/*
+				 * Use the PID as a key.
+				 */
+				LATENCY_EMPLACE(latencyTaskMap,
+						dataB[i]->entry->pid,
+						dataA[iA]->entry,
+						dataB[i]->entry)
+				break;
+			}
+		}
+	}
+}
+
+//! @cond Doxygen_Suppress
+
+#define ORANGE {255, 165, 0}
+
+//! @endcond
+
+static void liftBase(Point *point, Graph *graph)
+{
+	point->setY(point->y() - graph->height() * .8);
+};
+
+static Line *baseLine(Graph *graph)
+{
+	Point p0, p1;
+	Line *l;
+
+	p0 = graph->bin(0)._base;
+	liftBase(&p0, graph);
+	p1 = graph->bin(graph->size() - 1)._base;
+	liftBase(&p1, graph);
+
+	l = new Line(p0, p1);
+	l->_color = ORANGE;
+	return l;
+}
+
+/**
+ * This class represents the graphical element visualizing the latency between
+ * two trace events.
+ */
+class LatencyTick : public Line {
+public:
+	/** Constructor. */
+	LatencyTick(const Point &p0, const Point &p1, const LatencyPair &l)
+	: Line(p0, p1), _l(l) {
+		_color = ORANGE;
+	}
+
+	/**
+	 * @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
+	{
+		int dx, dy;
+
+		dx = pointX(0) - x;
+		dy = pointY(0) - y;
+
+		return sqrt(dx *dx + dy * dy);
+	}
+
+private:
+	LatencyTick() = delete;
+
+	LatencyPair _l;
+
+	/** On double click do. */
+	void _doubleClick() const override;
+
+};
+
+void LatencyTick::_doubleClick() const
+{
+	plugin_mark_entry(_l.first, 'A');
+	plugin_mark_entry(_l.second, 'B');
+}
+
+
+static LatencyTick *tick(Graph *graph, int bin, int height, const LatencyPair &l)
+{
+	Point p0, p1;
+
+	p0 = graph->bin(bin)._base;
+	liftBase(&p0, graph);
+	p1 = p0;
+	p1.setY(p1.y() - height);
+
+	return new LatencyTick(p0, p1, l);
+
+}
+
+/**
+ * @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: Can be CPU Id or Process Id.
+ * @param draw_action: Draw action identifier.
+ */
+__hidden void draw_latency(struct kshark_cpp_argv *argv_c,
+			   int sd, int val, int draw_action)
+{
+	plugin_latency_context *plugin_ctx = __get_context(sd);
+	struct kshark_context *kshark_ctx(nullptr);
+	kshark_data_stream *stream;
+	kshark_trace_histo *histo;
+	LatencyHashTable *hash;
+	KsCppArgV *argvCpp;
+	PlotObjList *shapes;
+	Graph *thisGraph;
+	int graphHeight;
+
+	if (!plugin_ctx)
+		return;
+
+	if (!plugin_ctx->second_pass_done) {
+		/* The second pass is not done yet. */
+		secondPass(plugin_ctx);
+		plugin_ctx->second_pass_done = true;
+	}
+
+	if (!kshark_instance(&kshark_ctx))
+		return;
+
+	stream = kshark_get_data_stream(kshark_ctx, sd);
+	if (!stream)
+		return;
+
+	/* Retrieve the arguments (C++ objects). */
+	argvCpp = KS_ARGV_TO_CPP(argv_c);
+	thisGraph = argvCpp->_graph;
+
+	if (thisGraph->size() == 0)
+		return;
+
+	graphHeight = thisGraph->height();
+	shapes = argvCpp->_shapes;
+	histo = argvCpp->_histo;
+
+	/* Start by drawing the base line of the Latency plot. */
+	shapes->push_front(baseLine(thisGraph));
+
+	auto lamScaledDelta = [=] (kshark_entry *eA, kshark_entry *eB) {
+		double height;
+
+		height = (eB->ts - eA->ts) / (double) plugin_ctx->max_latency;
+		height *= graphHeight * .6;
+		return height + 4;
+	};
+
+	auto lamPlotLat = [=] (auto p) {
+		kshark_entry *eA = p.second.first;
+		kshark_entry *eB = p.second.second;
+		int binB = ksmodel_get_bin(histo, eB);
+
+		if (binB >= 0)
+			shapes->push_front(tick(thisGraph,
+					   binB,
+					   lamScaledDelta(eA, eB),
+					   p.second));
+	};
+
+	/*
+	 * Use the latency hash tables to get all pairs that are relevant for
+	 * this plot.
+	 */
+	if (draw_action & KSHARK_CPU_DRAW)
+		hash = &latencyCPUMap;
+	else if (draw_action & KSHARK_TASK_DRAW)
+		hash = &latencyTaskMap;
+
+	auto range = hash->equal_range(val);
+	std::for_each(range.first, range.second, lamPlotLat);
+}
diff --git a/src/plugins/LatencyPlotDialog.cpp b/src/plugins/LatencyPlotDialog.cpp
new file mode 100644
index 0000000..1fe8c39
--- /dev/null
+++ b/src/plugins/LatencyPlotDialog.cpp
@@ -0,0 +1,184 @@
+// SPDX-License-Identifier: LGPL-2.1
+
+/*
+ * Copyright (C) 2020 VMware Inc, Yordan Karadzhov (VMware) <y.karadz@gmail.com>
+ */
+
+/**
+ *  @file    EventFieldDialog.cpp
+ *  @brief   Dialog class used by the LatencyPlot plugin.
+ */
+
+// C++
+#include <iostream>
+#include <vector>
+
+// KernelShark
+#include "KsMainWindow.hpp"
+#include "LatencyPlotDialog.hpp"
+
+/** The name of the menu item used to start the dialog of the plugin. */
+#define DIALOG_NAME "Plot Latency"
+
+/** Create plugin dialog widget. */
+LatencyPlotDialog::LatencyPlotDialog(QWidget *parent)
+: QDialog(parent),
+  _evtALabel("\tEvent A", this),
+  _evtBLabel("\tEvent B", this),
+  _applyButton("Apply", this),
+  _resetButton("Reset", this),
+  _cancelButton("Cancel", this)
+{
+	setWindowTitle(DIALOG_NAME);
+
+	_fieldSelectLayout.addWidget(&_evtALabel, 0, 0);
+	_fieldSelectLayout.addWidget(&_evtBLabel, 0, 1);
+
+	_fieldSelectLayout.addWidget(&_efsWidgetA, 1, 0);
+	_fieldSelectLayout.addWidget(&_efsWidgetB, 1, 1);
+	_topLayout.addLayout(&_fieldSelectLayout);
+
+	_buttonLayout.addWidget(&_applyButton);
+	_applyButton.setAutoDefault(false);
+
+	_buttonLayout.addWidget(&_resetButton);
+	_resetButton.setAutoDefault(false);
+
+	_buttonLayout.addWidget(&_cancelButton);
+	_cancelButton.setAutoDefault(false);
+
+	_buttonLayout.setAlignment(Qt::AlignLeft);
+	_topLayout.addLayout(&_buttonLayout);
+
+	connect(&_applyButton,	&QPushButton::pressed,
+		this,		&LatencyPlotDialog::_apply);
+
+	connect(&_applyButton,	&QPushButton::pressed,
+		this,		&QWidget::close);
+
+	connect(&_resetButton,	&QPushButton::pressed,
+		this,		&LatencyPlotDialog::_reset);
+
+	connect(&_resetButton,	&QPushButton::pressed,
+		this,		&QWidget::close);
+
+	connect(&_cancelButton,	&QPushButton::pressed,
+		this,		&QWidget::close);
+
+	setLayout(&_topLayout);
+}
+
+/** Update the dialog, using the current settings of the plugin. */
+void LatencyPlotDialog::update()
+{
+	_efsWidgetA.setStreamCombo();
+	_efsWidgetB.setStreamCombo();
+}
+
+static LatencyPlotDialog *lp_dialog(nullptr);
+
+/**
+ * Use the Events and Field names selected by the user to update the plugin's
+ * context.
+ */
+__hidden void plugin_set_event_fields(struct plugin_latency_context *plugin_ctx)
+{
+	std::string buff;
+	char *name;
+	int ret;
+
+	plugin_ctx->event_name[0] = plugin_ctx->event_name[1] = NULL;
+
+	buff = lp_dialog->_efsWidgetA.eventName().toStdString();
+	ret = asprintf(&name, "%s", buff.c_str());
+	if (ret > 0)
+		plugin_ctx->event_name[0] = name;
+
+	buff = lp_dialog->_efsWidgetB.eventName().toStdString();
+	ret = asprintf(&name, "%s", buff.c_str());
+	if (ret > 0)
+		plugin_ctx->event_name[1] = name;
+
+	plugin_ctx->field_name[0] = plugin_ctx->field_name[1] = NULL;
+
+	buff = lp_dialog->_efsWidgetA.fieldName().toStdString();
+	ret = asprintf(&name, "%s", buff.c_str());
+	if (ret > 0)
+		plugin_ctx->field_name[0] = name;
+
+	buff = lp_dialog->_efsWidgetB.fieldName().toStdString();
+	ret = asprintf(&name, "%s", buff.c_str());
+	if (ret > 0)
+		plugin_ctx->field_name[1] = name;
+}
+
+/**
+ * @brief Mark an entry in the KernelShark GUI.
+ *
+ * @param e: The entry to be selected ith the marker.
+ * @param mark: The marker to be used (A or B).
+ */
+__hidden void plugin_mark_entry(const struct kshark_entry *e, char mark)
+{
+	DualMarkerState st = DualMarkerState::A;
+	if (mark == 'B')
+		st = DualMarkerState::B;
+
+	lp_dialog->_gui_ptr->markEntry(e, st);
+}
+
+void LatencyPlotDialog::_apply()
+{
+	auto work = KsWidgetsLib::KsDataWork::UpdatePlugins;
+	int sdA = lp_dialog->_efsWidgetA.streamId();
+	int sdB = lp_dialog->_efsWidgetB.streamId();
+
+	/*
+	 * The plugin needs to process the data and this may take time
+	 * on large datasets. Show a "Work In Process" warning.
+	 */
+	_gui_ptr->wipPtr()->show(work);
+	_gui_ptr->registerPluginToStream("latency_plot", {sdA, sdB});
+	_gui_ptr->wipPtr()->hide(work);
+}
+
+void LatencyPlotDialog::_reset()
+{
+	auto work = KsWidgetsLib::KsDataWork::UpdatePlugins;
+	kshark_context *kshark_ctx(nullptr);
+	QVector<int> streamIds;
+
+	if (!kshark_instance(&kshark_ctx))
+		return;
+
+	streamIds = KsUtils::getStreamIdList(kshark_ctx);
+
+	/*
+	 * The plugin needs to process the data and this may take time
+	 * on large datasets. Show a "Work In Process" warning.
+	 */
+	_gui_ptr->wipPtr()->show(work);
+	_gui_ptr->unregisterPluginFromStream("latency_plot", streamIds);
+	_gui_ptr->wipPtr()->hide(work);
+}
+
+static void showDialog(KsMainWindow *ks)
+{
+	lp_dialog->update();
+	lp_dialog->show();
+}
+
+/** Add the dialog of the plugin to the KernelShark menus. */
+__hidden void *plugin_latency_add_menu(void *ks_ptr)
+{
+	if (!lp_dialog) {
+		lp_dialog = new LatencyPlotDialog();
+		lp_dialog->_gui_ptr = static_cast<KsMainWindow *>(ks_ptr);
+	}
+
+	QString menu("Tools/");
+	menu += DIALOG_NAME;
+	lp_dialog->_gui_ptr->addPluginMenu(menu, showDialog);
+
+	return lp_dialog;
+}
diff --git a/src/plugins/LatencyPlotDialog.hpp b/src/plugins/LatencyPlotDialog.hpp
new file mode 100644
index 0000000..8451488
--- /dev/null
+++ b/src/plugins/LatencyPlotDialog.hpp
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: LGPL-2.1 */
+
+/*
+ * Copyright (C) 2020 VMware Inc, Yordan Karadzhov (VMware) <y.karadz@gmail.com>
+ */
+
+/**
+ *  @file    LatencyPlotDialog.hpp
+ *  @brief   Dialog class used by the LatencyPlot plugin.
+ */
+
+#ifndef _KS_EFP_DIALOG_H
+#define _KS_EFP_DIALOG_H
+
+// KernelShark
+#include "plugins/latency_plot.h"
+#include "KsWidgetsLib.hpp"
+
+class KsMainWindow;
+
+/**
+ * The LatencyPlotDialog class provides a widget for selecting Trace event field to
+ * be visualized.
+ */
+
+class LatencyPlotDialog : public QDialog
+{
+	Q_OBJECT
+public:
+	explicit LatencyPlotDialog(QWidget *parent = nullptr);
+
+	void update();
+
+	/** Widget for selecting Treace event A. */
+	KsWidgetsLib::KsEventFieldSelectWidget	_efsWidgetA;
+
+	/** Widget for selecting Treace event B. */
+	KsWidgetsLib::KsEventFieldSelectWidget	_efsWidgetB;
+
+	/** KernelShark GUI (main window) object. */
+	KsMainWindow	*_gui_ptr;
+
+private:
+	QVBoxLayout	_topLayout;
+
+	QGridLayout	_fieldSelectLayout;
+
+	QHBoxLayout	_buttonLayout;
+
+	QLabel		_evtALabel, _evtBLabel;
+
+	QPushButton	_applyButton, _resetButton, _cancelButton;
+
+	void _apply();
+
+	void _reset();
+};
+
+#endif
diff --git a/src/plugins/latency_plot.c b/src/plugins/latency_plot.c
new file mode 100644
index 0000000..09b1eca
--- /dev/null
+++ b/src/plugins/latency_plot.c
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: LGPL-2.1
+
+/*
+ * Copyright (C) 2020 VMware Inc, Yordan Karadzhov (VMware) <y.karadz@gmail.com>
+ */
+
+/**
+ *  @file    latency_plot.c
+ *  @brief   Plugin for visualizing the latency between two trace events.
+ */
+
+// C
+#ifndef _GNU_SOURCE
+/** Use GNU C Library. */
+#define _GNU_SOURCE
+#endif // _GNU_SOURCE
+
+#include <stdio.h>
+#include <assert.h>
+
+// KernelShark
+#include "plugins/latency_plot.h"
+
+static void latency_free_context(struct plugin_latency_context *plugin_ctx)
+{
+	if (!plugin_ctx)
+		return;
+
+	free(plugin_ctx->event_name[0]);
+	free(plugin_ctx->field_name[0]);
+
+	free(plugin_ctx->event_name[1]);
+	free(plugin_ctx->field_name[1]);
+
+	kshark_free_data_container(plugin_ctx->data[0]);
+	kshark_free_data_container(plugin_ctx->data[1]);
+}
+
+/** A general purpose macro is used to define plugin context. */
+KS_DEFINE_PLUGIN_CONTEXT(struct plugin_latency_context, latency_free_context);
+
+static bool plugin_latency_init_context(struct kshark_data_stream *stream,
+	struct plugin_latency_context *plugin_ctx)
+{
+	plugin_set_event_fields(plugin_ctx);
+
+	plugin_ctx->event_id[0] =
+		kshark_find_event_id(stream, plugin_ctx->event_name[0]);
+
+	if (plugin_ctx->event_id[0] < 0) {
+		fprintf(stderr, "Event %s not found in stream %s:%s\n",
+			plugin_ctx->event_name[0], stream->file, stream->name);
+		return false;
+	}
+
+	plugin_ctx->event_id[1] =
+		kshark_find_event_id(stream, plugin_ctx->event_name[1]);
+	if (plugin_ctx->event_id[1] < 0) {
+		fprintf(stderr, "Event %s not found in stream %s:%s\n",
+			plugin_ctx->event_name[1], stream->file, stream->name);
+		return false;
+	}
+
+	plugin_ctx->second_pass_done = false;
+	plugin_ctx->max_latency = INT64_MIN;
+
+	plugin_ctx->data[0] = kshark_init_data_container();
+	plugin_ctx->data[1] = kshark_init_data_container();
+	if (!plugin_ctx->data[0] || !plugin_ctx->data[1])
+		return false;
+
+	return true;
+}
+
+static void plugin_get_field(struct kshark_data_stream *stream, void *rec,
+			     struct kshark_entry *entry,
+			     char *field_name,
+			     struct kshark_data_container *data)
+{
+	int64_t val;
+
+	kshark_read_record_field_int(stream, rec, field_name, &val);
+	kshark_data_container_append(data, entry, val);
+}
+
+static void plugin_get_field_a(struct kshark_data_stream *stream, void *rec,
+			       struct kshark_entry *entry)
+{
+	struct plugin_latency_context *plugin_ctx;
+
+	plugin_ctx = __get_context(stream->stream_id);
+	if (!plugin_ctx)
+		return;
+
+	plugin_get_field(stream, rec, entry,
+			 plugin_ctx->field_name[0],
+			 plugin_ctx->data[0]);
+}
+
+static void plugin_get_field_b(struct kshark_data_stream *stream, void *rec,
+			       struct kshark_entry *entry)
+{
+	struct plugin_latency_context *plugin_ctx;
+
+	plugin_ctx = __get_context(stream->stream_id);
+	if (!plugin_ctx)
+		return;
+
+	plugin_get_field(stream, rec, entry,
+			 plugin_ctx->field_name[1],
+			 plugin_ctx->data[1]);
+}
+
+/** Load this plugin. */
+int KSHARK_PLOT_PLUGIN_INITIALIZER(struct kshark_data_stream *stream)
+{
+	struct plugin_latency_context *plugin_ctx = __init(stream->stream_id);
+
+	if (!plugin_ctx || !plugin_latency_init_context(stream, plugin_ctx)) {
+		__close(stream->stream_id);
+		return 0;
+	}
+
+	/* Register Event handler to be executed during data loading. */
+	kshark_register_event_handler(stream,
+				      plugin_ctx->event_id[0],
+				      plugin_get_field_a);
+
+	kshark_register_event_handler(stream,
+				      plugin_ctx->event_id[1],
+				      plugin_get_field_b);
+
+	/* Register a drawing handler to plot on top of each Graph. */
+	kshark_register_draw_handler(stream, draw_latency);
+
+	return 1;
+}
+
+/** Unload this plugin. */
+int KSHARK_PLOT_PLUGIN_DEINITIALIZER(struct kshark_data_stream *stream)
+{
+	struct plugin_latency_context *plugin_ctx = __get_context(stream->stream_id);
+	int ret = 0;
+
+	if (plugin_ctx) {
+		kshark_unregister_event_handler(stream,
+						plugin_ctx->event_id[0],
+						plugin_get_field_a);
+
+		kshark_unregister_event_handler(stream,
+						plugin_ctx->event_id[1],
+						plugin_get_field_b);
+
+		kshark_unregister_draw_handler(stream, draw_latency);
+
+		ret = 1;
+	}
+
+	__close(stream->stream_id);
+
+	return ret;
+}
+
+/** Initialize the control interface of the plugin. */
+void *KSHARK_MENU_PLUGIN_INITIALIZER(void *gui_ptr)
+{
+	return plugin_latency_add_menu(gui_ptr);
+}
diff --git a/src/plugins/latency_plot.h b/src/plugins/latency_plot.h
new file mode 100644
index 0000000..16d78ad
--- /dev/null
+++ b/src/plugins/latency_plot.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: LGPL-2.1 */
+
+/*
+ * Copyright (C) 2020 VMware Inc, Yordan Karadzhov (VMware) <y.karadz@gmail.com>
+ */
+
+/**
+ *  @file    latency_plot.h
+ *  @brief   Plugin for visualizing the latency between two trace events.
+ */
+
+#ifndef _KS_PLUGIN_LATENCY_PLOT_H
+#define _KS_PLUGIN_LATENCY_PLOT_H
+
+// KernelShark
+#include "libkshark.h"
+#include "libkshark-plugin.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Structure representing a plugin-specific context. */
+struct plugin_latency_context {
+	/** Trace event names. */
+	char		*event_name[2];
+
+	/** Trace event identifiers. */
+	int		event_id[2];
+
+	/** Event field names. */
+	char		*field_name[2];
+
+	/** True if the second pass is already done. */
+	bool		second_pass_done;
+
+	/**
+	 * The maximum value of the latency between events A and B in this
+	 * data sample.
+	 */
+	int64_t		max_latency;
+
+	/** Container objects to store the trace event field's data. */
+	struct kshark_data_container	*data[2];
+};
+
+KS_DECLARE_PLUGIN_CONTEXT_METHODS(struct plugin_latency_context)
+
+void draw_latency(struct kshark_cpp_argv *argv_c,
+		  int sd, int pid, int draw_action);
+
+void *plugin_latency_add_menu(void *gui_ptr);
+
+void plugin_set_event_fields(struct plugin_latency_context *plugin_ctx);
+
+void plugin_mark_entry(const struct kshark_entry *e, char mark);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/tests/libkshark-gui-tests.cpp b/tests/libkshark-gui-tests.cpp
index a023c39..f96b3dd 100644
--- a/tests/libkshark-gui-tests.cpp
+++ b/tests/libkshark-gui-tests.cpp
@@ -148,6 +148,7 @@ BOOST_AUTO_TEST_CASE(KsUtils_getPluginList)
 {
 	QStringList plugins{"sched_events",
 			    "event_field_plot",
+			    "latency_plot",
 			    "missed_events"
 	};
 
-- 
2.27.0


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

* Re: [PATCH 2/2] kernel-shark: Add LatencyPlot plugin
  2021-04-29 15:44 ` [PATCH 2/2] kernel-shark: Add LatencyPlot plugin Yordan Karadzhov (VMware)
@ 2021-05-06 19:33   ` Steven Rostedt
  0 siblings, 0 replies; 4+ messages in thread
From: Steven Rostedt @ 2021-05-06 19:33 UTC (permalink / raw)
  To: Yordan Karadzhov (VMware); +Cc: linux-trace-devel

On Thu, 29 Apr 2021 18:44:21 +0300
"Yordan Karadzhov (VMware)" <y.karadz@gmail.com> wrote:

> The plugin allows the user to visualize the latency between two
> events under the condition that the values of given data fields
> in the two events are identical (for example having the same PID).
> The core logic that implements the processing of the data and
> the visualization itself is implemented in:

THIS FEATURE IS FREAKING HOT!!!!

OMG! Seriously Yordan, this is awesome. I could have soooo used this in the
past. I LOVE the way the markers automatically select both the start and
end of the latency. This is a feature that will get a LOT of attention!

WAY TO GO, Yordan!

-- Steve

> 
> src/plugins/latency_plot.h,
> src/plugins/latency_plot.c and
> src/plugins/LatencyPlot.cpp
> 
> The plugin also registers its own dialog, that allows the user to
> select the events (and the matching field) to be visualized. The
> widget of the dialog gets implemented in:
> 
> src/plugins/LatencyPlotDialog.hpp
> src/plugins/LatencyPlotDialog.cpp
> 
> Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>

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

end of thread, back to index

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-04-29 15:44 [PATCH 0/2] New plugins for KS 2.0 Yordan Karadzhov (VMware)
2021-04-29 15:44 ` [PATCH 1/2] kernel-shark: Add EventFieldPlot plugin Yordan Karadzhov (VMware)
2021-04-29 15:44 ` [PATCH 2/2] kernel-shark: Add LatencyPlot plugin Yordan Karadzhov (VMware)
2021-05-06 19:33   ` Steven Rostedt

Linux-Trace-Devel Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-trace-devel/0 linux-trace-devel/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-trace-devel linux-trace-devel/ https://lore.kernel.org/linux-trace-devel \
		linux-trace-devel@vger.kernel.org
	public-inbox-index linux-trace-devel

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-trace-devel


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git