Attach and suspend all threads on linux.

develop
Alexander Gavrilov 2011-05-09 13:17:35 +04:00
parent abc473db5d
commit 3935293986
1 changed files with 99 additions and 62 deletions

@ -27,6 +27,7 @@ distribution.
#include <string> #include <string>
#include <vector> #include <vector>
#include <map> #include <map>
#include <set>
#include <cstdio> #include <cstdio>
#include <cstring> #include <cstring>
using namespace std; using namespace std;
@ -37,6 +38,7 @@ using namespace std;
#include "dfhack/DFError.h" #include "dfhack/DFError.h"
#include <errno.h> #include <errno.h>
#include <sys/ptrace.h> #include <sys/ptrace.h>
#include <sys/syscall.h>
#include <md5wrapper.h> #include <md5wrapper.h>
using namespace DFHack; using namespace DFHack;
@ -45,7 +47,9 @@ namespace {
{ {
private: private:
uint8_t vector_start; uint8_t vector_start;
vector <uint32_t> thread_ids; set <uint32_t> thread_ids;
void waitForSuspend(set<uint32_t> &threads);
public: public:
NormalProcess(uint32_t pid, VersionInfoFactory * known_versions); NormalProcess(uint32_t pid, VersionInfoFactory * known_versions);
~NormalProcess() ~NormalProcess()
@ -230,33 +234,32 @@ bool NormalProcess::asyncSuspend()
bool NormalProcess::suspend() bool NormalProcess::suspend()
{ {
int status;
if(!attached) if(!attached)
return false; return false;
if(suspended) if(suspended)
return true; return true;
if (kill(my_pid, SIGSTOP) == -1)
{ set<uint32_t> threads;
// no, we got an error
perror("kill SIGSTOP error"); for (set<uint32_t>::iterator it = thread_ids.begin(); it != thread_ids.end(); ++it) {
return false; if (syscall(SYS_tgkill, my_pid, *it, SIGSTOP) == -1) {
} cerr << "couldn't stop thread " << *it << endl;
while(true) perror("kill SIGSTOP error");
{ } else {
// we wait on the pid threads.insert(*it);
pid_t w = waitpid(my_pid, &status, 0);
if (w == -1)
{
// child died
perror("DF exited during suspend call");
return false;
}
// stopped -> let's continue
if (WIFSTOPPED(status))
{
break;
} }
} }
if (threads.empty()) {
cerr << "couldn't suspend any of the threads";
return false;
}
waitForSuspend(threads);
if (!threads.empty())
cerr << "couldn't suspend some of the threads";
suspended = true; suspended = true;
return true; return true;
} }
@ -272,49 +275,82 @@ bool NormalProcess::resume()
return false; return false;
if(!suspended) if(!suspended)
return true; return true;
if (ptrace(PTRACE_CONT, my_pid, NULL, NULL) == -1)
{ bool ok = true;
// no, we got an error for (set<uint32_t>::iterator it = thread_ids.begin(); it != thread_ids.end(); ++it) {
perror("ptrace resume error"); int result = ptrace(PTRACE_CONT, *it, NULL, NULL);
return false; if(result == -1)
{
cerr << "couldn't resume thread " << *it << endl;
perror("ptrace resume error");
ok = false;
}
} }
suspended = false;
return true; if (ok)
suspended = false;
return ok;
} }
bool NormalProcess::attach() void NormalProcess::waitForSuspend(set<uint32_t> &threads)
{ {
int status; int status;
while (!threads.empty()) {
pid_t w = waitpid(-1, &status, __WALL);
if (w == -1) {
perror("waitpid");
return;
}
if (threads.find(w) == threads.end()
&& thread_ids.find(w) == thread_ids.end())
continue;
if (WIFSTOPPED(status)) {
threads.erase(w);
thread_ids.insert(w);
} else if (WIFEXITED(status) || WIFSIGNALED(status)) {
threads.erase(w);
thread_ids.erase(w);
}
}
}
bool NormalProcess::attach()
{
if(attached) if(attached)
{ {
if(!suspended) if(!suspended)
return suspend(); return suspend();
return true; return true;
} }
// can we attach?
if (ptrace(PTRACE_ATTACH , my_pid, NULL, NULL) == -1) set<uint32_t> threads;
{ vector<uint32_t> thread_vec;
// no, we got an error
perror("ptrace attach error"); if (!getThreadIDs(thread_vec))
cerr << "attach failed on pid " << my_pid << endl;
return false; return false;
}
while(true) for (vector<uint32_t>::iterator it = thread_vec.begin(); it != thread_vec.end(); ++it) {
{ if (ptrace(PTRACE_ATTACH, *it, NULL, NULL) == -1)
// we wait on the pid
pid_t w = waitpid(my_pid, &status, 0);
if (w == -1)
{
// child died
perror("wait inside attach()");
return false;
}
// stopped -> let's continue
if (WIFSTOPPED(status))
{ {
break; // no, we got an error
perror("ptrace attach error");
cerr << "attach failed on pid " << *it << endl;
continue;
} }
threads.insert(*it);
} }
thread_ids.clear();
waitForSuspend(threads);
if (thread_ids.empty()) {
cerr << "couldn't attach to any threads" << endl;
return false;
}
if (!threads.empty())
cerr << "couldn't attach to some threads" << endl;
suspended = true; suspended = true;
int proc_pid_mem = open(memFile.c_str(),O_RDONLY); int proc_pid_mem = open(memFile.c_str(),O_RDONLY);
@ -350,18 +386,19 @@ bool NormalProcess::detach()
} }
else else
{ {
// detach for (set<uint32_t>::iterator it = thread_ids.begin(); it != thread_ids.end();) {
result = ptrace(PTRACE_DETACH, my_pid, NULL, NULL); // detach
if(result == -1) result = ptrace(PTRACE_DETACH, *it, NULL, NULL);
{ if(result == -1)
cerr << "couldn't detach from process pid" << my_pid << endl; {
perror("ptrace detach"); cerr << "couldn't detach from process pid" << *it << endl;
return false; perror("ptrace detach");
} return false;;
else }
{ thread_ids.erase(it++);
attached = false;
return true;
} }
attached = false;
return true;
} }
} }