diff mbox series

[2/4] kernel-shark-qt: Add KernalShark Utils

Message ID 20181008151629.13973-3-ykaradzhov@vmware.com (mailing list archive)
State Superseded
Headers show
Series Add basic components to be used by the Qt GUI | expand

Commit Message

Yordan Karadzhov Oct. 8, 2018, 3:16 p.m. UTC
From: Yordan Karadzhov <ykaradzhov@vmware.com>

This patch introduces the kshark-gui library and defines some basic
components, like Data Store and Plugin Manager, used under the hood
of the KernelShark GUI.

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 kernel-shark-qt/build/deff.h.cmake |  16 +
 kernel-shark-qt/src/CMakeLists.txt |  20 +
 kernel-shark-qt/src/KsUtils.cpp    | 584 +++++++++++++++++++++++++++++
 kernel-shark-qt/src/KsUtils.hpp    | 231 ++++++++++++
 4 files changed, 851 insertions(+)
 create mode 100644 kernel-shark-qt/src/KsUtils.cpp
 create mode 100644 kernel-shark-qt/src/KsUtils.hpp

Comments

Steven Rostedt Oct. 9, 2018, 4:34 p.m. UTC | #1
On Mon,  8 Oct 2018 18:16:27 +0300
Yordan Karadzhov <y.karadz@gmail.com> wrote:


