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>
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];
50 double gettime_ns(void) {
53 clock_gettime(CLOCK_MONOTONIC, &n);
55 return (n.tv_sec + (n.tv_nsec / 1000000000.0));
58 void log_uptime(void) {
59 _cleanup_fclose_ FILE *f = NULL;
63 f = fopen("/proc/uptime", "r");
67 if (!fscanf(f, "%s %*s", str))
70 uptime = strtod(str, NULL);
72 log_start = gettime_ns();
74 /* start graph at kernel boot time */
76 graph_start = log_start;
78 graph_start = log_start - uptime;
81 static char *bufgetline(char *buf) {
87 c = strchr(buf, '\n');
93 static int pid_cmdline_strscpy(char *buffer, size_t buf_len, int pid) {
94 char filename[PATH_MAX];
95 _cleanup_close_ int fd=-1;
98 sprintf(filename, "%d/cmdline", pid);
99 fd = openat(procfd, filename, O_RDONLY);
103 n = read(fd, buffer, buf_len-1);
106 for (i = 0; i < n; i++)
107 if (buffer[i] == '\0')
114 void log_sample(int sample) {
116 static int schedstat;
132 /* all the per-process stuff goes here */
134 /* find all processes */
135 proc = opendir("/proc");
138 procfd = dirfd(proc);
145 vmstat = openat(procfd, "vmstat", O_RDONLY);
147 perror("open /proc/vmstat");
152 n = pread(vmstat, buf, sizeof(buf) - 1, 0);
161 if (sscanf(m, "%s %s", key, val) < 2)
163 if (streq(key, "pgpgin"))
164 blockstat[sample].bi = atoi(val);
165 if (streq(key, "pgpgout")) {
166 blockstat[sample].bo = atoi(val);
176 /* overall CPU utilization */
177 schedstat = openat(procfd, "schedstat", O_RDONLY);
178 if (schedstat == -1) {
179 perror("open /proc/schedstat");
184 n = pread(schedstat, buf, sizeof(buf) - 1, 0);
193 if (sscanf(m, "%s %*s %*s %*s %*s %*s %*s %s %s", key, rt, wt) < 3)
196 if (strstr(key, "cpu")) {
197 c = atoi((const char*)(key+3));
199 /* Oops, we only have room for MAXCPUS data */
201 cpustat[c].sample[sample].runtime = atoll(rt);
202 cpustat[c].sample[sample].waittime = atoll(wt);
215 e_fd = openat(procfd, "sys/kernel/random/entropy_avail", O_RDONLY);
219 n = pread(e_fd, buf, sizeof(buf) - 1, 0);
222 entropy_avail[sample] = atoi(buf);
227 while ((ent = readdir(proc)) != NULL) {
228 char filename[PATH_MAX];
230 struct ps_struct *ps;
232 if ((ent->d_name[0] < '0') || (ent->d_name[0] > '9'))
235 pid = atoi(ent->d_name);
241 while (ps->next_ps) {
247 /* end of our LL? then append a new record */
248 if (ps->pid != pid) {
249 _cleanup_fclose_ FILE *st = NULL;
251 struct ps_struct *parent;
253 ps->next_ps = calloc(1, sizeof(struct ps_struct));
255 perror("calloc(ps_struct)");
261 ps->sample = calloc(arg_samples_len + 1, sizeof(struct ps_sched_struct));
263 perror("calloc(ps_struct)");
269 /* mark our first sample */
272 /* get name, start time */
274 sprintf(filename, "%d/sched", pid);
275 ps->sched = openat(procfd, filename, O_RDONLY);
280 s = pread(ps->sched, buf, sizeof(buf) - 1, 0);
287 if (!sscanf(buf, "%s %*s %*s", key))
290 strscpy(ps->name, sizeof(ps->name), key);
293 if (arg_show_cmdline)
294 pid_cmdline_strscpy(ps->name, sizeof(ps->name), pid);
305 if (!sscanf(m, "%*s %*s %s", t))
308 ps->starttime = strtod(t, NULL) / 1000.0;
311 sprintf(filename, "%d/stat", pid);
312 fd = openat(procfd, filename, O_RDONLY);
313 st = fdopen(fd, "r");
316 if (!fscanf(st, "%*s %*s %*s %i", &p)) {
322 * setup child pointers
324 * these are used to paint the tree coherently later
325 * each parent has a LL of children, and a LL of siblings
328 continue; /* nothing to do for init atm */
330 /* kthreadd has ppid=0, which breaks our tree ordering */
335 while ((parent->next_ps && parent->pid != ps->ppid))
336 parent = parent->next_ps;
338 if ((!parent) || (parent->pid != ps->ppid)) {
341 parent = ps_first->next_ps;
346 if (!parent->children) {
347 /* it's the first child */
348 parent->children = ps;
350 /* walk all children and append */
351 struct ps_struct *children;
352 children = parent->children;
353 while (children->next)
354 children = children->next;
359 /* else -> found pid, append data in ps */
361 /* below here is all continuous logging parts - we get here on every
365 if (!ps->schedstat) {
366 sprintf(filename, "%d/schedstat", pid);
367 ps->schedstat = openat(procfd, filename, O_RDONLY);
368 if (ps->schedstat == -1)
371 s = pread(ps->schedstat, buf, sizeof(buf) - 1, 0);
373 /* clean up our file descriptors - assume that the process exited */
374 close(ps->schedstat);
378 // fclose(ps->smaps);
383 if (!sscanf(buf, "%s %s %*s", rt, wt))
387 ps->sample[sample].runtime = atoll(rt);
388 ps->sample[sample].waittime = atoll(wt);
390 ps->total = (ps->sample[ps->last].runtime
391 - ps->sample[ps->first].runtime)
398 sprintf(filename, "%d/smaps", pid);
399 fd = openat(procfd, filename, O_RDONLY);
400 ps->smaps = fdopen(fd, "r");
403 setvbuf(ps->smaps, smaps_buf, _IOFBF, sizeof(smaps_buf));
411 /* skip one line, this contains the object mapped */
412 if (fgets(buf, sizeof(buf), ps->smaps) == NULL)
414 /* then there's a 28 char 14 line block */
415 if (fread(buf, 1, 28 * 14, ps->smaps) != 28 * 14)
418 pss_kb = atoi(&buf[61]);
419 ps->sample[sample].pss += pss_kb;
422 if (ps->sample[sample].pss > ps->pss_max)
423 ps->pss_max = ps->sample[sample].pss;
426 /* catch process rename, try to randomize time */
427 mod = (arg_hz < 4.0) ? 4.0 : (arg_hz / 4.0);
428 if (((samples - ps->first) + pid) % (int)(mod) == 0) {
431 /* get name, start time */
433 sprintf(filename, "%d/sched", pid);
434 ps->sched = openat(procfd, filename, O_RDONLY);
438 s = pread(ps->sched, buf, sizeof(buf) - 1, 0);
440 /* clean up file descriptors */
443 close(ps->schedstat);
445 // fclose(ps->smaps);
450 if (!sscanf(buf, "%s %*s %*s", key))
453 strscpy(ps->name, sizeof(ps->name), key);
456 if (arg_show_cmdline)
457 pid_cmdline_strscpy(ps->name, sizeof(ps->name), pid);