Skip to content
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

Parent Process Object is Missing #17541

Closed
juvtib opened this issue Jun 19, 2022 · 9 comments · Fixed by #17545
Closed

Parent Process Object is Missing #17541

juvtib opened this issue Jun 19, 2022 · 9 comments · Fixed by #17545
Labels
Needs-Triage The issue is new and needs to be triaged by a work group. Resolution-Duplicate The issue is a duplicate. Resolution-Fixed The issue is fixed.

Comments

@juvtib
Copy link

juvtib commented Jun 19, 2022

Background

I want to list the processes on my system.

Get-Process 

 NPM(K)    PM(M)      WS(M)     CPU(s)      Id  SI ProcessName
 ------    -----      -----     ------      --  -- -----------
      0     0.00       3.75       0.00    127069 (sd-pam)
      0     0.00       9.84       1.25     962 962 accounts-daemon
      0     0.00       0.00       0.00     117   0 acpi_thermal_pm
...

Further, I would like to sort the processes by parent.

I'm on Linux. And I have a Parent property. I'm not sure if that is available on Windows.

Get-Process | Select-Object @{name='Parent'; expr={$_.Parent.Id}}, Id, Name | Sort-Object Parent, Id

Parent     Id Name
------     -- ----
       105455 Web Content
1        2617 firefox
1      135731 code --no-sandbox --force-user-env --unity-launch --enable-crashpad
1284     1362 Thunar
1370   127909 thunderbird
1667     1688 pwsh
1688    11884 vim

Issue

The issue I noticed was that some of the Parent Process IDs (PPID) were missing.

Note there are no IDs in the Parent column below.

Get-Process | Select-Object @{name='Parent'; expr={$_.Parent.Id}}, Id, Name | Sort-Object Parent, Id

Parent     Id Name
------     -- ----
            1 systemd
            2 kthreadd
          847 UVM global queue
          848 UVM deferred release queue
          849 UVM Tools Event Queue
...
       105455 Web Content
       105458 Web Content
...

It is not clear to me if there is a bug with PowerShell, a bug with .NET, or something I don't understand.

The one-liner I'm issuing seems correct to me.

But I'm not getting a Parent Process ID for worker processes, which is the output I expect.

I don't think there is a permission issue because I am the only user. And the native task manager shows the correct parent and child relationships for processes.

The affected processes are mostly Mozilla Firefox and Thunderbird workers.

Screenshot_2022-06-19_15-52-37

Environment

$PSVersionTable | Format-Table -Wrap

Name                           Value
----                           -----
PSVersion                      7.2.4
PSEdition                      Core
GitCommitId                    7.2.4
OS                             Linux 5.18.5-1-MANJARO #1 SMP PREEMPT_DYNAMIC
                               Thu Jun 16 12:28:47 UTC 2022
Platform                       Unix
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

@juvtib juvtib added the Needs-Triage The issue is new and needs to be triaged by a work group. label Jun 19, 2022
@jborean93
Copy link
Collaborator

jborean93 commented Jun 19, 2022

What version of PowerShell are you running on Windows. I still see the Parent property there. The Parent property is a code property added by PowerShell, I believe it was in 6 so it won't be present in Windows PowerShell (powershell.exe v5.1).

Keep in mind Windows recycles their PIDs fairly aggressively so you could get some false links between a process another another one if the original parent has exited.

@juvtib
Copy link
Author

juvtib commented Jun 19, 2022

You appear to be referring to the property which is always available.

Get-Process | Get-Member | Where-Object { $_.Name -match "parent" } | Format-List           

TypeName   : System.Diagnostics.Process
Name       : Parent
MemberType : CodeProperty
Definition : System.Object Parent{get=GetParentProcess;}

I'm referring to the instances. No PPID value is shown using my calculated property.

I do not use and do not have Windows.

Get-Process | 
  Select-Object @{name='PPID'; expr={$_.Parent.Id}}, Name | 
  Where-Object { $null -eq $_.PPID } | 
  Sort-Object PPID, Name 

PPID Name
---- ----
     Isolated Servic
     Isolated Web Co
     Isolated Web Co
     Isolated Web Co
     Isolated Web Co
     Isolated Web Co
     Isolated Web Co
     Isolated Web Co
     Isolated Web Co
     kthreadd
     Privileged Cont
     Privileged Cont
     Privileged Cont
     Privileged Cont
     Privileged Cont
     RDD Process
     RDD Process
     Socket Process
     Socket Process
     Socket Process
     Socket Process
     Socket Process
     systemd
     UVM deferred release queue
     UVM global queue
     UVM Tools Event Queue
     Web Content
     Web Content
     Web Content
     Web Content
     Web Content
     Web Content
     Web Content
     Web Content
     Web Content
     Web Content
     Web Content
     Web Content
     Web Content
     Web Content
     Web Content
     Web Content

