Cleaned up spec file including change of group, removal of extraneous setup arguments...
[external/bootchart.git] / log.c
1 /*
2  * log.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 #define _GNU_SOURCE 1
15 #include <unistd.h>
16 #include <stdlib.h>
17 #include <limits.h>
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <dirent.h>
23 #include <fcntl.h>
24 #include <time.h>
25
26
27 #include "bootchart.h"
28
29 /*
30  * Alloc a static 4k buffer for stdio - primarily used to increase
31  * PSS buffering from the default 1k stdin buffer to reduce
32  * read() overhead.
33  */
34 char smaps_buf[4096];
35
36
37 double gettime_ns(void)
38 {
39         struct timespec now;
40
41         clock_gettime(CLOCK_MONOTONIC, &now);
42
43         return (now.tv_sec + (now.tv_nsec / 1000000000.0));
44 }
45
46
47 void log_uptime(void)
48 {
49         FILE *f;
50         char str[32];
51         double uptime;
52
53         f = fopen("/proc/uptime", "r");
54         if (!f)
55                 return;
56         if (!fscanf(f, "%s %*s", str)) {
57                 fclose(f);
58                 return;
59         }
60         fclose(f);
61         uptime = strtod(str, NULL);
62
63         log_start = gettime_ns();
64
65         /* start graph at kernel boot time */
66         if (relative)
67                 graph_start = log_start;
68         else
69                 graph_start = log_start - uptime;
70 }
71
72
73 static char *bufgetline(char *buf)
74 {
75         char *c;
76
77         if (!buf)
78                 return NULL;
79
80         c = strchr(buf, '\n');
81         if (c)
82                 c++;
83         return c;
84 }
85
86
87 void log_sample(int sample)
88 {
89         static int vmstat;
90         static int schedstat;
91         static DIR *proc;
92         FILE *stat;
93         char buf[4095];
94         char key[256];
95         char val[256];
96         char rt[256];
97         char wt[256];
98         char *m;
99         int c;
100         int p;
101         int s;
102         int n;
103         struct dirent *ent;
104
105         if (!vmstat) {
106                 /* block stuff */
107                 vmstat = open("/proc/vmstat", O_RDONLY);
108                 if (vmstat == -1) {
109                         perror("open /proc/vmstat");
110                         exit (EXIT_FAILURE);
111                 }
112         }
113
114         n = pread(vmstat, buf, sizeof(buf) - 1, 0);
115         if (n <= 0) {
116                 close(vmstat);
117                 return;
118         }
119         buf[n] = '\0';
120
121         m = buf;
122         while (m) {
123                 if (sscanf(m, "%s %s", key, val) < 2)
124                         goto vmstat_next;
125                 if (!strcmp(key, "pgpgin"))
126                         blockstat[sample].bi = atoi(val);
127                 if (!strcmp(key, "pgpgout")) {
128                         blockstat[sample].bo = atoi(val);
129                         break;
130                 }
131 vmstat_next:
132                 m = bufgetline(m);
133                 if (!m)
134                         break;
135         }
136
137         if (!schedstat) {
138                 /* overall CPU utilization */
139                 schedstat = open("/proc/schedstat", O_RDONLY);
140                 if (schedstat == -1) {
141                         perror("open /proc/schedstat");
142                         exit (EXIT_FAILURE);
143                 }
144         }
145
146         n = pread(schedstat, buf, sizeof(buf) - 1, 0);
147         if (n <= 0) {
148                 close(schedstat);
149                 return;
150         }
151         buf[n] = '\0';
152
153         m = buf;
154         while (m) {
155                 if (sscanf(m, "%s %*s %*s %*s %*s %*s %*s %s %s", key, rt, wt) < 3)
156                         goto schedstat_next;
157
158                 if (strstr(key, "cpu")) {
159                         c = atoi((const char*)(key+3));
160                         if (c > MAXCPUS)
161                                 /* Oops, we only have room for MAXCPUS data */
162                                 break;
163                         cpustat[c].sample[sample].runtime = atoll(rt);
164                         cpustat[c].sample[sample].waittime = atoll(wt);
165
166                         if (c == cpus)
167                                 cpus = c + 1;
168                 }
169 schedstat_next:
170                 m = bufgetline(m);
171                 if (!m)
172                         break;
173         }
174
175         /* all the per-process stuff goes here */
176         if (!proc) {
177                 /* find all processes */
178                 proc = opendir("/proc");
179                 if (!proc)
180                         return;
181         } else {
182                 rewinddir(proc);
183         }
184
185         while ((ent = readdir(proc)) != NULL) {
186                 char filename[PATH_MAX];
187                 int pid;
188
189                 if ((ent->d_name[0] < '0') || (ent->d_name[0] > '9'))
190                         continue;
191
192                 pid = atoi(ent->d_name);
193
194                 if (pid >= MAXPIDS)
195                         continue;
196
197                 if (!ps[pid]) {
198                         char t[32];
199                         struct ps_struct *parent;
200
201                         pscount++;
202
203                         /* alloc & insert */
204                         ps[pid] = malloc(sizeof(struct ps_struct));
205                         if (!ps[pid]) {
206                                 perror("malloc ps[pid]");
207                                 exit (EXIT_FAILURE);
208                         }
209                         memset(ps[pid], 0, sizeof(struct ps_struct));
210
211                         /* mark our first sample */
212                         ps[pid]->first = sample;
213
214                         /* get name, start time */
215                         if (!ps[pid]->sched) {
216                                 sprintf(filename, "/proc/%d/sched", pid);
217                                 ps[pid]->sched = open(filename, O_RDONLY);
218                                 if (ps[pid]->sched == -1)
219                                         continue;
220                         }
221
222                         s = pread(ps[pid]->sched, buf, sizeof(buf) - 1, 0);
223                         if (s <= 0) {
224                                 close(ps[pid]->sched);
225                                 continue;
226                         }
227
228                         if (!sscanf(buf, "%s %*s %*s", key))
229                                 continue;
230
231                         strncpy(ps[pid]->name, key, 16);
232                         /* discard line 2 */
233                         m = bufgetline(buf);
234                         if (!m)
235                                 continue;
236
237                         m = bufgetline(m);
238                         if (!m)
239                                 continue;
240
241                         if (!sscanf(m, "%*s %*s %s", t))
242                                 continue;
243
244                         ps[pid]->starttime = strtod(t, NULL) / 1000.0;
245
246                         /* ppid */
247                         sprintf(filename, "/proc/%d/stat", pid);
248                         stat = fopen(filename, "r");
249                         if (!stat)
250                                 continue;
251                         if (!fscanf(stat, "%*s %*s %*s %i", &p)) {
252                                 fclose(stat);
253                                 continue;
254                         }
255                         fclose(stat);
256                         ps[pid]->ppid = p;
257
258                         /*
259                          * setup child pointers
260                          *
261                          * these are used to paint the tree coherently later
262                          * each parent has a LL of children, and a LL of siblings
263                          */
264                         if (pid == 1)
265                                 continue; /* nothing to do for init atm */
266
267                         parent = ps[ps[pid]->ppid];
268
269                         if (!parent) {
270                                 /* fix this asap */
271                                 ps[pid]->ppid = 1;
272                                 parent = ps[1];
273                         }
274
275                         if (!parent->children) {
276                                 /* it's the first child */
277                                 parent->children = ps[pid];
278                         } else {
279                                 /* walk all children and append */
280                                 struct ps_struct *children;
281                                 children = parent->children;
282                                 while (children->next)
283                                         children = children->next;
284                                 children->next = ps[pid];
285                         }
286
287                         ps[pid]->pid = pid;
288                 }
289
290                 /* below here is all continuous logging parts - we get here on every
291                  * iteration */
292
293                 /* rt, wt */
294                 if (!ps[pid]->schedstat) {
295                         sprintf(filename, "/proc/%d/schedstat", pid);
296                         ps[pid]->schedstat = open(filename, O_RDONLY);
297                         if (ps[pid]->schedstat == -1)
298                                 continue;
299                 }
300
301                 if (pread(ps[pid]->schedstat, buf, sizeof(buf) - 1, 0) <= 0) {
302                         /* clean up our file descriptors - assume that the process exited */
303                         close(ps[pid]->schedstat);
304                         if (ps[pid]->sched)
305                                 close(ps[pid]->sched);
306                         //if (ps[pid]->smaps)
307                         //      fclose(ps[pid]->smaps);
308                         continue;
309                 }
310                 if (!sscanf(buf, "%s %s %*s", rt, wt))
311                         continue;
312
313                 ps[pid]->pid = pid;
314                 ps[pid]->last = sample;
315                 ps[pid]->sample[sample].runtime = atoll(rt);
316                 ps[pid]->sample[sample].waittime = atoll(wt);
317
318                 ps[pid]->total = (ps[pid]->sample[ps[pid]->last].runtime
319                                  - ps[pid]->sample[ps[pid]->first].runtime)
320                                  / 1000000000.0;
321
322                 if (!pss)
323                         goto catch_rename;
324                 /* Pss */
325                 if (!ps[pid]->smaps) {
326                         sprintf(filename, "/proc/%d/smaps", pid);
327                         ps[pid]->smaps = fopen(filename, "r");
328                         setvbuf(ps[pid]->smaps, smaps_buf, _IOFBF, sizeof(smaps_buf));
329                         if (!ps[pid]->smaps)
330                                 continue;
331                 } else {
332                         rewind(ps[pid]->smaps);
333                 }
334
335                 while (1) {
336                         /* skip one line, this contains the object mapped */
337                         if (fgets(buf, sizeof(buf) -1, ps[pid]->smaps) == NULL)
338                                 break;
339                         /* then there's a 28 char 14 line block */
340                         if (fread(buf, 1, 28 * 14, ps[pid]->smaps) != 28 * 14)
341                                 break;
342
343                         int p;
344                         p = atoi(&buf[61]);
345                         ps[pid]->sample[sample].pss += p;
346                 }
347
348                 if (ps[pid]->sample[sample].pss > ps[pid]->pss_max)
349                         ps[pid]->pss_max = ps[pid]->sample[sample].pss;
350
351 catch_rename:
352                 /* catch process rename, try to randomize time */
353                 if (((samples - ps[pid]->first) + pid) % (hz / 4) == 0) {
354
355                         /* re-fetch name */
356                         /* get name, start time */
357                         if (!ps[pid]->sched) {
358                                 sprintf(filename, "/proc/%d/sched", pid);
359                                 ps[pid]->sched = open(filename, O_RDONLY);
360                                 if (ps[pid]->sched == -1)
361                                         continue;
362                         }
363                         if (pread(ps[pid]->sched, buf, sizeof(buf) - 1, 0) <= 0) {
364                                 /* clean up file descriptors */
365                                 close(ps[pid]->sched);
366                                 if (ps[pid]->schedstat)
367                                         close(ps[pid]->schedstat);
368                                 //if (ps[pid]->smaps)
369                                 //      fclose(ps[pid]->smaps);
370                                 continue;
371                         }
372
373                         if (!sscanf(buf, "%s %*s %*s", key))
374                                 continue;
375
376                         strncpy(ps[pid]->name, key, 16);
377                 }
378         }
379 }
380