Skip to content
Draft
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
4 changes: 4 additions & 0 deletions src/GdbCommandHandler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,10 @@ static vector<string> parse_cmd(string& str) {

if (resp == GdbCommandHandler::cmd_end_diversion()) {
LOG(debug) << "cmd must run outside of diversion (" << resp << ")";
if (gdb_server.remote_supports_replay_diversion()) {
return gdb_escape(string() + "Command '" + cmd->name() +
"' cannot run in a replay diversion.\n");
}
return resp;
}

Expand Down
23 changes: 22 additions & 1 deletion src/GdbConnection.cc
Original file line number Diff line number Diff line change
Expand Up @@ -656,6 +656,7 @@ bool GdbConnection::query(char* payload) {
multiprocess_supported_ = strstr(args, "multiprocess+") != nullptr;
hwbreak_supported_ = strstr(args, "hwbreak+") != nullptr;
swbreak_supported_ = strstr(args, "swbreak+") != nullptr;
replay_diversion_supported_ = strstr(args, "vReplayDiversion+") != nullptr;

stringstream supported;
// Encourage gdb to use very large packets since we support any packet size
Expand All @@ -670,7 +671,8 @@ bool GdbConnection::query(char* payload) {
";hwbreak+"
";swbreak+"
";ConditionalBreakpoints+"
";vContSupported+";
";vContSupported+"
";vReplayDiversion+";
if (features().reverse_execution) {
supported << ";ReverseContinue+"
";ReverseStep+";
Expand Down Expand Up @@ -1040,6 +1042,12 @@ bool GdbConnection::process_vpacket(char* payload) {
}
}

if (name == strstr(name, "ReplayDiversion:")) {
req = GdbRequest(DREQ_REPLAY_DIVERSION);
req.replay_diversion().want_diversion = name[16] == '1';
return true;
}

UNHANDLED_REQ() << "Unhandled gdb vpacket: v" << name;
return false;
}
Expand Down Expand Up @@ -1836,6 +1844,19 @@ void GdbConnection::reply_close(int err) {
consume_request();
}

void GdbConnection::reply_replay_diversion(const char* error_message) {
DEBUG_ASSERT(DREQ_REPLAY_DIVERSION == req.type);
if (error_message != nullptr) {
stringstream ss;
ss << "E." << error_message;
write_packet(ss.str().c_str());
} else {
write_packet("OK");
}

consume_request();
}

void GdbConnection::send_file_error_reply(int system_errno) {
int gdb_err;
switch (system_errno) {
Expand Down
30 changes: 29 additions & 1 deletion src/GdbConnection.h
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,9 @@ enum GdbRequestType {
DREQ_FILE_PREAD,
// vFile:close packet, uses params.file_close.
DREQ_FILE_CLOSE,

// vReplayDiversion packet, uses .replay_diversion.
DREQ_REPLAY_DIVERSION,
};

enum GdbRestartType {
Expand Down Expand Up @@ -185,7 +188,8 @@ struct GdbRequest {
file_setfs_(other.file_setfs_),
file_open_(other.file_open_),
file_pread_(other.file_pread_),
file_close_(other.file_close_) {}
file_close_(other.file_close_),
replay_diversion_(other.replay_diversion_) {}
GdbRequest& operator=(const GdbRequest& other) {
this->~GdbRequest();
new (this) GdbRequest(other);
Expand Down Expand Up @@ -245,6 +249,9 @@ struct GdbRequest {
struct FileClose {
int fd;
} file_close_;
struct ReplayDiversion {
bool want_diversion;
} replay_diversion_;

Mem& mem() {
DEBUG_ASSERT(type >= DREQ_MEM_FIRST && type <= DREQ_MEM_LAST);
Expand Down Expand Up @@ -338,6 +345,14 @@ struct GdbRequest {
DEBUG_ASSERT(type == DREQ_FILE_CLOSE);
return file_close_;
}
ReplayDiversion& replay_diversion() {
DEBUG_ASSERT(type == DREQ_REPLAY_DIVERSION);
return replay_diversion_;
}
const ReplayDiversion& replay_diversion() const {
DEBUG_ASSERT(type == DREQ_REPLAY_DIVERSION);
return replay_diversion_;
}

/**
* Return nonzero if this requires that program execution be resumed
Expand Down Expand Up @@ -553,6 +568,13 @@ class GdbConnection {
*/
void reply_close(int err);

/**
* Respond to a vReplayDiversion packet. If |error_message| is
* NULL, respond with 'OK'; otherwise, use |error_message| as the
* reason for a verbose error response.
*/
void reply_replay_diversion(const char* error_message);

/**
* Create a checkpoint of the given Session with the given id. Delete the
* existing checkpoint with that id if there is one.
Expand Down Expand Up @@ -603,6 +625,11 @@ class GdbConnection {
bool hwbreak_supported() { return hwbreak_supported_; }
bool swbreak_supported() { return swbreak_supported_; }

/**
* Returns true if the remote supports vReplayDiversion packets.
*/
bool replay_diversion_supported() { return replay_diversion_supported_; }

private:
/**
* read() incoming data exactly one time, successfully. May block.
Expand Down Expand Up @@ -696,6 +723,7 @@ class GdbConnection {
bool multiprocess_supported_; // client supports multiprocess extension
bool hwbreak_supported_; // client supports hwbreak extension
bool swbreak_supported_; // client supports swbreak extension
bool replay_diversion_supported_; // client supports vReplayDiversion extension
};

} // namespace rr
Expand Down
52 changes: 43 additions & 9 deletions src/GdbServer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -839,8 +839,10 @@ bool GdbServer::diverter_process_debugger_requests(
return false;

case DREQ_READ_SIGINFO: {
LOG(debug) << "Adding ref to diversion session";
++diversion_refcount;
if (!dbg->replay_diversion_supported()) {
LOG(debug) << "Adding ref to diversion session";
++diversion_refcount;
}
// TODO: maybe share with replayer.cc?
vector<uint8_t> si_bytes;
si_bytes.resize(req->mem().len);
Expand All @@ -860,15 +862,29 @@ bool GdbServer::diverter_process_debugger_requests(
}

case DREQ_WRITE_SIGINFO:
LOG(debug) << "Removing reference to diversion session ...";
DEBUG_ASSERT(diversion_refcount > 0);
--diversion_refcount;
if (diversion_refcount == 0) {
LOG(debug) << " ... dying at next continue request";
if (!dbg->replay_diversion_supported()) {
LOG(debug) << "Removing reference to diversion session ...";
DEBUG_ASSERT(diversion_refcount > 0);
--diversion_refcount;
if (diversion_refcount == 0) {
LOG(debug) << " ... dying at next continue request";
}
}
dbg->reply_write_siginfo();
continue;

case DREQ_REPLAY_DIVERSION:
if (req->replay_diversion().want_diversion) {
dbg->reply_replay_diversion("already-in-diversion");
continue;
}
LOG(debug) << "GDB asked for replay diversion to end";
DEBUG_ASSERT(diversion_refcount > 0);
diversion_refcount = 0;
dbg->reply_replay_diversion(nullptr);
*req = GdbRequest(DREQ_NONE);
return false;

case DREQ_RR_CMD: {
DEBUG_ASSERT(req->type == DREQ_RR_CMD);
Task* task = diversion_session.find_task(last_continue_tuid);
Expand Down Expand Up @@ -1145,14 +1161,32 @@ GdbRequest GdbServer::process_debugger_requests(ReportState state) {
// siginfo and then incorrectly start a diversion and go haywire :-(.
// Ideally we'd come up with a better way to detect diversions so that
// "print $_siginfo" works.
req = divert(timeline.current_session());
if (req.type == DREQ_NONE) {
if (!dbg->replay_diversion_supported()) {
req = divert(timeline.current_session());
if (req.type == DREQ_NONE) {
continue;
}
} else {
continue;
}
// Carry on to process the request that was rejected by
// the diversion session
}

if (req.type == DREQ_REPLAY_DIVERSION) {
if (req.replay_diversion().want_diversion) {
LOG(debug) << "GDB requested diversion";
dbg->reply_replay_diversion(nullptr);
req = divert(timeline.current_session());
if (req.type == DREQ_NONE) {
continue;
}
} else {
dbg->reply_replay_diversion("not-in-diversion");
continue;
}
}

if (req.is_resume_request()) {
Task* t = current_session().find_task(last_continue_tuid);
if (t) {
Expand Down
4 changes: 4 additions & 0 deletions src/GdbServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,10 @@ class GdbServer {

ReplayTimeline& get_timeline() { return timeline; }

bool remote_supports_replay_diversion() {
return dbg != nullptr && dbg->replay_diversion_supported();
}

private:
GdbServer(std::unique_ptr<GdbConnection>& dbg, Task* t);

Expand Down
8 changes: 5 additions & 3 deletions src/test/util.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
#
# Test runners may set the environment variable $RECORD_ARGS to pass
# arguments to rr for recording. This is only useful for tweaking the
# scheduler, don't use it for anything else.
# scheduler, don't use it for anything else. Test runners may also
# set $REPLAY_ARGS to pass arguments to rr for replay, allowing the
# use of an alternate GDB, for instance.
#

# delay_kill <sig> <delay_secs> <proc>
Expand Down Expand Up @@ -277,7 +279,7 @@ function record_async_signal { sig=$1; delay_secs=$2; exe=$3; exeargs=$4;

function replay { replayflags=$1
_RR_TRACE_DIR="$workdir" test-monitor $TIMEOUT replay.err \
$RR_EXE $GLOBAL_OPTIONS replay -a $replayflags 1> replay.out 2> replay.err
$RR_EXE $GLOBAL_OPTIONS replay -a $replayflags $REPLAY_ARGS 1> replay.out 2> replay.err
}

function rerun { rerunflags=$1
Expand All @@ -296,7 +298,7 @@ function do_ps { psflags=$1
function debug { expectscript=$1; replayargs=$2
_RR_TRACE_DIR="$workdir" test-monitor $TIMEOUT debug.err \
python3 $TESTDIR/$expectscript.py \
$RR_EXE $GLOBAL_OPTIONS replay -o-n -x $TESTDIR/test_setup.gdb $replayargs
$RR_EXE $GLOBAL_OPTIONS replay -o-n -x $TESTDIR/test_setup.gdb $replayargs $REPLAY_ARGS
if [[ $? == 0 ]]; then
passed
else
Expand Down
15 changes: 6 additions & 9 deletions src/test/when.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,23 +46,20 @@
# Ensure 'when' terminates a diversion
send_gdb('call strlen("abcd")')
send_gdb('when')
expect_gdb(re.compile(r'Current event: (\d+)'))
t3 = eval(last_match().group(1));
if t3 != t2:
expect_gdb(re.compile(r'(Current event: (\d+)|cannot run in a replay diversion)'))
if last_match().group(2) and int(last_match().group(2)) != t2:
failed('ERROR ... diversion changed event')

send_gdb('call strlen("abcd")')
send_gdb('when-ticks')
expect_gdb(re.compile(r'Current tick: (\d+)'))
ticks3 = eval(last_match().group(1));
if ticks3 != ticks2:
expect_gdb(re.compile(r'(Current tick: (\d+)|cannot run in a replay diversion)'))
if last_match().group(2) and int(last_match().group(2)) != ticks2:
failed('ERROR ... diversion changed ticks')

send_gdb('call strlen("abcd")')
send_gdb('when-tid')
expect_gdb(re.compile(r'Current tid: (\d+)'))
tid3 = eval(last_match().group(1));
if tid3 != tid2:
expect_gdb(re.compile(r'(Current tid: (\d+)|cannot run in a replay diversion)'))
if last_match().group(2) and int(last_match().group(2)) != tid2:
failed('ERROR ... diversion changed tid')

ok()