diff options
author | Dave Brolley <brolley@redhat.com> | 2009-12-14 16:39:01 -0500 |
---|---|---|
committer | Dave Brolley <brolley@redhat.com> | 2009-12-14 16:39:01 -0500 |
commit | b8f1753c091d3f75ea4a71bfb709d8e50780d3fb (patch) | |
tree | ea5e83a0556d4df262a2b4f9b26065dc312a8a1e | |
parent | 61b21980212779b5a35f6a196842bdca55a3ced6 (diff) | |
parent | 958c58e8231563e9349e4d8ea56c04c25e1501c0 (diff) | |
download | systemtap-steved-b8f1753c091d3f75ea4a71bfb709d8e50780d3fb.tar.gz systemtap-steved-b8f1753c091d3f75ea4a71bfb709d8e50780d3fb.tar.xz systemtap-steved-b8f1753c091d3f75ea4a71bfb709d8e50780d3fb.zip |
Merge branch 'master' of ssh://sources.redhat.com/git/systemtap
41 files changed, 2025 insertions, 713 deletions
@@ -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 @@ -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) + } } @@ -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. @@ -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); |