@@ -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
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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
new file mode 100644
@@ -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
+}
new file mode 100644
@@ -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);
+}
new file mode 100644
@@ -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
@@ -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"
};
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