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
past_eof(struct trace * trace,char * cur)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
next_mpstat_line(struct trace * trace)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
next_mpstat(struct trace * trace)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
first_mpstat(struct trace * trace)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
find_last_mpstat_time(struct trace * trace)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
guess_mpstat_cpus(struct trace * trace)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
count_mpstat_cpus(struct trace * trace)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
guess_filename(char * trace_name)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
read_mpstat(struct trace * trace,char * trace_name)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 */
read_mpstat_event(struct trace * trace,double * user,double * sys,double * iowait,double * irq,double * soft)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
add_mpstat_gld(int time,double sys,struct graph_line_data * gld)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 }
316