1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright (C) 2009-2013 Intel Corporation
9 Auke Kok <auke-jan.h.kok@intel.com>
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.
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.
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/>.
28 #include <sys/types.h>
39 #include "bootchart.h"
42 * Alloc a static 4k buffer for stdio - primarily used to increase
43 * PSS buffering from the default 1k stdin buffer to reduce
46 static char smaps_buf[4096];
51 double gettime_ns(void) {
54 clock_gettime(CLOCK_MONOTONIC, &n);
56 return (n.tv_sec + (n.tv_nsec / 1000000000.0));
59 void log_uptime(void) {
60 _cleanup_fclose_ FILE *f = NULL;
64 f = fopen("/proc/uptime", "re");
68 if (!fscanf(f, "%s %*s", str))
71 uptime = strtod(str, NULL);
73 log_start = gettime_ns();
75 /* start graph at kernel boot time */
77 graph_start = log_start;
79 graph_start = log_start - uptime;
82 static char *bufgetline(char *buf) {
88 c = strchr(buf, '\n');
94 static int pid_cmdline_strscpy(char *buffer, size_t buf_len, int pid) {
95 char filename[PATH_MAX];
96 _cleanup_close_ int fd=-1;
99 sprintf(filename, "%d/cmdline", pid);
100 fd = openat(procfd, filename, O_RDONLY);
104 n = read(fd, buffer, buf_len-1);
107 for (i = 0; i < n; i++)
108 if (buffer[i] == '\0')
115 void log_sample(int sample, struct list_sample_data **ptr) {
117 static int schedstat;
132 struct list_sample_data *sampledata;
133 struct ps_sched_struct *ps_prev = NULL;
137 /* all the per-process stuff goes here */
139 /* find all processes */
140 proc = opendir("/proc");
143 procfd = dirfd(proc);
150 vmstat = openat(procfd, "vmstat", O_RDONLY);
152 log_error("Failed to open /proc/vmstat: %m");
157 n = pread(vmstat, buf, sizeof(buf) - 1, 0);
166 if (sscanf(m, "%s %s", key, val) < 2)
168 if (streq(key, "pgpgin"))
169 sampledata->blockstat.bi = atoi(val);
170 if (streq(key, "pgpgout")) {
171 sampledata->blockstat.bo = atoi(val);
181 /* overall CPU utilization */
182 schedstat = openat(procfd, "schedstat", O_RDONLY);
183 if (schedstat == -1) {
184 log_error("Failed to open /proc/schedstat: %m");
189 n = pread(schedstat, buf, sizeof(buf) - 1, 0);
198 if (sscanf(m, "%s %*s %*s %*s %*s %*s %*s %s %s", key, rt, wt) < 3)
201 if (strstr(key, "cpu")) {
202 c = atoi((const char*)(key+3));
204 /* Oops, we only have room for MAXCPUS data */
206 sampledata->runtime[c] = atoll(rt);
207 sampledata->waittime[c] = atoll(wt);
220 e_fd = openat(procfd, "sys/kernel/random/entropy_avail", O_RDONLY);
224 n = pread(e_fd, buf, sizeof(buf) - 1, 0);
227 sampledata->entropy_avail = atoi(buf);
232 while ((ent = readdir(proc)) != NULL) {
233 char filename[PATH_MAX];
235 struct ps_struct *ps;
237 if ((ent->d_name[0] < '0') || (ent->d_name[0] > '9'))
240 pid = atoi(ent->d_name);
246 while (ps->next_ps) {
252 /* end of our LL? then append a new record */
253 if (ps->pid != pid) {
254 _cleanup_fclose_ FILE *st = NULL;
256 struct ps_struct *parent;
258 ps->next_ps = new0(struct ps_struct, 1);
266 ps->sample = new0(struct ps_sched_struct, 1);
271 ps->sample->sampledata = sampledata;
275 /* mark our first sample */
276 ps->first = ps->last = ps->sample;
277 ps->sample->runtime = atoll(rt);
278 ps->sample->waittime = atoll(wt);
280 /* get name, start time */
282 sprintf(filename, "%d/sched", pid);
283 ps->sched = openat(procfd, filename, O_RDONLY);
288 s = pread(ps->sched, buf, sizeof(buf) - 1, 0);
295 if (!sscanf(buf, "%s %*s %*s", key))
298 strscpy(ps->name, sizeof(ps->name), key);
301 if (arg_show_cmdline)
302 pid_cmdline_strscpy(ps->name, sizeof(ps->name), pid);
313 if (!sscanf(m, "%*s %*s %s", t))
316 ps->starttime = strtod(t, NULL) / 1000.0;
319 sprintf(filename, "%d/stat", pid);
320 fd = openat(procfd, filename, O_RDONLY);
321 st = fdopen(fd, "r");
324 if (!fscanf(st, "%*s %*s %*s %i", &p)) {
330 * setup child pointers
332 * these are used to paint the tree coherently later
333 * each parent has a LL of children, and a LL of siblings
336 continue; /* nothing to do for init atm */
338 /* kthreadd has ppid=0, which breaks our tree ordering */
343 while ((parent->next_ps && parent->pid != ps->ppid))
344 parent = parent->next_ps;
346 if (parent->pid != ps->ppid) {
349 parent = ps_first->next_ps;
354 if (!parent->children) {
355 /* it's the first child */
356 parent->children = ps;
358 /* walk all children and append */
359 struct ps_struct *children;
360 children = parent->children;
361 while (children->next)
362 children = children->next;
367 /* else -> found pid, append data in ps */
369 /* below here is all continuous logging parts - we get here on every
373 if (!ps->schedstat) {
374 sprintf(filename, "%d/schedstat", pid);
375 ps->schedstat = openat(procfd, filename, O_RDONLY);
376 if (ps->schedstat == -1)
379 s = pread(ps->schedstat, buf, sizeof(buf) - 1, 0);
381 /* clean up our file descriptors - assume that the process exited */
382 close(ps->schedstat);
386 // fclose(ps->smaps);
391 if (!sscanf(buf, "%s %s %*s", rt, wt))
394 ps->sample->next = new0(struct ps_sched_struct, 1);
399 ps->sample->next->prev = ps->sample;
400 ps->sample = ps->sample->next;
401 ps->last = ps->sample;
402 ps->sample->runtime = atoll(rt);
403 ps->sample->waittime = atoll(wt);
404 ps->sample->sampledata = sampledata;
405 ps->sample->ps_new = ps;
407 ps_prev->cross = ps->sample;
409 ps_prev = ps->sample;
410 ps->total = (ps->last->runtime - ps->first->runtime)
418 sprintf(filename, "%d/smaps", pid);
419 fd = openat(procfd, filename, O_RDONLY);
420 ps->smaps = fdopen(fd, "r");
423 setvbuf(ps->smaps, smaps_buf, _IOFBF, sizeof(smaps_buf));
428 /* test to see if we need to skip another field */
430 if (fgets(buf, sizeof(buf), ps->smaps) == NULL) {
433 if (fread(buf, 1, 28 * 15, ps->smaps) != (28 * 15)) {
436 if (buf[392] == 'V') {
447 /* skip one line, this contains the object mapped. */
448 if (fgets(buf, sizeof(buf), ps->smaps) == NULL) {
451 /* then there's a 28 char 14 line block */
452 if (fread(buf, 1, 28 * 14, ps->smaps) != 28 * 14) {
455 pss_kb = atoi(&buf[61]);
456 ps->sample->pss += pss_kb;
458 /* skip one more line if this is a newer kernel */
460 if (fgets(buf, sizeof(buf), ps->smaps) == NULL)
464 if (ps->sample->pss > ps->pss_max)
465 ps->pss_max = ps->sample->pss;
468 /* catch process rename, try to randomize time */
469 mod = (arg_hz < 4.0) ? 4.0 : (arg_hz / 4.0);
470 if (((samples - ps->pid) + pid) % (int)(mod) == 0) {
473 /* get name, start time */
475 sprintf(filename, "%d/sched", pid);
476 ps->sched = openat(procfd, filename, O_RDONLY);
480 s = pread(ps->sched, buf, sizeof(buf) - 1, 0);
482 /* clean up file descriptors */
485 close(ps->schedstat);
487 // fclose(ps->smaps);
492 if (!sscanf(buf, "%s %*s %*s", key))
495 strscpy(ps->name, sizeof(ps->name), key);
498 if (arg_show_cmdline)
499 pid_cmdline_strscpy(ps->name, sizeof(ps->name), pid);