chiark / gitweb /
adfaba57b0077afaae10fa06b898d3887ad37039
[elogind.git] / src / bootchart / bootchart.c
1 /***
2   bootchart.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 #include <sys/time.h>
24 #include <sys/types.h>
25 #include <sys/resource.h>
26 #include <stdio.h>
27 #include <signal.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <time.h>
32 #include <getopt.h>
33 #include <limits.h>
34 #include <errno.h>
35
36
37 #include "bootchart.h"
38 #include "util.h"
39 #include "fileio.h"
40
41 double graph_start;
42 double log_start;
43 double sampletime[MAXSAMPLES];
44 struct ps_struct *ps_first;
45 struct block_stat_struct blockstat[MAXSAMPLES];
46 int entropy_avail[MAXSAMPLES];
47 struct cpu_stat_struct cpustat[MAXCPUS];
48 int pscount;
49 int cpus;
50 double interval;
51 FILE *of = NULL;
52 int overrun = 0;
53 static int exiting = 0;
54
55 /* graph defaults */
56 int entropy = 0;
57 int initcall = 1;
58 int relative;
59 int filter = 1;
60 int pss = 0;
61 int samples;
62 int len = 500; /* we record len+1 (1 start sample) */
63 double hz = 25.0;   /* 20 seconds log time */
64 double scale_x = 100.0; /* 100px = 1sec */
65 double scale_y = 20.0;  /* 16px = 1 process bar */
66
67 char init_path[PATH_MAX] = "/sbin/init";
68 char output_path[PATH_MAX] = "/run/log";
69
70 static struct rlimit rlim;
71
72 static void signal_handler(int sig)
73 {
74         if (sig++)
75                 sig--;
76         exiting = 1;
77 }
78
79
80 int main(int argc, char *argv[])
81 {
82         struct sigaction sig;
83         struct ps_struct *ps;
84         char output_file[PATH_MAX];
85         char datestr[200];
86         time_t t = 0;
87         FILE *f;
88         int gind;
89         int i;
90
91         rlim.rlim_cur = 4096;
92         rlim.rlim_max = 4096;
93         (void) setrlimit(RLIMIT_NOFILE, &rlim);
94
95         f = fopen("/etc/systemd/bootchart.conf", "r");
96         if (f) {
97                 char buf[256];
98                 char *key;
99                 char *val;
100
101                 while (fgets(buf, 80, f) != NULL) {
102                         char *c;
103
104                         c = strchr(buf, '\n');
105                         if (c) *c = 0; /* remove trailing \n */
106
107                         if (buf[0] == '#')
108                                 continue; /* comment line */
109
110                         key = strtok(buf, "=");
111                         if (!key)
112                                 continue;
113                         val = strtok(NULL, "=");
114                         if (!val)
115                                 continue;
116
117                         // todo: filter leading/trailing whitespace
118
119                         if (streq(key, "samples"))
120                                 len = atoi(val);
121                         if (streq(key, "freq"))
122                                 hz = atof(val);
123                         if (streq(key, "rel"))
124                                 relative = atoi(val);
125                         if (streq(key, "filter"))
126                                 filter = atoi(val);
127                         if (streq(key, "pss"))
128                                 pss = atoi(val);
129                         if (streq(key, "output"))
130                                 strncpy(output_path, val, PATH_MAX - 1);
131                         if (streq(key, "init"))
132                                 strncpy(init_path, val, PATH_MAX - 1);
133                         if (streq(key, "scale_x"))
134                                 scale_x = atof(val);
135                         if (streq(key, "scale_y"))
136                                 scale_y = atof(val);
137                         if (streq(key, "entropy"))
138                                 entropy = atoi(val);
139                 }
140                 fclose(f);
141         }
142
143         while (1) {
144                 static struct option opts[] = {
145                         {"rel", 0, NULL, 'r'},
146                         {"freq", 1, NULL, 'f'},
147                         {"samples", 1, NULL, 'n'},
148                         {"pss", 0, NULL, 'p'},
149                         {"output", 1, NULL, 'o'},
150                         {"init", 1, NULL, 'i'},
151                         {"filter", 0, NULL, 'F'},
152                         {"help", 0, NULL, 'h'},
153                         {"scale-x", 1, NULL, 'x'},
154                         {"scale-y", 1, NULL, 'y'},
155                         {"entropy", 0, NULL, 'e'},
156                         {NULL, 0, NULL, 0}
157                 };
158
159                 gind = 0;
160
161                 i = getopt_long(argc, argv, "erpf:n:o:i:Fhx:y:", opts, &gind);
162                 if (i == -1)
163                         break;
164                 switch (i) {
165                 case 'r':
166                         relative = 1;
167                         break;
168                 case 'f':
169                         hz = atof(optarg);
170                         break;
171                 case 'F':
172                         filter = 0;
173                         break;
174                 case 'n':
175                         len = atoi(optarg);
176                         break;
177                 case 'o':
178                         strncpy(output_path, optarg, PATH_MAX - 1);
179                         break;
180                 case 'i':
181                         strncpy(init_path, optarg, PATH_MAX - 1);
182                         break;
183                 case 'p':
184                         pss = 1;
185                         break;
186                 case 'x':
187                         scale_x = atof(optarg);
188                         break;
189                 case 'y':
190                         scale_y = atof(optarg);
191                         break;
192                 case 'e':
193                         entropy = 1;
194                         break;
195                 case 'h':
196                         fprintf(stderr, "Usage: %s [OPTIONS]\n", argv[0]);
197                         fprintf(stderr, " --rel,     -r            Record time relative to recording\n");
198                         fprintf(stderr, " --freq,    -f N          Sample frequency [%f]\n", hz);
199                         fprintf(stderr, " --samples, -n N          Stop sampling at [%d] samples\n", len);
200                         fprintf(stderr, " --scale-x, -x N          Scale the graph horizontally [%f] \n", scale_x);
201                         fprintf(stderr, " --scale-y, -y N          Scale the graph vertically [%f] \n", scale_y);
202                         fprintf(stderr, " --pss,     -p            Enable PSS graph (CPU intensive)\n");
203                         fprintf(stderr, " --entropy, -e            Enable the entropy_avail graph\n");
204                         fprintf(stderr, " --output,  -o [PATH]     Path to output files [%s]\n", output_path);
205                         fprintf(stderr, " --init,    -i [PATH]     Path to init executable [%s]\n", init_path);
206                         fprintf(stderr, " --filter,  -F            Disable filtering of processes from the graph\n");
207                         fprintf(stderr, "                          that are of less importance or short-lived\n");
208                         fprintf(stderr, " --help,    -h            Display this message\n");
209                         fprintf(stderr, "See the installed README and bootchartd.conf.example for more information.\n");
210                         exit (EXIT_SUCCESS);
211                         break;
212                 default:
213                         break;
214                 }
215         }
216
217         if (len > MAXSAMPLES) {
218                 fprintf(stderr, "Error: samples exceeds maximum\n");
219                 exit(EXIT_FAILURE);
220         }
221
222         if (hz <= 0.0) {
223                 fprintf(stderr, "Error: Frequency needs to be > 0\n");
224                 exit(EXIT_FAILURE);
225         }
226
227         /*
228          * If the kernel executed us through init=/sbin/bootchartd, then
229          * fork:
230          * - parent execs executable specified via init_path[] (/sbin/init by default) as pid=1
231          * - child logs data
232          */
233         if (getpid() == 1) {
234                 if (fork()) {
235                         /* parent */
236                         execl(init_path, init_path, NULL);
237                 }
238         }
239         argv[0][0] = '@';
240
241         /* start with empty ps LL */
242         ps_first = calloc(1, sizeof(struct ps_struct));
243         if (!ps_first) {
244                 perror("calloc(ps_struct)");
245                 exit(EXIT_FAILURE);
246         }
247
248         /* handle TERM/INT nicely */
249         memset(&sig, 0, sizeof(struct sigaction));
250         sig.sa_handler = signal_handler;
251         sigaction(SIGHUP, &sig, NULL);
252
253         interval = (1.0 / hz) * 1000000000.0;
254
255         log_uptime();
256
257         /* main program loop */
258         while (!exiting) {
259                 int res;
260                 double sample_stop;
261                 struct timespec req;
262                 time_t newint_s;
263                 long newint_ns;
264                 double elapsed;
265                 double timeleft;
266
267                 sampletime[samples] = gettime_ns();
268
269                 if (!of && (access(output_path, R_OK|W_OK|X_OK) == 0)) {
270                         t = time(NULL);
271                         strftime(datestr, sizeof(datestr), "%Y%m%d-%H%M", localtime(&t));
272                         snprintf(output_file, PATH_MAX, "%s/bootchart-%s.svg", output_path, datestr);
273                         of = fopen(output_file, "w");
274                 }
275
276
277                 /* wait for /proc to become available, discarding samples */
278                 if (!(graph_start > 0.0))
279                         log_uptime();
280                 else
281                         log_sample(samples);
282
283                 sample_stop = gettime_ns();
284
285                 elapsed = (sample_stop - sampletime[samples]) * 1000000000.0;
286                 timeleft = interval - elapsed;
287
288                 newint_s = (time_t)(timeleft / 1000000000.0);
289                 newint_ns = (long)(timeleft - (newint_s * 1000000000.0));
290
291                 /*
292                  * check if we have not consumed our entire timeslice. If we
293                  * do, don't sleep and take a new sample right away.
294                  * we'll lose all the missed samples and overrun our total
295                  * time
296                  */
297                 if ((newint_ns > 0) || (newint_s > 0)) {
298                         req.tv_sec = newint_s;
299                         req.tv_nsec = newint_ns;
300
301                         res = nanosleep(&req, NULL);
302                         if (res) {
303                                 if (errno == EINTR) {
304                                         /* caught signal, probably HUP! */
305                                         break;
306                                 }
307                                 perror("nanosleep()");
308                                 exit (EXIT_FAILURE);
309                         }
310                 } else {
311                         overrun++;
312                         /* calculate how many samples we lost and scrap them */
313                         len = len + ((int)(newint_ns / interval));
314                 }
315
316                 samples++;
317
318                 if (samples > len)
319                         break;
320
321         }
322
323         /* do some cleanup, close fd's */
324         ps = ps_first;
325         while (ps->next_ps) {
326                 ps = ps->next_ps;
327                 if (ps->schedstat)
328                         close(ps->schedstat);
329                 if (ps->sched)
330                         close(ps->sched);
331                 if (ps->smaps)
332                         fclose(ps->smaps);
333         }
334         closedir(proc);
335
336         if (!of) {
337                 t = time(NULL);
338                 strftime(datestr, sizeof(datestr), "%Y%m%d-%H%M", localtime(&t));
339                 snprintf(output_file, PATH_MAX, "%s/bootchart-%s.svg", output_path, datestr);
340                 of = fopen(output_file, "w");
341         }
342
343         if (!of) {
344                 perror("open output_file");
345                 exit (EXIT_FAILURE);
346         }
347
348         svg_do();
349
350         fprintf(stderr, "bootchartd: Wrote %s\n", output_file);
351         fclose(of);
352
353         /* nitpic cleanups */
354         ps = ps_first;
355         while (ps->next_ps) {
356                 struct ps_struct *old = ps;
357                 ps = ps->next_ps;
358                 free(old->sample);
359                 free(old);
360         }
361         free(ps->sample);
362         free(ps);
363
364         /* don't complain when overrun once, happens most commonly on 1st sample */
365         if (overrun > 1)
366                 fprintf(stderr, "bootchartd: Warning: sample time overrun %i times\n", overrun);
367
368         return 0;
369 }