|
| 1 | +// Licensed to the .NET Foundation under one or more agreements. |
| 2 | +// The .NET Foundation licenses this file to you under the MIT license. |
| 3 | + |
| 4 | +using System; |
| 5 | +using System.Runtime.InteropServices; |
| 6 | + |
| 7 | +// C# equivalents for <sys/procfs.h> structures. See: struct lwpsinfo, struct psinfo. |
| 8 | +// We read directly onto these from procfs, so the layouts and sizes of these structures |
| 9 | +// must _exactly_ match those in <sys/procfs.h> |
| 10 | + |
| 11 | +// analyzer incorrectly flags fixed buffer length const |
| 12 | +// (https://github.com/dotnet/roslyn/issues/37593) |
| 13 | +#pragma warning disable CA1823 |
| 14 | + |
| 15 | +internal static partial class Interop |
| 16 | +{ |
| 17 | + internal static partial class @procfs |
| 18 | + { |
| 19 | + internal const string RootPath = "/proc/"; |
| 20 | + private const string psinfoFileName = "/psinfo"; |
| 21 | + private const string lwpDirName = "/lwp"; |
| 22 | + private const string lwpsinfoFileName = "/lwpsinfo"; |
| 23 | + |
| 24 | + // Constants from sys/procfs.h |
| 25 | + private const int PRARGSZ = 80; |
| 26 | + private const int PRCLSZ = 8; |
| 27 | + private const int PRFNSZ = 16; |
| 28 | + |
| 29 | + [StructLayout(LayoutKind.Sequential)] |
| 30 | + internal struct @timestruc_t |
| 31 | + { |
| 32 | + public long tv_sec; |
| 33 | + public long tv_nsec; |
| 34 | + } |
| 35 | + |
| 36 | + // lwp ps(1) information file. /proc/<pid>/lwp/<lwpid>/lwpsinfo |
| 37 | + // Equivalent to sys/procfs.h struct lwpsinfo |
| 38 | + // "unsafe" because it has fixed sized arrays. |
| 39 | + [StructLayout(LayoutKind.Sequential)] |
| 40 | + internal unsafe struct @lwpsinfo |
| 41 | + { |
| 42 | + private int pr_flag; /* lwp flags (DEPRECATED; do not use) */ |
| 43 | + public uint pr_lwpid; /* lwp id */ |
| 44 | + private long pr_addr; /* internal address of lwp */ |
| 45 | + private long pr_wchan; /* wait addr for sleeping lwp */ |
| 46 | + public byte pr_stype; /* synchronization event type */ |
| 47 | + public byte pr_state; /* numeric lwp state */ |
| 48 | + public byte pr_sname; /* printable character for pr_state */ |
| 49 | + public byte pr_nice; /* nice for cpu usage */ |
| 50 | + private short pr_syscall; /* system call number (if in syscall) */ |
| 51 | + private byte pr_oldpri; /* pre-SVR4, low value is high priority */ |
| 52 | + private byte pr_cpu; /* pre-SVR4, cpu usage for scheduling */ |
| 53 | + public int pr_pri; /* priority, high value is high priority */ |
| 54 | + private ushort pr_pctcpu; /* fixed pt. % of recent cpu time */ |
| 55 | + private ushort pr_pad; |
| 56 | + public timestruc_t pr_start; /* lwp start time, from the epoch */ |
| 57 | + public timestruc_t pr_time; /* usr+sys cpu time for this lwp */ |
| 58 | + private fixed byte pr_clname[PRCLSZ]; /* scheduling class name */ |
| 59 | + private fixed byte pr_name[PRFNSZ]; /* name of system lwp */ |
| 60 | + private int pr_onpro; /* processor which last ran this lwp */ |
| 61 | + private int pr_bindpro; /* processor to which lwp is bound */ |
| 62 | + private int pr_bindpset; /* processor set to which lwp is bound */ |
| 63 | + private int pr_lgrp; /* lwp home lgroup */ |
| 64 | + private fixed int pr_filler[4]; /* reserved for future use */ |
| 65 | + } |
| 66 | + private const int PR_LWPSINFO_SIZE = 128; // for debug assertions |
| 67 | + |
| 68 | + // process ps(1) information file. /proc/<pid>/psinfo |
| 69 | + // Equivalent to sys/procfs.h struct psinfo |
| 70 | + // "unsafe" because it has fixed sized arrays. |
| 71 | + [StructLayout(LayoutKind.Sequential)] |
| 72 | + internal unsafe struct @psinfo |
| 73 | + { |
| 74 | + private int pr_flag; /* process flags (DEPRECATED; do not use) */ |
| 75 | + public int pr_nlwp; /* number of active lwps in the process */ |
| 76 | + public int pr_pid; /* unique process id */ |
| 77 | + public int pr_ppid; /* process id of parent */ |
| 78 | + public int pr_pgid; /* pid of process group leader */ |
| 79 | + public int pr_sid; /* session id */ |
| 80 | + public uint pr_uid; /* real user id */ |
| 81 | + public uint pr_euid; /* effective user id */ |
| 82 | + public uint pr_gid; /* real group id */ |
| 83 | + public uint pr_egid; /* effective group id */ |
| 84 | + private long pr_addr; /* address of process */ |
| 85 | + public ulong pr_size; /* size of process image in Kbytes */ |
| 86 | + public ulong pr_rssize; /* resident set size in Kbytes */ |
| 87 | + private ulong pr_pad1; |
| 88 | + private ulong pr_ttydev; /* controlling tty device (or PRNODEV) */ |
| 89 | + private ushort pr_pctcpu; /* % of recent cpu time used by all lwps */ |
| 90 | + private ushort pr_pctmem; /* % of system memory used by process */ |
| 91 | + public timestruc_t pr_start; /* process start time, from the epoch */ |
| 92 | + public timestruc_t pr_time; /* usr+sys cpu time for this process */ |
| 93 | + public timestruc_t pr_ctime; /* usr+sys cpu time for reaped children */ |
| 94 | + public fixed byte pr_fname[PRFNSZ]; /* name of execed file */ |
| 95 | + public fixed byte pr_psargs[PRARGSZ]; /* initial characters of arg list */ |
| 96 | + public int pr_wstat; /* if zombie, the wait() status */ |
| 97 | + public int pr_argc; /* initial argument count */ |
| 98 | + private long pr_argv; /* address of initial argument vector */ |
| 99 | + private long pr_envp; /* address of initial environment vector */ |
| 100 | + private byte pr_dmodel; /* data model of the process */ |
| 101 | + private fixed byte pr_pad2[3]; |
| 102 | + public int pr_taskid; /* task id */ |
| 103 | + public int pr_projid; /* project id */ |
| 104 | + public int pr_nzomb; /* number of zombie lwps in the process */ |
| 105 | + public int pr_poolid; /* pool id */ |
| 106 | + public int pr_zoneid; /* zone id */ |
| 107 | + public int pr_contract; /* process contract */ |
| 108 | + private fixed int pr_filler[1]; /* reserved for future use */ |
| 109 | + public lwpsinfo pr_lwp; /* information for representative lwp */ |
| 110 | + // C# magic: Accessor method to get a Span for pr_psargs[] |
| 111 | + // Does not affect the size or layout of this struct. |
| 112 | + internal ReadOnlySpan<byte> PsArgsSpan => |
| 113 | + MemoryMarshal.CreateReadOnlySpan(ref pr_psargs[0], PRARGSZ); |
| 114 | + } |
| 115 | + private const int PR_PSINFO_SIZE = 416; // for debug assertions |
| 116 | + |
| 117 | + // Ouput type for TryGetThreadInfoById() |
| 118 | + internal struct ThreadInfo |
| 119 | + { |
| 120 | + internal uint Tid; |
| 121 | + internal int Priority; |
| 122 | + internal int NiceVal; |
| 123 | + internal char Status; |
| 124 | + internal Interop.Sys.TimeSpec StartTime; |
| 125 | + internal Interop.Sys.TimeSpec CpuTotalTime; // user+sys |
| 126 | + // add more fields when needed. |
| 127 | + } |
| 128 | + |
| 129 | + // Ouput type for TryGetProcessInfoById() |
| 130 | + internal struct ProcessInfo |
| 131 | + { |
| 132 | + internal int Pid; |
| 133 | + internal int ParentPid; |
| 134 | + internal int SessionId; |
| 135 | + internal int Priority; |
| 136 | + internal int NiceVal; |
| 137 | + internal nuint VirtualSize; |
| 138 | + internal nuint ResidentSetSize; |
| 139 | + internal Interop.Sys.TimeSpec StartTime; |
| 140 | + internal Interop.Sys.TimeSpec CpuTotalTime; // user+sys |
| 141 | + internal string? Args; |
| 142 | + // add more fields when needed. |
| 143 | + } |
| 144 | + |
| 145 | + internal static string GetInfoFilePathForProcess(int pid) => |
| 146 | + $"{RootPath}{(uint)pid}{psinfoFileName}"; |
| 147 | + |
| 148 | + internal static string GetLwpDirForProcess(int pid) => |
| 149 | + $"{RootPath}{(uint)pid}{lwpDirName}"; |
| 150 | + |
| 151 | + internal static string GetInfoFilePathForThread(int pid, int tid) => |
| 152 | + $"{RootPath}{(uint)pid}{lwpDirName}/{(uint)tid}{lwpsinfoFileName}"; |
| 153 | + |
| 154 | + } |
| 155 | +} |
0 commit comments