From 5ddc5963ce06ecea574e90ca503a3ee598522d8f Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Fri, 4 Dec 2009 13:08:01 +0100 Subject: support multiline data output from scripts run under the grapher This is accompanied by support for multiline output in hover text. * grapher/StapParser.cxx (ioCallback): Read data 'til the end of line character, not just '\n'. Be careful to use I/O functions that don't treat '\n' specially. * grapher/StapParser.hxx: ditto * grapher/CairoWidget.cxx (CairoTextBox::draw): Perform line breaks for hover text. * testsuite/systemtap.examples/general/grapher.stp: Do multiline output of keyboard events. Also, fix longstanding breaking in the pty probe. --- grapher/GraphWidget.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'grapher/GraphWidget.cxx') diff --git a/grapher/GraphWidget.cxx b/grapher/GraphWidget.cxx index 9067988a..e37485b8 100644 --- a/grapher/GraphWidget.cxx +++ b/grapher/GraphWidget.cxx @@ -275,7 +275,7 @@ namespace systemtap { if (!_hoverText) _hoverText = shared_ptr(new CairoTextBox()); - _hoverText->setOrigin(_mouseX + 5, _mouseY - 5); + _hoverText->setOrigin(_mouseX + 10, _mouseY - 5); Graph::DatasetList& dataSets = g->getDatasets(); for (Graph::DatasetList::reverse_iterator ritr = dataSets.rbegin(), end = dataSets.rend(); -- cgit From 2ebfefae53de7d6c001554a159dbcc8edfac9ec7 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Fri, 4 Dec 2009 17:12:06 +0100 Subject: hover text on the additional graphs * grapher/Graph.cxx (window2GraphCoords): Take the entire graph's position into account too. * grapher/GraphWidget.cxx (GraphWidget): Hook up the data dialog's OK button. (onDataDialogOpen): If a data set doesn't have a title, use it's name (key) instead. --- grapher/GraphWidget.cxx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'grapher/GraphWidget.cxx') diff --git a/grapher/GraphWidget.cxx b/grapher/GraphWidget.cxx index e37485b8..3d38627f 100644 --- a/grapher/GraphWidget.cxx +++ b/grapher/GraphWidget.cxx @@ -50,6 +50,11 @@ namespace systemtap _refXmlDataDialog->get_widget("dialog1", _dataDialog); Gtk::Button* button = 0; _refXmlDataDialog->get_widget("cancelbutton1", button); + button->signal_clicked() + .connect(sigc::mem_fun(*this, &GraphWidget::onDataDialogCancel), + false); + // XXX + _refXmlDataDialog->get_widget("okbutton1", button); button->signal_clicked() .connect(sigc::mem_fun(*this, &GraphWidget::onDataDialogCancel), false); @@ -263,7 +268,10 @@ namespace systemtap { Gtk::TreeModel::iterator litr = _listStore->append(); Gtk::TreeModel::Row row = *litr; - row[_dataColumns._dataName] = (*itr)->title; + if (!(*itr)->title.empty()) + row[_dataColumns._dataName] = (*itr)->title; + else + row[_dataColumns._dataName] = (*itr)->name; row[_dataColumns._graphData] = *itr; } } -- cgit From db41ebdabef8a7964ed92054ee7346cfded7179c Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Fri, 4 Dec 2009 19:30:08 +0100 Subject: Add new graph output to the last graph --- grapher/GraphWidget.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'grapher/GraphWidget.cxx') diff --git a/grapher/GraphWidget.cxx b/grapher/GraphWidget.cxx index 3d38627f..739d9450 100644 --- a/grapher/GraphWidget.cxx +++ b/grapher/GraphWidget.cxx @@ -84,7 +84,7 @@ namespace systemtap void GraphWidget::addGraphData(shared_ptr data) { - _graphs[0]->addGraphData(data); + _graphs.back()->addGraphData(data); _graphData.push_back(data); } -- cgit From e88061ec1fb047b65c247424dbadb10a85ff69ae Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Mon, 7 Dec 2009 12:23:27 +0100 Subject: Add a missing cairo context restore() It's not clear if this was causing any ill effects, but it is certainly bad form. * grapher/GraphWidget.cxx (on_expose_event): add missing cr->restore(). --- grapher/GraphWidget.cxx | 1 + 1 file changed, 1 insertion(+) (limited to 'grapher/GraphWidget.cxx') diff --git a/grapher/GraphWidget.cxx b/grapher/GraphWidget.cxx index 739d9450..cfec0adf 100644 --- a/grapher/GraphWidget.cxx +++ b/grapher/GraphWidget.cxx @@ -126,6 +126,7 @@ namespace systemtap } if (_hoverText && _hoverText->isVisible()) _hoverText->draw(cr); + cr->restore(); return true; } -- cgit From 5891de489db0e172162279247fb633a719fa3756 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Mon, 7 Dec 2009 12:43:56 +0100 Subject: option to display graph times relative to the start time This avoids having humongous numbers displayed on the graphs' x axis. Also, the dialog for adding a data set to a graph was cleaned up. * grapher/graph-dialog.glade: Add check box for display of relative time and a label for the data set list. Force the list of data sets to be larger. * grapher/Graph.hxx (_timeBase): new member * grapher/Graph.cxx (draw): Subtract _timeBase from displayed time value. * grapher/GraphWidget.hxx (DataModelColumns): Add a column for a data set's title, which is optional at the moment. * grapher/GraphWidget.hxx (_globalTimeBase, _timeBaseInitialized, _relativeTimesButton, _displayRelativeTimes): new members * grapher/GraphWidget.hxx (GraphWidget): Hook up check button for displaying relative time. (on_expose_event): Determine base time if needed; set base time in graphs. (onDataDialogOpen): Insert graph data set's name (key) and title into the list of data sets. --- grapher/GraphWidget.cxx | 46 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 4 deletions(-) (limited to 'grapher/GraphWidget.cxx') diff --git a/grapher/GraphWidget.cxx b/grapher/GraphWidget.cxx index cfec0adf..9d5e12f8 100644 --- a/grapher/GraphWidget.cxx +++ b/grapher/GraphWidget.cxx @@ -4,6 +4,9 @@ #include #include +#define __STDC_LIMIT_MACROS +#include + #include #include #include @@ -22,7 +25,7 @@ namespace systemtap GraphWidget::GraphWidget() : _trackingDrag(false), _width(600), _height(200), _mouseX(0.0), - _mouseY(0.0) + _mouseY(0.0), _globalTimeBase(0), _timeBaseInitialized(false) { add_events(Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK | Gdk::SCROLL_MASK); @@ -70,6 +73,14 @@ namespace systemtap _listStore = Gtk::ListStore::create(_dataColumns); _dataTreeView->set_model(_listStore); _dataTreeView->append_column("Data", _dataColumns._dataName); + _dataTreeView->append_column("Title", _dataColumns._dataTitle); + _refXmlDataDialog->get_widget("checkbutton1", _relativeTimesButton); + _relativeTimesButton->signal_clicked() + .connect(sigc::mem_fun(*this, + &GraphWidget::onRelativeTimesButtonClicked)); + // Set button's initial value from that in .glade file + _displayRelativeTimes = _relativeTimesButton->get_active(); + } catch (const Gnome::Glade::XmlError& ex ) { @@ -100,6 +111,7 @@ namespace systemtap shared_ptr graph(new Graph(x, y)); _height = y + graph->_height; graph->setOrigin(x, y); + graph->_timeBase = _globalTimeBase; _graphs.push_back(graph); queue_resize(); } @@ -115,8 +127,29 @@ namespace systemtap cr->save(); cr->set_source_rgba(0.0, 0.0, 0.0, 1.0); cr->paint(); + if (!_timeBaseInitialized && !_graphData.empty()) + { + int64_t earliest = INT64_MAX; + for (GraphDataList::iterator gd = _graphData.begin(), + end = _graphData.end(); + gd != end; + ++gd) + { + if (!(*gd)->times.empty() && (*gd)->times[0] < earliest) + earliest = (*gd)->times[0]; + } + if (earliest != INT64_MAX) + { + _globalTimeBase = earliest; + _timeBaseInitialized = true; + } + } for (GraphList::iterator g = _graphs.begin(); g != _graphs.end(); ++g) { + if (_displayRelativeTimes && _timeBaseInitialized) + (*g)->_timeBase = _globalTimeBase; + else + (*g)->_timeBase = 0.0; double x, y; (*g)->getOrigin(x, y); cr->save(); @@ -269,10 +302,9 @@ namespace systemtap { Gtk::TreeModel::iterator litr = _listStore->append(); Gtk::TreeModel::Row row = *litr; + row[_dataColumns._dataName] = (*itr)->name; if (!(*itr)->title.empty()) - row[_dataColumns._dataName] = (*itr)->title; - else - row[_dataColumns._dataName] = (*itr)->name; + row[_dataColumns._dataTitle] = (*itr)->title; row[_dataColumns._graphData] = *itr; } } @@ -326,4 +358,10 @@ namespace systemtap _hover_timeout_connection = Glib::signal_timeout() .connect(sigc::mem_fun(*this, &GraphWidget::onHoverTimeout), 1000); } + + void GraphWidget::onRelativeTimesButtonClicked() + { + _displayRelativeTimes = _relativeTimesButton->get_active(); + queue_draw(); + } } -- cgit From b930d6ec364e35bb04a0860b1a5f2fbdee6effe3 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Mon, 7 Dec 2009 16:50:44 +0100 Subject: Change data set list display to use a check box for inclusion in a graph Incidently, this supports removing a data set from a graph. Also, remove the Add, Remove, and Cancel buttons from the data set dialog. * grapher/GraphWidget.hxx (DataModelColumns::_dataEnabled): new member _listConnection: new member * grapher/GraphWidget.cxx (GraphWidget): Delete connections for removed buttons. Connect to dialog show and hide signals instead of map. Add "Enabled" column to dialog list. (onDataAdd, onDataRemove): delete (onDataDialogOpen): Initialize list store with all data sets and hook up row_changed signal (onDataDialogClose, onRowChanged): new functions * grapher/graph-dialog.glade: Remove obsolete buttons (Add, Remove, Cancel). --- grapher/GraphWidget.cxx | 68 +++++++++++++++++++++++++++---------------------- 1 file changed, 38 insertions(+), 30 deletions(-) (limited to 'grapher/GraphWidget.cxx') diff --git a/grapher/GraphWidget.cxx b/grapher/GraphWidget.cxx index 9d5e12f8..db325d83 100644 --- a/grapher/GraphWidget.cxx +++ b/grapher/GraphWidget.cxx @@ -52,26 +52,19 @@ namespace systemtap _refXmlDataDialog = Gnome::Glade::Xml::create(PKGDATADIR "/graph-dialog.glade"); _refXmlDataDialog->get_widget("dialog1", _dataDialog); Gtk::Button* button = 0; - _refXmlDataDialog->get_widget("cancelbutton1", button); + _refXmlDataDialog->get_widget("closebutton1", button); button->signal_clicked() .connect(sigc::mem_fun(*this, &GraphWidget::onDataDialogCancel), false); - // XXX - _refXmlDataDialog->get_widget("okbutton1", button); - button->signal_clicked() - .connect(sigc::mem_fun(*this, &GraphWidget::onDataDialogCancel), - false); - _refXmlDataDialog->get_widget("button1", button); - button->signal_clicked() - .connect(sigc::mem_fun(*this, &GraphWidget::onDataAdd), false); - _refXmlDataDialog->get_widget("button2", button); - button->signal_clicked() - .connect(sigc::mem_fun(*this, &GraphWidget::onDataRemove), false); _refXmlDataDialog->get_widget("treeview1", _dataTreeView); - _dataDialog->signal_map() + _dataDialog->signal_show() .connect(sigc::mem_fun(*this, &GraphWidget::onDataDialogOpen)); + _dataDialog->signal_hide() + .connect(sigc::mem_fun(*this, &GraphWidget::onDataDialogClose)); _listStore = Gtk::ListStore::create(_dataColumns); _dataTreeView->set_model(_listStore); + _dataTreeView->append_column_editable("Enabled", + _dataColumns._dataEnabled); _dataTreeView->append_column("Data", _dataColumns._dataName); _dataTreeView->append_column("Title", _dataColumns._dataTitle); _refXmlDataDialog->get_widget("checkbutton1", _relativeTimesButton); @@ -275,23 +268,6 @@ namespace systemtap _dataDialog->hide(); } - void GraphWidget::onDataAdd() - { - Glib::RefPtr treeSelection = - _dataTreeView->get_selection(); - Gtk::TreeModel::iterator iter = treeSelection->get_selected(); - if (iter) - { - Gtk::TreeModel::Row row = *iter; - shared_ptr data = row[_dataColumns._graphData]; - _activeGraph->addGraphData(data); - } - } - - void GraphWidget::onDataRemove() - { - } - void GraphWidget::onDataDialogOpen() { _listStore->clear(); @@ -306,7 +282,20 @@ namespace systemtap if (!(*itr)->title.empty()) row[_dataColumns._dataTitle] = (*itr)->title; row[_dataColumns._graphData] = *itr; + Graph::DatasetList& gsets = _activeGraph->getDatasets(); + Graph::DatasetList::iterator setItr + = find(gsets.begin(), gsets.end(), *itr); + row[_dataColumns._dataEnabled] = (setItr != gsets.end()); } + _listConnection =_listStore->signal_row_changed() + .connect(sigc::mem_fun(*this, &GraphWidget::onRowChanged)); + + } + + void GraphWidget::onDataDialogClose() + { + if (_listConnection.connected()) + _listConnection.disconnect(); } bool GraphWidget::onHoverTimeout() @@ -364,4 +353,23 @@ namespace systemtap _displayRelativeTimes = _relativeTimesButton->get_active(); queue_draw(); } + + void GraphWidget::onRowChanged(const Gtk::TreeModel::Path&, + const Gtk::TreeModel::iterator& litr) + { + Gtk::TreeModel::Row row = *litr; + bool val = row[_dataColumns._dataEnabled]; + shared_ptr data = row[_dataColumns._graphData]; + Graph::DatasetList& graphData = _activeGraph->getDatasets(); + if (val + && find(graphData.begin(), graphData.end(), data) == graphData.end()) + { + _activeGraph->addGraphData(data); + } + else if (!val) + { + graphData.erase(remove(graphData.begin(), graphData.end(), data), + graphData.end()); + } + } } -- cgit From 9175e50559751538f4da02e6e17c61a8f5191a31 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Mon, 7 Dec 2009 18:51:55 +0100 Subject: make the list items in the data set list unselectable The checkbox selects the list, so it is just sloppy to allow the item to be selected. * grapher/GraphWidget.hxx (no_select_fun): New function; just returns false. * grapher/GraphWidget.cxx (GraphWidget): Connect list store selection to function that prevents selection. --- grapher/GraphWidget.cxx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'grapher/GraphWidget.cxx') diff --git a/grapher/GraphWidget.cxx b/grapher/GraphWidget.cxx index db325d83..c8d8cc45 100644 --- a/grapher/GraphWidget.cxx +++ b/grapher/GraphWidget.cxx @@ -66,7 +66,13 @@ namespace systemtap _dataTreeView->append_column_editable("Enabled", _dataColumns._dataEnabled); _dataTreeView->append_column("Data", _dataColumns._dataName); - _dataTreeView->append_column("Title", _dataColumns._dataTitle); + _dataTreeView->append_column("Title", _dataColumns._dataTitle); + // Disable selection in list + Glib::RefPtr listSelection + = _dataTreeView->get_selection(); + listSelection + ->set_select_function(sigc::mem_fun(*this, + &GraphWidget::no_select_fun)); _refXmlDataDialog->get_widget("checkbutton1", _relativeTimesButton); _relativeTimesButton->signal_clicked() .connect(sigc::mem_fun(*this, -- cgit From f669d095ba7fe5a623b31abc05b4f6664059803b Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Mon, 7 Dec 2009 19:19:45 +0100 Subject: add copyright and license to grapher files --- grapher/GraphWidget.cxx | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'grapher/GraphWidget.cxx') diff --git a/grapher/GraphWidget.cxx b/grapher/GraphWidget.cxx index c8d8cc45..8335fda2 100644 --- a/grapher/GraphWidget.cxx +++ b/grapher/GraphWidget.cxx @@ -1,3 +1,11 @@ +// systemtap grapher +// Copyright (C) 2009 Red Hat Inc. +// +// This file is part of systemtap, and is free software. You can +// redistribute it and/or modify it under the terms of the GNU General +// Public License (GPL); either version 2, or (at your option) any +// later version. + #include #include #include -- cgit From cfd482078cd4805076cc6fd7e4e8642b97a03b25 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Tue, 8 Dec 2009 12:44:03 +0100 Subject: refactor list of data sets out of GraphWidget into a global data structure. Also, add a sigc signal for broadcasting data set changes. * grapher/GraphData.hxx (GraphDataList) new typedef (getGraphData, graphDataSignal): new functions * grapher/Graph.hxx (DatasetList): remove typedef in favor of systemtap::GraphDataList * grapher/GraphWidget.cxx (GraphWidget, onGraphDataChanged): Use graphDataSignal to notice new data sets that need to be added. Use the global data set list instead of one embedded in GraphWidget. * grapher/StapParser.hxx: delete the _widget member; use signals to broadcast that there are new data sets instead of calling into the widget. --- grapher/GraphWidget.cxx | 52 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 14 deletions(-) (limited to 'grapher/GraphWidget.cxx') diff --git a/grapher/GraphWidget.cxx b/grapher/GraphWidget.cxx index 8335fda2..bdf60ed2 100644 --- a/grapher/GraphWidget.cxx +++ b/grapher/GraphWidget.cxx @@ -87,7 +87,8 @@ namespace systemtap &GraphWidget::onRelativeTimesButtonClicked)); // Set button's initial value from that in .glade file _displayRelativeTimes = _relativeTimesButton->get_active(); - + graphDataSignal() + .connect(sigc::mem_fun(*this, &GraphWidget::onGraphDataChanged)); } catch (const Gnome::Glade::XmlError& ex ) { @@ -100,10 +101,32 @@ namespace systemtap { } - void GraphWidget::addGraphData(shared_ptr data) + void GraphWidget::onGraphDataChanged() { - _graphs.back()->addGraphData(data); - _graphData.push_back(data); + // add any new graph data to the last graph + GraphDataList newData; + GraphDataList& allData = getGraphData(); + for (GraphDataList::iterator gditr = allData.begin(), gdend = allData.end(); + gditr != gdend; + ++gditr) + { + bool found = false; + for (GraphList::iterator gitr = _graphs.begin(), gend = _graphs.end(); + gitr != gend; + ++gitr) + { + GraphDataList& gdata = (*gitr)->getDatasets(); + if (find(gdata.begin(), gdata.end(), *gditr) != gdata.end()) + { + found = true; + break; + } + } + if (!found) + newData.push_back(*gditr); + } + copy(newData.begin(), newData.end(), + back_inserter(_graphs.back()->getDatasets())); } void GraphWidget::addGraph() @@ -134,11 +157,12 @@ namespace systemtap cr->save(); cr->set_source_rgba(0.0, 0.0, 0.0, 1.0); cr->paint(); - if (!_timeBaseInitialized && !_graphData.empty()) + if (!_timeBaseInitialized && !getGraphData().empty()) { + GraphDataList& graphData = getGraphData(); int64_t earliest = INT64_MAX; - for (GraphDataList::iterator gd = _graphData.begin(), - end = _graphData.end(); + for (GraphDataList::iterator gd = graphData.begin(), + end = graphData.end(); gd != end; ++gd) { @@ -285,8 +309,8 @@ namespace systemtap void GraphWidget::onDataDialogOpen() { _listStore->clear(); - for (GraphDataList::iterator itr = _graphData.begin(), - end = _graphData.end(); + for (GraphDataList::iterator itr = getGraphData().begin(), + end = getGraphData().end(); itr != end; ++itr) { @@ -296,8 +320,8 @@ namespace systemtap if (!(*itr)->title.empty()) row[_dataColumns._dataTitle] = (*itr)->title; row[_dataColumns._graphData] = *itr; - Graph::DatasetList& gsets = _activeGraph->getDatasets(); - Graph::DatasetList::iterator setItr + GraphDataList& gsets = _activeGraph->getDatasets(); + GraphDataList::iterator setItr = find(gsets.begin(), gsets.end(), *itr); row[_dataColumns._dataEnabled] = (setItr != gsets.end()); } @@ -320,8 +344,8 @@ namespace systemtap if (!_hoverText) _hoverText = shared_ptr(new CairoTextBox()); _hoverText->setOrigin(_mouseX + 10, _mouseY - 5); - Graph::DatasetList& dataSets = g->getDatasets(); - for (Graph::DatasetList::reverse_iterator ritr = dataSets.rbegin(), + GraphDataList& dataSets = g->getDatasets(); + for (GraphDataList::reverse_iterator ritr = dataSets.rbegin(), end = dataSets.rend(); ritr != end; ++ritr) @@ -374,7 +398,7 @@ namespace systemtap Gtk::TreeModel::Row row = *litr; bool val = row[_dataColumns._dataEnabled]; shared_ptr data = row[_dataColumns._graphData]; - Graph::DatasetList& graphData = _activeGraph->getDatasets(); + GraphDataList& graphData = _activeGraph->getDatasets(); if (val && find(graphData.begin(), graphData.end(), data) == graphData.end()) { -- cgit