diff options
-rw-r--r-- | grapher/StapParser.cxx | 349 | ||||
-rw-r--r-- | grapher/StapParser.hxx | 11 | ||||
-rw-r--r-- | grapher/grapher.cxx | 392 | ||||
-rw-r--r-- | grapher/processwindow.glade | 184 |
4 files changed, 568 insertions, 368 deletions
diff --git a/grapher/StapParser.cxx b/grapher/StapParser.cxx index b82cc024..2513680b 100644 --- a/grapher/StapParser.cxx +++ b/grapher/StapParser.cxx @@ -30,7 +30,15 @@ namespace systemtap 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; @@ -72,171 +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) - { - childDiedSignal().emit(getPid()); - 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; - 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; - } - _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") - { - 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 ((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; + } + _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") { - if (!_csv.elements.empty()) + 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") + { + 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; - stringbuf data; - stream >> time; - stream.get(data, _lineEndChar); - parseData(itr->second, time, data.str()); + 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) { @@ -251,8 +264,28 @@ vector<string> commaSplit(const boost::sub_range<Glib::ustring>& range) 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); + + } } diff --git a/grapher/StapParser.hxx b/grapher/StapParser.hxx index cfb807a8..24e84fc9 100644 --- a/grapher/StapParser.hxx +++ b/grapher/StapParser.hxx @@ -37,10 +37,13 @@ namespace systemtap 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') + : _errFd(-1), _inFd(-1), _lineEndChar('\n'), _catchHUP(false) { } void parseData(std::tr1::shared_ptr<GraphDataBase> gdata, @@ -63,7 +66,13 @@ namespace systemtap { _process = process; } + void initIo(int inFd, int errFd, bool catchHUP); }; 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/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; } diff --git a/grapher/processwindow.glade b/grapher/processwindow.glade index ad1bdd14..a598fba6 100644 --- a/grapher/processwindow.glade +++ b/grapher/processwindow.glade @@ -212,10 +212,9 @@ </child> <child> - <widget class="GtkHBox" id="hbox1"> + <widget class="GtkHPaned" id="hpaned1"> <property name="visible">True</property> - <property name="homogeneous">False</property> - <property name="spacing">0</property> + <property name="can_focus">True</property> <child> <widget class="GtkScrolledWindow" id="scrolledwindow1"> @@ -228,7 +227,7 @@ <child> <widget class="GtkTreeView" id="treeview1"> - <property name="width_request">300</property> + <property name="width_request">250</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="headers_visible">True</property> @@ -242,37 +241,34 @@ </child> </widget> <packing> - <property name="padding">0</property> - <property name="expand">True</property> - <property name="fill">True</property> + <property name="shrink">True</property> + <property name="resize">False</property> </packing> </child> <child> - <widget class="GtkHandleBox" id="handlebox1"> - <property name="width_request">26</property> + <widget class="GtkVBox" id="vbox2"> + <property name="width_request">124</property> <property name="visible">True</property> - <property name="shadow_type">GTK_SHADOW_OUT</property> - <property name="handle_position">GTK_POS_LEFT</property> - <property name="snap_edge">GTK_POS_TOP</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> <child> - <widget class="GtkVBox" id="vbox2"> + <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="label1"> - <property name="height_request">17</property> + <widget class="GtkLabel" id="label5"> <property name="visible">True</property> - <property name="label" translatable="yes">label1</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</property> + <property name="xalign">0.5</property> <property name="yalign">0.5</property> <property name="xpad">0</property> <property name="ypad">0</property> @@ -289,48 +285,140 @@ </child> <child> - <widget class="GtkScrolledWindow" id="scrolledwindow2"> + <widget class="GtkLabel" id="label7"> <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> + <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="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> + <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">True</property> - <property name="fill">True</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="padding">0</property> - <property name="expand">True</property> - <property name="fill">True</property> + <property name="shrink">True</property> + <property name="resize">True</property> </packing> </child> </widget> |