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/>.
35 #include "time-util.h"
38 #include "bootchart.h"
39 #include "cgroup-util.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 / (double) NSEC_PER_SEC));
59 static double gettime_up(void) {
62 clock_gettime(CLOCK_BOOTTIME, &n);
63 return (n.tv_sec + (n.tv_nsec / (double) NSEC_PER_SEC));
66 void log_uptime(void) {
68 graph_start = log_start = gettime_ns();
70 double uptime = gettime_up();
72 log_start = gettime_ns();
73 graph_start = log_start - uptime;
77 static char *bufgetline(char *buf) {
83 c = strchr(buf, '\n');
89 static int pid_cmdline_strscpy(char *buffer, size_t buf_len, int pid) {
90 char filename[PATH_MAX];
91 _cleanup_close_ int fd=-1;
94 sprintf(filename, "%d/cmdline", pid);
95 fd = openat(procfd, filename, O_RDONLY);
99 n = read(fd, buffer, buf_len-1);
102 for (i = 0; i < n; i++)
103 if (buffer[i] == '\0')
110 void log_sample(int sample, struct list_sample_data **ptr) {
111 static int vmstat = -1;
112 static int schedstat = -1;
122 static int e_fd = -1;
127 struct list_sample_data *sampledata;
128 struct ps_sched_struct *ps_prev = NULL;
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 return log_error_errno(errno, "Failed to open /proc/vmstat: %m");
150 n = pread(vmstat, buf, sizeof(buf) - 1, 0);
160 if (sscanf(m, "%s %s", key, val) < 2)
162 if (streq(key, "pgpgin"))
163 sampledata->blockstat.bi = atoi(val);
164 if (streq(key, "pgpgout")) {
165 sampledata->blockstat.bo = atoi(val);
175 /* overall CPU utilization */
176 schedstat = openat(procfd, "schedstat", O_RDONLY);
177 if (schedstat == -1) {
178 log_error_errno(errno, "Failed to open /proc/schedstat (requires CONFIG_SCHEDSTATS=y in kernel config): %m");
183 n = pread(schedstat, buf, sizeof(buf) - 1, 0);
195 if (sscanf(m, "%s %*s %*s %*s %*s %*s %*s %s %s", key, rt, wt) < 3)
198 if (strstr(key, "cpu")) {
199 r = safe_atoi((const char*)(key+3), &c);
200 if (r < 0 || c > MAXCPUS -1)
201 /* Oops, we only have room for MAXCPUS data */
203 sampledata->runtime[c] = atoll(rt);
204 sampledata->waittime[c] = atoll(wt);
217 e_fd = openat(procfd, "sys/kernel/random/entropy_avail", O_RDONLY);
219 log_error_errno(errno, "Failed to open /proc/sys/kernel/random/entropy_avail: %m");
224 n = pread(e_fd, buf, sizeof(buf) - 1, 0);
230 sampledata->entropy_avail = atoi(buf);
234 while ((ent = readdir(proc)) != NULL) {
235 char filename[PATH_MAX];
237 struct ps_struct *ps;
239 if ((ent->d_name[0] < '0') || (ent->d_name[0] > '9'))
242 pid = atoi(ent->d_name);
248 while (ps->next_ps) {
254 /* end of our LL? then append a new record */
255 if (ps->pid != pid) {
256 _cleanup_fclose_ FILE *st = NULL;
258 struct ps_struct *parent;
261 ps->next_ps = new0(struct ps_struct, 1);
271 ps->sample = new0(struct ps_sched_struct, 1);
276 ps->sample->sampledata = sampledata;
280 /* mark our first sample */
281 ps->first = ps->last = ps->sample;
282 ps->sample->runtime = atoll(rt);
283 ps->sample->waittime = atoll(wt);
285 /* get name, start time */
287 sprintf(filename, "%d/sched", pid);
288 ps->sched = openat(procfd, filename, O_RDONLY);
293 s = pread(ps->sched, buf, sizeof(buf) - 1, 0);
301 if (!sscanf(buf, "%s %*s %*s", key))
304 strscpy(ps->name, sizeof(ps->name), key);
307 if (arg_show_cmdline)
308 pid_cmdline_strscpy(ps->name, sizeof(ps->name), pid);
319 if (!sscanf(m, "%*s %*s %s", t))
322 r = safe_atod(t, &ps->starttime);
326 ps->starttime /= 1000.0;
329 /* if this fails, that's OK */
330 cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER,
331 ps->pid, &ps->cgroup);
334 sprintf(filename, "%d/stat", pid);
335 fd = openat(procfd, filename, O_RDONLY);
338 st = fdopen(fd, "r");
343 if (!fscanf(st, "%*s %*s %*s %i", &p)) {
349 * setup child pointers
351 * these are used to paint the tree coherently later
352 * each parent has a LL of children, and a LL of siblings
355 continue; /* nothing to do for init atm */
357 /* kthreadd has ppid=0, which breaks our tree ordering */
362 while ((parent->next_ps && parent->pid != ps->ppid))
363 parent = parent->next_ps;
365 if (parent->pid != ps->ppid) {
368 parent = ps_first->next_ps;
373 if (!parent->children) {
374 /* it's the first child */
375 parent->children = ps;
377 /* walk all children and append */
378 struct ps_struct *children;
379 children = parent->children;
380 while (children->next)
381 children = children->next;
386 /* else -> found pid, append data in ps */
388 /* below here is all continuous logging parts - we get here on every
392 if (ps->schedstat < 0) {
393 sprintf(filename, "%d/schedstat", pid);
394 ps->schedstat = openat(procfd, filename, O_RDONLY);
395 if (ps->schedstat == -1)
398 s = pread(ps->schedstat, buf, sizeof(buf) - 1, 0);
400 /* clean up our file descriptors - assume that the process exited */
401 close(ps->schedstat);
408 // fclose(ps->smaps);
413 if (!sscanf(buf, "%s %s %*s", rt, wt))
416 ps->sample->next = new0(struct ps_sched_struct, 1);
417 if (!ps->sample->next) {
421 ps->sample->next->prev = ps->sample;
422 ps->sample = ps->sample->next;
423 ps->last = ps->sample;
424 ps->sample->runtime = atoll(rt);
425 ps->sample->waittime = atoll(wt);
426 ps->sample->sampledata = sampledata;
427 ps->sample->ps_new = ps;
429 ps_prev->cross = ps->sample;
431 ps_prev = ps->sample;
432 ps->total = (ps->last->runtime - ps->first->runtime)
440 sprintf(filename, "%d/smaps", pid);
441 fd = openat(procfd, filename, O_RDONLY);
444 ps->smaps = fdopen(fd, "r");
449 setvbuf(ps->smaps, smaps_buf, _IOFBF, sizeof(smaps_buf));
454 /* test to see if we need to skip another field */
456 if (fgets(buf, sizeof(buf), ps->smaps) == NULL) {
459 if (fread(buf, 1, 28 * 15, ps->smaps) != (28 * 15)) {
462 if (buf[392] == 'V') {
473 /* skip one line, this contains the object mapped. */
474 if (fgets(buf, sizeof(buf), ps->smaps) == NULL) {
477 /* then there's a 28 char 14 line block */
478 if (fread(buf, 1, 28 * 14, ps->smaps) != 28 * 14) {
481 pss_kb = atoi(&buf[61]);
482 ps->sample->pss += pss_kb;
484 /* skip one more line if this is a newer kernel */
486 if (fgets(buf, sizeof(buf), ps->smaps) == NULL)
490 if (ps->sample->pss > ps->pss_max)
491 ps->pss_max = ps->sample->pss;
494 /* catch process rename, try to randomize time */
495 mod = (arg_hz < 4.0) ? 4.0 : (arg_hz / 4.0);
496 if (((samples - ps->pid) + pid) % (int)(mod) == 0) {
499 /* get name, start time */
501 sprintf(filename, "%d/sched", pid);
502 ps->sched = openat(procfd, filename, O_RDONLY);
506 s = pread(ps->sched, buf, sizeof(buf) - 1, 0);
508 /* clean up file descriptors */
512 close(ps->schedstat);
516 // fclose(ps->smaps);
521 if (!sscanf(buf, "%s %*s %*s", key))
524 strscpy(ps->name, sizeof(ps->name), key);
527 if (arg_show_cmdline)
528 pid_cmdline_strscpy(ps->name, sizeof(ps->name), pid);