> index 0000000..2c4cecc
> --- /dev/null
> +++ b/kernel-shark-qt/src/KsUtils.cpp
> @@ -0,0 +1,584 @@
> +// SPDX-License-Identifier: LGPL-2.1
> +
> +/*
> + * Copyright (C) 2017 VMware Inc, Yordan Karadzhov <y.karadz@gmail.com>
> + */
> +
> +/**
> + *  @file    KsUtils.cpp
> + *  @brief   KernelShark Utils.
> + */
> +
> +// KernelShark
> +#include "KsUtils.hpp"
> +
> +namespace KsUtils {
> +
> +/** @brief Geat a sorteg vector of Task's Pids.  */

 "sorteg"?

> +QVector<int> getPidList()
> +{
> +	kshark_context *kshark_ctx(nullptr);
> +	int nTasks, *tempPids;
> +	QVector<int> pids;
> +
> +	if (!kshark_instance(&kshark_ctx))
> +		return pids;
> +
> +	nTasks = kshark_get_task_pids(kshark_ctx, &tempPids);
> +	for (int r = 0; r < nTasks; ++r) {
> +		pids.append(tempPids[r]);
> +	}
> +
> +	free(tempPids);
> +
> +	qSort(pids);
> +
> +	return pids;
> +}
> +
> +/**
> + * Set the bit of the filter mask of the kshark session context responsible
> + * for the visibility of the events in the Table View.
> + */
> +void listFilterSync(bool state)
> +{
> +	kshark_context *kshark_ctx(nullptr);

Add space here.

> +	if (!kshark_instance(&kshark_ctx))
> +		return;
> +
> +	if (state) {
> +		kshark_ctx->filter_mask |= KS_TEXT_VIEW_FILTER_MASK;
> +	} else {
> +		kshark_ctx->filter_mask &= ~KS_TEXT_VIEW_FILTER_MASK;
> +	}
> +}
> +
> +/**
> + * Set the bit of the filter mask of the kshark session context responsible
> + * for the visibility of the events in the Graph View.
> + */
> +void graphFilterSync(bool state)
> +{
> +	kshark_context *kshark_ctx(nullptr);

Add space here.

> +	if (!kshark_instance(&kshark_ctx))
> +		return;
> +
> +	if (state) {
> +		kshark_ctx->filter_mask |= KS_GRAPH_VIEW_FILTER_MASK;
> +	} else {
> +		kshark_ctx->filter_mask &= ~KS_GRAPH_VIEW_FILTER_MASK;
> +	}
> +}
> +
> +/**
> + * @brief Simple CPU matching function to be user for data collections.
> + *
> + * @param kshark_ctx: Input location for the session context pointer.
> + * @param e: kshark_entry to be checked.
> + * @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)
> +{
> +	if (e->cpu == cpu && (e->visible & KS_GRAPH_VIEW_FILTER_MASK))
> +		return true;
> +
> +	return false;

BTW, you can make the above:

	return (e->cpu == cpu) &&
		(e->visible & KS_GRAPH_VIEW_FILTER_MASK);

> +}
> +
> +}; // KsUtils
> +
> +/** A stream operator for converting QColor into KsPlot::Color. */
> +KsPlot::Color& operator <<(KsPlot::Color &thisColor, const QColor &c)
> +{
> +	thisColor.set(c.red(), c.green(), c.blue());
> +
> +	return thisColor;
> +}
> +
> +/** Create a default (empty) KsDataStore. */
> +KsDataStore::KsDataStore(QWidget *parent)
> +: QObject(parent),
> +  _pevent(nullptr),
> +  _rows(nullptr),
> +  _dataSize(0)
> +{}
> +
> +/** Destroy the KsDataStore object. */
> +KsDataStore::~KsDataStore()
> +{}
> +
> +/** Load trace data for file. */
> +void KsDataStore::loadDataFile(const QString &file)
> +{
> +	kshark_context *kshark_ctx(nullptr);

Add space here.

> +	if (!kshark_instance(&kshark_ctx))
> +		return;
> +
> +	clear();
> +
> +	if (!kshark_open(kshark_ctx, file.toStdString().c_str()) ||
> +	    !kshark_ctx->handle ||
> +	    !kshark_ctx->pevent) {

Hmm, if the kshark_open() succeeds but for some reason there's not a
pevent (can that happen?) should we do a kshark_close?

> +		qCritical() << "ERROR Loading file " << file;
> +		return;
> +	}
> +
> +	_pevent = kshark_ctx->pevent;

BTW, we need to rename "pevent" to "tep", as that name is now obsolete.

> +
> +	if (kshark_ctx->event_handlers == nullptr)
> +		kshark_handle_plugins(kshark_ctx, KSHARK_PLUGIN_INIT);
> +	else
> +		kshark_handle_plugins(kshark_ctx, KSHARK_PLUGIN_UPDATE);
> +
> +	_dataSize = kshark_load_data_entries(kshark_ctx, &_rows);
> +}
> +
> +void KsDataStore::_freeData()
> +{
> +	if (_dataSize) {
> +		for (size_t r = 0; r < _dataSize; ++r)
> +			free(_rows[r]);
> +
> +		free(_rows);
> +		_rows = nullptr;
> +	}
> +}
> +
> +/** Reload the trace data. */
> +void KsDataStore::reload()
> +{
> +	kshark_context *kshark_ctx(nullptr);

Add space here.

> +	if (!kshark_instance(&kshark_ctx))
> +		return;
> +
> +	_freeData();
> +
> +	_dataSize = kshark_load_data_entries(kshark_ctx, &_rows);
> +	_pevent = kshark_ctx->pevent;
> +
> +	emit updateWidgets(this);
> +}
> +
> +/** Free the loaded trace data and close the file. */
> +void KsDataStore::clear()
> +{
> +	kshark_context *kshark_ctx(nullptr);
> +
> +	_freeData();
> +
> +	_pevent = nullptr;
> +
> +	if (kshark_instance(&kshark_ctx) &&
> +	    kshark_ctx->handle)

You can keep the two on the same line, even if it breaks the 80
character limit.

> +		kshark_close(kshark_ctx);
> +}
> +
> +/** Update the visibility of the entries (filter). */
> +void KsDataStore::update()
> +{
> +	kshark_context *kshark_ctx(nullptr);

Add space here.

> +	if (!kshark_instance(&kshark_ctx))
> +		return;
> +
> +	if (kshark_filter_is_set(kshark_ctx)) {
> +		kshark_filter_entries(kshark_ctx, _rows, _dataSize);
> +		emit updateWidgets(this);
> +	}
> +}
> +
> +/** Register a collection of visible entries for each CPU. */
> +void KsDataStore::registerCPUCollections()
> +{
> +	kshark_context *kshark_ctx(nullptr);

Add space here.

> +	if (!kshark_instance(&kshark_ctx) ||
> +	    !kshark_filter_is_set(kshark_ctx))
> +		return;
> +
> +	int nCPUs = _pevent->cpus;
> +	for (int cpu = 0; cpu < nCPUs; ++cpu) {
> +		kshark_register_data_collection(kshark_ctx,
> +						_rows, _dataSize,
> +						KsUtils::matchCPUVisible,
> +						cpu,
> +						0);
> +	}
> +}
> +
> +void KsDataStore::_unregisterCPUCollections()
> +{
> +	kshark_context *kshark_ctx(nullptr);

Add space here.

> +	if (!kshark_instance(&kshark_ctx))
> +		return;
> +
> +	int nCPUs = _pevent->cpus;
> +	for (int cpu = 0; cpu < nCPUs; ++cpu) {
> +		kshark_unregister_data_collection(&kshark_ctx->collections,
> +						  KsUtils::matchCPUVisible,
> +						  cpu);
> +	}
> +}
> +
> +void KsDataStore::_applyIdFilter(int filterId, QVector<int> vec)
> +{
> +	kshark_context *kshark_ctx(nullptr);

Add space here.

> +	if (!kshark_instance(&kshark_ctx))
> +		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);

Hmm, I'm curious to why you clear both SHOW/HIDE filters if the
filterId is one of SHOW or HIDE?

Perhaps a comment should be here explaining it too.


> +			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);
> +			break;
> +		default:
> +			return;
> +	}
> +
> +	for (auto &&pid: vec)
> +		kshark_filter_add_id(kshark_ctx, filterId, pid);
> +
> +	if (!_pevent)
> +		return;
> +
> +	_unregisterCPUCollections();
> +
> +	/*
> +	 * If the advanced event filter is set the data has to be reloaded,

I think you need a comma between "set" and "the".

> +	 * because the advanced filter uses tep_records.
> +	 */
> +	if (kshark_ctx->advanced_event_filter->filters)
> +		reload();
> +	else
> +		kshark_filter_entries(kshark_ctx, _rows, _dataSize);
> +
> +	registerCPUCollections();
> +
> +	emit updateWidgets(this);
> +}
> +
> +/** Apply Show Task filter. */
> +void KsDataStore::applyPosTaskFilter(QVector<int> vec)
> +{
> +	_applyIdFilter(KS_SHOW_TASK_FILTER, vec);
> +}
> +
> +/** Apply Hide Task filter. */
> +void KsDataStore::applyNegTaskFilter(QVector<int> vec)
> +{
> +	_applyIdFilter(KS_HIDE_TASK_FILTER, vec);
> +}
> +
> +/** Apply Show Event filter. */
> +void KsDataStore::applyPosEventFilter(QVector<int> vec)
> +{
> +	_applyIdFilter(KS_SHOW_EVENT_FILTER, vec);
> +}
> +
> +/** Apply Hide Event filter. */
> +void KsDataStore::applyNegEventFilter(QVector<int> vec)
> +{
> +	_applyIdFilter(KS_HIDE_EVENT_FILTER, vec);
> +}
> +
> +/** Disable all filters. */
> +void KsDataStore::clearAllFilters()
> +{
> +	kshark_context *kshark_ctx(nullptr);
> +	kshark_instance(&kshark_ctx);
> +
> +	if (!_pevent)
> +		return;
> +
> +	_unregisterCPUCollections();
> +
> +	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);
> +
> +	tep_filter_reset(kshark_ctx->advanced_event_filter);
> +	kshark_clear_all_filters(kshark_ctx, _rows, _dataSize);
> +
> +	emit updateWidgets(this);
> +}
> +
> +/**
> + * @brief Create Plugin Manager. Use list of plugins declared in the
> + *	  CMake-generated header file.
> + */
> +KsPluginManager::KsPluginManager(QWidget *parent)
> +: QObject(parent)
> +{
> +	kshark_context *kshark_ctx(nullptr);
> +	_parsePluginList();
> +
> +	if (!kshark_instance(&kshark_ctx))
> +		return;
> +
> +	registerFromList(kshark_ctx);
> +}
> +
> +/** Parse the plugin list declared in the CMake-generated header file. */
> +void KsPluginManager::_parsePluginList()
> +{
> +	_ksPluginList = KsUtils::getPluginList();
> +	int nPlugins = _ksPluginList.count();
> +
> +	_registeredKsPlugins.resize(nPlugins);
> +	for (int i = 0; i < nPlugins; ++i) {
> +		if (_ksPluginList[i].contains(" default", Qt::CaseInsensitive)) {
> +			_ksPluginList[i].remove(" default", Qt::CaseInsensitive);
> +			_registeredKsPlugins[i] = true;
> +		} else {
> +			_registeredKsPlugins[i] = false;
> +		}
> +	}
> +}
> +
> +/**
> + * Register the plugins by using the information in "_ksPluginList" and
> + * "_registeredKsPlugins".
> + */
> +void KsPluginManager::registerFromList(kshark_context *kshark_ctx)
> +{
> +	auto lamRegBuiltIn = [&kshark_ctx](const QString &plugin)
> +	{
> +		char *lib;
> +		int n;
> +		n = asprintf(&lib, "%s/lib/plugin-%s.so",
> +			     KS_DIR, plugin.toStdString().c_str());
> +		if (n > 0) {

Usually the if statement after a call should be the error path, unless
we need to ignore the error. That is:

		if (n <= 0)
			return;

Then the below is not indented.

> +			kshark_register_plugin(kshark_ctx, lib);
> +			free(lib);
> +		}
> +	};
> +
> +	auto lamRegUser = [&kshark_ctx](const QString &plugin)
> +	{
> +		const char *lib = plugin.toStdString().c_str();
> +		kshark_register_plugin(kshark_ctx, lib);
> +	};
> +
> +	_forEachInList(_ksPluginList,
> +		       _registeredKsPlugins,
> +		       lamRegBuiltIn);
> +
> +	_forEachInList(_userPluginList,
> +		       _registeredUserPlugins,
> +		       lamRegUser);
> +}
> +
> +/**
> + * Unegister the plugins by using the information in "_ksPluginList" and
> + * "_registeredKsPlugins".
> + */
> +void KsPluginManager::unregisterFromList(kshark_context *kshark_ctx)
> +{
> +	auto lamUregBuiltIn = [&kshark_ctx] (const QString &plugin)
> +	{
> +		char *lib;
> +		int n;
> +		n = asprintf(&lib, "%s/lib/plugin-%s.so",
> +			     KS_DIR, plugin.toStdString().c_str());
> +		if (n > 0) {

same here.

> +			kshark_unregister_plugin(kshark_ctx, lib);
> +			free(lib);
> +		}
> +	};
> +
> +	auto lamUregUser = [&kshark_ctx] (const QString &plugin)
> +	{
> +		const char *lib = plugin.toStdString().c_str();
> +		kshark_unregister_plugin(kshark_ctx, lib);
> +	};
> +
> +	_forEachInList(_ksPluginList,
> +		       _registeredKsPlugins,
> +			lamUregBuiltIn);
> +
> +	_forEachInList(_userPluginList,
> +		       _registeredUserPlugins,
> +			lamUregUser);
> +}
> +
> +/**
> + * @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).
> + */
> +void KsPluginManager::registerPlugin(const QString &plugin)
> +{
> +	kshark_context *kshark_ctx(nullptr);
> +	char *lib;
> +	int n;
> +
> +	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.
> +			 */
> +			n = asprintf(&lib, "%s/lib/plugin-%s.so",
> +					KS_DIR, plugin.toStdString().c_str());
> +			if (n > 0) {

This is fine to keep it this way, as we return either way.

> +				kshark_register_plugin(kshark_ctx, lib);
> +				_registeredKsPlugins[i] = true;
> +				free(lib);
> +			}
> +
> +			return;

Add space here.

> +		} 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);
> +			}
> +
> +			return;
> +		}
> +	}
> +
> +	/* 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());
> +
> +		_userPluginList.append(plugin);
> +		_registeredUserPlugins.append(true);
> +	} else {
> +		qCritical() << "ERROR: " << plugin << "cannot be registered!";
> +	}
> +}
> +
> +/** @brief Unregister a Plugin.
> + *<br> WARNING: Do not use this function to unregister User plugins.

Hmm, do we need the <br> above? (if this is required by DocGen I find
it rather ugly :-( )

Also, if this is not to be used to unregister User Plugins, can you add
what should be used to unregister them?


> + * @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).
> + *
> + */
> +void KsPluginManager::unregisterPlugin(const QString &plugin)
> +{
> +	kshark_context *kshark_ctx(nullptr);
> +	char *lib;
> +	int n;
> +
> +	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.
> +			 */
> +			n = asprintf(&lib, "%s/lib/plugin-%s.so", KS_DIR,
> +				     plugin.toStdString().c_str());
> +			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);
> +			}
> +
> +			return;
> +		}
> +	}
> +}
> +
> +/** Unload all plugins. */
> +void KsPluginManager::unloadAll()
> +{
> +	kshark_context *kshark_ctx(nullptr);
> +	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);
> +
> +	unregisterFromList(kshark_ctx);
> +}
> +
> +/** @brief Update (change) the Plugins.
> + *
> + * @param pluginIds: The indexes of the plugins to be loaded.
> + */
> +void KsPluginManager::updatePlugins(QVector<int> pluginIds)
> +{
> +	kshark_context *kshark_ctx(nullptr);

Add space here.

> +	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);
> +	};
> +
> +	if (!kshark_ctx->pevent) {
> +		kshark_free_plugin_list(kshark_ctx->plugins);
> +		kshark_ctx->plugins = nullptr;
> +
> +		/*
> +		 * 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;
> +	}
> +
> +	/* Clean up all old plugins first. */
> +	unloadAll();
> +
> +	/* Now load. */
> +	register_plugins(pluginIds);
> +	kshark_handle_plugins(kshark_ctx, KSHARK_PLUGIN_INIT);
> +
> +	emit dataReload();
> +}
> diff --git a/kernel-shark-qt/src/KsUtils.hpp b/kernel-shark-qt/src/KsUtils.hpp
> new file mode 100644
> index 0000000..40142ca
> --- /dev/null
> +++ b/kernel-shark-qt/src/KsUtils.hpp

