Skip to content
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
11 changes: 5 additions & 6 deletions include/libsdb/sdb_process.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,19 +41,18 @@ class Process
Process(pid_t pid, bool cleanupOnExit, bool isBeingTraced);

private:
const pid_t d_pid;

pid_t d_pid;
ProcessState d_state;
const bool d_cleanupOnExit;
const bool d_isBeingTraced;
bool d_cleanupOnExit;
bool d_isBeingTraced;

public:
~Process();
static ProcessUPtr attach(pid_t pid);
static ProcessUPtr launch(std::filesystem::path path, bool traceProc = true);

Process(Process&&) = default;
Process& operator=(Process&&) = default;
Process(Process&&);
Process& operator=(Process&&);

/* Resume the debugee process */
void resume();
Expand Down
2 changes: 2 additions & 0 deletions include/libsdb/sdb_process_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ enum struct ProcessState
e_TERMINATED
};

std::ostream& operator<<(std::ostream& os, ProcessState state);

} // sdb

#endif
3 changes: 1 addition & 2 deletions src/sdb_pipe.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,14 @@

namespace sdb
{

namespace
{
static const int EMPTY_PIPE_FD = -1;
} // namespace

Pipe::Pipe(bool closeOnExec)
{
if (auto rc = pipe2(d_fds, 0 | (closeOnExec ? O_CLOEXEC : 0)); rc < 0)
if (auto rc = pipe2(d_fds, closeOnExec ? O_CLOEXEC : 0); rc < 0)
{
std::stringstream ss;
ss << "Failed to create pipe, rc=" << rc;
Expand Down
48 changes: 38 additions & 10 deletions src/sdb_process.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <unistd.h>
#include <utility>
#include <vector>

namespace sdb
Expand All @@ -29,6 +28,31 @@ Process::Process(pid_t pid, bool cleanupOnExit, bool isBeingTraced)
{
}

Process::Process(Process&& other)
: d_pid(other.d_pid), d_state(other.d_state),
d_cleanupOnExit(other.d_cleanupOnExit),
d_isBeingTraced(other.d_isBeingTraced)
{
// Ensure the underlying process is not cleaned
// when the object moved out of is destructed
other.d_pid = 0;
other.d_cleanupOnExit = false;
}

Process& Process::operator=(Process&& other)
{

Copilot AI Aug 25, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The move assignment operator doesn't handle self-assignment properly. If this == &other, the object will invalidate itself by setting other.d_pid = 0 and other.d_cleanupOnExit = false. Add a self-assignment check at the beginning of the function.

Suggested change
{
{
if (this == &other)
{
return *this;
}

Copilot uses AI. Check for mistakes.
d_pid = other.d_pid;
d_state = other.d_state;
d_cleanupOnExit = other.d_cleanupOnExit;
d_isBeingTraced = other.d_isBeingTraced;

// Ensure the underlying process is not cleaned
// when the object moved out of is destructed
other.d_pid = 0;
other.d_cleanupOnExit = false;
return *this;
}

Process::~Process()
{
if (d_pid == 0)
Expand All @@ -41,22 +65,23 @@ Process::~Process()
return;
}

if (d_state == ProcessState::e_RUNNING)
{
kill(d_pid, SIGSTOP);
waitOnSignal();
}

// Detach
if (d_isBeingTraced)
{
if (d_state == ProcessState::e_RUNNING)
{
kill(d_pid, SIGSTOP);
waitOnSignal();
}

// Detach
if (auto rc
= ptrace(PTRACE_DETACH, d_pid, /*addr=*/nullptr, /*data=*/nullptr);
rc < 0)
{
std::cout << "Unable to detach from process, rc=" << rc << std::endl;
return;
}

kill(d_pid, SIGCONT);
}

Expand Down Expand Up @@ -157,11 +182,14 @@ void Process::resume()
case ProcessState::e_STOPPED:
if (int rc = ptrace(PTRACE_CONT, d_pid, nullptr, nullptr); rc < 0)
{
std::cerr << "Unable to continue, rc=" << rc << std::endl;
std::stringstream ss;
ss << "Unable to continue proc with pid=" << d_pid << ", rc=" << rc
<< ". perror=";
perror(ss.str().c_str());

Copilot AI Aug 25, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using stringstream for simple string concatenation in error paths is inefficient. Consider using string concatenation with std::to_string() for better performance: std::string message = "Unable to continue proc with pid=" + std::to_string(d_pid) + ", rc=" + std::to_string(rc) + ". perror=";

Suggested change
perror(ss.str().c_str());
std::string message = "Unable to continue proc with pid=" + std::to_string(d_pid) +
", rc=" + std::to_string(rc) + ". perror=";
perror(message.c_str());

Copilot uses AI. Check for mistakes.
std::exit(-1);
}
d_state = ProcessState::e_RUNNING;
waitOnSignal();
// waitOnSignal();
break;
default:
std::cout << "Inferior process is not running" << std::endl;
Expand Down
2 changes: 2 additions & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,5 @@ target_link_libraries(sdb.u.t

include(GoogleTest)
gtest_discover_tests(sdb.u.t)

add_executable(testbin.tsk testbin.m.cpp)
42 changes: 38 additions & 4 deletions test/sdb_process.t.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <signal.h>
#include <sstream>
#include <string>
#include <thread>

#include <gmock/gmock.h>
#include <gtest/gtest.h>
Expand Down Expand Up @@ -45,6 +46,12 @@ char getProcessStatus(int pid)
const auto idxOfProcStatus = idxOfLastParen + 2;
return buf[idxOfProcStatus];
}

const auto processInTracingStoppedStatus
= [](int pid) { return getProcessStatus(pid) == 't'; };

const auto processInRunningStatus
= [](int pid) { return getProcessStatus(pid) == 'R'; };
} // namespace

TEST(ProcessTest, processLaunchedSuccessfully)
Expand All @@ -69,7 +76,9 @@ TEST(ProcessTest, launchNonExistentProgram)
TEST(ProcessTest, attachToProcess)
{
// Given
const std::filesystem::path procname{ "ls" };
const std::filesystem::path procname{ "./testbin.tsk" };
// Don't trace the process on launch
// since we'll attach to it below
const auto proc = Process::launch(procname, /*traceProc*/ false);
ASSERT_THAT(proc, NotNull());

Expand All @@ -78,10 +87,35 @@ TEST(ProcessTest, attachToProcess)

// Then
ASSERT_EQ(proc->pid(), attachedProc->pid());
auto processInTracingStoppedStatus
= [](int pid) { return getProcessStatus(pid) == 't'; };

ASSERT_TRUE(processInTracingStoppedStatus(attachedProc->pid()));
}

TEST(ProcessTest, attachToInvalidPid)
{
// Given / When / Then
EXPECT_THROW(Process::attach(0), ProcessAttachError);
}

TEST(ProcessTest, resumeAProcess)
{
// Given
auto proc = Process::launch("./testbin.tsk");
ASSERT_THAT(proc, NotNull());
const auto pid = proc->pid();
ASSERT_TRUE(processInTracingStoppedStatus(pid));
const auto procState = proc->state();
ASSERT_EQ(procState, ProcessState::e_STOPPED)
<< "unexpected proc state=" << procState;

// When
// TODO: Understand why this fails with the following
// error when I move it to a different thread::
// Unable to continue proc with pid=137233, rc=-1
// No such process
proc->resume();

// Then
ASSERT_TRUE(processInRunningStatus(pid));
}

} // namespace sdb
9 changes: 9 additions & 0 deletions test/testbin.m.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#include <chrono>
#include <thread>

using namespace std::chrono_literals;

int main()
{
std::this_thread::sleep_for(5s);
}