@jborean93
Copy link
Collaborator

My apologies, I thought the issue was that the Parent property itself was missing on Windows but I see that is not what you were saying. Looking into the code PowerShell is essentially looking at /proc/$pid/stat as per

public static int GetProcFSParentPid(int pid)
{
const int invalidPid = -1;
// read /proc/<pid>/stat
// 4th column will contain the ppid, 92 in the example below
// ex: 93 (bash) S 92 93 2 4294967295 ...
var path = $"/proc/{pid}/stat";
try
{
var stat = System.IO.File.ReadAllText(path);
var parts = stat.Split(' ', 5);
if (parts.Length < 5)
{
return invalidPid;
}
return int.Parse(parts[3]);
}
catch (Exception)
{
return invalidPid;
}
}
. The 4th column is the parent process id. Does the following work or does it error for you?

Function Get-ParentProcess {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory, ValueFromPipeline)]
        [System.Diagnostics.Process[]]
        $Process
    )

    process {
        foreach ($proc in $Process) {
            $stat = Get-Content -LiteralPath "/proc/$($proc.Id)/stat" -Raw
            $ppid = $stat.Split(' ')[3]

            Write-Verbose -Message "PPID is $ppid"
            Get-Process -Id $ppid
        }
    }
}

Get-Process ... | Get-ParentProcess -Verbose

If this fails for some reason it will at least indicate what the problem was unlike the Parent property which just silently ignores any exceptions.

@juvtib
Copy link
Author

juvtib commented Jun 20, 2022

Function Get-ParentProcess {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory, ValueFromPipeline)]
        [System.Diagnostics.Process[]]
        $Process
    )

    process {
        foreach ($proc in $Process) {
            $stat = Get-Content -LiteralPath "/proc/$($proc.Id)/stat" -Raw
            $ppid = $stat.Split(' ')[3]

            Write-Verbose -Message "PPID is $ppid"
            Get-Process -Id $ppid
        }
    }
}

Get-Process -Id 105455 | Get-ParentProcess -Verbose


VERBOSE: PPID is S
Get-Process: 
Line |
  15 |              Get-Process -Id $ppid
     |                              ~~~~~
     | Cannot bind parameter 'Id'. Cannot convert value "S" to type "System.Int32". Error: "Input string was not in a correct format."

After some digging, it appears the bug is splitting on space.

Most of the affected processes contain spaces in their apparent names.

var parts = stat.Split(' ', 5); 
Get-Process | 
  Where-Object { $null -eq $_.Parent.Id } | 
  ForEach-Object {
     Get-Content -LiteralPath "/proc/$($_.Id)/stat" -Raw
  } | 
  ForEach-Object {
     $_.Substring(0,75)
  }


