[08/10] kernel-shark-qt: Add a manager class for GUI sessions.
diff mbox series

Message ID 20181012161318.5302-9-ykaradzhov@vmware.com
State Superseded
Headers show
Series
  • Add Qt-based GUI for KernelShark
Related show

Commit Message

Yordan Karadzhov Oct. 12, 2018, 4:13 p.m. UTC
From: Yordan Karadzhov (VMware) <y.karadz@gmail.com>

The KsSession class provides instruments for importing/exporting
the state of the different components of the GUI from/to Json
documents. These instruments are used to save/load user session
in the GUI.

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 kernel-shark-qt/src/CMakeLists.txt |   1 +
 kernel-shark-qt/src/KsSession.cpp  | 574 +++++++++++++++++++++++++++++
 kernel-shark-qt/src/KsSession.hpp  | 100 +++++
 3 files changed, 675 insertions(+)
 create mode 100644 kernel-shark-qt/src/KsSession.cpp
 create mode 100644 kernel-shark-qt/src/KsSession.hpp

Patch
diff mbox series

diff --git a/kernel-shark-qt/src/CMakeLists.txt b/kernel-shark-qt/src/CMakeLists.txt
index 26b45f4..192cd12 100644
--- a/kernel-shark-qt/src/CMakeLists.txt
+++ b/kernel-shark-qt/src/CMakeLists.txt
@@ -44,6 +44,7 @@  if (Qt5Widgets_FOUND AND Qt5Network_FOUND)
 
     add_library(kshark-gui  SHARED  ${ks-guiLib_hdr_moc}    KsUtils.cpp
                                                             KsModels.cpp
+                                                            KsSession.cpp
                                                             KsGLWidget.cpp
                                                             KsDualMarker.cpp
                                                             KsWidgetsLib.cpp
diff --git a/kernel-shark-qt/src/KsSession.cpp b/kernel-shark-qt/src/KsSession.cpp
new file mode 100644
index 0000000..979bb3f
--- /dev/null
+++ b/kernel-shark-qt/src/KsSession.cpp
@@ -0,0 +1,574 @@ 
+// SPDX-License-Identifier: LGPL-2.1
+
+/*
+ * Copyright (C) 2017 VMware Inc, Yordan Karadzhov <ykaradzhov@vmware.com>
+ */
+
+/**
+ *  @file    KsSession.cpp
+ *  @brief   KernelShark Session.
+ */
+
+// KernelShark
+#include "libkshark.h"
+#include "KsSession.hpp"
+
+/** Create a KsSession object. */
+KsSession::KsSession()
+{
+	_config = kshark_config_new("kshark.config.session",
+				      KS_CONFIG_JSON);
+}
+
+/** Destroy a KsSession object. */
+KsSession::~KsSession()
+{
+	kshark_free_config_doc(_config);
+}
+
+/** Import a user session from a Json file. */
+void KsSession::importFromFile(QString jfileName)
+{
+	if (_config)
+		kshark_free_config_doc(_config);
+
+	_config = kshark_open_config_file(jfileName.toStdString().c_str(),
+					  "kshark.config.session");
+}
+
+/** Export the current user session from a Json file. */
+void KsSession::exportToFile(QString jfileName)
+{
+	kshark_save_config_file(jfileName.toStdString().c_str(), _config);
+}
+
+/**
+ * @brief Save the state of the visualization model.
+ *
+ * @param histo: Input location for the model descriptor.
+ */
+void KsSession::saveVisModel(kshark_trace_histo *histo)
+{
+	kshark_config_doc *model =
+		kshark_export_model(histo, KS_CONFIG_JSON);
+
+	kshark_config_doc_add(_config, "Model", model);
+}
+
+/**
+ * @brief Load the state of the visualization model.
+ *
+ * @param histo: Input location for the model descriptor.
+ */
+void KsSession::loadVisModel(KsGraphModel *model)
+{
+	kshark_config_doc *modelConf = kshark_config_alloc(KS_CONFIG_JSON);
+
+	if (!kshark_config_doc_get(_config, "Model", modelConf))
+		return;
+
+	kshark_import_model(model->histo(), modelConf);
+	model->update();
+}
+
+/** Save the trace data file. */
+void KsSession::saveDataFile(QString fileName)
+{
+	kshark_config_doc *file =
+		kshark_export_trace_file(fileName.toStdString().c_str(),
+					 KS_CONFIG_JSON);
+
+	kshark_config_doc_add(_config, "Data", file);
+}
+
+/** Get the trace data file. */
+QString KsSession::getDataFile(kshark_context *kshark_ctx)
+{
+	kshark_config_doc *file = kshark_config_alloc(KS_CONFIG_JSON);
+	const char *file_str;
+
+	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);
+
+	return QString();
+}
+
+/**
+ * @brief Save the configuration of the filters.
+ *
+ * @param kshark_ctx: Input location for context pointer.
+ */
+void KsSession::saveFilters(kshark_context *kshark_ctx)
+{
+	kshark_config_doc *filters =
+		kshark_export_all_filters(kshark_ctx, KS_CONFIG_JSON);
+
+	kshark_config_doc_add(_config, "Filters", filters);
+}
+
+/**
+ * @brief Load the configuration of the filters and filter the data.
+ *
+ * @param kshark_ctx: Input location for context pointer.
+ * @param data: Input location for KsDataStore object;
+ */
+void KsSession::loadFilters(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;
+
+	kshark_import_all_filters(kshark_ctx, filters);
+
+	if (kshark_ctx->advanced_event_filter->filters)
+		data->reload();
+	else
+		kshark_filter_entries(kshark_ctx, data->rows(), data->size());
+
+	data->registerCPUCollections();
+
+	emit data->updateWidgets(data);
+}
+
+/**
+ * @brief Save the state of the table.
+ *
+ * @param view: Input location for the KsTraceViewer widget.
+ */
+void KsSession::saveTable(const KsTraceViewer &view) {
+	kshark_config_doc *topRow = kshark_config_alloc(KS_CONFIG_JSON);
+	int64_t r = view.getTopRow();
+
+	topRow->conf_doc = json_object_new_int64(r);
+	kshark_config_doc_add(_config, "ViewTop",topRow);
+}
+
+/**
+ * @brief Load the state of the table.
+ *
+ * @param view: Input location for the KsTraceViewer widget.
+ */
+void KsSession::loadTable(KsTraceViewer *view) {
+	kshark_config_doc *topRow = kshark_config_alloc(KS_CONFIG_JSON);
+	size_t r = 0;
+
+	if (!kshark_config_doc_get(_config, "ViewTop", topRow))
+		return;
+
+	if (_config->format == KS_CONFIG_JSON)
+		r = json_object_get_int64(KS_JSON_CAST(topRow->conf_doc));
+
+	view->setTopRow(r);
+}
+
+/**
+ * @brief Save the KernelShark Main window size.
+ *
+ * @param window: Input location for the KsMainWindow widget.
+ */
+void KsSession::saveMainWindowSize(const QMainWindow &window)
+{
+	kshark_config_doc *windowConf = kshark_config_alloc(KS_CONFIG_JSON);
+	int width = window.width(), height = window.height();
+	json_object *jwindow = json_object_new_array();
+
+	json_object_array_put_idx(jwindow, 0, json_object_new_int(width));
+	json_object_array_put_idx(jwindow, 1, json_object_new_int(height));
+
+	windowConf->conf_doc = jwindow;
+	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(QMainWindow *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);
+		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->resize(width, height);
+	}
+}
+
+/**
+ * @brief Save the state of the Main window spliter.
+ *
+ * @param splitter: Input location for the splitter widget.
+ */
+void KsSession::saveSplitterSize(const QSplitter &splitter)
+{
+	kshark_config_doc *spl = kshark_config_alloc(KS_CONFIG_JSON);
+	json_object *jspl = json_object_new_array();
+	QList<int> sizes = splitter.sizes();
+
+	json_object_array_put_idx(jspl, 0, json_object_new_int(sizes[0]));
+	json_object_array_put_idx(jspl, 1, json_object_new_int(sizes[1]));
+
+	spl->conf_doc = jspl;
+	kshark_config_doc_add(_config, "Splitter", spl);
+}
+
+/**
+ * @brief Load the state of the Main window spliter.
+ *
+ * @param splitter: Input location for the splitter widget.
+ */
+void KsSession::loadSplitterSize(QSplitter *splitter)
+{
+	kshark_config_doc *spl = kshark_config_alloc(KS_CONFIG_JSON);
+	json_object *jspl, *jgraphsize, *jviewsize;
+	int graphSize, viewSize;
+	QList<int> sizes;
+
+	if (!kshark_config_doc_get(_config, "Splitter", spl))
+		return;
+
+	if (_config->format == KS_CONFIG_JSON) {
+		jspl = KS_JSON_CAST(spl->conf_doc);
+		jgraphsize = json_object_array_get_idx(jspl, 0);
+		jviewsize = json_object_array_get_idx(jspl, 1);
+
+		graphSize = json_object_get_int(jgraphsize);
+		viewSize = json_object_get_int(jviewsize);
+	}
+
+	sizes << graphSize << viewSize;
+	splitter->setSizes(sizes);
+}
+
+/** @brief Save the Color scheme used. */
+void KsSession::saveColorScheme() {
+	kshark_config_doc *colSch = kshark_config_alloc(KS_CONFIG_JSON);
+	double s = KsPlot::Color::getRainbowFrequency();
+
+	colSch->conf_doc = json_object_new_double(s);
+	kshark_config_doc_add(_config, "ColorScheme", colSch);
+}
+
+/** @brief Get the Color scheme used. */
+float KsSession::getColorScheme() {
+	kshark_config_doc *colSch = kshark_config_alloc(KS_CONFIG_JSON);
+
+	/* Default color scheme. */
+	float s = 0.75;
+
+	if (!kshark_config_doc_get(_config, "ColorScheme", colSch))
+		return s;
+
+	if (_config->format == KS_CONFIG_JSON)
+		s = json_object_get_double(KS_JSON_CAST(colSch->conf_doc));
+
+	return s;
+}
+
+/**
+ * @brief Save the list of the graphs plotted.
+ *
+ * @param glw: Input location for the KsGLWidget widget.
+ */
+void KsSession::saveGraphs(const KsGLWidget &glw)
+{
+	_saveCPUPlots(glw._cpuList);
+	_saveTaskPlots(glw._taskList);
+}
+
+/**
+ * @brief Load the list of the graphs and plot.
+ *
+ * @param graphs: Input location for the KsTraceGraph widget.
+ */
+void KsSession::loadGraphs(KsTraceGraph *graphs)
+{
+	graphs->cpuReDraw(_getCPUPlots());
+	graphs->taskReDraw(_getTaskPlots());
+}
+
+void KsSession::_saveCPUPlots(const QVector<int> &cpus)
+{
+	kshark_config_doc *cpuPlts = kshark_config_alloc(KS_CONFIG_JSON);
+	json_object *jcpus = json_object_new_array();
+
+	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);
+	}
+
+	cpuPlts->conf_doc = jcpus;
+	kshark_config_doc_add(_config, "CPUPlots", cpuPlts);
+}
+
+QVector<int> KsSession::_getCPUPlots()
+{
+	kshark_config_doc *cpuPlts = kshark_config_alloc(KS_CONFIG_JSON);
+	json_object *jcpus;
+	QVector<int> cpus;
+	size_t length;
+
+	if (!kshark_config_doc_get(_config, "CPUPlots", cpuPlts))
+		return cpus;
+
+	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);
+		}
+	}
+
+	return cpus;
+}
+
+void KsSession::_saveTaskPlots(const QVector<int> &tasks)
+{
+	kshark_config_doc *taskPlts = kshark_config_alloc(KS_CONFIG_JSON);
+	json_object *jtasks = json_object_new_array();
+
+	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);
+	}
+
+	taskPlts->conf_doc = jtasks;
+	kshark_config_doc_add(_config, "TaskPlots", taskPlts);
+}
+
+QVector<int> KsSession::_getTaskPlots()
+{
+	kshark_config_doc *taskPlts = kshark_config_alloc(KS_CONFIG_JSON);
+	json_object *jtasks;
+	QVector<int> tasks;
+	size_t length;
+
+	if (!kshark_config_doc_get(_config, "TaskPlots", taskPlts))
+		return tasks;
+
+	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);
+		}
+	}
+
+	return tasks;
+}
+
+/**
+ * @brief Save the state of the Dual marker.
+ *
+ * @param dm: Input location for the KsDualMarkerSM object.
+ */
+void KsSession::saveDualMarker(KsDualMarkerSM *dm)
+{
+	struct kshark_config_doc *markers =
+		kshark_config_new("kshark.config.markers", KS_CONFIG_JSON);
+	json_object *jd_mark = KS_JSON_CAST(markers->conf_doc);
+
+	auto save_mark = [&jd_mark] (KsGraphMark *m, const char *name)
+	{
+		json_object *jmark = json_object_new_object();
+
+		if (m->_isSet) {
+			json_object_object_add(jmark, "isSet",
+					       json_object_new_boolean(true));
+
+			json_object_object_add(jmark, "row",
+					       json_object_new_int(m->_pos));
+		} else {
+			json_object_object_add(jmark, "isSet",
+					       json_object_new_boolean(false));
+		}
+
+		json_object_object_add(jd_mark, name, jmark);
+	};
+
+	save_mark(&dm->markerA(), "markA");
+	save_mark(&dm->markerB(), "markB");
+
+	if (dm->getState() == DualMarkerState::A)
+		json_object_object_add(jd_mark, "Active",
+				       json_object_new_string("A"));
+	else
+		json_object_object_add(jd_mark, "Active",
+				       json_object_new_string("B"));
+
+	kshark_config_doc_add(_config, "Markers", markers);
+}
+
+/**
+ * @brief Load the state of the Dual marker.
+ *
+ * @param dm: Input location for the KsDualMarkerSM object.
+ * @param graphs: Input location for the KsTraceGraph widget.
+ */
+void KsSession::loadDualMarker(KsDualMarkerSM *dm, KsTraceGraph *graphs)
+{
+	uint64_t pos;
+
+	dm->reset();
+	dm->setState(DualMarkerState::A);
+
+	if (_getMarker("markA", &pos)) {
+		graphs->markEntry(pos);
+	} else {
+		dm->markerA().remove();
+	}
+
+	dm->setState(DualMarkerState::B);
+	if (_getMarker("markB", &pos)) {
+		graphs->markEntry(pos);
+	} else {
+		dm->markerB().remove();
+	}
+
+	dm->setState(_getMarkerState());
+	pos = dm->activeMarker()._pos;
+
+	emit graphs->glPtr()->updateView(pos, true);
+}
+
+json_object *KsSession::_getMarkerJson()
+{
+	struct kshark_config_doc *markers =
+		kshark_config_alloc(KS_CONFIG_JSON);
+
+	if (!kshark_config_doc_get(_config, "Markers", markers) ||
+	    !kshark_type_check(markers, "kshark.config.markers"))
+		return nullptr;
+
+	return KS_JSON_CAST(markers->conf_doc);
+}
+
+bool KsSession::_getMarker(const char* name, size_t *pos)
+{
+	json_object *jd_mark, *jmark;
+
+	*pos = 0;
+	jd_mark = _getMarkerJson();
+	if (!jd_mark)
+		return false;
+
+	if (json_object_object_get_ex(jd_mark, name, &jmark)) {
+		json_object *jis_set;
+		json_object_object_get_ex(jmark, "isSet", &jis_set);
+		if (!json_object_get_boolean(jis_set))
+			return false;
+
+		json_object *jpos;
+		json_object_object_get_ex(jmark, "row", &jpos);
+		*pos = json_object_get_int64(jpos);
+	}
+
+	return true;
+}
+
+DualMarkerState KsSession::_getMarkerState()
+{
+	json_object *jd_mark, *jstate;
+	const char* state;
+
+	jd_mark = _getMarkerJson();
+	json_object_object_get_ex(jd_mark, "Active", &jstate);
+	state = json_object_get_string(jstate);
+
+	if (strcmp(state, "A") == 0)
+		return DualMarkerState::A;
+
+	return DualMarkerState::B;
+}
+
+/**
+ * @brief Save the configuration of the plugins.
+ *
+ * @param pm: Input location for the KsPluginManager object.
+ */
+void KsSession::savePlugins(const KsPluginManager &pm)
+{
+	struct 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_object_add(jplugins, "Plugin List", jlist);
+	kshark_config_doc_add(_config, "Plugins", plugins);
+}
+
+/**
+ * @brief Load the configuration of the plugins.
+ *
+ * @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)
+{
+	kshark_config_doc *plugins = kshark_config_alloc(KS_CONFIG_JSON);
+	json_object *jplugins, *jlist, *jpl;
+	int length;
+
+	if (!kshark_config_doc_get(_config, "Plugins", plugins) ||
+	    !kshark_type_check(plugins, "kshark.config.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);
+			pm->_ksPluginList[i] =
+				json_object_get_string(json_object_array_get_idx(jpl, 0));
+
+			pm->_registeredKsPlugins[i] =
+				json_object_get_boolean(json_object_array_get_idx(jpl, 1));
+		}
+	}
+
+	pm->registerFromList(kshark_ctx);
+}
diff --git a/kernel-shark-qt/src/KsSession.hpp b/kernel-shark-qt/src/KsSession.hpp
new file mode 100644
index 0000000..4f5a2c4
--- /dev/null
+++ b/kernel-shark-qt/src/KsSession.hpp
@@ -0,0 +1,100 @@ 
+/* SPDX-License-Identifier: LGPL-2.1 */
+
+/*
+ * Copyright (C) 2017 VMware Inc, Yordan Karadzhov <ykaradzhov@vmware.com>
+ */
+
+/**
+ *  @file    KsSession.hpp
+ *  @brief   KernelShark Session.
+ */
+
+#ifndef _KS_SESSION_H
+#define _KS_SESSION_H
+
+// Qt
+#include <QtWidgets>
+
+// KernelShark
+#include "KsDualMarker.hpp"
+#include "KsTraceGraph.hpp"
+#include "KsTraceViewer.hpp"
+
+/**
+ * The KsSession class provides instruments for importing/exporting the state
+ * of the different components of the GUI from/to Json documents. These
+ * instruments are used to save/load user session in the GUI.
+ */
+class KsSession
+{
+public:
+	KsSession();
+
+	virtual ~KsSession();
+
+	/** Get the configuration document object. */
+	kshark_config_doc *getConfDocPtr() const {return _config;}
+
+	void importFromFile(QString jfileName);
+
+	void exportToFile(QString jfileName);
+
+	void saveDataFile(QString fileName);
+
+	QString getDataFile(kshark_context *kshark_ctx);
+
+	void saveVisModel(kshark_trace_histo *histo);
+
+	void loadVisModel(KsGraphModel *model);
+
+	void saveGraphs(const KsGLWidget &glw);
+
+	void loadGraphs(KsTraceGraph *graphs);
+
+	void saveFilters(kshark_context *kshark_ctx);
+
+	void loadFilters(kshark_context *kshark_ctx, KsDataStore *data);
+
+	void saveMainWindowSize(const QMainWindow &window);
+
+	void loadMainWindowSize(QMainWindow *window);
+
+	void saveSplitterSize(const QSplitter &splitter);
+
+	void loadSplitterSize(QSplitter *splitter);
+
+	void saveDualMarker(KsDualMarkerSM *dm);
+
+	void loadDualMarker(KsDualMarkerSM *dmm, KsTraceGraph *graphs);
+
+	void savePlugins(const KsPluginManager &pm);
+
+	void loadPlugins(kshark_context *kshark_ctx, KsPluginManager *pm);
+
+	void saveTable(const KsTraceViewer &view);
+
+	void loadTable(KsTraceViewer *view);
+
+	void saveColorScheme();
+
+	float getColorScheme();
+
+private:
+	kshark_config_doc *_config;
+
+	json_object *_getMarkerJson();
+
+	void _saveCPUPlots(const QVector<int> &cpus);
+
+	QVector<int> _getCPUPlots();
+
+	void _saveTaskPlots(const QVector<int> &tasks);
+
+	QVector<int> _getTaskPlots();
+
+	bool _getMarker(const char* name, size_t *pos);
+
+	DualMarkerState _getMarkerState();
+};
+
+#endif