diff --git a/src/TiledArray/util/bug.cpp b/src/TiledArray/util/bug.cpp index ff37f14343..0105635f37 100644 --- a/src/TiledArray/util/bug.cpp +++ b/src/TiledArray/util/bug.cpp @@ -77,7 +77,6 @@ Debugger::~Debugger() { for (int i = 0; i < NSIG; i++) { if (mysigs_[i]) signals[i] = nullptr; } - delete[] mysigs_; } void Debugger::init() { @@ -91,7 +90,7 @@ void Debugger::init() { debug_ = 1; wait_for_debugger_ = 1; - mysigs_ = new int[NSIG]; + mysigs_ = std::make_unique(NSIG); for (int i = 0; i < NSIG; i++) { mysigs_[i] = 0; } @@ -106,14 +105,14 @@ static void handler(int sig) { void Debugger::handle(int sig) { if (sig >= NSIG) return; typedef void (*handler_type)(int); - signal(sig, (handler_type)handler); + std::signal(sig, (handler_type)handler); signals[sig] = this; mysigs_[sig] = 1; } void Debugger::release(int sig) { if (sig >= NSIG) return; - signal(sig, SIG_DFL); + std::signal(sig, SIG_DFL); signals[sig] = nullptr; mysigs_[sig] = 0; } @@ -180,25 +179,48 @@ void Debugger::default_cmd() { } } +const std::string Debugger::gdb_cmd_ = + "gdb -ex \"set variable debugger_ready_=1\" --pid=$(PID) $(EXEC)"; +const std::string Debugger::lldb_cmd_ = + "lldb -p $(PID) -o \"expr debugger_ready_=1\""; + void Debugger::resolve_cmd_alias() { if (cmd_ == "gdb_xterm") { - cmd_ = - "xterm -title \"$(PREFIX)$(EXEC)\" -e gdb -ex \"set variable " - "debugger_ready_=1\" --pid=$(PID) $(EXEC) &"; + cmd_ = "xterm -title \"$(PREFIX)$(EXEC)\" -e " + gdb_cmd_ + " &"; } else if (cmd_ == "lldb_xterm") { - cmd_ = - "xterm -title \"$(PREFIX)$(EXEC)\" -e lldb -p $(PID) -o \"expr " - "debugger_ready_=1\" &"; + cmd_ = "xterm -title \"$(PREFIX)$(EXEC)\" -e " + lldb_cmd_ + " &"; } } +std::string Debugger::replace_macros(std::string str) { + if (!str.empty()) { + int pid = getpid(); + std::string::size_type pos; + std::string pidvar("$(PID)"); + while ((pos = str.find(pidvar)) != std::string::npos) { + std::string pidstr; + pidstr += std::to_string(pid); + str.replace(pos, pidvar.size(), pidstr); + } + std::string execvar("$(EXEC)"); + while ((pos = str.find(execvar)) != std::string::npos) { + str.replace(pos, execvar.size(), exec_); + } + std::string prefixvar("$(PREFIX)"); + while ((pos = str.find(prefixvar)) != std::string::npos) { + str.replace(pos, prefixvar.size(), prefix_); + } + } + return str; +} + void Debugger::set_cmd(const char *cmd) { if (cmd) { cmd_ = cmd; - resolve_cmd_alias(); } else { cmd_.resize(0); } + this->resolve_cmd_alias(); } void Debugger::debug(const char *reason) { @@ -209,58 +231,48 @@ void Debugger::debug(const char *reason) { std::cout << "no reason given"; std::cout << std::endl; - if (!cmd_.empty()) { - int pid = getpid(); - // contruct the command name - std::string cmd = cmd_; - std::string::size_type pos; - std::string pidvar("$(PID)"); - while ((pos = cmd.find(pidvar)) != std::string::npos) { - std::string pidstr; - pidstr += std::to_string(pid); - cmd.replace(pos, pidvar.size(), pidstr); - } - std::string execvar("$(EXEC)"); - while ((pos = cmd.find(execvar)) != std::string::npos) { - cmd.replace(pos, execvar.size(), exec_); - } - std::string prefixvar("$(PREFIX)"); - while ((pos = cmd.find(prefixvar)) != std::string::npos) { - cmd.replace(pos, prefixvar.size(), prefix_); - } - - // start the debugger - // before starting the debugger de-register signal handler for SIGTRAP to - // let the debugger take over - release(SIGTRAP); + const std::string cmd = replace_macros(cmd_); + // start the debugger + // before starting the debugger de-register signal handler for SIGTRAP to + // let the debugger take over + release(SIGTRAP); + int system_retvalue = 0; + if (!cmd.empty()) { std::cout << prefix_ << "Debugger: starting \"" << cmd << "\"" << std::endl; - debugger_ready_ = 0; - const auto system_retvalue = system(cmd.c_str()); - if (system_retvalue != 0) { // call to system() failed - std::cout << prefix_ - << "Failed debugger launch: system() did not succeed ..." - << std::endl; - } else { // call to system() succeeded - // wait until the debugger is ready - if (sleep_) { - std::cout << prefix_ << "Sleeping " << sleep_ - << " seconds to wait for debugger ..." << std::endl; - sleep(sleep_); - } - if (wait_for_debugger_) { - std::string make_ready_message; - if (cmd_.find(" gdb ") != std::string::npos || - cmd_.find(" lldb ") != std::string::npos) { - make_ready_message = - " configure debugging session (set breakpoints/watchpoints, " - "etc.) then type 'c' to continue running"; - } - - std::cout << prefix_ << ": waiting for the user ..." - << make_ready_message << std::endl; - while (!debugger_ready_) - ; + system_retvalue = std::system(cmd.c_str()); + } + if (system_retvalue != 0) { + std::cout << prefix_ + << "Failed debugger launch: system() did not succeed ..." + << std::endl; + } else { // call to system() succeeded + // wait until the debugger is ready + if (sleep_) { + std::cout << prefix_ << "Debugger: sleeping " << sleep_ + << " seconds to wait for debugger ..." << std::endl; + sleep(sleep_); + } + if (wait_for_debugger_) { + std::cout << prefix_ << "Debugger: waiting for the user ..."; + if (cmd_.find(" gdb ") != std::string::npos || + cmd_.find(" lldb ") != std::string::npos) { + std::cout + << " configure debugging session (set breakpoints/watchpoints, " + "etc.) then type 'c' to continue running"; + } else if (cmd.empty()) { + std::cout << " attach debugger to process " << std::to_string(getpid()) + << " as follows:" << std::endl + << prefix_ + << "Debugger: - if using gdb: " << replace_macros(gdb_cmd_) + << std::endl + << prefix_ + << "Debugger: - if using lldb: " << replace_macros(lldb_cmd_); } + std::cout << std::endl; + + debugger_ready_ = 0; + while (!debugger_ready_) + ; } } } @@ -286,6 +298,10 @@ void Debugger::got_signal(int sig) { else signame = "UNKNOWN SIGNAL"; + for (auto const &action : actions_) { + action(); + } + actions_.clear(); if (traceback_) { traceback(signame); } @@ -355,6 +371,10 @@ void Debugger::__traceback(const std::string &prefix, const char *reason) { std::cout << result.str(nframes_to_skip) << std::endl; } +void Debugger::register_prelaunch_action(std::function action) { + actions_.push_back(action); +} + void create_debugger(const char *cmd, const char *exec, std::int64_t rank) { auto debugger = std::make_shared(); if (cmd) debugger->set_cmd(cmd); diff --git a/src/TiledArray/util/bug.h b/src/TiledArray/util/bug.h index 2d217ceaee..5367497b62 100644 --- a/src/TiledArray/util/bug.h +++ b/src/TiledArray/util/bug.h @@ -30,6 +30,7 @@ #include #include +#include #include #include #include @@ -291,7 +292,7 @@ class Debugger { bool sleep_; bool wait_for_debugger_; bool handle_sigint_; - int *mysigs_; + std::unique_ptr mysigs_; void init(); @@ -325,11 +326,11 @@ class Debugger { @param reason optional string specifying the reason for traceback */ virtual void traceback(const char *reason); - /// Turn on or off debugging on a signel. The default is on. + /// Turn on or off debugging on a signal. The default is on. virtual void set_debug_on_signal(int); - /// Turn on or off traceback on a signel. The default is on. + /// Turn on or off traceback on a signal. The default is on. virtual void set_traceback_on_signal(int); - /// Turn on or off exit after a signel. The default is on. + /// Turn on or off exit after a signal. The default is on. virtual void set_exit_on_signal(int); /** Turn on or off running an infinite loop after the debugger is started. This loop gives the debugger a chance to attack to the process. @@ -370,7 +371,7 @@ class Debugger { virtual void default_cmd(); /** Set the name of the executable for the current process. It is up to the programmer to set this, even if the Debugger - is initialized with the KeyVal constructor. */ + is initialized with the constructor. */ virtual void set_exec(const char *); /// Called when signal sig is received. This is mainly for internal use. @@ -381,9 +382,23 @@ class Debugger { /// Return the global default debugger. static std::shared_ptr default_debugger(); + /// Register a (one-time) action to be executed when debugger is launched + /// @param action an action to be executed + /// @note multiple actions registered via this will be executed in order of + /// their registration + void register_prelaunch_action(std::function action); + private: /// Replaces alias in cmd_ with its full form void resolve_cmd_alias(); + /// Replace macros (\c PID , \c EXEC , \c PREFIX ) in \p cmd by their values + /// \param cmd a string + /// \return processed str + std::string replace_macros(std::string cmd); + + static const std::string gdb_cmd_; + static const std::string lldb_cmd_; + std::vector> actions_; // prelaunch actions }; /// Use this to create a Debugger object and make it the default