Repository navigation
Get a list of threads from Linux debuggee - #47
Conversation
|
|
||
| fn name(&self) -> Option<String>; | ||
| fn thread_id(&self) -> Self::ThreadId; | ||
| } |
There was a problem hiding this comment.
This doesn't feel like the right abstraction. Every thread has it's own register set that can be read and written. Also individual threads can be waiting on the debugger and be running. On Linux at least ptrace attaches to a single thread, not a single process:
https://man7.org/linux/man-pages/man2/ptrace.2.html
A tracee first needs to be attached to the tracer. Attachment and subsequent commands are per thread: in a multithreaded process, every thread can be individually attached to a (potentially different) tracer, or left not attached and thus not debugged. Therefore, "tracee" always means "(one) thread", never "a (possibly multithreaded) process".
This also means that you need to enable the PTRACE_O_TRACECLONE option to attach to spawned threads.
There was a problem hiding this comment.
We could say that Target has one or more Thread's with one being the main thread. read_regs will then ve moved to Thread, but read and write remain part of Target. The unpause method should remain part of Target I think, at least for as long as we don't have an equivalent to gdb's non-stop mode in which individual threads are paused and continued.
There was a problem hiding this comment.
Let me see if I get it:
- Move the read/write registers methods to the thread trait
- Add a way to retrieve a main thread from a target
If that is correct, I have a couple of questions:
- What should be the abstraction for the registers structure be in the trait?
- Should
threads()return all threads (including the main thread?)
There was a problem hiding this comment.
What should be the abstraction for the registers structure be in the trait?
Just type Registers: Debug; or something like that for now I think. It is possible to later add additional bounds as necessary. I think threads should return a concrete thread type though for that to work.
Should threads() return all threads (including the main thread?)
I don't have an opinion on this.
There was a problem hiding this comment.
Should threads() return all threads (including the main thread?)
Yes, I believe so. At least, most (all?) major debuggers impose this behaviour - e.g., if you launch a single-threaded process in gdb and run the info threads command, the output will include the main thread.
- introduces a Thread struct - retrieves a list of threads in linux Notes: - I decided to use procfs because libproc-rs does not support getting the list of threads from linux right now [1]. I imagine this crate being useful in the future anyhow. [1] https://github.com/andrewdavidmackenzie/libproc-rs/blob/123d1762727c9c5d4e81888d8ea1a7f9701ddb45/src/libproc/proc_pid.rs#L260
Fails to compile on OSX. It's not needed there anywhere, so this is better.
Co-authored-by: bjorn3 <bjorn3@users.noreply.github.com>
That is the only place it's being used.
- Was printing the wrong message. - Was not waiting for the thread to start before retrieving the list - of threads. Seen it failed on CI.
Adding to supress warning until it's used on a integration test or by something else.
It's the original type in the kernel.
And remove silly dead code supression :D
nbaksalyar
left a comment
There was a problem hiding this comment.
Thank you for the PR! I left a few suggestions & comments.
|
|
||
| fn name(&self) -> Option<String>; | ||
| fn thread_id(&self) -> Self::ThreadId; | ||
| } |
FYI, I've raised a new issue for Rust: rust-lang/rust#74845 |
Makes API more flexible. Co-authored-by: Nikita Baksalyar <nikita.baksalyar@gmail.com>
The aplly suggestion did remove the previous code?
Note that is inconsistent: rust-lang/rust#74845
nbaksalyar
left a comment
There was a problem hiding this comment.
LGTM, thank you! 👍
I think the questions re trait Thread can be addressed in separate issues/PRs.
|
Yep, there is a bug, it should tolerate tasks that have gone away . I imagine this hit that. |
- Banged the thread change - Had duplicate procfs entries
Some tasks might not be there between getting them from the iterator and actually trying to read their page.
Fixed. Now we just skip threads not found and/or not ready yet. |
It does not make much sense. Threads come and go, we don't have any consistency guarantees when calling the threads method. If the thread info is not ready it can be caught in a subsequent call to threads
| // ok to skip. Thread is gone or it's page is not complete yet. | ||
| } | ||
| Err(err) => return Err(Box::new(err)), | ||
| } |
There was a problem hiding this comment.
A thread could change it's own name I think. Maybe LinuxThread should only store pid and tid and name() would read the current name or return None when the thread doesn't exist anymore?
There was a problem hiding this comment.
Ah... interesting. Ok will check this later today then.
Regarding keeping the pid and tid: as far as I can tell they are the same thing.
So we only need to keep one. Is this correct?
There was a problem hiding this comment.
procfs::process::Task contains the pid of the whole process and the tid of the specific thread. You could also directly store that struct.
There was a problem hiding this comment.
You are right, a thread can change the name. From man procfs:
A thread may modify its comm value, or that of any of other thread in the same thread group
Going with your idea of having the name method return the most recent: do you think it will be fine to swallow all other procfs::ProcError into a None? If so, the user wouldn't know something went wrong. Should we use a result on the name?
Also I'm not sure I understand the benefits of the change. If you are getting a list of threads you either have them all paused or as soon as you get your information it might already be outdated. So the user will always have this issue of seeing old data, even if we push it elsewhere. He will always need to poll the thread list to get up-to-date information.
There was a problem hiding this comment.
Should we use a result on the name?
Yes
If you are getting a list of threads you either have them all paused or as soon as you get your information it might already be outdated.
I think once we support actually debugging threads, we would store a list of all active threads. When using PTRACE_O_TRACECLONE to automatically attach to new threads we have to manually continue the new thread anyway, so we could just as well store it in a list. Only when attaching to a new process do we need to list all active threads. Making the LinuxThread persistent also has the benefit that you could continue and pause individual threads rather than the whole process.
There was a problem hiding this comment.
Done.
I think once we support actually debugging threads, we would store a list of all active threads. When using PTRACE_O_TRACECLONE to automatically attach to new threads we have to manually continue the new thread anyway, so we could just as well store it in a list. Only when attaching to a new process do we need to list all active threads. Making the LinuxThread persistent also has the benefit that you could continue and pause individual threads rather than the whole process.
Alright, I completely missed that the first time you mentioned. I think I get it now - thank you!. We are going to be in lockstep with threads getting notified through ptrace that a new thread was created and adding that to the list of the process.
|
@nbaksalyar @bjorn3 Is this ok? My understanding from the discussion is that 2 essential features are missing:
But I think it might be better to handle them as separate PR's? |
Keeping the task to retrieve the name forces us to change the API to be a bit more complex but it allows to capture thread names changes.
Definitely. This PR underwent quite a number of changes & rebases already - thanks for your patience! |
Fixes #36
Notes:
I dropped a couple of the suggestions in the ticket (#36) because:
I decided to use procfs because libproc-rs does not support getting the list of threads from linux right now. I imagine this crate being useful in the future anyhow.
Having a default associated type in the Threat trait is not stable.
Returning a slice of Threat references does not make much sense to me because the threats change over time and I feel it's more useful to get a fresh current view every time.