diff options
author | Dave Brolley <brolley@redhat.com> | 2009-12-14 16:39:01 -0500 |
---|---|---|
committer | Dave Brolley <brolley@redhat.com> | 2009-12-14 16:39:01 -0500 |
commit | b8f1753c091d3f75ea4a71bfb709d8e50780d3fb (patch) | |
tree | ea5e83a0556d4df262a2b4f9b26065dc312a8a1e /grapher | |
parent | 61b21980212779b5a35f6a196842bdca55a3ced6 (diff) | |
parent | 958c58e8231563e9349e4d8ea56c04c25e1501c0 (diff) | |
download | systemtap-steved-b8f1753c091d3f75ea4a71bfb709d8e50780d3fb.tar.gz systemtap-steved-b8f1753c091d3f75ea4a71bfb709d8e50780d3fb.tar.xz systemtap-steved-b8f1753c091d3f75ea4a71bfb709d8e50780d3fb.zip |
Merge branch 'master' of ssh://sources.redhat.com/git/systemtap
Diffstat (limited to 'grapher')
-rw-r--r-- | grapher/CairoWidget.cxx | 50 | ||||
-rw-r--r-- | grapher/CairoWidget.hxx | 8 | ||||
-rw-r--r-- | grapher/Graph.cxx | 28 | ||||
-rw-r--r-- | grapher/Graph.hxx | 15 | ||||
-rw-r--r-- | grapher/GraphData.hxx | 22 | ||||
-rw-r--r-- | grapher/GraphStyle.cxx | 58 | ||||
-rw-r--r-- | grapher/GraphStyle.hxx | 12 | ||||
-rw-r--r-- | grapher/GraphWidget.cxx | 163 | ||||
-rw-r--r-- | grapher/GraphWidget.hxx | 33 | ||||
-rw-r--r-- | grapher/Makefile.am | 2 | ||||
-rw-r--r-- | grapher/Makefile.in | 5 | ||||
-rw-r--r-- | grapher/StapParser.cxx | 370 | ||||
-rw-r--r-- | grapher/StapParser.hxx | 97 | ||||
-rw-r--r-- | grapher/graph-dialog.glade | 117 | ||||
-rw-r--r-- | grapher/grapher.cxx | 596 | ||||
-rw-r--r-- | grapher/processwindow.glade | 460 | ||||
-rw-r--r-- | grapher/processwindow.gladep | 8 |
17 files changed, 1554 insertions, 490 deletions
diff --git a/grapher/CairoWidget.cxx b/grapher/CairoWidget.cxx index f627dfaa..26c2d029 100644 --- a/grapher/CairoWidget.cxx +++ b/grapher/CairoWidget.cxx @@ -1,9 +1,23 @@ +// 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 <math.h> +#include <vector> + +#include <boost/algorithm/string.hpp> namespace systemtap { + using namespace std; + using namespace boost; + void CairoPlayButton::draw(Cairo::RefPtr<Cairo::Context> cr) { if (!_visible) @@ -45,9 +59,28 @@ namespace systemtap if (!_visible) return; cr->save(); - Cairo::TextExtents extents; - cr->get_text_extents(contents, extents); - double width = extents.width, height = extents.height; + 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<string> lines; + split(lines, contents, is_any_of("\n")); + vector<Cairo::TextExtents> extents; + double width = 0.0, height = 0.0; + for (vector<string>::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 += fontHeight; + } 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 +89,15 @@ 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); + double texty = _y0; + for (vector<string>::iterator itr = lines.begin(), end = lines.end(); + itr != end; + ++itr) + { + cr->move_to(_x0, texty + fontExtent.ascent); + cr->show_text(*itr); + texty += fontHeight; + } cr->restore(); } } 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 55ffcdf2..ea1bd091 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 <sstream> @@ -8,13 +16,16 @@ namespace systemtap { using namespace std; using namespace std::tr1; + + GraphDataList GraphDataBase::graphData; + sigc::signal<void> GraphDataBase::graphDataChanged; Graph::Graph(double x, double y) : _width(600), _height(200), _graphX(0), _graphY(0), _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); @@ -32,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) @@ -46,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) @@ -84,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) { @@ -150,7 +161,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? @@ -205,4 +216,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 - _graphY) + _yOffset + _graphHeight; + } } diff --git a/grapher/Graph.hxx b/grapher/Graph.hxx index b9efb2a2..c928fec2 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 @@ -11,7 +19,6 @@ namespace systemtap class Graph : public CairoWidget { public: - typedef std::vector<std::tr1::shared_ptr<GraphDataBase> > DatasetList; friend class GraphWidget; Graph(double x = 0.0, double y = 0.0); virtual void draw(Cairo::RefPtr<Cairo::Context> cr); @@ -39,10 +46,12 @@ namespace systemtap double _xOffset; double _yOffset; std::tr1::shared_ptr<CairoPlayButton> _playButton; - DatasetList& getDatasets() { return _datasets; } + int64_t _timeBase; + 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 04f415f3..fb1ca9f5 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 @@ -11,10 +19,14 @@ #include <boost/circular_buffer.hpp> +#include <gtkmm.h> + #include "GraphStyle.hxx" namespace systemtap { + struct GraphDataBase; + typedef std::vector<std::tr1::shared_ptr<GraphDataBase> > GraphDataList; struct GraphDataBase { virtual ~GraphDataBase() {} @@ -35,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<void> graphDataChanged; }; template<typename T> @@ -61,5 +76,12 @@ namespace systemtap Element; std::vector<Element> elements; }; + + inline GraphDataList& getGraphData() { return GraphDataBase::graphData; } + + inline sigc::signal<void>& graphDataSignal() + { + return GraphDataBase::graphDataChanged; + } } #endif diff --git a/grapher/GraphStyle.cxx b/grapher/GraphStyle.cxx index 55fc73f4..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" @@ -50,7 +58,7 @@ namespace systemtap { shared_ptr<GraphData<double> > realData = dynamic_pointer_cast<GraphData<double> >(graphData); - if (!realData) + if (!realData || graphData->times.empty()) return -1; int64_t left, right; double top, bottom; @@ -58,6 +66,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; @@ -132,20 +142,11 @@ 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 +154,38 @@ namespace systemtap cr->restore(); } } - + + ssize_t GraphStyleEvent::dataIndexAtPoint(double x, double y, + shared_ptr<GraphDataBase> graphData, + shared_ptr<Graph> graph) + { + shared_ptr<GraphData<string> > stringData + = dynamic_pointer_cast<GraphData<string> >(graphData); + if (!stringData || graphData->times.empty()) + return -1; + int64_t left, right; + double top, bottom; + graph->getExtents(left, right, top, bottom); + double horizScale = (graph->_zoomFactor * graph->_graphWidth + / static_cast<double>(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<ssize_t>(distance(lower, ditr)); + } + return -1; + } } diff --git a/grapher/GraphStyle.hxx b/grapher/GraphStyle.hxx index 9625f451..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 <tr1/memory> @@ -48,6 +56,10 @@ namespace systemtap public: void draw(std::tr1::shared_ptr<GraphDataBase> graphData, Graph* graph, Cairo::RefPtr<Cairo::Context> cr); + virtual ssize_t dataIndexAtPoint(double x, double y, + std::tr1::shared_ptr<GraphDataBase> + graphData, + std::tr1::shared_ptr<Graph> graph); static GraphStyleEvent instance; }; } diff --git a/grapher/GraphWidget.cxx b/grapher/GraphWidget.cxx index 9067988a..bdf60ed2 100644 --- a/grapher/GraphWidget.cxx +++ b/grapher/GraphWidget.cxx @@ -1,9 +1,20 @@ +// 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 <algorithm> #include <ctime> #include <iterator> #include <math.h> #include <iostream> +#define __STDC_LIMIT_MACROS +#include <stdint.h> + #include <glibmm/timer.h> #include <cairomm/context.h> #include <libglademm.h> @@ -22,7 +33,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); @@ -49,22 +60,35 @@ 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); - _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); + // Disable selection in list + Glib::RefPtr<Gtk::TreeSelection> 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, + &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 ) { @@ -77,10 +101,32 @@ namespace systemtap { } - void GraphWidget::addGraphData(shared_ptr<GraphDataBase> data) + void GraphWidget::onGraphDataChanged() { - _graphs[0]->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() @@ -95,6 +141,7 @@ namespace systemtap shared_ptr<Graph> graph(new Graph(x, y)); _height = y + graph->_height; graph->setOrigin(x, y); + graph->_timeBase = _globalTimeBase; _graphs.push_back(graph); queue_resize(); } @@ -110,8 +157,30 @@ namespace systemtap cr->save(); cr->set_source_rgba(0.0, 0.0, 0.0, 1.0); cr->paint(); + if (!_timeBaseInitialized && !getGraphData().empty()) + { + GraphDataList& graphData = getGraphData(); + 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(); @@ -121,6 +190,7 @@ namespace systemtap } if (_hoverText && _hoverText->isVisible()) _hoverText->draw(cr); + cr->restore(); return true; } @@ -236,36 +306,34 @@ namespace systemtap _dataDialog->hide(); } - void GraphWidget::onDataAdd() - { - Glib::RefPtr<Gtk::TreeSelection> treeSelection = - _dataTreeView->get_selection(); - Gtk::TreeModel::iterator iter = treeSelection->get_selected(); - if (iter) - { - Gtk::TreeModel::Row row = *iter; - shared_ptr<GraphDataBase> data = row[_dataColumns._graphData]; - _activeGraph->addGraphData(data); - } - } - - void GraphWidget::onDataRemove() - { - } - 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) { Gtk::TreeModel::iterator litr = _listStore->append(); Gtk::TreeModel::Row row = *litr; - row[_dataColumns._dataName] = (*itr)->title; + row[_dataColumns._dataName] = (*itr)->name; + if (!(*itr)->title.empty()) + row[_dataColumns._dataTitle] = (*itr)->title; row[_dataColumns._graphData] = *itr; + GraphDataList& gsets = _activeGraph->getDatasets(); + GraphDataList::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() @@ -275,9 +343,9 @@ namespace systemtap { if (!_hoverText) _hoverText = shared_ptr<CairoTextBox>(new CairoTextBox()); - _hoverText->setOrigin(_mouseX + 5, _mouseY - 5); - Graph::DatasetList& dataSets = g->getDatasets(); - for (Graph::DatasetList::reverse_iterator ritr = dataSets.rbegin(), + _hoverText->setOrigin(_mouseX + 10, _mouseY - 5); + GraphDataList& dataSets = g->getDatasets(); + for (GraphDataList::reverse_iterator ritr = dataSets.rbegin(), end = dataSets.rend(); ritr != end; ++ritr) @@ -317,4 +385,29 @@ 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(); + } + + void GraphWidget::onRowChanged(const Gtk::TreeModel::Path&, + const Gtk::TreeModel::iterator& litr) + { + Gtk::TreeModel::Row row = *litr; + bool val = row[_dataColumns._dataEnabled]; + shared_ptr<GraphDataBase> data = row[_dataColumns._graphData]; + GraphDataList& 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 61d50e70..89b86db9 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 @@ -18,10 +26,14 @@ namespace systemtap public: DataModelColumns() { + add(_dataEnabled); add(_dataName); + add(_dataTitle); add(_graphData); } + Gtk::TreeModelColumn<bool> _dataEnabled; Gtk::TreeModelColumn<Glib::ustring> _dataName; + Gtk::TreeModelColumn<Glib::ustring> _dataTitle; Gtk::TreeModelColumn<std::tr1::shared_ptr<GraphDataBase> > _graphData; }; @@ -30,14 +42,11 @@ namespace systemtap public: GraphWidget(); virtual ~GraphWidget(); - void addGraphData(std::tr1::shared_ptr<GraphDataBase> data); void addGraph(); protected: typedef std::vector<std::tr1::shared_ptr<Graph> > GraphList; GraphList _graphs; - typedef std::vector<std::tr1::shared_ptr<GraphDataBase> > GraphDataList; - GraphDataList _graphData; // For click and drag std::tr1::shared_ptr<Graph> _activeGraph; // Dragging all graphs simultaneously, or perhaps seperately @@ -62,9 +71,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<Gtk::ListStore> _listStore; @@ -72,8 +80,23 @@ namespace systemtap std::tr1::shared_ptr<CairoTextBox> _hoverText; double _mouseX; double _mouseY; + int64_t _globalTimeBase; + bool _timeBaseInitialized; std::tr1::shared_ptr<Graph> getGraphUnderPoint(double x, double y); void establishHoverTimeout(); + Gtk::CheckButton* _relativeTimesButton; + bool _displayRelativeTimes; + void onRelativeTimesButtonClicked(); + void onRowChanged(const Gtk::TreeModel::Path&, + const Gtk::TreeModel::iterator&); + sigc::connection _listConnection; + bool no_select_fun(const Glib::RefPtr<Gtk::TreeModel>& model, + const Gtk::TreeModel::Path& path, + bool) + { + return false; + } + void onGraphDataChanged(); }; } #endif // SYSTEMTAP_GRAPHWIDGET_H 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 6218e229..2595e8cc 100644 --- a/grapher/StapParser.cxx +++ b/grapher/StapParser.cxx @@ -1,9 +1,18 @@ +// 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 <unistd.h> #include <gtkmm/window.h> #include <algorithm> +#include <iomanip> #include <iostream> #include <sstream> #include <cstring> @@ -16,6 +25,20 @@ namespace systemtap using namespace std; using namespace std::tr1; + sigc::signal<void, pid_t>& childDiedSignal() + { + static sigc::signal<void, pid_t> deathSignal; + return deathSignal; + } + + ParserList parsers; + + sigc::signal<void>& parserListChangedSignal() + { + static sigc::signal<void> listChangedSignal; + return listChangedSignal; + } + vector<string> commaSplit(const boost::sub_range<Glib::ustring>& range) { using namespace boost; @@ -57,159 +80,176 @@ vector<string> commaSplit(const boost::sub_range<Glib::ustring>& range) } bool StapParser::ioCallback(Glib::IOCondition ioCondition) - { - using namespace std; - using std::tr1::shared_ptr; - using namespace boost; - if (ioCondition & Glib::IO_HUP) - { - _win->hide(); - return true; - } - if ((ioCondition & Glib::IO_IN) == 0) + { + using namespace std; + using std::tr1::shared_ptr; + using namespace boost; + if (ioCondition & Glib::IO_HUP) + { + if (_catchHUP) + { + 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) - { - _win->hide(); - return true; - } - buf[bytes_read] = '\0'; - _buffer += buf; - string::size_type ret = string::npos; - while ((ret = _buffer.find('\n')) != 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. - sub_range<Glib::ustring> 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<GraphData<double> > - dataSet(new GraphData<double>); - 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)); - _widget->addGraphData(dataSet); - } - else if (style == "discreet") - { - std::tr1::shared_ptr<GraphData<string> > - dataSet(new GraphData<string>); - 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)); - _widget->addGraphData(dataSet); - } - } - else if ((found = find_first(dataString, "%CSV:"))) - { - vector<string> tokens - = commaSplit(sub_range<Glib::ustring>(found.end(), - dataString.end())); - for (vector<string>::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 - { - 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<GraphDataBase> 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<Glib::ustring> 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<GraphData<double> > + dataSet(new GraphData<double>); + 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<GraphData<string> > + dataSet(new GraphData<string>); + 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<string> tokens + = commaSplit(sub_range<Glib::ustring>(found.end(), + dataString.end())); + for (vector<string>::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<char>(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<GraphDataBase> 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<string> tokens = commaSplit(dataString); - int i = 0; - int64_t time; - vector<string>::iterator tokIter = tokens.begin(); - std::istringstream timeStream(*tokIter++); - timeStream >> time; - for (vector<string>::iterator e = tokens.end(); - tokIter != e; - ++tokIter, ++i) + vector<string> tokens = commaSplit(dataString); + int i = 0; + int64_t time; + vector<string>::iterator tokIter = tokens.begin(); + std::istringstream timeStream(*tokIter++); + timeStream >> time; + for (vector<string>::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; - string data; - stream >> time >> data; - parseData(itr->second, time, data); + 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) { @@ -221,11 +261,49 @@ vector<string> commaSplit(const boost::sub_range<Glib::ustring>& 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) + if (write(STDERR_FILENO, buf, bytes_read) < 0) ; return true; } + + 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::ioCallback), + _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 a77ad1bc..169533b6 100644 --- a/grapher/StapParser.hxx +++ b/grapher/StapParser.hxx @@ -1,32 +1,79 @@ +// 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" #include <string> +#include <tr1/memory> + +#include <unistd.h> + namespace systemtap { -class StapParser -{ - std::string _buffer; - typedef std::map<std::string, std::tr1::shared_ptr<GraphDataBase> > DataMap; - DataMap _dataSets; - CSVData _csv; - Gtk::Window* _win; - GraphWidget* _widget; - int _errFd; - int _inFd; -public: - StapParser(Gtk::Window* win, - GraphWidget* widget) : _win(win), _widget(widget), _errFd(-1), - _inFd(-1) + // arguments and script for a stap process + struct StapProcess { - } - void parseData(std::tr1::shared_ptr<GraphDataBase> 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) : argv(0), 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<std::string, std::tr1::shared_ptr<GraphDataBase> > DataMap; + DataMap _dataSets; + CSVData _csv; + int _errFd; + int _inFd; + unsigned char _lineEndChar; + bool _catchHUP; + std::tr1::shared_ptr<StapProcess> _process; + sigc::connection _ioConnection; + sigc::connection _errIoConnection; + public: + StapParser() + : _errFd(-1), _inFd(-1), _lineEndChar('\n'), _catchHUP(false) + { + } + void parseData(std::tr1::shared_ptr<GraphDataBase> 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<StapProcess> getProcess() { return _process; } + void setProcess(std::tr1::shared_ptr<StapProcess> process) + { + _process = process; + } + void initIo(int inFd, int errFd, bool catchHUP); + void disconnect(); + }; + + sigc::signal<void, pid_t>& childDiedSignal(); + + typedef std::vector<std::tr1::shared_ptr<StapParser> > ParserList; + extern ParserList parsers; + + sigc::signal<void>& parserListChangedSignal(); } diff --git a/grapher/graph-dialog.glade b/grapher/graph-dialog.glade index cca2e0e3..8076d2b0 100644 --- a/grapher/graph-dialog.glade +++ b/grapher/graph-dialog.glade @@ -31,29 +31,15 @@ <property name="layout_style">GTK_BUTTONBOX_END</property> <child> - <widget class="GtkButton" id="cancelbutton1"> + <widget class="GtkButton" id="closebutton1"> <property name="visible">True</property> <property name="can_default">True</property> <property name="can_focus">True</property> - <property name="label">gtk-cancel</property> + <property name="label">gtk-close</property> <property name="use_stock">True</property> <property name="relief">GTK_RELIEF_NORMAL</property> <property name="focus_on_click">True</property> - <property name="response_id">-6</property> - <signal name="clicked" handler="on_cancelbutton1_clicked" last_modification_time="Fri, 07 Aug 2009 10:05:21 GMT"/> - </widget> - </child> - - <child> - <widget class="GtkButton" id="okbutton1"> - <property name="visible">True</property> - <property name="can_default">True</property> - <property name="can_focus">True</property> - <property name="label">gtk-ok</property> - <property name="use_stock">True</property> - <property name="relief">GTK_RELIEF_NORMAL</property> - <property name="focus_on_click">True</property> - <property name="response_id">-5</property> + <property name="response_id">-7</property> <signal name="clicked" handler="on_okbutton1_clicked" last_modification_time="Fri, 07 Aug 2009 10:05:04 GMT"/> </widget> </child> @@ -67,6 +53,31 @@ </child> <child> + <widget class="GtkLabel" id="label1"> + <property name="visible">True</property> + <property name="label" translatable="yes">Data sets</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> <widget class="GtkHBox" id="hbox1"> <property name="visible">True</property> <property name="homogeneous">False</property> @@ -83,6 +94,8 @@ <child> <widget class="GtkTreeView" id="treeview1"> + <property name="width_request">200</property> + <property name="height_request">100</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="headers_visible">False</property> @@ -101,57 +114,6 @@ <property name="fill">True</property> </packing> </child> - - <child> - <widget class="GtkVBox" id="vbox1"> - <property name="visible">True</property> - <property name="homogeneous">False</property> - <property name="spacing">0</property> - - <child> - <widget class="GtkButton" id="button1"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="label">gtk-add</property> - <property name="use_stock">True</property> - <property name="relief">GTK_RELIEF_NORMAL</property> - <property name="focus_on_click">True</property> - <signal name="clicked" handler="on_button1_clicked" last_modification_time="Fri, 07 Aug 2009 10:05:30 GMT"/> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> - - <child> - <widget class="GtkButton" id="button2"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="label">gtk-remove</property> - <property name="use_stock">True</property> - <property name="relief">GTK_RELIEF_NORMAL</property> - <property name="focus_on_click">True</property> - <signal name="clicked" handler="on_button2_clicked" last_modification_time="Fri, 07 Aug 2009 10:05:46 GMT"/> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> - - <child> - <placeholder/> - </child> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> </widget> <packing> <property name="padding">0</property> @@ -159,6 +121,25 @@ <property name="fill">True</property> </packing> </child> + + <child> + <widget class="GtkCheckButton" id="checkbutton1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Display relative times</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">True</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> </widget> </child> </widget> diff --git a/grapher/grapher.cxx b/grapher/grapher.cxx index 969bc762..0111184a 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" @@ -31,65 +39,85 @@ #include <getopt.h> using namespace std; +using namespace tr1; 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<ssize_t>(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<StapParser>& parser) const { - pid_t pid; - int status; - if ((pid = waitpid(-1, &status, WNOHANG)) == -1) - { - std::perror("waitpid"); - return -1; - } - else - { - return 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; + return parser->getPid() == pid; } -private: - int sigfd; + pid_t pid; }; // Depending on how args are passed, either launch stap directly or @@ -97,14 +125,18 @@ private: class StapLauncher : public ChildDeathReader { public: - StapLauncher() : _argv(0), _childPid(-1), _deathCallback(0) {} + StapLauncher() : _argv(0), _childPid(-1) + { + childDiedSignal().connect(sigc::mem_fun(*this, &StapLauncher::onChildDied)); + } StapLauncher(char** argv) - : _argv(argv), _childPid(-1), _deathCallback(0) + : _argv(argv), _childPid(-1) { + childDiedSignal().connect(sigc::mem_fun(*this, &StapLauncher::onChildDied)); } StapLauncher(const string& stapArgs, const string& script, const string& scriptArgs) - : _childPid(-1), _deathCallback(0), _win(0), _widget(0) + : _childPid(-1) { setArgs(stapArgs, script, scriptArgs); } @@ -121,6 +153,7 @@ public: void setArgs(const string& stapArgs, const string& script, const string& scriptArgs) { + _argv = 0; _stapArgs = stapArgs; _script = script; _scriptArgs = scriptArgs; @@ -140,43 +173,42 @@ public: _script.clear(); _scriptArgs.clear(); } - void setDeathCallback(ChildDeathReader::Callback* callback) - { - _deathCallback = callback; - } - void setWinParams(Gtk::Window* win, GraphWidget* widget) - { - _win = win; - _widget = widget; - } + int launch(); - void cleanUp(); - tr1::shared_ptr<StapParser> makeStapParser() + int launchUsingParser(shared_ptr<StapParser> parser); + shared_ptr<StapParser> makeStapParser() { - tr1::shared_ptr<StapParser> result(new StapParser(_win, _widget)); - _parsers.push_back(ParserInstance(-1, result)); + shared_ptr<StapParser> result(new StapParser); + parsers.push_back(result); + parserListChangedSignal().emit(); return result; } - pid_t reap() +public: + void onChildDied(pid_t pid) { - 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); - if (itr != _parsers.end()) - itr->childPid = -1; - return pid; + = find_if(parsers.begin(), parsers.end(), PidPred(pid)); + if (itr != parsers.end()) + { + (*itr)->disconnect(); + tr1::shared_ptr<StapProcess> sp = (*itr)->getProcess(); + if (sp) + { + sp->pid = -1; + parserListChangedSignal().emit(); + } + } } 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) { - if (itr->childPid >= 0) - kill(itr->childPid, SIGTERM); + if ((*itr)->getPid() >= 0) + kill((*itr)->getPid(), SIGTERM); } } protected: @@ -185,26 +217,40 @@ protected: string _script; string _scriptArgs; int _childPid; - ChildDeathReader::Callback* _deathCallback; - Gtk::Window* _win; - GraphWidget* _widget; - struct ParserInstance - { - ParserInstance() : childPid(-1) {} - ParserInstance(int childPid_, tr1::shared_ptr<StapParser> stapParser_) - : childPid(childPid_), stapParser(stapParser_) - { - } - pid_t childPid; - tr1::shared_ptr<StapParser> stapParser; - }; - typedef vector<ParserInstance> ParserList; - ParserList _parsers; }; int StapLauncher::launch() { - int childPid = -1; + tr1::shared_ptr<StapParser> sp(new StapParser); + shared_ptr<StapProcess> 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<StapParser> sp) +{ + shared_ptr<StapProcess> 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) @@ -219,28 +265,23 @@ 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) + if (pipe(&pipefd[0]) < 0 || pipe(&pipefd[2]) < 0) { std::perror("pipe"); exit(1); } - if (pipe(&pipefd[2]) < 0) + if ((proc->pid = fork()) == -1) { - std::perror("pipe"); exit(1); } - if ((childPid = fork()) == -1) - { - exit(1); - } - else if (childPid) + else if (proc->pid) { close(pipefd[1]); close(pipefd[3]); @@ -251,73 +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<char*>(0)); } _exit(1); } - tr1::shared_ptr<StapParser> sp(new StapParser(_win, _widget)); - _parsers.push_back(ParserInstance(childPid, 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); - 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) - { - if (itr->childPid > 0) - kill(itr->childPid, SIGTERM); - int status; - pid_t killedPid = -1; - if ((killedPid = wait(&status)) == -1) - { - std::perror("wait"); - } - else if (killedPid != itr->childPid) - { - std::cerr << "wait: killed Pid " << killedPid << " != child Pid " - << itr->childPid << "\n"; - } - else if (_deathCallback) - _deathCallback->childDied(itr->childPid); - } + sp->initIo(pipefd[0], pipefd[2], false); + return proc->pid; } class GraphicalStapLauncher : public StapLauncher @@ -335,7 +331,229 @@ private: Gtk::Entry* _scriptArgEntry; }; -class GrapherWindow : public Gtk::Window, public ChildDeathReader::Callback +GraphicalStapLauncher *graphicalLauncher = 0; + +class ProcModelColumns : public Gtk::TreeModelColumnRecord +{ +public: + ProcModelColumns() + { + add(_iconName); + add(_scriptName); + add(_parser); + } + Gtk::TreeModelColumn<Glib::ustring> _iconName; + Gtk::TreeModelColumn<Glib::ustring> _scriptName; + Gtk::TreeModelColumn<shared_ptr<StapParser> > _parser; +}; + +// This should probably be a Gtk window, with the appropriate glade magic +class ProcWindow +{ +public: + ProcWindow(); + ProcModelColumns _modelColumns; + Glib::RefPtr<Gnome::Glade::Xml> _xml; + Gtk::Window* _window; + Gtk::TreeView* _dataTreeView; + Gtk::Button* _killButton; + Gtk::Button* _restartButton; + Gtk::Label* _stapArgsLabel; + Gtk::Label* _scriptArgsLabel; + Glib::RefPtr<Gtk::ListStore> _listStore; + Glib::RefPtr<Gtk::TreeSelection> _listSelection; + void onClose(); + void show(); + void hide(); + void onParserListChanged(); + void onSelectionChanged(); + void onKill(); + void onRestart(); +private: + bool _open; + void refresh(); +}; + +ProcWindow::ProcWindow() + : _open(false) +{ + 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); + // 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", _killButton); + _killButton->signal_clicked() + .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)); + _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() +{ + _window->hide(); +} + +void ProcWindow::show() +{ + _open = true; + refresh(); + _window->show(); + +} + +void ProcWindow::hide() +{ + _open = false; + _window->hide(); +} + +void ProcWindow::refresh() +{ + // If a process is already selected, try to leave it selected after + // the list is reconstructed. + shared_ptr<StapParser> 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<StapParser> parser = *spitr; + shared_ptr<StapProcess> 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; + } + 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; + } + } + } +} + +void ProcWindow::onParserListChanged() +{ + if (_open) + { + refresh(); + _window->queue_draw(); + } +} + +void ProcWindow::onSelectionChanged() +{ + Gtk::TreeModel::iterator itr = _listSelection->get_selected(); + shared_ptr<StapParser> parser; + shared_ptr<StapProcess> proc; + if (itr) + { + Gtk::TreeModel::Row row = *itr; + parser = row[_modelColumns._parser]; + proc = parser->getProcess(); + } + if (proc) + { + 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(""); + } +} + +void ProcWindow::onKill() +{ + Gtk::TreeModel::iterator itr = _listSelection->get_selected(); + if (!itr) + return; + Gtk::TreeModel::Row row = *itr; + shared_ptr<StapParser> parser = row[_modelColumns._parser]; + shared_ptr<StapProcess> 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<StapParser> parser = row[_modelColumns._parser]; + shared_ptr<StapProcess> proc = parser->getProcess(); + if (!proc) + return; + if (graphicalLauncher->launchUsingParser(parser) > 0) + parserListChangedSignal().emit(); +} + +class GrapherWindow : public Gtk::Window { public: GrapherWindow(); @@ -343,34 +561,33 @@ public: Gtk::VBox m_Box; Gtk::ScrolledWindow scrolled; GraphWidget w; - void childDied(int pid); - void setGraphicalLauncher(GraphicalStapLauncher* launcher) - { - _graphicalLauncher = launcher; - } - GraphicalStapLauncher* getGraphicalLauncher() { return _graphicalLauncher; } protected: virtual void on_menu_file_quit(); virtual void on_menu_script_start(); + virtual void on_menu_proc_window(); void addGraph(); + void onParserListChanged(); // menu support Glib::RefPtr<Gtk::UIManager> m_refUIManager; Glib::RefPtr<Gtk::ActionGroup> m_refActionGroup; - GraphicalStapLauncher* _graphicalLauncher; - + shared_ptr<ProcWindow> _procWindow; + bool _quitting; }; + GrapherWindow::GrapherWindow() + : _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: 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"), @@ -378,6 +595,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); @@ -391,6 +614,9 @@ GrapherWindow::GrapherWindow() " <menuitem action='AddGraph'/>" " <menuitem action='FileQuit'/>" " </menu>" + " <menu action='WindowMenu'>" + " <menuitem action='ProcessWindow'/>" + " </menu>" " </menubar>" "</ui>"; try @@ -414,51 +640,55 @@ GrapherWindow::GrapherWindow() void GrapherWindow::on_menu_file_quit() { - hide(); + using namespace boost; + _quitting = true; + if (find_if(parsers.begin(), parsers.end(), !bind<bool>(PidPred(-1), _1)) + != parsers.end()) + graphicalLauncher->killAll(); + else + hide(); } void GrapherWindow::on_menu_script_start() { - _graphicalLauncher->runDialog(); + graphicalLauncher->runDialog(); } -void GrapherWindow::childDied(int pid) + +void GrapherWindow::on_menu_proc_window() { - hide(); + _procWindow->show(); } - - +void GrapherWindow::onParserListChanged() +{ + using namespace boost; + if (_quitting + && (find_if(parsers.begin(), parsers.end(), !bind<bool>(PidPred(-1), _1)) + == parsers.end())) + hide(); +} 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); - launcher.setWinParams(&win, &win.w); - win.setGraphicalLauncher(&launcher); - if (argc == 2 && !std::strcmp(argv[1], "-")) { - tr1::shared_ptr<StapParser> 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); + tr1::shared_ptr<StapParser> sp = graphicalLauncher->makeStapParser(); + sp->initIo(STDIN_FILENO, -1, true); } else if (argc > 1) { - launcher.setArgv(argv + 1); - launcher.setDeathCallback(&win); - launcher.launch(); + graphicalLauncher->setArgv(argv + 1); + graphicalLauncher->launch(); } Gtk::Main::run(win); - launcher.cleanUp(); return 0; } diff --git a/grapher/processwindow.glade b/grapher/processwindow.glade new file mode 100644 index 00000000..a598fba6 --- /dev/null +++ b/grapher/processwindow.glade @@ -0,0 +1,460 @@ +<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*--> +<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd"> + +<glade-interface> + +<widget class="GtkWindow" id="window1"> + <property name="title" translatable="yes">stap processes</property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_NONE</property> + <property name="modal">False</property> + <property name="resizable">True</property> + <property name="destroy_with_parent">False</property> + <property name="decorated">True</property> + <property name="skip_taskbar_hint">False</property> + <property name="skip_pager_hint">False</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> + <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> + <property name="focus_on_map">True</property> + <property name="urgency_hint">False</property> + + <child> + <widget class="GtkVBox" id="vbox1"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkToolbar" id="toolbar1"> + <property name="visible">True</property> + <property name="orientation">GTK_ORIENTATION_HORIZONTAL</property> + <property name="toolbar_style">GTK_TOOLBAR_BOTH</property> + <property name="tooltips">True</property> + <property name="show_arrow">True</property> + + <child> + <widget class="GtkToolItem" id="toolitem1"> + <property name="visible">True</property> + <property name="visible_horizontal">True</property> + <property name="visible_vertical">True</property> + <property name="is_important">False</property> + + <child> + <widget class="GtkButton" id="button1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + + <child> + <widget class="GtkAlignment" id="alignment3"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">0</property> + <property name="yscale">0</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">0</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkHBox" id="hbox4"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">2</property> + + <child> + <widget class="GtkImage" id="image3"> + <property name="visible">True</property> + <property name="stock">gtk-cancel</property> + <property name="icon_size">4</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label4"> + <property name="visible">True</property> + <property name="label" translatable="yes">Kill</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + </child> + </widget> + </child> + </widget> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="homogeneous">False</property> + </packing> + </child> + + <child> + <widget class="GtkToolItem" id="toolitem2"> + <property name="visible">True</property> + <property name="visible_horizontal">True</property> + <property name="visible_vertical">True</property> + <property name="is_important">False</property> + + <child> + <widget class="GtkButton" id="button2"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + + <child> + <widget class="GtkAlignment" id="alignment2"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">0</property> + <property name="yscale">0</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">0</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkHBox" id="hbox3"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">2</property> + + <child> + <widget class="GtkImage" id="image2"> + <property name="visible">True</property> + <property name="stock">gtk-refresh</property> + <property name="icon_size">4</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label3"> + <property name="visible">True</property> + <property name="label" translatable="yes">Restart</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + </child> + </widget> + </child> + </widget> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="homogeneous">False</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkHPaned" id="hpaned1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_ALWAYS</property> + <property name="vscrollbar_policy">GTK_POLICY_ALWAYS</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + + <child> + <widget class="GtkTreeView" id="treeview1"> + <property name="width_request">250</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="headers_visible">True</property> + <property name="rules_hint">False</property> + <property name="reorderable">False</property> + <property name="enable_search">True</property> + <property name="fixed_height_mode">False</property> + <property name="hover_selection">False</property> + <property name="hover_expand">False</property> + </widget> + </child> + </widget> + <packing> + <property name="shrink">True</property> + <property name="resize">False</property> + </packing> + </child> + + <child> + <widget class="GtkVBox" id="vbox2"> + <property name="width_request">124</property> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkHBox" id="hbox5"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkLabel" id="label5"> + <property name="visible">True</property> + <property name="label" translatable="yes">stap arguments: </property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label7"> + <property name="visible">True</property> + <property name="label" translatable="yes"></property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox6"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkLabel" id="label6"> + <property name="visible">True</property> + <property name="label" translatable="yes">script arguments: </property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label8"> + <property name="visible">True</property> + <property name="label" translatable="yes"></property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow2"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_ALWAYS</property> + <property name="vscrollbar_policy">GTK_POLICY_ALWAYS</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + + <child> + <widget class="GtkTextView" id="textview1"> + <property name="width_request">20</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="overwrite">False</property> + <property name="accepts_tab">True</property> + <property name="justification">GTK_JUSTIFY_LEFT</property> + <property name="wrap_mode">GTK_WRAP_NONE</property> + <property name="cursor_visible">True</property> + <property name="pixels_above_lines">0</property> + <property name="pixels_below_lines">0</property> + <property name="pixels_inside_wrap">0</property> + <property name="left_margin">0</property> + <property name="right_margin">0</property> + <property name="indent">0</property> + <property name="text" translatable="yes"></property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="shrink">True</property> + <property name="resize">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkHButtonBox" id="hbuttonbox1"> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_END</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkButton" id="button5"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-close</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + </child> +</widget> + +</glade-interface> 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 @@ +<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*--> +<!DOCTYPE glade-project SYSTEM "http://glade.gnome.org/glade-project-2.0.dtd"> + +<glade-project> + <name></name> + <program_name></program_name> + <gnome_support>FALSE</gnome_support> +</glade-project> |