1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright (C) 2009-2013 Intel Coproration
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>
38 #include "bootchart.h"
41 * Alloc a static 4k buffer for stdio - primarily used to increase
42 * PSS buffering from the default 1k stdin buffer to reduce
45 static char smaps_buf[4096];
49 double gettime_ns(void) {
52 clock_gettime(CLOCK_MONOTONIC, &n);
54 return (n.tv_sec + (n.tv_nsec / 1000000000.0));
57 void log_uptime(void) {
58 FILE _cleanup_fclose_ *f = NULL;
62 f = fopen("/proc/uptime", "r");
66 if (!fscanf(f, "%s %*s", str))
69 uptime = strtod(str, NULL);
71 log_start = gettime_ns();
73 /* start graph at kernel boot time */
75 graph_start = log_start;
77 graph_start = log_start - uptime;
80 static char *bufgetline(char *buf) {
86 c = strchr(buf, '\n');
92 static int pid_cmdline_strncpy(char *buffer, int pid, size_t buf_len) {
93 char filename[PATH_MAX];
94 int _cleanup_close_ fd=-1;
97 sprintf(filename, "%d/cmdline", pid);
98 fd = openat(procfd, filename, O_RDONLY);
102 n = read(fd, buffer, buf_len-1);
105 for (i = 0; i < n; i++)
106 if (buffer[i] == '\0')
113 void log_sample(int sample) {
115 static int schedstat;
131 /* all the per-process stuff goes here */
133 /* find all processes */
134 proc = opendir("/proc");
137 procfd = dirfd(proc);
144 vmstat = openat(procfd, "vmstat", O_RDONLY);
146 perror("open /proc/vmstat");
151 n = pread(vmstat, buf, sizeof(buf) - 1, 0);
160 if (sscanf(m, "%s %s", key, val) < 2)
162 if (streq(key, "pgpgin"))
163 blockstat[sample].bi = atoi(val);
164 if (streq(key, "pgpgout")) {
165 blockstat[sample].bo = atoi(val);
175 /* overall CPU utilization */
176 schedstat = openat(procfd, "schedstat", O_RDONLY);
177 if (schedstat == -1) {
178 perror("open /proc/schedstat");
183 n = pread(schedstat, buf, sizeof(buf) - 1, 0);
192 if (sscanf(m, "%s %*s %*s %*s %*s %*s %*s %s %s", key, rt, wt) < 3)
195 if (strstr(key, "cpu")) {
196 c = atoi((const char*)(key+3));
198 /* Oops, we only have room for MAXCPUS data */
200 cpustat[c].sample[sample].runtime = atoll(rt);
201 cpustat[c].sample[sample].waittime = atoll(wt);
214 e_fd = openat(procfd, "sys/kernel/random/entropy_avail", O_RDONLY);
218 n = pread(e_fd, buf, sizeof(buf) - 1, 0);
221 entropy_avail[sample] = atoi(buf);
226 while ((ent = readdir(proc)) != NULL) {
227 char filename[PATH_MAX];
229 struct ps_struct *ps;
231 if ((ent->d_name[0] < '0') || (ent->d_name[0] > '9'))
234 pid = atoi(ent->d_name);
240 while (ps->next_ps) {
246 /* end of our LL? then append a new record */
247 if (ps->pid != pid) {
248 FILE _cleanup_fclose_ *st = NULL;
250 struct ps_struct *parent;
252 ps->next_ps = calloc(1, sizeof(struct ps_struct));
254 perror("calloc(ps_struct)");
260 ps->sample = calloc(arg_samples_len + 1, sizeof(struct ps_sched_struct));
262 perror("calloc(ps_struct)");
268 /* mark our first sample */
271 /* get name, start time */
273 sprintf(filename, "%d/sched", pid);
274 ps->sched = openat(procfd, filename, O_RDONLY);
279 s = pread(ps->sched, buf, sizeof(buf) - 1, 0);
286 if (!sscanf(buf, "%s %*s %*s", key))
289 strncpy(ps->name, key, 256);
292 if (arg_show_cmdline)
293 pid_cmdline_strncpy(ps->name, pid, 256);
304 if (!sscanf(m, "%*s %*s %s", t))
307 ps->starttime = strtod(t, NULL) / 1000.0;
310 sprintf(filename, "%d/stat", pid);
311 fd = openat(procfd, filename, O_RDONLY);
312 st = fdopen(fd, "r");
315 if (!fscanf(st, "%*s %*s %*s %i", &p)) {
321 * setup child pointers
323 * these are used to paint the tree coherently later
324 * each parent has a LL of children, and a LL of siblings
327 continue; /* nothing to do for init atm */
329 /* kthreadd has ppid=0, which breaks our tree ordering */
334 while ((parent->next_ps && parent->pid != ps->ppid))
335 parent = parent->next_ps;
337 if ((!parent) || (parent->pid != ps->ppid)) {
340 parent = ps_first->next_ps;
345 if (!parent->children) {
346 /* it's the first child */
347 parent->children = ps;
349 /* walk all children and append */
350 struct ps_struct *children;
351 children = parent->children;
352 while (children->next)
353 children = children->next;
358 /* else -> found pid, append data in ps */
360 /* below here is all continuous logging parts - we get here on every
364 if (!ps->schedstat) {
365 sprintf(filename, "%d/schedstat", pid);
366 ps->schedstat = openat(procfd, filename, O_RDONLY);
367 if (ps->schedstat == -1)
370 s = pread(ps->schedstat, buf, sizeof(buf) - 1, 0);
372 /* clean up our file descriptors - assume that the process exited */
373 close(ps->schedstat);
377 // fclose(ps->smaps);
382 if (!sscanf(buf, "%s %s %*s", rt, wt))
386 ps->sample[sample].runtime = atoll(rt);
387 ps->sample[sample].waittime = atoll(wt);
389 ps->total = (ps->sample[ps->last].runtime
390 - ps->sample[ps->first].runtime)
397 sprintf(filename, "%d/smaps", pid);
398 fd = openat(procfd, filename, O_RDONLY);
399 ps->smaps = fdopen(fd, "r");
402 setvbuf(ps->smaps, smaps_buf, _IOFBF, sizeof(smaps_buf));
410 /* skip one line, this contains the object mapped */
411 if (fgets(buf, sizeof(buf), ps->smaps) == NULL)
413 /* then there's a 28 char 14 line block */
414 if (fread(buf, 1, 28 * 14, ps->smaps) != 28 * 14)
417 pss_kb = atoi(&buf[61]);
418 ps->sample[sample].pss += pss_kb;
421 if (ps->sample[sample].pss > ps->pss_max)
422 ps->pss_max = ps->sample[sample].pss;
425 /* catch process rename, try to randomize time */
426 mod = (arg_hz < 4.0) ? 4.0 : (arg_hz / 4.0);
427 if (((samples - ps->first) + pid) % (int)(mod) == 0) {
430 /* get name, start time */
432 sprintf(filename, "%d/sched", pid);
433 ps->sched = openat(procfd, filename, O_RDONLY);
437 s = pread(ps->sched, buf, sizeof(buf) - 1, 0);
439 /* clean up file descriptors */
442 close(ps->schedstat);
444 // fclose(ps->smaps);
449 if (!sscanf(buf, "%s %*s %*s", key))
452 strncpy(ps->name, key, 256);
455 if (arg_show_cmdline)
456 pid_cmdline_strncpy(ps->name, pid, 256);