]>
Commit | Line | Data |
---|---|---|
6d031c0b LP |
1 | /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ |
2 | ||
cd3bccaa | 3 | /*** |
6d031c0b | 4 | This file is part of systemd. |
cd3bccaa AK |
5 | |
6 | Copyright (C) 2009-2013 Intel Coproration | |
7 | ||
8 | Authors: | |
9 | Auke Kok <auke-jan.h.kok@intel.com> | |
10 | ||
11 | systemd is free software; you can redistribute it and/or modify it | |
12 | under the terms of the GNU Lesser General Public License as published by | |
13 | the Free Software Foundation; either version 2.1 of the License, or | |
14 | (at your option) any later version. | |
15 | ||
16 | systemd is distributed in the hope that it will be useful, but | |
17 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
19 | Lesser General Public License for more details. | |
20 | ||
21 | You should have received a copy of the GNU Lesser General Public License | |
22 | along with systemd; If not, see <http://www.gnu.org/licenses/>. | |
23 | ***/ | |
83fdc450 | 24 | |
83fdc450 AK |
25 | #include <unistd.h> |
26 | #include <stdlib.h> | |
27 | #include <limits.h> | |
28 | #include <sys/types.h> | |
29 | #include <sys/stat.h> | |
30 | #include <stdio.h> | |
31 | #include <string.h> | |
32 | #include <dirent.h> | |
33 | #include <fcntl.h> | |
34 | #include <time.h> | |
35 | ||
53f5329f | 36 | #include "util.h" |
6d031c0b LP |
37 | #include "store.h" |
38 | #include "bootchart.h" | |
83fdc450 AK |
39 | |
40 | /* | |
41 | * Alloc a static 4k buffer for stdio - primarily used to increase | |
42 | * PSS buffering from the default 1k stdin buffer to reduce | |
43 | * read() overhead. | |
44 | */ | |
45 | static char smaps_buf[4096]; | |
46 | DIR *proc; | |
6d031c0b | 47 | int procfd = -1; |
83fdc450 | 48 | |
6d031c0b | 49 | double gettime_ns(void) { |
2c408fbf | 50 | struct timespec n; |
83fdc450 | 51 | |
2c408fbf | 52 | clock_gettime(CLOCK_MONOTONIC, &n); |
83fdc450 | 53 | |
2c408fbf | 54 | return (n.tv_sec + (n.tv_nsec / 1000000000.0)); |
83fdc450 AK |
55 | } |
56 | ||
6d031c0b | 57 | void log_uptime(void) { |
57dbefdf | 58 | FILE _cleanup_fclose_ *f = NULL; |
28989b63 TA |
59 | char str[32]; |
60 | double uptime; | |
61 | ||
62 | f = fopen("/proc/uptime", "r"); | |
f2f85884 | 63 | |
28989b63 TA |
64 | if (!f) |
65 | return; | |
57dbefdf | 66 | if (!fscanf(f, "%s %*s", str)) |
28989b63 | 67 | return; |
57dbefdf | 68 | |
28989b63 TA |
69 | uptime = strtod(str, NULL); |
70 | ||
71 | log_start = gettime_ns(); | |
72 | ||
73 | /* start graph at kernel boot time */ | |
6d031c0b | 74 | if (arg_relative) |
28989b63 TA |
75 | graph_start = log_start; |
76 | else | |
77 | graph_start = log_start - uptime; | |
83fdc450 AK |
78 | } |
79 | ||
6d031c0b | 80 | static char *bufgetline(char *buf) { |
28989b63 | 81 | char *c; |
83fdc450 | 82 | |
28989b63 TA |
83 | if (!buf) |
84 | return NULL; | |
83fdc450 | 85 | |
28989b63 TA |
86 | c = strchr(buf, '\n'); |
87 | if (c) | |
88 | c++; | |
89 | return c; | |
83fdc450 AK |
90 | } |
91 | ||
e90f9fa4 | 92 | static int pid_cmdline_strncpy(char *buffer, int pid, size_t buf_len) { |
6d031c0b LP |
93 | char filename[PATH_MAX]; |
94 | int _cleanup_close_ fd=-1; | |
95 | ssize_t n; | |
e90f9fa4 | 96 | |
6d031c0b LP |
97 | sprintf(filename, "%d/cmdline", pid); |
98 | fd = openat(procfd, filename, O_RDONLY); | |
99 | if (fd < 0) | |
100 | return -errno; | |
e90f9fa4 | 101 | |
6d031c0b | 102 | n = read(fd, buffer, buf_len-1); |
e90f9fa4 HH |
103 | if (n > 0) { |
104 | int i; | |
105 | for (i = 0; i < n; i++) | |
106 | if (buffer[i] == '\0') | |
107 | buffer[i] = ' '; | |
108 | buffer[n] = '\0'; | |
109 | } | |
6d031c0b | 110 | return 0; |
e90f9fa4 | 111 | } |
83fdc450 | 112 | |
6d031c0b | 113 | void log_sample(int sample) { |
28989b63 TA |
114 | static int vmstat; |
115 | static int schedstat; | |
28989b63 TA |
116 | char buf[4095]; |
117 | char key[256]; | |
118 | char val[256]; | |
119 | char rt[256]; | |
120 | char wt[256]; | |
121 | char *m; | |
122 | int c; | |
123 | int p; | |
124 | int mod; | |
125 | static int e_fd; | |
126 | ssize_t s; | |
127 | ssize_t n; | |
128 | struct dirent *ent; | |
f2f85884 HH |
129 | int fd; |
130 | ||
131 | /* all the per-process stuff goes here */ | |
132 | if (!proc) { | |
133 | /* find all processes */ | |
134 | proc = opendir("/proc"); | |
135 | if (!proc) | |
136 | return; | |
137 | procfd = dirfd(proc); | |
138 | } else { | |
139 | rewinddir(proc); | |
140 | } | |
28989b63 TA |
141 | |
142 | if (!vmstat) { | |
143 | /* block stuff */ | |
f2f85884 | 144 | vmstat = openat(procfd, "vmstat", O_RDONLY); |
28989b63 TA |
145 | if (vmstat == -1) { |
146 | perror("open /proc/vmstat"); | |
147 | exit (EXIT_FAILURE); | |
148 | } | |
149 | } | |
150 | ||
151 | n = pread(vmstat, buf, sizeof(buf) - 1, 0); | |
152 | if (n <= 0) { | |
153 | close(vmstat); | |
154 | return; | |
155 | } | |
156 | buf[n] = '\0'; | |
157 | ||
158 | m = buf; | |
159 | while (m) { | |
160 | if (sscanf(m, "%s %s", key, val) < 2) | |
161 | goto vmstat_next; | |
53f5329f | 162 | if (streq(key, "pgpgin")) |
28989b63 | 163 | blockstat[sample].bi = atoi(val); |
53f5329f | 164 | if (streq(key, "pgpgout")) { |
28989b63 TA |
165 | blockstat[sample].bo = atoi(val); |
166 | break; | |
167 | } | |
83fdc450 | 168 | vmstat_next: |
28989b63 TA |
169 | m = bufgetline(m); |
170 | if (!m) | |
171 | break; | |
172 | } | |
173 | ||
174 | if (!schedstat) { | |
175 | /* overall CPU utilization */ | |
f2f85884 | 176 | schedstat = openat(procfd, "schedstat", O_RDONLY); |
28989b63 TA |
177 | if (schedstat == -1) { |
178 | perror("open /proc/schedstat"); | |
179 | exit (EXIT_FAILURE); | |
180 | } | |
181 | } | |
182 | ||
183 | n = pread(schedstat, buf, sizeof(buf) - 1, 0); | |
184 | if (n <= 0) { | |
185 | close(schedstat); | |
186 | return; | |
187 | } | |
188 | buf[n] = '\0'; | |
189 | ||
190 | m = buf; | |
191 | while (m) { | |
192 | if (sscanf(m, "%s %*s %*s %*s %*s %*s %*s %s %s", key, rt, wt) < 3) | |
193 | goto schedstat_next; | |
194 | ||
195 | if (strstr(key, "cpu")) { | |
196 | c = atoi((const char*)(key+3)); | |
197 | if (c > MAXCPUS) | |
198 | /* Oops, we only have room for MAXCPUS data */ | |
199 | break; | |
200 | cpustat[c].sample[sample].runtime = atoll(rt); | |
201 | cpustat[c].sample[sample].waittime = atoll(wt); | |
202 | ||
203 | if (c == cpus) | |
204 | cpus = c + 1; | |
205 | } | |
83fdc450 | 206 | schedstat_next: |
28989b63 TA |
207 | m = bufgetline(m); |
208 | if (!m) | |
209 | break; | |
210 | } | |
211 | ||
6d031c0b | 212 | if (arg_entropy) { |
28989b63 | 213 | if (!e_fd) { |
f2f85884 | 214 | e_fd = openat(procfd, "sys/kernel/random/entropy_avail", O_RDONLY); |
28989b63 TA |
215 | } |
216 | ||
217 | if (e_fd) { | |
218 | n = pread(e_fd, buf, sizeof(buf) - 1, 0); | |
ef2648c1 LN |
219 | if (n > 0) { |
220 | buf[n] = '\0'; | |
28989b63 | 221 | entropy_avail[sample] = atoi(buf); |
ef2648c1 | 222 | } |
28989b63 TA |
223 | } |
224 | } | |
225 | ||
28989b63 TA |
226 | while ((ent = readdir(proc)) != NULL) { |
227 | char filename[PATH_MAX]; | |
228 | int pid; | |
229 | struct ps_struct *ps; | |
230 | ||
231 | if ((ent->d_name[0] < '0') || (ent->d_name[0] > '9')) | |
232 | continue; | |
233 | ||
234 | pid = atoi(ent->d_name); | |
235 | ||
236 | if (pid >= MAXPIDS) | |
237 | continue; | |
238 | ||
239 | ps = ps_first; | |
240 | while (ps->next_ps) { | |
241 | ps = ps->next_ps; | |
242 | if (ps->pid == pid) | |
243 | break; | |
244 | } | |
245 | ||
246 | /* end of our LL? then append a new record */ | |
247 | if (ps->pid != pid) { | |
57dbefdf | 248 | FILE _cleanup_fclose_ *st = NULL; |
28989b63 TA |
249 | char t[32]; |
250 | struct ps_struct *parent; | |
251 | ||
a2e9b338 | 252 | ps->next_ps = calloc(1, sizeof(struct ps_struct)); |
28989b63 | 253 | if (!ps->next_ps) { |
a2e9b338 | 254 | perror("calloc(ps_struct)"); |
28989b63 TA |
255 | exit (EXIT_FAILURE); |
256 | } | |
28989b63 TA |
257 | ps = ps->next_ps; |
258 | ps->pid = pid; | |
259 | ||
6d031c0b | 260 | ps->sample = calloc(arg_samples_len + 1, sizeof(struct ps_sched_struct)); |
28989b63 | 261 | if (!ps->sample) { |
a2e9b338 | 262 | perror("calloc(ps_struct)"); |
28989b63 TA |
263 | exit (EXIT_FAILURE); |
264 | } | |
28989b63 TA |
265 | |
266 | pscount++; | |
267 | ||
268 | /* mark our first sample */ | |
269 | ps->first = sample; | |
270 | ||
271 | /* get name, start time */ | |
272 | if (!ps->sched) { | |
f2f85884 HH |
273 | sprintf(filename, "%d/sched", pid); |
274 | ps->sched = openat(procfd, filename, O_RDONLY); | |
28989b63 TA |
275 | if (ps->sched == -1) |
276 | continue; | |
277 | } | |
278 | ||
279 | s = pread(ps->sched, buf, sizeof(buf) - 1, 0); | |
280 | if (s <= 0) { | |
281 | close(ps->sched); | |
282 | continue; | |
283 | } | |
ef2648c1 | 284 | buf[s] = '\0'; |
28989b63 TA |
285 | |
286 | if (!sscanf(buf, "%s %*s %*s", key)) | |
287 | continue; | |
288 | ||
e90f9fa4 HH |
289 | strncpy(ps->name, key, 256); |
290 | ||
291 | /* cmdline */ | |
6d031c0b | 292 | if (arg_show_cmdline) |
e90f9fa4 HH |
293 | pid_cmdline_strncpy(ps->name, pid, 256); |
294 | ||
28989b63 TA |
295 | /* discard line 2 */ |
296 | m = bufgetline(buf); | |
297 | if (!m) | |
298 | continue; | |
299 | ||
300 | m = bufgetline(m); | |
301 | if (!m) | |
302 | continue; | |
303 | ||
304 | if (!sscanf(m, "%*s %*s %s", t)) | |
305 | continue; | |
306 | ||
307 | ps->starttime = strtod(t, NULL) / 1000.0; | |
308 | ||
309 | /* ppid */ | |
f2f85884 HH |
310 | sprintf(filename, "%d/stat", pid); |
311 | fd = openat(procfd, filename, O_RDONLY); | |
312 | st = fdopen(fd, "r"); | |
28989b63 TA |
313 | if (!st) |
314 | continue; | |
315 | if (!fscanf(st, "%*s %*s %*s %i", &p)) { | |
28989b63 TA |
316 | continue; |
317 | } | |
28989b63 TA |
318 | ps->ppid = p; |
319 | ||
320 | /* | |
321 | * setup child pointers | |
322 | * | |
323 | * these are used to paint the tree coherently later | |
324 | * each parent has a LL of children, and a LL of siblings | |
325 | */ | |
326 | if (pid == 1) | |
327 | continue; /* nothing to do for init atm */ | |
328 | ||
329 | /* kthreadd has ppid=0, which breaks our tree ordering */ | |
330 | if (ps->ppid == 0) | |
331 | ps->ppid = 1; | |
332 | ||
333 | parent = ps_first; | |
334 | while ((parent->next_ps && parent->pid != ps->ppid)) | |
335 | parent = parent->next_ps; | |
336 | ||
337 | if ((!parent) || (parent->pid != ps->ppid)) { | |
338 | /* orphan */ | |
339 | ps->ppid = 1; | |
340 | parent = ps_first->next_ps; | |
341 | } | |
342 | ||
343 | ps->parent = parent; | |
344 | ||
345 | if (!parent->children) { | |
346 | /* it's the first child */ | |
347 | parent->children = ps; | |
348 | } else { | |
349 | /* walk all children and append */ | |
350 | struct ps_struct *children; | |
351 | children = parent->children; | |
352 | while (children->next) | |
353 | children = children->next; | |
354 | children->next = ps; | |
355 | } | |
356 | } | |
357 | ||
358 | /* else -> found pid, append data in ps */ | |
359 | ||
360 | /* below here is all continuous logging parts - we get here on every | |
361 | * iteration */ | |
362 | ||
363 | /* rt, wt */ | |
364 | if (!ps->schedstat) { | |
f2f85884 HH |
365 | sprintf(filename, "%d/schedstat", pid); |
366 | ps->schedstat = openat(procfd, filename, O_RDONLY); | |
28989b63 TA |
367 | if (ps->schedstat == -1) |
368 | continue; | |
369 | } | |
ef2648c1 LN |
370 | s = pread(ps->schedstat, buf, sizeof(buf) - 1, 0); |
371 | if (s <= 0) { | |
28989b63 TA |
372 | /* clean up our file descriptors - assume that the process exited */ |
373 | close(ps->schedstat); | |
374 | if (ps->sched) | |
375 | close(ps->sched); | |
376 | //if (ps->smaps) | |
377 | // fclose(ps->smaps); | |
378 | continue; | |
379 | } | |
ef2648c1 LN |
380 | buf[s] = '\0'; |
381 | ||
28989b63 TA |
382 | if (!sscanf(buf, "%s %s %*s", rt, wt)) |
383 | continue; | |
384 | ||
385 | ps->last = sample; | |
386 | ps->sample[sample].runtime = atoll(rt); | |
387 | ps->sample[sample].waittime = atoll(wt); | |
388 | ||
389 | ps->total = (ps->sample[ps->last].runtime | |
390 | - ps->sample[ps->first].runtime) | |
391 | / 1000000000.0; | |
392 | ||
6d031c0b | 393 | if (!arg_pss) |
28989b63 TA |
394 | goto catch_rename; |
395 | /* Pss */ | |
396 | if (!ps->smaps) { | |
f2f85884 HH |
397 | sprintf(filename, "%d/smaps", pid); |
398 | fd = openat(procfd, filename, O_RDONLY); | |
399 | ps->smaps = fdopen(fd, "r"); | |
28989b63 TA |
400 | if (!ps->smaps) |
401 | continue; | |
402 | setvbuf(ps->smaps, smaps_buf, _IOFBF, sizeof(smaps_buf)); | |
403 | } else { | |
404 | rewind(ps->smaps); | |
405 | } | |
406 | ||
407 | while (1) { | |
408 | int pss_kb; | |
409 | ||
410 | /* skip one line, this contains the object mapped */ | |
411 | if (fgets(buf, sizeof(buf), ps->smaps) == NULL) | |
412 | break; | |
413 | /* then there's a 28 char 14 line block */ | |
414 | if (fread(buf, 1, 28 * 14, ps->smaps) != 28 * 14) | |
415 | break; | |
416 | ||
417 | pss_kb = atoi(&buf[61]); | |
418 | ps->sample[sample].pss += pss_kb; | |
419 | } | |
420 | ||
421 | if (ps->sample[sample].pss > ps->pss_max) | |
422 | ps->pss_max = ps->sample[sample].pss; | |
83fdc450 AK |
423 | |
424 | catch_rename: | |
28989b63 | 425 | /* catch process rename, try to randomize time */ |
6d031c0b | 426 | mod = (arg_hz < 4.0) ? 4.0 : (arg_hz / 4.0); |
28989b63 TA |
427 | if (((samples - ps->first) + pid) % (int)(mod) == 0) { |
428 | ||
429 | /* re-fetch name */ | |
430 | /* get name, start time */ | |
431 | if (!ps->sched) { | |
f2f85884 HH |
432 | sprintf(filename, "%d/sched", pid); |
433 | ps->sched = openat(procfd, filename, O_RDONLY); | |
28989b63 TA |
434 | if (ps->sched == -1) |
435 | continue; | |
436 | } | |
ef2648c1 LN |
437 | s = pread(ps->sched, buf, sizeof(buf) - 1, 0); |
438 | if (s <= 0) { | |
28989b63 TA |
439 | /* clean up file descriptors */ |
440 | close(ps->sched); | |
441 | if (ps->schedstat) | |
442 | close(ps->schedstat); | |
443 | //if (ps->smaps) | |
444 | // fclose(ps->smaps); | |
445 | continue; | |
446 | } | |
ef2648c1 | 447 | buf[s] = '\0'; |
28989b63 TA |
448 | |
449 | if (!sscanf(buf, "%s %*s %*s", key)) | |
450 | continue; | |
451 | ||
e90f9fa4 HH |
452 | strncpy(ps->name, key, 256); |
453 | ||
454 | /* cmdline */ | |
6d031c0b | 455 | if (arg_show_cmdline) |
e90f9fa4 | 456 | pid_cmdline_strncpy(ps->name, pid, 256); |
28989b63 TA |
457 | } |
458 | } | |
83fdc450 | 459 | } |