diff mbox series

[v2,14/23] kernel-shark-qt: Add dialog for of trace data recording

Message ID 20181016155232.5257-15-ykaradzhov@vmware.com (mailing list archive)
State Accepted
Headers show
Series Add Qt-based GUI for KernelShark | expand

Commit Message

Yordan Karadzhov Oct. 16, 2018, 3:53 p.m. UTC
From: Yordan Karadzhov (VMware) <y.karadz@gmail.com>

This patch defines a dialog to be used for trace data recording.
The dialog has two main areas: a Control panel and a terminal-like
widget for monitoring the recording process.

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 kernel-shark-qt/src/CMakeLists.txt      |   2 +
 kernel-shark-qt/src/KsCaptureDialog.cpp | 562 ++++++++++++++++++++++++
 kernel-shark-qt/src/KsCaptureDialog.hpp | 185 ++++++++
 3 files changed, 749 insertions(+)
 create mode 100644 kernel-shark-qt/src/KsCaptureDialog.cpp
 create mode 100644 kernel-shark-qt/src/KsCaptureDialog.hpp
diff mbox series

Patch

diff --git a/kernel-shark-qt/src/CMakeLists.txt b/kernel-shark-qt/src/CMakeLists.txt
index b51980f..3c9e1bf 100644
--- a/kernel-shark-qt/src/CMakeLists.txt
+++ b/kernel-shark-qt/src/CMakeLists.txt
@@ -39,6 +39,7 @@  if (Qt5Widgets_FOUND AND Qt5Network_FOUND)
                         KsTraceGraph.hpp
                         KsTraceViewer.hpp
                         KsMainWindow.hpp
+                        KsCaptureDialog.hpp
                         KsAdvFilteringDialog.hpp)
 
     QT5_WRAP_CPP(ks-guiLib_hdr_moc ${ks-guiLib_hdr})
@@ -52,6 +53,7 @@  if (Qt5Widgets_FOUND AND Qt5Network_FOUND)
                                                             KsTraceGraph.cpp
                                                             KsTraceViewer.cpp
                                                             KsMainWindow.cpp
