-
Notifications
You must be signed in to change notification settings - Fork 146
/
Copy pathInterop.ProcFsStat.cs
200 lines (180 loc) · 8.57 KB
/
Interop.ProcFsStat.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#nullable enable
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Text;
internal static partial class Interop
{
internal static partial class ProcFs
{
private const string ExeFileName = "/exe";
private const string CmdLineFileName = "/cmdline";
private const string StatFileName = "/stat";
private const string FileDescriptorDirectoryName = "/fd/";
private const string TaskDirectoryName = "/task/";
internal const string SelfExeFilePath = RootPath + "self" + ExeFileName;
internal const string SelfCmdLineFilePath = RootPath + "self" + CmdLineFileName;
internal const string ProcStatFilePath = RootPath + "stat";
internal struct ParsedStat
{
// Commented out fields are available in the stat data file but
// are currently not used. If/when needed, they can be uncommented,
// and the corresponding entry can be added back to StatParser, replacing
// the MoveNext() with the appropriate ParseNext* call and assignment.
internal int pid;
internal string comm;
internal char state;
internal int ppid;
//internal int pgrp;
internal int session;
//internal int tty_nr;
//internal int tpgid;
//internal uint flags;
//internal ulong minflt;
//internal ulong cminflt;
//internal ulong majflt;
//internal ulong cmajflt;
internal ulong utime;
internal ulong stime;
internal ulong cutime;
internal ulong cstime;
//internal long priority;
internal long nice;
//internal long num_threads;
//internal long itrealvalue;
internal ulong starttime;
internal ulong vsize;
internal long rss;
internal ulong rsslim;
//internal ulong startcode;
//internal ulong endcode;
//internal ulong startstack;
//internal ulong kstkesp;
//internal ulong kstkeip;
//internal ulong signal;
//internal ulong blocked;
//internal ulong sigignore;
//internal ulong sigcatch;
//internal ulong wchan;
//internal ulong nswap;
//internal ulong cnswap;
//internal int exit_signal;
//internal int processor;
//internal uint rt_priority;
//internal uint policy;
//internal ulong delayacct_blkio_ticks;
//internal ulong guest_time;
//internal long cguest_time;
}
internal static string GetExeFilePathForProcess(int pid)
{
return RootPath + pid.ToString(CultureInfo.InvariantCulture) + ExeFileName;
}
internal static string GetCmdLinePathForProcess(int pid)
{
return RootPath + pid.ToString(CultureInfo.InvariantCulture) + CmdLineFileName;
}
internal static string GetStatFilePathForProcess(int pid)
{
return RootPath + pid.ToString(CultureInfo.InvariantCulture) + StatFileName;
}
internal static string GetTaskDirectoryPathForProcess(int pid)
{
return RootPath + pid.ToString(CultureInfo.InvariantCulture) + TaskDirectoryName;
}
internal static string GetFileDescriptorDirectoryPathForProcess(int pid)
{
return RootPath + pid.ToString(CultureInfo.InvariantCulture) + FileDescriptorDirectoryName;
}
private static string GetStatFilePathForThread(int pid, int tid)
{
// Perf note: Calling GetTaskDirectoryPathForProcess will allocate a string,
// which we then use in another Concat call to produce another string. The straightforward alternative,
// though, since we have five input strings, is to use the string.Concat overload that takes a params array.
// This results in allocating not only the params array but also a defensive copy inside of Concat,
// which means allocating two five-element arrays. This two-string approach will result not only in fewer
// allocations, but also typically in less memory allocated, and it's a bit more maintainable.
return GetTaskDirectoryPathForProcess(pid) + tid.ToString(CultureInfo.InvariantCulture) + StatFileName;
}
internal static bool TryReadStatFile(int pid, out ParsedStat result)
{
bool b = TryParseStatFile(GetStatFilePathForProcess(pid), out result);
Debug.Assert(!b || result.pid == pid, "Expected process ID from stat file to match supplied pid");
return b;
}
internal static bool TryReadStatFile(int pid, int tid, out ParsedStat result)
{
bool b = TryParseStatFile(GetStatFilePathForThread(pid, tid), out result);
Debug.Assert(!b || result.pid == tid, "Expected thread ID from stat file to match supplied tid");
return b;
}
internal static bool TryParseStatFile(string statFilePath, out ParsedStat result)
{
if (!TryReadFile(statFilePath, out string? statFileContents))
{
// 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.
result = default(ParsedStat);
return false;
}
var parser = new StringParser(statFileContents, ' ');
var results = default(ParsedStat);
results.pid = parser.ParseNextInt32();
results.comm = parser.MoveAndExtractNextInOuterParens();
results.state = parser.ParseNextChar();
results.ppid = parser.ParseNextInt32();
parser.MoveNextOrFail(); // pgrp
results.session = parser.ParseNextInt32();
parser.MoveNextOrFail(); // tty_nr
parser.MoveNextOrFail(); // tpgid
parser.MoveNextOrFail(); // flags
parser.MoveNextOrFail(); // majflt
parser.MoveNextOrFail(); // cmagflt
parser.MoveNextOrFail(); // minflt
parser.MoveNextOrFail(); // cminflt
results.utime = parser.ParseNextUInt64(); // utime
results.stime = parser.ParseNextUInt64(); // stime
results.cutime = parser.ParseNextUInt64(); // cutime
results.cstime = parser.ParseNextUInt64(); // cstime
parser.MoveNextOrFail(); // priority
results.nice = parser.ParseNextInt64();
parser.MoveNextOrFail(); // num_threads
parser.MoveNextOrFail(); // itrealvalue
results.starttime = parser.ParseNextUInt64();
results.vsize = parser.ParseNextUInt64();
results.rss = parser.ParseNextInt64();
results.rsslim = parser.ParseNextUInt64();
// The following lines are commented out as there's no need to parse through
// the rest of the entry (we've gotten all of the data we need). Should any
// of these fields be needed in the future, uncomment all of the lines up
// through and including the one that's needed. For now, these are being left
// commented to document what's available in the remainder of the entry.
//parser.MoveNextOrFail(); // startcode
//parser.MoveNextOrFail(); // endcode
//parser.MoveNextOrFail(); // startstack
//parser.MoveNextOrFail(); // kstkesp
//parser.MoveNextOrFail(); // kstkeip
//parser.MoveNextOrFail(); // signal
//parser.MoveNextOrFail(); // blocked
//parser.MoveNextOrFail(); // sigignore
//parser.MoveNextOrFail(); // sigcatch
//parser.MoveNextOrFail(); // wchan
//parser.MoveNextOrFail(); // nswap
//parser.MoveNextOrFail(); // cnswap
//parser.MoveNextOrFail(); // exit_signal
//parser.MoveNextOrFail(); // processor
//parser.MoveNextOrFail(); // rt_priority
//parser.MoveNextOrFail(); // policy
//parser.MoveNextOrFail(); // delayacct_blkio_ticks
//parser.MoveNextOrFail(); // guest_time
//parser.MoveNextOrFail(); // cguest_time
result = results;
return true;
}
}
}