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];
49 double gettime_ns(void) {
52 clock_gettime(CLOCK_MONOTONIC, &n);
54 return (n.tv_sec + (n.tv_nsec / (double) NSEC_PER_SEC));
57 static double gettime_up(void) {
60 clock_gettime(CLOCK_BOOTTIME, &n);
61 return (n.tv_sec + (n.tv_nsec / (double) NSEC_PER_SEC));
64 void log_uptime(void) {
66 graph_start = log_start = gettime_ns();
68 double uptime = gettime_up();
70 log_start = gettime_ns();
71 graph_start = log_start - uptime;
75 static char *bufgetline(char *buf) {
81 c = strchr(buf, '\n');
87 static int pid_cmdline_strscpy(int procfd, char *buffer, size_t buf_len, int pid) {
88 char filename[PATH_MAX];
89 _cleanup_close_ int fd=-1;
92 sprintf(filename, "%d/cmdline", pid);
93 fd = openat(procfd, filename, O_RDONLY);
97 n = read(fd, buffer, buf_len-1);
100 for (i = 0; i < n; i++)
101 if (buffer[i] == '\0')
108 int log_sample(DIR *proc, int sample, struct list_sample_data **ptr) {
109 static int vmstat = -1;
110 static int schedstat = -1;
120 static int e_fd = -1;
125 struct list_sample_data *sampledata;
126 struct ps_sched_struct *ps_prev = NULL;
131 procfd = dirfd(proc);
137 vmstat = openat(procfd, "vmstat", O_RDONLY);
139 return log_error_errno(errno, "Failed to open /proc/vmstat: %m");
142 n = pread(vmstat, buf, sizeof(buf) - 1, 0);
144 vmstat = safe_close(vmstat);
153 if (sscanf(m, "%s %s", key, val) < 2)
155 if (streq(key, "pgpgin"))
156 sampledata->blockstat.bi = atoi(val);
157 if (streq(key, "pgpgout")) {
158 sampledata->blockstat.bo = atoi(val);
168 /* overall CPU utilization */
169 schedstat = openat(procfd, "schedstat", O_RDONLY);
171 return log_error_errno(errno, "Failed to open /proc/schedstat (requires CONFIG_SCHEDSTATS=y in kernel config): %m");
174 n = pread(schedstat, buf, sizeof(buf) - 1, 0);
176 schedstat = safe_close(schedstat);
187 if (sscanf(m, "%s %*s %*s %*s %*s %*s %*s %s %s", key, rt, wt) < 3)
190 if (strstr(key, "cpu")) {
191 r = safe_atoi((const char*)(key+3), &c);
192 if (r < 0 || c > MAXCPUS -1)
193 /* Oops, we only have room for MAXCPUS data */
195 sampledata->runtime[c] = atoll(rt);
196 sampledata->waittime[c] = atoll(wt);
209 e_fd = openat(procfd, "sys/kernel/random/entropy_avail", O_RDONLY);
211 return log_error_errno(errno, "Failed to open /proc/sys/kernel/random/entropy_avail: %m");
214 n = pread(e_fd, buf, sizeof(buf) - 1, 0);
220 sampledata->entropy_avail = atoi(buf);
224 while ((ent = readdir(proc)) != NULL) {
225 char filename[PATH_MAX];
227 struct ps_struct *ps;
229 if ((ent->d_name[0] < '0') || (ent->d_name[0] > '9'))
232 pid = atoi(ent->d_name);
238 while (ps->next_ps) {
244 /* end of our LL? then append a new record */
245 if (ps->pid != pid) {
246 _cleanup_fclose_ FILE *st = NULL;
248 struct ps_struct *parent;
251 ps->next_ps = new0(struct ps_struct, 1);
260 ps->sample = new0(struct ps_sched_struct, 1);
264 ps->sample->sampledata = sampledata;
268 /* mark our first sample */
269 ps->first = ps->last = ps->sample;
270 ps->sample->runtime = atoll(rt);
271 ps->sample->waittime = atoll(wt);
273 /* get name, start time */
275 sprintf(filename, "%d/sched", pid);
276 ps->sched = openat(procfd, filename, O_RDONLY);
281 s = pread(ps->sched, buf, sizeof(buf) - 1, 0);
289 if (!sscanf(buf, "%s %*s %*s", key))
292 strscpy(ps->name, sizeof(ps->name), key);
295 if (arg_show_cmdline)
296 pid_cmdline_strscpy(procfd, ps->name, sizeof(ps->name), pid);
307 if (!sscanf(m, "%*s %*s %s", t))
310 r = safe_atod(t, &ps->starttime);
314 ps->starttime /= 1000.0;
317 /* if this fails, that's OK */
318 cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER,
319 ps->pid, &ps->cgroup);
322 sprintf(filename, "%d/stat", pid);
323 fd = openat(procfd, filename, O_RDONLY);
326 st = fdopen(fd, "r");
331 if (!fscanf(st, "%*s %*s %*s %i", &p)) {
337 * setup child pointers
339 * these are used to paint the tree coherently later
340 * each parent has a LL of children, and a LL of siblings
343 continue; /* nothing to do for init atm */
345 /* kthreadd has ppid=0, which breaks our tree ordering */
350 while ((parent->next_ps && parent->pid != ps->ppid))
351 parent = parent->next_ps;
353 if (parent->pid != ps->ppid) {
356 parent = ps_first->next_ps;
361 if (!parent->children) {
362 /* it's the first child */
363 parent->children = ps;
365 /* walk all children and append */
366 struct ps_struct *children;
367 children = parent->children;
368 while (children->next)
369 children = children->next;
374 /* else -> found pid, append data in ps */
376 /* below here is all continuous logging parts - we get here on every
380 if (ps->schedstat < 0) {
381 sprintf(filename, "%d/schedstat", pid);
382 ps->schedstat = openat(procfd, filename, O_RDONLY);
383 if (ps->schedstat == -1)
386 s = pread(ps->schedstat, buf, sizeof(buf) - 1, 0);
388 /* clean up our file descriptors - assume that the process exited */
389 close(ps->schedstat);
396 // fclose(ps->smaps);
401 if (!sscanf(buf, "%s %s %*s", rt, wt))
404 ps->sample->next = new0(struct ps_sched_struct, 1);
405 if (!ps->sample->next)
408 ps->sample->next->prev = ps->sample;
409 ps->sample = ps->sample->next;
410 ps->last = ps->sample;
411 ps->sample->runtime = atoll(rt);
412 ps->sample->waittime = atoll(wt);
413 ps->sample->sampledata = sampledata;
414 ps->sample->ps_new = ps;
416 ps_prev->cross = ps->sample;
418 ps_prev = ps->sample;
419 ps->total = (ps->last->runtime - ps->first->runtime)
427 sprintf(filename, "%d/smaps", pid);
428 fd = openat(procfd, filename, O_RDONLY);
431 ps->smaps = fdopen(fd, "r");
436 setvbuf(ps->smaps, smaps_buf, _IOFBF, sizeof(smaps_buf));
441 /* test to see if we need to skip another field */
443 if (fgets(buf, sizeof(buf), ps->smaps) == NULL) {
446 if (fread(buf, 1, 28 * 15, ps->smaps) != (28 * 15)) {
449 if (buf[392] == 'V') {
460 /* skip one line, this contains the object mapped. */
461 if (fgets(buf, sizeof(buf), ps->smaps) == NULL) {
464 /* then there's a 28 char 14 line block */
465 if (fread(buf, 1, 28 * 14, ps->smaps) != 28 * 14) {
468 pss_kb = atoi(&buf[61]);
469 ps->sample->pss += pss_kb;
471 /* skip one more line if this is a newer kernel */
473 if (fgets(buf, sizeof(buf), ps->smaps) == NULL)
477 if (ps->sample->pss > ps->pss_max)
478 ps->pss_max = ps->sample->pss;
481 /* catch process rename, try to randomize time */
482 mod = (arg_hz < 4.0) ? 4.0 : (arg_hz / 4.0);
483 if (((samples - ps->pid) + pid) % (int)(mod) == 0) {
486 /* get name, start time */
488 sprintf(filename, "%d/sched", pid);
489 ps->sched = openat(procfd, filename, O_RDONLY);
493 s = pread(ps->sched, buf, sizeof(buf) - 1, 0);
495 /* clean up file descriptors */
499 close(ps->schedstat);
503 // fclose(ps->smaps);
508 if (!sscanf(buf, "%s %*s %*s", key))
511 strscpy(ps->name, sizeof(ps->name), key);
514 if (arg_show_cmdline)
515 pid_cmdline_strscpy(procfd, ps->name, sizeof(ps->name), pid);