iowatcher: Add bounds checking in find_step
[blktrace.git] / iowatcher / mpstat.c
1 /*
2  * Copyright (C) 2012 Fusion-io
3  *
4  *  This program is free software; you can redistribute it and/or
5  *  modify it under the terms of the GNU General Public
6  *  License v2 as published by the Free Software Foundation.
7  *
8  *  This program is distributed in the hope that it will be useful,
9  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  *  GNU General Public License for more details.
12  *
13  *  You should have received a copy of the GNU General Public License
14  *  along with this program; if not, write to the Free Software
15  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16  */
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <fcntl.h>
20 #include <unistd.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <math.h>
24 #include <inttypes.h>
25 #include <string.h>
26 #include <asm/types.h>
27 #include <errno.h>
28 #include <sys/mman.h>
29 #include <time.h>
30 #include <math.h>
31
32 #include "plot.h"
33 #include "blkparse.h"
34 #include "list.h"
35 #include "tracers.h"
36 #include "mpstat.h"
37
38 char line[1024];
39 int line_len = 1024;
40
41 static char record_header[] = "CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest   %idle\n";
42 static char record_header_v2[] = "CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle\n";
43
44 int record_header_len = sizeof(record_header);
45 int record_header_v2_len = sizeof(record_header_v2);
46
47 static int past_eof(struct trace *trace, char *cur)
48 {
49         if (cur >= trace->mpstat_start + trace->mpstat_len)
50                 return 1;
51         return 0;
52 }
53
54 int next_mpstat_line(struct trace *trace)
55 {
56         char *next;
57         char *cur = trace->mpstat_cur;
58
59         next = strchr(cur, '\n');
60         if (!next)
61                 return 1;
62         next++;
63         if (past_eof(trace, next))
64                 return 1;
65         trace->mpstat_cur = next;
66         return 0;
67 }
68
69 char *next_mpstat(struct trace *trace)
70 {
71         char *cur;
72
73         cur = strstr(trace->mpstat_cur, record_header);
74         if (cur) {
75                 cur += record_header_len;
76         } else {
77                 cur = strstr(trace->mpstat_cur, record_header_v2);
78                 if (cur)
79                         cur += record_header_v2_len;
80         }
81         if (!cur)
82                 return NULL;
83
84         if (past_eof(trace, cur))
85                 return NULL;
86         trace->mpstat_cur = cur;
87         return cur;
88 }
89
90 char *first_mpstat(struct trace *trace)
91 {
92         char *cur = trace->mpstat_cur;
93
94         trace->mpstat_cur = trace->mpstat_start;
95
96         cur = next_mpstat(trace);
97         if (!cur)
98                 return NULL;
99         return cur;
100 }
101
102 static void find_last_mpstat_time(struct trace *trace)
103 {
104         int num_mpstats = 0;
105         char *cur;
106
107         first_mpstat(trace);
108
109         cur = first_mpstat(trace);
110         while (cur) {
111                 num_mpstats++;
112                 cur = next_mpstat(trace);
113         }
114         first_mpstat(trace);
115         trace->mpstat_seconds = num_mpstats;
116 }
117
118 static int guess_mpstat_cpus(struct trace *trace)
119 {
120         char *cur;
121         int ret;
122         int count = 0;
123
124         cur = first_mpstat(trace);
125         if (!cur)
126                 return 0;
127
128         while (1) {
129                 ret = next_mpstat_line(trace);
130                 if (ret)
131                         break;
132
133                 cur = trace->mpstat_cur;
134                 count++;
135
136                 if (!cur)
137                         break;
138
139                 if (cur[0] == '\n')
140                         break;
141         }
142         trace->mpstat_num_cpus = count - 1;
143         return 0;
144 }
145
146 static int count_mpstat_cpus(struct trace *trace)
147 {
148         char *cur = trace->mpstat_start;
149         char *cpu;
150         char *record;
151         int len; char *line;
152
153         first_mpstat(trace);
154         cpu = strstr(cur, " CPU)");
155         if (!cpu)
156                 return guess_mpstat_cpus(trace);
157
158         line = strndup(cur, cpu - cur);
159
160         record = strrchr(line, '(');
161         if (!record) {
162                 free(line);
163                 return 0;
164         }
165         record++;
166
167         len = line + strlen(line) - record;
168
169         cur = strndup(record, len);
170         trace->mpstat_num_cpus = atoi(cur);
171         first_mpstat(trace);
172         free(line);
173
174         return trace->mpstat_num_cpus;
175 }
176
177 static char *guess_filename(char *trace_name)
178 {
179         struct stat st;
180         int ret;
181         char *cur;
182         char *tmp;
183
184         snprintf(line, line_len, "%s.mpstat", trace_name);
185         ret = stat(line, &st);
186         if (ret == 0)
187                 return trace_name;
188
189         cur = strrchr(trace_name, '.');
190         if (!cur) {
191                 return trace_name;
192         }
193
194         tmp = strndup(trace_name, cur - trace_name);
195         snprintf(line, line_len, "%s.mpstat", tmp);
196         ret = stat(line, &st);
197         if (ret == 0)
198                 return tmp;
199
200         free(tmp);
201         return trace_name;
202 }
203
204 int read_mpstat(struct trace *trace, char *trace_name)
205 {
206         int fd;
207         struct stat st;
208         int ret;
209         char *p;
210
211         if (record_header_len == 0) {
212                 record_header_len = strlen(record_header);
213         }
214
215         snprintf(line, line_len, "%s.mpstat", guess_filename(trace_name));
216         fd = open(line, O_RDONLY);
217         if (fd < 0)
218                 return 0;
219
220         ret = fstat(fd, &st);
221         if (ret < 0) {
222                 fprintf(stderr, "stat failed on %s err %s\n", line, strerror(errno));
223                 goto fail_fd;
224         }
225         p = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
226         if (p == MAP_FAILED) {
227                 fprintf(stderr, "Unable to mmap trace file %s, err %s\n", line, strerror(errno));
228                 goto fail_fd;
229         }
230         trace->mpstat_start = p;
231         trace->mpstat_len = st.st_size;
232         trace->mpstat_cur = p;
233         trace->mpstat_fd = fd;
234         find_last_mpstat_time(trace);
235         count_mpstat_cpus(trace);
236
237         first_mpstat(trace);
238
239         return 0;
240
241 fail_fd:
242         close(fd);
243         return 0;
244 }
245
246 /*
247  * 09:56:26 AM  CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest   %idle
248  *
249  * or
250  *
251  * 10:18:51 AM  CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle
252  *
253  *
254  * this reads just one line in the mpstat
255  */
256 int read_mpstat_event(struct trace *trace, double *user,
257                       double *sys, double *iowait, double *irq,
258                       double *soft)
259 {
260         char *cur = trace->mpstat_cur;
261         char *nptr;
262         double val;
263
264         /* jump past the date and CPU number */
265         cur += 16;
266         if (past_eof(trace, cur))
267                 return 1;
268
269         /* usr time */
270         val = strtod(cur, &nptr);
271         if (val == 0 && cur == nptr)
272                 return 1;
273         *user = val;
274
275         /* nice time, pitch this one */
276         cur = nptr;
277         val = strtod(cur, &nptr);
278         if (val == 0 && cur == nptr)
279                 return 1;
280
281         /* system time */
282         cur = nptr;
283         val = strtod(cur, &nptr);
284         if (val == 0 && cur == nptr)
285                 return 1;
286         *sys = val;
287
288         cur = nptr;
289         val = strtod(cur, &nptr);
290         if (val == 0 && cur == nptr)
291                 return 1;
292         *iowait = val;
293
294         cur = nptr;
295         val = strtod(cur, &nptr);
296         if (val == 0 && cur == nptr)
297                 return 1;
298         *irq = val;
299
300         cur = nptr;
301         val = strtod(cur, &nptr);
302         if (val == 0 && cur == nptr)
303                 return 1;
304         *soft = val;
305
306         return 0;
307 }
308
309 int add_mpstat_gld(int time, double sys, struct graph_line_data *gld)
310 {
311         gld->data[time].sum = sys;
312         gld->data[time].count = 1;
313         return 0;
314
315 }