iowatcher: Add bounds checking in find_step
[blktrace.git] / iowatcher / mpstat.c
CommitLineData
fd70186d
CM
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
660b0411 15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
fd70186d
CM
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
38char line[1024];
39int line_len = 1024;
40
2203e914
CM
41static char record_header[] = "CPU %usr %nice %sys %iowait %irq %soft %steal %guest %idle\n";
42static char record_header_v2[] = "CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle\n";
43
44int record_header_len = sizeof(record_header);
45int record_header_v2_len = sizeof(record_header_v2);
fd70186d
CM
46
47static 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
54int 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
69char *next_mpstat(struct trace *trace)
70{
d7d5d7c3 71 char *cur;
fd70186d 72
2203e914
CM
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 }
fd70186d
CM
81 if (!cur)
82 return NULL;
83
fd70186d
CM
84 if (past_eof(trace, cur))
85 return NULL;
86 trace->mpstat_cur = cur;
87 return cur;
88}
89
90char *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
102static 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
d7d5d7c3
CM
118static 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
fd70186d
CM
146static int count_mpstat_cpus(struct trace *trace)
147{
148 char *cur = trace->mpstat_start;
149 char *cpu;
150 char *record;
2203e914 151 int len; char *line;
fd70186d
CM
152
153 first_mpstat(trace);
fd70186d
CM
154 cpu = strstr(cur, " CPU)");
155 if (!cpu)
d7d5d7c3
CM
156 return guess_mpstat_cpus(trace);
157
fd70186d
CM
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);
2203e914 173
fd70186d
CM
174 return trace->mpstat_num_cpus;
175}
176
177static 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
204int 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));
fd70186d
CM
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 }
fd70186d
CM
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
241fail_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 *
2203e914
CM
249 * or
250 *
251 * 10:18:51 AM CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle
252 *
253 *
fd70186d
CM
254 * this reads just one line in the mpstat
255 */
256int 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
309int 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}