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