summaryrefslogtreecommitdiffstats
path: root/grapher
diff options
context:
space:
mode:
Diffstat (limited to 'grapher')
-rw-r--r--grapher/CairoWidget.cxx50
-rw-r--r--grapher/CairoWidget.hxx8
-rw-r--r--grapher/Graph.cxx28
-rw-r--r--grapher/Graph.hxx15
-rw-r--r--grapher/GraphData.hxx22
-rw-r--r--grapher/GraphStyle.cxx58
-rw-r--r--grapher/GraphStyle.hxx12
-rw-r--r--grapher/GraphWidget.cxx163
-rw-r--r--grapher/GraphWidget.hxx33
-rw-r--r--grapher/Makefile.am2
-rw-r--r--grapher/Makefile.in5
-rw-r--r--grapher/StapParser.cxx370
-rw-r--r--grapher/StapParser.hxx97
-rw-r--r--grapher/graph-dialog.glade117
-rw-r--r--grapher/grapher.cxx596
-rw-r--r--grapher/processwindow.glade460
-rw-r--r--grapher/processwindow.gladep8
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>