| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
#ifndef _GNU_SOURCE |
|
2
|
|
|
|
|
|
|
#define _GNU_SOURCE /* for canonicalize_file_name */ |
|
3
|
|
|
|
|
|
|
#endif |
|
4
|
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
#include /* is_digit */ |
|
6
|
|
|
|
|
|
|
#include /* opendir, readdir_r */ |
|
7
|
|
|
|
|
|
|
#include |
|
8
|
|
|
|
|
|
|
#include /* BOOL */ |
|
9
|
|
|
|
|
|
|
#include /* *scanf family */ |
|
10
|
|
|
|
|
|
|
#include /* malloc family */ |
|
11
|
|
|
|
|
|
|
#include /* strchr */ |
|
12
|
|
|
|
|
|
|
#include /* time_t */ |
|
13
|
|
|
|
|
|
|
#include |
|
14
|
|
|
|
|
|
|
#include |
|
15
|
|
|
|
|
|
|
#include |
|
16
|
|
|
|
|
|
|
#include /* statfs */ |
|
17
|
|
|
|
|
|
|
/* glibc only goodness */ |
|
18
|
|
|
|
|
|
|
#include /* glibc's handy obstacks */ |
|
19
|
|
|
|
|
|
|
/* pthreads */ |
|
20
|
|
|
|
|
|
|
#include /* pthread_once */ |
|
21
|
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
#define obstack_chunk_alloc malloc |
|
23
|
|
|
|
|
|
|
#define obstack_chunk_free free |
|
24
|
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
#include "os/Linux.h" |
|
26
|
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
/* NOTE: Before this was actually milliseconds even though it said microseconds, now it is correct. */ |
|
28
|
|
|
|
|
|
|
#define JIFFIES_TO_MICROSECONDS(x) (((x) * 1e6) / system_hertz) |
|
29
|
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
/* some static values that won't change, */ |
|
31
|
|
|
|
|
|
|
static pthread_once_t globals_init = PTHREAD_ONCE_INIT; |
|
32
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
static long long boot_time; |
|
34
|
|
|
|
|
|
|
static unsigned page_size; |
|
35
|
|
|
|
|
|
|
static unsigned long long system_memory; |
|
36
|
|
|
|
|
|
|
static unsigned system_hertz; |
|
37
|
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
static bool init_failed = false; |
|
39
|
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
/* get_string() |
|
42
|
|
|
|
|
|
|
* |
|
43
|
|
|
|
|
|
|
* Access strings in read only section |
|
44
|
|
|
|
|
|
|
* |
|
45
|
|
|
|
|
|
|
* @param elem String we want to retrive (look at enum strings) |
|
46
|
|
|
|
|
|
|
* @return Address of string |
|
47
|
|
|
|
|
|
|
*/ |
|
48
|
106
|
|
|
|
|
|
inline static const char *get_string(int elem) |
|
49
|
|
|
|
|
|
|
{ |
|
50
|
106
|
|
|
|
|
|
return strings + strings_index[elem]; |
|
51
|
|
|
|
|
|
|
} |
|
52
|
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
/* init_static_vars() |
|
54
|
|
|
|
|
|
|
* |
|
55
|
|
|
|
|
|
|
* Called by pthread_once to initialize global variables (system settings that don't change) |
|
56
|
|
|
|
|
|
|
*/ |
|
57
|
3
|
|
|
|
|
|
static void init_static_vars() |
|
58
|
|
|
|
|
|
|
{ |
|
59
|
|
|
|
|
|
|
struct obstack mem_pool; |
|
60
|
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
char *file_text, *file_off; |
|
62
|
|
|
|
|
|
|
off_t file_len; |
|
63
|
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
unsigned long long total_memory; |
|
65
|
|
|
|
|
|
|
|
|
66
|
3
|
|
|
|
|
|
boot_time = -1; |
|
67
|
3
|
|
|
|
|
|
system_memory = -1; |
|
68
|
|
|
|
|
|
|
|
|
69
|
3
|
|
|
|
|
|
page_size = getpagesize(); |
|
70
|
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
/* initilize our mem stack, tempoary memory */ |
|
72
|
3
|
|
|
|
|
|
obstack_init(&mem_pool); |
|
73
|
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
/* find hertz size, I'm hoping this is gotten from elf note AT_CLKTCK */ |
|
75
|
3
|
|
|
|
|
|
system_hertz = sysconf(_SC_CLK_TCK); |
|
76
|
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
/* find boot time */ |
|
78
|
|
|
|
|
|
|
/* read /proc/stat in */ |
|
79
|
3
|
50
|
|
|
|
|
if((file_text = read_file("stat", NULL, &file_len, &mem_pool)) == NULL) { |
|
80
|
0
|
|
|
|
|
|
goto fail; |
|
81
|
|
|
|
|
|
|
} |
|
82
|
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
/* look for the line that starts with btime |
|
84
|
|
|
|
|
|
|
* NOTE: incrementing file_off after strchr is legal because file_text will |
|
85
|
|
|
|
|
|
|
* be null terminated, so worst case after '\n' there will be '\0' and |
|
86
|
|
|
|
|
|
|
* strncmp will fail or sscanf won't return 1 |
|
87
|
|
|
|
|
|
|
* Only increment on the first line */ |
|
88
|
60
|
50
|
|
|
|
|
for(file_off = file_text; file_off; file_off = strchr(file_off, '\n')) { |
|
89
|
60
|
100
|
|
|
|
|
if(file_off != file_text) { |
|
90
|
57
|
|
|
|
|
|
file_off++; |
|
91
|
|
|
|
|
|
|
} |
|
92
|
|
|
|
|
|
|
|
|
93
|
60
|
100
|
|
|
|
|
if(strncmp(file_off, "btime", 5) == 0) { |
|
94
|
3
|
50
|
|
|
|
|
if(sscanf(file_off, "btime %lld", &boot_time) == 1) { |
|
95
|
3
|
|
|
|
|
|
break; |
|
96
|
|
|
|
|
|
|
} |
|
97
|
|
|
|
|
|
|
} |
|
98
|
|
|
|
|
|
|
} |
|
99
|
|
|
|
|
|
|
|
|
100
|
3
|
50
|
|
|
|
|
obstack_free(&mem_pool, file_text); |
|
|
|
50
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
/* did we scrape the number of pages successfuly? */ |
|
103
|
3
|
50
|
|
|
|
|
if(boot_time == -1) { |
|
104
|
0
|
|
|
|
|
|
goto fail; |
|
105
|
|
|
|
|
|
|
} |
|
106
|
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
/* find total number of system pages */ |
|
108
|
|
|
|
|
|
|
/* read /proc/meminfo */ |
|
109
|
3
|
50
|
|
|
|
|
if((file_text = read_file("meminfo", NULL, &file_len, &mem_pool)) == NULL) { |
|
110
|
0
|
|
|
|
|
|
goto fail; |
|
111
|
|
|
|
|
|
|
} |
|
112
|
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
/* look for the line that starts with: MemTotal */ |
|
114
|
3
|
50
|
|
|
|
|
for(file_off = file_text; file_off; file_off = strchr(file_off, '\n')) { |
|
115
|
3
|
50
|
|
|
|
|
if(file_off != file_text) { |
|
116
|
0
|
|
|
|
|
|
file_off++; |
|
117
|
|
|
|
|
|
|
} |
|
118
|
|
|
|
|
|
|
|
|
119
|
3
|
50
|
|
|
|
|
if(strncmp(file_off, "MemTotal:", 9) == 0) { |
|
120
|
3
|
50
|
|
|
|
|
if(sscanf(file_off, "MemTotal: %llu", &system_memory) == 1) { |
|
121
|
3
|
|
|
|
|
|
system_memory *= 1024; /* convert to bytes */ |
|
122
|
3
|
|
|
|
|
|
break; |
|
123
|
|
|
|
|
|
|
} |
|
124
|
|
|
|
|
|
|
} |
|
125
|
|
|
|
|
|
|
} |
|
126
|
|
|
|
|
|
|
|
|
127
|
3
|
50
|
|
|
|
|
obstack_free(&mem_pool, file_text); |
|
|
|
50
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
/* did we scrape the number of pages successfuly? */ |
|
130
|
3
|
50
|
|
|
|
|
if(total_memory == -1) { |
|
131
|
0
|
|
|
|
|
|
goto fail; |
|
132
|
|
|
|
|
|
|
} |
|
133
|
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
/* initialize system hertz value */ |
|
135
|
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
/* cleanup */ |
|
137
|
3
|
50
|
|
|
|
|
obstack_free(&mem_pool, NULL); |
|
|
|
0
|
|
|
|
|
|
|
138
|
3
|
|
|
|
|
|
return; |
|
139
|
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
/* mark failure and cleanup allocated resources */ |
|
141
|
|
|
|
|
|
|
fail: |
|
142
|
0
|
0
|
|
|
|
|
obstack_free(&mem_pool, NULL); |
|
|
|
0
|
|
|
|
|
|
|
143
|
0
|
|
|
|
|
|
init_failed = true; |
|
144
|
|
|
|
|
|
|
} |
|
145
|
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
/* OS_initialize() |
|
147
|
|
|
|
|
|
|
* |
|
148
|
|
|
|
|
|
|
* Called by XS parts whenever Proc::ProcessTable::new is called |
|
149
|
|
|
|
|
|
|
* |
|
150
|
|
|
|
|
|
|
* NOTE: There's a const char* -> char* conversion that's evil, but can't fix |
|
151
|
|
|
|
|
|
|
* this without breaking the Proc::ProcessTable XS API. |
|
152
|
|
|
|
|
|
|
*/ |
|
153
|
3
|
|
|
|
|
|
char *OS_initialize() |
|
154
|
|
|
|
|
|
|
{ |
|
155
|
|
|
|
|
|
|
struct statfs sfs; |
|
156
|
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
/* did we already try to initilize before and fail, if pthrad_once only |
|
158
|
|
|
|
|
|
|
* let us flag a failure from the init function; behavor of longjmp is |
|
159
|
|
|
|
|
|
|
* undefined, so that avaue is out out of the question */ |
|
160
|
3
|
50
|
|
|
|
|
if(init_failed) { |
|
161
|
0
|
|
|
|
|
|
return (char *)get_string(STR_ERR_INIT); |
|
162
|
|
|
|
|
|
|
} |
|
163
|
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
/* check if /proc is mounted, let this go before initializing globals (below), |
|
165
|
|
|
|
|
|
|
* since the missing /proc message might me helpful */ |
|
166
|
3
|
50
|
|
|
|
|
if(statfs("/proc", &sfs) == -1) { |
|
167
|
0
|
|
|
|
|
|
return (char *)get_string(STR_ERR_PROC_STATFS); |
|
168
|
|
|
|
|
|
|
} |
|
169
|
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
/* one time initialization of some values that won't change */ |
|
171
|
3
|
|
|
|
|
|
pthread_once(&globals_init, init_static_vars); |
|
172
|
|
|
|
|
|
|
|
|
173
|
3
|
|
|
|
|
|
return NULL; |
|
174
|
|
|
|
|
|
|
} |
|
175
|
|
|
|
|
|
|
|
|
176
|
583
|
|
|
|
|
|
inline static void field_enable(char *format_str, enum field field) |
|
177
|
|
|
|
|
|
|
{ |
|
178
|
583
|
|
|
|
|
|
format_str[field] = tolower(format_str[field]); |
|
179
|
583
|
|
|
|
|
|
} |
|
180
|
|
|
|
|
|
|
|
|
181
|
212
|
|
|
|
|
|
inline static void field_enable_range(char *format_str, enum field field1, |
|
182
|
|
|
|
|
|
|
enum field field2) |
|
183
|
|
|
|
|
|
|
{ |
|
184
|
|
|
|
|
|
|
int i; |
|
185
|
|
|
|
|
|
|
|
|
186
|
1696
|
100
|
|
|
|
|
for(i = field1; i <= field2; i++) { |
|
187
|
1484
|
|
|
|
|
|
format_str[i] = tolower(format_str[i]); |
|
188
|
|
|
|
|
|
|
} |
|
189
|
212
|
|
|
|
|
|
} |
|
190
|
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
/* proc_pid_file() |
|
192
|
|
|
|
|
|
|
* |
|
193
|
|
|
|
|
|
|
* Build a path to the pid directory in proc '/proc/${pid}' with an optional |
|
194
|
|
|
|
|
|
|
* relative path at the end. Put the resultant path on top of the obstack. |
|
195
|
|
|
|
|
|
|
* |
|
196
|
|
|
|
|
|
|
* @return Address of the create path string. |
|
197
|
|
|
|
|
|
|
*/ |
|
198
|
430
|
|
|
|
|
|
inline static char *proc_pid_file(const char *pid, const char *file, |
|
199
|
|
|
|
|
|
|
struct obstack *mem_pool) |
|
200
|
|
|
|
|
|
|
{ |
|
201
|
|
|
|
|
|
|
/* path to dir */ |
|
202
|
430
|
|
|
|
|
|
obstack_printf(mem_pool, "/proc/%s", pid); |
|
203
|
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
/* additional path (not just the dir) */ |
|
205
|
430
|
100
|
|
|
|
|
if(file) { |
|
206
|
371
|
|
|
|
|
|
obstack_printf(mem_pool, "/%s", file); |
|
207
|
|
|
|
|
|
|
} |
|
208
|
|
|
|
|
|
|
|
|
209
|
430
|
50
|
|
|
|
|
obstack_1grow(mem_pool, '\0'); |
|
210
|
|
|
|
|
|
|
|
|
211
|
430
|
50
|
|
|
|
|
return (char *)obstack_finish(mem_pool); |
|
|
|
50
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
} |
|
213
|
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
/* read_file() |
|
215
|
|
|
|
|
|
|
* |
|
216
|
|
|
|
|
|
|
* Reads the contents of a file using an obstack for memory. It can read files like |
|
217
|
|
|
|
|
|
|
* /proc/stat or /proc/${pid}/stat. |
|
218
|
|
|
|
|
|
|
* |
|
219
|
|
|
|
|
|
|
* @param path String representing the part following /proc (usualy this is |
|
220
|
|
|
|
|
|
|
* the process pid number) |
|
221
|
|
|
|
|
|
|
* @param extra_path Path to the file to read in (relative to /proc/${path}/) |
|
222
|
|
|
|
|
|
|
* @param len Pointer to the value where the length will be saved |
|
223
|
|
|
|
|
|
|
* |
|
224
|
|
|
|
|
|
|
* @return Pointer to a null terminate string allocated on the obstack, or |
|
225
|
|
|
|
|
|
|
* NULL when it fails (doesn't clean up the obstack). |
|
226
|
|
|
|
|
|
|
*/ |
|
227
|
271
|
|
|
|
|
|
static char *read_file(const char *path, const char *extra_path, |
|
228
|
|
|
|
|
|
|
off_t *len, struct obstack *mem_pool) |
|
229
|
|
|
|
|
|
|
{ |
|
230
|
271
|
|
|
|
|
|
int fd, result = -1; |
|
231
|
|
|
|
|
|
|
char *text, *file, *start; |
|
232
|
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
/* build the filename in our tempoary storage */ |
|
234
|
271
|
|
|
|
|
|
file = proc_pid_file(path, extra_path, mem_pool); |
|
235
|
|
|
|
|
|
|
|
|
236
|
271
|
|
|
|
|
|
fd = open(file, O_RDONLY); |
|
237
|
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
/* free tmp memory we allocated for the file contents, this way we are not |
|
239
|
|
|
|
|
|
|
* poluting the obstack and the only thing left on it is the file */ |
|
240
|
271
|
50
|
|
|
|
|
obstack_free(mem_pool, file); |
|
|
|
50
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
|
|
242
|
271
|
50
|
|
|
|
|
if(fd == -1) { |
|
243
|
0
|
|
|
|
|
|
return NULL; |
|
244
|
|
|
|
|
|
|
} |
|
245
|
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
/* read file into our buffer */ |
|
247
|
883
|
100
|
|
|
|
|
for(*len = 0; result; *len += result) { |
|
248
|
612
|
100
|
|
|
|
|
obstack_blank(mem_pool, 1024); |
|
249
|
612
|
|
|
|
|
|
start = obstack_base(mem_pool) + *len; |
|
250
|
|
|
|
|
|
|
|
|
251
|
612
|
50
|
|
|
|
|
if((result = read(fd, start, 1024)) == -1) { |
|
252
|
0
|
0
|
|
|
|
|
obstack_free(mem_pool, obstack_finish(mem_pool)); |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
253
|
0
|
|
|
|
|
|
close(fd); |
|
254
|
0
|
|
|
|
|
|
return NULL; |
|
255
|
|
|
|
|
|
|
} |
|
256
|
|
|
|
|
|
|
} |
|
257
|
|
|
|
|
|
|
|
|
258
|
271
|
|
|
|
|
|
start = obstack_base(mem_pool) + *len; |
|
259
|
271
|
|
|
|
|
|
*start = '\0'; |
|
260
|
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
/* finalize our text buffer */ |
|
262
|
271
|
50
|
|
|
|
|
text = obstack_finish(mem_pool); |
|
|
|
50
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
/* not bothering checking return value, because it's possible that the |
|
265
|
|
|
|
|
|
|
* process went away */ |
|
266
|
271
|
|
|
|
|
|
close(fd); |
|
267
|
|
|
|
|
|
|
|
|
268
|
271
|
|
|
|
|
|
return text; |
|
269
|
|
|
|
|
|
|
} |
|
270
|
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
/* get_user_info() |
|
272
|
|
|
|
|
|
|
* |
|
273
|
|
|
|
|
|
|
* Find the user/group id of the process |
|
274
|
|
|
|
|
|
|
* |
|
275
|
|
|
|
|
|
|
* @param pid String representing the pid |
|
276
|
|
|
|
|
|
|
* @param prs Data structure where to put the scraped values |
|
277
|
|
|
|
|
|
|
* @param mem_pool Obstack to use for temory storage |
|
278
|
|
|
|
|
|
|
*/ |
|
279
|
53
|
|
|
|
|
|
static void get_user_info(char *pid, char *format_str, struct procstat *prs, |
|
280
|
|
|
|
|
|
|
struct obstack *mem_pool) |
|
281
|
|
|
|
|
|
|
{ |
|
282
|
|
|
|
|
|
|
char * path_pid; |
|
283
|
|
|
|
|
|
|
struct stat stat_pid; |
|
284
|
|
|
|
|
|
|
int result; |
|
285
|
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
/* (temp) /proc/${pid} */ |
|
287
|
53
|
|
|
|
|
|
path_pid = proc_pid_file(pid, NULL, mem_pool); |
|
288
|
|
|
|
|
|
|
|
|
289
|
53
|
|
|
|
|
|
result = stat(path_pid, &stat_pid); |
|
290
|
|
|
|
|
|
|
|
|
291
|
53
|
50
|
|
|
|
|
obstack_free(mem_pool, path_pid); |
|
|
|
50
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
|
|
293
|
53
|
50
|
|
|
|
|
if(result == -1) { |
|
294
|
0
|
|
|
|
|
|
return; |
|
295
|
|
|
|
|
|
|
} |
|
296
|
|
|
|
|
|
|
|
|
297
|
53
|
|
|
|
|
|
prs->uid = stat_pid.st_uid; |
|
298
|
53
|
|
|
|
|
|
prs->gid = stat_pid.st_gid; |
|
299
|
|
|
|
|
|
|
|
|
300
|
53
|
|
|
|
|
|
field_enable(format_str, F_UID); |
|
301
|
53
|
|
|
|
|
|
field_enable(format_str, F_GID); |
|
302
|
|
|
|
|
|
|
} |
|
303
|
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
/* get_proc_stat() |
|
305
|
|
|
|
|
|
|
* |
|
306
|
|
|
|
|
|
|
* Reads a processes stat file in the proc filesystem '/proc/${pid}/stat' and |
|
307
|
|
|
|
|
|
|
* fills the procstat structure with the values. |
|
308
|
|
|
|
|
|
|
* |
|
309
|
|
|
|
|
|
|
* @param pid String representing the pid |
|
310
|
|
|
|
|
|
|
* @param prs Data structure where to put the scraped values |
|
311
|
|
|
|
|
|
|
* @param mem_pool Obstack to use for temory storage |
|
312
|
|
|
|
|
|
|
*/ |
|
313
|
53
|
|
|
|
|
|
static bool get_proc_stat(char *pid, char *format_str, struct procstat *prs, |
|
314
|
|
|
|
|
|
|
struct obstack *mem_pool) |
|
315
|
|
|
|
|
|
|
{ |
|
316
|
|
|
|
|
|
|
char *stat_text, *stat_cont, *close_paren, *open_paren; |
|
317
|
|
|
|
|
|
|
int result; |
|
318
|
|
|
|
|
|
|
off_t stat_len; |
|
319
|
|
|
|
|
|
|
long dummy_l; |
|
320
|
|
|
|
|
|
|
int dummy_i; |
|
321
|
|
|
|
|
|
|
|
|
322
|
53
|
|
|
|
|
|
bool read_ok = true; |
|
323
|
|
|
|
|
|
|
|
|
324
|
53
|
50
|
|
|
|
|
if((stat_text = read_file(pid, "stat", &stat_len, mem_pool)) == NULL) { |
|
325
|
0
|
|
|
|
|
|
return false; |
|
326
|
|
|
|
|
|
|
} |
|
327
|
|
|
|
|
|
|
|
|
328
|
53
|
50
|
|
|
|
|
if(sscanf(stat_text, "%d (", &prs->pid) != 1) { |
|
329
|
0
|
|
|
|
|
|
goto done; |
|
330
|
|
|
|
|
|
|
} |
|
331
|
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
/* replace the first ')' with a '\0', the contents look like this: |
|
333
|
|
|
|
|
|
|
* pid (program_name) state ... |
|
334
|
|
|
|
|
|
|
* if we don't find ')' then it's incorrectly formated */ |
|
335
|
53
|
50
|
|
|
|
|
if((close_paren = strrchr(stat_text, ')')) == NULL) { |
|
336
|
0
|
|
|
|
|
|
read_ok = false; |
|
337
|
0
|
|
|
|
|
|
goto done; |
|
338
|
|
|
|
|
|
|
} |
|
339
|
53
|
|
|
|
|
|
*close_paren = '\0'; |
|
340
|
|
|
|
|
|
|
|
|
341
|
53
|
50
|
|
|
|
|
if((open_paren = strchr(stat_text, '(')) == NULL) { |
|
342
|
0
|
|
|
|
|
|
read_ok = false; |
|
343
|
0
|
|
|
|
|
|
goto done; |
|
344
|
|
|
|
|
|
|
} |
|
345
|
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
|
|
347
|
53
|
|
|
|
|
|
int comm_esize = sizeof(prs->comm) - 1; |
|
348
|
53
|
|
|
|
|
|
int comm_len = close_paren - open_paren - 1; |
|
349
|
|
|
|
|
|
|
|
|
350
|
53
|
50
|
|
|
|
|
if(comm_len > comm_esize) { |
|
351
|
0
|
|
|
|
|
|
comm_len = comm_esize; |
|
352
|
|
|
|
|
|
|
} |
|
353
|
53
|
50
|
|
|
|
|
if(comm_len > 0) { |
|
354
|
53
|
|
|
|
|
|
strncpy(prs->comm, open_paren + 1, comm_esize); |
|
355
|
53
|
|
|
|
|
|
prs->comm[comm_esize] = '\0'; |
|
356
|
|
|
|
|
|
|
} |
|
357
|
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
/* address at which we pickup again, after the ')' |
|
360
|
|
|
|
|
|
|
* NOTE: we don't bother checking bounds since strchr didn't return NULL |
|
361
|
|
|
|
|
|
|
* thus the NULL terminator will be at least close_paren+1, which is ok */ |
|
362
|
53
|
|
|
|
|
|
stat_cont = close_paren + 1; |
|
363
|
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
/* scrape the remaining values */ |
|
365
|
53
|
|
|
|
|
|
result = sscanf(stat_cont, |
|
366
|
|
|
|
|
|
|
" %c" /* state */ |
|
367
|
|
|
|
|
|
|
" %d" /* ppid */ |
|
368
|
|
|
|
|
|
|
" %d" /* pgrp */ |
|
369
|
|
|
|
|
|
|
" %d" /* sid */ |
|
370
|
|
|
|
|
|
|
" %d" /* tty */ |
|
371
|
|
|
|
|
|
|
" %d" /* tty_pgid (dummy) */ |
|
372
|
|
|
|
|
|
|
" %u" /* flags */ |
|
373
|
|
|
|
|
|
|
" %lu" /* minflt */ |
|
374
|
|
|
|
|
|
|
" %lu" /* cminflt */ |
|
375
|
|
|
|
|
|
|
" %lu" /* majflt */ |
|
376
|
|
|
|
|
|
|
" %lu" /* cmajflt */ |
|
377
|
|
|
|
|
|
|
" %llu" /* utime */ |
|
378
|
|
|
|
|
|
|
" %llu" /* stime */ |
|
379
|
|
|
|
|
|
|
" %llu" /* cutime */ |
|
380
|
|
|
|
|
|
|
" %lld" /* cstime */ |
|
381
|
|
|
|
|
|
|
" %ld" /* priority */ |
|
382
|
|
|
|
|
|
|
" %ld" /* nice (dummy) */ |
|
383
|
|
|
|
|
|
|
" %ld" /* num_threads (dummy) */ |
|
384
|
|
|
|
|
|
|
" %d" /* itrealvalue (dummy) */ |
|
385
|
|
|
|
|
|
|
" %llu" /* start_time */ |
|
386
|
|
|
|
|
|
|
" %lu" /* vsize */ |
|
387
|
|
|
|
|
|
|
" %ld" /* rss */ |
|
388
|
|
|
|
|
|
|
" %ld" /* rsslim */ |
|
389
|
|
|
|
|
|
|
" %lu" /* start_code (dummy) */ |
|
390
|
|
|
|
|
|
|
" %lu" /* end_code (dummy) */ |
|
391
|
|
|
|
|
|
|
" %lu" /* start_stack (dummy) */ |
|
392
|
|
|
|
|
|
|
" %lu" /* esp (dummy) */ |
|
393
|
|
|
|
|
|
|
" %lu" /* eip (dummy) */ |
|
394
|
|
|
|
|
|
|
" %lu" /* pending (dummy) */ |
|
395
|
|
|
|
|
|
|
" %lu" /* blocked (dummy) */ |
|
396
|
|
|
|
|
|
|
" %lu" /* sigign (dummy) */ |
|
397
|
|
|
|
|
|
|
" %lu" /* sigcatch (dummy) */ |
|
398
|
|
|
|
|
|
|
" %lu" /* wchan (obsolete) */ |
|
399
|
|
|
|
|
|
|
, |
|
400
|
|
|
|
|
|
|
&prs->state_c, /* %c */ |
|
401
|
|
|
|
|
|
|
&prs->ppid, &prs->pgrp, /* %d %d */ |
|
402
|
|
|
|
|
|
|
&prs->sid, /* %d */ |
|
403
|
|
|
|
|
|
|
&prs->tty, &dummy_i, /* tty, tty_pgid */ |
|
404
|
|
|
|
|
|
|
&prs->flags, /* %u */ |
|
405
|
|
|
|
|
|
|
&prs->minflt, &prs->cminflt, |
|
406
|
|
|
|
|
|
|
&prs->majflt, &prs->cmajflt, /* %lu %lu %lu %lu */ |
|
407
|
|
|
|
|
|
|
&prs->utime, &prs->stime, |
|
408
|
|
|
|
|
|
|
&prs->cutime, &prs->cstime, |
|
409
|
|
|
|
|
|
|
&prs->priority, |
|
410
|
|
|
|
|
|
|
&dummy_l, /* nice */ |
|
411
|
|
|
|
|
|
|
&dummy_l, /* num threads */ |
|
412
|
|
|
|
|
|
|
&dummy_i, /* timeout obsolete */ |
|
413
|
|
|
|
|
|
|
&prs->start_time, |
|
414
|
|
|
|
|
|
|
&prs->vsize, &prs->rss, |
|
415
|
|
|
|
|
|
|
&dummy_l, |
|
416
|
|
|
|
|
|
|
&dummy_l, &dummy_l, |
|
417
|
|
|
|
|
|
|
&dummy_l, |
|
418
|
|
|
|
|
|
|
&dummy_l, &dummy_l, |
|
419
|
|
|
|
|
|
|
&dummy_l, &dummy_l, &dummy_l, &dummy_l, |
|
420
|
|
|
|
|
|
|
&prs->wchan); |
|
421
|
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
/* 33 items in scanf's list... It's all or nothing baby */ |
|
423
|
53
|
50
|
|
|
|
|
if(result != 33) { |
|
424
|
0
|
|
|
|
|
|
read_ok = false; |
|
425
|
0
|
|
|
|
|
|
goto done; |
|
426
|
|
|
|
|
|
|
} |
|
427
|
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
/* enable fields; F_STATE is not the range */ |
|
429
|
53
|
|
|
|
|
|
field_enable_range(format_str, F_PID, F_WCHAN); |
|
430
|
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
done: |
|
432
|
53
|
50
|
|
|
|
|
obstack_free(mem_pool, stat_text); |
|
|
|
50
|
|
|
|
|
|
|
433
|
53
|
|
|
|
|
|
return read_ok; |
|
434
|
|
|
|
|
|
|
} |
|
435
|
|
|
|
|
|
|
|
|
436
|
106
|
|
|
|
|
|
static void eval_link(char *pid, char *link_rel, enum field field, char **ptr, |
|
437
|
|
|
|
|
|
|
char *format_str, struct obstack *mem_pool) |
|
438
|
|
|
|
|
|
|
{ |
|
439
|
|
|
|
|
|
|
char *link_file, *link; |
|
440
|
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
/* path to the link file like. /proc/{pid}/{link_rel} */ |
|
442
|
106
|
|
|
|
|
|
link_file = proc_pid_file(pid, link_rel, mem_pool); |
|
443
|
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
/* It's okay to use canonicalize_file_name instead of readlink on linux |
|
445
|
|
|
|
|
|
|
* for the cwd symlink, since on linux the links we care about will never |
|
446
|
|
|
|
|
|
|
* be relative links (cwd, exec) |
|
447
|
|
|
|
|
|
|
* Doing this because readlink works on static buffers */ |
|
448
|
|
|
|
|
|
|
/* canonicalize_file_name is no good for musl, use realpath instead */ |
|
449
|
106
|
|
|
|
|
|
link = realpath(link_file, NULL); |
|
450
|
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
/* we no longer need need the path to the link file */ |
|
452
|
106
|
50
|
|
|
|
|
obstack_free(mem_pool, link_file); |
|
|
|
50
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
|
|
454
|
106
|
50
|
|
|
|
|
if(link == NULL) { |
|
455
|
0
|
|
|
|
|
|
return; |
|
456
|
|
|
|
|
|
|
} |
|
457
|
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
/* copy the path onto our obstack, set the value (somewhere in pts) |
|
459
|
|
|
|
|
|
|
* and free the results of canonicalize_file_name */ |
|
460
|
106
|
|
|
|
|
|
obstack_printf(mem_pool, "%s", link); |
|
461
|
106
|
50
|
|
|
|
|
obstack_1grow(mem_pool, '\0'); |
|
462
|
|
|
|
|
|
|
|
|
463
|
106
|
50
|
|
|
|
|
*ptr = (char *)obstack_finish(mem_pool); |
|
|
|
50
|
|
|
|
|
|
|
464
|
106
|
|
|
|
|
|
free(link); |
|
465
|
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
/* enable whatever field we successfuly retrived */ |
|
467
|
106
|
|
|
|
|
|
field_enable(format_str, field); |
|
468
|
|
|
|
|
|
|
} |
|
469
|
|
|
|
|
|
|
|
|
470
|
53
|
|
|
|
|
|
static void get_proc_cmndline(char *pid, char *format_str, struct procstat *prs, |
|
471
|
|
|
|
|
|
|
struct obstack *mem_pool) |
|
472
|
|
|
|
|
|
|
{ |
|
473
|
|
|
|
|
|
|
char *cmndline_text, *cur; |
|
474
|
|
|
|
|
|
|
off_t cmndline_off; |
|
475
|
|
|
|
|
|
|
|
|
476
|
53
|
50
|
|
|
|
|
if((cmndline_text = read_file(pid, "cmdline", &cmndline_off, mem_pool)) == NULL) { |
|
477
|
0
|
|
|
|
|
|
return; |
|
478
|
|
|
|
|
|
|
} |
|
479
|
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
/* replace all '\0' with spaces (except for the last one */ |
|
481
|
7483
|
100
|
|
|
|
|
for(cur = cmndline_text; cur < cmndline_text + cmndline_off - 1; cur++) { |
|
482
|
7430
|
100
|
|
|
|
|
if(*cur == '\0') { |
|
483
|
342
|
|
|
|
|
|
*cur = ' '; |
|
484
|
|
|
|
|
|
|
} |
|
485
|
|
|
|
|
|
|
} |
|
486
|
|
|
|
|
|
|
|
|
487
|
53
|
|
|
|
|
|
prs->cmndline = cmndline_text; |
|
488
|
53
|
|
|
|
|
|
field_enable(format_str, F_CMNDLINE); |
|
489
|
|
|
|
|
|
|
} |
|
490
|
|
|
|
|
|
|
|
|
491
|
53
|
|
|
|
|
|
static void get_proc_cmdline(char *pid, char *format_str, struct procstat *prs, |
|
492
|
|
|
|
|
|
|
struct obstack *mem_pool) |
|
493
|
|
|
|
|
|
|
{ |
|
494
|
|
|
|
|
|
|
char *cmdline_text; |
|
495
|
|
|
|
|
|
|
off_t cmdline_off; |
|
496
|
|
|
|
|
|
|
|
|
497
|
53
|
50
|
|
|
|
|
if((cmdline_text = read_file(pid, "cmdline", &cmdline_off, mem_pool)) == NULL) { |
|
498
|
0
|
|
|
|
|
|
return; |
|
499
|
|
|
|
|
|
|
} |
|
500
|
|
|
|
|
|
|
|
|
501
|
53
|
|
|
|
|
|
prs->cmdline = cmdline_text; |
|
502
|
53
|
|
|
|
|
|
prs->cmdline_len = cmdline_off; |
|
503
|
53
|
|
|
|
|
|
field_enable(format_str, F_CMDLINE); |
|
504
|
|
|
|
|
|
|
} |
|
505
|
|
|
|
|
|
|
|
|
506
|
53
|
|
|
|
|
|
static void get_proc_environ(char *pid, char *format_str, struct procstat *prs, |
|
507
|
|
|
|
|
|
|
struct obstack *mem_pool) |
|
508
|
|
|
|
|
|
|
{ |
|
509
|
|
|
|
|
|
|
char *environ_text; |
|
510
|
|
|
|
|
|
|
off_t environ_off; |
|
511
|
|
|
|
|
|
|
|
|
512
|
53
|
50
|
|
|
|
|
if((environ_text = read_file(pid, "environ", &environ_off, mem_pool)) == NULL) { |
|
513
|
0
|
|
|
|
|
|
return; |
|
514
|
|
|
|
|
|
|
} |
|
515
|
|
|
|
|
|
|
|
|
516
|
53
|
|
|
|
|
|
prs->environ = environ_text; |
|
517
|
53
|
|
|
|
|
|
prs->environ_len = environ_off; |
|
518
|
53
|
|
|
|
|
|
field_enable(format_str, F_ENVIRON); |
|
519
|
|
|
|
|
|
|
} |
|
520
|
|
|
|
|
|
|
|
|
521
|
53
|
|
|
|
|
|
static void get_proc_status(char *pid, char *format_str, struct procstat *prs, |
|
522
|
|
|
|
|
|
|
struct obstack *mem_pool) |
|
523
|
|
|
|
|
|
|
{ |
|
524
|
|
|
|
|
|
|
char *status_text, *loc; |
|
525
|
|
|
|
|
|
|
off_t status_len; |
|
526
|
|
|
|
|
|
|
int dummy_i; |
|
527
|
|
|
|
|
|
|
|
|
528
|
53
|
50
|
|
|
|
|
if((status_text = read_file(pid, "status", &status_len, mem_pool)) == NULL) { |
|
529
|
0
|
|
|
|
|
|
return; |
|
530
|
|
|
|
|
|
|
} |
|
531
|
|
|
|
|
|
|
|
|
532
|
53
|
|
|
|
|
|
loc = status_text; |
|
533
|
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
/* |
|
535
|
|
|
|
|
|
|
* get the euid, egid and so on out of /proc/$$/status |
|
536
|
|
|
|
|
|
|
* where the 2 lines in which we are interested in are: |
|
537
|
|
|
|
|
|
|
* [5] Uid: 500 500 500 500 |
|
538
|
|
|
|
|
|
|
* [6] Gid: 500 500 500 500 |
|
539
|
|
|
|
|
|
|
* added by scip |
|
540
|
|
|
|
|
|
|
*/ |
|
541
|
|
|
|
|
|
|
|
|
542
|
530
|
50
|
|
|
|
|
for(loc = status_text; loc; loc = strchr(loc, '\n')) { |
|
543
|
|
|
|
|
|
|
/* skip past the \n character */ |
|
544
|
530
|
100
|
|
|
|
|
if(loc != status_text) { |
|
545
|
477
|
|
|
|
|
|
loc++; |
|
546
|
|
|
|
|
|
|
} |
|
547
|
|
|
|
|
|
|
|
|
548
|
530
|
100
|
|
|
|
|
if(strncmp(loc, "Uid:", 4) == 0) { |
|
549
|
53
|
|
|
|
|
|
sscanf(loc + 4, " %d %d %d %d", &dummy_i, &prs->euid, &prs->suid, &prs->fuid); |
|
550
|
53
|
|
|
|
|
|
field_enable_range(format_str, F_EUID, F_FUID); |
|
551
|
477
|
100
|
|
|
|
|
} else if(strncmp(loc, "Gid:", 4) == 0) { |
|
552
|
53
|
|
|
|
|
|
sscanf(loc + 4, " %d %d %d %d", &dummy_i, &prs->egid, &prs->sgid, &prs->fgid); |
|
553
|
53
|
|
|
|
|
|
field_enable_range(format_str, F_EGID, F_FGID); |
|
554
|
424
|
100
|
|
|
|
|
} else if(strncmp(loc, "TracerPid:", 10) == 0) { |
|
555
|
53
|
|
|
|
|
|
sscanf(loc + 10, " %d", &prs->tracer); |
|
556
|
53
|
|
|
|
|
|
field_enable(format_str, F_TRACER); |
|
557
|
|
|
|
|
|
|
} |
|
558
|
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
/* short circuit condition */ |
|
560
|
530
|
100
|
|
|
|
|
if(islower(format_str[F_EUID]) && islower(format_str[F_EGID]) && islower(format_str[F_TRACER])) { |
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
561
|
53
|
|
|
|
|
|
goto done; |
|
562
|
|
|
|
|
|
|
} |
|
563
|
|
|
|
|
|
|
} |
|
564
|
|
|
|
|
|
|
|
|
565
|
|
|
|
|
|
|
done: |
|
566
|
53
|
50
|
|
|
|
|
obstack_free(mem_pool, status_text); |
|
|
|
50
|
|
|
|
|
|
|
567
|
|
|
|
|
|
|
} |
|
568
|
|
|
|
|
|
|
|
|
569
|
|
|
|
|
|
|
/* fixup_stat_values() |
|
570
|
|
|
|
|
|
|
* |
|
571
|
|
|
|
|
|
|
* Correct, calculate, covert values to user expected values. |
|
572
|
|
|
|
|
|
|
* |
|
573
|
|
|
|
|
|
|
* @param format_str String containing field index types |
|
574
|
|
|
|
|
|
|
* @param prs Data structure to peform fixups on |
|
575
|
|
|
|
|
|
|
*/ |
|
576
|
53
|
|
|
|
|
|
static void fixup_stat_values(char *format_str, struct procstat *prs) |
|
577
|
|
|
|
|
|
|
{ |
|
578
|
|
|
|
|
|
|
/* set the state pointer to the right (const) string */ |
|
579
|
53
|
|
|
|
|
|
switch(prs->state_c) { |
|
580
|
|
|
|
|
|
|
case 'S': |
|
581
|
48
|
|
|
|
|
|
prs->state = get_string(SLEEP); |
|
582
|
48
|
|
|
|
|
|
break; |
|
583
|
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
case 'W': |
|
585
|
0
|
|
|
|
|
|
prs->state = get_string(WAIT); /*Waking state. Could be mapped to WAKING, but would break backward compatibility */ |
|
586
|
0
|
|
|
|
|
|
break; |
|
587
|
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
case 'R': |
|
589
|
5
|
|
|
|
|
|
prs->state = get_string(RUN); |
|
590
|
5
|
|
|
|
|
|
break; |
|
591
|
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
case 'I': |
|
593
|
0
|
|
|
|
|
|
prs->state = get_string(IDLE); |
|
594
|
0
|
|
|
|
|
|
break; |
|
595
|
|
|
|
|
|
|
|
|
596
|
|
|
|
|
|
|
case 'Z': |
|
597
|
0
|
|
|
|
|
|
prs->state = get_string(DEFUNCT); |
|
598
|
0
|
|
|
|
|
|
break; |
|
599
|
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
case 'D': |
|
601
|
0
|
|
|
|
|
|
prs->state = get_string(UWAIT); |
|
602
|
0
|
|
|
|
|
|
break; |
|
603
|
|
|
|
|
|
|
|
|
604
|
|
|
|
|
|
|
case 'T': |
|
605
|
|
|
|
|
|
|
case 'H': /* GNU Hurd HALTED state */ |
|
606
|
0
|
|
|
|
|
|
prs->state = get_string(STOP); |
|
607
|
0
|
|
|
|
|
|
break; |
|
608
|
|
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
case 'x': |
|
610
|
0
|
|
|
|
|
|
prs->state = get_string(DEAD); |
|
611
|
0
|
|
|
|
|
|
break; |
|
612
|
|
|
|
|
|
|
|
|
613
|
|
|
|
|
|
|
case 'X': |
|
614
|
0
|
|
|
|
|
|
prs->state = get_string(DEAD); |
|
615
|
0
|
|
|
|
|
|
break; |
|
616
|
|
|
|
|
|
|
|
|
617
|
|
|
|
|
|
|
case 'K': |
|
618
|
0
|
|
|
|
|
|
prs->state = get_string(WAKEKILL); |
|
619
|
0
|
|
|
|
|
|
break; |
|
620
|
|
|
|
|
|
|
|
|
621
|
|
|
|
|
|
|
case 't': |
|
622
|
0
|
|
|
|
|
|
prs->state = get_string(TRACINGSTOP); |
|
623
|
0
|
|
|
|
|
|
break; |
|
624
|
|
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
case 'P': |
|
626
|
0
|
|
|
|
|
|
prs->state = get_string(PARKED); |
|
627
|
0
|
|
|
|
|
|
break; |
|
628
|
|
|
|
|
|
|
|
|
629
|
|
|
|
|
|
|
/* unknown state, state is already set to NULL */ |
|
630
|
|
|
|
|
|
|
default: |
|
631
|
0
|
|
|
|
|
|
ppt_warn("Ran into unknown state (hex char: %x)", (int)prs->state_c); |
|
632
|
0
|
|
|
|
|
|
goto skip_state_format; |
|
633
|
|
|
|
|
|
|
} |
|
634
|
|
|
|
|
|
|
|
|
635
|
53
|
|
|
|
|
|
field_enable(format_str, F_STATE); |
|
636
|
|
|
|
|
|
|
|
|
637
|
|
|
|
|
|
|
skip_state_format: |
|
638
|
|
|
|
|
|
|
|
|
639
|
53
|
|
|
|
|
|
prs->start_time = (prs->start_time / system_hertz) + boot_time; |
|
640
|
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
/* fix time */ |
|
642
|
|
|
|
|
|
|
|
|
643
|
53
|
|
|
|
|
|
prs->stime = JIFFIES_TO_MICROSECONDS(prs->stime); |
|
644
|
53
|
|
|
|
|
|
prs->utime = JIFFIES_TO_MICROSECONDS(prs->utime); |
|
645
|
53
|
|
|
|
|
|
prs->cstime = JIFFIES_TO_MICROSECONDS(prs->cstime); |
|
646
|
53
|
|
|
|
|
|
prs->cutime = JIFFIES_TO_MICROSECONDS(prs->cutime); |
|
647
|
|
|
|
|
|
|
|
|
648
|
|
|
|
|
|
|
/* derived time values */ |
|
649
|
53
|
|
|
|
|
|
prs->time = prs->utime + prs->stime; |
|
650
|
53
|
|
|
|
|
|
prs->ctime = prs->cutime + prs->cstime; |
|
651
|
|
|
|
|
|
|
|
|
652
|
53
|
|
|
|
|
|
field_enable_range(format_str, F_TIME, F_CTIME); |
|
653
|
|
|
|
|
|
|
|
|
654
|
|
|
|
|
|
|
/* fix rss to be in bytes (returned from kernel in pages) */ |
|
655
|
53
|
|
|
|
|
|
prs->rss *= page_size; |
|
656
|
53
|
|
|
|
|
|
} |
|
657
|
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
/* calc_prec() |
|
659
|
|
|
|
|
|
|
* |
|
660
|
|
|
|
|
|
|
* calculate the two cpu/memory precentage values |
|
661
|
|
|
|
|
|
|
*/ |
|
662
|
53
|
|
|
|
|
|
static void calc_prec(char *format_str, struct procstat *prs, struct obstack *mem_pool) |
|
663
|
|
|
|
|
|
|
{ |
|
664
|
|
|
|
|
|
|
int len; |
|
665
|
|
|
|
|
|
|
|
|
666
|
|
|
|
|
|
|
/* calculate pctcpu - NOTE: This assumes the cpu time is in microsecond units! |
|
667
|
|
|
|
|
|
|
* multiplying by 1/1e6 puts all units back in seconds. Then multiply by 100.0f to get a percentage. |
|
668
|
|
|
|
|
|
|
*/ |
|
669
|
|
|
|
|
|
|
|
|
670
|
53
|
|
|
|
|
|
float pctcpu = (100.0f * (prs->utime + prs->stime) * 1 / 1e6) / (time(NULL) - prs->start_time); |
|
671
|
|
|
|
|
|
|
|
|
672
|
53
|
|
|
|
|
|
len = snprintf(prs->pctcpu, LENGTH_PCTCPU, "%6.2f", pctcpu); |
|
673
|
53
|
50
|
|
|
|
|
if(len >= LENGTH_PCTCPU) { |
|
674
|
0
|
|
|
|
|
|
ppt_warn("percent cpu truncated from %d, set LENGTH_PCTCPU to at least: %d)", len, len + 1); |
|
675
|
|
|
|
|
|
|
} |
|
676
|
|
|
|
|
|
|
|
|
677
|
|
|
|
|
|
|
|
|
678
|
53
|
|
|
|
|
|
field_enable(format_str, F_PCTCPU); |
|
679
|
|
|
|
|
|
|
|
|
680
|
|
|
|
|
|
|
/* calculate pctmem */ |
|
681
|
53
|
50
|
|
|
|
|
if(system_memory > 0) { |
|
682
|
53
|
|
|
|
|
|
sprintf(prs->pctmem, "%3.2f", (float)prs->rss / system_memory * 100.f); |
|
683
|
53
|
|
|
|
|
|
field_enable(format_str, F_PCTMEM); |
|
684
|
|
|
|
|
|
|
} |
|
685
|
53
|
|
|
|
|
|
} |
|
686
|
|
|
|
|
|
|
|
|
687
|
|
|
|
|
|
|
/* is_pid() |
|
688
|
|
|
|
|
|
|
* |
|
689
|
|
|
|
|
|
|
* |
|
690
|
|
|
|
|
|
|
* @return Boolean value. |
|
691
|
|
|
|
|
|
|
*/ |
|
692
|
363
|
|
|
|
|
|
inline static bool is_pid(const char *str) |
|
693
|
|
|
|
|
|
|
{ |
|
694
|
502
|
100
|
|
|
|
|
for(; *str; str++) { |
|
695
|
449
|
100
|
|
|
|
|
if(!isdigit(*str)) { |
|
696
|
310
|
|
|
|
|
|
return false; |
|
697
|
|
|
|
|
|
|
} |
|
698
|
|
|
|
|
|
|
} |
|
699
|
|
|
|
|
|
|
|
|
700
|
53
|
|
|
|
|
|
return true; |
|
701
|
|
|
|
|
|
|
} |
|
702
|
|
|
|
|
|
|
|
|
703
|
0
|
|
|
|
|
|
inline static bool pid_exists(const char *str, struct obstack *mem_pool) |
|
704
|
|
|
|
|
|
|
{ |
|
705
|
0
|
|
|
|
|
|
char *pid_dir_path = NULL; |
|
706
|
|
|
|
|
|
|
int result; |
|
707
|
|
|
|
|
|
|
|
|
708
|
0
|
|
|
|
|
|
obstack_printf(mem_pool, "/proc/%s", str); |
|
709
|
0
|
0
|
|
|
|
|
obstack_1grow(mem_pool, '\0'); |
|
710
|
0
|
0
|
|
|
|
|
pid_dir_path = obstack_finish(mem_pool); |
|
|
|
0
|
|
|
|
|
|
|
711
|
|
|
|
|
|
|
|
|
712
|
|
|
|
|
|
|
/* directory exists? */ |
|
713
|
0
|
|
|
|
|
|
result = (access(pid_dir_path, F_OK) != -1); |
|
714
|
|
|
|
|
|
|
|
|
715
|
0
|
0
|
|
|
|
|
obstack_free(mem_pool, pid_dir_path); |
|
|
|
0
|
|
|
|
|
|
|
716
|
|
|
|
|
|
|
|
|
717
|
0
|
|
|
|
|
|
return result; |
|
718
|
|
|
|
|
|
|
} |
|
719
|
|
|
|
|
|
|
|
|
720
|
5
|
|
|
|
|
|
void OS_get_table() |
|
721
|
|
|
|
|
|
|
{ |
|
722
|
|
|
|
|
|
|
/* dir walker storage */ |
|
723
|
|
|
|
|
|
|
DIR * dir; |
|
724
|
|
|
|
|
|
|
struct dirent *dir_ent, *dir_result; |
|
725
|
|
|
|
|
|
|
|
|
726
|
|
|
|
|
|
|
/* all our storage is going to be here */ |
|
727
|
|
|
|
|
|
|
struct obstack mem_pool; |
|
728
|
|
|
|
|
|
|
|
|
729
|
|
|
|
|
|
|
/* container for scraped process values */ |
|
730
|
|
|
|
|
|
|
struct procstat *prs; |
|
731
|
|
|
|
|
|
|
|
|
732
|
|
|
|
|
|
|
/* string containing our local copy of format_str, elements will be |
|
733
|
|
|
|
|
|
|
* lower cased if we are able to figure them out */ |
|
734
|
|
|
|
|
|
|
char *format_str; |
|
735
|
|
|
|
|
|
|
|
|
736
|
|
|
|
|
|
|
/* initialize a small memory pool for this function */ |
|
737
|
5
|
|
|
|
|
|
obstack_init(&mem_pool); |
|
738
|
|
|
|
|
|
|
|
|
739
|
|
|
|
|
|
|
/* put the dirent on the obstack, since it's rather large */ |
|
740
|
5
|
50
|
|
|
|
|
dir_ent = obstack_alloc(&mem_pool, sizeof(struct dirent)); |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
741
|
|
|
|
|
|
|
|
|
742
|
5
|
50
|
|
|
|
|
if((dir = opendir("/proc")) == NULL) { |
|
743
|
0
|
|
|
|
|
|
return; |
|
744
|
|
|
|
|
|
|
} |
|
745
|
|
|
|
|
|
|
|
|
746
|
|
|
|
|
|
|
/* Iterate through all the process entries (numeric) under /proc */ |
|
747
|
368
|
50
|
|
|
|
|
while(readdir_r(dir, dir_ent, &dir_result) == 0 && dir_result) { |
|
|
|
100
|
|
|
|
|
|
|
748
|
|
|
|
|
|
|
/* Only look at this file if it's a proc id; that is, all numbers */ |
|
749
|
363
|
100
|
|
|
|
|
if(!is_pid(dir_result->d_name)) { |
|
750
|
310
|
|
|
|
|
|
continue; |
|
751
|
|
|
|
|
|
|
} |
|
752
|
|
|
|
|
|
|
|
|
753
|
|
|
|
|
|
|
/* allocate container for storing process values */ |
|
754
|
53
|
50
|
|
|
|
|
prs = obstack_alloc(&mem_pool, sizeof(struct procstat)); |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
755
|
53
|
|
|
|
|
|
bzero(prs, sizeof(struct procstat)); |
|
756
|
|
|
|
|
|
|
|
|
757
|
|
|
|
|
|
|
/* initialize the format string */ |
|
758
|
53
|
|
|
|
|
|
obstack_printf(&mem_pool, "%s", get_string(STR_DEFAULT_FORMAT)); |
|
759
|
53
|
50
|
|
|
|
|
obstack_1grow(&mem_pool, '\0'); |
|
760
|
53
|
50
|
|
|
|
|
format_str = (char *)obstack_finish(&mem_pool); |
|
|
|
50
|
|
|
|
|
|
|
761
|
|
|
|
|
|
|
|
|
762
|
|
|
|
|
|
|
/* get process' uid/guid */ |
|
763
|
53
|
|
|
|
|
|
get_user_info(dir_result->d_name, format_str, prs, &mem_pool); |
|
764
|
|
|
|
|
|
|
|
|
765
|
|
|
|
|
|
|
/* scrape /proc/${pid}/stat */ |
|
766
|
53
|
50
|
|
|
|
|
if(get_proc_stat(dir_result->d_name, format_str, prs, &mem_pool) == false) { |
|
767
|
|
|
|
|
|
|
/* did the pid directory go away mid flight? */ |
|
768
|
0
|
0
|
|
|
|
|
if(pid_exists(dir_result->d_name, &mem_pool) == false) { |
|
769
|
0
|
|
|
|
|
|
continue; |
|
770
|
|
|
|
|
|
|
} |
|
771
|
|
|
|
|
|
|
} |
|
772
|
|
|
|
|
|
|
|
|
773
|
|
|
|
|
|
|
/* correct values (times) found in /proc/${pid}/stat */ |
|
774
|
53
|
|
|
|
|
|
fixup_stat_values(format_str, prs); |
|
775
|
|
|
|
|
|
|
|
|
776
|
|
|
|
|
|
|
/* get process' cmndline */ |
|
777
|
53
|
|
|
|
|
|
get_proc_cmndline(dir_result->d_name, format_str, prs, &mem_pool); |
|
778
|
|
|
|
|
|
|
|
|
779
|
|
|
|
|
|
|
/* get process' cmdline */ |
|
780
|
53
|
|
|
|
|
|
get_proc_cmdline(dir_result->d_name, format_str, prs, &mem_pool); |
|
781
|
|
|
|
|
|
|
|
|
782
|
|
|
|
|
|
|
/* get process' environ */ |
|
783
|
53
|
|
|
|
|
|
get_proc_environ(dir_result->d_name, format_str, prs, &mem_pool); |
|
784
|
|
|
|
|
|
|
|
|
785
|
|
|
|
|
|
|
/* get process' cwd & exec values from the symblink */ |
|
786
|
53
|
|
|
|
|
|
eval_link(dir_result->d_name, "cwd", F_CWD, &prs->cwd, format_str, |
|
787
|
|
|
|
|
|
|
&mem_pool); |
|
788
|
53
|
|
|
|
|
|
eval_link(dir_result->d_name, "exe", F_EXEC, &prs->exec, format_str, |
|
789
|
|
|
|
|
|
|
&mem_pool); |
|
790
|
|
|
|
|
|
|
|
|
791
|
|
|
|
|
|
|
/* scrape from /proc/{$pid}/status */ |
|
792
|
53
|
|
|
|
|
|
get_proc_status(dir_result->d_name, format_str, prs, &mem_pool); |
|
793
|
|
|
|
|
|
|
|
|
794
|
|
|
|
|
|
|
/* calculate precent cpu & mem values */ |
|
795
|
53
|
|
|
|
|
|
calc_prec(format_str, prs, &mem_pool); |
|
796
|
|
|
|
|
|
|
|
|
797
|
|
|
|
|
|
|
/* Go ahead and bless into a perl object */ |
|
798
|
|
|
|
|
|
|
/* Linux.h defines const char* const* Fiels, but we cast it away, as bless_into_proc only understands char** */ |
|
799
|
53
|
|
|
|
|
|
bless_into_proc(format_str, (char **)field_names, |
|
800
|
|
|
|
|
|
|
prs->uid, |
|
801
|
|
|
|
|
|
|
prs->gid, |
|
802
|
|
|
|
|
|
|
prs->pid, |
|
803
|
53
|
|
|
|
|
|
prs->comm, |
|
804
|
|
|
|
|
|
|
prs->ppid, |
|
805
|
|
|
|
|
|
|
prs->pgrp, |
|
806
|
|
|
|
|
|
|
prs->sid, |
|
807
|
|
|
|
|
|
|
prs->tty, |
|
808
|
|
|
|
|
|
|
prs->flags, |
|
809
|
|
|
|
|
|
|
prs->minflt, |
|
810
|
|
|
|
|
|
|
prs->cminflt, |
|
811
|
|
|
|
|
|
|
prs->majflt, |
|
812
|
|
|
|
|
|
|
prs->cmajflt, |
|
813
|
|
|
|
|
|
|
prs->utime, |
|
814
|
|
|
|
|
|
|
prs->stime, |
|
815
|
|
|
|
|
|
|
prs->cutime, |
|
816
|
|
|
|
|
|
|
prs->cstime, |
|
817
|
|
|
|
|
|
|
prs->priority, |
|
818
|
|
|
|
|
|
|
prs->start_time, |
|
819
|
|
|
|
|
|
|
prs->vsize, |
|
820
|
|
|
|
|
|
|
prs->rss, |
|
821
|
|
|
|
|
|
|
prs->wchan, |
|
822
|
|
|
|
|
|
|
prs->time, |
|
823
|
|
|
|
|
|
|
prs->ctime, |
|
824
|
|
|
|
|
|
|
prs->state, |
|
825
|
|
|
|
|
|
|
prs->euid, |
|
826
|
|
|
|
|
|
|
prs->suid, |
|
827
|
|
|
|
|
|
|
prs->fuid, |
|
828
|
|
|
|
|
|
|
prs->egid, |
|
829
|
|
|
|
|
|
|
prs->sgid, |
|
830
|
|
|
|
|
|
|
prs->fgid, |
|
831
|
53
|
|
|
|
|
|
prs->pctcpu, |
|
832
|
53
|
|
|
|
|
|
prs->pctmem, |
|
833
|
|
|
|
|
|
|
prs->cmndline, |
|
834
|
|
|
|
|
|
|
prs->exec, |
|
835
|
|
|
|
|
|
|
prs->cwd, |
|
836
|
|
|
|
|
|
|
prs->cmdline, |
|
837
|
|
|
|
|
|
|
prs->cmdline_len, |
|
838
|
|
|
|
|
|
|
prs->environ, |
|
839
|
|
|
|
|
|
|
prs->environ_len, |
|
840
|
|
|
|
|
|
|
prs->tracer |
|
841
|
|
|
|
|
|
|
); |
|
842
|
|
|
|
|
|
|
|
|
843
|
|
|
|
|
|
|
/* we want a new prs, for the next itteration */ |
|
844
|
53
|
100
|
|
|
|
|
obstack_free(&mem_pool, prs); |
|
|
|
50
|
|
|
|
|
|
|
845
|
|
|
|
|
|
|
} |
|
846
|
|
|
|
|
|
|
|
|
847
|
5
|
|
|
|
|
|
closedir(dir); |
|
848
|
|
|
|
|
|
|
|
|
849
|
|
|
|
|
|
|
/* free all our tempoary memory */ |
|
850
|
5
|
50
|
|
|
|
|
obstack_free(&mem_pool, NULL); |
|
|
|
0
|
|
|
|
|
|
|
851
|
|
|
|
|
|
|
} |