]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/bootchart/store.c
utmp-wtmp: don't try to read past end of string
[thirdparty/systemd.git] / src / bootchart / store.c
CommitLineData
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 */
45static char smaps_buf[4096];
46DIR *proc;
6d031c0b 47int procfd = -1;
83fdc450 48
6d031c0b 49double 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 57void 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 80static 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 92static 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 113void 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 168vmstat_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 206schedstat_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
424catch_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}