Is this private to libkshark and not going to be exported?

In other words, do we need to worry about namespace?

-- Steve

> @@ -0,0 +1,231 @@
> +/* SPDX-License-Identifier: LGPL-2.1 */
> +
> +/*
> + * Copyright (C) 2017 VMware Inc, Yordan Karadzhov <y.karadz@gmail.com>
> + */
> +
> +/**
> + *  @file    KsUtils.hpp
> + *  @brief   KernelShark Utils.
> + */
> +
> +#ifndef _KS_UTILS_H
> +#define _KS_UTILS_H
> +
> +// C++ 11
> +#include <chrono>
> +
> +// Qt
> +#include <QtWidgets>
> +
> +// KernelShark
> +#include "libkshark.h"
> +#include "libkshark-model.h"
> +#include "KsCmakeDef.hpp"
> +#include "KsPlotTools.hpp"
> +
> +/** Macro providing the height of the screen in pixels. */
> +#define SCREEN_HEIGHT  QApplication::desktop()->screenGeometry().height()
> +
> +/** Macro providing the width of the screen in pixels. */
> +#define SCREEN_WIDTH   QApplication::desktop()->screenGeometry().width()
> +
> +//! @cond Doxygen_Suppress
> +
> +auto fontHeight = [] ()
> +{
> +	QFont font;
> +	QFontMetrics fm(font);
> +	return fm.height();
> +};
> +
> +auto stringWidth = [](QString s)
> +{
> +	QFont font;
> +	QFontMetrics fm(font);
> +	return fm.width(s);
> +};
> +
> +//! @endcond
> +
> +/** Macro providing the height of the font in pixels. */
> +#define FONT_HEIGHT		fontHeight()
> +
> +/** Macro providing the width of the font in pixels. */
> +#define FONT_WIDTH 		stringWidth("4")
> +
> +/** 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)
> +
> +//! @cond Doxygen_Suppress
> +
> +#define KS_JSON_CAST(doc) \
> +reinterpret_cast<json_object *>(doc)
> +
> +#define KS_C_STR_CAST(doc) \
> +reinterpret_cast<const char *>(doc)
> +
> +typedef std::chrono::high_resolution_clock::time_point  hd_time;
> +
> +#define GET_TIME std::chrono::high_resolution_clock::now()
> +
> +#define GET_DURATION(t0) \
> +std::chrono::duration_cast<std::chrono::duration<double>>( \
> +std::chrono::high_resolution_clock::now() - t0).count()
> +
> +//! @endcond
> +
> +namespace KsUtils {
> +
> +QVector<int> getPidList();
> +
> +/** @brief Geat the list of plugins. */
> +inline QStringList getPluginList() {return plugins.split(";");}
> +
> +void listFilterSync(bool state);
> +
> +void graphFilterSync(bool state);
> +
> +/** @brief Convert the timestamp of the trace record into a string showing
> + *	   the time in seconds.
> + *
> + * @param ts: Input location for the timestamp.
> + * @param prec: the number of digits after the decimal point in the return
> + *		string.
> + *
> + * @returns String showing the time in seconds.
> + */
> +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);
> +}; // KsUtils
> +
> +/** Identifier of the Dual Marker active state. */
> +enum class DualMarkerState {
> +	A,
> +	B
> +};
> +
> +/**
> + * The KsDataStore class provides the access to trace data for all KernelShark
> + * widgets.
> + */
> +class KsDataStore : public QObject
> +{
> +	Q_OBJECT
> +public:
> +	explicit KsDataStore(QWidget *parent = nullptr);
> +
> +	~KsDataStore();
> +
> +	void loadDataFile(const QString &file);
> +
> +	void clear();
> +
> +	/** Get the page event used to parse the page.. */
> +	tep_handle *pevent() const {return _pevent;}
> +
> +	/** Get the trace data array.. */
> +	struct kshark_entry **rows() const {return _rows;}
> +
> +	/** Get the size of the data array. */
> +	size_t size() const {return _dataSize;}
> +
> +	void reload();
> +
> +	void update();
> +
> +	void registerCPUCollections();
> +
> +	void applyPosTaskFilter(QVector<int>);
> +
> +	void applyNegTaskFilter(QVector<int>);
> +
> +	void applyPosEventFilter(QVector<int>);
> +
> +	void applyNegEventFilter(QVector<int>);
> +
> +	void clearAllFilters();
> +
> +signals:
> +	/**
> +	 * This signal is emitted when the data has changed and the View
> +	 * widgets have to update.
> +	 */
> +	void updateWidgets(KsDataStore *);
> +
> +private:
> +	/** Page event used to parse the page. */
> +	tep_handle		*_pevent;
> +
> +	/** Trace data array. */
> +	struct kshark_entry	**_rows;
> +
> +	/** The size of the data array. */
> +	size_t			_dataSize;
> +
> +	void _freeData();
> +	void _unregisterCPUCollections();
> +	void _applyIdFilter(int filterId, QVector<int> vec);
> +};
> +
> +/** A Plugin Manage class. */
> +class KsPluginManager : public QObject
> +{
> +	Q_OBJECT
> +public:
> +	explicit KsPluginManager(QWidget *parent = nullptr);
> +
> +	/** A list of available built-in plugins. */
> +	QStringList	_ksPluginList;
> +
> +	/** A list of registered built-in plugins. */
> +	QVector<bool>	_registeredKsPlugins;
> +
> +	/** A list of available user plugins. */
> +	QStringList	_userPluginList;
> +
> +	/** A list of registered user plugins. */
> +	QVector<bool>	_registeredUserPlugins;
> +
> +	void registerFromList(kshark_context *kshark_ctx);
> +	void unregisterFromList(kshark_context *kshark_ctx);
> +
> +	void registerPlugin(const QString &plugin);
> +	void unregisterPlugin(const QString &plugin);
> +	void unloadAll();
> +
> +	void updatePlugins(QVector<int> pluginId);
> +
> +signals:
> +	/** This signal is emitted when a plugin is loaded or unloaded. */
> +	void dataReload();
> +
> +private:
> +	void _parsePluginList();
> +
> +	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]);
> +			}
> +		}
> +	}
> +};
> +
> +KsPlot::Color& operator <<(KsPlot::Color &thisColor, const QColor &c);
> +
> +#endif
Yordan Karadzhov Oct. 10, 2018, 2:12 p.m. UTC | #2
On  9.10.2018 19:34, Steven Rostedt wrote:
>> +	if (!kshark_instance(&kshark_ctx))
>> +		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);
> Hmm, I'm curious to why you clear both SHOW/HIDE filters if the
> filterId is one of SHOW or HIDE?
> 
> Perhaps a comment should be here explaining it too.
> 
> 

