// ----------------------------- // ---- PAL layer ends here ---- // ----------------------------- private static ProcessInfo CreateProcessInfo(int pid) { // Read /proc/pid/stat to get information about the process, and churn that into a ProcessInfo ProcessInfo pi; try { Interop.procfs.ParsedStat procFsStat = Interop.procfs.ReadStatFile(pid); pi = new ProcessInfo { ProcessId = pid, ProcessName = procFsStat.comm, BasePriority = (int)procFsStat.nice, VirtualBytes = (long)procFsStat.vsize, WorkingSet = procFsStat.rss, SessionId = procFsStat.session, // We don't currently fill in the other values. // A few of these could probably be filled in from getrusage, // but only for the current process or its children, not for // arbitrary other processes. }; } catch (IOException) { // Between the time that we get an ID and the time that we try to read the associated stat // file(s), the process could be gone. return(null); } // Then read through /proc/pid/task/ to find each thread in the process... try { string tasksDir = Interop.procfs.GetTaskDirectoryPathForProcess(pid); foreach (string taskDir in Directory.EnumerateDirectories(tasksDir)) { string dirName = Path.GetFileName(taskDir); int tid; if (int.TryParse(dirName, NumberStyles.Integer, CultureInfo.InvariantCulture, out tid)) { // ...and read its associated /proc/pid/task/tid/stat file to create a ThreadInfo Interop.procfs.ParsedStat stat = Interop.procfs.ReadStatFile(pid, tid); pi._threadInfoList.Add(new ThreadInfo { _processId = pid, _threadId = (ulong)tid, _basePriority = pi.BasePriority, _currentPriority = (int)stat.nice, _startAddress = (IntPtr)stat.startstack, _threadState = ProcFsStateToThreadState(stat.state), _threadWaitReason = ThreadWaitReason.Unknown }); } } } catch (IOException) { } // process and/or threads may go away by the time we try to read from them // Finally return what we've built up return(pi); }
/// <summary> /// Creates a ProcessInfo from the data parsed from a /proc/pid/stat file and the associated tasks directory. /// </summary> internal static ProcessInfo CreateProcessInfo(ref Interop.procfs.ParsedStat procFsStat, ref Interop.procfs.ParsedStatus procFsStatus, string?processName = null) { int pid = procFsStat.pid; var pi = new ProcessInfo() { ProcessId = pid, ProcessName = processName ?? Process.GetUntruncatedProcessName(ref procFsStat) ?? string.Empty, BasePriority = (int)procFsStat.nice, SessionId = procFsStat.session, PoolPagedBytes = (long)procFsStatus.VmSwap, VirtualBytes = (long)procFsStatus.VmSize, VirtualBytesPeak = (long)procFsStatus.VmPeak, WorkingSetPeak = (long)procFsStatus.VmHWM, WorkingSet = (long)procFsStatus.VmRSS, PageFileBytes = (long)procFsStatus.VmSwap, PrivateBytes = (long)procFsStatus.VmData, // We don't currently fill in the other values. // A few of these could probably be filled in from getrusage, // but only for the current process or its children, not for // arbitrary other processes. }; // Then read through /proc/pid/task/ to find each thread in the process... string tasksDir = Interop.procfs.GetTaskDirectoryPathForProcess(pid); try { foreach (string taskDir in Directory.EnumerateDirectories(tasksDir)) { // ...and read its associated /proc/pid/task/tid/stat file to create a ThreadInfo string dirName = Path.GetFileName(taskDir); int tid; Interop.procfs.ParsedStat stat; if (int.TryParse(dirName, NumberStyles.Integer, CultureInfo.InvariantCulture, out tid) && Interop.procfs.TryReadStatFile(pid, tid, out stat)) { pi._threadInfoList.Add(new ThreadInfo() { _processId = pid, _threadId = (ulong)tid, _basePriority = pi.BasePriority, _currentPriority = (int)stat.nice, _startAddress = IntPtr.Zero, _threadState = ProcFsStateToThreadState(stat.state), _threadWaitReason = ThreadWaitReason.Unknown }); } } } catch (IOException) { // Between the time that we get an ID and the time that we try to read the associated // directories and files in procfs, the process could be gone. } // Finally return what we've built up return(pi); }
/// <summary> /// Creates a ProcessInfo from the data parsed from a /proc/pid/stat file and the associated tasks directory. /// </summary> internal static ProcessInfo CreateProcessInfo(Interop.procfs.ParsedStat procFsStat, ReusableTextReader reusableReader) { int pid = procFsStat.pid; var pi = new ProcessInfo() { ProcessId = pid, ProcessName = procFsStat.comm, BasePriority = (int)procFsStat.nice, VirtualBytes = (long)procFsStat.vsize, WorkingSet = procFsStat.rss, SessionId = procFsStat.session, // We don't currently fill in the other values. // A few of these could probably be filled in from getrusage, // but only for the current process or its children, not for // arbitrary other processes. }; // Then read through /proc/pid/task/ to find each thread in the process... string tasksDir = Interop.procfs.GetTaskDirectoryPathForProcess(pid); foreach (string taskDir in Directory.EnumerateDirectories(tasksDir)) { // ...and read its associated /proc/pid/task/tid/stat file to create a ThreadInfo string dirName = Path.GetFileName(taskDir); int tid; Interop.procfs.ParsedStat stat; if (int.TryParse(dirName, NumberStyles.Integer, CultureInfo.InvariantCulture, out tid) && Interop.procfs.TryReadStatFile(pid, tid, out stat, reusableReader)) { pi._threadInfoList.Add(new ThreadInfo() { _processId = pid, _threadId = (ulong)tid, _basePriority = pi.BasePriority, _currentPriority = (int)stat.nice, _startAddress = (IntPtr)stat.startstack, _threadState = ProcFsStateToThreadState(stat.state), _threadWaitReason = ThreadWaitReason.Unknown }); } } // Finally return what we've built up return(pi); }
/// <summary>Gets the name that was used to start the process, or null if it could not be retrieved.</summary> /// <param name="stat">The stat for the target process.</param> internal static string GetUntruncatedProcessName(ref Interop.procfs.ParsedStat stat) { string cmdLineFilePath = Interop.procfs.GetCmdLinePathForProcess(stat.pid); byte[]? rentedArray = null; try { // bufferSize == 1 used to avoid unnecessary buffer in FileStream using (var fs = new FileStream(cmdLineFilePath, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 1, useAsync: false)) { Span <byte> buffer = stackalloc byte[512]; int bytesRead = 0; while (true) { // Resize buffer if it was too small. if (bytesRead == buffer.Length) { uint newLength = (uint)buffer.Length * 2; byte[] tmp = ArrayPool <byte> .Shared.Rent((int)newLength); buffer.CopyTo(tmp); byte[]? toReturn = rentedArray; buffer = rentedArray = tmp; if (toReturn != null) { ArrayPool <byte> .Shared.Return(toReturn); } } Debug.Assert(bytesRead < buffer.Length); int n = fs.Read(buffer.Slice(bytesRead)); bytesRead += n; // cmdline contains the argv array separated by '\0' bytes. // stat.comm contains a possibly truncated version of the process name. // When the program is a native executable, the process name will be in argv[0]. // When the program is a script, argv[0] contains the interpreter, and argv[1] contains the script name. Span <byte> argRemainder = buffer.Slice(0, bytesRead); int argEnd = argRemainder.IndexOf((byte)'\0'); if (argEnd != -1) { // Check if argv[0] has the process name. string?name = GetUntruncatedNameFromArg(argRemainder.Slice(0, argEnd), prefix: stat.comm); if (name != null) { return(name); } // Check if argv[1] has the process name. argRemainder = argRemainder.Slice(argEnd + 1); argEnd = argRemainder.IndexOf((byte)'\0'); if (argEnd != -1) { name = GetUntruncatedNameFromArg(argRemainder.Slice(0, argEnd), prefix: stat.comm); return(name ?? stat.comm); } } if (n == 0) { return(stat.comm); } } } } catch (IOException) { return(stat.comm); } finally { if (rentedArray != null) { ArrayPool <byte> .Shared.Return(rentedArray); } }
/// <summary> /// Creates a ProcessInfo from the data parsed from a /proc/pid/stat file and the associated tasks directory. /// </summary> internal static ProcessInfo CreateProcessInfo(Interop.procfs.ParsedStat procFsStat, ReusableTextReader reusableReader) { int pid = procFsStat.pid; // Get long process name if possible, otherwise use a fall back method. string procName = Path.GetFileName(Process.GetExePath(pid)); if (string.IsNullOrEmpty(procName)) { procName = procFsStat.comm; } var pi = new ProcessInfo() { ProcessId = pid, ProcessName = procName, BasePriority = (int)procFsStat.nice, VirtualBytes = (long)procFsStat.vsize, WorkingSet = procFsStat.rss * Environment.SystemPageSize, SessionId = procFsStat.session, // We don't currently fill in the other values. // A few of these could probably be filled in from getrusage, // but only for the current process or its children, not for // arbitrary other processes. }; // Then read through /proc/pid/task/ to find each thread in the process... string tasksDir = Interop.procfs.GetTaskDirectoryPathForProcess(pid); try { foreach (string taskDir in Directory.EnumerateDirectories(tasksDir)) { // ...and read its associated /proc/pid/task/tid/stat file to create a ThreadInfo string dirName = Path.GetFileName(taskDir); int tid; Interop.procfs.ParsedStat stat; if (int.TryParse(dirName, NumberStyles.Integer, CultureInfo.InvariantCulture, out tid) && Interop.procfs.TryReadStatFile(pid, tid, out stat, reusableReader)) { unsafe { pi._threadInfoList.Add(new ThreadInfo() { _processId = pid, _threadId = (ulong)tid, _basePriority = pi.BasePriority, _currentPriority = (int)stat.nice, _startAddress = IntPtr.Zero, _threadState = ProcFsStateToThreadState(stat.state), _threadWaitReason = ThreadWaitReason.Unknown }); } } } } catch (IOException) { // Between the time that we get an ID and the time that we try to read the associated // directories and files in procfs, the process could be gone. } // Finally return what we've built up return(pi); }