summaryrefslogtreecommitdiffstats
path: root/grapher/grapher.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'grapher/grapher.cxx')
-rw-r--r--grapher/grapher.cxx392
1 files changed, 231 insertions, 161 deletions
diff --git a/grapher/grapher.cxx b/grapher/grapher.cxx
index 2a9a617b..6dead221 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<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()
- {
- 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)
+ PidPred(pid_t pid_) : pid(pid_) {}
+ bool operator()(const shared_ptr<StapParser>& parser) const
{
- 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
@@ -106,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);
}
@@ -149,49 +172,35 @@ 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();
shared_ptr<StapParser> makeStapParser()
{
shared_ptr<StapParser> result(new StapParser);
- _parsers.push_back(result);
+ parsers.push_back(result);
+ parserListChangedSignal().emit();
return result;
}
-private:
- struct pidPred
- {
- pidPred(pid_t pid_) : pid(pid_) {}
- bool operator()(const shared_ptr<StapParser>& 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));
- if (itr != _parsers.end())
- (*itr)->setProcess(tr1::shared_ptr<StapProcess>());
- return pid;
+ = find_if(parsers.begin(), parsers.end(), PidPred(pid));
+ if (itr != parsers.end())
+ {
+ 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)
{
@@ -199,17 +208,12 @@ public:
kill((*itr)->getPid(), SIGTERM);
}
}
- typedef vector<shared_ptr<StapParser> > ParserList;
- ParserList _parsers;
protected:
char** _argv;
string _stapArgs;
string _script;
string _scriptArgs;
int _childPid;
- ChildDeathReader::Callback* _deathCallback;
- Gtk::Window* _win;
- GraphWidget* _widget;
};
int StapLauncher::launch()
@@ -287,55 +291,12 @@ 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], false);
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 if (_deathCallback)
- _deathCallback->childDied(childPid);
- }
-}
-
class GraphicalStapLauncher : public StapLauncher
{
public:
@@ -356,13 +317,16 @@ class ProcModelColumns : public Gtk::TreeModelColumnRecord
public:
ProcModelColumns()
{
+ add(_iconName);
add(_scriptName);
add(_proc);
}
+ Gtk::TreeModelColumn<Glib::ustring> _iconName;
Gtk::TreeModelColumn<Glib::ustring> _scriptName;
Gtk::TreeModelColumn<shared_ptr<StapProcess> > _proc;
};
+// This should probably be a Gtk window, with the appropriate glade magic
class ProcWindow
{
public:
@@ -371,11 +335,25 @@ public:
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();
+private:
+ bool _open;
+ void refresh();
};
ProcWindow::ProcWindow()
+ : _open(false)
{
try
{
@@ -390,12 +368,31 @@ 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", _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()
@@ -403,7 +400,91 @@ void ProcWindow::onClose()
_window->hide();
}
-class GrapherWindow : public Gtk::Window, public ChildDeathReader::Callback
+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<StapProcess> sp = (*spitr)->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";
+ }
+ }
+}
+
+void ProcWindow::onParserListChanged()
+{
+ if (_open)
+ {
+ refresh();
+ _window->queue_draw();
+ }
+}
+
+void ProcWindow::onSelectionChanged()
+{
+ Gtk::TreeModel::iterator itr = _listSelection->get_selected();
+ shared_ptr<StapProcess> 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()
+{
+ Gtk::TreeModel::iterator itr = _listSelection->get_selected();
+ if (!itr)
+ return;
+ Gtk::TreeModel::Row row = *itr;
+ shared_ptr<StapProcess> proc = row[_modelColumns._proc];
+ if (proc->pid >= 0)
+ kill(proc->pid, SIGTERM);
+}
+
+class GrapherWindow : public Gtk::Window
{
public:
GrapherWindow();
@@ -411,7 +492,6 @@ public:
Gtk::VBox m_Box;
Gtk::ScrolledWindow scrolled;
GraphWidget w;
- void childDied(int pid);
void setGraphicalLauncher(GraphicalStapLauncher* launcher)
{
_graphicalLauncher = launcher;
@@ -422,21 +502,24 @@ protected:
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)
+ : _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:
@@ -494,7 +577,13 @@ 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()
@@ -505,30 +594,18 @@ 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<StapProcess> 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)
+void GrapherWindow::onParserListChanged()
{
- hide();
+ 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);
@@ -537,27 +614,20 @@ int main(int argc, char** argv)
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);
+ sp->initIo(STDIN_FILENO, -1, true);
}
else if (argc > 1)
{
launcher.setArgv(argv + 1);
- launcher.setDeathCallback(&win);
launcher.launch();
}
Gtk::Main::run(win);
- launcher.cleanUp();
return 0;
}