This enforces that we cannot have both SHOW and HIDE filters being set.
I think that, having SHOW and HIDE filters set in the same time can be 
useful in very limited number of cases. On the other hand this is an 
easy way to confuse yourself.

Note that this restriction is at the level of the GUI code. The C API 
allows for having both SHOW and HIDE filters in the same time.

Thanks!
Yordan

>> +			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);
>> +			break;
>> +		default:
>> +			return;
>> +	}
>> +
>> +	for (auto &&pid: vec)
>> +		kshark_filter_add_id(kshark_ctx, filterId, pid);
>> +
Steven Rostedt Oct. 10, 2018, 3:22 p.m. UTC | #3
On Wed, 10 Oct 2018 17:12:40 +0300
Yordan Karadzhov <ykaradzhov@vmware.com> wrote:

> On  9.10.2018 19:34, Steven Rostedt wrote:
> >> +	if (!kshark_instance(&kshark_ctx))
> >> +		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);  
> > Hmm, I'm curious to why you clear both SHOW/HIDE filters if the
> > filterId is one of SHOW or HIDE?
> > 
> > Perhaps a comment should be here explaining it too.
> > 
> >   
> 
> This enforces that we cannot have both SHOW and HIDE filters being set.
> I think that, having SHOW and HIDE filters set in the same time can be 
> useful in very limited number of cases. On the other hand this is an 
> easy way to confuse yourself.
> 
> Note that this restriction is at the level of the GUI code. The C API 
> allows for having both SHOW and HIDE filters in the same time.
>

Actually, there are real uses to do both. You may want to show a
specific kind of event, and then you may want to filter a subset of
that event.

