PDA

View Full Version : [QThread] ptrace in thread



Macok
7th February 2009, 19:51
I'm writting an application which will be doing something with memory of other process.
These actions may take some time, so I decided to place them in another thread.
Here's the simplified class of this thread:
Thread : public QThread{
public:
int pid;
void run();
};and run() function:
void Thread::run(){
ptrace(PTRACE_ATTACH, pid);
sleep(60);
ptrace(PTRACE_DETACH, pid);
}Of course i put those sleep for example only, in my program there are another functions, but it doesn't matter.

User can start or stop this thread by clicking on buttons - Start, and Cancel.
I have no problems with starting this thread, here's the simplified code:
connect(ui->pushButton, SIGNAL(clicked()), this, SLOT(startThread()));

(...)

void MainWindow::startThread(){
thread.pid=getPid();
thread.start();
}But problem appears, when user clicks on Cancel button.
Before doing anything with memory of other process it's needed to attach this process.
As you see, I'm doing it at the beggining of run() function.
But when process is attached it's impossible to use it - it must be detached first.
That's why i added the last line to run():
ptrace(PTRACE_DETACH, pid);But when user clicks Cancel process also need to be detached, so I overloaded terminate() function:
void Thread::terminate(){
ptrace(PTRACE_DETACH, pid);
QThread::terminate();
}
connect(ui->pushButton_2, SIGNAL(clicked()), this, SLOT(stopThread()));

(...)

void MainWindow::stopThread(){
thread.terminate();
}I think that should work fine, but it doesn't.
When user doesn't click Cancel, 60 seconds passess and process is detaching.
But when user click Cancel detaching fails.
How is it possible?
The same function, called in exactly the same way, and first works but second doesn't.

Thanks in advance and sorry for bad english!

wysota
7th February 2009, 21:16
I would do it differently. I would not use another thread but instead I would call ::waitpid() with the WNOHANG option to make it return immediately. This would make sure your parent process never hangs on waitpid() and you don't have to use a thread but instead you can use a simple timer that will call waitpid() periodically and emit a signal or post an event whenever waitpid() returns meaningful data. Of course you can still do it with an external thread (again using WNOHANG and starting an event loop for the thread) which will keep your tracing thread responsive all the time. Then there is no need to call terminate() which you should never ever do. Instead just raise a flag or emit a signal or post an event that will be checked by the tracing thread and let it end itself gracefully.

Here is a variant without threads:

QTimer *t = new QTimer(this);
connect(t, SIGNAL(timeout()), this, SLOT(checkWait()));
t->start(1000); // or whatever else you want here
//...
void X::checkWait(){
int status;
int options = WNOHANG|WUNTRACED; // just a guess...
pid_t pid = ::waitpid(child, &status, options);
if(WIFSTOPPED(status)){
// do tracing here or:
emit childStopped(); // connect to this signal in your tracing method
}
}

Threaded variant:

Thread *thread = new Thread(...);
//...
connect(cancelButton, SIGNAL(clicked()), thread, SLOT(quit()));
thread->start();
thread->moveToThread(thread); // change thread affinity of the thread object
//...
void Thread::run(){
// attach trace
QTimer *t = new QTimer(...); // same as above
exec(); // start event loop and block
// detach trace
}

Macok
8th February 2009, 13:54
Thanks for the reply.
I don't understand your method, so I'll better stay with the threaded variant :)
Maybe I write something more about my program:
It'll be memory addresses searcher, something like CheatEngine for windows.
User puts process name and value, then program scan whole memory of the process and tells user the memory addresses when this value is placed.

Your idea with timer seems to be good, but I have a problem with connecting timeout() signal to a proper slot.
Here's the code:
//this function is called when user click Start button
void MainWindow::startThread(){
ui->pushButton_2->setEnabled(true);
thread=new Thread;
connect(ui->pushButton_2, SIGNAL(clicked()), thread, SLOT(quit()));
thread->pid=proc[ui->comboBox->currentIndex()].pid;
thread->start();
thread->moveToThread(thread);
}
void Thread::run(){
ptrace(PTRACE_ATTACH, pid, 0, 0);
::wait();

QTimer *t=new QTimer(this);

connect(t, SIGNAL(timeout()), this, SLOT(checkWait()));
t->start(1000);
exec();
ptrace(PTRACE_DETACH, pid, 0, 0);
}
void Thread::checkWait(){
//Here I will place memory scanning
}It's compiling without any warnings, but when I run program it write out that in the console:
Object::connect: No such slot QThread::checkWait()What's wrong?

PS. Why do you use dynamic memory allocation when creating Thread object (thread=new Thread)?
What diffrence does it make?

wysota
8th February 2009, 21:04
void Thread::checkWait(){
//Here I will place memory scanning
}

Oh, I see your usecase is a bit different. In that case read this article:
http://doc.trolltech.com/qq/qq27-responsive-guis.html

By the way, the error means you didn't declare checkWait() as a slot in your class or you forgot the Q_OBJECT macro.


PS. Why do you use dynamic memory allocation when creating Thread object (thread=new Thread)?
What diffrence does it make?

So that it doesn't get destroyed when the method where it is constructed runs out of scope.

Macok
8th February 2009, 22:00
Omg, I declared checkWait() as public, instead public slot.
Thanks a lot!