From f669d095ba7fe5a623b31abc05b4f6664059803b Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Mon, 7 Dec 2009 19:19:45 +0100 Subject: add copyright and license to grapher files --- grapher/grapher.cxx | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'grapher/grapher.cxx') diff --git a/grapher/grapher.cxx b/grapher/grapher.cxx index 969bc762..1e5e30b8 100644 --- a/grapher/grapher.cxx +++ b/grapher/grapher.cxx @@ -1,3 +1,11 @@ +// systemtap grapher +// Copyright (C) 2009 Red Hat Inc. +// +// This file is part of systemtap, and is free software. You can +// redistribute it and/or modify it under the terms of the GNU General +// Public License (GPL); either version 2, or (at your option) any +// later version. + #include "GraphWidget.hxx" #include "StapParser.hxx" -- cgit From 72a9e5a2d3788f5465eb5e1610f2402744054f2e Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Tue, 8 Dec 2009 11:06:04 +0100 Subject: tiny refactoring of signal and pipe code --- grapher/grapher.cxx | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) (limited to 'grapher/grapher.cxx') diff --git a/grapher/grapher.cxx b/grapher/grapher.cxx index 1e5e30b8..567b0405 100644 --- a/grapher/grapher.cxx +++ b/grapher/grapher.cxx @@ -227,19 +227,14 @@ int StapLauncher::launch() &ChildDeathReader::ioCallback), signalPipe[0], Glib::IO_IN); } + struct sigaction action; + action.sa_sigaction = handleChild; + sigemptyset(&action.sa_mask); + action.sa_flags = SA_SIGINFO | SA_NOCLDSTOP; + sigaction(SIGCLD, &action, 0); } - struct sigaction action; - action.sa_sigaction = handleChild; - sigemptyset(&action.sa_mask); - action.sa_flags = SA_SIGINFO | SA_NOCLDSTOP; - sigaction(SIGCLD, &action, 0); int pipefd[4]; - if (pipe(&pipefd[0]) < 0) - { - std::perror("pipe"); - exit(1); - } - if (pipe(&pipefd[2]) < 0) + if (pipe(&pipefd[0]) < 0 || pipe(&pipefd[2]) < 0) { std::perror("pipe"); exit(1); -- cgit From cfd482078cd4805076cc6fd7e4e8642b97a03b25 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Tue, 8 Dec 2009 12:44:03 +0100 Subject: refactor list of data sets out of GraphWidget into a global data structure. Also, add a sigc signal for broadcasting data set changes. * grapher/GraphData.hxx (GraphDataList) new typedef (getGraphData, graphDataSignal): new functions * grapher/Graph.hxx (DatasetList): remove typedef in favor of systemtap::GraphDataList * grapher/GraphWidget.cxx (GraphWidget, onGraphDataChanged): Use graphDataSignal to notice new data sets that need to be added. Use the global data set list instead of one embedded in GraphWidget. * grapher/StapParser.hxx: delete the _widget member; use signals to broadcast that there are new data sets instead of calling into the widget. --- grapher/grapher.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'grapher/grapher.cxx') diff --git a/grapher/grapher.cxx b/grapher/grapher.cxx index 567b0405..4769877f 100644 --- a/grapher/grapher.cxx +++ b/grapher/grapher.cxx @@ -161,7 +161,7 @@ public: void cleanUp(); tr1::shared_ptr makeStapParser() { - tr1::shared_ptr result(new StapParser(_win, _widget)); + tr1::shared_ptr result(new StapParser(_win)); _parsers.push_back(ParserInstance(-1, result)); return result; } @@ -274,7 +274,7 @@ int StapLauncher::launch() } _exit(1); } - tr1::shared_ptr sp(new StapParser(_win, _widget)); + tr1::shared_ptr sp(new StapParser(_win)); _parsers.push_back(ParserInstance(childPid, sp)); sp->setErrFd(pipefd[2]); sp->setInFd(pipefd[0]); -- cgit From 8cabc8bcdcd22e8726734f1a18f23ed7d9c19e9f Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Tue, 8 Dec 2009 23:17:47 +0100 Subject: grapher: start of a dialog for displaying active stap processes The names of active scripts are displayed in a list; mock buttons suggest being able to stop and restart them. * grapher/processwindow.glade: new file * grapher/Makefile.am: add processwindow.glade to installed files * grapher/StapParser.hxx (StapProcess): new class (StapParser): factor out members that are now in StapProcess (ioCallback): Use the new childDied signal instead of aborting the whole grapher when a child dies. * grapher/grapher.cxx (ProcModelColumns, ProcWindow): classes for displaying stap process window. (GrapherWindow::on_menu_proc_window): new function --- grapher/grapher.cxx | 156 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 126 insertions(+), 30 deletions(-) (limited to 'grapher/grapher.cxx') diff --git a/grapher/grapher.cxx b/grapher/grapher.cxx index 4769877f..2a9a617b 100644 --- a/grapher/grapher.cxx +++ b/grapher/grapher.cxx @@ -39,6 +39,7 @@ #include using namespace std; +using namespace tr1; using namespace systemtap; @@ -159,22 +160,33 @@ public: } int launch(); void cleanUp(); - tr1::shared_ptr makeStapParser() + shared_ptr makeStapParser() { - tr1::shared_ptr result(new StapParser(_win)); - _parsers.push_back(ParserInstance(-1, result)); + shared_ptr result(new StapParser); + _parsers.push_back(result); return result; } +private: + struct pidPred + { + pidPred(pid_t pid_) : pid(pid_) {} + bool operator()(const shared_ptr& parser) const + { + return parser->getPid() == pid; + } + pid_t pid; + }; +public: pid_t reap() { + using namespace boost; pid_t pid = ChildDeathReader::reap(); if (pid < 0) return pid; ParserList::iterator itr - = find_if(_parsers.begin(), _parsers.end(), - boost::bind(&ParserInstance::childPid, _1) == pid); + = find_if(_parsers.begin(), _parsers.end(), pidPred(pid)); if (itr != _parsers.end()) - itr->childPid = -1; + (*itr)->setProcess(tr1::shared_ptr()); return pid; } void killAll() @@ -183,10 +195,12 @@ public: itr != end; ++itr) { - if (itr->childPid >= 0) - kill(itr->childPid, SIGTERM); + if ((*itr)->getPid() >= 0) + kill((*itr)->getPid(), SIGTERM); } } + typedef vector > ParserList; + ParserList _parsers; protected: char** _argv; string _stapArgs; @@ -196,18 +210,6 @@ protected: ChildDeathReader::Callback* _deathCallback; Gtk::Window* _win; GraphWidget* _widget; - struct ParserInstance - { - ParserInstance() : childPid(-1) {} - ParserInstance(int childPid_, tr1::shared_ptr stapParser_) - : childPid(childPid_), stapParser(stapParser_) - { - } - pid_t childPid; - tr1::shared_ptr stapParser; - }; - typedef vector ParserList; - ParserList _parsers; }; int StapLauncher::launch() @@ -274,8 +276,18 @@ int StapLauncher::launch() } _exit(1); } - tr1::shared_ptr sp(new StapParser(_win)); - _parsers.push_back(ParserInstance(childPid, sp)); + tr1::shared_ptr sp(new StapParser); + shared_ptr proc(new StapProcess(childPid)); + if (_argv) + proc->argv = _argv; + else + { + proc->stapArgs = _stapArgs; + proc->script = _script; + proc->scriptArgs = _scriptArgs; + } + sp->setProcess(proc); + _parsers.push_back(sp); sp->setErrFd(pipefd[2]); sp->setInFd(pipefd[0]); Glib::signal_io().connect(sigc::mem_fun(sp.get(), @@ -305,21 +317,22 @@ void StapLauncher::cleanUp() itr != end; ++itr) { - if (itr->childPid > 0) - kill(itr->childPid, SIGTERM); + pid_t childPid = (*itr)->getPid(); + if (childPid > 0) + kill(childPid, SIGTERM); int status; pid_t killedPid = -1; if ((killedPid = wait(&status)) == -1) { std::perror("wait"); } - else if (killedPid != itr->childPid) + else if (killedPid != childPid) { std::cerr << "wait: killed Pid " << killedPid << " != child Pid " - << itr->childPid << "\n"; + << childPid << "\n"; } else if (_deathCallback) - _deathCallback->childDied(itr->childPid); + _deathCallback->childDied(childPid); } } @@ -338,6 +351,58 @@ private: Gtk::Entry* _scriptArgEntry; }; +class ProcModelColumns : public Gtk::TreeModelColumnRecord +{ +public: + ProcModelColumns() + { + add(_scriptName); + add(_proc); + } + Gtk::TreeModelColumn _scriptName; + Gtk::TreeModelColumn > _proc; +}; + +class ProcWindow +{ +public: + ProcWindow(); + ProcModelColumns _modelColumns; + Glib::RefPtr _xml; + Gtk::Window* _window; + Gtk::TreeView* _dataTreeView; + Glib::RefPtr _listStore; + void onClose(); +}; + +ProcWindow::ProcWindow() +{ + try + { + _xml = Gnome::Glade::Xml::create(PKGDATADIR "/processwindow.glade"); + _xml->get_widget("window1", _window); + _xml->get_widget("treeview1", _dataTreeView); + + } + catch (const Gnome::Glade::XmlError& ex ) + { + std::cerr << ex.what() << std::endl; + throw; + } + _listStore = Gtk::ListStore::create(_modelColumns); + _dataTreeView->set_model(_listStore); + _dataTreeView->append_column("Script", _modelColumns._scriptName); + Gtk::Button* button = 0; + _xml->get_widget("button5", button); + button->signal_clicked().connect(sigc::mem_fun(*this, &ProcWindow::onClose), + false); +} + +void ProcWindow::onClose() +{ + _window->hide(); +} + class GrapherWindow : public Gtk::Window, public ChildDeathReader::Callback { public: @@ -355,25 +420,28 @@ public: protected: virtual void on_menu_file_quit(); virtual void on_menu_script_start(); + virtual void on_menu_proc_window(); void addGraph(); // menu support Glib::RefPtr m_refUIManager; Glib::RefPtr m_refActionGroup; GraphicalStapLauncher* _graphicalLauncher; - + shared_ptr _procWindow; }; + GrapherWindow::GrapherWindow() + : _procWindow(new ProcWindow) { set_title("systemtap grapher"); add(m_Box); - + //Create actions for menus and toolbars: m_refActionGroup = Gtk::ActionGroup::create(); //File menu: m_refActionGroup->add(Gtk::Action::create("FileMenu", "File")); - m_refActionGroup->add(Gtk::Action::create("StartScript", "Start script"), + m_refActionGroup->add(Gtk::Action::create("StartScript", "Start script..."), sigc::mem_fun(*this, &GrapherWindow::on_menu_script_start)); m_refActionGroup->add(Gtk::Action::create("AddGraph", "Add graph"), @@ -381,6 +449,12 @@ GrapherWindow::GrapherWindow() m_refActionGroup->add(Gtk::Action::create("FileQuit", Gtk::Stock::QUIT), sigc::mem_fun(*this, &GrapherWindow::on_menu_file_quit)); + // Window menu + m_refActionGroup->add(Gtk::Action::create("WindowMenu", "Window")); + m_refActionGroup->add(Gtk::Action::create("ProcessWindow", + "Stap processes..."), + sigc::mem_fun(*this, + &GrapherWindow::on_menu_proc_window)); m_refUIManager = Gtk::UIManager::create(); m_refUIManager->insert_action_group(m_refActionGroup); @@ -394,6 +468,9 @@ GrapherWindow::GrapherWindow() " " " " " " + " " + " " + " " " " ""; try @@ -425,6 +502,25 @@ void GrapherWindow::on_menu_script_start() _graphicalLauncher->runDialog(); } + +void GrapherWindow::on_menu_proc_window() +{ + _procWindow->_listStore->clear(); + for (StapLauncher::ParserList::iterator spitr + = _graphicalLauncher->_parsers.begin(), + end = _graphicalLauncher->_parsers.end(); + spitr != end; + ++spitr) + { + shared_ptr sp = (*spitr)->getProcess(); + Gtk::TreeModel::iterator litr = _procWindow->_listStore->append(); + Gtk::TreeModel::Row row = *litr; + if (sp) + row[_procWindow->_modelColumns._scriptName] = sp->script; + } + _procWindow->_window->show(); +} + void GrapherWindow::childDied(int pid) { hide(); -- cgit From 3e1613e1f7ab589089e8ed5a504330bb9cb128db Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Wed, 9 Dec 2009 22:09:39 +0100 Subject: show the status of stap processes in the process window Also, the "kill" button now works. * grapher/StapParser.hxx (_ioConnection, _errIoConnection): new members for sigc connections. (initIo): New function (parsers, parserListChangedSignal): new variables * grapher/StapParser.cxx (ioCallback): disconnect signals when child dies (initIo): new function * grapher/grapher.cxx (StapLauncher): eliminate death callback in favor of childDied signal. Use global parsers list (ProcWindow::_listSelection): new member (ProcWindow::show, hide, onParserListChanged, onSelectionChanged, onKill): new functions (ProcWindow::ProcWindow): Set up cell renderer for status icon * grapher/processwindow.glade: labels for display script and stap arguments --- grapher/grapher.cxx | 167 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 113 insertions(+), 54 deletions(-) (limited to 'grapher/grapher.cxx') diff --git a/grapher/grapher.cxx b/grapher/grapher.cxx index 2a9a617b..00670fe0 100644 --- a/grapher/grapher.cxx +++ b/grapher/grapher.cxx @@ -106,14 +106,14 @@ private: class StapLauncher : public ChildDeathReader { public: - StapLauncher() : _argv(0), _childPid(-1), _deathCallback(0) {} + StapLauncher() : _argv(0), _childPid(-1) {} StapLauncher(char** argv) - : _argv(argv), _childPid(-1), _deathCallback(0) + : _argv(argv), _childPid(-1) { } StapLauncher(const string& stapArgs, const string& script, const string& scriptArgs) - : _childPid(-1), _deathCallback(0), _win(0), _widget(0) + : _childPid(-1), _win(0), _widget(0) { setArgs(stapArgs, script, scriptArgs); } @@ -149,10 +149,7 @@ public: _script.clear(); _scriptArgs.clear(); } - void setDeathCallback(ChildDeathReader::Callback* callback) - { - _deathCallback = callback; - } + void setWinParams(Gtk::Window* win, GraphWidget* widget) { _win = win; @@ -163,7 +160,8 @@ public: shared_ptr makeStapParser() { shared_ptr result(new StapParser); - _parsers.push_back(result); + parsers.push_back(result); + parserListChangedSignal().emit(); return result; } private: @@ -184,14 +182,22 @@ public: if (pid < 0) return pid; ParserList::iterator itr - = find_if(_parsers.begin(), _parsers.end(), pidPred(pid)); - if (itr != _parsers.end()) - (*itr)->setProcess(tr1::shared_ptr()); + = find_if(parsers.begin(), parsers.end(), pidPred(pid)); + if (itr != parsers.end()) + { + tr1::shared_ptr sp = (*itr)->getProcess(); + if (sp) + { + sp->pid = -1; + parserListChangedSignal().emit(); + } + } + childDiedSignal().emit(pid); return pid; } void killAll() { - for (ParserList::iterator itr = _parsers.begin(), end = _parsers.end(); + for (ParserList::iterator itr = parsers.begin(), end = parsers.end(); itr != end; ++itr) { @@ -199,15 +205,12 @@ public: kill((*itr)->getPid(), SIGTERM); } } - typedef vector > ParserList; - ParserList _parsers; protected: char** _argv; string _stapArgs; string _script; string _scriptArgs; int _childPid; - ChildDeathReader::Callback* _deathCallback; Gtk::Window* _win; GraphWidget* _widget; }; @@ -287,17 +290,9 @@ int StapLauncher::launch() proc->scriptArgs = _scriptArgs; } sp->setProcess(proc); - _parsers.push_back(sp); - sp->setErrFd(pipefd[2]); - sp->setInFd(pipefd[0]); - Glib::signal_io().connect(sigc::mem_fun(sp.get(), - &StapParser::errIoCallback), - pipefd[2], - Glib::IO_IN); - Glib::signal_io().connect(sigc::mem_fun(sp.get(), - &StapParser::ioCallback), - pipefd[0], - Glib::IO_IN | Glib::IO_HUP); + parsers.push_back(sp); + parserListChangedSignal().emit(); + sp->initIo(pipefd[0], pipefd[2]); return childPid; } @@ -313,7 +308,7 @@ void StapLauncher::cleanUp() char buf; while (read(signalPipe[0], &buf, 1) > 0) reap(); - for (ParserList::iterator itr = _parsers.begin(), end = _parsers.end(); + for (ParserList::iterator itr = parsers.begin(), end = parsers.end(); itr != end; ++itr) { @@ -331,8 +326,10 @@ void StapLauncher::cleanUp() std::cerr << "wait: killed Pid " << killedPid << " != child Pid " << childPid << "\n"; } - else if (_deathCallback) - _deathCallback->childDied(childPid); + else + { + childDiedSignal().emit(childPid); + } } } @@ -356,13 +353,16 @@ class ProcModelColumns : public Gtk::TreeModelColumnRecord public: ProcModelColumns() { + add(_iconName); add(_scriptName); add(_proc); } + Gtk::TreeModelColumn _iconName; Gtk::TreeModelColumn _scriptName; Gtk::TreeModelColumn > _proc; }; +// This should probably be a Gtk window, with the appropriate glade magic class ProcWindow { public: @@ -372,10 +372,20 @@ public: Gtk::Window* _window; Gtk::TreeView* _dataTreeView; Glib::RefPtr _listStore; + Glib::RefPtr _listSelection; void onClose(); + void show(); + void hide(); + void onParserListChanged(); + void onSelectionChanged(); + void onKill(); +private: + bool _open; + void refresh(); }; ProcWindow::ProcWindow() + : _open(false) { try { @@ -390,12 +400,27 @@ ProcWindow::ProcWindow() throw; } _listStore = Gtk::ListStore::create(_modelColumns); - _dataTreeView->set_model(_listStore); + _dataTreeView->set_model(_listStore); + // Display a nice icon for the state of the process + Gtk::CellRendererPixbuf* cell = Gtk::manage(new Gtk::CellRendererPixbuf); + _dataTreeView->append_column("State", *cell); + Gtk::TreeViewColumn* column = _dataTreeView->get_column(0); + if (column) + column->add_attribute(cell->property_icon_name(), _modelColumns._iconName); _dataTreeView->append_column("Script", _modelColumns._scriptName); Gtk::Button* button = 0; _xml->get_widget("button5", button); button->signal_clicked().connect(sigc::mem_fun(*this, &ProcWindow::onClose), false); + _xml->get_widget("button1", button); + button->signal_clicked().connect(sigc::mem_fun(*this, &ProcWindow::onKill), + false); + parserListChangedSignal() + .connect(sigc::mem_fun(*this, &ProcWindow::onParserListChanged)); + _listSelection = _dataTreeView->get_selection(); + _listSelection->signal_changed() + .connect(sigc::mem_fun(*this, &ProcWindow::onSelectionChanged)); + } void ProcWindow::onClose() @@ -403,6 +428,63 @@ void ProcWindow::onClose() _window->hide(); } +void ProcWindow::show() +{ + _open = true; + refresh(); + _window->show(); + +} + +void ProcWindow::hide() +{ + _open = false; + _window->hide(); +} + +void ProcWindow::refresh() +{ + _listStore->clear(); + for (ParserList::iterator spitr = parsers.begin(), end = parsers.end(); + spitr != end; + ++spitr) + { + shared_ptr sp = (*spitr)->getProcess(); + if (sp) + { + Gtk::TreeModel::iterator litr = _listStore->append(); + Gtk::TreeModel::Row row = *litr; + row[_modelColumns._iconName] = sp->pid >= 0 ? "gtk-yes" : "gtk-no"; + row[_modelColumns._scriptName] = sp->script; + row[_modelColumns._proc] = sp; + } + } +} + +void ProcWindow::onParserListChanged() +{ + if (_open) + { + refresh(); + _window->queue_draw(); + } +} + +void ProcWindow::onSelectionChanged() +{ +} + +void ProcWindow::onKill() +{ + Gtk::TreeModel::iterator itr = _listSelection->get_selected(); + if (!itr) + return; + Gtk::TreeModel::Row row = *itr; + shared_ptr proc = row[_modelColumns._proc]; + if (proc->pid >= 0) + kill(proc->pid, SIGTERM); +} + class GrapherWindow : public Gtk::Window, public ChildDeathReader::Callback { public: @@ -411,7 +493,6 @@ public: Gtk::VBox m_Box; Gtk::ScrolledWindow scrolled; GraphWidget w; - void childDied(int pid); void setGraphicalLauncher(GraphicalStapLauncher* launcher) { _graphicalLauncher = launcher; @@ -505,30 +586,9 @@ void GrapherWindow::on_menu_script_start() void GrapherWindow::on_menu_proc_window() { - _procWindow->_listStore->clear(); - for (StapLauncher::ParserList::iterator spitr - = _graphicalLauncher->_parsers.begin(), - end = _graphicalLauncher->_parsers.end(); - spitr != end; - ++spitr) - { - shared_ptr sp = (*spitr)->getProcess(); - Gtk::TreeModel::iterator litr = _procWindow->_listStore->append(); - Gtk::TreeModel::Row row = *litr; - if (sp) - row[_procWindow->_modelColumns._scriptName] = sp->script; - } - _procWindow->_window->show(); + _procWindow->show(); } -void GrapherWindow::childDied(int pid) -{ - hide(); -} - - - - int main(int argc, char** argv) { Gtk::Main app(argc, argv); @@ -553,7 +613,6 @@ int main(int argc, char** argv) else if (argc > 1) { launcher.setArgv(argv + 1); - launcher.setDeathCallback(&win); launcher.launch(); } Gtk::Main::run(win); -- cgit From f8e5918969fb63a6148c906025e4ec1f010ca6c6 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Thu, 10 Dec 2009 13:13:30 +0100 Subject: grapher: change SIGCHLD handling and exit cleanup The signal handler now calls waitpid() and writes the pid and status to its pipe. * grapher.cxx (ChildInfo): new class (handleChild): wait for zombies and write their status down the pipe. (ChildDeathReader::ioCallback): Read dead child's pid from signal handler pipe and emit signal. (ChildDeathReader::reap): delete (ChildDeathReader::Callback): delete (StapLauncher::StapLauncher): Connect to childDied signal. (StapLauncher setWinParams, reap, cleanUp): delete (onChildDied): new function that updates the parsers list when a child dies. (GrapherWindow): Remove ChildDeathReader::Callback base class (GrapherWindow::onParserListChanged): New function; exits if program is quitting and no parsers are left. (on_menu_file_quit): Kill all children; don't hide (and exit) unless there are no parsers. (main): Don't do any cleanup after Gtk loop exits. --- grapher/grapher.cxx | 210 ++++++++++++++++++++++++---------------------------- 1 file changed, 95 insertions(+), 115 deletions(-) (limited to 'grapher/grapher.cxx') diff --git a/grapher/grapher.cxx b/grapher/grapher.cxx index 00670fe0..8e7a4dba 100644 --- a/grapher/grapher.cxx +++ b/grapher/grapher.cxx @@ -46,59 +46,78 @@ using namespace systemtap; // magic for noticing that the child stap process has died. int signalPipe[2] = {-1, -1}; +struct ChildInfo + { + pid_t pid; + int waitInfo; +}; + extern "C" -{ - void handleChild(int signum, siginfo_t* info, void* context) - { - char buf[1]; - ssize_t err; - buf[0] = 1; - err = write(signalPipe[1], buf, 1); - } + { + void handleChild(int signum, siginfo_t* info, void* context) + { + struct ChildInfo childInfo; + ssize_t err; + + // Loop doing waitpid because the SIGCHLD signal isn't queued; + // multiple signals might get lost. If we get an error because + // there are no zombie children (return value <= 0 because of + // WNOHANG or no children exist at all), assume that an earlier + // invocation of handleChild already cleaned them up. + while ((childInfo.pid = waitpid(-1, &childInfo.waitInfo, WNOHANG))) + { + if (childInfo.pid < 0 && errno != ECHILD) + { + char errbuf[256]; + strerror_r(errno, errbuf, sizeof(errbuf)); + err = write(STDERR_FILENO, errbuf, strlen(errbuf)); + err = write(STDERR_FILENO, "\n", 1); + return; + } + else if (childInfo.pid > 0) + { + err = write(signalPipe[1], &childInfo, sizeof(childInfo)); + } + else + return; + } + } } // Waits for a gtk I/O signal, indicating that a child has died, then // performs an action class ChildDeathReader + { + public: + ChildDeathReader() : sigfd(-1) {} + ChildDeathReader(int sigfd_) : sigfd(sigfd_) {} + int getSigfd() { return sigfd; } + void setSigfd(int sigfd_) { sigfd = sigfd_; } + + bool ioCallback(Glib::IOCondition ioCondition) + { + if ((ioCondition & Glib::IO_IN) == 0) + return true; + ChildInfo info; + if (read(sigfd, &info, sizeof(info)) < static_cast(sizeof(info))) + cerr << "couldn't read ChildInfo from signal handler\n"; + else + childDiedSignal().emit(info.pid); + return true; + } +private: + int sigfd; +}; + +struct PidPred { -public: - struct Callback - { - virtual ~Callback() {} - virtual void childDied(int pid) {} - }; - ChildDeathReader() : sigfd(-1) {} - ChildDeathReader(int sigfd_) : sigfd(sigfd_) {} - int getSigfd() { return sigfd; } - void setSigfd(int sigfd_) { sigfd = sigfd_; } - virtual pid_t reap() + PidPred(pid_t pid_) : pid(pid_) {} + bool operator()(const shared_ptr& parser) const { - pid_t pid; - int status; - if ((pid = waitpid(-1, &status, WNOHANG)) == -1) - { - std::perror("waitpid"); - return -1; - } - else - { - return pid; - } + return parser->getPid() == pid; } - bool ioCallback(Glib::IOCondition ioCondition) - { - if ((ioCondition & Glib::IO_IN) == 0) - return true; - char buf; - - if (read(sigfd, &buf, 1) <= 0) - return true; - reap(); - return true; - } -private: - int sigfd; + pid_t pid; }; // Depending on how args are passed, either launch stap directly or @@ -106,14 +125,18 @@ private: class StapLauncher : public ChildDeathReader { public: - StapLauncher() : _argv(0), _childPid(-1) {} + StapLauncher() : _argv(0), _childPid(-1) + { + childDiedSignal().connect(sigc::mem_fun(*this, &StapLauncher::onChildDied)); + } StapLauncher(char** argv) : _argv(argv), _childPid(-1) { + childDiedSignal().connect(sigc::mem_fun(*this, &StapLauncher::onChildDied)); } StapLauncher(const string& stapArgs, const string& script, const string& scriptArgs) - : _childPid(-1), _win(0), _widget(0) + : _childPid(-1) { setArgs(stapArgs, script, scriptArgs); } @@ -150,13 +173,7 @@ public: _scriptArgs.clear(); } - void setWinParams(Gtk::Window* win, GraphWidget* widget) - { - _win = win; - _widget = widget; - } int launch(); - void cleanUp(); shared_ptr makeStapParser() { shared_ptr result(new StapParser); @@ -164,25 +181,11 @@ public: parserListChangedSignal().emit(); return result; } -private: - struct pidPred - { - pidPred(pid_t pid_) : pid(pid_) {} - bool operator()(const shared_ptr& parser) const - { - return parser->getPid() == pid; - } - pid_t pid; - }; public: - pid_t reap() + void onChildDied(pid_t pid) { - using namespace boost; - pid_t pid = ChildDeathReader::reap(); - if (pid < 0) - return pid; ParserList::iterator itr - = find_if(parsers.begin(), parsers.end(), pidPred(pid)); + = find_if(parsers.begin(), parsers.end(), PidPred(pid)); if (itr != parsers.end()) { tr1::shared_ptr sp = (*itr)->getProcess(); @@ -192,12 +195,12 @@ public: parserListChangedSignal().emit(); } } - childDiedSignal().emit(pid); - return pid; } void killAll() { - for (ParserList::iterator itr = parsers.begin(), end = parsers.end(); + ParserList parsersCopy(parsers.begin(), parsers.end()); + for (ParserList::iterator itr = parsersCopy.begin(), + end = parsersCopy.end(); itr != end; ++itr) { @@ -211,8 +214,6 @@ protected: string _script; string _scriptArgs; int _childPid; - Gtk::Window* _win; - GraphWidget* _widget; }; int StapLauncher::launch() @@ -296,43 +297,6 @@ int StapLauncher::launch() return childPid; } -void StapLauncher::cleanUp() -{ - struct sigaction action; - action.sa_handler = SIG_DFL; - sigemptyset(&action.sa_mask); - action.sa_flags = 0; - sigaction(SIGCLD, &action, 0); - // Drain any outstanding signals - close(signalPipe[1]); - char buf; - while (read(signalPipe[0], &buf, 1) > 0) - reap(); - for (ParserList::iterator itr = parsers.begin(), end = parsers.end(); - itr != end; - ++itr) - { - pid_t childPid = (*itr)->getPid(); - if (childPid > 0) - kill(childPid, SIGTERM); - int status; - pid_t killedPid = -1; - if ((killedPid = wait(&status)) == -1) - { - std::perror("wait"); - } - else if (killedPid != childPid) - { - std::cerr << "wait: killed Pid " << killedPid << " != child Pid " - << childPid << "\n"; - } - else - { - childDiedSignal().emit(childPid); - } - } -} - class GraphicalStapLauncher : public StapLauncher { public: @@ -485,7 +449,7 @@ void ProcWindow::onKill() kill(proc->pid, SIGTERM); } -class GrapherWindow : public Gtk::Window, public ChildDeathReader::Callback +class GrapherWindow : public Gtk::Window { public: GrapherWindow(); @@ -503,21 +467,24 @@ protected: virtual void on_menu_script_start(); virtual void on_menu_proc_window(); void addGraph(); + void onParserListChanged(); // menu support Glib::RefPtr m_refUIManager; Glib::RefPtr m_refActionGroup; GraphicalStapLauncher* _graphicalLauncher; shared_ptr _procWindow; + bool _quitting; }; GrapherWindow::GrapherWindow() - : _procWindow(new ProcWindow) + : _procWindow(new ProcWindow), _quitting(false) { set_title("systemtap grapher"); add(m_Box); - + parserListChangedSignal() + .connect(sigc::mem_fun(*this, &GrapherWindow::onParserListChanged)); //Create actions for menus and toolbars: m_refActionGroup = Gtk::ActionGroup::create(); //File menu: @@ -575,7 +542,13 @@ GrapherWindow::GrapherWindow() void GrapherWindow::on_menu_file_quit() { - hide(); + using namespace boost; + _quitting = true; + if (find_if(parsers.begin(), parsers.end(), !bind(PidPred(-1), _1)) + != parsers.end()) + _graphicalLauncher->killAll(); + else + hide(); } void GrapherWindow::on_menu_script_start() @@ -589,6 +562,15 @@ void GrapherWindow::on_menu_proc_window() _procWindow->show(); } +void GrapherWindow::onParserListChanged() +{ + using namespace boost; + if (_quitting + && (find_if(parsers.begin(), parsers.end(), !bind(PidPred(-1), _1)) + == parsers.end())) + hide(); +} + int main(int argc, char** argv) { Gtk::Main app(argc, argv); @@ -597,7 +579,6 @@ int main(int argc, char** argv) win.set_title("Grapher"); win.set_default_size(600, 250); - launcher.setWinParams(&win, &win.w); win.setGraphicalLauncher(&launcher); @@ -616,7 +597,6 @@ int main(int argc, char** argv) launcher.launch(); } Gtk::Main::run(win); - launcher.cleanUp(); return 0; } -- cgit From b35632fd3d2547414c80023c5c60c847e3dc92ea Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Thu, 10 Dec 2009 15:11:08 +0100 Subject: grapher: more implementation stap process window The process arguments are displayed, and the "kill" button is insensitive when a process is dead or nothing is selected. * grapher/grapher.cxx (ProcWindow _killButton, _restartButton, _stapArgsLabel, _scriptArgsLabel): new members (ProcWindow::ProcWindow): hook 'em up (ProcWindow::onSelectionChanged): Enable / disable kill button and display process arguments. * grapher/processwindow.glade: Replace bizzare handle window with a horizontal pane. Tweak various widget sizes. --- grapher/grapher.cxx | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) (limited to 'grapher/grapher.cxx') diff --git a/grapher/grapher.cxx b/grapher/grapher.cxx index 8e7a4dba..e3d5289a 100644 --- a/grapher/grapher.cxx +++ b/grapher/grapher.cxx @@ -335,6 +335,10 @@ public: Glib::RefPtr _xml; Gtk::Window* _window; Gtk::TreeView* _dataTreeView; + Gtk::Button* _killButton; + Gtk::Button* _restartButton; + Gtk::Label* _stapArgsLabel; + Gtk::Label* _scriptArgsLabel; Glib::RefPtr _listStore; Glib::RefPtr _listSelection; void onClose(); @@ -376,15 +380,19 @@ ProcWindow::ProcWindow() _xml->get_widget("button5", button); button->signal_clicked().connect(sigc::mem_fun(*this, &ProcWindow::onClose), false); - _xml->get_widget("button1", button); - button->signal_clicked().connect(sigc::mem_fun(*this, &ProcWindow::onKill), - false); + _xml->get_widget("button1", _killButton); + _killButton->signal_clicked() + .connect(sigc::mem_fun(*this, &ProcWindow::onKill), false); + _killButton->set_sensitive(false); + _xml->get_widget("button2", _restartButton); + _restartButton->set_sensitive(false); parserListChangedSignal() .connect(sigc::mem_fun(*this, &ProcWindow::onParserListChanged)); _listSelection = _dataTreeView->get_selection(); _listSelection->signal_changed() .connect(sigc::mem_fun(*this, &ProcWindow::onSelectionChanged)); - + _xml->get_widget("label7", _stapArgsLabel); + _xml->get_widget("label8", _scriptArgsLabel); } void ProcWindow::onClose() @@ -436,6 +444,28 @@ void ProcWindow::onParserListChanged() void ProcWindow::onSelectionChanged() { + Gtk::TreeModel::iterator itr = _listSelection->get_selected(); + shared_ptr proc; + if (itr) + { + Gtk::TreeModel::Row row = *itr; + proc = row[_modelColumns._proc]; + } + if (proc) + { + if (proc->pid >= 0) + _killButton->set_sensitive(true); + else + _killButton->set_sensitive(false); + _stapArgsLabel->set_text(proc->stapArgs); + _scriptArgsLabel->set_text(proc->scriptArgs); + } + else + { + _killButton->set_sensitive(false); + _stapArgsLabel->set_text(""); + _scriptArgsLabel->set_text(""); + } } void ProcWindow::onKill() -- cgit From 1b9fad80af5504ef03c2a88504dbc47bea003721 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Thu, 10 Dec 2009 21:34:27 +0100 Subject: grapher: integrate graph events from stdin with the stap process framework. This was the original way to do graphing and had bitrotted some. * grapher/StapParser.cxx (initIO): new catchHUP argument (ioCallback): Only disconnect signals, etc. on IN_HUP if catchHUP is true. (errIoCallback): Write error messages to stderr, not stdout. * grapher/grapher.cxx (StapLauncher::launch): Don't catchHUP on our stap process children. (ProcWindow::refresh): Display something reasonable for the stap "process" that is feeding stdin. (main): Use StapParser::initIO to initialize parser reading from stdin. --- grapher/grapher.cxx | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'grapher/grapher.cxx') diff --git a/grapher/grapher.cxx b/grapher/grapher.cxx index e3d5289a..6dead221 100644 --- a/grapher/grapher.cxx +++ b/grapher/grapher.cxx @@ -293,7 +293,7 @@ int StapLauncher::launch() sp->setProcess(proc); parsers.push_back(sp); parserListChangedSignal().emit(); - sp->initIo(pipefd[0], pipefd[2]); + sp->initIo(pipefd[0], pipefd[2], false); return childPid; } @@ -422,14 +422,19 @@ void ProcWindow::refresh() ++spitr) { shared_ptr sp = (*spitr)->getProcess(); + Gtk::TreeModel::iterator litr = _listStore->append(); + Gtk::TreeModel::Row row = *litr; if (sp) { - Gtk::TreeModel::iterator litr = _listStore->append(); - Gtk::TreeModel::Row row = *litr; row[_modelColumns._iconName] = sp->pid >= 0 ? "gtk-yes" : "gtk-no"; row[_modelColumns._scriptName] = sp->script; row[_modelColumns._proc] = sp; } + else + { + row[_modelColumns._iconName] = "gtk-yes"; + row[_modelColumns._scriptName] = "standard input"; + } } } @@ -615,11 +620,7 @@ int main(int argc, char** argv) if (argc == 2 && !std::strcmp(argv[1], "-")) { tr1::shared_ptr sp = launcher.makeStapParser(); - sp->setInFd(STDIN_FILENO); - Glib::signal_io().connect(sigc::mem_fun(sp.get(), - &StapParser::ioCallback), - STDIN_FILENO, - Glib::IO_IN | Glib::IO_HUP); + sp->initIo(STDIN_FILENO, -1, true); } else if (argc > 1) { -- cgit From e47f92ea31a605802c59541ca325ffd567c45ca4 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Fri, 11 Dec 2009 14:03:47 +0100 Subject: grapher: implement restarting a stap process * grapher/StapParser.cxx (StapParser::disconnect): new function * grapher/StapParser.hxx (StapProcess::StapProcess): initialize argv to 0 * grapher/grapher.cxx (StapLauncher::setArgs): Set argv to 0 (StapLauncher launch, launchUsingParser): Refactor launch(), extracting function a that (re)launches a stap process using an existing parser. (StapLauncher::onChildDied): call disconnect() on dead parser. (GrapherWindow::_graphicalLauncher, setGraphicalLauncher): delete member, replacing with... (graphicalLauncher): new variable (ProcModelColumns): Store parser object in the list model instead of just a StapProcess object. (ProcWindow::onRestart): new function (ProcWindow::refresh): Preserve the list selection when the process list is refreshed. (ProcWindow::onSelectionChanged): Manage the restart button's state. --- grapher/grapher.cxx | 155 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 108 insertions(+), 47 deletions(-) (limited to 'grapher/grapher.cxx') diff --git a/grapher/grapher.cxx b/grapher/grapher.cxx index 6dead221..0111184a 100644 --- a/grapher/grapher.cxx +++ b/grapher/grapher.cxx @@ -153,6 +153,7 @@ public: void setArgs(const string& stapArgs, const string& script, const string& scriptArgs) { + _argv = 0; _stapArgs = stapArgs; _script = script; _scriptArgs = scriptArgs; @@ -174,6 +175,7 @@ public: } int launch(); + int launchUsingParser(shared_ptr parser); shared_ptr makeStapParser() { shared_ptr result(new StapParser); @@ -188,6 +190,7 @@ public: = find_if(parsers.begin(), parsers.end(), PidPred(pid)); if (itr != parsers.end()) { + (*itr)->disconnect(); tr1::shared_ptr sp = (*itr)->getProcess(); if (sp) { @@ -218,7 +221,36 @@ protected: int StapLauncher::launch() { - int childPid = -1; + tr1::shared_ptr sp(new StapParser); + shared_ptr proc(new StapProcess(-1)); + if (_argv) + proc->argv = _argv; + else + { + proc->stapArgs = _stapArgs; + proc->script = _script; + proc->scriptArgs = _scriptArgs; + } + sp->setProcess(proc); + pid_t childPid = launchUsingParser(sp); + if (childPid >= 0) + { + parsers.push_back(sp); + parserListChangedSignal().emit(); + } + return childPid; +} + +int StapLauncher::launchUsingParser(shared_ptr sp) +{ + shared_ptr proc = sp->getProcess(); + if (!proc) + { + cerr << "Can't launch parser with null process structure\n"; + return -1; + } + + proc->pid = -1; if (signalPipe[0] < 0) { if (pipe(&signalPipe[0]) < 0) @@ -245,11 +277,11 @@ int StapLauncher::launch() std::perror("pipe"); exit(1); } - if ((childPid = fork()) == -1) + if ((proc->pid = fork()) == -1) { exit(1); } - else if (childPid) + else if (proc->pid) { close(pipefd[1]); close(pipefd[3]); @@ -260,41 +292,28 @@ int StapLauncher::launch() dup2(pipefd[3], STDERR_FILENO); for_each(&pipefd[0], &pipefd[4], close); for_each(&signalPipe[0], &signalPipe[2], close); - if (_argv) + if (proc->argv) { char argv0[] = "stap"; - char** argvEnd = _argv; + char** argvEnd = proc->argv; for (; *argvEnd; ++argvEnd) ; - char** realArgv = new char*[argvEnd - _argv + 2]; + char** realArgv = new char*[argvEnd - proc->argv + 2]; realArgv[0] = argv0; std::copy(_argv, argvEnd + 1, &realArgv[1]); execvp("stap", realArgv); } else { - string argString = "stap" + _stapArgs + " " + _script + " " - + _scriptArgs; + string argString = "stap" + proc->stapArgs + " " + proc->script + " " + + proc->scriptArgs; execl("/bin/sh", "sh", "-c", argString.c_str(), static_cast(0)); } _exit(1); } - tr1::shared_ptr sp(new StapParser); - shared_ptr proc(new StapProcess(childPid)); - if (_argv) - proc->argv = _argv; - else - { - proc->stapArgs = _stapArgs; - proc->script = _script; - proc->scriptArgs = _scriptArgs; - } - sp->setProcess(proc); - parsers.push_back(sp); - parserListChangedSignal().emit(); sp->initIo(pipefd[0], pipefd[2], false); - return childPid; + return proc->pid; } class GraphicalStapLauncher : public StapLauncher @@ -312,6 +331,8 @@ private: Gtk::Entry* _scriptArgEntry; }; +GraphicalStapLauncher *graphicalLauncher = 0; + class ProcModelColumns : public Gtk::TreeModelColumnRecord { public: @@ -319,11 +340,11 @@ public: { add(_iconName); add(_scriptName); - add(_proc); + add(_parser); } Gtk::TreeModelColumn _iconName; Gtk::TreeModelColumn _scriptName; - Gtk::TreeModelColumn > _proc; + Gtk::TreeModelColumn > _parser; }; // This should probably be a Gtk window, with the appropriate glade magic @@ -347,6 +368,7 @@ public: void onParserListChanged(); void onSelectionChanged(); void onKill(); + void onRestart(); private: bool _open; void refresh(); @@ -385,6 +407,8 @@ ProcWindow::ProcWindow() .connect(sigc::mem_fun(*this, &ProcWindow::onKill), false); _killButton->set_sensitive(false); _xml->get_widget("button2", _restartButton); + _restartButton->signal_clicked() + .connect(sigc::mem_fun(*this, &ProcWindow::onRestart), false); _restartButton->set_sensitive(false); parserListChangedSignal() .connect(sigc::mem_fun(*this, &ProcWindow::onParserListChanged)); @@ -416,25 +440,53 @@ void ProcWindow::hide() void ProcWindow::refresh() { + // If a process is already selected, try to leave it selected after + // the list is reconstructed. + shared_ptr selectedParser; + { + Gtk::TreeModel::iterator itr = _listSelection->get_selected(); + if (itr) + { + Gtk::TreeModel::Row row = *itr; + selectedParser = row[_modelColumns._parser]; + } + } _listStore->clear(); for (ParserList::iterator spitr = parsers.begin(), end = parsers.end(); spitr != end; ++spitr) { - shared_ptr sp = (*spitr)->getProcess(); + shared_ptr parser = *spitr; + shared_ptr sp = parser->getProcess(); Gtk::TreeModel::iterator litr = _listStore->append(); Gtk::TreeModel::Row row = *litr; if (sp) { row[_modelColumns._iconName] = sp->pid >= 0 ? "gtk-yes" : "gtk-no"; row[_modelColumns._scriptName] = sp->script; - row[_modelColumns._proc] = sp; } else { row[_modelColumns._iconName] = "gtk-yes"; row[_modelColumns._scriptName] = "standard input"; } + row[_modelColumns._parser] = parser; + } + if (selectedParser) + { + Gtk::TreeModel::Children children = _listStore->children(); + for (Gtk::TreeModel::Children::const_iterator itr = children.begin(), + end = children.end(); + itr != end; + ++itr) + { + Gtk::TreeModel::Row row = *itr; + if (row[_modelColumns._parser] == selectedParser) + { + _listSelection->select(row); + break; + } + } } } @@ -450,24 +502,26 @@ void ProcWindow::onParserListChanged() void ProcWindow::onSelectionChanged() { Gtk::TreeModel::iterator itr = _listSelection->get_selected(); + shared_ptr parser; shared_ptr proc; if (itr) { Gtk::TreeModel::Row row = *itr; - proc = row[_modelColumns._proc]; + parser = row[_modelColumns._parser]; + proc = parser->getProcess(); } if (proc) { - if (proc->pid >= 0) - _killButton->set_sensitive(true); - else - _killButton->set_sensitive(false); + bool procRunning = proc->pid >= 0; + _killButton->set_sensitive(procRunning); + _restartButton->set_sensitive(!procRunning); _stapArgsLabel->set_text(proc->stapArgs); _scriptArgsLabel->set_text(proc->scriptArgs); } else { _killButton->set_sensitive(false); + _restartButton->set_sensitive(false); _stapArgsLabel->set_text(""); _scriptArgsLabel->set_text(""); } @@ -479,11 +533,26 @@ void ProcWindow::onKill() if (!itr) return; Gtk::TreeModel::Row row = *itr; - shared_ptr proc = row[_modelColumns._proc]; - if (proc->pid >= 0) + shared_ptr parser = row[_modelColumns._parser]; + shared_ptr proc = parser->getProcess(); + if (proc && proc->pid >= 0) kill(proc->pid, SIGTERM); } +void ProcWindow::onRestart() +{ + Gtk::TreeModel::iterator itr = _listSelection->get_selected(); + if (!itr) + return; + Gtk::TreeModel::Row row = *itr; + shared_ptr parser = row[_modelColumns._parser]; + shared_ptr proc = parser->getProcess(); + if (!proc) + return; + if (graphicalLauncher->launchUsingParser(parser) > 0) + parserListChangedSignal().emit(); +} + class GrapherWindow : public Gtk::Window { public: @@ -492,11 +561,6 @@ public: Gtk::VBox m_Box; Gtk::ScrolledWindow scrolled; GraphWidget w; - void setGraphicalLauncher(GraphicalStapLauncher* launcher) - { - _graphicalLauncher = launcher; - } - GraphicalStapLauncher* getGraphicalLauncher() { return _graphicalLauncher; } protected: virtual void on_menu_file_quit(); virtual void on_menu_script_start(); @@ -506,7 +570,6 @@ protected: // menu support Glib::RefPtr m_refUIManager; Glib::RefPtr m_refActionGroup; - GraphicalStapLauncher* _graphicalLauncher; shared_ptr _procWindow; bool _quitting; }; @@ -581,14 +644,14 @@ void GrapherWindow::on_menu_file_quit() _quitting = true; if (find_if(parsers.begin(), parsers.end(), !bind(PidPred(-1), _1)) != parsers.end()) - _graphicalLauncher->killAll(); + graphicalLauncher->killAll(); else hide(); } void GrapherWindow::on_menu_script_start() { - _graphicalLauncher->runDialog(); + graphicalLauncher->runDialog(); } @@ -609,23 +672,21 @@ void GrapherWindow::onParserListChanged() int main(int argc, char** argv) { Gtk::Main app(argc, argv); - GraphicalStapLauncher launcher; + graphicalLauncher = new GraphicalStapLauncher; GrapherWindow win; win.set_title("Grapher"); win.set_default_size(600, 250); - win.setGraphicalLauncher(&launcher); - if (argc == 2 && !std::strcmp(argv[1], "-")) { - tr1::shared_ptr sp = launcher.makeStapParser(); + tr1::shared_ptr sp = graphicalLauncher->makeStapParser(); sp->initIo(STDIN_FILENO, -1, true); } else if (argc > 1) { - launcher.setArgv(argv + 1); - launcher.launch(); + graphicalLauncher->setArgv(argv + 1); + graphicalLauncher->launch(); } Gtk::Main::run(win); return 0; -- cgit