3177 (Isolated Web Co) S 2937 1284 1284 0 -1 4194560 172529 0 924 0 3529 12
82263 (Isolated Web Co) S 82104 1284 1284 0 -1 4194560 9421304 0 84997387 0
82450 (Isolated Web Co) S 82104 1284 1284 0 -1 4194560 187535 0 2112 0 3049
83835 (Isolated Web Co) S 2617 1284 1284 0 -1 4194560 259935 0 8260 0 23795
105212 (Isolated Web Co) S 82104 1284 1284 0 -1 4194560 21147 0 0 0 128 43 
141300 (Isolated Web Co) S 3229 1284 1284 0 -1 4194560 25914 0 2791 0 771 3
141355 (Isolated Web Co) S 3229 1284 1284 0 -1 4194560 9323 0 0 0 29 19 0 0
185104 (Isolated Web Co) S 184933 1284 1284 0 -1 4194560 255626 0 10621 0 1
201038 (Isolated Web Co) S 200795 1284 1284 0 -1 4194560 56720 0 173 0 493 
201127 (Isolated Web Co) S 200795 1284 1284 0 -1 4194560 55610 0 151 0 392 
201174 (Isolated Web Co) S 200795 1284 1284 0 -1 4194560 70349 0 77 0 441 9
203995 (Isolated Web Co) S 200795 1284 1284 0 -1 4194560 38886 0 35 0 146 4
204042 (Isolated Web Co) S 200795 1284 1284 0 -1 4194560 35830 0 75 0 362 1
204434 (Isolated Web Co) S 200795 1284 1284 0 -1 4194560 15414 0 81 0 66 25
205640 (Isolated Web Co) S 200795 1284 1284 0 -1 4194560 39121 0 29 0 148 3
209752 (Isolated Web Co) S 2937 1284 1284 0 -1 4194560 62907 0 158 0 336 50
209819 (Isolated Web Co) S 2937 1284 1284 0 -1 4194560 17616 0 385 0 97 23 
2 (kthreadd) S 0 0 0 0 -1 2129984 0 0 0 0 0 22 0 0 20 0 1 0 22 0 0 18446744
2714 (Privileged Cont) S 2617 1284 1284 0 -1 4194560 41170 0 6 0 294 157 0 
3034 (Privileged Cont) S 2937 1284 1284 0 -1 4194560 6521 0 0 0 56 36 0 0 2
3324 (Privileged Cont) S 3229 1284 1284 0 -1 4194560 13953 0 4 0 119 77 0 0
82201 (Privileged Cont) S 82104 1284 1284 0 -1 4194560 14005 0 0 0 207 184 
185046 (Privileged Cont) S 184933 1284 1284 0 -1 4194560 7142 0 0 0 32 19 0
200891 (Privileged Cont) S 200795 1284 1284 0 -1 4194560 9790 0 0 0 47 24 0
2930 (RDD Process) S 2617 1284 1284 0 -1 4194560 3913 0 1 0 18 16 0 0 20 0 
82555 (RDD Process) S 82104 1284 1284 0 -1 4194560 9689 0 8690 0 681 581 0 
2678 (Socket Process) S 2617 1284 1284 0 -1 4194560 3623 0 1 0 16 19 0 0 20
2999 (Socket Process) S 2937 1284 1284 0 -1 4194560 3639 0 0 0 39 26 0 0 20
3292 (Socket Process) S 3229 1284 1284 0 -1 4194560 3596 0 0 0 17 18 0 0 20
82167 (Socket Process) S 82104 1284 1284 0 -1 4194560 3601 0 0 0 9 12 0 0 2
184971 (Socket Process) S 184933 1284 1284 0 -1 4194560 3602 0 0 0 6 4 0 0 
200858 (Socket Process) S 200795 1284 1284 0 -1 4194560 3604 0 0 0 6 3 0 0 
1 (systemd) S 0 1 1 0 -1 4194560 32765 74580425 169 404020 140 169 2301895 
848 (UVM deferred release queue) S 2 0 0 0 -1 2129984 0 0 0 0 0 0 0 0 20 0 
847 (UVM global queue) S 2 0 0 0 -1 2129984 0 0 0 0 0 0 0 0 20 0 1 0 726 0 
849 (UVM Tools Event Queue) S 2 0 0 0 -1 2129984 0 0 0 0 0 0 0 0 20 0 1 0 7
105455 (Web Content) S 82104 1284 1284 0 -1 4194560 5027 0 0 0 20 16 0 0 20
105458 (Web Content) S 82104 1284 1284 0 -1 4194560 5033 0 0 0 19 16 0 0 20
128011 (Web Content) S 127909 1284 1284 0 -1 4194304 7705 0 0 0 49 21 0 0 2
144932 (Web Content) S 3229 1284 1284 0 -1 4194560 4852 0 0 0 15 9 0 0 20 0
158355 (Web Content) S 82104 1284 1284 0 -1 4194560 5026 0 0 0 13 11 0 0 20
174293 (Web Content) S 3229 1284 1284 0 -1 4194560 4840 0 0 0 7 10 0 0 20 0
174296 (Web Content) S 3229 1284 1284 0 -1 4194560 4838 0 0 0 11 6 0 0 20 0
185323 (Web Content) S 184933 1284 1284 0 -1 4194560 4757 0 0 0 11 8 0 0 20
185407 (Web Content) S 184933 1284 1284 0 -1 4194560 4754 0 0 0 11 7 0 0 20
185556 (Web Content) S 184933 1284 1284 0 -1 4194560 4757 0 0 0 9 8 0 0 20 
192103 (Web Content) S 2617 1284 1284 0 -1 4194560 4782 0 0 0 8 5 0 0 20 0 
199060 (Web Content) S 2617 1284 1284 0 -1 4194560 4778 0 0 0 8 4 0 0 20 0 
200119 (Web Content) S 2617 1284 1284 0 -1 4194560 4775 0 0 0 6 5 0 0 20 0 
208265 (Web Content) S 200795 1284 1284 0 -1 4194560 4905 0 0 0 10 5 0 0 20
208700 (Web Content) S 200795 1284 1284 0 -1 4194560 4917 0 0 0 8 6 0 0 20 
209208 (Web Content) S 200795 1284 1284 0 -1 4194560 4896 0 0 0 8 5 0 0 20 
209987 (Web Content) S 2937 1284 1284 0 -1 4194560 4683 0 0 0 7 3 0 0 20 0 
210030 (Web Content) S 2937 1284 1284 0 -1 4194560 4689 0 0 0 6 4 0 0 20 0 
210152 (Web Content) S 2937 1284 1284 0 -1 4194560 4611 0 0 0 5 1 0 0 20 0 

