iowatcher: Fix io line graphs at the edge of the X axis
[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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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 char *record_header = "CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest   %idle\n";
42 int record_header_len = 0;
43
44 static int past_eof(struct trace *trace, char *cur)
45 {
46         if (cur >= trace->mpstat_start + trace->mpstat_len)
47                 return 1;
48         return 0;
49 }
50
51 int next_mpstat_line(struct trace *trace)
52 {
53         char *next;
54         char *cur = trace->mpstat_cur;
55
56         next = strchr(cur, '\n');
57         if (!next)
58                 return 1;
59         next++;
60         if (past_eof(trace, next))
61                 return 1;
62         trace->mpstat_cur = next;
63         return 0;
64 }
65
66 char *next_mpstat(struct trace *trace)
67 {
68         char *cur = trace->mpstat_cur;
69
70         cur = strstr(cur, record_header);
71         if (!cur)
72                 return NULL;
73
74         cur += record_header_len;
75         if (past_eof(trace, cur))
76                 return NULL;
77         trace->mpstat_cur = cur;
78         return cur;
79 }
80
81 char *first_mpstat(struct trace *trace)
82 {
83         char *cur = trace->mpstat_cur;
84
85         trace->mpstat_cur = trace->mpstat_start;
86
87         cur = next_mpstat(trace);
88         if (!cur)
89                 return NULL;
90         return cur;
91 }
92
93 static void find_last_mpstat_time(struct trace *trace)
94 {
95         int num_mpstats = 0;
96         char *cur;
97
98         first_mpstat(trace);
99
100         cur = first_mpstat(trace);
101         while (cur) {
102                 num_mpstats++;
103                 cur = next_mpstat(trace);
104         }
105         first_mpstat(trace);
106         trace->mpstat_seconds = num_mpstats;
107 }
108
109 static int count_mpstat_cpus(struct trace *trace)
110 {
111         char *cur = trace->mpstat_start;
112         char *cpu;
113         char *record;
114         int len;
115         char *line;
116
117         first_mpstat(trace);
118
119         cpu = strstr(cur, " CPU)");
120         if (!cpu)
121                 return 0;
122         line = strndup(cur, cpu - cur);
123
124         record = strrchr(line, '(');
125         if (!record) {
126                 free(line);
127                 return 0;
128         }
129         record++;
130
131         len = line + strlen(line) - record;
132
133         cur = strndup(record, len);
134         trace->mpstat_num_cpus = atoi(cur);
135         first_mpstat(trace);
136         free(line);
137         return trace->mpstat_num_cpus;
138 }
139
140 static char *guess_filename(char *trace_name)
141 {
142         struct stat st;
143         int ret;
144         char *cur;
145         char *tmp;
146
147         snprintf(line, line_len, "%s.mpstat", trace_name);
148         ret = stat(line, &st);
149         if (ret == 0)
150                 return trace_name;
151
152         cur = strrchr(trace_name, '.');
153         if (!cur) {
154                 return trace_name;
155         }
156
157         tmp = strndup(trace_name, cur - trace_name);
158         snprintf(line, line_len, "%s.mpstat", tmp);
159         ret = stat(line, &st);
160         if (ret == 0)
161                 return tmp;
162
163         free(tmp);
164         return trace_name;
165 }
166
167 int read_mpstat(struct trace *trace, char *trace_name)
168 {
169         int fd;
170         struct stat st;
171         int ret;
172         char *p;
173
174         if (record_header_len == 0) {
175                 record_header_len = strlen(record_header);
176         }
177
178         snprintf(line, line_len, "%s.mpstat", guess_filename(trace_name));
179
180         fd = open(line, O_RDONLY);
181         if (fd < 0)
182                 return 0;
183
184         ret = fstat(fd, &st);
185         if (ret < 0) {
186                 fprintf(stderr, "stat failed on %s err %s\n", line, strerror(errno));
187                 goto fail_fd;
188         }
189         p = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
190         if (p == MAP_FAILED) {
191                 fprintf(stderr, "Unable to mmap trace file %s, err %s\n", line, strerror(errno));
192                 goto fail_fd;
193         }
194
195         trace->mpstat_start = p;
196         trace->mpstat_len = st.st_size;
197         trace->mpstat_cur = p;
198         trace->mpstat_fd = fd;
199         find_last_mpstat_time(trace);
200         count_mpstat_cpus(trace);
201
202         first_mpstat(trace);
203
204         return 0;
205
206 fail_fd:
207         close(fd);
208         return 0;
209 }
210
211 /*
212  * 09:56:26 AM  CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest   %idle
213  *
214  * this reads just one line in the mpstat
215  */
216 int read_mpstat_event(struct trace *trace, double *user,
217                       double *sys, double *iowait, double *irq,
218                       double *soft)
219 {
220         char *cur = trace->mpstat_cur;
221         char *nptr;
222         double val;
223
224         /* jump past the date and CPU number */
225         cur += 16;
226         if (past_eof(trace, cur))
227                 return 1;
228
229         /* usr time */
230         val = strtod(cur, &nptr);
231         if (val == 0 && cur == nptr)
232                 return 1;
233         *user = val;
234
235         /* nice time, pitch this one */
236         cur = nptr;
237         val = strtod(cur, &nptr);
238         if (val == 0 && cur == nptr)
239                 return 1;
240
241         /* system time */
242         cur = nptr;
243         val = strtod(cur, &nptr);
244         if (val == 0 && cur == nptr)
245                 return 1;
246         *sys = val;
247
248         cur = nptr;
249         val = strtod(cur, &nptr);
250         if (val == 0 && cur == nptr)
251                 return 1;
252         *iowait = val;
253
254         cur = nptr;
255         val = strtod(cur, &nptr);
256         if (val == 0 && cur == nptr)
257                 return 1;
258         *irq = val;
259
260         cur = nptr;
261         val = strtod(cur, &nptr);
262         if (val == 0 && cur == nptr)
263                 return 1;
264         *soft = val;
265
266         return 0;
267 }
268
269 int add_mpstat_gld(int time, double sys, struct graph_line_data *gld)
270 {
271         gld->data[time].sum = sys;
272         gld->data[time].count = 1;
273         return 0;
274
275 }