-- Steve
Yordan Karadzhov Oct. 10, 2018, 3:25 p.m. UTC | #4
On 10.10.2018 18:22, Steven Rostedt wrote:
> On Wed, 10 Oct 2018 17:12:40 +0300
> Yordan Karadzhov <ykaradzhov@vmware.com> wrote:
> 
>> On  9.10.2018 19:34, Steven Rostedt wrote:
>>>> +	if (!kshark_instance(&kshark_ctx))
>>>> +		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);
>>> Hmm, I'm curious to why you clear both SHOW/HIDE filters if the
>>> filterId is one of SHOW or HIDE?
>>>
>>> Perhaps a comment should be here explaining it too.
>>>
>>>    
>>
>> This enforces that we cannot have both SHOW and HIDE filters being set.
>> I think that, having SHOW and HIDE filters set in the same time can be
>> useful in very limited number of cases. On the other hand this is an
>> easy way to confuse yourself.
>>
>> Note that this restriction is at the level of the GUI code. The C API
>> allows for having both SHOW and HIDE filters in the same time.
>>
> 
> Actually, there are real uses to do both. You may want to show a
> specific kind of event, and then you may want to filter a subset of
> that event.
> 

But if you want to filter a subset of one event you have to use the 
advanced filtering, while the logic in the "switch" is only for the "Id 
filters".

Thanks!
Yordan


> -- Steve
>
Yordan Karadzhov Oct. 10, 2018, 3:27 p.m. UTC | #5
On  9.10.2018 19:34, Steven Rostedt wrote:
>> +
>> +/** @brief Unregister a Plugin.
>> + *<br> WARNING: Do not use this function to unregister User plugins.
> Hmm, do we need the <br> above? (if this is required by DocGen I find
> it rather ugly:-(  )

<br> means "break the line". Perhaps I can make it like this:


/** @brief Unregister a Plugin.
  *<br>
  * WARNING: Do not use this function to unregister User plugins.
  *
  * @param plugin: provide here the name of the plugin [...]
  *
  */

Thanks!
Yordan
Yordan Karadzhov Oct. 10, 2018, 3:27 p.m. UTC | #6
On  9.10.2018 19:34, Steven Rostedt wrote:
> Is this private to libkshark and not going to be exported?
> 
> In other words, do we need to worry about namespace?

The namespace here is used mostly for making the code easy to read.
C++ people do this because it makes the code look more OO-ish.

Note that

namespace Foo
{
	bool bar1(int x);

	void bar2();
};

is equivalent to

class Foo {
	static bool bar1(int x);

	static void bar2();
};

Thanks!
Yordan
Steven Rostedt Oct. 10, 2018, 7:02 p.m. UTC | #7
On Wed, 10 Oct 2018 18:25:52 +0300
Yordan Karadzhov <ykaradzhov@vmware.com> wrote:

> > Actually, there are real uses to do both. You may want to show a
> > specific kind of event, and then you may want to filter a subset of
> > that event.
> >   
> 
> But if you want to filter a subset of one event you have to use the 
> advanced filtering, while the logic in the "switch" is only for the "Id 
> filters".

I guess I was thinking about filtering to show all events under a
specific system, and then hiding events from within that system, but it
probably is just easier to pick those events not to show in the gui (I
was playing with the old KS to see how it did things).

-- Steve
Steven Rostedt Oct. 10, 2018, 7:04 p.m. UTC | #8
On Wed, 10 Oct 2018 18:27:53 +0300
Yordan Karadzhov <ykaradzhov@vmware.com> wrote:

> On  9.10.2018 19:34, Steven Rostedt wrote:
> > Is this private to libkshark and not going to be exported?
> > 
> > In other words, do we need to worry about namespace?  
> 
> The namespace here is used mostly for making the code easy to read.
> C++ people do this because it makes the code look more OO-ish.
> 
> Note that

Never mind. My question was because I *missed* seeing:

 +namespace KsUtils {

And was asking if we want to have a namespace.

-- Steve

> 
> namespace Foo
> {
> 	bool bar1(int x);
> 
> 	void bar2();
> };
> 
> is equivalent to
> 
> class Foo {
> 	static bool bar1(int x);
> 
> 	static void bar2();
> };
> 
> Thanks!
> Yordan
diff mbox series

Patch

diff --git a/kernel-shark-qt/build/deff.h.cmake b/kernel-shark-qt/build/deff.h.cmake
index 44ea08b..d1a1bb7 100644
--- a/kernel-shark-qt/build/deff.h.cmake
+++ b/kernel-shark-qt/build/deff.h.cmake
@@ -14,7 +14,23 @@ 
 /** KernelShark source code path. */
 #cmakedefine KS_DIR "@KS_DIR@"
 
+/** KernelShark configuration directory path. */
+#cmakedefine KS_CONF_DIR "@KS_CONF_DIR@"
+
 /** Location of the trace-cmd executable. */
 #cmakedefine TRACECMD_BIN_DIR "@TRACECMD_BIN_DIR@"
 
+#ifdef __cplusplus
+
+	#include <QString>
+
+	/**
+	 * String containing semicolon-separated list of plugin names.
+	 * The plugins to be loaded when KernelShark starts are tagged
+	 * with "default".
+	 */
+	const QString plugins = "@PLUGINS@";
+
+#endif /* __cplusplus */
+
 #endif // _KS_CONFIG_H
diff --git a/kernel-shark-qt/src/CMakeLists.txt b/kernel-shark-qt/src/CMakeLists.txt
index 305cea7..e897e9a 100644
--- a/kernel-shark-qt/src/CMakeLists.txt
+++ b/kernel-shark-qt/src/CMakeLists.txt
@@ -28,6 +28,26 @@  if (OPENGL_FOUND AND GLUT_FOUND)
 
 endif (OPENGL_FOUND AND GLUT_FOUND)
 
+if (Qt5Widgets_FOUND AND Qt5Network_FOUND)
+
+    message(STATUS "libkshark-gui")
+    set (ks-guiLib_hdr  KsUtils.hpp)
+
+    QT5_WRAP_CPP(ks-guiLib_hdr_moc ${ks-guiLib_hdr})
+
+    add_library(kshark-gui  SHARED  ${ks-guiLib_hdr_moc}    KsUtils.cpp)
+
+    target_link_libraries(kshark-gui kshark-plot
+                                     ${CMAKE_DL_LIBS}
+                                     ${TRACEEVENT_LIBRARY}
+                                     ${TRACECMD_LIBRARY}
+                                     Qt5::Widgets
+                                     Qt5::Network)
+
+    set_target_properties(kshark-gui PROPERTIES  SUFFIX ".so.${KS_VERSION_STRING}")
+
+endif (Qt5Widgets_FOUND AND Qt5Network_FOUND)
+
 add_subdirectory(plugins)
 
 configure_file( ${KS_DIR}/build/deff.h.cmake
diff --git a/kernel-shark-qt/src/KsUtils.cpp b/kernel-shark-qt/src/KsUtils.cpp
new file mode 100644
index 0000000..2c4cecc
--- /dev/null
+++ b/kernel-shark-qt/src/KsUtils.cpp
@@ -0,0 +1,584 @@ 
+// SPDX-License-Identifier: LGPL-2.1
+
+/*
+ * Copyright (C) 2017 VMware Inc, Yordan Karadzhov <y.karadz@gmail.com>
+ */
+
+/**
+ *  @file    KsUtils.cpp
+ *  @brief   KernelShark Utils.
+ */
+
+// KernelShark
+#include "KsUtils.hpp"
+
+namespace KsUtils {
+
+/** @brief Geat a sorteg vector of Task's Pids.  */
+QVector<int> getPidList()
+{
+	kshark_context *kshark_ctx(nullptr);
+	int nTasks, *tempPids;
+	QVector<int> pids;
+
+	if (!kshark_instance(&kshark_ctx))
+		return pids;
+
+	nTasks = kshark_get_task_pids(kshark_ctx, &tempPids);
+	for (int r = 0; r < nTasks; ++r) {
+		pids.append(tempPids[r]);
+	}
+
+	free(tempPids);
+
+	qSort(pids);
+
+	return pids;
+}
+
+/**
+ * Set the bit of the filter mask of the kshark session context responsible
+ * for the visibility of the events in the Table View.
+ */
+void listFilterSync(bool state)
+{
+	kshark_context *kshark_ctx(nullptr);
+	if (!kshark_instance(&kshark_ctx))
+		return;
+
+	if (state) {
+		kshark_ctx->filter_mask |= KS_TEXT_VIEW_FILTER_MASK;
+	} else {
+		kshark_ctx->filter_mask &= ~KS_TEXT_VIEW_FILTER_MASK;
+	}
+}
+
+/**
+ * Set the bit of the filter mask of the kshark session context responsible
+ * for the visibility of the events in the Graph View.
+ */
+void graphFilterSync(bool state)
+{
+	kshark_context *kshark_ctx(nullptr);
+	if (!kshark_instance(&kshark_ctx))
+		return;
+
+	if (state) {
+		kshark_ctx->filter_mask |= KS_GRAPH_VIEW_FILTER_MASK;
+	} else {
+		kshark_ctx->filter_mask &= ~KS_GRAPH_VIEW_FILTER_MASK;
+	}
+}
+
+/**
+ * @brief Simple CPU matching function to be user for data collections.
+ *
+ * @param kshark_ctx: Input location for the session context pointer.
+ * @param e: kshark_entry to be checked.
+ * @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)
+{
+	if (e->cpu == cpu && (e->visible & KS_GRAPH_VIEW_FILTER_MASK))
+		return true;
+
+	return false;
+}
+
+}; // KsUtils
+
+/** A stream operator for converting QColor into KsPlot::Color. */
+KsPlot::Color& operator <<(KsPlot::Color &thisColor, const QColor &c)
+{
+	thisColor.set(c.red(), c.green(), c.blue());
+
+	return thisColor;
+}
+
+/** Create a default (empty) KsDataStore. */
+KsDataStore::KsDataStore(QWidget *parent)
+: QObject(parent),
+  _pevent(nullptr),
+  _rows(nullptr),
+  _dataSize(0)
+{}
+
+/** Destroy the KsDataStore object. */
+KsDataStore::~KsDataStore()
+{}
+
+/** Load trace data for file. */
+void KsDataStore::loadDataFile(const QString &file)
+{
+	kshark_context *kshark_ctx(nullptr);
+	if (!kshark_instance(&kshark_ctx))
+		return;
+
+	clear();
+
+	if (!kshark_open(kshark_ctx, file.toStdString().c_str()) ||
+	    !kshark_ctx->handle ||
+	    !kshark_ctx->pevent) {
+		qCritical() << "ERROR Loading file " << file;
+		return;
+	}
+
+	_pevent = kshark_ctx->pevent;
+
+	if (kshark_ctx->event_handlers == nullptr)
+		kshark_handle_plugins(kshark_ctx, KSHARK_PLUGIN_INIT);
+	else
+		kshark_handle_plugins(kshark_ctx, KSHARK_PLUGIN_UPDATE);
+
+	_dataSize = kshark_load_data_entries(kshark_ctx, &_rows);
+}
+
+void KsDataStore::_freeData()
+{
+	if (_dataSize) {
+		for (size_t r = 0; r < _dataSize; ++r)
+			free(_rows[r]);
+
+		free(_rows);
+		_rows = nullptr;
+	}
+}
+
+/** Reload the trace data. */
+void KsDataStore::reload()
+{
+	kshark_context *kshark_ctx(nullptr);
+	if (!kshark_instance(&kshark_ctx))
+		return;
+
+	_freeData();
+
+	_dataSize = kshark_load_data_entries(kshark_ctx, &_rows);
+	_pevent = kshark_ctx->pevent;
+
+	emit updateWidgets(this);
+}
+
+/** Free the loaded trace data and close the file. */
+void KsDataStore::clear()
+{
+	kshark_context *kshark_ctx(nullptr);
+
+	_freeData();
+
+	_pevent = nullptr;
+
+	if (kshark_instance(&kshark_ctx) &&
+	    kshark_ctx->handle)
+		kshark_close(kshark_ctx);
+}
+
+/** Update the visibility of the entries (filter). */
+void KsDataStore::update()
+{
+	kshark_context *kshark_ctx(nullptr);
+	if (!kshark_instance(&kshark_ctx))
+		return;
+
+	if (kshark_filter_is_set(kshark_ctx)) {
+		kshark_filter_entries(kshark_ctx, _rows, _dataSize);
+		emit updateWidgets(this);
+	}
+}
+
+/** Register a collection of visible entries for each CPU. */
+void KsDataStore::registerCPUCollections()
+{
+	kshark_context *kshark_ctx(nullptr);
+	if (!kshark_instance(&kshark_ctx) ||
+	    !kshark_filter_is_set(kshark_ctx))
+		return;
+
+	int nCPUs = _pevent->cpus;
+	for (int cpu = 0; cpu < nCPUs; ++cpu) {
+		kshark_register_data_collection(kshark_ctx,
+						_rows, _dataSize,
+						KsUtils::matchCPUVisible,
+						cpu,
+						0);
+	}
+}
+
+void KsDataStore::_unregisterCPUCollections()
+{
+	kshark_context *kshark_ctx(nullptr);
+	if (!kshark_instance(&kshark_ctx))
+		return;
+
+	int nCPUs = _pevent->cpus;
+	for (int cpu = 0; cpu < nCPUs; ++cpu) {
+		kshark_unregister_data_collection(&kshark_ctx->collections,
+						  KsUtils::matchCPUVisible,
+						  cpu);
+	}
+}
+
+void KsDataStore::_applyIdFilter(int filterId, QVector<int> vec)
+{
+	kshark_context *kshark_ctx(nullptr);
+	if (!kshark_instance(&kshark_ctx))
+		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);
+			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);
+			break;
+		default:
+			return;
+	}
+
+	for (auto &&pid: vec)
+		kshark_filter_add_id(kshark_ctx, filterId, pid);
+
+	if (!_pevent)
+		return;
+
+	_unregisterCPUCollections();
+
+	/*
+	 * 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)
+		reload();
+	else
+		kshark_filter_entries(kshark_ctx, _rows, _dataSize);
+
+	registerCPUCollections();
+
+	emit updateWidgets(this);
+}
+
+/** Apply Show Task filter. */
+void KsDataStore::applyPosTaskFilter(QVector<int> vec)
+{
+	_applyIdFilter(KS_SHOW_TASK_FILTER, vec);
+}
+
+/** Apply Hide Task filter. */
+void KsDataStore::applyNegTaskFilter(QVector<int> vec)
+{
+	_applyIdFilter(KS_HIDE_TASK_FILTER, vec);
+}
+
+/** Apply Show Event filter. */
+void KsDataStore::applyPosEventFilter(QVector<int> vec)
+{
+	_applyIdFilter(KS_SHOW_EVENT_FILTER, vec);
+}
+
+/** Apply Hide Event filter. */
+void KsDataStore::applyNegEventFilter(QVector<int> vec)
+{
+	_applyIdFilter(KS_HIDE_EVENT_FILTER, vec);
+}
+
+/** Disable all filters. */
+void KsDataStore::clearAllFilters()
+{
+	kshark_context *kshark_ctx(nullptr);
+	kshark_instance(&kshark_ctx);
+
+	if (!_pevent)
+		return;
+
+	_unregisterCPUCollections();
+
+	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);
+
+	tep_filter_reset(kshark_ctx->advanced_event_filter);
+	kshark_clear_all_filters(kshark_ctx, _rows, _dataSize);
+
+	emit updateWidgets(this);
+}
+
+/**
+ * @brief Create Plugin Manager. Use list of plugins declared in the
+ *	  CMake-generated header file.
+ */
+KsPluginManager::KsPluginManager(QWidget *parent)
+: QObject(parent)
+{
+	kshark_context *kshark_ctx(nullptr);
+	_parsePluginList();
+
+	if (!kshark_instance(&kshark_ctx))
+		return;
+
+	registerFromList(kshark_ctx);
+}
+
+/** Parse the plugin list declared in the CMake-generated header file. */
+void KsPluginManager::_parsePluginList()
+{
+	_ksPluginList = KsUtils::getPluginList();
+	int nPlugins = _ksPluginList.count();
+
+	_registeredKsPlugins.resize(nPlugins);
+	for (int i = 0; i < nPlugins; ++i) {
+		if (_ksPluginList[i].contains(" default", Qt::CaseInsensitive)) {
+			_ksPluginList[i].remove(" default", Qt::CaseInsensitive);
+			_registeredKsPlugins[i] = true;
+		} else {
+			_registeredKsPlugins[i] = false;
+		}
+	}
+}
+
+/**
+ * Register the plugins by using the information in "_ksPluginList" and
+ * "_registeredKsPlugins".
+ */
+void KsPluginManager::registerFromList(kshark_context *kshark_ctx)
+{
+	auto lamRegBuiltIn = [&kshark_ctx](const QString &plugin)
+	{
+		char *lib;
+		int n;
+		n = asprintf(&lib, "%s/lib/plugin-%s.so",
+			     KS_DIR, plugin.toStdString().c_str());
+		if (n > 0) {
+			kshark_register_plugin(kshark_ctx, lib);
+			free(lib);
+		}
+	};
+
+	auto lamRegUser = [&kshark_ctx](const QString &plugin)
+	{
+		const char *lib = plugin.toStdString().c_str();
+		kshark_register_plugin(kshark_ctx, lib);
+	};
+
+	_forEachInList(_ksPluginList,
+		       _registeredKsPlugins,
+		       lamRegBuiltIn);
+
+	_forEachInList(_userPluginList,
+		       _registeredUserPlugins,
+		       lamRegUser);
+}
+
+/**
+ * Unegister the plugins by using the information in "_ksPluginList" and
+ * "_registeredKsPlugins".
+ */
+void KsPluginManager::unregisterFromList(kshark_context *kshark_ctx)
+{
+	auto lamUregBuiltIn = [&kshark_ctx] (const QString &plugin)
+	{
+		char *lib;
+		int n;
+		n = asprintf(&lib, "%s/lib/plugin-%s.so",
+			     KS_DIR, plugin.toStdString().c_str());
+		if (n > 0) {
+			kshark_unregister_plugin(kshark_ctx, lib);
+			free(lib);
+		}
+	};
+
+	auto lamUregUser = [&kshark_ctx] (const QString &plugin)
+	{
+		const char *lib = plugin.toStdString().c_str();
+		kshark_unregister_plugin(kshark_ctx, lib);
+	};
+
+	_forEachInList(_ksPluginList,
+		       _registeredKsPlugins,
+			lamUregBuiltIn);
+
+	_forEachInList(_userPluginList,
+		       _registeredUserPlugins,
+			lamUregUser);
+}
+
+/**
+ * @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).
+ */
+void KsPluginManager::registerPlugin(const QString &plugin)
+{
+	kshark_context *kshark_ctx(nullptr);
+	char *lib;
+	int n;
+
+	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.
+			 */
+			n = asprintf(&lib, "%s/lib/plugin-%s.so",
+					KS_DIR, plugin.toStdString().c_str());
+			if (n > 0) {
+				kshark_register_plugin(kshark_ctx, lib);
+				_registeredKsPlugins[i] = true;
+				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_register_plugin(kshark_ctx, lib);
+				_registeredKsPlugins[i] = true;
+				free(lib);
+			}
+
+			return;
+		}
+	}
+
+	/* 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());
+
+		_userPluginList.append(plugin);
+		_registeredUserPlugins.append(true);
+	} else {
+		qCritical() << "ERROR: " << plugin << "cannot be registered!";
+	}
+}
+
+/** @brief Unregister a Plugin.
+ *<br> WARNING: Do not use this function to unregister User plugins.
+ * @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).
+ *
+ */
+void KsPluginManager::unregisterPlugin(const QString &plugin)
+{
+	kshark_context *kshark_ctx(nullptr);
+	char *lib;
+	int n;
+
+	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.
+			 */
+			n = asprintf(&lib, "%s/lib/plugin-%s.so", KS_DIR,
+				     plugin.toStdString().c_str());
+			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);
+			}
+
+			return;
+		}
+	}
+}
+
+/** Unload all plugins. */
+void KsPluginManager::unloadAll()
+{
+	kshark_context *kshark_ctx(nullptr);
+	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);
+
+	unregisterFromList(kshark_ctx);
+}
+
+/** @brief Update (change) the Plugins.
+ *
+ * @param pluginIds: The indexes of the plugins to be loaded.
+ */
+void KsPluginManager::updatePlugins(QVector<int> pluginIds)
+{
+	kshark_context *kshark_ctx(nullptr);
+	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);
+	};
+
+	if (!kshark_ctx->pevent) {
+		kshark_free_plugin_list(kshark_ctx->plugins);
+		kshark_ctx->plugins = nullptr;
+
+		/*
+		 * 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;
+	}
+
+	/* Clean up all old plugins first. */
+	unloadAll();
+
+	/* Now load. */
+	register_plugins(pluginIds);
+	kshark_handle_plugins(kshark_ctx, KSHARK_PLUGIN_INIT);
+
+	emit dataReload();
+}
diff --git a/kernel-shark-qt/src/KsUtils.hpp b/kernel-shark-qt/src/KsUtils.hpp
new file mode 100644
index 0000000..40142ca
--- /dev/null
+++ b/kernel-shark-qt/src/KsUtils.hpp
@@ -0,0 +1,231 @@ 
+/* SPDX-License-Identifier: LGPL-2.1 */
+
+/*
+ * Copyright (C) 2017 VMware Inc, Yordan Karadzhov <y.karadz@gmail.com>
+ */
+
+/**
+ *  @file    KsUtils.hpp
+ *  @brief   KernelShark Utils.
+ */
+
+#ifndef _KS_UTILS_H
+#define _KS_UTILS_H
+
+// C++ 11
+#include <chrono>
+
+// Qt
+#include <QtWidgets>
+
+// KernelShark
+#include "libkshark.h"
+#include "libkshark-model.h"
+#include "KsCmakeDef.hpp"
+#include "KsPlotTools.hpp"
+
+/** Macro providing the height of the screen in pixels. */
+#define SCREEN_HEIGHT  QApplication::desktop()->screenGeometry().height()
+
+/** Macro providing the width of the screen in pixels. */
+#define SCREEN_WIDTH   QApplication::desktop()->screenGeometry().width()
+
+//! @cond Doxygen_Suppress
+
+auto fontHeight = [] ()
+{
+	QFont font;
+	QFontMetrics fm(font);
+	return fm.height();
+};
+
+auto stringWidth = [](QString s)
+{
+	QFont font;
+	QFontMetrics fm(font);
+	return fm.width(s);
+};
+
+//! @endcond
+
+/** Macro providing the height of the font in pixels. */
+#define FONT_HEIGHT		fontHeight()
+
+/** Macro providing the width of the font in pixels. */
+#define FONT_WIDTH 		stringWidth("4")
+
+/** 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)
+
+//! @cond Doxygen_Suppress
+
+#define KS_JSON_CAST(doc) \
+reinterpret_cast<json_object *>(doc)
+
+#define KS_C_STR_CAST(doc) \
+reinterpret_cast<const char *>(doc)
+
+typedef std::chrono::high_resolution_clock::time_point  hd_time;
+
+#define GET_TIME std::chrono::high_resolution_clock::now()
+
+#define GET_DURATION(t0) \
+std::chrono::duration_cast<std::chrono::duration<double>>( \
+std::chrono::high_resolution_clock::now() - t0).count()
+
+//! @endcond
+
+namespace KsUtils {
+
+QVector<int> getPidList();
+
+/** @brief Geat the list of plugins. */
+inline QStringList getPluginList() {return plugins.split(";");}
+
+void listFilterSync(bool state);
+
+void graphFilterSync(bool state);
+
+/** @brief Convert the timestamp of the trace record into a string showing
+ *	   the time in seconds.
+ *
+ * @param ts: Input location for the timestamp.
+ * @param prec: the number of digits after the decimal point in the return
+ *		string.
+ *
+ * @returns String showing the time in seconds.
+ */
+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);
+}; // KsUtils
+
+/** Identifier of the Dual Marker active state. */
+enum class DualMarkerState {
+	A,
+	B
+};
+
+/**
+ * The KsDataStore class provides the access to trace data for all KernelShark
+ * widgets.
+ */
+class KsDataStore : public QObject
+{
+	Q_OBJECT
+public:
+	explicit KsDataStore(QWidget *parent = nullptr);
+
+	~KsDataStore();
+
+	void loadDataFile(const QString &file);
+
+	void clear();
+
+	/** Get the page event used to parse the page.. */
+	tep_handle *pevent() const {return _pevent;}
+
+	/** Get the trace data array.. */
+	struct kshark_entry **rows() const {return _rows;}
+
+	/** Get the size of the data array. */
+	size_t size() const {return _dataSize;}
+
+	void reload();
+
+	void update();
+
+	void registerCPUCollections();
+
+	void applyPosTaskFilter(QVector<int>);
+
+	void applyNegTaskFilter(QVector<int>);
+
+	void applyPosEventFilter(QVector<int>);
+
+	void applyNegEventFilter(QVector<int>);
+
+	void clearAllFilters();
+
+signals:
+	/**
+	 * This signal is emitted when the data has changed and the View
+	 * widgets have to update.
+	 */
+	void updateWidgets(KsDataStore *);
+
+private:
+	/** Page event used to parse the page. */
+	tep_handle		*_pevent;
+
+	/** Trace data array. */
+	struct kshark_entry	**_rows;
+
+	/** The size of the data array. */
+	size_t			_dataSize;
+
+	void _freeData();
+	void _unregisterCPUCollections();
+	void _applyIdFilter(int filterId, QVector<int> vec);
+};
+
+/** A Plugin Manage class. */
+class KsPluginManager : public QObject
+{
+	Q_OBJECT
+public:
+	explicit KsPluginManager(QWidget *parent = nullptr);
+
+	/** A list of available built-in plugins. */
+	QStringList	_ksPluginList;
+
+	/** A list of registered built-in plugins. */
+	QVector<bool>	_registeredKsPlugins;
+
+	/** A list of available user plugins. */
+	QStringList	_userPluginList;
+
+	/** A list of registered user plugins. */
+	QVector<bool>	_registeredUserPlugins;
+
+	void registerFromList(kshark_context *kshark_ctx);
+	void unregisterFromList(kshark_context *kshark_ctx);
+
+	void registerPlugin(const QString &plugin);
+	void unregisterPlugin(const QString &plugin);
+	void unloadAll();
+
+	void updatePlugins(QVector<int> pluginId);
+
+signals:
+	/** This signal is emitted when a plugin is loaded or unloaded. */
+	void dataReload();
+
+private:
+	void _parsePluginList();
+
+	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]);
+			}
+		}
+	}
+};
+
+KsPlot::Color& operator <<(KsPlot::Color &thisColor, const QColor &c);
+
+#endif