Tizen 2.0 Release
[external/bootchart.git] / bootchart.c
1 /*
2  * bootchart.c
3  *
4  * Copyright (c) 2009 Intel Coproration
5  * Authors:
6  *   Auke Kok <auke-jan.h.kok@intel.com>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; version 2
11  * of the License.
12  */
13
14
15 #include <sys/time.h>
16 #include <sys/types.h>
17 #include <sys/resource.h>
18 #include <stdio.h>
19 #include <signal.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
23 #include <time.h>
24 #include <getopt.h>
25 #include <limits.h>
26
27
28 #include "bootchart.h"
29
30 double graph_start;
31 double log_start;
32 double sampletime[MAXSAMPLES];
33 struct ps_struct *ps[MAXPIDS]; /* ll */
34 struct block_stat_struct blockstat[MAXSAMPLES];
35 struct cpu_stat_struct cpustat[MAXCPUS];
36 int pscount;
37 int cpus;
38 double interval;
39 FILE *of;
40 int overrun = 0;
41 static int exiting = 0;
42
43 /* graph defaults */
44 int initcall = 1;
45 int relative;
46 int filter = 1;
47 int pss = 0;
48 int samples;
49 int len = 500; /* we record len+1 (1 start sample) */
50 int hz = 25;   /* 20 seconds log time */
51 int scale_x = 100; /* 100px = 1sec */
52 int scale_y = 20;  /* 16px = 1 process bar */
53
54 char init_path[PATH_MAX] = "/sbin/init";
55 char output_path[PATH_MAX] = "/var/log";
56
57 static struct rlimit rlim;
58
59 static void signal_handler(int sig)
60 {
61         if (sig++)
62                 sig--;
63         exiting = 1;
64 }
65
66
67 int main(int argc, char *argv[])
68 {
69         struct sigaction sig;
70         char output_file[PATH_MAX];
71         char datestr[200];
72         time_t t;
73         FILE *f;
74         int i;
75
76         memset(&t, 0, sizeof(time_t));
77
78         rlim.rlim_cur = 4096;
79         rlim.rlim_max = 4096;
80         (void) setrlimit(RLIMIT_NOFILE, &rlim);
81
82         f = fopen("/etc/bootchartd.conf", "r");
83         if (f) {
84                 char buf[256];
85                 char *key;
86                 char *val;
87
88                 while (fgets(buf, 80, f) != NULL) {
89                         char *c;
90
91                         c = strchr(buf, '\n');
92                         if (c) *c = 0; /* remove trailing \n */
93
94                         if (buf[0] == '#')
95                                 continue; /* comment line */
96
97                         key = strtok(buf, "=");
98                         if (!key)
99                                 continue;
100                         val = strtok(NULL, "=");
101                         if (!val)
102                                 continue;
103
104                         // todo: filter leading/trailing whitespace
105
106                         if (!strcmp(key, "samples"))
107                                 len = atoi(val);
108                         if (!strcmp(key, "freq"))
109                                 hz = atoi(val);
110                         if (!strcmp(key, "rel"))
111                                 relative = atoi(val);
112                         if (!strcmp(key, "filter"))
113                                 filter = atoi(val);
114                         if (!strcmp(key, "pss"))
115                                 pss = atoi(val);
116                         if (!strcmp(key, "output"))
117                                 strncpy(output_path, val, PATH_MAX - 1);
118                         if (!strcmp(key, "init"))
119                                 strncpy(init_path, val, PATH_MAX - 1);
120                         if (!strcmp(key, "scale_x"))
121                                 scale_x = atoi(val);
122                         if (!strcmp(key, "scale_y"))
123                                 scale_y = atoi(val);
124                 }
125                 fclose(f);
126         }
127
128         while (1) {
129                 static struct option opts[] = {
130                         {"rel", 0, NULL, 'r'},
131                         {"freq", 1, NULL, 'f'},
132                         {"samples", 1, NULL, 'n'},
133                         {"pss", 0, NULL, 'p'},
134                         {"output", 1, NULL, 'o'},
135                         {"init", 1, NULL, 'i'},
136                         {"filter", 0, NULL, 'F'},
137                         {"help", 0, NULL, 'h'},
138                         {"scale-x", 1, NULL, 'x'},
139                         {"scale-y", 1, NULL, 'y'},
140                         {NULL, 0, NULL, 0}
141                 };
142
143                 int index = 0, c;
144
145                 c = getopt_long(argc, argv, "rpf:n:o:i:Fhx:y:", opts, &index);
146                 if (c == -1)
147                         break;
148                 switch (c) {
149                 case 'r':
150                         relative = 1;
151                         break;
152                 case 'f':
153                         hz = atoi(optarg);
154                         break;
155                 case 'F':
156                         filter = 0;
157                         break;
158                 case 'n':
159                         len = atoi(optarg);
160                         break;
161                 case 'o':
162                         strncpy(output_path, optarg, PATH_MAX - 1);
163                         break;
164                 case 'i':
165                         strncpy(init_path, optarg, PATH_MAX - 1);
166                         break;
167                 case 'p':
168                         pss = 1;
169                         break;
170                 case 'x':
171                         scale_x = atoi(optarg);
172                         break;
173                 case 'y':
174                         scale_y = atoi(optarg);
175                         break;
176                 case 'h':
177                         fprintf(stderr, "Usage: %s [OPTIONS]\n", argv[0]);
178                         fprintf(stderr, " --rel,     -r            Record time relative to recording\n");
179                         fprintf(stderr, " --freq,    -f N          Sample frequency [%d]\n", hz);
180                         fprintf(stderr, " --samples, -n N          Stop sampling at [%d] samples\n", len);
181                         fprintf(stderr, " --scale-x, -x N          Scale the graph horizontally [%d] \n", scale_x);
182                         fprintf(stderr, " --scale-y, -y N          Scale the graph vertically [%d] \n", scale_y);
183                         fprintf(stderr, " --pss,     -p            Enable PSS graph (CPU intensive)\n");
184                         fprintf(stderr, " --output,  -o [PATH]     Path to output files [%s]\n", output_path);
185                         fprintf(stderr, " --init,    -i [PATH]     Path to init executable [%s]\n", init_path);
186                         fprintf(stderr, " --filter,  -F            Disable filtering of processes from the graph\n");
187                         fprintf(stderr, "                          that are of less importance or short-lived\n");
188                         fprintf(stderr, " --help,    -h            Display this message\n");
189                         fprintf(stderr, "See the installed README and bootchartd.conf.example for more information.\n");
190                         exit (EXIT_SUCCESS);
191                         break;
192                 default:
193                         break;
194                 }
195         }
196
197         if (len > MAXSAMPLES) {
198                 fprintf(stderr, "Error: samples exceeds maximum\n");
199                 exit(EXIT_FAILURE);
200         }
201
202         /*
203          * If the kernel executed us through init=/sbin/bootchartd, then
204          * fork:
205          * - parent execs executable specified via init_path[] (/sbin/init by default) as pid=1
206          * - child logs data
207          */
208         if (getpid() == 1) {
209                 if (fork()) {
210                         /* parent */
211                         execl(init_path, init_path, NULL);
212                 }
213         }
214
215         /* handle TERM/INT nicely */
216         memset(&sig, 0, sizeof(struct sigaction));
217         sig.sa_handler = signal_handler;
218         sigaction(SIGTERM, &sig, NULL);
219         sigaction(SIGINT, &sig, NULL);
220
221         interval = (1.0 / hz) * 1000000000.0;
222
223         log_uptime();
224
225         /* main program loop */
226         while (!exiting) {
227                 int res;
228                 double sample_stop;
229                 struct timespec req;
230                 long newint_ns;
231
232                 sampletime[samples] = gettime_ns();
233
234                 /* wait for /proc to become available, discarding samples */
235                 if (!graph_start)
236                         log_uptime();
237                 else
238                         log_sample(samples);
239
240                 sample_stop = gettime_ns();
241
242                 req.tv_sec = 0;
243                 newint_ns = interval - ((sample_stop - sampletime[samples]) * 1000000000);
244
245                 /*
246                  * check if we have not consumed our entire timeslice. If we
247                  * do, don't sleep and take a new sample right away.
248                  * we'll lose all the missed samples and overrun our total
249                  * time
250                  */
251                 if (newint_ns > 0) {
252                         req.tv_nsec = newint_ns;
253
254                         res = nanosleep(&req, NULL);
255                         if (res) {
256                                 perror("nanosleep()");
257                                 exit (EXIT_FAILURE);
258                         }
259                 } else {
260                         overrun++;
261                         /* calculate how many samples we lost and scrap them */
262                         len = len + ((int)(newint_ns / interval));
263                 }
264
265                 samples++;
266
267                 if (samples > len)
268                         break;
269
270         }
271
272         /* do some cleanup, close fd's */
273         for ( i = 0; i < MAXPIDS ; i++) {
274                 if (!ps[i])
275                         continue;
276         /*
277                 if (ps[i]->schedstat)
278                         close(ps[i]->schedstat);
279                 if (ps[i]->sched)
280                         close(ps[i]->sched);
281                 if (ps[i]->smaps)
282                         fclose(ps[i]->smaps);
283          */
284         }
285
286         t = time(NULL);
287         strftime(datestr, sizeof(datestr), "%Y%m%d-%H%M", localtime(&t));
288         sprintf(output_file, "%s/bootchart-%s.svg", output_path, datestr);
289
290         of = fopen(output_file, "w");
291         if (!of) {
292                 perror("open output_file");
293                 exit (EXIT_FAILURE);
294         }
295
296         svg_do();
297
298         fprintf(stderr, "bootchartd: Wrote %s\n", output_file);
299         fclose(of);
300
301         /* don't complain when overrun once, happens most commonly on 1st sample */
302         if (overrun > 1)
303                 fprintf(stderr, "bootchartd: Warning: sample time overrun %i times\n", overrun);
304
305         return 0;
306 }