+                                                            KsCaptureDialog.cpp
                                                             KsAdvFilteringDialog.cpp)
 
     target_link_libraries(kshark-gui kshark-plot
diff --git a/kernel-shark-qt/src/KsCaptureDialog.cpp b/kernel-shark-qt/src/KsCaptureDialog.cpp
new file mode 100644
index 0000000..ee1abc3
--- /dev/null
+++ b/kernel-shark-qt/src/KsCaptureDialog.cpp
@@ -0,0 +1,562 @@ 
+// SPDX-License-Identifier: LGPL-2.1
+
+/*
+ * Copyright (C) 2017 VMware Inc, Yordan Karadzhov <ykaradzhov@vmware.com>
+ */
+
+/**
+ *  @file    KsCaptureDialog.cpp
+ *  @brief   Dialog for trace data recording.
+ */
+
+// Qt
+#include <QLocalSocket>
+
+// KernelShark
+#include "libkshark.h"
+#include "KsUtils.hpp"
+#include "KsCmakeDef.hpp"
+#include "KsCaptureDialog.hpp"
+
+static inline tep_handle *local_events()
+{
+	return tracecmd_local_events(tracecmd_get_tracing_dir());
+}
+
+/** @brief Create KsCaptureControl widget. */
+KsCaptureControl::KsCaptureControl(QWidget *parent)
+: QWidget(parent),
+  _localTEP(local_events()),
+  _eventsWidget(_localTEP, this),
+  _pluginsLabel("Plugin: ", this),
+  _outputLabel("Output file: ", this),
+  _commandLabel("Command: ", this),
+  _outputLineEdit("trace.dat", this),
+  _commandLineEdit("sleep 0.1", this),
+  _settingsToolBar(this),
+  _controlToolBar(this),
+  _pluginsComboBox(this),
+  _importSettingsButton("Import Settings", this),
+  _exportSettingsButton("Export Settings", this),
+  _outputBrowseButton("Browse", this),
+  _commandCheckBox("Display output", this),
+  _captureButton("Capture", &_controlToolBar),
+  _applyButton("Apply", &_controlToolBar),
+  _closeButton("Close", &_controlToolBar)
+{
+	QStringList pluginList = _getPlugins();
+	int row(0);
+
+	auto lamAddLine = [&] {
+		QFrame* line = new QFrame();
+
+		line->setFrameShape(QFrame::HLine);
+		line->setFrameShadow(QFrame::Sunken);
+		_topLayout.addWidget(line);
+	};
+
+	if (pluginList.count() == 0) {
+		/*
+		 * No plugins have been found. Most likely this is because
+		 * the process has no Root privileges.
+		 */
+		QString message("Error: No events or plugins found.\n");
+		message += "Root privileges are required.";
+		QLabel *errorLabel = new QLabel(message);
+
+		errorLabel->setStyleSheet("QLabel {color : red;}");
+		_topLayout.addWidget(errorLabel);
+
+		lamAddLine();
+	}
+
+	pluginList.prepend("nop");
+
+	_settingsToolBar.addWidget(&_importSettingsButton);
+	_settingsToolBar.addSeparator();
+	_settingsToolBar.addWidget(&_exportSettingsButton);
+	_topLayout.addWidget(&_settingsToolBar);
+
+	lamAddLine();
+
+	_eventsWidget.setDefault(false);
+	_eventsWidget.setMinimumHeight(25 * FONT_HEIGHT);
+	_topLayout.addWidget(&_eventsWidget);
+
+	_pluginsLabel.adjustSize();
+	_execLayout.addWidget(&_pluginsLabel, row, 0);
+
+	_pluginsComboBox.addItems(pluginList);
+	_execLayout.addWidget(&_pluginsComboBox, row++, 1);
+
+	_outputLabel.adjustSize();
+	_execLayout.addWidget(&_outputLabel, row, 0);
+	_outputLineEdit.setFixedWidth(FONT_WIDTH * 30);
+	_execLayout.addWidget(&_outputLineEdit, row, 1);
+	_outputBrowseButton.adjustSize();
+	_execLayout.addWidget(&_outputBrowseButton, row++, 2);
+
+	_commandLabel.adjustSize();
+	_commandLabel.setFixedWidth(_outputLabel.width());
+	_execLayout.addWidget(&_commandLabel, row, 0);
+	_commandLineEdit.setFixedWidth(FONT_WIDTH * 30);
+	_execLayout.addWidget(&_commandLineEdit, row, 1);
+	_commandCheckBox.setCheckState(Qt::Unchecked);
+	_commandCheckBox.adjustSize();
+	_execLayout.addWidget(&_commandCheckBox, row++, 2);
+
+	_topLayout.addLayout(&_execLayout);
+
+	lamAddLine();
+
+	_captureButton.setFixedWidth(STRING_WIDTH("_Capture_") + FONT_WIDTH * 2);
+	_applyButton.setFixedWidth(_captureButton.width());
+	_closeButton.setFixedWidth(_captureButton.width());
+
+	_controlToolBar.addWidget(&_captureButton);
+	_controlToolBar.addWidget(&_applyButton);
+	_controlToolBar.addWidget(&_closeButton);
+	_topLayout.addWidget(&_controlToolBar);
+
+	setLayout(&_topLayout);
+
+	connect(&_importSettingsButton,	&QPushButton::pressed,
+		this,			&KsCaptureControl::_importSettings);
+
+	connect(&_exportSettingsButton,	&QPushButton::pressed,
+		this,			&KsCaptureControl::_exportSettings);
+
+	connect(&_outputBrowseButton,	&QPushButton::pressed,
+		this,			&KsCaptureControl::_browse);
+
+	connect(&_applyButton,		&QPushButton::pressed,
+		this,			&KsCaptureControl::_apply);
+}
+
+/**
+ * Use the settings of the Control panel to generate a list of command line
+ * arguments for trace-cmd.
+ */
+QStringList KsCaptureControl::getArgs()
+{
+	QStringList argv;
+
+	argv << "record";
+	argv << "-p" << _pluginsComboBox.currentText();
+
+	if (_eventsWidget.all()) {
+		argv << "-e" << "all";
+	} else {
+		QVector<int> evtIds = _eventsWidget.getCheckedIds();
+		tep_event_format *event;
+
+		for (auto const &id: evtIds) {
+			event = tep_find_event(_localTEP, id);
+			if (!event)
+				continue;
+
+			argv << "-e" + QString(event->system) +
+				":" + QString(event->name);
+		}
+	}
+
+	argv << "-o" << outputFileName();
+	argv << _commandLineEdit.text().split(" ");
+
+	return argv;
+}
+
+QStringList KsCaptureControl::_getPlugins()
+{
+	QStringList pluginList;
+	char **all_plugins;
+
+	all_plugins = tracecmd_local_plugins(tracecmd_get_tracing_dir());
+
+	if (!all_plugins)
+		return pluginList;
+
+	for (int i = 0; all_plugins[i]; ++i) {
+		/*
+		 * TODO plugin selection here.
+		 * printf("plugin %i %s\n", i, all_plugins[i]);
+		 */
+		pluginList << all_plugins[i];
+		free(all_plugins[i]);
+	}
+
+	free (all_plugins);
+	qSort(pluginList);
+
+	return pluginList;
+}
+
+void KsCaptureControl::_importSettings()
+{
+	int nEvts = tep_get_events_count(_localTEP);
+	kshark_config_doc *conf, *jevents, *temp;
+	QVector<bool> v(nEvts, false);
+	tracecmd_filter_id *eventHash;
+	tep_event_format **events;
+	QString fileName;
+
+
+	/** Get all available events. */
+	events = tep_list_events(_localTEP, TEP_EVENT_SORT_SYSTEM);
+
+	/* Get the configuration document. */
+	fileName = QFileDialog::getOpenFileName(this,
+						"Import from Filter",
+						KS_DIR,
+						"Kernel Shark Config files (*.json);;");
+
+	if (fileName.isEmpty())
+		return;
+
+	conf = kshark_open_config_file(fileName.toStdString().c_str(),
+				       "kshark.config.record");
+	if (!conf)
+		return;
+
+	/*
+	 * Load the hash table of selected events from the configuration
+	 * document.
+	 */
+	jevents = kshark_config_alloc(KS_CONFIG_JSON);
+	if (!kshark_config_doc_get(conf, "Events", jevents))
+		return;
+
+	eventHash = tracecmd_filter_id_hash_alloc();
+	kshark_import_event_filter(_localTEP, eventHash, "Events", jevents);
+	for (int i = 0; i < nEvts; ++i) {
+		if (tracecmd_filter_id_find(eventHash, events[i]->id))
+			v[i] = true;
+	}
+
+	_eventsWidget.set(v);
+	tracecmd_filter_id_hash_free(eventHash);
+
+	/** Get all available plugins. */
+	temp = kshark_string_config_alloc();
+
+	if (kshark_config_doc_get(conf, "Plugin", temp))
+		_pluginsComboBox.setCurrentText(KS_C_STR_CAST(temp->conf_doc));
+
+	if (kshark_config_doc_get(conf, "Output", temp))
+		_outputLineEdit.setText(KS_C_STR_CAST(temp->conf_doc));
+
+	if (kshark_config_doc_get(conf, "Command", temp))
+		_commandLineEdit.setText(KS_C_STR_CAST(temp->conf_doc));
+}
+
+void KsCaptureControl::_exportSettings()
+{
+	kshark_config_doc *conf, *events;
+	json_object *jplugin;
+	QString plugin, out, comm;
+	QVector<int> ids;
+	QString fileName =
+		QFileDialog::getSaveFileName(this,
+					     "Export to File",
+					     KS_DIR,
+					     "Kernel Shark Config files (*.json);;");
+
+	if (fileName.isEmpty())
+		return;
+
+	if (!fileName.endsWith(".json")) {
+		fileName += ".json";
+		if (QFileInfo(fileName).exists()) {
+			if (!KsWidgetsLib::fileExistsDialog(fileName))
+				return;
+		}
+	}
+
+	/* Create a configuration document. */
+	conf = kshark_record_config_new(KS_CONFIG_JSON);
+	events = kshark_filter_config_new(KS_CONFIG_JSON);
+
+	/*
+	 * Use the tracecmd_filter_id to save all selected events in the
+	 * configuration file.
+	 */
+	ids = _eventsWidget.getCheckedIds();
+	tracecmd_filter_id *eventHash = tracecmd_filter_id_hash_alloc();
+	for (auto const &id: ids)
+		tracecmd_filter_id_add(eventHash, id);
+
+	kshark_export_event_filter(_localTEP, eventHash, "Events", events);
+	kshark_config_doc_add(conf, "Events", events);
+
+	tracecmd_filter_id_hash_free(eventHash);
+
+	/* Save the plugin. */
+	plugin = _pluginsComboBox.currentText();
+	jplugin = json_object_new_string(plugin.toStdString().c_str());
+	kshark_config_doc_add(conf, "Plugin", kshark_json_to_conf(jplugin));
+
+	/* Save the output file. */
+	out = outputFileName();
+	json_object *jout = json_object_new_string(out.toStdString().c_str());
+	kshark_config_doc_add(conf, "Output", kshark_json_to_conf(jout));
+
+	/* Save the command. */
+	comm = _commandLineEdit.text();
+	json_object *jcomm = json_object_new_string(comm.toStdString().c_str());
+	kshark_config_doc_add(conf, "Command", kshark_json_to_conf(jcomm));
+
+	kshark_save_config_file(fileName.toStdString().c_str(), conf);
+}
+
+void KsCaptureControl::_browse()
+{
+	QString fileName =
+		QFileDialog::getSaveFileName(this,
+					     "Save File",
+					     KS_DIR,
+					     "trace-cmd files (*.dat);;All files (*)");
+
+	if (!fileName.isEmpty())
+		_outputLineEdit.setText(fileName);
+}
+
+void KsCaptureControl::_apply()
+{
+	emit argsReady(getArgs().join(" "));
+}
+
+/** @brief Create KsCaptureMonitor widget. */
+KsCaptureMonitor::KsCaptureMonitor(QWidget *parent)
+: QWidget(parent),
+  _mergedChannels(false),
+  _argsModified(false),
+  _panel(this),
+  _name("Output display", this),
+  _space("max size ", this),
+  _readOnlyCB("read only", this),
+  _maxLinNumEdit(QString("%1").arg(KS_CAP_MON_MAX_LINE_NUM), this),
+  _consolOutput("", this)
+{
+	_panel.setMaximumHeight(FONT_HEIGHT * 1.75);
+	_panel.addWidget(&_name);
+
+	_space.setAlignment(Qt::AlignRight);
+	_panel.addWidget(&_space);
+
+	_maxLinNumEdit.setFixedWidth(FONT_WIDTH * 7);
+	_panel.addWidget(&_maxLinNumEdit);
+	_panel.addSeparator();
+	_readOnlyCB.setCheckState(Qt::Checked);
+	_panel.addWidget(&_readOnlyCB);
+	_layout.addWidget(&_panel);
+
+	_consolOutput.setStyleSheet("QLabel {background-color : white;}");
+	_consolOutput.setMinimumWidth(FONT_WIDTH * 60);
+	_consolOutput.setMinimumHeight(FONT_HEIGHT * 10);
+	_consolOutput.setMaximumBlockCount(KS_CAP_MON_MAX_LINE_NUM);
+
+	_space.setMinimumWidth(FONT_WIDTH * 50 - _name.width() - _readOnlyCB.width());
+	_consolOutput.setReadOnly(true);
+	_layout.addWidget(&_consolOutput);
+
+	this->setLayout(&_layout);
+
+	connect(&_maxLinNumEdit,	&QLineEdit::textChanged,
+		this,			&KsCaptureMonitor::_maxLineNumber);
+
+	connect(&_readOnlyCB,		&QCheckBox::stateChanged,
+		this,			&KsCaptureMonitor::_readOnly);
+
+	connect(&_consolOutput,		&QPlainTextEdit::textChanged,
+		this,			&KsCaptureMonitor::_argVModified);
+
+	this->show();
+}
+
+void KsCaptureMonitor::_maxLineNumber(const QString &test)
+{
+	bool ok;
+	int max = test.toInt(&ok);
+
+	if (ok)
+		_consolOutput.setMaximumBlockCount(max);
+}
+
+void KsCaptureMonitor::_readOnly(int state)
+{
+	if (state == Qt::Checked)
+		_consolOutput.setReadOnly(true);
+	else
+		_consolOutput.setReadOnly(false);
+}
+
+void KsCaptureMonitor::_argsReady(const QString &args)
+{
+	_name.setText("Capture options:");
+	_consolOutput.setPlainText(args);
+	_argsModified = false;
+}
+
+void KsCaptureMonitor::_argVModified()
+{
+	_argsModified = true;
+}
+
+void KsCaptureMonitor::_printAllStandardError()
+{
+	QProcess *_capture = (QProcess*) sender();
+
+	_consolOutput.moveCursor(QTextCursor::End);
+	_consolOutput.insertPlainText(_capture->readAllStandardError());
+	_consolOutput.moveCursor(QTextCursor::End);
+	QCoreApplication::processEvents();
+}
+
+void KsCaptureMonitor::_printAllStandardOutput()
+{
+	QProcess *_capture = (QProcess*) sender();
+
+	if (!_mergedChannels)
+		return;
+
+	_consolOutput.appendPlainText(_capture->readAllStandardOutput());
+	QCoreApplication::processEvents();
+}
+
+/**
+ * Connect the Capture monitor widget to the signals of the recording process.
+ */
+void KsCaptureMonitor::connectMe(QProcess *proc, KsCaptureControl *ctrl)
+{
+	connect(proc,	&QProcess::started,
+		this,	&KsCaptureMonitor::_captureStarted);
+
+	/* Using the old Signal-Slot syntax because QProcess::finished has overloads. */
+	connect(proc,	SIGNAL(finished(int, QProcess::ExitStatus)),
+		this,	SLOT(_captureFinished(int, QProcess::ExitStatus)));
+
+	connect(proc,	&QProcess::readyReadStandardError,
+		this,	&KsCaptureMonitor::_printAllStandardError);
+
+	connect(proc,	&QProcess::readyReadStandardOutput,
+		this,	&KsCaptureMonitor::_printAllStandardOutput);
+
+	connect(ctrl,	&KsCaptureControl::argsReady,
+		this,	&KsCaptureMonitor::_argsReady);
+}
+
+void KsCaptureMonitor::_captureStarted()
+{
+	_name.setText("Terminal output:");
+	_readOnlyCB.setCheckState(Qt::Checked);
+
+	QCoreApplication::processEvents();
+}
+
+void KsCaptureMonitor::_captureFinished(int exit, QProcess::ExitStatus status)
+{
+	QProcess *_capture = (QProcess *)sender();
+
+	if (exit != 0 || status != QProcess::NormalExit) {
+		QString errMessage("Capture process failed: ");
+
+		errMessage += _capture->errorString();
+		_consolOutput.appendPlainText(errMessage);
+
+		QCoreApplication::processEvents();
+	}
+}
+
+/** Print a message. */
+void KsCaptureMonitor::print(const QString &message)
+{
+	_consolOutput.appendPlainText(message);
+}
+
+/** @brief Create KsCaptureDialog widget. */
+KsCaptureDialog::KsCaptureDialog(QWidget *parent)
+: QWidget(parent),
+  _captureCtrl(this),
+  _captureMon(this),
+  _captureProc(this)
+{
+	QString captureExe(TRACECMD_BIN_DIR);
+
+	this->setWindowTitle("Capture");
+	_layout.addWidget(&_captureCtrl);
+	_layout.addWidget(&_captureMon);
+	this->setLayout(&_layout);
+
+	connect(&_captureCtrl._commandCheckBox,	&QCheckBox::stateChanged,
+		this,				&KsCaptureDialog::_setChannelMode);
+
+	connect(&_captureCtrl._captureButton,	&QPushButton::pressed,
+		this,				&KsCaptureDialog::_capture);
+
+	connect(&_captureCtrl._closeButton,	&QPushButton::pressed,
+		this,				&KsCaptureDialog::close);
+
+	captureExe += "/trace-cmd";
+	_captureProc.setProgram(captureExe);
+
+	_captureMon.connectMe(&_captureProc, &_captureCtrl);
+}
+
+void KsCaptureDialog::_capture()
+{
+	QStringList argv;
+	int argc;
+
+	if(_captureMon._argsModified) {
+		argv = _captureMon.text().split(" ");
+	} else {
+		argv = _captureCtrl.getArgs();
+	}
+
+	_captureMon.print("\n");
+	_captureMon.print(QString("trace-cmd " + argv.join(" ") + "\n"));
+	_captureProc.setArguments(argv);
+	_captureProc.start();
+	_captureProc.waitForFinished();
+
+	argc = argv.count();
+	for (int i = 0; i < argc; ++i) {
+		if (argv[i] == "-o") {
+			_sendOpenReq(argv[i + 1]);
+			break;
+		}
+	}
+
+	/* Reset the _argsModified flag. */
+	_captureMon._argsModified = false;
+}
+
+void KsCaptureDialog::_setChannelMode(int state)
+{
+	if (state > 0) {
+		_captureMon._mergedChannels = true;
+	} else {
+		_captureMon._mergedChannels = false;
+	}
+}
+
+void KsCaptureDialog::_sendOpenReq(const QString &fileName)
+{
+	QLocalSocket *socket = new QLocalSocket(this);
+
+	socket->connectToServer("KSCapture", QIODevice::WriteOnly);
+	if (socket->waitForConnected()) {
+		QByteArray block;
+		QDataStream out(&block, QIODevice::WriteOnly);
+		const QString message = fileName;
+
+		out << quint32(message.size());
+		out << message;
+
+		socket->write(block);
+		socket->flush();
+		socket->disconnectFromServer();
+	} else {
+		_captureMon.print(socket->errorString());
+	}
+}
diff --git a/kernel-shark-qt/src/KsCaptureDialog.hpp b/kernel-shark-qt/src/KsCaptureDialog.hpp
new file mode 100644
index 0000000..d65f475
--- /dev/null
+++ b/kernel-shark-qt/src/KsCaptureDialog.hpp
@@ -0,0 +1,185 @@ 
+/* SPDX-License-Identifier: LGPL-2.1 */
+
+/*
+ * Copyright (C) 2017 VMware Inc, Yordan Karadzhov <ykaradzhov@vmware.com>
+ */
+
+/**
+ *  @file    KsCaptureDialog.hpp
+ *  @brief   Dialog for trace data recording.
+ */
+
+#ifndef _KS_CAPTURE_H
+#define _KS_CAPTURE_H
+
+// Qt
+#include <QtWidgets>
+
+// KernelShark
+#include "KsWidgetsLib.hpp"
+
+/**
+ * The KsCaptureControl class provides a control panel for the KernelShark
+ * Capture dialog.
+ */
+class KsCaptureControl : public QWidget
+{
+	Q_OBJECT
+public:
+	explicit KsCaptureControl(QWidget *parent = 0);
+
+	QStringList getArgs();
+
+	/** Get the name of the tracing data output file. */
+	QString outputFileName() const {return _outputLineEdit.text();}
+
+	/** Set the name of the tracing data output file. */
+	void setOutputFileName(const QString &f) {_outputLineEdit.setText(f);}
+
+signals:
+	/** This signal is emitted when the "Apply" button is pressed. */
+	void argsReady(const QString &args);
+
+private:
+	tep_handle		*_localTEP;
+
+	KsEventsCheckBoxWidget	_eventsWidget;
+
+	QVBoxLayout	_topLayout;
+
+	QGridLayout	_execLayout;
+
+	QLabel		_pluginsLabel, _outputLabel, _commandLabel;
+
+	QLineEdit	_outputLineEdit, _commandLineEdit;
+
+	QToolBar	_settingsToolBar, _controlToolBar;
+
+	QComboBox	_pluginsComboBox;
+
+	QPushButton	_importSettingsButton, _exportSettingsButton;
+
+	QPushButton	_outputBrowseButton;
+
+	QStringList _getPlugins();
+
+	void _importSettings();
+
+	void _exportSettings();
+
+	void _browse();
+
+	void _apply();
+
+public:
+	/**
+	 * A Check box used to indicate if the output of the command needs to
+	 * be shown by the KsCaptureMonitor widget.
+	 */
+	QCheckBox	_commandCheckBox;
+
+	/** Capture button for the control panel. */
+	QPushButton	_captureButton;
+
+	/** Apply button for the control panel. */
+	QPushButton	_applyButton;
+
+	/** Close button for the control panel. */
+	QPushButton	_closeButton;
+};
+
+/**
+ * The KsCaptureMonitor class provides a terminal-like widget for monitoring
+ * the tracing data recording process.
+ */
+class KsCaptureMonitor : public QWidget
+{
+	Q_OBJECT
+public:
+	explicit KsCaptureMonitor(QWidget *parent = 0);
+
+	/** Get the text shown by the widget. */
+	QString text() const {return _consolOutput.toPlainText();}
+
+	/** Clear the text shown by the widget. */
+	void clear() {_consolOutput.clear();}
+
+	void print(const QString &message);
+
+	void connectMe(QProcess *proc, KsCaptureControl *ctrl);
+
+	/** A flag indicating if the stdout and stderr channels are _merged. */
+	bool		_mergedChannels;
+
+	/**
+	 * A flag indicating, if the list of the command line arguments for trace-cmd
+	 * has been edited by the user.
+	 */
+	bool		_argsModified;
+
+private:
+	QVBoxLayout	_layout;
+
+	QToolBar	_panel;
+
+	QLabel		_name, _space;
+
+	QCheckBox	_readOnlyCB;
+
+	QLineEdit	_maxLinNumEdit;
+
+	QPlainTextEdit	_consolOutput;
+
+	void _argsReady(const QString &test);
+
+	void _maxLineNumber(const QString &test);
+
+	void _readOnly(int);
+
+	void _argVModified();
+
+	void _captureStarted();
+
+	void _printAllStandardError();
+
+	void _printAllStandardOutput();
+
+private slots:
+	void _captureFinished(int, QProcess::ExitStatus);
+};
+
+/** Default number of lines shown by the KsCaptureMonitor widget. */
+#define KS_CAP_MON_MAX_LINE_NUM 200
+
+/**
+ * The KsCaptureDialog class provides a dialog for recording of tracing data.
+ */
+class KsCaptureDialog : public QWidget
+{
+	Q_OBJECT
+public:
+	explicit KsCaptureDialog(QWidget *parent = 0);
+
+	/** Set the name of the tracing data output file. */
+	void setOutputFileName(const QString &f)
+	{
+		_captureCtrl.setOutputFileName(f);
+	}
+
+private:
+	QHBoxLayout		_layout;
+
+	KsCaptureControl	_captureCtrl;
+
+	KsCaptureMonitor	_captureMon;
+
+	QProcess		_captureProc;
+
+	void _capture();
+
+	void _setChannelMode(int state);
+
+	void _sendOpenReq(const QString &fileName);
+};
+
+#endif // _KS_CAPTURE_H