A minority of processes appear to spawn directly from process 0. And I am picking those up as $nulls.

I assume these child processes should have a parent object in their output so I could $_.Parent.Id.

Get-Process | 
  Where-Object { $null -eq $_.Parent.Id } | 
  ForEach-Object {
     Get-Content -LiteralPath "/proc/$($_.Id)/stat" -Raw
  } | 
  ForEach-Object {
     $_.Substring(0,75)
  } | 
  Select-String '\(\w+\)'


2 (kthreadd) S 0 0 0 0 -1 2129984 0 0 0 0 0 22 0 0 20 0 1 0 22 0 0 18446744
1 (systemd) S 0 1 1 0 -1 4194560 32765 74619170 169 404034 141 169 2302207 

I don't know C# or the /proc/$($_.Id)/stat format well.

But you need something that properly captures the second column.

Then you can access the fourth column correctly.

This might also affect macOS.

Get-Process | 
  Where-Object { $null -eq $_.Parent.Id } | 
  ForEach-Object {
     Get-Content -LiteralPath "/proc/$($_.Id)/stat" -Raw
  } | 
  ForEach-Object {
     ($_.Substring(0,75)) -match '^(\d+) (\(.+\)) (\w) (\d+)' | Out-Null
     $matches[4]
  } | 
  Select-Object -First 5


2937
82104
82104
2617
82104

Mike

@jborean93
Copy link
Collaborator

jborean93 commented Jun 20, 2022

After some digging, it appears the bug is splitting on space.

That does seem to be the case, the docs for /proc/[pid]/stat can be found at https://man7.org/linux/man-pages/man5/proc.5.html with the 2nd column stating

The filename of the executable, in parentheses. Strings longer than TASK_COMM_LEN (16) characters (including the terminating null byte) are silently truncated. This is visible whether or not the executable is swapped out.

Looking into fs/proc/array.c I can see https://github.com/torvalds/linux/blob/a111daf0c53ae91e71fd2bfe7497862d14132e3e/fs/proc/array.c#L567-L572 which indicates it's a simple value wrapped in (...) limited to 64 characters (not 16). I think you are right though and the logic in PowerShell needs to be better than to split by space. We could try and beef up the matching with regex but there's no way to guarantee the 2nd column record doesn't contain ( or ) itself like 2 (testing (123) name) S 0 0 0 ... making it quite difficult to parse properly. What might be a better method is to read /proc/[pid/status and find the line starting with PPid: \d+

Get-Content /proc/$pid/status | Select-String -Pattern 'PPid:\s+\d+'

This is helpful because each line is a new record and the labels clearly indicate what the value is for. The label also does not appear to be localized so this should be fine across languages https://github.com/torvalds/linux/blob/a111daf0c53ae91e71fd2bfe7497862d14132e3e/fs/proc/array.c#L183.

This might also affect macOS.

macOS uses a different method to get the ppid, there's a system call that seems to be available, or at least accessible through libpsl-native as per

internal static int NonWindowsGetProcessParentPid(int pid)
{
return IsMacOS ? Unix.NativeMethods.GetPPid(pid) : Unix.GetProcFSParentPid(pid);
}
.

Edit: Just for reference sake this is the GetPPid call implemented in libpsl-native https://github.com/PowerShell/PowerShell-Native/blob/677c086f20209f70ecad379ec48de2562f1db668/src/libpsl-native/src/getppid.cpp#L27-L48.

GitHub
Linux kernel source tree. Contribute to torvalds/linux development by creating an account on GitHub.
GitHub
Linux kernel source tree. Contribute to torvalds/linux development by creating an account on GitHub.

@iSazonov
Copy link
Collaborator

Dup #12908 (PR #12925)

@jborean93
Copy link
Collaborator

I've created an alternative solution to #12925, hopefully one of the 2 can move forward #17545.

@ghost ghost added the In-PR Indicates that a PR is out for the issue label Jun 20, 2022
@juvtib
Copy link
Author

juvtib commented Jun 21, 2022

Thanks for taking a look at this.

I'm sorry I didn't see the duplicate issue earlier. The connection was not obvious to me until after your comments.

Hopefully, one of the patches will be accepted.

Mike

@ghost
Copy link

ghost commented Jun 22, 2022

This issue has been marked as duplicate and has not had any activity for 1 day. It has been closed for housekeeping purposes.

@ghost ghost closed this as completed Jun 22, 2022
@ghost ghost added Resolution-Fixed The issue is fixed. and removed In-PR Indicates that a PR is out for the issue labels Sep 29, 2022
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needs-Triage The issue is new and needs to be triaged by a work group. Resolution-Duplicate The issue is a duplicate. Resolution-Fixed The issue is fixed.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants