summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Brolley <brolley@redhat.com>2009-12-14 16:39:01 -0500
committerDave Brolley <brolley@redhat.com>2009-12-14 16:39:01 -0500
commitb8f1753c091d3f75ea4a71bfb709d8e50780d3fb (patch)
treeea5e83a0556d4df262a2b4f9b26065dc312a8a1e
parent61b21980212779b5a35f6a196842bdca55a3ced6 (diff)
parent958c58e8231563e9349e4d8ea56c04c25e1501c0 (diff)
downloadsystemtap-steved-b8f1753c091d3f75ea4a71bfb709d8e50780d3fb.tar.gz
systemtap-steved-b8f1753c091d3f75ea4a71bfb709d8e50780d3fb.tar.xz
systemtap-steved-b8f1753c091d3f75ea4a71bfb709d8e50780d3fb.zip
Merge branch 'master' of ssh://sources.redhat.com/git/systemtap
-rwxr-xr-xdtrace.in19
-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
-rw-r--r--includes/sys/sdt.h20
-rw-r--r--loc2c.c19
-rw-r--r--runtime/addr-map.c11
-rw-r--r--runtime/alloc.c19
-rw-r--r--runtime/itrace.c9
-rw-r--r--runtime/print.c31
-rw-r--r--runtime/regs.c2
-rw-r--r--runtime/staprun/relay.c6
-rw-r--r--runtime/stat-common.c18
-rw-r--r--tapset-utrace.cxx102
-rw-r--r--tapset/tcpmib.stp11
-rw-r--r--tapsets.cxx189
-rwxr-xr-xtestsuite/buildok/tcpmib-all-probes.stp7
-rw-r--r--testsuite/lib/systemtap.exp4
-rw-r--r--testsuite/systemtap.apps/mysql.exp13
-rw-r--r--testsuite/systemtap.apps/postgres.exp6
-rw-r--r--testsuite/systemtap.apps/stap-tcl.stp2
-rw-r--r--testsuite/systemtap.apps/tcl.exp6
-rw-r--r--testsuite/systemtap.base/dtrace.exp72
-rw-r--r--testsuite/systemtap.base/sdt_misc.exp101
-rwxr-xr-xtestsuite/systemtap.examples/general/grapher.stp20
-rw-r--r--util.cxx4
-rw-r--r--util.h3
41 files changed, 2025 insertions, 713 deletions
diff --git a/dtrace.in b/dtrace.in
index 74d70e77..3be05038 100755
--- a/dtrace.in
+++ b/dtrace.in
@@ -46,7 +46,7 @@ class provider:
self.f = open(provider)
self.h = open(header,mode='w')
self.h.write("/* Generated by the Systemtap dtrace wrapper */\n")
- # self.h.write("\n#define STAP_HAS_SEMAPHORES 1\n\n")
+ self.h.write("\n#define STAP_HAS_SEMAPHORES 1\n\n")
self.h.write("\n#include <sys/sdt.h>\n\n")
in_comment = False
typedefs = ""
@@ -107,7 +107,7 @@ class provider:
i += 1
self.h.write ('/* %s (%s) */\n' % (this_probe_canon,args_string))
# XXX Enable this when .so semaphores work properly
- self.h.write ('#define %s_ENABLED() 1 /*%s_semaphore*/\n' % (this_probe_canon,this_probe))
+ self.h.write ('#define %s_ENABLED() %s_semaphore\n' % (this_probe_canon,this_probe))
# NB: unsigned short is fixed in ABI
self.h.write ("__extension__ extern unsigned short %s_semaphore __attribute__ ((unused)) __attribute__ ((section (\".probes\")));\n" % (this_probe))
self.h.write (define_str + ") \\\n")
@@ -120,13 +120,15 @@ class provider:
def usage ():
- print "Usage " + sys.argv[0] + " [--help] [-h | -G] -s File.d [-o File]"
+ print "Usage " + sys.argv[0] + " [--help] [-h | -G] [-C [-I<Path>]] -s File.d [-o <File>]"
def help ():
usage()
print "Where -h builds a systemtap header file from the .d file"
+ print " -C when used with -h, also run cpp preprocessor"
print " -o specifies an explicit output file name,"
- print " The default for -G is file.o and -h is file.h"
+ print " the default for -G is file.o and -h is file.h"
+ print " -I when running cpp pass through this -I include Path"
print " -s specifies the name of the .d input file"
print " -G builds a stub file.o from file.d,"
print " which is required by some packages that use dtrace."
@@ -159,6 +161,8 @@ keep_temps = False
use_cpp = False
h_ext = '.h'
filename = ""
+s_filename = ""
+includes = []
while (i < len (sys.argv)):
if (sys.argv[i] == "-o"):
i += 1
@@ -170,6 +174,8 @@ while (i < len (sys.argv)):
use_cpp = True
elif (sys.argv[i] == "-h"):
build_header = True
+ elif (sys.argv[i].startswith("-I")):
+ includes.append(sys.argv[i])
elif (sys.argv[i] == "-G"):
build_source = True
elif (sys.argv[i] == "-k"):
@@ -185,9 +191,10 @@ if (build_header == False and build_source == False):
if (s_filename != "" and use_cpp):
(d,fn) = mkstemp(suffix=".d")
- retcode = call(["cpp", s_filename, fn])
+ args = ['cpp'] + includes + [s_filename, fn]
+ retcode = call(args)
if (retcode != 0):
- print "\"cpp s_filename\" failed"
+ print "\"cpp includes s_filename\" failed"
usage()
sys.exit(1)
s_filename = fn
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>
diff --git a/includes/sys/sdt.h b/includes/sys/sdt.h
index 3847c497..7c23d55a 100644
--- a/includes/sys/sdt.h
+++ b/includes/sys/sdt.h
@@ -12,9 +12,11 @@
#ifdef __LP64__
-#define STAP_PROBE_ADDR "\t.quad "
+#define STAP_PROBE_ADDR(arg) "\t.quad " arg
+#elif defined (__BIG_ENDIAN__)
+#define STAP_PROBE_ADDR(arg) "\t.long 0\n\t.long " arg
#else
-#define STAP_PROBE_ADDR "\t.long "
+#define STAP_PROBE_ADDR(arg) "\t.long " arg
#endif
/* Allocated section needs to be writable when creating pic shared objects
@@ -26,21 +28,21 @@
/* An allocated section .probes that holds the probe names and addrs. */
#define STAP_PROBE_DATA_(probe,guard,arg) \
__asm__ volatile (".section .probes," ALLOCSEC "\n" \
- "\t.align 8\n" \
+ "\t.balign 8\n" \
"1:\n\t.asciz " #probe "\n" \
- "\t.align 4\n" \
+ "\t.balign 4\n" \
"\t.int " #guard "\n" \
- "\t.align 8\n" \
- STAP_PROBE_ADDR "1b\n" \
- "\t.align 8\n" \
- STAP_PROBE_ADDR #arg "\n" \
+ "\t.balign 8\n" \
+ STAP_PROBE_ADDR("1b\n") \
+ "\t.balign 8\n" \
+ STAP_PROBE_ADDR(#arg "\n") \
"\t.int 0\n" \
"\t.previous\n")
#define STAP_PROBE_DATA(probe, guard, arg) \
STAP_PROBE_DATA_(#probe,guard,arg)
-#if defined STAP_HAS_SEMAPHORES && defined EXPERIMENTAL_UTRACE_SDT
+#if defined STAP_HAS_SEMAPHORES && ! defined EXPERIMENTAL_KPROBE_SDT
#define STAP_SEMAPHORE(probe) \
if (__builtin_expect ( probe ## _semaphore , 0))
#else
diff --git a/loc2c.c b/loc2c.c
index 008d5a42..d3ec55fa 100644
--- a/loc2c.c
+++ b/loc2c.c
@@ -76,6 +76,15 @@ struct location
};
};
+/* Select the C type to use in the emitted code to represent slots in the
+ DWARF expression stack. For really proper semantics this should be the
+ target address size. */
+static const char *
+stack_slot_type (struct location *context __attribute__ ((unused)), bool sign)
+{
+ return sign ? STACK_TYPE : UTYPE;
+}
+
static struct location *
alloc_location (struct obstack *pool, struct location *origin)
{
@@ -491,7 +500,8 @@ translate (struct obstack *pool, int indent, Dwarf_Addr addrbias,
POP (b);
POP (a);
push ("(%s) " STACKFMT " >> (%s)" STACKFMT,
- UTYPE, a, UTYPE, b);
+ stack_slot_type (loc, true), a,
+ stack_slot_type (loc, true), b);
break;
}
@@ -2187,7 +2197,8 @@ emit_loc_address (FILE *out, struct location *loc, unsigned int indent,
else
{
emit ("%*s{\n", indent * 2, "");
- emit ("%*s%s " STACKFMT, (indent + 1) * 2, "", STACK_TYPE, 0);
+ emit ("%*s%s " STACKFMT, (indent + 1) * 2, "",
+ stack_slot_type (loc, false), 0);
unsigned i;
for (i = 1; i < loc->address.stack_depth; ++i)
emit (", " STACKFMT, i);
@@ -2207,7 +2218,7 @@ emit_loc_value (FILE *out, struct location *loc, unsigned int indent,
bool *used_deref, unsigned int *max_stack)
{
if (declare)
- emit ("%*s%s %s;\n", indent * 2, "", STACK_TYPE, target);
+ emit ("%*s%s %s;\n", indent * 2, "", stack_slot_type (loc, false), target);
emit_header (out, loc, indent++);
@@ -2260,7 +2271,7 @@ c_emit_location (FILE *out, struct location *loc, int indent,
{
if (l->byte_size == 0 || l->byte_size == (Dwarf_Word) -1)
emit ("%*s%s %s;\n", (indent + 1) * 2, "",
- STACK_TYPE, l->address.declare);
+ stack_slot_type (l, false), l->address.declare);
else
emit ("%*suint%" PRIu64 "_t %s;\n", (indent + 1) * 2, "",
l->byte_size * 8, l->address.declare);
diff --git a/runtime/addr-map.c b/runtime/addr-map.c
index 35de7a64..abb723f3 100644
--- a/runtime/addr-map.c
+++ b/runtime/addr-map.c
@@ -163,12 +163,11 @@ add_bad_addr_entry(unsigned long min_addr, unsigned long max_addr,
spin_unlock(&addr_map_lock);
if (new_map)
{
- kfree(new_map);
+ _stp_kfree(new_map);
new_map = 0;
}
- new_map = kmalloc(sizeof(*new_map)
- + sizeof(*new_entry) * (old_size + 1),
- GFP_KERNEL);
+ new_map = _stp_kmalloc(sizeof(*new_map)
+ + sizeof(*new_entry) * (old_size + 1));
if (!new_map)
return -ENOMEM;
new_map->size = old_size + 1;
@@ -191,7 +190,7 @@ add_bad_addr_entry(unsigned long min_addr, unsigned long max_addr,
if (existing_max)
*existing_max = max_entry;
spin_unlock(&addr_map_lock);
- kfree(new_map);
+ _stp_kfree(new_map);
return 1;
}
existing = upper_bound(min_addr, old_map);
@@ -210,7 +209,7 @@ add_bad_addr_entry(unsigned long min_addr, unsigned long max_addr,
blackmap = new_map;
spin_unlock(&addr_map_lock);
if (old_map)
- kfree(old_map);
+ _stp_kfree(old_map);
return 0;
}
diff --git a/runtime/alloc.c b/runtime/alloc.c
index 403d20ee..2e98b94e 100644
--- a/runtime/alloc.c
+++ b/runtime/alloc.c
@@ -16,7 +16,7 @@
static int _stp_allocated_net_memory = 0;
#define STP_ALLOC_FLAGS (GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN)
-//#define DEBUG_MEM
+/* #define DEBUG_MEM */
/*
* If DEBUG_MEM is defined (stap -DDEBUG_MEM ...) then full memory
* tracking is used. Each allocation is recorded and matched with
@@ -32,10 +32,10 @@ static int _stp_allocated_net_memory = 0;
* would be nice, but DEBUG_MEM is only for testing.
*/
-#ifdef DEBUG_MEM
+static int _stp_allocated_memory = 0;
+#ifdef DEBUG_MEM
static DEFINE_SPINLOCK(_stp_mem_lock);
-static int _stp_allocated_memory = 0;
#define MEM_MAGIC 0xc11cf77f
#define MEM_FENCE_SIZE 32
@@ -176,11 +176,11 @@ static void _stp_mem_debug_free(void *addr, enum _stp_memtype type)
static void *_stp_kmalloc(size_t size)
{
+ _stp_allocated_memory += size;
#ifdef DEBUG_MEM
void *ret = kmalloc(size + MEM_DEBUG_SIZE, STP_ALLOC_FLAGS);
if (likely(ret)) {
ret = _stp_mem_debug_setup(ret, size, MEM_KMALLOC);
- _stp_allocated_memory += size;
}
return ret;
#else
@@ -196,10 +196,10 @@ static void *_stp_kzalloc(size_t size)
if (likely(ret)) {
ret = _stp_mem_debug_setup(ret, size, MEM_KMALLOC);
memset (ret, 0, size);
- _stp_allocated_memory += size;
}
#else
void *ret = kmalloc(size, STP_ALLOC_FLAGS);
+ _stp_allocated_memory += size;
if (likely(ret))
memset (ret, 0, size);
#endif /* DEBUG_MEM */
@@ -209,9 +209,9 @@ static void *_stp_kzalloc(size_t size)
{
#ifdef DEBUG_MEM
void *ret = kzalloc(size + MEM_DEBUG_SIZE, STP_ALLOC_FLAGS);
+ _stp_allocated_memory += size;
if (likely(ret)) {
ret = _stp_mem_debug_setup(ret, size, MEM_KMALLOC);
- _stp_allocated_memory += size;
}
return ret;
#else
@@ -222,11 +222,11 @@ static void *_stp_kzalloc(size_t size)
static void *_stp_vmalloc(unsigned long size)
{
+ _stp_allocated_memory += size;
#ifdef DEBUG_MEM
void *ret = __vmalloc(size + MEM_DEBUG_SIZE, STP_ALLOC_FLAGS, PAGE_KERNEL);
if (likely(ret)) {
ret = _stp_mem_debug_setup(ret, size, MEM_VMALLOC);
- _stp_allocated_memory += size;
}
return ret;
#else
@@ -248,6 +248,8 @@ static void *_stp_alloc_percpu(size_t size)
if (size > _STP_MAX_PERCPU_SIZE)
return NULL;
+ _stp_allocated_memory += size * num_online_cpus();
+
#ifdef STAPCONF_ALLOC_PERCPU_ALIGN
ret = __alloc_percpu(size, 8);
#else
@@ -261,7 +263,6 @@ static void *_stp_alloc_percpu(size_t size)
return NULL;
}
_stp_mem_debug_percpu(m, ret, size);
- _stp_allocated_memory += size * num_online_cpus();
}
#endif
return ret;
@@ -272,11 +273,11 @@ static void *_stp_alloc_percpu(size_t size)
#else
static void *_stp_kmalloc_node(size_t size, int node)
{
+ _stp_allocated_memory += size;
#ifdef DEBUG_MEM
void *ret = kmalloc_node(size + MEM_DEBUG_SIZE, STP_ALLOC_FLAGS, node);
if (likely(ret)) {
ret = _stp_mem_debug_setup(ret, size, MEM_KMALLOC);
- _stp_allocated_memory += size;
}
return ret;
#else
diff --git a/runtime/itrace.c b/runtime/itrace.c
index f2ed86f2..5b2437a4 100644
--- a/runtime/itrace.c
+++ b/runtime/itrace.c
@@ -219,7 +219,12 @@ static struct itrace_info *create_itrace_info(
if (debug)
printk(KERN_INFO "create_itrace_info: tid=%d\n", tsk->pid);
/* initialize ui */
- ui = kzalloc(sizeof(struct itrace_info), GFP_USER);
+ ui = _stp_kzalloc(sizeof(struct itrace_info));
+ if (ui == NULL) {
+ printk(KERN_ERR "%s:%d: Unable to allocate memory\n",
+ __FUNCTION__, __LINE__);
+ return NULL;
+ }
ui->tsk = tsk;
ui->tid = tsk->pid;
ui->step_flag = step_flag;
@@ -329,7 +334,7 @@ void static remove_usr_itrace_info(struct itrace_info *ui)
spin_lock(&itrace_lock);
list_del(&ui->link);
spin_unlock(&itrace_lock);
- kfree(ui);
+ _stp_kfree(ui);
}
void static cleanup_usr_itrace(void)
diff --git a/runtime/print.c b/runtime/print.c
index 335403fb..09ebd05e 100644
--- a/runtime/print.c
+++ b/runtime/print.c
@@ -217,14 +217,8 @@ static void _stp_print_char (const char c)
static void _stp_print_kernel_info(char *vstr, int ctx, int num_probes)
{
printk(KERN_DEBUG
- "%s: systemtap: %s, base: %p, memory: %lu+%lu+%u+%u"
-#ifdef DEBUG_MEM
- "+%u"
-#endif
- " data+text+ctx+net"
-#ifdef DEBUG_MEM
- "+alloc"
-#endif
+ "%s: systemtap: %s, base: %p"
+ ", memory: %ludata/%lutext/%uctx/%unet/%ualloc kb"
", probes: %d"
#ifndef STP_PRIVILEGED
", unpriv-uid: %d"
@@ -234,19 +228,18 @@ static void _stp_print_kernel_info(char *vstr, int ctx, int num_probes)
vstr,
#ifndef STAPCONF_GRSECURITY
THIS_MODULE->module_core,
- (unsigned long) (THIS_MODULE->core_size - THIS_MODULE->core_text_size),
- (unsigned long) THIS_MODULE->core_text_size,
+ (unsigned long) (THIS_MODULE->core_size - THIS_MODULE->core_text_size)/1024,
+ (unsigned long) (THIS_MODULE->core_text_size)/1024,
#else
- THIS_MODULE->module_core_rx,
- (unsigned long) (THIS_MODULE->core_size_rw - THIS_MODULE->core_size_rx),
- (unsigned long) THIS_MODULE->core_size_rx,
-#endif
- ctx,
- _stp_allocated_net_memory,
-#ifdef DEBUG_MEM
- _stp_allocated_memory - _stp_allocated_net_memory,
+ THIS_MODULE->module_core_rx,
+ (unsigned long) (THIS_MODULE->core_size_rw - THIS_MODULE->core_size_rx)/1024,
+ (unsigned long) (THIS_MODULE->core_size_rx)/1024,
#endif
- num_probes
+ ctx/1024,
+ _stp_allocated_net_memory/1024,
+ (_stp_allocated_memory - _stp_allocated_net_memory - ctx)/1024,
+ /* (un-double-counting net/ctx because they're also stp_alloc'd) */
+ num_probes
#ifndef STP_PRIVILEGED
, _stp_uid
#endif
diff --git a/runtime/regs.c b/runtime/regs.c
index e963affa..61f2f317 100644
--- a/runtime/regs.c
+++ b/runtime/regs.c
@@ -267,7 +267,7 @@ static const char *processor_modes[]=
static void _stp_print_regs(struct pt_regs * regs)
{
- unsigned long flags = condition_codes(regs);
+ unsigned long flags = regs->ARM_cpsr;
#ifdef CONFIG_SMP
_stp_printf(" CPU: %d", smp_processor_id());
diff --git a/runtime/staprun/relay.c b/runtime/staprun/relay.c
index 59d7ac9a..39a2f393 100644
--- a/runtime/staprun/relay.c
+++ b/runtime/staprun/relay.c
@@ -166,9 +166,9 @@ static void *reader_thread(void *data)
if (stop_threads)
break;
if (switch_file[cpu]) {
- switch_file[cpu] = 0;
if (switch_outfile(cpu, &fnum) < 0)
goto error_out;
+ switch_file[cpu] = 0;
wsize = 0;
}
} else {
@@ -179,9 +179,11 @@ static void *reader_thread(void *data)
while ((rc = read(relay_fd[cpu], buf, sizeof(buf))) > 0) {
/* Switching file */
- if (fsize_max && wsize + rc > fsize_max) {
+ if ((fsize_max && wsize + rc > fsize_max) ||
+ switch_file[cpu]) {
if (switch_outfile(cpu, &fnum) < 0)
goto error_out;
+ switch_file[cpu] = 0;
wsize = 0;
}
if (write(out_fd[cpu], buf, rc) != rc) {
diff --git a/runtime/stat-common.c b/runtime/stat-common.c
index f9703049..fabe8404 100644
--- a/runtime/stat-common.c
+++ b/runtime/stat-common.c
@@ -34,9 +34,10 @@ static int _stp_stat_calc_buckets(int stop, int start, int interval)
return buckets;
}
-static int needed_space(uint64_t v)
+static int needed_space(int64_t v)
{
int space = 0;
+ uint64_t tmp;
if (v == 0)
return 1;
@@ -45,9 +46,10 @@ static int needed_space(uint64_t v)
space++;
v = -v;
}
- while (v) {
+ tmp = v;
+ while (tmp) {
/* v /= 10; */
- do_div (v, 10);
+ do_div(tmp, 10);
space++;
}
return space;
@@ -134,7 +136,8 @@ static void _stp_stat_print_histogram_buf(char *buf, size_t size, Hist st, stat
{
int scale, i, j, val_space, cnt_space;
int low_bucket = -1, high_bucket = 0, over = 0, under = 0;
- uint64_t val, v, valmax = 0;
+ int64_t val, valmax = 0;
+ uint64_t v;
int eliding = 0;
char *cur_buf = buf, *fake = buf;
char **bufptr = (buf == NULL ? &fake : &cur_buf);
@@ -282,7 +285,7 @@ static void _stp_stat_print_histogram(Hist st, stat *sd)
_stp_print_flush();
}
-static void __stp_stat_add (Hist st, stat *sd, uint64_t val)
+static void __stp_stat_add(Hist st, stat *sd, int64_t val)
{
int n;
if (sd->count == 0) {
@@ -310,7 +313,10 @@ static void __stp_stat_add (Hist st, stat *sd, uint64_t val)
if (val < 0)
val = 0;
else {
- do_div (val, st->interval);
+ uint64_t tmp = val;
+
+ do_div(tmp, st->interval);
+ val = tmp;
val++;
}
diff --git a/tapset-utrace.cxx b/tapset-utrace.cxx
index 795d88b7..bd668a2b 100644
--- a/tapset-utrace.cxx
+++ b/tapset-utrace.cxx
@@ -29,6 +29,7 @@ static const string TOK_END("end");
static const string TOK_THREAD("thread");
static const string TOK_SYSCALL("syscall");
static const string TOK_RETURN("return");
+static const string TOK_LIBRARY("library");
// ------------------------------------------------------------------------
@@ -52,13 +53,15 @@ struct utrace_derived_probe: public derived_probe
{
bool has_path;
string path;
+ bool has_library;
+ string library;
int64_t pid;
enum utrace_derived_probe_flags flags;
bool target_symbol_seen;
utrace_derived_probe (systemtap_session &s, probe* p, probe_point* l,
- bool hp, string &pn, int64_t pd,
- enum utrace_derived_probe_flags f);
+ bool hp, string &pn, bool hl, string &ln,
+ int64_t pd, enum utrace_derived_probe_flags f);
void join_group (systemtap_session& s);
void emit_unprivileged_assertion (translator_output*);
@@ -115,10 +118,10 @@ struct utrace_var_expanding_visitor: public var_expanding_visitor
utrace_derived_probe::utrace_derived_probe (systemtap_session &s,
probe* p, probe_point* l,
- bool hp, string &pn, int64_t pd,
- enum utrace_derived_probe_flags f):
+ bool hp, string &pn, bool hl, string &ln,
+ int64_t pd, enum utrace_derived_probe_flags f):
derived_probe (p, new probe_point (*l) /* .components soon rewritten */ ),
- has_path(hp), path(pn), pid(pd), flags(f),
+ has_path(hp), path(pn), has_library(hl), library(ln), pid(pd), flags(f),
target_symbol_seen(false)
{
if (s.kernel_config["CONFIG_UTRACE"] != string("y"))
@@ -599,9 +602,11 @@ struct utrace_builder: public derived_probe_builder
vector<derived_probe *> & finished_results)
{
string path;
+ string lib;
int64_t pid;
bool has_path = get_param (parameters, TOK_PROCESS, path);
+ bool has_lib = get_param (parameters, TOK_LIBRARY, lib);
bool has_pid = get_param (parameters, TOK_PROCESS, pid);
enum utrace_derived_probe_flags flags = UDPF_NONE;
@@ -647,9 +652,11 @@ struct utrace_builder: public derived_probe_builder
// XXX: could we use /proc/$pid/exe in unwindsym_modules and elsewhere?
}
+ if (has_lib)
+ lib = find_executable (lib, "LD_LIBRARY_PATH");
finished_results.push_back(new utrace_derived_probe(sess, base, location,
- has_path, path, pid,
+ has_path, path, has_lib, lib, pid,
flags));
}
@@ -750,11 +757,25 @@ utrace_derived_probe_group::emit_probe_decl (systemtap_session& s,
s.op->line() << " .engine_attached=0,";
if (p->sdt_semaphore_addr != 0)
- s.op->line() << " .sdt_sem_address=(unsigned long)0x"
+ s.op->line() << " .sdt_sem_offset=(unsigned long)0x"
<< hex << p->sdt_semaphore_addr << dec << "ULL,";
-
+ s.op->line() << " .sdt_sem_address=0,";
s.op->line() << " .tsk=0,";
s.op->line() << " },";
+
+ if (p->has_library && p->sdt_semaphore_addr != 0)
+ {
+ s.op->newline() << "{";
+ s.op->line() << " .tgt={";
+ s.op->line() << " .procname=\"" << p->path << "\",";
+ s.op->line() << " .mmap_callback=&stap_utrace_mmap_found,";
+ s.op->line() << " },";
+ s.op->line() << " .pathname=\"" << p->library << "\",";
+ s.op->line() << " .sdt_sem_offset=(unsigned long)0x"
+ << hex << p->sdt_semaphore_addr << dec << "ULL,";
+ s.op->line() << " .sdt_sem_address=0,";
+ s.op->line() << " },";
+ }
}
@@ -789,7 +810,11 @@ utrace_derived_probe_group::emit_module_decls (systemtap_session& s)
s.op->newline() << "struct utrace_engine_ops ops;";
s.op->newline() << "unsigned long events;";
s.op->newline() << "struct task_struct *tsk;";
+ s.op->newline() << "unsigned long sdt_sem_offset;";
+ // FIXME: if this probe is attached to more than 1 task, having 1
+ // address here won't work
s.op->newline() << "unsigned long sdt_sem_address;";
+ s.op->newline(0) << "const char *pathname;";
s.op->newline(-1) << "};";
@@ -913,15 +938,23 @@ utrace_derived_probe_group::emit_module_decls (systemtap_session& s)
s.op->indent(-1);
s.op->newline(-1) << "}";
+ s.op->newline() << "if (p->sdt_sem_offset && p->sdt_sem_address == 0) {";
+ s.op->indent(1);
+ // If the probe is in the executable itself, the offset *is* the address.
+ s.op->newline() << "p->sdt_sem_address = p->sdt_sem_offset;";
+ s.op->newline(-1) << "}";
+
+ // Before writing to the semaphore, we need to check for VM_WRITE access.
s.op->newline() << "if (p->sdt_sem_address != 0) {";
s.op->newline(1) << "size_t sdt_semaphore;";
// XXX p could get registered to more than one task!
s.op->newline() << "p->tsk = tsk;";
- s.op->newline() << "__access_process_vm (tsk, p->sdt_sem_address, &sdt_semaphore, sizeof (sdt_semaphore), 0);";
- s.op->newline() << "sdt_semaphore += 1;";
+
+ s.op->newline() << "if (__access_process_vm (tsk, p->sdt_sem_address, &sdt_semaphore, sizeof (sdt_semaphore), 0) == sizeof (sdt_semaphore)) {";
+ s.op->newline(1) << "sdt_semaphore ++;";
s.op->newline() << "__access_process_vm (tsk, p->sdt_sem_address, &sdt_semaphore, sizeof (sdt_semaphore), 1);";
s.op->newline(-1) << "}";
-
+ s.op->newline(-1) << "}";
s.op->newline(-1) << "}";
// Since this engine could be attached to multiple threads, don't
@@ -985,6 +1018,44 @@ utrace_derived_probe_group::emit_module_decls (systemtap_session& s)
s.op->newline() << "return rc;";
s.op->newline(-1) << "}";
+ // The task_finder_mmap_callback
+ s.op->newline() << "static int stap_utrace_mmap_found (struct stap_task_finder_target *tgt, struct task_struct *tsk, char *path, unsigned long addr, unsigned long length, unsigned long offset, unsigned long vm_flags) {";
+ s.op->newline(1) << "struct stap_utrace_probe *p = container_of(tgt, struct stap_utrace_probe, tgt);";
+ s.op->newline() << "int rc = 0;";
+ // the shared library we're interested in
+ s.op->newline() << "if (path == NULL || strcmp (path, p->pathname)) return 0;";
+ s.op->newline() << "if (p->sdt_sem_address == 0) {";
+ s.op->indent(1);
+ // If the probe is in the executable itself, the offset *is* the
+ // address.
+ s.op->newline() << "if (vm_flags & VM_EXECUTABLE) {";
+ s.op->indent(1);
+ s.op->newline() << "p->sdt_sem_address = addr + p->sdt_sem_offset;";
+ s.op->newline(-1) << "}";
+ // If the probe is in a .so, we have to calculate the address.
+ s.op->newline() << "else {";
+ s.op->indent(1);
+ s.op->newline() << "p->sdt_sem_address = (addr - offset) + p->sdt_sem_offset;";
+ s.op->newline(-1) << "}";
+ s.op->newline(-1) << "}";
+
+ s.op->newline() << "if (p->sdt_sem_address) {";
+ s.op->newline(1) << "unsigned short sdt_semaphore = 0;"; // NB: fixed size
+ s.op->newline() << "if (__access_process_vm (tsk, p->sdt_sem_address, &sdt_semaphore, sizeof (sdt_semaphore), 0) == sizeof (sdt_semaphore)) {";
+
+ s.op->newline(1) << "if (vm_flags & VM_WRITE) {";
+ s.op->indent(1);
+ s.op->newline() << "sdt_semaphore ++;";
+ s.op->newline() << "#ifdef DEBUG_UTRACE";
+ s.op->newline() << "_stp_dbug (__FUNCTION__,__LINE__, \"+semaphore %#x @ %#lx\\n\", sdt_semaphore, p->sdt_sem_address);";
+ s.op->newline() << "#endif";
+ s.op->newline() << "__access_process_vm (tsk, p->sdt_sem_address, &sdt_semaphore, sizeof (sdt_semaphore), 1);";
+ s.op->newline(-1) << "}";
+ s.op->newline(-1) << "}";
+ s.op->newline(-1) << "}";
+ s.op->newline() << "return 0;";
+ s.op->newline(-1) << "}";
+
s.op->newline() << "static struct stap_utrace_probe stap_utrace_probes[] = {";
s.op->indent(1);
@@ -1055,14 +1126,15 @@ utrace_derived_probe_group::emit_module_exit (systemtap_session& s)
s.op->newline() << "if (p->engine_attached) {";
s.op->newline(1) << "stap_utrace_detach_ops(&p->ops);";
+ // Before writing to the semaphore, we need to check for VM_WRITE access.
s.op->newline() << "if (p->sdt_sem_address) {";
s.op->newline(1) << "size_t sdt_semaphore;";
// XXX p could get registered to more than one task!
- s.op->newline() << "__access_process_vm (p->tsk, p->sdt_sem_address, &sdt_semaphore, sizeof (sdt_semaphore), 0);";
- s.op->newline() << "sdt_semaphore -= 1;";
+ s.op->newline() << "if (__access_process_vm (p->tsk, p->sdt_sem_address, &sdt_semaphore, sizeof (sdt_semaphore), 0) == sizeof (sdt_semaphore)) {";
+ s.op->newline(1) << "sdt_semaphore --;";
s.op->newline() << "__access_process_vm (p->tsk, p->sdt_sem_address, &sdt_semaphore, sizeof (sdt_semaphore), 1);";
s.op->newline(-1) << "}";
-
+ s.op->newline(-1) << "}";
s.op->newline(-1) << "}";
s.op->newline(-1) << "}";
@@ -1094,6 +1166,8 @@ register_tapset_utrace(systemtap_session& s)
->bind(builder);
roots[i]->bind(TOK_SYSCALL)->bind(TOK_RETURN)
->bind(builder);
+ roots[i]->bind_str(TOK_LIBRARY)->bind(TOK_SYSCALL)
+ ->bind(builder);
}
}
diff --git a/tapset/tcpmib.stp b/tapset/tcpmib.stp
index 497fb7dd..aba7837b 100644
--- a/tapset/tcpmib.stp
+++ b/tapset/tcpmib.stp
@@ -99,16 +99,6 @@ function tcpmib_remote_port:long(sk:long)
CATCH_DEREF_FAULT();
%}
-function tcpmib_filter_key:long (sk:long, op:long) {
- // ensure all these functions will build
- if ( tcpmib_get_state(sk) ) return 0
- if ( tcpmib_local_addr(sk) ) return 0
- if ( tcpmib_remote_addr(sk) ) return 0
- if ( tcpmib_local_port(sk) ) return 0
- if ( tcpmib_remote_port(sk) ) return 0
- return op
-}
-
/**
* probe tcpmib.ActiveOpens - Count an active opening of a socket.
* @sk: Pointer to the struct sock being acted on.
@@ -124,6 +114,7 @@ tcpmib.ActiveOpens=kernel.function("tcp_connect").return
sk = $sk;
op = 1;
if ( $return ) next;
+ // definition in tcpipstat.stp
key = tcpmib_filter_key(sk,op);
if ( key ) ActiveOpens[key] += op;
}
diff --git a/tapsets.cxx b/tapsets.cxx
index 51f1ccc9..555a6587 100644
--- a/tapsets.cxx
+++ b/tapsets.cxx
@@ -260,6 +260,7 @@ static const string TOK_PROCESS("process");
static const string TOK_MARK("mark");
static const string TOK_TRACE("trace");
static const string TOK_LABEL("label");
+static const string TOK_LIBRARY("library");
static int query_cu (Dwarf_Die * cudie, void * arg);
static void query_addr(Dwarf_Addr addr, dwarf_query *q);
@@ -349,8 +350,10 @@ struct dwarf_derived_probe: public derived_probe
string module;
string section;
Dwarf_Addr addr;
+ string path;
bool has_return;
bool has_maxactive;
+ bool has_library;
long maxactive_val;
bool access_vars;
@@ -460,7 +463,9 @@ struct base_query
bool has_kernel;
bool has_module;
bool has_process;
+ bool has_library;
string module_val; // has_kernel => module_val = "kernel"
+ string path; // executable path if module is a .so
virtual void handle_query_module() = 0;
};
@@ -478,8 +483,15 @@ base_query::base_query(dwflpp & dw, literal_map_t const & params):
has_process = false;
else
{
+ string library_name;
has_process = get_string_param(params, TOK_PROCESS, module_val);
- if (has_process)
+ has_library = get_string_param (params, TOK_LIBRARY, library_name);
+ if (has_library)
+ {
+ path = find_executable (module_val);
+ module_val = find_executable (library_name, "LD_LIBRARY_PATH");
+ }
+ else if (has_process)
module_val = find_executable (module_val);
}
@@ -2840,11 +2852,13 @@ dwarf_derived_probe::dwarf_derived_probe(const string& funcname,
Dwarf_Die* scope_die /* may be null */)
: derived_probe (q.base_probe, new probe_point(*q.base_loc) /* .components soon rewritten */ ),
module (module), section (section), addr (addr),
+ path (q.path),
has_return (q.has_return),
has_maxactive (q.has_maxactive),
+ has_library (q.has_library),
maxactive_val (q.maxactive_val),
access_vars(false),
- saved_longs(0), saved_strings(0),
+ saved_longs(0), saved_strings(0),
entry_handler(0)
{
if (q.has_process)
@@ -3140,6 +3154,8 @@ dwarf_derived_probe::register_patterns(systemtap_session& s)
register_function_and_statement_variants(root->bind_str(TOK_PROCESS), dw);
root->bind_str(TOK_PROCESS)->bind_str(TOK_FUNCTION)->bind_str(TOK_LABEL)
->bind(dw);
+ root->bind_str(TOK_PROCESS)->bind_str(TOK_LIBRARY)->bind_str(TOK_MARK)
+ ->bind(dw);
root->bind_str(TOK_PROCESS)->bind_str(TOK_MARK)
->bind(dw);
root->bind_str(TOK_PROCESS)->bind_num(TOK_MARK)
@@ -3813,7 +3829,13 @@ sdt_query::init_probe_scn()
bool
sdt_query::get_next_probe()
{
- // Extract probe info from the .probes section
+ // Extract probe info from the .probes section, e.g.
+ // 74657374 5f70726f 62655f32 00000000 test_probe_2....
+ // 50524233 00000000 980c2000 00000000 PRB3...... .....
+ // 01000000 00000000 00000000 00000000 ................
+ // test_probe_2 is probe_name, probe_type is 50524233,
+ // *probe_name (pbe->name) is 980c2000, probe_arg (pbe->arg) is 1
+ // probe_scn_offset is position currently being scanned in .probes
while (probe_scn_offset < pdata->d_size)
{
@@ -3839,6 +3861,8 @@ sdt_query::get_next_probe()
probe_scn_offset += sizeof(__uint32_t);
probe_scn_offset += probe_scn_offset % sizeof(__uint64_t);
pbe = (struct probe_entry*) ((char*)pdata->d_buf + probe_scn_offset);
+ if (pbe->name == 0)
+ return false;
probe_name = (char*)((char*)pdata->d_buf + pbe->name - (char*)probe_scn_addr);
probe_arg = pbe->arg;
if (sess.verbose > 4)
@@ -3983,46 +4007,52 @@ sdt_query::convert_probe (probe *base)
void
sdt_query::convert_location (probe *base, probe_point *location)
{
- switch (probe_type)
- {
- case uprobe_type:
- if (sess.verbose > 3)
- clog << "probe_type == uprobe_type, use statement addr: 0x"
- << hex << probe_arg << dec << endl;
- // process("executable").statement(probe_arg)
- location->components[1]->functor = TOK_STATEMENT;
- location->components[1]->arg = new literal_number(probe_arg);
- break;
+ for (unsigned i = 0; i < location->components.size(); ++i)
+ if (location->components[i]->functor == TOK_MARK)
+ switch (probe_type)
+ {
+ case uprobe_type:
+ if (sess.verbose > 3)
+ clog << "probe_type == uprobe_type, use statement addr: 0x"
+ << hex << probe_arg << dec << endl;
+ // process("executable").statement(probe_arg)
+ location->components[i]->functor = TOK_STATEMENT;
+ location->components[i]->arg = new literal_number(probe_arg);
+ break;
- case kprobe_type:
- if (sess.verbose > 3)
- clog << "probe_type == kprobe_type" << endl;
- // kernel.function("*getegid*")
- location->components[0]->functor = TOK_KERNEL;
- location->components[0]->arg = NULL;
- location->components[1]->functor = TOK_FUNCTION;
- location->components[1]->arg = new literal_string("*getegid*");
- break;
+ case kprobe_type:
+ if (sess.verbose > 3)
+ clog << "probe_type == kprobe_type" << endl;
+ // kernel.function("*getegid*")
+ location->components[i]->functor = TOK_FUNCTION;
+ location->components[i]->arg = new literal_string("*getegid*");
+ break;
- case utrace_type:
- if (sess.verbose > 3)
- clog << "probe_type == utrace_type" << endl;
- // process("executable").syscall
- location->components[1]->functor = "syscall";
- location->components[1]->arg = NULL;
- break;
+ case utrace_type:
+ if (sess.verbose > 3)
+ clog << "probe_type == utrace_type" << endl;
+ // process("executable").syscall
+ location->components[i]->functor = "syscall";
+ location->components[i]->arg = NULL;
+ break;
- default:
- if (sess.verbose > 3)
- clog << "probe_type == use_uprobe_no_dwarf, use label name: "
- << "_stapprobe1_" << mark_name << endl;
- // process("executable").function("*").label("_stapprobe1_MARK_NAME")
- location->components[1]->functor = TOK_FUNCTION;
- location->components[1]->arg = new literal_string("*");
- location->components.push_back(new probe_point::component(TOK_LABEL));
- location->components[2]->arg = new literal_string("_stapprobe1_" + mark_name);
- break;
- }
+ default:
+ if (sess.verbose > 3)
+ clog << "probe_type == use_uprobe_no_dwarf, use label name: "
+ << "_stapprobe1_" << mark_name << endl;
+ // process("executable").function("*").label("_stapprobe1_MARK_NAME")
+ location->components[1]->functor = TOK_FUNCTION;
+ location->components[1]->arg = new literal_string("*");
+ location->components.push_back(new probe_point::component(TOK_LABEL));
+ location->components[2]->arg = new literal_string("_stapprobe1_" + mark_name);
+ break;
+ }
+ else if (location->components[i]->functor == TOK_PROCESS
+ && probe_type == kprobe_type)
+ {
+ location->components[i]->functor = TOK_KERNEL;
+ location->components[i]->arg = NULL;
+ }
base->locations.clear();
base->locations.push_back(location);
@@ -4053,7 +4083,11 @@ dwarf_builder::build(systemtap_session & sess,
}
else if (get_param (parameters, TOK_PROCESS, module_name))
{
- module_name = find_executable (module_name); // canonicalize it
+ string library_name;
+ if (get_param (parameters, TOK_LIBRARY, library_name))
+ module_name = find_executable (library_name, "LD_LIBRARY_PATH");
+ else
+ module_name = find_executable (module_name); // canonicalize it
if (sess.kernel_config["CONFIG_UTRACE"] != string("y"))
throw semantic_error ("process probes not available without kernel CONFIG_UTRACE");
@@ -4557,7 +4591,6 @@ uprobe_derived_probe_group::emit_module_decls (systemtap_session& s)
// too big to embed in the initialized .data stap_uprobe_spec array.
s.op->newline() << "static struct stap_uprobe {";
s.op->newline(1) << "union { struct uprobe up; struct uretprobe urp; };";
- s.op->newline() << "unsigned long sdt_sem_address;"; // relocated to this process
s.op->newline() << "int spec_index;"; // index into stap_uprobe_specs; <0 == free && unregistered
s.op->newline(-1) << "} stap_uprobes [MAXUPROBES];"; // XXX: consider a slab cache or somesuch
s.op->newline() << "DEFINE_MUTEX(stap_uprobes_lock);"; // protects against concurrent registration/unregistration
@@ -4600,6 +4633,8 @@ uprobe_derived_probe_group::emit_module_decls (systemtap_session& s)
}
if (p->section != ".absolute") // ET_DYN
{
+ if (p->has_library && p->sdt_semaphore_addr != 0)
+ s.op->line() << " .procname=\"" << p->path << "\", ";
s.op->line() << " .mmap_callback=&stap_uprobe_mmap_found, ";
s.op->line() << " .munmap_callback=&stap_uprobe_munmap_found, ";
}
@@ -4615,13 +4650,17 @@ uprobe_derived_probe_group::emit_module_decls (systemtap_session& s)
s.op->assert_0_indent();
+ s.op->newline() << "unsigned long sdt_sem_address [] = {";
+ for (unsigned i =0; i<probes.size(); i++)
+ s.op->line() << "0,";
+ s.op->line() << "0};";
s.op->newline() << "static const struct stap_uprobe_spec {"; // NB: read-only structure
s.op->newline(1) << "unsigned tfi;"; // index into stap_uprobe_finders[]
s.op->newline() << "unsigned return_p:1;";
s.op->newline() << "unsigned long address;";
s.op->newline() << "const char *pp;";
s.op->newline() << "void (*ph) (struct context*);";
- s.op->newline() << "unsigned long sdt_sem_address;";
+ s.op->newline() << "unsigned long sdt_sem_offset;";
s.op->newline(-1) << "} stap_uprobe_specs [] = {"; // NB: read-only structure
s.op->indent(1);
for (unsigned i =0; i<probes.size(); i++)
@@ -4637,7 +4676,7 @@ uprobe_derived_probe_group::emit_module_decls (systemtap_session& s)
s.op->line() << " .ph=&" << p->name << ",";
if (p->sdt_semaphore_addr != 0)
- s.op->line() << " .sdt_sem_address=(unsigned long)0x"
+ s.op->line() << " .sdt_sem_offset=(unsigned long)0x"
<< hex << p->sdt_semaphore_addr << dec << "ULL,";
if (p->has_return)
@@ -4706,7 +4745,7 @@ uprobe_derived_probe_group::emit_module_decls (systemtap_session& s)
// unregistration.
s.op->newline();
- s.op->newline() << "static int stap_uprobe_change_plus (struct task_struct *tsk, unsigned long relocation, unsigned long length, const struct stap_uprobe_tf *stf) {";
+ s.op->newline() << "static int stap_uprobe_change_plus (struct task_struct *tsk, unsigned long relocation, unsigned long length, const struct stap_uprobe_tf *stf, unsigned long offset, unsigned long vm_flags) {";
s.op->newline(1) << "int tfi = (stf - stap_uprobe_finders);";
s.op->newline() << "int spec_index;";
@@ -4757,6 +4796,20 @@ uprobe_derived_probe_group::emit_module_decls (systemtap_session& s)
// was full (registration; MAXUPROBES) or that no matching entry was
// found (unregistration; should not happen).
+ s.op->newline() << "if (sups->sdt_sem_offset && sdt_sem_address[spec_index] == 0) {";
+ s.op->indent(1);
+ // If the probe is in the executable itself, the offset *is* the address.
+ s.op->newline() << "if (vm_flags & VM_EXECUTABLE) {";
+ s.op->indent(1);
+ s.op->newline() << "sdt_sem_address[spec_index] = relocation + sups->sdt_sem_offset;";
+ s.op->newline(-1) << "}";
+ // If the probe is in a .so, we have to calculate the address.
+ s.op->newline() << "else {";
+ s.op->indent(1);
+ s.op->newline() << "sdt_sem_address[spec_index] = (relocation - offset) + sups->sdt_sem_offset;";
+ s.op->newline(-1) << "}";
+ s.op->newline(-1) << "}"; // sdt_sem_offset
+
s.op->newline() << "if (slotted_p) {";
s.op->newline(1) << "struct stap_uprobe *sup = & stap_uprobes[i];";
s.op->newline() << "if (sups->return_p) {";
@@ -4770,23 +4823,23 @@ uprobe_derived_probe_group::emit_module_decls (systemtap_session& s)
s.op->newline() << "sup->up.handler = &enter_uprobe_probe;";
s.op->newline() << "rc = register_uprobe (& sup->up);";
- // PR10655: also handle ENABLED semaphore manipulations here
- s.op->newline() << "if (!rc && sups->sdt_sem_address) {";
- s.op->newline(1) << "unsigned short sdt_semaphore;"; // NB: fixed size
- s.op->newline() << "sup->sdt_sem_address = relocation + sups->sdt_sem_address;";
- s.op->newline() << "(void) __access_process_vm (tsk, sup->sdt_sem_address, &sdt_semaphore, sizeof (sdt_semaphore), 0);";
- s.op->newline() << "sdt_semaphore ++;";
- s.op->newline() << "#ifdef DEBUG_UPROBES";
- s.op->newline() << "_stp_dbug (__FUNCTION__,__LINE__, \"+semaphore %#x @ %#lx\\n\", sdt_semaphore, sup->sdt_sem_address);";
- s.op->newline() << "#endif";
-
- s.op->newline() << "(void) __access_process_vm (tsk, sup->sdt_sem_address, &sdt_semaphore, sizeof (sdt_semaphore), 1);";
+ s.op->newline() << "if (sdt_sem_address[spec_index]) {";
+ s.op->newline(1) << "unsigned short sdt_semaphore = 0;"; // NB: fixed size
+ s.op->newline() << "if ( __access_process_vm (tsk, sdt_sem_address[spec_index], &sdt_semaphore, sizeof (sdt_semaphore), 0)) {";
+ // We have an executable or a writeable segment of a .so
+ s.op->newline(1) << "if (vm_flags == 0 || vm_flags & VM_WRITE) {";
+ s.op->newline(1) << "sdt_semaphore ++;";
+ s.op->newline() << "__access_process_vm (tsk, sdt_sem_address[spec_index], &sdt_semaphore, sizeof (sdt_semaphore), 1);";
+ s.op->newline(-1) << "}";
+ s.op->newline(-1) << "}"; // sdt_sem_address
// XXX: error handling in __access_process_vm!
// XXX: need to analyze possibility of race condition
+
s.op->newline(-1) << "}";
s.op->newline(-1) << "}";
- s.op->newline() << "if (rc) {"; // failed to register
+
+ s.op->newline() << "if (rc && !(vm_flags & VM_WRITE)) {"; // failed to register
s.op->newline(1) << "_stp_warn (\"u*probe failed %s[%d] '%s' addr %p rc %d\\n\", tsk->comm, tsk->tgid, sups->pp, (void*)(relocation + sups->address), rc);";
// NB: we need to release this slot, so we need to borrow the mutex temporarily.
s.op->newline() << "mutex_lock (& stap_uprobes_lock);";
@@ -4919,7 +4972,7 @@ uprobe_derived_probe_group::emit_module_decls (systemtap_session& s)
// ET_EXEC events are modeled as if shlib events, but with 0 relocation bases
s.op->newline() << "if (register_p)";
- s.op->newline(1) << "return stap_uprobe_change_plus (tsk, 0, TASK_SIZE, stf);";
+ s.op->newline(1) << "return stap_uprobe_change_plus (tsk, 0, TASK_SIZE, stf, 0, 0);";
s.op->newline(-1) << "else";
s.op->newline(1) << "return stap_uprobe_change_minus (tsk, 0, TASK_SIZE, stf);";
s.op->indent(-1);
@@ -4932,18 +4985,19 @@ uprobe_derived_probe_group::emit_module_decls (systemtap_session& s)
s.op->newline() << "static int stap_uprobe_mmap_found (struct stap_task_finder_target *tgt, struct task_struct *tsk, char *path, unsigned long addr, unsigned long length, unsigned long offset, unsigned long vm_flags) {";
s.op->newline(1) << "const struct stap_uprobe_tf *stf = container_of(tgt, struct stap_uprobe_tf, finder);";
// 1 - shared libraries' executable segments load from offset 0 - ld.so convention
- s.op->newline() << "if (offset != 0) return 0;";
+ // offset != 0 is now allowed so stap_uprobe_change_plus can set a semaphore,
+ // i.e. a static extern, in a shared object
// 2 - the shared library we're interested in
s.op->newline() << "if (path == NULL || strcmp (path, stf->pathname)) return 0;";
- // 3 - mapping should be executable
- s.op->newline() << "if (!(vm_flags & VM_EXEC)) return 0;";
+ // 3 - mapping should be executable or writeable (for semaphore in .so)
+ s.op->newline() << "if (!((vm_flags & VM_EXEC) || (vm_flags & VM_WRITE))) return 0;";
s.op->newline() << "#ifdef DEBUG_TASK_FINDER_VMA";
s.op->newline() << "_stp_dbug (__FUNCTION__,__LINE__, \"+mmap pid %d path %s addr %p length %u offset %p stf %p %p path %s\\n\", "
<< "tsk->tgid, path, (void *) addr, (unsigned)length, (void*) offset, tgt, stf, stf->pathname);";
s.op->newline() << "#endif";
- s.op->newline() << "return stap_uprobe_change_plus (tsk, addr, length, stf);";
+ s.op->newline() << "return stap_uprobe_change_plus (tsk, addr, length, stf, offset, vm_flags);";
s.op->newline(-1) << "}";
s.op->assert_0_indent();
@@ -5021,7 +5075,7 @@ uprobe_derived_probe_group::emit_module_exit (systemtap_session& s)
s.op->newline() << "if (sup->spec_index < 0) continue;"; // free slot
// PR10655: decrement that ENABLED semaphore
- s.op->newline() << "if (sups->sdt_sem_address != 0) {";
+ s.op->newline() << "if (sdt_sem_address[sup->spec_index]) {";
s.op->newline(1) << "unsigned short sdt_semaphore;"; // NB: fixed size
s.op->newline() << "pid_t pid = (sups->return_p ? sup->urp.u.pid : sup->up.pid);";
s.op->newline() << "struct task_struct *tsk;";
@@ -5041,12 +5095,13 @@ uprobe_derived_probe_group::emit_module_exit (systemtap_session& s)
s.op->newline() << "#endif /* 2.6.31 */";
s.op->newline() << "if (tsk) {"; // just in case the thing exited while we weren't watching
- s.op->newline(1) << "(void) __access_process_vm (tsk, sup->sdt_sem_address, &sdt_semaphore, sizeof (sdt_semaphore), 0);";
- s.op->newline() << "sdt_semaphore --;";
+ s.op->newline(1) << "if (__access_process_vm (tsk, sdt_sem_address[sup->spec_index], &sdt_semaphore, sizeof (sdt_semaphore), 0)) {";
+ s.op->newline(1) << "sdt_semaphore --;";
s.op->newline() << "#ifdef DEBUG_UPROBES";
- s.op->newline() << "_stp_dbug (__FUNCTION__,__LINE__, \"-semaphore %#x @ %#lx\\n\", sdt_semaphore, sup->sdt_sem_address);";
+ s.op->newline() << "_stp_dbug (__FUNCTION__,__LINE__, \"-semaphore %#x @ %#lx\\n\", sdt_semaphore, sdt_sem_address[sup->spec_index]);";
s.op->newline() << "#endif";
- s.op->newline() << "(void) __access_process_vm (tsk, sup->sdt_sem_address, &sdt_semaphore, sizeof (sdt_semaphore), 1);";
+ s.op->newline() << "(void) __access_process_vm (tsk, sdt_sem_address[sup->spec_index], &sdt_semaphore, sizeof (sdt_semaphore), 1);";
+ s.op->newline(-1) << "}"; // access_process_vm
// XXX: error handling in __access_process_vm!
// XXX: need to analyze possibility of race condition
s.op->newline(-1) << "}";
diff --git a/testsuite/buildok/tcpmib-all-probes.stp b/testsuite/buildok/tcpmib-all-probes.stp
index 8d1105e1..5b44a99a 100755
--- a/testsuite/buildok/tcpmib-all-probes.stp
+++ b/testsuite/buildok/tcpmib-all-probes.stp
@@ -4,6 +4,13 @@
probe tcpmib.* {}
+// This function is just for test, the real one is
+// in tcpipstat.stp
+function tcpmib_filter_key:long (sk:long, op:long) {
+ if (!sk) return 0
+ return op
+}
+
probe begin{
print(tcpmib_get_state(0) +
tcpmib_local_addr(0) +
diff --git a/testsuite/lib/systemtap.exp b/testsuite/lib/systemtap.exp
index f16facc2..13e6d1a2 100644
--- a/testsuite/lib/systemtap.exp
+++ b/testsuite/lib/systemtap.exp
@@ -175,7 +175,9 @@ proc shutdown_server {} {
}
# Remove the temporary stap script
- exec /bin/rm -fr $net_path
+ if [file exists $net_path] {
+ exec /bin/rm -fr $net_path
+ }
}
proc get_system_info {} {
diff --git a/testsuite/systemtap.apps/mysql.exp b/testsuite/systemtap.apps/mysql.exp
index efeffbae..497949c0 100644
--- a/testsuite/systemtap.apps/mysql.exp
+++ b/testsuite/systemtap.apps/mysql.exp
@@ -4,14 +4,16 @@ set test "mysql"
global env
-if {! [info exists env(SYSTEMTAP_TEST_SDT)]} {
- unsupported "mysql (\"SYSTEMTAP_TEST_SDT\" not in env)"
+if {! [info exists env(SYSTEMTAP_TESTAPPS)] || (
+ ! [string match "tcl" $env(SYSTEMTAP_TESTAPPS)] &&
+ ! [string match "all" $env(SYSTEMTAP_TESTAPPS)])} {
+ untested "$test sdt app"
return
}
########## Create /tmp/stap-mysql.stp ##########
set msdata "[pwd]/stap-mysql"
-set mysqlrelease "mysql-5.4.1-beta"
+set mysqlrelease "mysql-5.4.3-beta"
set mysqldir "[pwd]/mysql/install/"
set testsuite "[pwd]"
@@ -291,6 +293,11 @@ kill \$STAPPID
if \[ ! -r $mysqlrelease.tar.gz \] ; then
wget http://dev.mysql.com/get/Downloads/MySQL-5.4/$mysqlrelease.tar.gz/from/ftp://mirror.services.wisc.edu/mirrors/mysql/
fi
+if \[ ! -r $mysqlrelease.tar.gz \] ; then
+ echo FAIL: wget $mysqlrelease.tar.gz
+ exit
+fi
+
if \[ ! -d mysql/src \] ; then
tar -x -z -f $mysqlrelease.tar.gz
diff --git a/testsuite/systemtap.apps/postgres.exp b/testsuite/systemtap.apps/postgres.exp
index 2d58a54f..b7f522a1 100644
--- a/testsuite/systemtap.apps/postgres.exp
+++ b/testsuite/systemtap.apps/postgres.exp
@@ -4,8 +4,10 @@ set test "postgres"
global env
-if {! [info exists env(SYSTEMTAP_TEST_SDT)]} {
- unsupported "postgres (\"SYSTEMTAP_TEST_SDT\" not in env)"
+if {! [info exists env(SYSTEMTAP_TESTAPPS)] || (
+ ! [string match "tcl" $env(SYSTEMTAP_TESTAPPS)] &&
+ ! [string match "all" $env(SYSTEMTAP_TESTAPPS)])} {
+ untested "$test sdt app"
return
}
diff --git a/testsuite/systemtap.apps/stap-tcl.stp b/testsuite/systemtap.apps/stap-tcl.stp
index d3293b09..db3e3690 100644
--- a/testsuite/systemtap.apps/stap-tcl.stp
+++ b/testsuite/systemtap.apps/stap-tcl.stp
@@ -1,6 +1,6 @@
global counts
-probe process(@1).mark("*") {
+probe process(@1).library(@2).mark("*") {
counts[$$name]<<<1 # PR10878; check also $$parms length
}
diff --git a/testsuite/systemtap.apps/tcl.exp b/testsuite/systemtap.apps/tcl.exp
index bfcf2239..c95fa5e0 100644
--- a/testsuite/systemtap.apps/tcl.exp
+++ b/testsuite/systemtap.apps/tcl.exp
@@ -29,7 +29,8 @@ if {$rc != 0} {
}
set test "stap-tcl.stp compilation"
-set rc [catch {exec stap -DMAXSKIPPED=8024 -t -p4 $srcdir/$subdir/stap-tcl.stp tcl/install/lib/libtcl${tclreleasemajor}.so} out]
+verbose -log "spawn stap -DMAXSKIPPED=8024 -t -p4 $srcdir/$subdir/stap-tcl.stp tcl/install/bin/tclsh${tclreleasemajor} tcl/install/lib/libtcl${tclreleasemajor}.so"
+set rc [catch {exec stap -DMAXSKIPPED=8024 -t -p4 $srcdir/$subdir/stap-tcl.stp tcl/install/bin/tclsh${tclreleasemajor} tcl/install/lib/libtcl${tclreleasemajor}.so} out]
clone_output $out
if {$rc != 0} {
fail $test
@@ -47,7 +48,8 @@ if {![installtest_p]} {
set ok 0
set ko 0
set lines 0
-spawn stap -DMAXSKIPPED=8024 -t -c "tcl/install/bin/tclsh${tclreleasemajor} tcl/src/tests/all.tcl > tcl-test.out" $srcdir/$subdir/stap-tcl.stp tcl/install/lib/libtcl${tclreleasemajor}.so
+verbose -log "spawn stap -DMAXSKIPPED=8024 -t -c \"tcl/install/bin/tclsh${tclreleasemajor} tcl/src/tests/all.tcl > tcl-test.out\" $srcdir/$subdir/stap-tcl.stp tcl/install/bin/tclsh${tclreleasemajor} tcl/install/lib/libtcl${tclreleasemajor}.so"
+spawn stap -DMAXSKIPPED=8024 -t -c "tcl/install/bin/tclsh${tclreleasemajor} tcl/src/tests/all.tcl > tcl-test.out" $srcdir/$subdir/stap-tcl.stp tcl/install/bin/tclsh${tclreleasemajor} tcl/install/lib/libtcl${tclreleasemajor}.so
expect {
-timeout 1000
-re {^OK [^\r\n]*[\r\n]} { incr ok; exp_continue }
diff --git a/testsuite/systemtap.base/dtrace.exp b/testsuite/systemtap.base/dtrace.exp
index f4cac5aa..f68af4f5 100644
--- a/testsuite/systemtap.base/dtrace.exp
+++ b/testsuite/systemtap.base/dtrace.exp
@@ -8,7 +8,7 @@ if {[installtest_p]} {
set dtrace ../dtrace
}
-exec mkdir /tmp/dtrace
+exec mkdir -p /tmp/dtrace
set dpath "/tmp/dtrace/test.d"
set fp [open $dpath "w"]
@@ -23,11 +23,34 @@ provider tstsyscall
"
close $fp
+exec mkdir -p /tmp/dtrace_inc
+set ipath "/tmp/dtrace_inc/dtest.h"
+set $fp [open $ipath "w"]
+puts $fp "
+#define INT16 short
+#define INT32 int
+"
+close $fp
+
+set idpath "/tmp/dtrace/itest.d"
+set $fp [open $idpath "w"]
+puts $fp "
+#include <dtest.h>
+
+provider tstsyscall
+{
+ probe test(INT16 arg1, INT32 arg2, INT32 arg3, INT32 arg4, struct astruct arg5)
+}
+"
+close $fp
+
+set incpath "/tmp/dtrace_inc"
+
# -----------------------------------------------------------------
# test command line option and file handling
verbose -log "$dtrace -G -s $dpath -o XXX.o"
-exec $dtrace -G -s $dpath -o XXX.o
+catch {exec $dtrace -G -s $dpath -o XXX.o}
if {[file exists XXX.o]} then {
pass "dtrace -G -o XXX.o"
} else {
@@ -36,7 +59,7 @@ if {[file exists XXX.o]} then {
exec rm -f XXX.o
verbose -log "$dtrace -G -s $dpath -o XXX"
-exec $dtrace -G -s $dpath -o XXX
+catch {exec $dtrace -G -s $dpath -o XXX}
if {[file exists XXX.o]} then {
pass "dtrace -G -o XXX"
} else {
@@ -45,7 +68,7 @@ if {[file exists XXX.o]} then {
exec rm -f XXX.o
verbose -log "$dtrace -h -s $dpath -o XXX.h"
-exec $dtrace -h -s $dpath -o XXX.h
+catch {exec $dtrace -h -s $dpath -o XXX.h}
if {[file exists XXX.h]} then {
pass "dtrace -h -o XXX.h"
} else {
@@ -54,7 +77,7 @@ if {[file exists XXX.h]} then {
exec rm -f XXX.h
verbose -log "$dtrace -h -s $dpath -o XXX"
-exec $dtrace -h -s $dpath -o XXX
+catch {exec $dtrace -h -s $dpath -o XXX}
if {[file exists XXX]} then {
pass "dtrace -h -o XXX"
} else {
@@ -63,7 +86,7 @@ if {[file exists XXX]} then {
exec rm -f XXX
verbose -log "$dtrace -G -s $dpath -o /tmp/XXX.o"
-exec $dtrace -G -s $dpath -o /tmp/XXX.o
+catch {exec $dtrace -G -s $dpath -o /tmp/XXX.o}
if {[file exists /tmp/XXX.o]} then {
pass "dtrace -G -o /tmp/XXX.o"
} else {
@@ -72,7 +95,7 @@ if {[file exists /tmp/XXX.o]} then {
exec rm -f /tmp/XXX.o
verbose -log "$dtrace -G -s $dpath -o /tmp/XXX"
-exec $dtrace -G -s $dpath -o /tmp/XXX
+catch {exec $dtrace -G -s $dpath -o /tmp/XXX}
if {[file exists /tmp/XXX.o]} then {
pass "dtrace -G -o /tmp/XXX.o"
} else {
@@ -81,7 +104,7 @@ if {[file exists /tmp/XXX.o]} then {
exec rm -f /tmp/XXX.o
verbose -log "$dtrace -h -s $dpath -o /tmp/XXX.h"
-exec $dtrace -h -s $dpath -o /tmp/XXX.h
+catch {exec $dtrace -h -s $dpath -o /tmp/XXX.h}
if {[file exists /tmp/XXX.h]} then {
pass "dtrace -h -o /tmp/XXX.h"
} else {
@@ -90,7 +113,7 @@ if {[file exists /tmp/XXX.h]} then {
exec rm -f /tmp/XXX.h
verbose -log "$dtrace -h -s $dpath -o /tmp/XXX"
-exec $dtrace -h -s $dpath -o /tmp/XXX
+catch {exec $dtrace -h -s $dpath -o /tmp/XXX}
if {[file exists /tmp/XXX]} then {
pass "dtrace -h -o /tmp/XXX"
} else {
@@ -99,7 +122,7 @@ if {[file exists /tmp/XXX]} then {
exec rm -f /tmp/XXX
verbose -log "$dtrace -G -s $dpath"
-exec $dtrace -G -s $dpath
+catch {exec $dtrace -G -s $dpath}
if {[file exists test.o]} then {
pass "dtrace -G"
} else {
@@ -108,7 +131,7 @@ if {[file exists test.o]} then {
exec rm -f test.o
verbose -log "$dtrace -h -s $dpath"
-exec $dtrace -h -s $dpath
+catch {exec $dtrace -h -s $dpath}
if {[file exists test.h]} then {
pass "dtrace -h"
} else {
@@ -118,7 +141,7 @@ exec rm -f test.o
set ok 0
verbose -log "$dtrace -C -h -s $dpath -o XXX.h"
-exec $dtrace -C -h -s $dpath -o XXX.h
+catch {exec $dtrace -C -h -s $dpath -o XXX.h}
spawn cat XXX.h
expect {
"short arg1, int arg2, int arg3, int arg4" {incr ok}
@@ -130,5 +153,28 @@ if { $ok != 0} {
}
exec rm -f XXX.h
-exec /bin/rm -r /tmp/dtrace
+set ok 0
+verbose -log "$dtrace -C -I$incpath -h -s $idpath -o XXX.h"
+catch {exec $dtrace -C -I$incpath -h -s $idpath -o XXX.h}
+spawn cat XXX.h
+expect {
+ "short arg1, int arg2, int arg3, int arg4" {incr ok}
+}
+if { $ok != 0} {
+ pass "dtrace -C -Iincpath -h -o XXX.h"
+} else {
+ fail "dtrace -C -Iincpath -h -o XXX.h"
+}
+exec rm -f XXX.h
+
+verbose -log "$dtrace -I$incpath -G -s $idpath"
+catch {exec $dtrace -G -s $dpath}
+if {[file exists test.o]} then {
+ pass "dtrace -Iincpath -G"
+} else {
+ fail "dtrace -Iincpath -G"
+}
+exec rm -f test.o
+
+exec /bin/rm -r /tmp/dtrace /tmp/dtrace_inc
# -----------------------------------------------------------------
diff --git a/testsuite/systemtap.base/sdt_misc.exp b/testsuite/systemtap.base/sdt_misc.exp
index 4e6f953f..062181a5 100644
--- a/testsuite/systemtap.base/sdt_misc.exp
+++ b/testsuite/systemtap.base/sdt_misc.exp
@@ -3,20 +3,17 @@ set test "sdt_misc"
# Test miscellaneous features of .mark probes
# Compile a C program to use as the user-space probing target
-set sup_srcpath "[pwd]/static_user_markers.c"
-set sup_exepath "[pwd]/static_user_markers.x"
-set sup_sopath "[pwd]/libsdt.so"
-set supcplus_exepath "[pwd]/static_user_markers_cplus.x"
+set sup_srcpath "[pwd]/sdt_misc.c"
+set supcplus_exepath "[pwd]/sdt_misc_cplus.x"
set fp [open $sup_srcpath "w"]
puts $fp "
#include <stdlib.h>
-#define USE_STAP_PROBE 1
-#include \"static_user_markers_.h\"
+#include \"sdt_misc_.h\"
void
bar (int i)
{
- STATIC_USER_MARKERS_TEST_PROBE_2(i);
+ SDT_MISC_TEST_PROBE_2(i);
if (i == 0)
i = 1000;
STAP_PROBE1(static_uprobes,test_probe_2,i);
@@ -28,7 +25,7 @@ baz (int i, char* s)
STAP_PROBE1(static_uprobes,test_probe_0,i);
if (i == 0)
i = 1000;
- STATIC_USER_MARKERS_TEST_PROBE_3(i,s);
+ SDT_MISC_TEST_PROBE_3(i,s);
}
void
@@ -42,7 +39,7 @@ buz (int parm)
struct astruct bstruct = {parm, parm + 1};
if (parm == 0)
parm = 1000;
- DTRACE_PROBE1(static_user_markers,test_probe_4,&bstruct);
+ DTRACE_PROBE1(sdt_misc,test_probe_4,&bstruct);
}
#ifndef NO_MAIN
@@ -57,34 +54,50 @@ main ()
"
close $fp
-set sup_stppath "[pwd]/static_user_markers.stp"
+set sup_stppath "[pwd]/sdt_misc.stp"
set fp [open $sup_stppath "w"]
puts $fp "
-probe process(\"static_user_markers.x\").mark(\"test_probe_0\")
+%( \$# > 1 %?
+probe process(@1).library(@2).mark(\"test_probe_0\")
+%:
+probe process(@1).mark(\"test_probe_0\")
+%)
{
printf(\"In %s probe %#x\\n\", \$\$name, \$arg1)
}
-probe process(\"static_user_markers.x\").mark(\"test_probe_2\")
+%( \$# > 1 %?
+probe process(@1).library(@2).mark(\"test_probe_2\")
+%:
+probe process(@1).mark(\"test_probe_2\")
+%)
{
printf(\"In %s probe %#x\\n\", \$\$name, \$arg1)
}
-probe process(\"static_user_markers.x\").mark(\"test_probe_3\")
+%( \$# > 1 %?
+probe process(@1).library(@2).mark(\"test_probe_3\")
+%:
+probe process(@1).mark(\"test_probe_3\")
+%)
{
printf(\"In %s probe %#x %#x\\n\", \$\$name, \$arg1, \$arg2)
}
-probe process(\"static_user_markers.x\").mark(\"test_probe_4\")
+%( \$# > 1 %?
+probe process(@1).library(@2).mark(\"test_probe_4\")
+%:
+probe process(@1).mark(\"test_probe_4\")
+%)
{
printf(\"In %s dtrace probe %#x %#x\\n\", \$\$name, \$arg1->a, \$arg1->b)
}
"
close $fp
-set sup_dpath "[pwd]/static_user_markers_.d"
-set sup_hpath "[pwd]/static_user_markers_.h"
-set sup_opath "[pwd]/static_user_markers_.o"
+set sup_dpath "[pwd]/sdt_misc_.d"
+set sup_hpath "[pwd]/sdt_misc_.h"
+set sup_opath "[pwd]/sdt_misc_.o"
set fp [open $sup_dpath "w"]
puts $fp "
-provider static_user_markers {
+provider sdt_misc {
probe test_probe_0 ();
probe test_probe_2 (int i);
probe test_probe_3 (int i, char* x);
@@ -130,7 +143,7 @@ set pbtype_mssgs {{uprobe} {utrace} {kprobe}}
for {set i 0} {$i < [llength $pbtype_flags]} {incr i} {
set pbtype_flag [lindex $pbtype_flags $i]
set pbtype_mssg [lindex $pbtype_mssgs $i]
-set testprog "sdt.c.exe.$i"
+set sup_exepath "[pwd]/sdt_misc-$pbtype_mssg.x"
set sup_flags "additional_flags=-I$srcdir/../includes/sys"
set sup_flags "$sup_flags additional_flags=-I$sdtdir"
@@ -163,7 +176,7 @@ if {![utrace_p]} {
set ok 0
verbose -log "spawn stap -c $sup_exepath $sup_stppath"
-spawn stap -c $sup_exepath $sup_stppath
+spawn stap -c $sup_exepath $sup_stppath $sup_exepath
expect {
-timeout 180
-re {In test_probe_2 probe 0x2} { incr ok; exp_continue }
@@ -198,17 +211,17 @@ set ok 0
set fail "types"
verbose -log "spawn stap -c ./sdt_types.x $srcdir/$subdir/sdt_types.stp ./sdt_types.x"
spawn stap -c ./sdt_types.x $srcdir/$subdir/sdt_types.stp ./sdt_types.x
+
expect {
-timeout 180
-re {FAIL: [a-z_]+var} { regexp " .*$" $expect_out(0,string) s;
incr ok; set fail "$fail $s"; exp_continue }
- timeout { fail "$test (timeout) }
+ timeout { fail "$test (timeout)" }
eof { }
}
wait
-set pbtype_mssgs {{uprobe} {utrace} {kprobe}}
if { $ok != 0} {
if { $pbtype_mssg == "uprobe" } {
fail "$test $fail $pbtype_mssg"
@@ -222,7 +235,7 @@ if { $ok != 0} {
# Test probe in shared object
-set sup_srcmainpath "[pwd]/static_user_markers_.c"
+set sup_srcmainpath "[pwd]/sdt_misc_.c"
set fp [open $sup_srcmainpath "w"]
puts $fp "
int
@@ -238,9 +251,11 @@ close $fp
set sup_flags "$sup_flags additional_flags=-shared"
set sup_flags "$sup_flags additional_flags=-fPIC"
set sup_flags "$sup_flags additional_flags=-DNO_MAIN"
+set sup_sopath "[pwd]/libsdt-$pbtype_mssg.so"
+set sup_exepath "[pwd]/sdt_misc-$pbtype_mssg-shared.x"
set res0 [target_compile $sup_srcpath $sup_sopath executable $sup_flags ]
set sup0_flags "additional_flags=-g additional_flags=-Wl,-rpath,[pwd]"
-set sup0_flags "$sup0_flags additional_flags=-L[pwd] additional_flags=-lsdt"
+set sup0_flags "$sup0_flags additional_flags=-L[pwd] additional_flags=-lsdt-$pbtype_mssg"
set res [target_compile $sup_srcmainpath $sup_exepath executable $sup0_flags ]
if { $res0 != "" || $res != "" } {
verbose "target_compile failed: $res0 $res" 2
@@ -254,21 +269,28 @@ if { $res0 != "" || $res != "" } {
}
set ok 0
-# verbose -log "stap -c $sup_exepath -e probe process(\"$sup_sopath\").mark(\"test_probe_2\") {printf(\"In %s probe %#x\\n\", \$\$name, \$arg1)}"
-# spawn stap -c $sup_exepath -e "probe process(\"$sup_sopath\").mark(\"test_probe_2\") {printf(\"In %s probe %#x\\n\", \$\$name, \$arg1)}"
-# expect {
-# -timeout 180
-# -re {In test_probe_2 probe 0x2} { incr ok; exp_continue }
-# timeout { fail "$test (timeout)" }
-# eof { }
-# }
-# wait
-
-if {$ok == 2} {
+verbose -log "spawn stap -c $sup_exepath $sup_stppath $sup_exepath $sup_sopath"
+if { $pbtype_mssg != "kprobe" } {
+ spawn stap -c $sup_exepath $sup_stppath $sup_exepath $sup_sopath
+} else {
+ spawn stap -c $sup_exepath $sup_stppath $sup_sopath
+}
+expect {
+ -timeout 180
+ -re {In test_probe_2 probe 0x2} { incr ok; exp_continue }
+ -re {In test_probe_0 probe 0x3} { incr ok; exp_continue }
+ -re {In test_probe_3 probe 0x3 0x[0-9a-f][0-9a-f]} { incr ok; exp_continue }
+ -re {In test_probe_4 dtrace probe 0x4 0x5} { incr ok; exp_continue }
+ timeout { fail "$test (timeout)" }
+ eof { }
+}
+
+wait
+
+if {$ok == 5} {
pass "$test shared $pbtype_mssg"
} else {
-# fail "$test shared ($ok) $pbtype_mssg"
- xfail "$test shared ($ok) $pbtype_mssg"
+ fail "$test ($ok) shared $pbtype_mssg"
}
# Test .mark probe wildcard matching
@@ -288,9 +310,12 @@ if { $ok == 45 } {
fail "$test wildcard ($ok) $pbtype_mssg"
}
+if { $verbose == 0 } {
+ catch {exec rm -f libsdt-$pbtype_mssg.so sdt_misc-$pbtype_mssg.x sdt_misc-$pbtype_mssg-shared.x }
+}
# for {set i 0}
}
if { $verbose == 0 } {
-catch {exec rm -f $sup_srcpath $sup_exepath $sup_sopath $supcplus_exepath $sup_dpath $sup_hpath $sup_opath $sup_stppath $sdt_types.x $sup_srcmainpath}
+ catch {exec rm -f sdt_misc_.c sdt_misc.c sdt_misc_.d sdt_misc_.h sdt_misc_.o sdt_misc.stp sdt_types.x}
}
diff --git a/testsuite/systemtap.examples/general/grapher.stp b/testsuite/systemtap.examples/general/grapher.stp
index e8655b37..baeeff5c 100755
--- a/testsuite/systemtap.examples/general/grapher.stp
+++ b/testsuite/systemtap.examples/general/grapher.stp
@@ -8,7 +8,7 @@ printf ("%%DataSet:pty 50 0000ff discreet\n");
printf ("cpu %%Title:CPU utilization\n");
printf ("cpu %%XAxisTitle:Time\n");
printf ("cpu %%YAxisTitle:Percent\n");
-
+printf ("%%LineEnd:0\n");
}
# CPU utilization
@@ -26,17 +26,27 @@ function qsq_util_reset(q) {
probe timer.ms(100) { # collect utilization percentages frequently
foreach (q in qnames)
- printf("cpu %d %d\n", gettimeofday_ms(), qsq_util_reset(q))
+ printf("cpu %d %d%c", gettimeofday_ms(), qsq_util_reset(q), 0)
}
probe kernel.function("kbd_event") {
if ($event_type == 1 && $value)
- printf("kbd %d %d\n", gettimeofday_ms(), $event_code)
+ printf("kbd %d %d\n0x%x%c", gettimeofday_ms(), $event_code, $event_code, 0)
}
probe kernel.function("pty_write") {
- if (count > 0)
- printf("pty %d %.5s\n", gettimeofday_ms(), buf)
+ if ($count > 0) {
+ printf("pty %d ", gettimeofday_ms())
+ str = kernel_string($buf)
+ for (i = 0; i < $count; ++i) {
+ if (i > 1)
+ printf("\n")
+ # yes it's gross
+ c = substr(str, i, 1)
+ printf("%s", text_str(c))
+ }
+ printf("%c", 0)
+ }
}
diff --git a/util.cxx b/util.cxx
index bffeb04e..736e5a30 100644
--- a/util.cxx
+++ b/util.cxx
@@ -244,7 +244,7 @@ tokenize(const string& str, vector<string>& tokens,
// same policy as execvp(). A program name not containing a slash
// will be searched along the $PATH.
-string find_executable(const string& name)
+string find_executable(const string& name, const string& env_path)
{
string retpath;
@@ -259,7 +259,7 @@ string find_executable(const string& name)
}
else // Nope, search $PATH.
{
- char *path = getenv("PATH");
+ char *path = getenv(env_path.c_str());
if (path)
{
// Split PATH up.
diff --git a/util.h b/util.h
index bb6c71bc..8fc64cbd 100644
--- a/util.h
+++ b/util.h
@@ -15,7 +15,8 @@ int remove_file_or_dir(const char *dir);
bool in_group_id (gid_t target_gid);
void tokenize(const std::string& str, std::vector<std::string>& tokens,
const std::string& delimiters);
-std::string find_executable(const std::string& name);
+std::string find_executable(const std::string& name,
+ const std::string& env_path = "PATH");
const std::string cmdstr_quoted(const std::string& cmd);
std::string git_revision(const std::string& path);
int stap_system(int verbose, const std::string& command);