Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Powellsr/fix/debug attach handling #473

Merged
merged 6 commits into from
Nov 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 81 additions & 61 deletions src/TiledArray/util/bug.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ Debugger::~Debugger() {
for (int i = 0; i < NSIG; i++) {
if (mysigs_[i]) signals[i] = nullptr;
}
delete[] mysigs_;
}

void Debugger::init() {
Expand All @@ -91,7 +90,7 @@ void Debugger::init() {
debug_ = 1;
wait_for_debugger_ = 1;

mysigs_ = new int[NSIG];
mysigs_ = std::make_unique<int[]>(NSIG);
for (int i = 0; i < NSIG; i++) {
mysigs_[i] = 0;
}
Expand All @@ -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;
}
Expand Down Expand Up @@ -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) {
Expand All @@ -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_)
;
}
}
}
Expand All @@ -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);
}
Expand Down Expand Up @@ -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<void()> action) {
actions_.push_back(action);
}

void create_debugger(const char *cmd, const char *exec, std::int64_t rank) {
auto debugger = std::make_shared<TiledArray::Debugger>();
if (cmd) debugger->set_cmd(cmd);
Expand Down
25 changes: 20 additions & 5 deletions src/TiledArray/util/bug.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

#include <cassert>
#include <cstdint>
#include <functional>
#include <memory>
#include <string>
#include <unordered_map>
Expand Down Expand Up @@ -291,7 +292,7 @@ class Debugger {
bool sleep_;
bool wait_for_debugger_;
bool handle_sigint_;
int *mysigs_;
std::unique_ptr<int[]> mysigs_;

void init();

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand All @@ -381,9 +382,23 @@ class Debugger {
/// Return the global default debugger.
static std::shared_ptr<Debugger> 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<void()> 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<std::function<void()>> actions_; // prelaunch actions
};

/// Use this to create a Debugger object and make it the default
Expand Down
Loading