-
-
Notifications
You must be signed in to change notification settings - Fork 411
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Process tree loop is possible with some kernels #1017
base: main
Are you sure you want to change the base?
Conversation
Note this ptrace(2) behavior of FreeBSD kernel is documented in the man page:
|
ProcessList.c
Outdated
if(display_size < vsize) { | ||
for (int i = 0; i < vsize; i++) { | ||
Process* process = (Process*)Vector_get(this->processes, i); | ||
if(!process->isRoot && Vector_indexOf(this->displayList, process, Process_pidEqualCompare) == -1) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that Vector_indexOf
takes linear time, so this special case bumps tree build complexity to O(n^2)
.
I'd suggest marking all processes as unseen in the root finding phase before sort by setting tree_depth
to a guard value like UINT_MAX
and checking processes for that guard value instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You may also want to chase to loop in case the process you encounter first is not one of the loop processes, but a descendant of one of them.
0bb1bbf
to
67cba08
Compare
Good idea; however I think setting
I currently have no idea how to handle it efficiently. |
Start from the first missing process you see. As long as the parent of the current process has not been marked as visited, you mark the current process as visited and switch to the parent. When you see that the parent has already been marked as seen, both the current process and the parent are known to belong to the loop body, so you can start building the tree from either. Continue the scan for missing processes in case there are more missing processes. This discovery step is still |
67cba08
to
fe4ccb2
Compare
ProcessList.c
Outdated
do { | ||
// Make sure we break at the loop itself, not a descendant of it. | ||
process->tree_depth = 1; | ||
Process *parent = ProcessList_findProcess(this, process->ppid); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should use Process_getParentPid
for consistency with the tree sort code.
if(!parent) break; | ||
process = parent; | ||
j--; | ||
} while(!process->isRoot && !process->tree_depth); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do I understand correctly that all three of j > 0
, if(!parent) break;
and !process->isRoot
in this while
are sanity checks that should never trigger if the logic of this code is correct? If so, they likely should have assertions so that logic errors cannot be introduced unnoticed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also note that, as you exit this loop on process->tree_depth != 0
, you'll need to reset it to 0 again for the newfound root.
ProcessList.c
Outdated
// here. | ||
int display_size = Vector_size(this->displayList); | ||
if(display_size < vsize) { | ||
for (int i = 0, j = vsize - display_size; i < vsize && j > 0; i++) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
j
looks kinds meaningless - you don't try to track it in the inner loop, and it differs from vsize - display_size
either as you don't know the number of nodes added to the displayList.
Yep, I missed this. The outer
Other checks you mentioned are sanity checks. |
fe4ccb2
to
a28fdf1
Compare
How about something like |
a28fdf1
to
52104ff
Compare
Makes sence, |
52104ff
to
1df5d1b
Compare
Added assert(3)s. |
Looks good to me from the content POV. Sorry, I'm not familiar enough with the styleguide to review code style, so I'll leave that to @BenBE or someone else from the main team. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The nesting of this section of the code is somewhat borderline (as is most of that part of the existing code in this area) in regards to indentation and complexity. So while most comments seem to indicate a minor white space change, they are mostly for completeness. ;-)
Furthermore some of the checks used to reduce the number of loop iterations may be worth pulling out to the for
loop iterating over the around lines 320 or 325.
Another performance consideration affecting this code affects the process parent lookup around line 328: Have you checked if looking up that process object using the process hashtable is viable? If so, search might be considerably faster, as no linear search has to be performed comparing process IDs.
Vector_add(this->displayList, process); | ||
ProcessList_buildTreeBranch(this, process->pid, 0, 0, process->showChildren); | ||
continue; | ||
} | ||
} | ||
|
||
// Under some ptrace(2) implementations, a process ptrace(2)-attaching a | ||
// parent process in its process tree will causing the traced process to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// parent process in its process tree will causing the traced process to | |
// parent process in its process tree will cause the traced process to |
// process tree. In this case build separated tree(s) for loop(s) left | ||
// here. | ||
int display_size = Vector_size(this->displayList); | ||
if(display_size < vsize) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if(display_size < vsize) { | |
if (display_size < vsize) { |
if(display_size < vsize) { | ||
for (int i = 0; i < vsize; i++) { | ||
Process* process = (Process*)Vector_get(this->processes, i); | ||
if(!process->isRoot && process->tree_depth == 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if(!process->isRoot && process->tree_depth == 0) { | |
if (!process->isRoot && process->tree_depth == 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it may be worth considering to invert this condition to allow skipping the entire loop body that follows and thus reduce the overall indentation level.
process->tree_depth = 1; | ||
Process *parent = ProcessList_findProcess(this, Process_getParentPid(process)); | ||
assert(parent != NULL); | ||
if(!parent) break; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if(!parent) break; | |
if (!parent) | |
break; |
if(!parent) break; | ||
process = parent; | ||
assert(!process->isRoot); | ||
} while(!process->isRoot && !process->tree_depth); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
} while(!process->isRoot && !process->tree_depth); | |
} while (!process->isRoot && !process->tree_depth); |
process->indent = 0; | ||
Vector_add(this->displayList, process); | ||
ProcessList_buildTreeBranch(this, process->pid, 0, 0, process->showChildren); | ||
if(Vector_size(this->displayList) == vsize) break; // No more loop exist. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if(Vector_size(this->displayList) == vsize) break; // No more loop exist. | |
if (Vector_size(this->displayList) == vsize) | |
break; // No more loops exist. |
ProcessList_buildTreeBranch(this, process->pid, 0, 0, process->showChildren); | ||
if(Vector_size(this->displayList) == vsize) break; // No more loop exist. | ||
} | ||
// There appears to have more loop(s) left, trying to continue find. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this explanatory comment somewhat confuses in the context of the previous check one nesting layer in.
|
Trying this PR locally and there seems to be some problem I don't fully understand yet. |
@Low-power Sorry, I missed that you're missing the other part - |
I don't think I understand this, but shouldn't marking a node as root ( |
Yeah, that's the check I'm suggesting to add. Before this PR that was not needed because of a special case in |
@Low-power Could you please update your PR to fix the issues that people reported? TIA. |
Some ptrace(2) implementations have a behavior of 're-parenting' the traced process to the tracer, which may causing problem with htop 'tree view'.
For example, to create such a loop on FreeBSD:
Use gdb(1) to attach the current shell (bash(1)) in a terminal (
pts/46
):Inspect processes associated with the previous terminal using ps(1), in another terminal session:
Note the 2 processes has formed a loop.
htop crashing messages:
This commit adds a rescan logic, which will rescan for looped processes once there appears to be one or more loops.