From 03ebf81c360e429ef1e4f8bae48fe524f712589a Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Thu, 3 Dec 2009 19:31:08 +0100 Subject: Hover text for event-style graphs Don't draw the data value for an event; only display it in hover text. * grapher/Graph.hxx (window2GraphCoords): new function * grapher/Graph.cxx (window2GraphCoords): ditto * grapher/GraphStyle.cxx (GraphStyleEvent::dataIndexAtPoint): new function implementation. --- grapher/Graph.cxx | 7 +++++++ grapher/Graph.hxx | 1 + grapher/GraphStyle.cxx | 48 +++++++++++++++++++++++++++++++++++------------- grapher/GraphStyle.hxx | 4 ++++ 4 files changed, 47 insertions(+), 13 deletions(-) (limited to 'grapher') diff --git a/grapher/Graph.cxx b/grapher/Graph.cxx index 55ffcdf2..57f1dc9a 100644 --- a/grapher/Graph.cxx +++ b/grapher/Graph.cxx @@ -205,4 +205,11 @@ namespace systemtap return (_left + (_right - _left) * ((x - _xOffset)/(_zoomFactor * _graphWidth))); } + + void Graph::window2GraphCoords(double x, double y, + double& xgraph, double& ygraph) + { + xgraph = x -_xOffset; + ygraph = -y + _yOffset + _graphHeight; + } } diff --git a/grapher/Graph.hxx b/grapher/Graph.hxx index b9efb2a2..044a66d3 100644 --- a/grapher/Graph.hxx +++ b/grapher/Graph.hxx @@ -41,6 +41,7 @@ namespace systemtap std::tr1::shared_ptr _playButton; DatasetList& getDatasets() { return _datasets; } int64_t getTimeAtPoint(double x); + void window2GraphCoords(double x, double y, double& xgraph, double& ygraph); protected: DatasetList _datasets; int64_t _left; diff --git a/grapher/GraphStyle.cxx b/grapher/GraphStyle.cxx index 55fc73f4..cf3855e3 100644 --- a/grapher/GraphStyle.cxx +++ b/grapher/GraphStyle.cxx @@ -132,20 +132,9 @@ namespace systemtap ditr != de; ++ditr) { - size_t dataIndex = ditr - graphData->times.begin(); + // size_t dataIndex = ditr - graphData->times.begin(); double eventHeight = graph->_graphHeight * (graphData->scale / 100.0); cr->save(); - cr->select_font_face("Sans", Cairo::FONT_SLANT_NORMAL, - Cairo::FONT_WEIGHT_NORMAL); - cr->set_font_size(12.0); - cr->set_source_rgba(graphData->color[0], graphData->color[1], - graphData->color[2], 1.0); - cr->save(); - cr->scale(1.0, -1.0); - cr->move_to((*ditr - left) * horizScale, - -eventHeight - 3.0 * graph->_lineWidth - 2.0); - cr->show_text(stringData->data[dataIndex]); - cr->restore(); cr->rectangle((*ditr - left) * horizScale - 1.5 * graph->_lineWidth, eventHeight - 1.5 * graph->_lineWidth, 3.0 * graph->_lineWidth, 3.0 * graph->_lineWidth); @@ -153,5 +142,38 @@ namespace systemtap cr->restore(); } } - + + ssize_t GraphStyleEvent::dataIndexAtPoint(double x, double y, + shared_ptr graphData, + shared_ptr graph) + { + shared_ptr > stringData + = dynamic_pointer_cast >(graphData); + if (!stringData) + return -1; + int64_t left, right; + double top, bottom; + graph->getExtents(left, right, top, bottom); + double horizScale = (graph->_zoomFactor * graph->_graphWidth + / static_cast(right - left)); + double eventHeight = graph->_graphHeight * (graphData->scale / 100.0); + GraphDataBase::TimeList::iterator lower + = lower_bound(graphData->times.begin(), graphData->times.end(), left); + GraphDataBase::TimeList::iterator upper + = upper_bound(graphData->times.begin(), graphData->times.end(), right); + // easier to transform x,y into graph coordinates + double xgraph, ygraph; + graph->window2GraphCoords(x, y, xgraph, ygraph); + double yrect = eventHeight - 1.5 * graph->_lineWidth; + for (GraphDataBase::TimeList::iterator ditr = lower, de = upper; + ditr != de; + ++ditr) + { + double xrect = (*ditr - left) * horizScale - 1.5 * graph->_lineWidth; + if (xrect <= xgraph && xgraph < xrect + 3.0 * graph->_lineWidth + && yrect <= ygraph && ygraph < yrect + 3.0 * graph->_lineWidth) + return static_cast(distance(lower, ditr)); + } + return -1; + } } diff --git a/grapher/GraphStyle.hxx b/grapher/GraphStyle.hxx index 9625f451..ce75d698 100644 --- a/grapher/GraphStyle.hxx +++ b/grapher/GraphStyle.hxx @@ -48,6 +48,10 @@ namespace systemtap public: void draw(std::tr1::shared_ptr graphData, Graph* graph, Cairo::RefPtr cr); + virtual ssize_t dataIndexAtPoint(double x, double y, + std::tr1::shared_ptr + graphData, + std::tr1::shared_ptr graph); static GraphStyleEvent instance; }; } -- cgit From 457a91c061a1658648fc155504795bf6f4c44740 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Thu, 3 Dec 2009 23:45:51 +0100 Subject: Error check in hover text code for empty graphs * grapher/GraphStyle.cxx(GraphStyleEvent::dataIndexAtPoint, GraphStyleBar::dataIndexAtPoint): check for empty graph data --- grapher/GraphStyle.cxx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'grapher') diff --git a/grapher/GraphStyle.cxx b/grapher/GraphStyle.cxx index cf3855e3..fdc8c4d6 100644 --- a/grapher/GraphStyle.cxx +++ b/grapher/GraphStyle.cxx @@ -50,7 +50,7 @@ namespace systemtap { shared_ptr > realData = dynamic_pointer_cast >(graphData); - if (!realData) + if (!realData || graphData->times.empty()) return -1; int64_t left, right; double top, bottom; @@ -58,6 +58,8 @@ namespace systemtap double t = graph->getTimeAtPoint(x); TimeListPair range = equal_range(graphData->times.begin(), graphData->times.end(), t); + if (range.first == graphData->times.end()) + return -1; size_t dataIndex = distance(graphData->times.begin(), range.first); double val = realData->data[dataIndex]; double ycoord = val * graph->_graphHeight / graphData->scale; @@ -149,7 +151,7 @@ namespace systemtap { shared_ptr > stringData = dynamic_pointer_cast >(graphData); - if (!stringData) + if (!stringData || graphData->times.empty()) return -1; int64_t left, right; double top, bottom; -- cgit 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/CairoWidget.cxx | 36 +++++++++++++++++++++++++++++++----- grapher/GraphWidget.cxx | 2 +- grapher/StapParser.cxx | 27 +++++++++++++++++++-------- grapher/StapParser.hxx | 3 ++- 4 files changed, 53 insertions(+), 15 deletions(-) (limited to 'grapher') diff --git a/grapher/CairoWidget.cxx b/grapher/CairoWidget.cxx index f627dfaa..db0e464e 100644 --- a/grapher/CairoWidget.cxx +++ b/grapher/CairoWidget.cxx @@ -1,9 +1,15 @@ #include "CairoWidget.hxx" #include +#include + +#include namespace systemtap { + using namespace std; + using namespace boost; + void CairoPlayButton::draw(Cairo::RefPtr cr) { if (!_visible) @@ -44,10 +50,22 @@ namespace systemtap { if (!_visible) return; + vector lines; + split(lines, contents, is_any_of("\n")); + vector extents; + double width = 0.0, height = 0.0; + for (vector::iterator itr = lines.begin(), end = lines.end(); + itr != end; + ++itr) + { + Cairo::TextExtents extent; + cr->get_text_extents(*itr, extent); + extents.push_back(extent); + width = max(width, extent.width); + height += extent.height; + } + cr->save(); - Cairo::TextExtents extents; - cr->get_text_extents(contents, extents); - double width = extents.width, height = extents.height; cr->move_to(_x0 - 2, _y0 - 2); cr->line_to(_x0 + width + 2, _y0 - 2); cr->line_to(_x0 + width + 2, _y0 + height + 2); @@ -56,8 +74,16 @@ namespace systemtap cr->set_source_rgba(1.0, 1.0, 1.0, .8); cr->fill(); cr->set_source_rgba(0.0, 0.0, 0.0, 1.0); - cr->move_to(_x0, _y0 + height); - cr->show_text(contents); + vector::iterator titr = extents.begin(); + double texty = _y0; + for (vector::iterator itr = lines.begin(), end = lines.end(); + itr != end; + ++itr,++titr) + { + cr->move_to(_x0, texty + titr->height); + cr->show_text(*itr); + texty += titr->height; + } cr->restore(); } } 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(); diff --git a/grapher/StapParser.cxx b/grapher/StapParser.cxx index 6218e229..2a246475 100644 --- a/grapher/StapParser.cxx +++ b/grapher/StapParser.cxx @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -76,14 +77,14 @@ vector commaSplit(const boost::sub_range& range) _win->hide(); return true; } - buf[bytes_read] = '\0'; - _buffer += buf; + _buffer.append(buf, bytes_read); string::size_type ret = string::npos; - while ((ret = _buffer.find('\n')) != string::npos) + while ((ret = _buffer.find(_lineEndChar)) != string::npos) { Glib::ustring dataString(_buffer, 0, ret); - // %DataSet and %CSV declare a data set; all other statements begin with - // the name of a data set. + // %DataSet and %CSV declare a data set; all other + // statements begin with the name of a data set. + // Except %LineEnd :) sub_range found; if (dataString[0] == '%') { @@ -142,6 +143,15 @@ vector commaSplit(const boost::sub_range& range) setIter->second)); } } + else if ((found = find_first(dataString, "%LineEnd:"))) + { + istringstream stream(Glib::ustring(found.end(), + dataString.end())); + int charAsInt = 0; + // parse hex and octal numbers too + stream >> std::setbase(0) >> charAsInt; + _lineEndChar = static_cast(charAsInt); + } else { cerr << "Unknown declaration " << dataString << endl; @@ -199,9 +209,10 @@ vector commaSplit(const boost::sub_range& range) else { int64_t time; - string data; - stream >> time >> data; - parseData(itr->second, time, data); + stringbuf data; + stream >> time; + stream.get(data, _lineEndChar); + parseData(itr->second, time, data.str()); } } } diff --git a/grapher/StapParser.hxx b/grapher/StapParser.hxx index a77ad1bc..4f1cbd5a 100644 --- a/grapher/StapParser.hxx +++ b/grapher/StapParser.hxx @@ -14,10 +14,11 @@ class StapParser GraphWidget* _widget; int _errFd; int _inFd; + unsigned char _lineEndChar; public: StapParser(Gtk::Window* win, GraphWidget* widget) : _win(win), _widget(widget), _errFd(-1), - _inFd(-1) + _inFd(-1), _lineEndChar('\n') { } void parseData(std::tr1::shared_ptr gdata, -- cgit From 52cf0905d93c33f7d6f768478572ea08df4c8af0 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Fri, 4 Dec 2009 14:11:25 +0100 Subject: tweak multiline hover text to have proper interline spacing * grapher/CairoWidget.cxx (CairoTextBox::draw): Use font information to caculate legible spacing. Also change the font to something more readable. * testsuite/systemtap.examples/general/grapher.stp: Put carriage returns in the right spots. --- grapher/CairoWidget.cxx | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) (limited to 'grapher') diff --git a/grapher/CairoWidget.cxx b/grapher/CairoWidget.cxx index db0e464e..eefe3d28 100644 --- a/grapher/CairoWidget.cxx +++ b/grapher/CairoWidget.cxx @@ -50,6 +50,15 @@ namespace systemtap { if (!_visible) return; + cr->save(); + cr->select_font_face("Sans", Cairo::FONT_SLANT_NORMAL, + Cairo::FONT_WEIGHT_BOLD); + cr->set_font_size(10.0); + Cairo::FontExtents fontExtent; + cr->get_font_extents(fontExtent); + // Some naughty fonts have a height less than ascent + descent + double fontHeight = max(fontExtent.ascent + fontExtent.descent + 1.0, + fontExtent.height); vector lines; split(lines, contents, is_any_of("\n")); vector extents; @@ -62,10 +71,8 @@ namespace systemtap cr->get_text_extents(*itr, extent); extents.push_back(extent); width = max(width, extent.width); - height += extent.height; + height += fontHeight; } - - cr->save(); cr->move_to(_x0 - 2, _y0 - 2); cr->line_to(_x0 + width + 2, _y0 - 2); cr->line_to(_x0 + width + 2, _y0 + height + 2); @@ -74,15 +81,14 @@ namespace systemtap cr->set_source_rgba(1.0, 1.0, 1.0, .8); cr->fill(); cr->set_source_rgba(0.0, 0.0, 0.0, 1.0); - vector::iterator titr = extents.begin(); double texty = _y0; for (vector::iterator itr = lines.begin(), end = lines.end(); itr != end; - ++itr,++titr) + ++itr) { - cr->move_to(_x0, texty + titr->height); + cr->move_to(_x0, texty + fontExtent.ascent); cr->show_text(*itr); - texty += titr->height; + texty += fontHeight; } cr->restore(); } -- 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/Graph.cxx | 2 +- grapher/GraphWidget.cxx | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'grapher') diff --git a/grapher/Graph.cxx b/grapher/Graph.cxx index 57f1dc9a..b3429ef7 100644 --- a/grapher/Graph.cxx +++ b/grapher/Graph.cxx @@ -210,6 +210,6 @@ namespace systemtap double& xgraph, double& ygraph) { xgraph = x -_xOffset; - ygraph = -y + _yOffset + _graphHeight; + ygraph = -(y - _graphY) + _yOffset + _graphHeight; } } 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 8d6779975df8f3bef98ad2b2ae4ce310a543f781 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Fri, 4 Dec 2009 17:31:55 +0100 Subject: restore event square color * grapher/GraphStyle.cxx (GraphStyleEvent::draw): set event color in inner loop. --- grapher/GraphStyle.cxx | 2 ++ 1 file changed, 2 insertions(+) (limited to 'grapher') diff --git a/grapher/GraphStyle.cxx b/grapher/GraphStyle.cxx index fdc8c4d6..b9429e2a 100644 --- a/grapher/GraphStyle.cxx +++ b/grapher/GraphStyle.cxx @@ -137,6 +137,8 @@ namespace systemtap // size_t dataIndex = ditr - graphData->times.begin(); double eventHeight = graph->_graphHeight * (graphData->scale / 100.0); cr->save(); + cr->set_source_rgba(graphData->color[0], graphData->color[1], + graphData->color[2], 1.0); cr->rectangle((*ditr - left) * horizScale - 1.5 * graph->_lineWidth, eventHeight - 1.5 * graph->_lineWidth, 3.0 * graph->_lineWidth, 3.0 * graph->_lineWidth); -- 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') 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') 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/Graph.cxx | 4 ++-- grapher/Graph.hxx | 1 + grapher/GraphWidget.cxx | 46 ++++++++++++++++++++++++++++++++++++++++++---- grapher/GraphWidget.hxx | 7 +++++++ grapher/graph-dialog.glade | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 98 insertions(+), 6 deletions(-) (limited to 'grapher') diff --git a/grapher/Graph.cxx b/grapher/Graph.cxx index b3429ef7..baa1182f 100644 --- a/grapher/Graph.cxx +++ b/grapher/Graph.cxx @@ -14,7 +14,7 @@ namespace systemtap _graphWidth(580), _graphHeight(180), _lineWidth(2), _autoScaling(true), _autoScrolling(true), _zoomFactor(1.0), _xOffset(20.0), _yOffset(0.0), - _playButton(new CairoPlayButton), + _playButton(new CairoPlayButton), _timeBase(0), _left(0), _right(1), _top(5.0), _bottom(0.0) { setOrigin(x, y); @@ -150,7 +150,7 @@ namespace systemtap cr->move_to(x, _yOffset); cr->line_to(x, _graphHeight); std::ostringstream stream; - stream << tickVal; + stream << (tickVal - _timeBase); Cairo::TextExtents extents; cr->get_text_extents(stream.str(), extents); // Room for this label? diff --git a/grapher/Graph.hxx b/grapher/Graph.hxx index 044a66d3..93e23deb 100644 --- a/grapher/Graph.hxx +++ b/grapher/Graph.hxx @@ -39,6 +39,7 @@ namespace systemtap double _xOffset; double _yOffset; std::tr1::shared_ptr _playButton; + int64_t _timeBase; DatasetList& getDatasets() { return _datasets; } int64_t getTimeAtPoint(double x); void window2GraphCoords(double x, double y, double& xgraph, double& ygraph); 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(); + } } diff --git a/grapher/GraphWidget.hxx b/grapher/GraphWidget.hxx index 61d50e70..ea10720c 100644 --- a/grapher/GraphWidget.hxx +++ b/grapher/GraphWidget.hxx @@ -19,9 +19,11 @@ namespace systemtap DataModelColumns() { add(_dataName); + add(_dataTitle); add(_graphData); } Gtk::TreeModelColumn _dataName; + Gtk::TreeModelColumn _dataTitle; Gtk::TreeModelColumn > _graphData; }; @@ -72,8 +74,13 @@ namespace systemtap std::tr1::shared_ptr _hoverText; double _mouseX; double _mouseY; + int64_t _globalTimeBase; + bool _timeBaseInitialized; std::tr1::shared_ptr getGraphUnderPoint(double x, double y); void establishHoverTimeout(); + Gtk::CheckButton* _relativeTimesButton; + bool _displayRelativeTimes; + void onRelativeTimesButtonClicked(); }; } #endif // SYSTEMTAP_GRAPHWIDGET_H diff --git a/grapher/graph-dialog.glade b/grapher/graph-dialog.glade index cca2e0e3..85c10128 100644 --- a/grapher/graph-dialog.glade +++ b/grapher/graph-dialog.glade @@ -66,6 +66,31 @@ + + + True + Data sets + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + True @@ -83,6 +108,8 @@ + 200 + 100 True True False @@ -159,6 +186,25 @@ True + + + + True + True + Display relative times + True + GTK_RELIEF_NORMAL + True + True + False + True + + + 0 + False + False + + -- 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 ++++++++++++++++++++++++-------------------- grapher/GraphWidget.hxx | 8 ++++-- grapher/graph-dialog.glade | 71 ++-------------------------------------------- 3 files changed, 47 insertions(+), 100 deletions(-) (limited to 'grapher') 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()); + } + } } diff --git a/grapher/GraphWidget.hxx b/grapher/GraphWidget.hxx index ea10720c..146a08c3 100644 --- a/grapher/GraphWidget.hxx +++ b/grapher/GraphWidget.hxx @@ -18,10 +18,12 @@ namespace systemtap public: DataModelColumns() { + add(_dataEnabled); add(_dataName); add(_dataTitle); add(_graphData); } + Gtk::TreeModelColumn _dataEnabled; Gtk::TreeModelColumn _dataName; Gtk::TreeModelColumn _dataTitle; Gtk::TreeModelColumn > _graphData; @@ -64,9 +66,8 @@ namespace systemtap Gtk::Dialog* _dataDialog; Gtk::TreeView* _dataTreeView; void onDataDialogCancel(); - void onDataAdd(); - void onDataRemove(); void onDataDialogOpen(); + void onDataDialogClose(); bool onHoverTimeout(); DataModelColumns _dataColumns; Glib::RefPtr _listStore; @@ -81,6 +82,9 @@ namespace systemtap Gtk::CheckButton* _relativeTimesButton; bool _displayRelativeTimes; void onRelativeTimesButtonClicked(); + void onRowChanged(const Gtk::TreeModel::Path&, + const Gtk::TreeModel::iterator&); + sigc::connection _listConnection; }; } #endif // SYSTEMTAP_GRAPHWIDGET_H diff --git a/grapher/graph-dialog.glade b/grapher/graph-dialog.glade index 85c10128..8076d2b0 100644 --- a/grapher/graph-dialog.glade +++ b/grapher/graph-dialog.glade @@ -31,29 +31,15 @@ GTK_BUTTONBOX_END - + True True True - gtk-cancel + gtk-close True GTK_RELIEF_NORMAL True - -6 - - - - - - - True - True - True - gtk-ok - True - GTK_RELIEF_NORMAL - True - -5 + -7 @@ -128,57 +114,6 @@ True - - - - True - False - 0 - - - - True - True - gtk-add - True - GTK_RELIEF_NORMAL - True - - - - 0 - False - False - - - - - - True - True - gtk-remove - True - GTK_RELIEF_NORMAL - True - - - - 0 - False - False - - - - - - - - - 0 - False - False - - 0 -- 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 +++++++- grapher/GraphWidget.hxx | 6 ++++++ 2 files changed, 13 insertions(+), 1 deletion(-) (limited to 'grapher') 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, diff --git a/grapher/GraphWidget.hxx b/grapher/GraphWidget.hxx index 146a08c3..a2260aef 100644 --- a/grapher/GraphWidget.hxx +++ b/grapher/GraphWidget.hxx @@ -85,6 +85,12 @@ namespace systemtap void onRowChanged(const Gtk::TreeModel::Path&, const Gtk::TreeModel::iterator&); sigc::connection _listConnection; + bool no_select_fun(const Glib::RefPtr& model, + const Gtk::TreeModel::Path& path, + bool) + { + return false; + } }; } #endif // SYSTEMTAP_GRAPHWIDGET_H -- 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/CairoWidget.cxx | 8 ++++++++ grapher/CairoWidget.hxx | 8 ++++++++ grapher/Graph.cxx | 8 ++++++++ grapher/Graph.hxx | 8 ++++++++ grapher/GraphData.hxx | 8 ++++++++ grapher/GraphStyle.cxx | 8 ++++++++ grapher/GraphStyle.hxx | 8 ++++++++ grapher/GraphWidget.cxx | 8 ++++++++ grapher/GraphWidget.hxx | 8 ++++++++ grapher/StapParser.cxx | 8 ++++++++ grapher/StapParser.hxx | 8 ++++++++ grapher/grapher.cxx | 8 ++++++++ 12 files changed, 96 insertions(+) (limited to 'grapher') diff --git a/grapher/CairoWidget.cxx b/grapher/CairoWidget.cxx index eefe3d28..26c2d029 100644 --- a/grapher/CairoWidget.cxx +++ b/grapher/CairoWidget.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 "CairoWidget.hxx" #include diff --git a/grapher/CairoWidget.hxx b/grapher/CairoWidget.hxx index 8cfb816a..bcabafb2 100644 --- a/grapher/CairoWidget.hxx +++ b/grapher/CairoWidget.hxx @@ -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. + #ifndef SYSTEMTAP_CAIROWIDGET_H #define SYSTEMTAP_CAIROWIDGET_H 1 diff --git a/grapher/Graph.cxx b/grapher/Graph.cxx index baa1182f..2d203ead 100644 --- a/grapher/Graph.cxx +++ b/grapher/Graph.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 "Graph.hxx" #include diff --git a/grapher/Graph.hxx b/grapher/Graph.hxx index 93e23deb..4dcb5169 100644 --- a/grapher/Graph.hxx +++ b/grapher/Graph.hxx @@ -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. + #ifndef SYSTEMTAP_GRAPH_HXX #define SYSTEMTAP_GRAPH_HXX 1 diff --git a/grapher/GraphData.hxx b/grapher/GraphData.hxx index 04f415f3..fbb2bb8f 100644 --- a/grapher/GraphData.hxx +++ b/grapher/GraphData.hxx @@ -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. + #ifndef SYSTEMTAP_GRAPHDATA_HXX #define SYSTEMTAP_GRAPHDATA_HXX 1 diff --git a/grapher/GraphStyle.cxx b/grapher/GraphStyle.cxx index b9429e2a..69ff4089 100644 --- a/grapher/GraphStyle.cxx +++ b/grapher/GraphStyle.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 "GraphStyle.hxx" #include "GraphData.hxx" diff --git a/grapher/GraphStyle.hxx b/grapher/GraphStyle.hxx index ce75d698..bea4922a 100644 --- a/grapher/GraphStyle.hxx +++ b/grapher/GraphStyle.hxx @@ -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. + #ifndef SYSTEMTAP_GRAPHSTYLE_HXX #define SYSTEMTAP_GRAPHSTYLE_HXX 1 #include 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 diff --git a/grapher/GraphWidget.hxx b/grapher/GraphWidget.hxx index a2260aef..0e9f2a29 100644 --- a/grapher/GraphWidget.hxx +++ b/grapher/GraphWidget.hxx @@ -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. + #ifndef SYSTEMTAP_GRAPHWIDGET_H #define SYSTEMTAP_GRAPHWIDGET_H diff --git a/grapher/StapParser.cxx b/grapher/StapParser.cxx index 2a246475..edb7f5f2 100644 --- a/grapher/StapParser.cxx +++ b/grapher/StapParser.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 "StapParser.hxx" #include diff --git a/grapher/StapParser.hxx b/grapher/StapParser.hxx index 4f1cbd5a..476b0071 100644 --- a/grapher/StapParser.hxx +++ b/grapher/StapParser.hxx @@ -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 "GraphData.hxx" #include "GraphWidget.hxx" diff --git a/grapher/grapher.cxx b/grapher/grapher.cxx index 969bc762..1e5e30b8 100644 --- a/grapher/grapher.cxx +++ b/grapher/grapher.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 "GraphWidget.hxx" #include "StapParser.hxx" -- cgit From 72a9e5a2d3788f5465eb5e1610f2402744054f2e Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Tue, 8 Dec 2009 11:06:04 +0100 Subject: tiny refactoring of signal and pipe code --- grapher/grapher.cxx | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) (limited to 'grapher') diff --git a/grapher/grapher.cxx b/grapher/grapher.cxx index 1e5e30b8..567b0405 100644 --- a/grapher/grapher.cxx +++ b/grapher/grapher.cxx @@ -227,19 +227,14 @@ int StapLauncher::launch() &ChildDeathReader::ioCallback), signalPipe[0], Glib::IO_IN); } + struct sigaction action; + action.sa_sigaction = handleChild; + sigemptyset(&action.sa_mask); + action.sa_flags = SA_SIGINFO | SA_NOCLDSTOP; + sigaction(SIGCLD, &action, 0); } - struct sigaction action; - action.sa_sigaction = handleChild; - sigemptyset(&action.sa_mask); - action.sa_flags = SA_SIGINFO | SA_NOCLDSTOP; - sigaction(SIGCLD, &action, 0); int pipefd[4]; - if (pipe(&pipefd[0]) < 0) - { - std::perror("pipe"); - exit(1); - } - if (pipe(&pipefd[2]) < 0) + if (pipe(&pipefd[0]) < 0 || pipe(&pipefd[2]) < 0) { std::perror("pipe"); exit(1); -- 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/Graph.cxx | 9 ++++++--- grapher/Graph.hxx | 5 ++--- grapher/GraphData.hxx | 14 +++++++++++++ grapher/GraphWidget.cxx | 52 ++++++++++++++++++++++++++++++++++++------------- grapher/GraphWidget.hxx | 4 +--- grapher/StapParser.cxx | 6 ++++-- grapher/StapParser.hxx | 7 ++----- grapher/grapher.cxx | 4 ++-- 8 files changed, 69 insertions(+), 32 deletions(-) (limited to 'grapher') diff --git a/grapher/Graph.cxx b/grapher/Graph.cxx index 2d203ead..ea1bd091 100644 --- a/grapher/Graph.cxx +++ b/grapher/Graph.cxx @@ -16,6 +16,9 @@ namespace systemtap { using namespace std; using namespace std::tr1; + + GraphDataList GraphDataBase::graphData; + sigc::signal GraphDataBase::graphDataChanged; Graph::Graph(double x, double y) : _width(600), _height(200), _graphX(0), _graphY(0), @@ -40,7 +43,7 @@ namespace systemtap int linesPossible = (int)(_graphWidth / (_lineWidth + 2.0)); // Find latest time. int64_t latestTime = 0; - for (DatasetList::iterator ditr = _datasets.begin(), + for (GraphDataList::iterator ditr = _datasets.begin(), de = _datasets.end(); ditr != de; ++ditr) @@ -54,7 +57,7 @@ namespace systemtap } int64_t minDiff = 0; int64_t maxTotal = 0; - for (DatasetList::iterator ditr = _datasets.begin(), + for (GraphDataList::iterator ditr = _datasets.begin(), de = _datasets.end(); ditr != de; ++ditr) @@ -92,7 +95,7 @@ namespace systemtap cr->translate(_xOffset, _yOffset); cr->set_line_width(_lineWidth); - for (DatasetList::iterator itr = _datasets.begin(), e = _datasets.end(); + for (GraphDataList::iterator itr = _datasets.begin(), e = _datasets.end(); itr != e; ++itr) { diff --git a/grapher/Graph.hxx b/grapher/Graph.hxx index 4dcb5169..c928fec2 100644 --- a/grapher/Graph.hxx +++ b/grapher/Graph.hxx @@ -19,7 +19,6 @@ namespace systemtap class Graph : public CairoWidget { public: - typedef std::vector > DatasetList; friend class GraphWidget; Graph(double x = 0.0, double y = 0.0); virtual void draw(Cairo::RefPtr cr); @@ -48,11 +47,11 @@ namespace systemtap double _yOffset; std::tr1::shared_ptr _playButton; int64_t _timeBase; - DatasetList& getDatasets() { return _datasets; } + GraphDataList& getDatasets() { return _datasets; } int64_t getTimeAtPoint(double x); void window2GraphCoords(double x, double y, double& xgraph, double& ygraph); protected: - DatasetList _datasets; + GraphDataList _datasets; int64_t _left; int64_t _right; double _top; diff --git a/grapher/GraphData.hxx b/grapher/GraphData.hxx index fbb2bb8f..fb1ca9f5 100644 --- a/grapher/GraphData.hxx +++ b/grapher/GraphData.hxx @@ -19,10 +19,14 @@ #include +#include + #include "GraphStyle.hxx" namespace systemtap { + struct GraphDataBase; + typedef std::vector > GraphDataList; struct GraphDataBase { virtual ~GraphDataBase() {} @@ -43,6 +47,9 @@ namespace systemtap std::string xAxisText; std::string yAxisText; TimeList times; + static GraphDataList graphData; + // signal stuff for telling everyone about changes to the data set list + static sigc::signal graphDataChanged; }; template @@ -69,5 +76,12 @@ namespace systemtap Element; std::vector elements; }; + + inline GraphDataList& getGraphData() { return GraphDataBase::graphData; } + + inline sigc::signal& graphDataSignal() + { + return GraphDataBase::graphDataChanged; + } } #endif 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()) { diff --git a/grapher/GraphWidget.hxx b/grapher/GraphWidget.hxx index 0e9f2a29..89b86db9 100644 --- a/grapher/GraphWidget.hxx +++ b/grapher/GraphWidget.hxx @@ -42,14 +42,11 @@ namespace systemtap public: GraphWidget(); virtual ~GraphWidget(); - void addGraphData(std::tr1::shared_ptr data); void addGraph(); protected: typedef std::vector > GraphList; GraphList _graphs; - typedef std::vector > GraphDataList; - GraphDataList _graphData; // For click and drag std::tr1::shared_ptr _activeGraph; // Dragging all graphs simultaneously, or perhaps seperately @@ -99,6 +96,7 @@ namespace systemtap { return false; } + void onGraphDataChanged(); }; } #endif // SYSTEMTAP_GRAPHWIDGET_H diff --git a/grapher/StapParser.cxx b/grapher/StapParser.cxx index edb7f5f2..653c00de 100644 --- a/grapher/StapParser.cxx +++ b/grapher/StapParser.cxx @@ -118,7 +118,8 @@ vector commaSplit(const boost::sub_range& range) dataSet->color[2] = (hexColor & 0xff) / 255.0; dataSet->scale = scale; _dataSets.insert(std::make_pair(setName, dataSet)); - _widget->addGraphData(dataSet); + getGraphData().push_back(dataSet); + graphDataSignal().emit(); } else if (style == "discreet") { @@ -131,7 +132,8 @@ vector commaSplit(const boost::sub_range& range) dataSet->color[2] = (hexColor & 0xff) / 255.0; dataSet->scale = scale; _dataSets.insert(std::make_pair(setName, dataSet)); - _widget->addGraphData(dataSet); + getGraphData().push_back(dataSet); + graphDataSignal().emit(); } } else if ((found = find_first(dataString, "%CSV:"))) diff --git a/grapher/StapParser.hxx b/grapher/StapParser.hxx index 476b0071..eba8a7af 100644 --- a/grapher/StapParser.hxx +++ b/grapher/StapParser.hxx @@ -7,7 +7,6 @@ // later version. #include "GraphData.hxx" -#include "GraphWidget.hxx" #include namespace systemtap @@ -19,14 +18,12 @@ class StapParser DataMap _dataSets; CSVData _csv; Gtk::Window* _win; - GraphWidget* _widget; int _errFd; int _inFd; unsigned char _lineEndChar; public: - StapParser(Gtk::Window* win, - GraphWidget* widget) : _win(win), _widget(widget), _errFd(-1), - _inFd(-1), _lineEndChar('\n') + StapParser(Gtk::Window* win) + : _win(win), _errFd(-1), _inFd(-1), _lineEndChar('\n') { } void parseData(std::tr1::shared_ptr gdata, diff --git a/grapher/grapher.cxx b/grapher/grapher.cxx index 567b0405..4769877f 100644 --- a/grapher/grapher.cxx +++ b/grapher/grapher.cxx @@ -161,7 +161,7 @@ public: void cleanUp(); tr1::shared_ptr makeStapParser() { - tr1::shared_ptr result(new StapParser(_win, _widget)); + tr1::shared_ptr result(new StapParser(_win)); _parsers.push_back(ParserInstance(-1, result)); return result; } @@ -274,7 +274,7 @@ int StapLauncher::launch() } _exit(1); } - tr1::shared_ptr sp(new StapParser(_win, _widget)); + tr1::shared_ptr sp(new StapParser(_win)); _parsers.push_back(ParserInstance(childPid, sp)); sp->setErrFd(pipefd[2]); sp->setInFd(pipefd[0]); -- cgit From 8cabc8bcdcd22e8726734f1a18f23ed7d9c19e9f Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Tue, 8 Dec 2009 23:17:47 +0100 Subject: grapher: start of a dialog for displaying active stap processes The names of active scripts are displayed in a list; mock buttons suggest being able to stop and restart them. * grapher/processwindow.glade: new file * grapher/Makefile.am: add processwindow.glade to installed files * grapher/StapParser.hxx (StapProcess): new class (StapParser): factor out members that are now in StapProcess (ioCallback): Use the new childDied signal instead of aborting the whole grapher when a child dies. * grapher/grapher.cxx (ProcModelColumns, ProcWindow): classes for displaying stap process window. (GrapherWindow::on_menu_proc_window): new function --- grapher/Makefile.am | 2 +- grapher/Makefile.in | 5 +- grapher/StapParser.cxx | 12 +- grapher/StapParser.hxx | 77 ++++++--- grapher/grapher.cxx | 156 ++++++++++++++---- grapher/processwindow.glade | 372 +++++++++++++++++++++++++++++++++++++++++++ grapher/processwindow.gladep | 8 + 7 files changed, 573 insertions(+), 59 deletions(-) create mode 100644 grapher/processwindow.glade create mode 100644 grapher/processwindow.gladep (limited to 'grapher') diff --git a/grapher/Makefile.am b/grapher/Makefile.am index 5bca286a..1087f44d 100644 --- a/grapher/Makefile.am +++ b/grapher/Makefile.am @@ -9,5 +9,5 @@ stapgraph_CPPFLAGS = -DPKGDATADIR='"${pkgdatadir}"' stapgraph_CXXFLAGS = $(libglade_CFLAGS) -Wall -Werror stapgraph_SOURCES = grapher.cxx StapParser.cxx Graph.cxx GraphWidget.cxx CairoWidget.cxx GraphStyle.cxx stapgraph_LDADD = $(libglade_LIBS) -dist_pkgdata_DATA = graph-dialog.glade stap-start.glade +dist_pkgdata_DATA = graph-dialog.glade stap-start.glade processwindow.glade endif diff --git a/grapher/Makefile.in b/grapher/Makefile.in index f06402bc..c608e516 100644 --- a/grapher/Makefile.in +++ b/grapher/Makefile.in @@ -112,7 +112,8 @@ am__base_list = \ man1dir = $(mandir)/man1 NROFF = nroff MANS = $(man_MANS) -am__dist_pkgdata_DATA_DIST = graph-dialog.glade stap-start.glade +am__dist_pkgdata_DATA_DIST = graph-dialog.glade stap-start.glade \ + processwindow.glade DATA = $(dist_pkgdata_DATA) ETAGS = etags CTAGS = ctags @@ -246,7 +247,7 @@ top_srcdir = @top_srcdir@ @BUILD_GRAPHER_TRUE@stapgraph_CXXFLAGS = $(libglade_CFLAGS) -Wall -Werror @BUILD_GRAPHER_TRUE@stapgraph_SOURCES = grapher.cxx StapParser.cxx Graph.cxx GraphWidget.cxx CairoWidget.cxx GraphStyle.cxx @BUILD_GRAPHER_TRUE@stapgraph_LDADD = $(libglade_LIBS) -@BUILD_GRAPHER_TRUE@dist_pkgdata_DATA = graph-dialog.glade stap-start.glade +@BUILD_GRAPHER_TRUE@dist_pkgdata_DATA = graph-dialog.glade stap-start.glade processwindow.glade all: all-am .SUFFIXES: diff --git a/grapher/StapParser.cxx b/grapher/StapParser.cxx index 653c00de..b82cc024 100644 --- a/grapher/StapParser.cxx +++ b/grapher/StapParser.cxx @@ -25,6 +25,12 @@ namespace systemtap using namespace std; using namespace std::tr1; + sigc::signal& childDiedSignal() + { + static sigc::signal deathSignal; + return deathSignal; + } + vector commaSplit(const boost::sub_range& range) { using namespace boost; @@ -72,7 +78,7 @@ vector commaSplit(const boost::sub_range& range) using namespace boost; if (ioCondition & Glib::IO_HUP) { - _win->hide(); + childDiedSignal().emit(getPid()); return true; } if ((ioCondition & Glib::IO_IN) == 0) @@ -82,7 +88,7 @@ vector commaSplit(const boost::sub_range& range) bytes_read = read(_inFd, buf, sizeof(buf) - 1); if (bytes_read <= 0) { - _win->hide(); + childDiedSignal().emit(getPid()); return true; } _buffer.append(buf, bytes_read); @@ -242,7 +248,7 @@ vector commaSplit(const boost::sub_range& range) bytes_read = read(_errFd, buf, sizeof(buf) - 1); if (bytes_read <= 0) { - _win->hide(); + cerr << "StapParser: error reading from stderr!\n"; return true; } if (write(STDOUT_FILENO, buf, bytes_read) < 0) diff --git a/grapher/StapParser.hxx b/grapher/StapParser.hxx index eba8a7af..cfb807a8 100644 --- a/grapher/StapParser.hxx +++ b/grapher/StapParser.hxx @@ -9,30 +9,61 @@ #include "GraphData.hxx" #include +#include + +#include + namespace systemtap { -class StapParser -{ - std::string _buffer; - typedef std::map > DataMap; - DataMap _dataSets; - CSVData _csv; - Gtk::Window* _win; - int _errFd; - int _inFd; - unsigned char _lineEndChar; -public: - StapParser(Gtk::Window* win) - : _win(win), _errFd(-1), _inFd(-1), _lineEndChar('\n') + // arguments and script for a stap process + struct StapProcess { - } - void parseData(std::tr1::shared_ptr gdata, - int64_t time, const std::string& dataString); - bool ioCallback(Glib::IOCondition ioCondition); - bool errIoCallback(Glib::IOCondition ioCondition); - int getErrFd() { return _errFd; } - void setErrFd(int fd) { _errFd = fd; } - int getInFd() { return _inFd; } - void setInFd(int fd) { _inFd = fd; } -}; + StapProcess(pid_t pid_ = -1) : pid(pid_) {} + std::string stapArgs; + std::string script; + std::string scriptArgs; + // arguments passed from a single array, like from the command line. + char **argv; + // -1 if the grapher is reading from stdin + pid_t pid; + }; + + class StapParser + { + std::string _buffer; + typedef std::map > DataMap; + DataMap _dataSets; + CSVData _csv; + int _errFd; + int _inFd; + unsigned char _lineEndChar; + std::tr1::shared_ptr _process; + public: + StapParser() + : _errFd(-1), _inFd(-1), _lineEndChar('\n') + { + } + void parseData(std::tr1::shared_ptr gdata, + int64_t time, const std::string& dataString); + bool ioCallback(Glib::IOCondition ioCondition); + bool errIoCallback(Glib::IOCondition ioCondition); + int getErrFd() const { return _errFd; } + void setErrFd(int fd) { _errFd = fd; } + int getInFd() const { return _inFd; } + void setInFd(int fd) { _inFd = fd; } + pid_t getPid() const + { + if (_process) + return _process->pid; + else + return -1; + } + std::tr1::shared_ptr getProcess() { return _process; } + void setProcess(std::tr1::shared_ptr process) + { + _process = process; + } + }; + + sigc::signal& childDiedSignal(); } diff --git a/grapher/grapher.cxx b/grapher/grapher.cxx index 4769877f..2a9a617b 100644 --- a/grapher/grapher.cxx +++ b/grapher/grapher.cxx @@ -39,6 +39,7 @@ #include using namespace std; +using namespace tr1; using namespace systemtap; @@ -159,22 +160,33 @@ public: } int launch(); void cleanUp(); - tr1::shared_ptr makeStapParser() + shared_ptr makeStapParser() { - tr1::shared_ptr result(new StapParser(_win)); - _parsers.push_back(ParserInstance(-1, result)); + shared_ptr result(new StapParser); + _parsers.push_back(result); return result; } +private: + struct pidPred + { + pidPred(pid_t pid_) : pid(pid_) {} + bool operator()(const shared_ptr& parser) const + { + return parser->getPid() == pid; + } + pid_t pid; + }; +public: pid_t reap() { + using namespace boost; pid_t pid = ChildDeathReader::reap(); if (pid < 0) return pid; ParserList::iterator itr - = find_if(_parsers.begin(), _parsers.end(), - boost::bind(&ParserInstance::childPid, _1) == pid); + = find_if(_parsers.begin(), _parsers.end(), pidPred(pid)); if (itr != _parsers.end()) - itr->childPid = -1; + (*itr)->setProcess(tr1::shared_ptr()); return pid; } void killAll() @@ -183,10 +195,12 @@ public: itr != end; ++itr) { - if (itr->childPid >= 0) - kill(itr->childPid, SIGTERM); + if ((*itr)->getPid() >= 0) + kill((*itr)->getPid(), SIGTERM); } } + typedef vector > ParserList; + ParserList _parsers; protected: char** _argv; string _stapArgs; @@ -196,18 +210,6 @@ protected: ChildDeathReader::Callback* _deathCallback; Gtk::Window* _win; GraphWidget* _widget; - struct ParserInstance - { - ParserInstance() : childPid(-1) {} - ParserInstance(int childPid_, tr1::shared_ptr stapParser_) - : childPid(childPid_), stapParser(stapParser_) - { - } - pid_t childPid; - tr1::shared_ptr stapParser; - }; - typedef vector ParserList; - ParserList _parsers; }; int StapLauncher::launch() @@ -274,8 +276,18 @@ int StapLauncher::launch() } _exit(1); } - tr1::shared_ptr sp(new StapParser(_win)); - _parsers.push_back(ParserInstance(childPid, sp)); + tr1::shared_ptr sp(new StapParser); + shared_ptr proc(new StapProcess(childPid)); + if (_argv) + proc->argv = _argv; + else + { + proc->stapArgs = _stapArgs; + proc->script = _script; + proc->scriptArgs = _scriptArgs; + } + sp->setProcess(proc); + _parsers.push_back(sp); sp->setErrFd(pipefd[2]); sp->setInFd(pipefd[0]); Glib::signal_io().connect(sigc::mem_fun(sp.get(), @@ -305,21 +317,22 @@ void StapLauncher::cleanUp() itr != end; ++itr) { - if (itr->childPid > 0) - kill(itr->childPid, SIGTERM); + pid_t childPid = (*itr)->getPid(); + if (childPid > 0) + kill(childPid, SIGTERM); int status; pid_t killedPid = -1; if ((killedPid = wait(&status)) == -1) { std::perror("wait"); } - else if (killedPid != itr->childPid) + else if (killedPid != childPid) { std::cerr << "wait: killed Pid " << killedPid << " != child Pid " - << itr->childPid << "\n"; + << childPid << "\n"; } else if (_deathCallback) - _deathCallback->childDied(itr->childPid); + _deathCallback->childDied(childPid); } } @@ -338,6 +351,58 @@ private: Gtk::Entry* _scriptArgEntry; }; +class ProcModelColumns : public Gtk::TreeModelColumnRecord +{ +public: + ProcModelColumns() + { + add(_scriptName); + add(_proc); + } + Gtk::TreeModelColumn _scriptName; + Gtk::TreeModelColumn > _proc; +}; + +class ProcWindow +{ +public: + ProcWindow(); + ProcModelColumns _modelColumns; + Glib::RefPtr _xml; + Gtk::Window* _window; + Gtk::TreeView* _dataTreeView; + Glib::RefPtr _listStore; + void onClose(); +}; + +ProcWindow::ProcWindow() +{ + try + { + _xml = Gnome::Glade::Xml::create(PKGDATADIR "/processwindow.glade"); + _xml->get_widget("window1", _window); + _xml->get_widget("treeview1", _dataTreeView); + + } + catch (const Gnome::Glade::XmlError& ex ) + { + std::cerr << ex.what() << std::endl; + throw; + } + _listStore = Gtk::ListStore::create(_modelColumns); + _dataTreeView->set_model(_listStore); + _dataTreeView->append_column("Script", _modelColumns._scriptName); + Gtk::Button* button = 0; + _xml->get_widget("button5", button); + button->signal_clicked().connect(sigc::mem_fun(*this, &ProcWindow::onClose), + false); +} + +void ProcWindow::onClose() +{ + _window->hide(); +} + class GrapherWindow : public Gtk::Window, public ChildDeathReader::Callback { public: @@ -355,25 +420,28 @@ public: protected: virtual void on_menu_file_quit(); virtual void on_menu_script_start(); + virtual void on_menu_proc_window(); void addGraph(); // menu support Glib::RefPtr m_refUIManager; Glib::RefPtr m_refActionGroup; GraphicalStapLauncher* _graphicalLauncher; - + shared_ptr _procWindow; }; + GrapherWindow::GrapherWindow() + : _procWindow(new ProcWindow) { set_title("systemtap grapher"); add(m_Box); - + //Create actions for menus and toolbars: m_refActionGroup = Gtk::ActionGroup::create(); //File menu: m_refActionGroup->add(Gtk::Action::create("FileMenu", "File")); - m_refActionGroup->add(Gtk::Action::create("StartScript", "Start script"), + m_refActionGroup->add(Gtk::Action::create("StartScript", "Start script..."), sigc::mem_fun(*this, &GrapherWindow::on_menu_script_start)); m_refActionGroup->add(Gtk::Action::create("AddGraph", "Add graph"), @@ -381,6 +449,12 @@ GrapherWindow::GrapherWindow() m_refActionGroup->add(Gtk::Action::create("FileQuit", Gtk::Stock::QUIT), sigc::mem_fun(*this, &GrapherWindow::on_menu_file_quit)); + // Window menu + m_refActionGroup->add(Gtk::Action::create("WindowMenu", "Window")); + m_refActionGroup->add(Gtk::Action::create("ProcessWindow", + "Stap processes..."), + sigc::mem_fun(*this, + &GrapherWindow::on_menu_proc_window)); m_refUIManager = Gtk::UIManager::create(); m_refUIManager->insert_action_group(m_refActionGroup); @@ -394,6 +468,9 @@ GrapherWindow::GrapherWindow() " " " " " " + " " + " " + " " " " ""; try @@ -425,6 +502,25 @@ void GrapherWindow::on_menu_script_start() _graphicalLauncher->runDialog(); } + +void GrapherWindow::on_menu_proc_window() +{ + _procWindow->_listStore->clear(); + for (StapLauncher::ParserList::iterator spitr + = _graphicalLauncher->_parsers.begin(), + end = _graphicalLauncher->_parsers.end(); + spitr != end; + ++spitr) + { + shared_ptr sp = (*spitr)->getProcess(); + Gtk::TreeModel::iterator litr = _procWindow->_listStore->append(); + Gtk::TreeModel::Row row = *litr; + if (sp) + row[_procWindow->_modelColumns._scriptName] = sp->script; + } + _procWindow->_window->show(); +} + void GrapherWindow::childDied(int pid) { hide(); diff --git a/grapher/processwindow.glade b/grapher/processwindow.glade new file mode 100644 index 00000000..ad1bdd14 --- /dev/null +++ b/grapher/processwindow.glade @@ -0,0 +1,372 @@ + + + + + + + stap processes + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + True + False + True + False + False + GDK_WINDOW_TYPE_HINT_DIALOG + GDK_GRAVITY_NORTH_WEST + True + False + + + + True + False + 0 + + + + True + GTK_ORIENTATION_HORIZONTAL + GTK_TOOLBAR_BOTH + True + True + + + + True + True + True + False + + + + True + True + GTK_RELIEF_NORMAL + True + + + + True + 0.5 + 0.5 + 0 + 0 + 0 + 0 + 0 + 0 + + + + True + False + 2 + + + + True + gtk-cancel + 4 + 0.5 + 0.5 + 0 + 0 + + + 0 + False + False + + + + + + True + Kill + True + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + + + + + + False + False + + + + + + True + True + True + False + + + + True + True + GTK_RELIEF_NORMAL + True + + + + True + 0.5 + 0.5 + 0 + 0 + 0 + 0 + 0 + 0 + + + + True + False + 2 + + + + True + gtk-refresh + 4 + 0.5 + 0.5 + 0 + 0 + + + 0 + False + False + + + + + + True + Restart + True + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + + + + + + False + False + + + + + 0 + False + False + + + + + + True + False + 0 + + + + True + True + GTK_POLICY_ALWAYS + GTK_POLICY_ALWAYS + GTK_SHADOW_IN + GTK_CORNER_TOP_LEFT + + + + 300 + True + True + True + False + False + True + False + False + False + + + + + 0 + True + True + + + + + + 26 + True + GTK_SHADOW_OUT + GTK_POS_LEFT + GTK_POS_TOP + + + + True + False + 0 + + + + 17 + True + label1 + False + False + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + True + GTK_POLICY_ALWAYS + GTK_POLICY_ALWAYS + GTK_SHADOW_IN + GTK_CORNER_TOP_LEFT + + + + 20 + True + True + True + False + True + GTK_JUSTIFY_LEFT + GTK_WRAP_NONE + True + 0 + 0 + 0 + 0 + 0 + 0 + + + + + + 0 + True + True + + + + + + + 0 + True + True + + + + + 0 + True + True + + + + + + True + GTK_BUTTONBOX_END + 0 + + + + True + True + True + gtk-close + True + GTK_RELIEF_NORMAL + True + + + + + 0 + True + True + + + + + + + diff --git a/grapher/processwindow.gladep b/grapher/processwindow.gladep new file mode 100644 index 00000000..183077ba --- /dev/null +++ b/grapher/processwindow.gladep @@ -0,0 +1,8 @@ + + + + + + + FALSE + -- cgit From 3e1613e1f7ab589089e8ed5a504330bb9cb128db Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Wed, 9 Dec 2009 22:09:39 +0100 Subject: show the status of stap processes in the process window Also, the "kill" button now works. * grapher/StapParser.hxx (_ioConnection, _errIoConnection): new members for sigc connections. (initIo): New function (parsers, parserListChangedSignal): new variables * grapher/StapParser.cxx (ioCallback): disconnect signals when child dies (initIo): new function * grapher/grapher.cxx (StapLauncher): eliminate death callback in favor of childDied signal. Use global parsers list (ProcWindow::_listSelection): new member (ProcWindow::show, hide, onParserListChanged, onSelectionChanged, onKill): new functions (ProcWindow::ProcWindow): Set up cell renderer for status icon * grapher/processwindow.glade: labels for display script and stap arguments --- grapher/StapParser.cxx | 338 ++++++++++++++++++++++++-------------------- grapher/StapParser.hxx | 8 ++ grapher/grapher.cxx | 167 +++++++++++++++------- grapher/processwindow.glade | 132 ++++++++++++++--- 4 files changed, 418 insertions(+), 227 deletions(-) (limited to 'grapher') diff --git a/grapher/StapParser.cxx b/grapher/StapParser.cxx index b82cc024..a9a5109a 100644 --- a/grapher/StapParser.cxx +++ b/grapher/StapParser.cxx @@ -30,7 +30,15 @@ namespace systemtap static sigc::signal deathSignal; return deathSignal; } - + + ParserList parsers; + + sigc::signal& parserListChangedSignal() + { + static sigc::signal listChangedSignal; + return listChangedSignal; + } + vector commaSplit(const boost::sub_range& range) { using namespace boost; @@ -72,171 +80,173 @@ vector commaSplit(const boost::sub_range& range) } bool StapParser::ioCallback(Glib::IOCondition ioCondition) - { - using namespace std; - using std::tr1::shared_ptr; - using namespace boost; - if (ioCondition & Glib::IO_HUP) - { - childDiedSignal().emit(getPid()); - return true; - } - if ((ioCondition & Glib::IO_IN) == 0) + { + using namespace std; + using std::tr1::shared_ptr; + using namespace boost; + if (ioCondition & Glib::IO_HUP) + { + childDiedSignal().emit(getPid()); + _ioConnection.disconnect(); + _errIoConnection.disconnect(); + return true; + } + if ((ioCondition & Glib::IO_IN) == 0) + return true; + char buf[256]; + ssize_t bytes_read = 0; + bytes_read = read(_inFd, buf, sizeof(buf) - 1); + if (bytes_read <= 0) + { + childDiedSignal().emit(getPid()); return true; - char buf[256]; - ssize_t bytes_read = 0; - bytes_read = read(_inFd, buf, sizeof(buf) - 1); - if (bytes_read <= 0) - { - childDiedSignal().emit(getPid()); - return true; - } - _buffer.append(buf, bytes_read); - string::size_type ret = string::npos; - while ((ret = _buffer.find(_lineEndChar)) != string::npos) - { - Glib::ustring dataString(_buffer, 0, ret); - // %DataSet and %CSV declare a data set; all other - // statements begin with the name of a data set. - // Except %LineEnd :) - sub_range found; - if (dataString[0] == '%') - { - if ((found = find_first(dataString, "%DataSet:"))) - { - string setName; - int hexColor; - double scale; - string style; - istringstream stream(Glib::ustring(found.end(), - dataString.end())); - stream >> setName >> scale >> std::hex >> hexColor - >> style; - if (style == "bar" || style == "dot") - { - std::tr1::shared_ptr > - dataSet(new GraphData); - dataSet->name = setName; - if (style == "dot") - dataSet->style = &GraphStyleDot::instance; - dataSet->color[0] = (hexColor >> 16) / 255.0; - dataSet->color[1] = ((hexColor >> 8) & 0xff) / 255.0; - dataSet->color[2] = (hexColor & 0xff) / 255.0; - dataSet->scale = scale; - _dataSets.insert(std::make_pair(setName, dataSet)); - getGraphData().push_back(dataSet); - graphDataSignal().emit(); - } - else if (style == "discreet") - { - std::tr1::shared_ptr > - dataSet(new GraphData); - dataSet->name = setName; - dataSet->style = &GraphStyleEvent::instance; - dataSet->color[0] = (hexColor >> 16) / 255.0; - dataSet->color[1] = ((hexColor >> 8) & 0xff) / 255.0; - dataSet->color[2] = (hexColor & 0xff) / 255.0; - dataSet->scale = scale; - _dataSets.insert(std::make_pair(setName, dataSet)); - getGraphData().push_back(dataSet); - graphDataSignal().emit(); - } - } - else if ((found = find_first(dataString, "%CSV:"))) - { - vector tokens - = commaSplit(sub_range(found.end(), - dataString.end())); - for (vector::iterator tokIter = tokens.begin(), - e = tokens.end(); - tokIter != e; - ++tokIter) - { - DataMap::iterator setIter = _dataSets.find(*tokIter); - if (setIter != _dataSets.end()) - _csv.elements - .push_back(CSVData::Element(*tokIter, - setIter->second)); - } - } - else if ((found = find_first(dataString, "%LineEnd:"))) - { - istringstream stream(Glib::ustring(found.end(), - dataString.end())); - int charAsInt = 0; - // parse hex and octal numbers too - stream >> std::setbase(0) >> charAsInt; - _lineEndChar = static_cast(charAsInt); - } - else - { - cerr << "Unknown declaration " << dataString << endl; - } - } - else - { - std::istringstream stream(dataString); - string setName; - stream >> setName; - DataMap::iterator itr = _dataSets.find(setName); - if (itr != _dataSets.end()) - { - shared_ptr gdata = itr->second; - string decl; - // Hack: scan from the beginning of dataString again - if (findTaggedValue(dataString, "%Title:", decl)) - { - gdata->title = decl; - } - else if (findTaggedValue(dataString, "%XAxisTitle:", decl)) - { - gdata->xAxisText = decl; - } - else if (findTaggedValue(dataString, "%YAxisTitle:", decl)) - { - gdata->yAxisText = decl; - } - else if ((found = find_first(dataString, "%YMax:"))) - { - double ymax; - std::istringstream - stream(Glib::ustring(found.end(), dataString.end())); - stream >> ymax; - gdata->scale = ymax; - } - else + } + _buffer.append(buf, bytes_read); + string::size_type ret = string::npos; + while ((ret = _buffer.find(_lineEndChar)) != string::npos) + { + Glib::ustring dataString(_buffer, 0, ret); + // %DataSet and %CSV declare a data set; all other + // statements begin with the name of a data set. + // Except %LineEnd :) + sub_range found; + if (dataString[0] == '%') + { + if ((found = find_first(dataString, "%DataSet:"))) + { + string setName; + int hexColor; + double scale; + string style; + istringstream stream(Glib::ustring(found.end(), + dataString.end())); + stream >> setName >> scale >> std::hex >> hexColor + >> style; + if (style == "bar" || style == "dot") + { + std::tr1::shared_ptr > + dataSet(new GraphData); + dataSet->name = setName; + if (style == "dot") + dataSet->style = &GraphStyleDot::instance; + dataSet->color[0] = (hexColor >> 16) / 255.0; + dataSet->color[1] = ((hexColor >> 8) & 0xff) / 255.0; + dataSet->color[2] = (hexColor & 0xff) / 255.0; + dataSet->scale = scale; + _dataSets.insert(std::make_pair(setName, dataSet)); + getGraphData().push_back(dataSet); + graphDataSignal().emit(); + } + else if (style == "discreet") { - if (!_csv.elements.empty()) + std::tr1::shared_ptr > + dataSet(new GraphData); + dataSet->name = setName; + dataSet->style = &GraphStyleEvent::instance; + dataSet->color[0] = (hexColor >> 16) / 255.0; + dataSet->color[1] = ((hexColor >> 8) & 0xff) / 255.0; + dataSet->color[2] = (hexColor & 0xff) / 255.0; + dataSet->scale = scale; + _dataSets.insert(std::make_pair(setName, dataSet)); + getGraphData().push_back(dataSet); + graphDataSignal().emit(); + } + } + else if ((found = find_first(dataString, "%CSV:"))) + { + vector tokens + = commaSplit(sub_range(found.end(), + dataString.end())); + for (vector::iterator tokIter = tokens.begin(), + e = tokens.end(); + tokIter != e; + ++tokIter) + { + DataMap::iterator setIter = _dataSets.find(*tokIter); + if (setIter != _dataSets.end()) + _csv.elements + .push_back(CSVData::Element(*tokIter, + setIter->second)); + } + } + else if ((found = find_first(dataString, "%LineEnd:"))) + { + istringstream stream(Glib::ustring(found.end(), + dataString.end())); + int charAsInt = 0; + // parse hex and octal numbers too + stream >> std::setbase(0) >> charAsInt; + _lineEndChar = static_cast(charAsInt); + } + else + { + cerr << "Unknown declaration " << dataString << endl; + } + } + else + { + std::istringstream stream(dataString); + string setName; + stream >> setName; + DataMap::iterator itr = _dataSets.find(setName); + if (itr != _dataSets.end()) + { + shared_ptr gdata = itr->second; + string decl; + // Hack: scan from the beginning of dataString again + if (findTaggedValue(dataString, "%Title:", decl)) + { + gdata->title = decl; + } + else if (findTaggedValue(dataString, "%XAxisTitle:", decl)) + { + gdata->xAxisText = decl; + } + else if (findTaggedValue(dataString, "%YAxisTitle:", decl)) + { + gdata->yAxisText = decl; + } + else if ((found = find_first(dataString, "%YMax:"))) + { + double ymax; + std::istringstream + stream(Glib::ustring(found.end(), dataString.end())); + stream >> ymax; + gdata->scale = ymax; + } + else + { + if (!_csv.elements.empty()) { - vector tokens = commaSplit(dataString); - int i = 0; - int64_t time; - vector::iterator tokIter = tokens.begin(); - std::istringstream timeStream(*tokIter++); - timeStream >> time; - for (vector::iterator e = tokens.end(); - tokIter != e; - ++tokIter, ++i) + vector tokens = commaSplit(dataString); + int i = 0; + int64_t time; + vector::iterator tokIter = tokens.begin(); + std::istringstream timeStream(*tokIter++); + timeStream >> time; + for (vector::iterator e = tokens.end(); + tokIter != e; + ++tokIter, ++i) { - parseData(_csv.elements[i].second, time, - *tokIter); + parseData(_csv.elements[i].second, time, + *tokIter); } } - else + else { - int64_t time; - stringbuf data; - stream >> time; - stream.get(data, _lineEndChar); - parseData(itr->second, time, data.str()); + int64_t time; + stringbuf data; + stream >> time; + stream.get(data, _lineEndChar); + parseData(itr->second, time, data.str()); } } - } - } - _buffer.erase(0, ret + 1); - } - return true; - } + } + } + _buffer.erase(0, ret + 1); + } + return true; + } bool StapParser::errIoCallback(Glib::IOCondition ioCondition) { @@ -255,4 +265,18 @@ vector commaSplit(const boost::sub_range& range) ; return true; } + + void StapParser::initIo(int inFd, int errFd) + { + _inFd = inFd; + _errFd = errFd; + _ioConnection = Glib::signal_io() + .connect(sigc::mem_fun(*this, &StapParser::errIoCallback), + _errFd, + Glib::IO_IN); + _errIoConnection = Glib::signal_io() + .connect(sigc::mem_fun(*this, &StapParser::ioCallback), + _inFd, + Glib::IO_IN | Glib::IO_HUP); + } } diff --git a/grapher/StapParser.hxx b/grapher/StapParser.hxx index cfb807a8..4dd711e6 100644 --- a/grapher/StapParser.hxx +++ b/grapher/StapParser.hxx @@ -38,6 +38,8 @@ namespace systemtap int _inFd; unsigned char _lineEndChar; std::tr1::shared_ptr _process; + sigc::connection _ioConnection; + sigc::connection _errIoConnection; public: StapParser() : _errFd(-1), _inFd(-1), _lineEndChar('\n') @@ -63,7 +65,13 @@ namespace systemtap { _process = process; } + void initIo(int inFd, int errFd); }; sigc::signal& childDiedSignal(); + + typedef std::vector > ParserList; + extern ParserList parsers; + + sigc::signal& parserListChangedSignal(); } diff --git a/grapher/grapher.cxx b/grapher/grapher.cxx index 2a9a617b..00670fe0 100644 --- a/grapher/grapher.cxx +++ b/grapher/grapher.cxx @@ -106,14 +106,14 @@ private: class StapLauncher : public ChildDeathReader { public: - StapLauncher() : _argv(0), _childPid(-1), _deathCallback(0) {} + StapLauncher() : _argv(0), _childPid(-1) {} StapLauncher(char** argv) - : _argv(argv), _childPid(-1), _deathCallback(0) + : _argv(argv), _childPid(-1) { } StapLauncher(const string& stapArgs, const string& script, const string& scriptArgs) - : _childPid(-1), _deathCallback(0), _win(0), _widget(0) + : _childPid(-1), _win(0), _widget(0) { setArgs(stapArgs, script, scriptArgs); } @@ -149,10 +149,7 @@ public: _script.clear(); _scriptArgs.clear(); } - void setDeathCallback(ChildDeathReader::Callback* callback) - { - _deathCallback = callback; - } + void setWinParams(Gtk::Window* win, GraphWidget* widget) { _win = win; @@ -163,7 +160,8 @@ public: shared_ptr makeStapParser() { shared_ptr result(new StapParser); - _parsers.push_back(result); + parsers.push_back(result); + parserListChangedSignal().emit(); return result; } private: @@ -184,14 +182,22 @@ public: if (pid < 0) return pid; ParserList::iterator itr - = find_if(_parsers.begin(), _parsers.end(), pidPred(pid)); - if (itr != _parsers.end()) - (*itr)->setProcess(tr1::shared_ptr()); + = find_if(parsers.begin(), parsers.end(), pidPred(pid)); + if (itr != parsers.end()) + { + tr1::shared_ptr sp = (*itr)->getProcess(); + if (sp) + { + sp->pid = -1; + parserListChangedSignal().emit(); + } + } + childDiedSignal().emit(pid); return pid; } void killAll() { - for (ParserList::iterator itr = _parsers.begin(), end = _parsers.end(); + for (ParserList::iterator itr = parsers.begin(), end = parsers.end(); itr != end; ++itr) { @@ -199,15 +205,12 @@ public: kill((*itr)->getPid(), SIGTERM); } } - typedef vector > ParserList; - ParserList _parsers; protected: char** _argv; string _stapArgs; string _script; string _scriptArgs; int _childPid; - ChildDeathReader::Callback* _deathCallback; Gtk::Window* _win; GraphWidget* _widget; }; @@ -287,17 +290,9 @@ int StapLauncher::launch() proc->scriptArgs = _scriptArgs; } sp->setProcess(proc); - _parsers.push_back(sp); - sp->setErrFd(pipefd[2]); - sp->setInFd(pipefd[0]); - Glib::signal_io().connect(sigc::mem_fun(sp.get(), - &StapParser::errIoCallback), - pipefd[2], - Glib::IO_IN); - Glib::signal_io().connect(sigc::mem_fun(sp.get(), - &StapParser::ioCallback), - pipefd[0], - Glib::IO_IN | Glib::IO_HUP); + parsers.push_back(sp); + parserListChangedSignal().emit(); + sp->initIo(pipefd[0], pipefd[2]); return childPid; } @@ -313,7 +308,7 @@ void StapLauncher::cleanUp() char buf; while (read(signalPipe[0], &buf, 1) > 0) reap(); - for (ParserList::iterator itr = _parsers.begin(), end = _parsers.end(); + for (ParserList::iterator itr = parsers.begin(), end = parsers.end(); itr != end; ++itr) { @@ -331,8 +326,10 @@ void StapLauncher::cleanUp() std::cerr << "wait: killed Pid " << killedPid << " != child Pid " << childPid << "\n"; } - else if (_deathCallback) - _deathCallback->childDied(childPid); + else + { + childDiedSignal().emit(childPid); + } } } @@ -356,13 +353,16 @@ class ProcModelColumns : public Gtk::TreeModelColumnRecord public: ProcModelColumns() { + add(_iconName); add(_scriptName); add(_proc); } + Gtk::TreeModelColumn _iconName; Gtk::TreeModelColumn _scriptName; Gtk::TreeModelColumn > _proc; }; +// This should probably be a Gtk window, with the appropriate glade magic class ProcWindow { public: @@ -372,10 +372,20 @@ public: Gtk::Window* _window; Gtk::TreeView* _dataTreeView; Glib::RefPtr _listStore; + Glib::RefPtr _listSelection; void onClose(); + void show(); + void hide(); + void onParserListChanged(); + void onSelectionChanged(); + void onKill(); +private: + bool _open; + void refresh(); }; ProcWindow::ProcWindow() + : _open(false) { try { @@ -390,12 +400,27 @@ ProcWindow::ProcWindow() throw; } _listStore = Gtk::ListStore::create(_modelColumns); - _dataTreeView->set_model(_listStore); + _dataTreeView->set_model(_listStore); + // Display a nice icon for the state of the process + Gtk::CellRendererPixbuf* cell = Gtk::manage(new Gtk::CellRendererPixbuf); + _dataTreeView->append_column("State", *cell); + Gtk::TreeViewColumn* column = _dataTreeView->get_column(0); + if (column) + column->add_attribute(cell->property_icon_name(), _modelColumns._iconName); _dataTreeView->append_column("Script", _modelColumns._scriptName); Gtk::Button* button = 0; _xml->get_widget("button5", button); button->signal_clicked().connect(sigc::mem_fun(*this, &ProcWindow::onClose), false); + _xml->get_widget("button1", button); + button->signal_clicked().connect(sigc::mem_fun(*this, &ProcWindow::onKill), + false); + parserListChangedSignal() + .connect(sigc::mem_fun(*this, &ProcWindow::onParserListChanged)); + _listSelection = _dataTreeView->get_selection(); + _listSelection->signal_changed() + .connect(sigc::mem_fun(*this, &ProcWindow::onSelectionChanged)); + } void ProcWindow::onClose() @@ -403,6 +428,63 @@ void ProcWindow::onClose() _window->hide(); } +void ProcWindow::show() +{ + _open = true; + refresh(); + _window->show(); + +} + +void ProcWindow::hide() +{ + _open = false; + _window->hide(); +} + +void ProcWindow::refresh() +{ + _listStore->clear(); + for (ParserList::iterator spitr = parsers.begin(), end = parsers.end(); + spitr != end; + ++spitr) + { + shared_ptr sp = (*spitr)->getProcess(); + if (sp) + { + Gtk::TreeModel::iterator litr = _listStore->append(); + Gtk::TreeModel::Row row = *litr; + row[_modelColumns._iconName] = sp->pid >= 0 ? "gtk-yes" : "gtk-no"; + row[_modelColumns._scriptName] = sp->script; + row[_modelColumns._proc] = sp; + } + } +} + +void ProcWindow::onParserListChanged() +{ + if (_open) + { + refresh(); + _window->queue_draw(); + } +} + +void ProcWindow::onSelectionChanged() +{ +} + +void ProcWindow::onKill() +{ + Gtk::TreeModel::iterator itr = _listSelection->get_selected(); + if (!itr) + return; + Gtk::TreeModel::Row row = *itr; + shared_ptr proc = row[_modelColumns._proc]; + if (proc->pid >= 0) + kill(proc->pid, SIGTERM); +} + class GrapherWindow : public Gtk::Window, public ChildDeathReader::Callback { public: @@ -411,7 +493,6 @@ public: Gtk::VBox m_Box; Gtk::ScrolledWindow scrolled; GraphWidget w; - void childDied(int pid); void setGraphicalLauncher(GraphicalStapLauncher* launcher) { _graphicalLauncher = launcher; @@ -505,30 +586,9 @@ void GrapherWindow::on_menu_script_start() void GrapherWindow::on_menu_proc_window() { - _procWindow->_listStore->clear(); - for (StapLauncher::ParserList::iterator spitr - = _graphicalLauncher->_parsers.begin(), - end = _graphicalLauncher->_parsers.end(); - spitr != end; - ++spitr) - { - shared_ptr sp = (*spitr)->getProcess(); - Gtk::TreeModel::iterator litr = _procWindow->_listStore->append(); - Gtk::TreeModel::Row row = *litr; - if (sp) - row[_procWindow->_modelColumns._scriptName] = sp->script; - } - _procWindow->_window->show(); + _procWindow->show(); } -void GrapherWindow::childDied(int pid) -{ - hide(); -} - - - - int main(int argc, char** argv) { Gtk::Main app(argc, argv); @@ -553,7 +613,6 @@ int main(int argc, char** argv) else if (argc > 1) { launcher.setArgv(argv + 1); - launcher.setDeathCallback(&win); launcher.launch(); } Gtk::Main::run(win); diff --git a/grapher/processwindow.glade b/grapher/processwindow.glade index ad1bdd14..3886cb34 100644 --- a/grapher/processwindow.glade +++ b/grapher/processwindow.glade @@ -263,23 +263,60 @@ 0 - - 17 + True - label1 - False - False - GTK_JUSTIFY_LEFT - False - False - 0 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 + False + 0 + + + + True + stap arguments: + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + 0 @@ -288,6 +325,69 @@ + + + True + False + 0 + + + + True + script arguments: + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + + True + + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + 0 + True + True + + + True -- cgit From f8e5918969fb63a6148c906025e4ec1f010ca6c6 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Thu, 10 Dec 2009 13:13:30 +0100 Subject: grapher: change SIGCHLD handling and exit cleanup The signal handler now calls waitpid() and writes the pid and status to its pipe. * grapher.cxx (ChildInfo): new class (handleChild): wait for zombies and write their status down the pipe. (ChildDeathReader::ioCallback): Read dead child's pid from signal handler pipe and emit signal. (ChildDeathReader::reap): delete (ChildDeathReader::Callback): delete (StapLauncher::StapLauncher): Connect to childDied signal. (StapLauncher setWinParams, reap, cleanUp): delete (onChildDied): new function that updates the parsers list when a child dies. (GrapherWindow): Remove ChildDeathReader::Callback base class (GrapherWindow::onParserListChanged): New function; exits if program is quitting and no parsers are left. (on_menu_file_quit): Kill all children; don't hide (and exit) unless there are no parsers. (main): Don't do any cleanup after Gtk loop exits. --- grapher/grapher.cxx | 210 ++++++++++++++++++++++++---------------------------- 1 file changed, 95 insertions(+), 115 deletions(-) (limited to 'grapher') diff --git a/grapher/grapher.cxx b/grapher/grapher.cxx index 00670fe0..8e7a4dba 100644 --- a/grapher/grapher.cxx +++ b/grapher/grapher.cxx @@ -46,59 +46,78 @@ using namespace systemtap; // magic for noticing that the child stap process has died. int signalPipe[2] = {-1, -1}; +struct ChildInfo + { + pid_t pid; + int waitInfo; +}; + extern "C" -{ - void handleChild(int signum, siginfo_t* info, void* context) - { - char buf[1]; - ssize_t err; - buf[0] = 1; - err = write(signalPipe[1], buf, 1); - } + { + void handleChild(int signum, siginfo_t* info, void* context) + { + struct ChildInfo childInfo; + ssize_t err; + + // Loop doing waitpid because the SIGCHLD signal isn't queued; + // multiple signals might get lost. If we get an error because + // there are no zombie children (return value <= 0 because of + // WNOHANG or no children exist at all), assume that an earlier + // invocation of handleChild already cleaned them up. + while ((childInfo.pid = waitpid(-1, &childInfo.waitInfo, WNOHANG))) + { + if (childInfo.pid < 0 && errno != ECHILD) + { + char errbuf[256]; + strerror_r(errno, errbuf, sizeof(errbuf)); + err = write(STDERR_FILENO, errbuf, strlen(errbuf)); + err = write(STDERR_FILENO, "\n", 1); + return; + } + else if (childInfo.pid > 0) + { + err = write(signalPipe[1], &childInfo, sizeof(childInfo)); + } + else + return; + } + } } // Waits for a gtk I/O signal, indicating that a child has died, then // performs an action class ChildDeathReader + { + public: + ChildDeathReader() : sigfd(-1) {} + ChildDeathReader(int sigfd_) : sigfd(sigfd_) {} + int getSigfd() { return sigfd; } + void setSigfd(int sigfd_) { sigfd = sigfd_; } + + bool ioCallback(Glib::IOCondition ioCondition) + { + if ((ioCondition & Glib::IO_IN) == 0) + return true; + ChildInfo info; + if (read(sigfd, &info, sizeof(info)) < static_cast(sizeof(info))) + cerr << "couldn't read ChildInfo from signal handler\n"; + else + childDiedSignal().emit(info.pid); + return true; + } +private: + int sigfd; +}; + +struct PidPred { -public: - struct Callback - { - virtual ~Callback() {} - virtual void childDied(int pid) {} - }; - ChildDeathReader() : sigfd(-1) {} - ChildDeathReader(int sigfd_) : sigfd(sigfd_) {} - int getSigfd() { return sigfd; } - void setSigfd(int sigfd_) { sigfd = sigfd_; } - virtual pid_t reap() + PidPred(pid_t pid_) : pid(pid_) {} + bool operator()(const shared_ptr& parser) const { - pid_t pid; - int status; - if ((pid = waitpid(-1, &status, WNOHANG)) == -1) - { - std::perror("waitpid"); - return -1; - } - else - { - return pid; - } + return parser->getPid() == pid; } - bool ioCallback(Glib::IOCondition ioCondition) - { - if ((ioCondition & Glib::IO_IN) == 0) - return true; - char buf; - - if (read(sigfd, &buf, 1) <= 0) - return true; - reap(); - return true; - } -private: - int sigfd; + pid_t pid; }; // Depending on how args are passed, either launch stap directly or @@ -106,14 +125,18 @@ private: class StapLauncher : public ChildDeathReader { public: - StapLauncher() : _argv(0), _childPid(-1) {} + StapLauncher() : _argv(0), _childPid(-1) + { + childDiedSignal().connect(sigc::mem_fun(*this, &StapLauncher::onChildDied)); + } StapLauncher(char** argv) : _argv(argv), _childPid(-1) { + childDiedSignal().connect(sigc::mem_fun(*this, &StapLauncher::onChildDied)); } StapLauncher(const string& stapArgs, const string& script, const string& scriptArgs) - : _childPid(-1), _win(0), _widget(0) + : _childPid(-1) { setArgs(stapArgs, script, scriptArgs); } @@ -150,13 +173,7 @@ public: _scriptArgs.clear(); } - void setWinParams(Gtk::Window* win, GraphWidget* widget) - { - _win = win; - _widget = widget; - } int launch(); - void cleanUp(); shared_ptr makeStapParser() { shared_ptr result(new StapParser); @@ -164,25 +181,11 @@ public: parserListChangedSignal().emit(); return result; } -private: - struct pidPred - { - pidPred(pid_t pid_) : pid(pid_) {} - bool operator()(const shared_ptr& parser) const - { - return parser->getPid() == pid; - } - pid_t pid; - }; public: - pid_t reap() + void onChildDied(pid_t pid) { - using namespace boost; - pid_t pid = ChildDeathReader::reap(); - if (pid < 0) - return pid; ParserList::iterator itr - = find_if(parsers.begin(), parsers.end(), pidPred(pid)); + = find_if(parsers.begin(), parsers.end(), PidPred(pid)); if (itr != parsers.end()) { tr1::shared_ptr sp = (*itr)->getProcess(); @@ -192,12 +195,12 @@ public: parserListChangedSignal().emit(); } } - childDiedSignal().emit(pid); - return pid; } void killAll() { - for (ParserList::iterator itr = parsers.begin(), end = parsers.end(); + ParserList parsersCopy(parsers.begin(), parsers.end()); + for (ParserList::iterator itr = parsersCopy.begin(), + end = parsersCopy.end(); itr != end; ++itr) { @@ -211,8 +214,6 @@ protected: string _script; string _scriptArgs; int _childPid; - Gtk::Window* _win; - GraphWidget* _widget; }; int StapLauncher::launch() @@ -296,43 +297,6 @@ int StapLauncher::launch() return childPid; } -void StapLauncher::cleanUp() -{ - struct sigaction action; - action.sa_handler = SIG_DFL; - sigemptyset(&action.sa_mask); - action.sa_flags = 0; - sigaction(SIGCLD, &action, 0); - // Drain any outstanding signals - close(signalPipe[1]); - char buf; - while (read(signalPipe[0], &buf, 1) > 0) - reap(); - for (ParserList::iterator itr = parsers.begin(), end = parsers.end(); - itr != end; - ++itr) - { - pid_t childPid = (*itr)->getPid(); - if (childPid > 0) - kill(childPid, SIGTERM); - int status; - pid_t killedPid = -1; - if ((killedPid = wait(&status)) == -1) - { - std::perror("wait"); - } - else if (killedPid != childPid) - { - std::cerr << "wait: killed Pid " << killedPid << " != child Pid " - << childPid << "\n"; - } - else - { - childDiedSignal().emit(childPid); - } - } -} - class GraphicalStapLauncher : public StapLauncher { public: @@ -485,7 +449,7 @@ void ProcWindow::onKill() kill(proc->pid, SIGTERM); } -class GrapherWindow : public Gtk::Window, public ChildDeathReader::Callback +class GrapherWindow : public Gtk::Window { public: GrapherWindow(); @@ -503,21 +467,24 @@ protected: virtual void on_menu_script_start(); virtual void on_menu_proc_window(); void addGraph(); + void onParserListChanged(); // menu support Glib::RefPtr m_refUIManager; Glib::RefPtr m_refActionGroup; GraphicalStapLauncher* _graphicalLauncher; shared_ptr _procWindow; + bool _quitting; }; GrapherWindow::GrapherWindow() - : _procWindow(new ProcWindow) + : _procWindow(new ProcWindow), _quitting(false) { set_title("systemtap grapher"); add(m_Box); - + parserListChangedSignal() + .connect(sigc::mem_fun(*this, &GrapherWindow::onParserListChanged)); //Create actions for menus and toolbars: m_refActionGroup = Gtk::ActionGroup::create(); //File menu: @@ -575,7 +542,13 @@ GrapherWindow::GrapherWindow() void GrapherWindow::on_menu_file_quit() { - hide(); + using namespace boost; + _quitting = true; + if (find_if(parsers.begin(), parsers.end(), !bind(PidPred(-1), _1)) + != parsers.end()) + _graphicalLauncher->killAll(); + else + hide(); } void GrapherWindow::on_menu_script_start() @@ -589,6 +562,15 @@ void GrapherWindow::on_menu_proc_window() _procWindow->show(); } +void GrapherWindow::onParserListChanged() +{ + using namespace boost; + if (_quitting + && (find_if(parsers.begin(), parsers.end(), !bind(PidPred(-1), _1)) + == parsers.end())) + hide(); +} + int main(int argc, char** argv) { Gtk::Main app(argc, argv); @@ -597,7 +579,6 @@ int main(int argc, char** argv) win.set_title("Grapher"); win.set_default_size(600, 250); - launcher.setWinParams(&win, &win.w); win.setGraphicalLauncher(&launcher); @@ -616,7 +597,6 @@ int main(int argc, char** argv) launcher.launch(); } Gtk::Main::run(win); - launcher.cleanUp(); return 0; } -- cgit From b35632fd3d2547414c80023c5c60c847e3dc92ea Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Thu, 10 Dec 2009 15:11:08 +0100 Subject: grapher: more implementation stap process window The process arguments are displayed, and the "kill" button is insensitive when a process is dead or nothing is selected. * grapher/grapher.cxx (ProcWindow _killButton, _restartButton, _stapArgsLabel, _scriptArgsLabel): new members (ProcWindow::ProcWindow): hook 'em up (ProcWindow::onSelectionChanged): Enable / disable kill button and display process arguments. * grapher/processwindow.glade: Replace bizzare handle window with a horizontal pane. Tweak various widget sizes. --- grapher/grapher.cxx | 38 +++++- grapher/processwindow.glade | 290 +++++++++++++++++++++----------------------- 2 files changed, 173 insertions(+), 155 deletions(-) (limited to 'grapher') diff --git a/grapher/grapher.cxx b/grapher/grapher.cxx index 8e7a4dba..e3d5289a 100644 --- a/grapher/grapher.cxx +++ b/grapher/grapher.cxx @@ -335,6 +335,10 @@ public: Glib::RefPtr _xml; Gtk::Window* _window; Gtk::TreeView* _dataTreeView; + Gtk::Button* _killButton; + Gtk::Button* _restartButton; + Gtk::Label* _stapArgsLabel; + Gtk::Label* _scriptArgsLabel; Glib::RefPtr _listStore; Glib::RefPtr _listSelection; void onClose(); @@ -376,15 +380,19 @@ ProcWindow::ProcWindow() _xml->get_widget("button5", button); button->signal_clicked().connect(sigc::mem_fun(*this, &ProcWindow::onClose), false); - _xml->get_widget("button1", button); - button->signal_clicked().connect(sigc::mem_fun(*this, &ProcWindow::onKill), - false); + _xml->get_widget("button1", _killButton); + _killButton->signal_clicked() + .connect(sigc::mem_fun(*this, &ProcWindow::onKill), false); + _killButton->set_sensitive(false); + _xml->get_widget("button2", _restartButton); + _restartButton->set_sensitive(false); parserListChangedSignal() .connect(sigc::mem_fun(*this, &ProcWindow::onParserListChanged)); _listSelection = _dataTreeView->get_selection(); _listSelection->signal_changed() .connect(sigc::mem_fun(*this, &ProcWindow::onSelectionChanged)); - + _xml->get_widget("label7", _stapArgsLabel); + _xml->get_widget("label8", _scriptArgsLabel); } void ProcWindow::onClose() @@ -436,6 +444,28 @@ void ProcWindow::onParserListChanged() void ProcWindow::onSelectionChanged() { + Gtk::TreeModel::iterator itr = _listSelection->get_selected(); + shared_ptr proc; + if (itr) + { + Gtk::TreeModel::Row row = *itr; + proc = row[_modelColumns._proc]; + } + if (proc) + { + if (proc->pid >= 0) + _killButton->set_sensitive(true); + else + _killButton->set_sensitive(false); + _stapArgsLabel->set_text(proc->stapArgs); + _scriptArgsLabel->set_text(proc->scriptArgs); + } + else + { + _killButton->set_sensitive(false); + _stapArgsLabel->set_text(""); + _scriptArgsLabel->set_text(""); + } } void ProcWindow::onKill() diff --git a/grapher/processwindow.glade b/grapher/processwindow.glade index 3886cb34..a598fba6 100644 --- a/grapher/processwindow.glade +++ b/grapher/processwindow.glade @@ -212,10 +212,9 @@ - + True - False - 0 + True @@ -228,7 +227,7 @@ - 300 + 250 True True True @@ -242,81 +241,41 @@ - 0 - True - True + True + False - - 26 + + 124 True - GTK_SHADOW_OUT - GTK_POS_LEFT - GTK_POS_TOP + False + 0 - + True False 0 - + True - False - 0 - - - - True - stap arguments: - False - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - False - False - - - - - - True - - False - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - False - False - - + stap arguments: + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 0 @@ -326,111 +285,140 @@ - + True - False - 0 + + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 + + + 0 + False + False + + + + + 0 + False + False + + - - - True - script arguments: - False - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - False - False - - + + + True + False + 0 - - - True - - False - False - GTK_JUSTIFY_LEFT - False - False - 0.5 - 0.5 - 0 - 0 - PANGO_ELLIPSIZE_NONE - -1 - False - 0 - - - 0 - False - False - - + + + True + script arguments: + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 0 - True - True + False + False - + True - True - GTK_POLICY_ALWAYS - GTK_POLICY_ALWAYS - GTK_SHADOW_IN - GTK_CORNER_TOP_LEFT - - - - 20 - True - True - True - False - True - GTK_JUSTIFY_LEFT - GTK_WRAP_NONE - True - 0 - 0 - 0 - 0 - 0 - 0 - - - + + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + PANGO_ELLIPSIZE_NONE + -1 + False + 0 0 - True - True + False + False + + 0 + True + True + + + + + + True + True + GTK_POLICY_ALWAYS + GTK_POLICY_ALWAYS + GTK_SHADOW_IN + GTK_CORNER_TOP_LEFT + + + + 20 + True + True + True + False + True + GTK_JUSTIFY_LEFT + GTK_WRAP_NONE + True + 0 + 0 + 0 + 0 + 0 + 0 + + + + + + 0 + True + True + - 0 - True - True + True + True -- cgit From 1b9fad80af5504ef03c2a88504dbc47bea003721 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Thu, 10 Dec 2009 21:34:27 +0100 Subject: grapher: integrate graph events from stdin with the stap process framework. This was the original way to do graphing and had bitrotted some. * grapher/StapParser.cxx (initIO): new catchHUP argument (ioCallback): Only disconnect signals, etc. on IN_HUP if catchHUP is true. (errIoCallback): Write error messages to stderr, not stdout. * grapher/grapher.cxx (StapLauncher::launch): Don't catchHUP on our stap process children. (ProcWindow::refresh): Display something reasonable for the stap "process" that is feeding stdin. (main): Use StapParser::initIO to initialize parser reading from stdin. --- grapher/StapParser.cxx | 31 ++++++++++++++++++++----------- grapher/StapParser.hxx | 5 +++-- grapher/grapher.cxx | 17 +++++++++-------- 3 files changed, 32 insertions(+), 21 deletions(-) (limited to 'grapher') diff --git a/grapher/StapParser.cxx b/grapher/StapParser.cxx index a9a5109a..2513680b 100644 --- a/grapher/StapParser.cxx +++ b/grapher/StapParser.cxx @@ -86,9 +86,12 @@ vector commaSplit(const boost::sub_range& range) using namespace boost; if (ioCondition & Glib::IO_HUP) { - childDiedSignal().emit(getPid()); - _ioConnection.disconnect(); - _errIoConnection.disconnect(); + if (_catchHUP) + { + childDiedSignal().emit(getPid()); + _ioConnection.disconnect(); + _errIoConnection.disconnect(); + } return true; } if ((ioCondition & Glib::IO_IN) == 0) @@ -261,22 +264,28 @@ vector commaSplit(const boost::sub_range& range) cerr << "StapParser: error reading from stderr!\n"; return true; } - if (write(STDOUT_FILENO, buf, bytes_read) < 0) + if (write(STDERR_FILENO, buf, bytes_read) < 0) ; return true; } - void StapParser::initIo(int inFd, int errFd) + void StapParser::initIo(int inFd, int errFd, bool catchHUP) { _inFd = inFd; _errFd = errFd; + _catchHUP = catchHUP; + Glib::IOCondition inCond = Glib::IO_IN; + if (catchHUP) + inCond |= Glib::IO_HUP; + if (_errFd >= 0) + { + _errIoConnection = Glib::signal_io() + .connect(sigc::mem_fun(*this, &StapParser::errIoCallback), + _errFd, Glib::IO_IN); + } _ioConnection = Glib::signal_io() - .connect(sigc::mem_fun(*this, &StapParser::errIoCallback), - _errFd, - Glib::IO_IN); - _errIoConnection = Glib::signal_io() .connect(sigc::mem_fun(*this, &StapParser::ioCallback), - _inFd, - Glib::IO_IN | Glib::IO_HUP); + _inFd, inCond); + } } diff --git a/grapher/StapParser.hxx b/grapher/StapParser.hxx index 4dd711e6..24e84fc9 100644 --- a/grapher/StapParser.hxx +++ b/grapher/StapParser.hxx @@ -37,12 +37,13 @@ namespace systemtap int _errFd; int _inFd; unsigned char _lineEndChar; + bool _catchHUP; std::tr1::shared_ptr _process; sigc::connection _ioConnection; sigc::connection _errIoConnection; public: StapParser() - : _errFd(-1), _inFd(-1), _lineEndChar('\n') + : _errFd(-1), _inFd(-1), _lineEndChar('\n'), _catchHUP(false) { } void parseData(std::tr1::shared_ptr gdata, @@ -65,7 +66,7 @@ namespace systemtap { _process = process; } - void initIo(int inFd, int errFd); + void initIo(int inFd, int errFd, bool catchHUP); }; sigc::signal& childDiedSignal(); diff --git a/grapher/grapher.cxx b/grapher/grapher.cxx index e3d5289a..6dead221 100644 --- a/grapher/grapher.cxx +++ b/grapher/grapher.cxx @@ -293,7 +293,7 @@ int StapLauncher::launch() sp->setProcess(proc); parsers.push_back(sp); parserListChangedSignal().emit(); - sp->initIo(pipefd[0], pipefd[2]); + sp->initIo(pipefd[0], pipefd[2], false); return childPid; } @@ -422,14 +422,19 @@ void ProcWindow::refresh() ++spitr) { shared_ptr sp = (*spitr)->getProcess(); + Gtk::TreeModel::iterator litr = _listStore->append(); + Gtk::TreeModel::Row row = *litr; if (sp) { - Gtk::TreeModel::iterator litr = _listStore->append(); - Gtk::TreeModel::Row row = *litr; row[_modelColumns._iconName] = sp->pid >= 0 ? "gtk-yes" : "gtk-no"; row[_modelColumns._scriptName] = sp->script; row[_modelColumns._proc] = sp; } + else + { + row[_modelColumns._iconName] = "gtk-yes"; + row[_modelColumns._scriptName] = "standard input"; + } } } @@ -615,11 +620,7 @@ int main(int argc, char** argv) if (argc == 2 && !std::strcmp(argv[1], "-")) { tr1::shared_ptr sp = launcher.makeStapParser(); - sp->setInFd(STDIN_FILENO); - Glib::signal_io().connect(sigc::mem_fun(sp.get(), - &StapParser::ioCallback), - STDIN_FILENO, - Glib::IO_IN | Glib::IO_HUP); + sp->initIo(STDIN_FILENO, -1, true); } else if (argc > 1) { -- cgit From e47f92ea31a605802c59541ca325ffd567c45ca4 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Fri, 11 Dec 2009 14:03:47 +0100 Subject: grapher: implement restarting a stap process * grapher/StapParser.cxx (StapParser::disconnect): new function * grapher/StapParser.hxx (StapProcess::StapProcess): initialize argv to 0 * grapher/grapher.cxx (StapLauncher::setArgs): Set argv to 0 (StapLauncher launch, launchUsingParser): Refactor launch(), extracting function a that (re)launches a stap process using an existing parser. (StapLauncher::onChildDied): call disconnect() on dead parser. (GrapherWindow::_graphicalLauncher, setGraphicalLauncher): delete member, replacing with... (graphicalLauncher): new variable (ProcModelColumns): Store parser object in the list model instead of just a StapProcess object. (ProcWindow::onRestart): new function (ProcWindow::refresh): Preserve the list selection when the process list is refreshed. (ProcWindow::onSelectionChanged): Manage the restart button's state. --- grapher/StapParser.cxx | 18 ++++++ grapher/StapParser.hxx | 3 +- grapher/grapher.cxx | 155 ++++++++++++++++++++++++++++++++++--------------- 3 files changed, 128 insertions(+), 48 deletions(-) (limited to 'grapher') diff --git a/grapher/StapParser.cxx b/grapher/StapParser.cxx index 2513680b..2595e8cc 100644 --- a/grapher/StapParser.cxx +++ b/grapher/StapParser.cxx @@ -288,4 +288,22 @@ vector commaSplit(const boost::sub_range& range) _inFd, inCond); } + + void StapParser::disconnect() + { + if (_ioConnection.connected()) + _ioConnection.disconnect(); + if (_errIoConnection.connected()) + _errIoConnection.disconnect(); + if (_inFd >= 0) + { + close(_inFd); + _inFd = -1; + } + if (_errFd >= 0) + { + close(_errFd); + _errFd = -1; + } + } } diff --git a/grapher/StapParser.hxx b/grapher/StapParser.hxx index 24e84fc9..169533b6 100644 --- a/grapher/StapParser.hxx +++ b/grapher/StapParser.hxx @@ -18,7 +18,7 @@ namespace systemtap // arguments and script for a stap process struct StapProcess { - StapProcess(pid_t pid_ = -1) : pid(pid_) {} + StapProcess(pid_t pid_ = -1) : argv(0), pid(pid_) {} std::string stapArgs; std::string script; std::string scriptArgs; @@ -67,6 +67,7 @@ namespace systemtap _process = process; } void initIo(int inFd, int errFd, bool catchHUP); + void disconnect(); }; sigc::signal& childDiedSignal(); diff --git a/grapher/grapher.cxx b/grapher/grapher.cxx index 6dead221..0111184a 100644 --- a/grapher/grapher.cxx +++ b/grapher/grapher.cxx @@ -153,6 +153,7 @@ public: void setArgs(const string& stapArgs, const string& script, const string& scriptArgs) { + _argv = 0; _stapArgs = stapArgs; _script = script; _scriptArgs = scriptArgs; @@ -174,6 +175,7 @@ public: } int launch(); + int launchUsingParser(shared_ptr parser); shared_ptr makeStapParser() { shared_ptr result(new StapParser); @@ -188,6 +190,7 @@ public: = find_if(parsers.begin(), parsers.end(), PidPred(pid)); if (itr != parsers.end()) { + (*itr)->disconnect(); tr1::shared_ptr sp = (*itr)->getProcess(); if (sp) { @@ -218,7 +221,36 @@ protected: int StapLauncher::launch() { - int childPid = -1; + tr1::shared_ptr sp(new StapParser); + shared_ptr proc(new StapProcess(-1)); + if (_argv) + proc->argv = _argv; + else + { + proc->stapArgs = _stapArgs; + proc->script = _script; + proc->scriptArgs = _scriptArgs; + } + sp->setProcess(proc); + pid_t childPid = launchUsingParser(sp); + if (childPid >= 0) + { + parsers.push_back(sp); + parserListChangedSignal().emit(); + } + return childPid; +} + +int StapLauncher::launchUsingParser(shared_ptr sp) +{ + shared_ptr proc = sp->getProcess(); + if (!proc) + { + cerr << "Can't launch parser with null process structure\n"; + return -1; + } + + proc->pid = -1; if (signalPipe[0] < 0) { if (pipe(&signalPipe[0]) < 0) @@ -245,11 +277,11 @@ int StapLauncher::launch() std::perror("pipe"); exit(1); } - if ((childPid = fork()) == -1) + if ((proc->pid = fork()) == -1) { exit(1); } - else if (childPid) + else if (proc->pid) { close(pipefd[1]); close(pipefd[3]); @@ -260,41 +292,28 @@ int StapLauncher::launch() dup2(pipefd[3], STDERR_FILENO); for_each(&pipefd[0], &pipefd[4], close); for_each(&signalPipe[0], &signalPipe[2], close); - if (_argv) + if (proc->argv) { char argv0[] = "stap"; - char** argvEnd = _argv; + char** argvEnd = proc->argv; for (; *argvEnd; ++argvEnd) ; - char** realArgv = new char*[argvEnd - _argv + 2]; + char** realArgv = new char*[argvEnd - proc->argv + 2]; realArgv[0] = argv0; std::copy(_argv, argvEnd + 1, &realArgv[1]); execvp("stap", realArgv); } else { - string argString = "stap" + _stapArgs + " " + _script + " " - + _scriptArgs; + string argString = "stap" + proc->stapArgs + " " + proc->script + " " + + proc->scriptArgs; execl("/bin/sh", "sh", "-c", argString.c_str(), static_cast(0)); } _exit(1); } - tr1::shared_ptr sp(new StapParser); - shared_ptr proc(new StapProcess(childPid)); - if (_argv) - proc->argv = _argv; - else - { - proc->stapArgs = _stapArgs; - proc->script = _script; - proc->scriptArgs = _scriptArgs; - } - sp->setProcess(proc); - parsers.push_back(sp); - parserListChangedSignal().emit(); sp->initIo(pipefd[0], pipefd[2], false); - return childPid; + return proc->pid; } class GraphicalStapLauncher : public StapLauncher @@ -312,6 +331,8 @@ private: Gtk::Entry* _scriptArgEntry; }; +GraphicalStapLauncher *graphicalLauncher = 0; + class ProcModelColumns : public Gtk::TreeModelColumnRecord { public: @@ -319,11 +340,11 @@ public: { add(_iconName); add(_scriptName); - add(_proc); + add(_parser); } Gtk::TreeModelColumn _iconName; Gtk::TreeModelColumn _scriptName; - Gtk::TreeModelColumn > _proc; + Gtk::TreeModelColumn > _parser; }; // This should probably be a Gtk window, with the appropriate glade magic @@ -347,6 +368,7 @@ public: void onParserListChanged(); void onSelectionChanged(); void onKill(); + void onRestart(); private: bool _open; void refresh(); @@ -385,6 +407,8 @@ ProcWindow::ProcWindow() .connect(sigc::mem_fun(*this, &ProcWindow::onKill), false); _killButton->set_sensitive(false); _xml->get_widget("button2", _restartButton); + _restartButton->signal_clicked() + .connect(sigc::mem_fun(*this, &ProcWindow::onRestart), false); _restartButton->set_sensitive(false); parserListChangedSignal() .connect(sigc::mem_fun(*this, &ProcWindow::onParserListChanged)); @@ -416,25 +440,53 @@ void ProcWindow::hide() void ProcWindow::refresh() { + // If a process is already selected, try to leave it selected after + // the list is reconstructed. + shared_ptr selectedParser; + { + Gtk::TreeModel::iterator itr = _listSelection->get_selected(); + if (itr) + { + Gtk::TreeModel::Row row = *itr; + selectedParser = row[_modelColumns._parser]; + } + } _listStore->clear(); for (ParserList::iterator spitr = parsers.begin(), end = parsers.end(); spitr != end; ++spitr) { - shared_ptr sp = (*spitr)->getProcess(); + shared_ptr parser = *spitr; + shared_ptr sp = parser->getProcess(); Gtk::TreeModel::iterator litr = _listStore->append(); Gtk::TreeModel::Row row = *litr; if (sp) { row[_modelColumns._iconName] = sp->pid >= 0 ? "gtk-yes" : "gtk-no"; row[_modelColumns._scriptName] = sp->script; - row[_modelColumns._proc] = sp; } else { row[_modelColumns._iconName] = "gtk-yes"; row[_modelColumns._scriptName] = "standard input"; } + row[_modelColumns._parser] = parser; + } + if (selectedParser) + { + Gtk::TreeModel::Children children = _listStore->children(); + for (Gtk::TreeModel::Children::const_iterator itr = children.begin(), + end = children.end(); + itr != end; + ++itr) + { + Gtk::TreeModel::Row row = *itr; + if (row[_modelColumns._parser] == selectedParser) + { + _listSelection->select(row); + break; + } + } } } @@ -450,24 +502,26 @@ void ProcWindow::onParserListChanged() void ProcWindow::onSelectionChanged() { Gtk::TreeModel::iterator itr = _listSelection->get_selected(); + shared_ptr parser; shared_ptr proc; if (itr) { Gtk::TreeModel::Row row = *itr; - proc = row[_modelColumns._proc]; + parser = row[_modelColumns._parser]; + proc = parser->getProcess(); } if (proc) { - if (proc->pid >= 0) - _killButton->set_sensitive(true); - else - _killButton->set_sensitive(false); + bool procRunning = proc->pid >= 0; + _killButton->set_sensitive(procRunning); + _restartButton->set_sensitive(!procRunning); _stapArgsLabel->set_text(proc->stapArgs); _scriptArgsLabel->set_text(proc->scriptArgs); } else { _killButton->set_sensitive(false); + _restartButton->set_sensitive(false); _stapArgsLabel->set_text(""); _scriptArgsLabel->set_text(""); } @@ -479,11 +533,26 @@ void ProcWindow::onKill() if (!itr) return; Gtk::TreeModel::Row row = *itr; - shared_ptr proc = row[_modelColumns._proc]; - if (proc->pid >= 0) + shared_ptr parser = row[_modelColumns._parser]; + shared_ptr proc = parser->getProcess(); + if (proc && proc->pid >= 0) kill(proc->pid, SIGTERM); } +void ProcWindow::onRestart() +{ + Gtk::TreeModel::iterator itr = _listSelection->get_selected(); + if (!itr) + return; + Gtk::TreeModel::Row row = *itr; + shared_ptr parser = row[_modelColumns._parser]; + shared_ptr proc = parser->getProcess(); + if (!proc) + return; + if (graphicalLauncher->launchUsingParser(parser) > 0) + parserListChangedSignal().emit(); +} + class GrapherWindow : public Gtk::Window { public: @@ -492,11 +561,6 @@ public: Gtk::VBox m_Box; Gtk::ScrolledWindow scrolled; GraphWidget w; - void setGraphicalLauncher(GraphicalStapLauncher* launcher) - { - _graphicalLauncher = launcher; - } - GraphicalStapLauncher* getGraphicalLauncher() { return _graphicalLauncher; } protected: virtual void on_menu_file_quit(); virtual void on_menu_script_start(); @@ -506,7 +570,6 @@ protected: // menu support Glib::RefPtr m_refUIManager; Glib::RefPtr m_refActionGroup; - GraphicalStapLauncher* _graphicalLauncher; shared_ptr _procWindow; bool _quitting; }; @@ -581,14 +644,14 @@ void GrapherWindow::on_menu_file_quit() _quitting = true; if (find_if(parsers.begin(), parsers.end(), !bind(PidPred(-1), _1)) != parsers.end()) - _graphicalLauncher->killAll(); + graphicalLauncher->killAll(); else hide(); } void GrapherWindow::on_menu_script_start() { - _graphicalLauncher->runDialog(); + graphicalLauncher->runDialog(); } @@ -609,23 +672,21 @@ void GrapherWindow::onParserListChanged() int main(int argc, char** argv) { Gtk::Main app(argc, argv); - GraphicalStapLauncher launcher; + graphicalLauncher = new GraphicalStapLauncher; GrapherWindow win; win.set_title("Grapher"); win.set_default_size(600, 250); - win.setGraphicalLauncher(&launcher); - if (argc == 2 && !std::strcmp(argv[1], "-")) { - tr1::shared_ptr sp = launcher.makeStapParser(); + tr1::shared_ptr sp = graphicalLauncher->makeStapParser(); sp->initIo(STDIN_FILENO, -1, true); } else if (argc > 1) { - launcher.setArgv(argv + 1); - launcher.launch(); + graphicalLauncher->setArgv(argv + 1); + graphicalLauncher->launch(); } Gtk::Main::run(win); return 0; -- cgit