]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/bootchart/store.c
bootchart: properly terminate string
[thirdparty/systemd.git] / src / bootchart / store.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright (C) 2009-2013 Intel Coproration
7
8 Authors:
9 Auke Kok <auke-jan.h.kok@intel.com>
10
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.
15
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.
20
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/>.
23 ***/
24
25 #include <unistd.h>
26 #include <stdlib.h>
27 #include <limits.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <dirent.h>
33 #include <fcntl.h>
34 #include <time.h>
35
36 #include "util.h"
37 #include "strxcpyx.h"
38 #include "store.h"
39 #include "bootchart.h"
40
41 /*
42 * Alloc a static 4k buffer for stdio - primarily used to increase
43 * PSS buffering from the default 1k stdin buffer to reduce
44 * read() overhead.
45 */
46 static char smaps_buf[4096];
47 DIR *proc;
48 int procfd = -1;
49
50 double gettime_ns(void) {
51 struct timespec n;
52
53 clock_gettime(CLOCK_MONOTONIC, &n);
54
55 return (n.tv_sec + (n.tv_nsec / 1000000000.0));
56 }
57
58 void log_uptime(void) {
59 FILE _cleanup_fclose_ *f = NULL;
60 char str[32];
61 double uptime;
62
63 f = fopen("/proc/uptime", "r");
64
65 if (!f)
66 return;
67 if (!fscanf(f, "%s %*s", str))
68 return;
69
70 uptime = strtod(str, NULL);
71
72 log_start = gettime_ns();
73
74 /* start graph at kernel boot time */
75 if (arg_relative)
76 graph_start = log_start;
77 else
78 graph_start = log_start - uptime;
79 }
80
81 static char *bufgetline(char *buf) {
82 char *c;
83
84 if (!buf)
85 return NULL;
86
87 c = strchr(buf, '\n');
88 if (c)
89 c++;
90 return c;
91 }
92
93 static int pid_cmdline_strscpy(char *buffer, size_t buf_len, int pid) {
94 char filename[PATH_MAX];
95 int _cleanup_close_ fd=-1;
96 ssize_t n;
97
98 sprintf(filename, "%d/cmdline", pid);
99 fd = openat(procfd, filename, O_RDONLY);
100 if (fd < 0)
101 return -errno;
102
103 n = read(fd, buffer, buf_len-1);
104 if (n > 0) {
105 int i;
106 for (i = 0; i < n; i++)
107 if (buffer[i] == '\0')
108 buffer[i] = ' ';
109 buffer[n] = '\0';
110 }
111 return 0;
112 }
113
114 void log_sample(int sample) {
115 static int vmstat;
116 static int schedstat;
117 char buf[4095];
118 char key[256];
119 char val[256];
120 char rt[256];
121 char wt[256];
122 char *m;
123 int c;
124 int p;
125 int mod;
126 static int e_fd;
127 ssize_t s;
128 ssize_t n;
129 struct dirent *ent;
130 int fd;
131
132 /* all the per-process stuff goes here */
133 if (!proc) {
134 /* find all processes */
135 proc = opendir("/proc");
136 if (!proc)
137 return;
138 procfd = dirfd(proc);
139 } else {
140 rewinddir(proc);
141 }
142
143 if (!vmstat) {
144 /* block stuff */
145 vmstat = openat(procfd, "vmstat", O_RDONLY);
146 if (vmstat == -1) {
147 perror("open /proc/vmstat");
148 exit (EXIT_FAILURE);
149 }
150 }
151
152 n = pread(vmstat, buf, sizeof(buf) - 1, 0);
153 if (n <= 0) {
154 close(vmstat);
155 return;
156 }
157 buf[n] = '\0';
158
159 m = buf;
160 while (m) {
161 if (sscanf(m, "%s %s", key, val) < 2)
162 goto vmstat_next;
163 if (streq(key, "pgpgin"))
164 blockstat[sample].bi = atoi(val);
165 if (streq(key, "pgpgout")) {
166 blockstat[sample].bo = atoi(val);
167 break;
168 }
169 vmstat_next:
170 m = bufgetline(m);
171 if (!m)
172 break;
173 }
174
175 if (!schedstat) {
176 /* overall CPU utilization */
177 schedstat = openat(procfd, "schedstat", O_RDONLY);
178 if (schedstat == -1) {
179 perror("open /proc/schedstat");
180 exit (EXIT_FAILURE);
181 }
182 }
183
184 n = pread(schedstat, buf, sizeof(buf) - 1, 0);
185 if (n <= 0) {
186 close(schedstat);
187 return;
188 }
189 buf[n] = '\0';
190
191 m = buf;
192 while (m) {
193 if (sscanf(m, "%s %*s %*s %*s %*s %*s %*s %s %s", key, rt, wt) < 3)
194 goto schedstat_next;
195
196 if (strstr(key, "cpu")) {
197 c = atoi((const char*)(key+3));
198 if (c > MAXCPUS)
199 /* Oops, we only have room for MAXCPUS data */
200 break;
201 cpustat[c].sample[sample].runtime = atoll(rt);
202 cpustat[c].sample[sample].waittime = atoll(wt);
203
204 if (c == cpus)
205 cpus = c + 1;
206 }
207 schedstat_next:
208 m = bufgetline(m);
209 if (!m)
210 break;
211 }
212
213 if (arg_entropy) {
214 if (!e_fd) {
215 e_fd = openat(procfd, "sys/kernel/random/entropy_avail", O_RDONLY);
216 }
217
218 if (e_fd) {
219 n = pread(e_fd, buf, sizeof(buf) - 1, 0);
220 if (n > 0) {
221 buf[n] = '\0';
222 entropy_avail[sample] = atoi(buf);
223 }
224 }
225 }
226
227 while ((ent = readdir(proc)) != NULL) {
228 char filename[PATH_MAX];
229 int pid;
230 struct ps_struct *ps;
231
232 if ((ent->d_name[0] < '0') || (ent->d_name[0] > '9'))
233 continue;
234
235 pid = atoi(ent->d_name);
236
237 if (pid >= MAXPIDS)
238 continue;
239
240 ps = ps_first;
241 while (ps->next_ps) {
242 ps = ps->next_ps;
243 if (ps->pid == pid)
244 break;
245 }
246
247 /* end of our LL? then append a new record */
248 if (ps->pid != pid) {
249 FILE _cleanup_fclose_ *st = NULL;
250 char t[32];
251 struct ps_struct *parent;
252
253 ps->next_ps = calloc(1, sizeof(struct ps_struct));
254 if (!ps->next_ps) {
255 perror("calloc(ps_struct)");
256 exit (EXIT_FAILURE);
257 }
258 ps = ps->next_ps;
259 ps->pid = pid;
260
261 ps->sample = calloc(arg_samples_len + 1, sizeof(struct ps_sched_struct));
262 if (!ps->sample) {
263 perror("calloc(ps_struct)");
264 exit (EXIT_FAILURE);
265 }
266
267 pscount++;
268
269 /* mark our first sample */
270 ps->first = sample;
271
272 /* get name, start time */
273 if (!ps->sched) {
274 sprintf(filename, "%d/sched", pid);
275 ps->sched = openat(procfd, filename, O_RDONLY);
276 if (ps->sched == -1)
277 continue;
278 }
279
280 s = pread(ps->sched, buf, sizeof(buf) - 1, 0);
281 if (s <= 0) {
282 close(ps->sched);
283 continue;
284 }
285 buf[s] = '\0';
286
287 if (!sscanf(buf, "%s %*s %*s", key))
288 continue;
289
290 strscpy(ps->name, sizeof(ps->name), key);
291
292 /* cmdline */
293 if (arg_show_cmdline)
294 pid_cmdline_strscpy(ps->name, sizeof(ps->name), pid);
295
296 /* discard line 2 */
297 m = bufgetline(buf);
298 if (!m)
299 continue;
300
301 m = bufgetline(m);
302 if (!m)
303 continue;
304
305 if (!sscanf(m, "%*s %*s %s", t))
306 continue;
307
308 ps->starttime = strtod(t, NULL) / 1000.0;
309
310 /* ppid */
311 sprintf(filename, "%d/stat", pid);
312 fd = openat(procfd, filename, O_RDONLY);
313 st = fdopen(fd, "r");
314 if (!st)
315 continue;
316 if (!fscanf(st, "%*s %*s %*s %i", &p)) {
317 continue;
318 }
319 ps->ppid = p;
320
321 /*
322 * setup child pointers
323 *
324 * these are used to paint the tree coherently later
325 * each parent has a LL of children, and a LL of siblings
326 */
327 if (pid == 1)
328 continue; /* nothing to do for init atm */
329
330 /* kthreadd has ppid=0, which breaks our tree ordering */
331 if (ps->ppid == 0)
332 ps->ppid = 1;
333
334 parent = ps_first;
335 while ((parent->next_ps && parent->pid != ps->ppid))
336 parent = parent->next_ps;
337
338 if ((!parent) || (parent->pid != ps->ppid)) {
339 /* orphan */
340 ps->ppid = 1;
341 parent = ps_first->next_ps;
342 }
343
344 ps->parent = parent;
345
346 if (!parent->children) {
347 /* it's the first child */
348 parent->children = ps;
349 } else {
350 /* walk all children and append */
351 struct ps_struct *children;
352 children = parent->children;
353 while (children->next)
354 children = children->next;
355 children->next = ps;
356 }
357 }
358
359 /* else -> found pid, append data in ps */
360
361 /* below here is all continuous logging parts - we get here on every
362 * iteration */
363
364 /* rt, wt */
365 if (!ps->schedstat) {
366 sprintf(filename, "%d/schedstat", pid);
367 ps->schedstat = openat(procfd, filename, O_RDONLY);
368 if (ps->schedstat == -1)
369 continue;
370 }
371 s = pread(ps->schedstat, buf, sizeof(buf) - 1, 0);
372 if (s <= 0) {
373 /* clean up our file descriptors - assume that the process exited */
374 close(ps->schedstat);
375 if (ps->sched)
376 close(ps->sched);
377 //if (ps->smaps)
378 // fclose(ps->smaps);
379 continue;
380 }
381 buf[s] = '\0';
382
383 if (!sscanf(buf, "%s %s %*s", rt, wt))
384 continue;
385
386 ps->last = sample;
387 ps->sample[sample].runtime = atoll(rt);
388 ps->sample[sample].waittime = atoll(wt);
389
390 ps->total = (ps->sample[ps->last].runtime
391 - ps->sample[ps->first].runtime)
392 / 1000000000.0;
393
394 if (!arg_pss)
395 goto catch_rename;
396 /* Pss */
397 if (!ps->smaps) {
398 sprintf(filename, "%d/smaps", pid);
399 fd = openat(procfd, filename, O_RDONLY);
400 ps->smaps = fdopen(fd, "r");
401 if (!ps->smaps)
402 continue;
403 setvbuf(ps->smaps, smaps_buf, _IOFBF, sizeof(smaps_buf));
404 } else {
405 rewind(ps->smaps);
406 }
407
408 while (1) {
409 int pss_kb;
410
411 /* skip one line, this contains the object mapped */
412 if (fgets(buf, sizeof(buf), ps->smaps) == NULL)
413 break;
414 /* then there's a 28 char 14 line block */
415 if (fread(buf, 1, 28 * 14, ps->smaps) != 28 * 14)
416 break;
417
418 pss_kb = atoi(&buf[61]);
419 ps->sample[sample].pss += pss_kb;
420 }
421
422 if (ps->sample[sample].pss > ps->pss_max)
423 ps->pss_max = ps->sample[sample].pss;
424
425 catch_rename:
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) {
429
430 /* re-fetch name */
431 /* get name, start time */
432 if (!ps->sched) {
433 sprintf(filename, "%d/sched", pid);
434 ps->sched = openat(procfd, filename, O_RDONLY);
435 if (ps->sched == -1)
436 continue;
437 }
438 s = pread(ps->sched, buf, sizeof(buf) - 1, 0);
439 if (s <= 0) {
440 /* clean up file descriptors */
441 close(ps->sched);
442 if (ps->schedstat)
443 close(ps->schedstat);
444 //if (ps->smaps)
445 // fclose(ps->smaps);
446 continue;
447 }
448 buf[s] = '\0';
449
450 if (!sscanf(buf, "%s %*s %*s", key))
451 continue;
452
453 strscpy(ps->name, sizeof(ps->name), key);
454
455 /* cmdline */
456 if (arg_show_cmdline)
457 pid_cmdline_strscpy(ps->name, sizeof(ps->name), pid);
458 }
459 }
460 }