2 * FCP report generators
6 * Copyright IBM Corp. 2008
7 * Author(s): Stefan Raspl <raspl@linux.vnet.ibm.com>
10 #define __STDC_LIMIT_MACROS
16 #include "ziorep_utils.hpp"
17 #include "ziorep_cfgreader.hpp"
20 #include "ziomon_msg_tools.h"
23 extern const char *toolname
;
29 * Read all essential data from the files,
30 * including the headers, timestamp of first message
31 * and a DeviceFilter for all available devices.
32 * Note that 'agg' is NULL if not available and must
33 * be free()'d otherwise. */
34 static int get_initial_data(const char *filename
, struct file_header
*f_hdr
,
35 struct aggr_data
**agg
,
36 DeviceFilter
&dev_filt
, ConfigReader
&cfg
)
42 if (open_data_files(&fp
, filename
, f_hdr
, agg
))
47 * Retrieve first real frame
49 MsgTypeFilter msgtype_filter
;
50 NoopCollapser nop_col
;
51 Frameset
frameset(&nop_col
);
52 list
<MsgTypes
> type_flt
;
53 type_flt
.push_back(ioerr
);
55 // we retrieve the first interval only, and eventually the .agg data
58 begin
= (*agg
)->end_time
;
60 begin
= f_hdr
->begin_time
;
63 begin
+ f_hdr
->interval_length
,
64 f_hdr
->interval_length
, &type_flt
,
65 (DeviceFilter
*)NULL
, filename
, &rc
);
66 vector
<struct ioerr_cnt
*> ioerrs
;
68 if ( framer
.get_next_frameset(frameset
) != 0 ) {
69 fprintf(stderr
, "%s: Could not read"
70 " any frames in %s%s\n", toolname
, filename
,
76 * Construct a DeviceFilter with all devices.
77 * NOTE: The very first ioerr msg might already have been moved to the .agg
78 * file - hence we have to consider the .agg data as well!
80 ioerrs
= frameset
.get_ioerr_stats();
82 for (vector
<struct ioerr_cnt
*>::const_iterator i
= ioerrs
.begin();
83 i
!= ioerrs
.end(); ++i
) {
84 vverbose_msg(" add device: hctl=[%d:%d:%d:%d], mm=%d\n",
85 (*i
)->identifier
.host
, (*i
)->identifier
.channel
,
86 (*i
)->identifier
.target
, (*i
)->identifier
.lun
,
87 cfg
.get_mm_by_ident(&(*i
)->identifier
, &rc
));
88 dev_filt
.add_device(cfg
.get_mm_by_ident(&(*i
)->identifier
, &rc
), &(*i
)->identifier
);
92 } while ( frameset
.is_aggregated() && !ioerrs
.size());
94 if (dev_filt
.get_host_id_list().size() == 0 || dev_filt
.get_mm_list().size() == 0) {
95 fprintf(stderr
, "%s: Could not retrieve initial data"
96 " - data files corrupted or broken, or the .agg file is missing.\n",
101 verbose_msg("retrieve initial data FINISHED\n");
107 int add_all_devices(ConfigReader
&cfg
, DeviceFilter
&dev_filt
)
112 cfg
.get_unique_mms(lst
);
113 for (list
<__u32
>::const_iterator i
= lst
.begin();
114 i
!= lst
.end(); ++i
) {
115 dev_filt
.add_device(*i
, cfg
.get_ident_by_mm_internal(*i
, &rc
));
123 int get_all_devices(const char *filename
, DeviceFilter
&dev_filt
,
126 struct file_header f_hdr
;
127 struct aggr_data
*agg
;
130 rc
= get_initial_data(filename
, &f_hdr
, &agg
, dev_filt
, cfg
);
131 discard_aggr_data_struct(agg
);
140 const char* print_time_formatted(__u64 timestamp
)
142 time_t t
= timestamp
;
143 static char buf
[BUF_SZ
];
145 strftime(buf
, BUF_SZ
- 1, "%Y-%m-%d %H:%M:%S", localtime(&t
));
151 const char* print_time_formatted_short(__u64 timestamp
)
153 time_t t
= timestamp
;
154 static char buf
[BUF_SZ
];
156 strftime(buf
, BUF_SZ
- 1, "%H:%M:%S", localtime(&t
));
162 // adjust lower timestamp to last frame boundary that was touched
163 static __u64
round_lower_boundary(__u64 time
, __u64 begin
,
164 __u32 interval_length
)
166 __u64 tmp
= (time
- begin
)%interval_length
;
169 time
+= (interval_length
- tmp
);
175 // adjust upper timestamp to last frame boundary that was touched
176 static __u64
round_upper_boundary(__u64 time
, __u64 begin
,
177 __u32 interval_length
)
179 __u64 tmp
= (time
- begin
)%interval_length
;
189 * The times will be adjusted to respective exakt frame boundaries.
190 * The end time is only ever used to determine whether we are done - we stop
191 * in case the begin of the timeframe is later than the end date.
192 * Note that in case we scratch into the .agg data, we
194 * (b) synonymously associate the .agg data with the timestamp of its
195 * final frame. This is _utterly_ important, especially in context
196 * with the interval: If it is user adjusted, and the date range touches
197 * the .agg data, we adjust the begin to the .agg data's final frame's
198 * timestamp, and make sure that when we process the agg data, the next
199 * frame is 1 unit of the original interval length away. Only after we
200 * have shifted into the 'regular' .log data can we apply any user-set
203 int adjust_timeframe(const char *filename
, __u64
*begin
, __u64
*end
,
206 struct file_header f_hdr
;
207 struct aggr_data
*agg
= NULL
;
212 verbose_msg("adjust timeframe:\n");
214 // check begin and end time
216 fprintf(stderr
, "%s: Start time must be"
217 " prior to end time.\n", toolname
);
222 if (open_data_files(&fp
, filename
, &f_hdr
, &agg
)) {
226 close_data_files(fp
);
228 // check if begin scratches into .agg data
229 // if so, we use the _end_ time of the .agg frame
230 if (*begin
== 0 && agg
) {
231 verbose_msg(" begin time: take from .agg data\n");
232 *begin
= agg
->end_time
;
234 else if (agg
&& *begin
< agg
->begin_time
) {
235 fprintf(stderr
, "%s: Warning: Begin of timeframe is before"
236 " earliest available data, which is %s.\n", toolname
,
237 print_time_formatted(agg
->begin_time
));
238 *begin
= agg
->end_time
;
239 verbose_msg(" begin time: adjust to begin from .agg data\n");
241 else if (agg
&& *begin
<= agg
->end_time
) {
242 *begin
= agg
->end_time
;
243 verbose_msg(" begin time: set to end of .agg data\n");
245 else if (*begin
< f_hdr
.begin_time
) {
246 *begin
= f_hdr
.begin_time
;
247 verbose_msg(" begin time: adjust to begin of .log data\n");
249 else if (*begin
> f_hdr
.end_time
) {
250 fprintf(stderr
, "%s: Begin of timeframe is past the"
251 " end of available data, which is %s.\n",
252 toolname
, print_time_formatted(f_hdr
.end_time
));
257 verbose_msg(" begin time: round to nearest boundary\n");
258 *begin
= round_lower_boundary(*begin
, f_hdr
.begin_time
, f_hdr
.interval_length
);
261 verbose_msg(" begin time set to: %s", ctime(&t
));
263 if (*end
== UINT64_MAX
) {
264 *end
= f_hdr
.end_time
;
265 verbose_msg(" end time : take from .log data\n");
267 else if (agg
&& *end
< agg
->begin_time
) {
268 fprintf(stderr
, "%s: End of timeframe is prior to earliest"
269 " available data, which is %s.\n", toolname
,
270 print_time_formatted(agg
->begin_time
));
274 else if (agg
&& *end
< agg
->end_time
) {
275 *end
= agg
->end_time
;
276 verbose_msg(" end time : take from .agg data\n");
278 else if (!agg
&& *end
< f_hdr
.begin_time
) {
279 fprintf(stderr
, "%s: End of timeframe is prior to earliest"
280 " available data, which is %s.\n", toolname
,
281 print_time_formatted(f_hdr
.begin_time
));
285 else if (*end
> f_hdr
.end_time
) {
286 fprintf(stderr
, "%s: Warning: End of timeframe is after"
287 " latest available data, which is %s.\n", toolname
,
288 print_time_formatted(f_hdr
.end_time
));
289 *end
= f_hdr
.end_time
;
290 verbose_msg(" end time : adjust to end of .log data\n");
293 *end
= round_upper_boundary(*end
, f_hdr
.begin_time
,
294 f_hdr
.interval_length
);
295 verbose_msg(" end time : round to nearest boundary\n");
298 verbose_msg(" end time set to : %s", ctime(&t
));
299 assert((*end
- *begin
) % f_hdr
.interval_length
== 0);
301 if (*interval
== UINT32_MAX
) {
302 *interval
= f_hdr
.interval_length
;
303 verbose_msg("using original interval length: %lus\n",
304 (long unsigned int)*interval
);
306 /* the exact frame boundaries don't include the length of the very
307 first interval, so we have to add one more to our calculations */
308 if (*interval
&& (*end
- *begin
+ f_hdr
.interval_length
) % *interval
!= 0) {
309 // cut off rest in case of user-set interval
310 *end
-= (*end
- *begin
) % *interval
+ f_hdr
.interval_length
;
312 verbose_msg(" cut off at : %s", ctime(&t
));
315 // check if the interval is correct
316 if (*interval
% f_hdr
.interval_length
) {
317 fprintf(stderr
, "%s: Data aggregation interval %lu"
318 " is incompatible with source data. Please use"
319 " a multiple of %lu and try again.\n", toolname
,
320 (long unsigned int)*interval
,
321 (long unsigned int)(f_hdr
.interval_length
));
328 discard_aggr_data_struct(agg
);
336 int print_report(FILE *fp
, __u64 begin
, __u64 end
, __u32 interval
,
337 char *filename
, __u64 topline
,
338 list
<MsgTypes
> *filter_types
,
339 DeviceFilter
&dev_filter
, Collapser
&col
,
342 int frames_printed
= 0;
343 bool first_time
= true;
346 Frameset
frameset(&col
);
347 Framer
framer(begin
, end
, interval
,
348 filter_types
, &dev_filter
,
354 if (topline
&& printer
.print_csv()) {
355 fprintf(stderr
, "%s: Warning: Cannot use '-t' with CSV mode,"
356 " ignoring\n", toolname
);
360 verbose_msg("print report for:\n");
362 verbose_msg(" begin : %s", ctime(&t
));
364 verbose_msg(" end : %s", (end
== UINT64_MAX
? "-\n" : ctime(&t
)));
365 verbose_msg(" interval : %lu\n", (long unsigned int)interval
);
366 verbose_msg(" topline : %llu\n", (long long unsigned int)topline
);
367 verbose_msg(" csv mode : %d\n", printer
.print_csv());
369 while ( (rc
= framer
.get_next_frameset(frameset
, true)) == 0 ) {
370 vverbose_msg("printing frameset %d\n", frames_printed
);
371 if (first_time
|| (topline
&& frames_printed
% topline
== 0)) {
373 printer
.print_topline(fp
);
375 if (printer
.print_frame(fp
, frameset
, dev_filter
) < 0)
381 return frames_printed
;
387 int print_summary_report(FILE *fp
, char *filename
, ConfigReader
&cfg
)
391 struct file_header f_hdr
;
392 struct aggr_data
*a_hdr
;
393 DeviceFilter dev_filt
;
395 if (get_initial_data(filename
, &f_hdr
, &a_hdr
, dev_filt
, cfg
))
398 rc
+= fprintf(fp
, "Data Summary\n");
399 rc
+= fprintf(fp
, "------------\n");
401 rc
+= fprintf(fp
, "Aggregated range: ");
403 rc
+= fprintf(fp
, "%s to ",
404 print_time_formatted(a_hdr
->begin_time
- f_hdr
.interval_length
));
405 rc
+= fprintf(fp
, "%s\n",
406 print_time_formatted(a_hdr
->end_time
));
409 rc
+= fprintf(fp
, "none\n");
411 discard_aggr_data_struct(a_hdr
);
415 rc
+= fprintf(fp
, "Detailed range: %s to ",
416 print_time_formatted(f_hdr
.begin_time
- f_hdr
.interval_length
));
417 rc
+= fprintf(fp
, "%s\n", print_time_formatted(f_hdr
.end_time
));
418 rc
+= fprintf(fp
, "Interval length: %d seconds\n",
419 f_hdr
.interval_length
);
421 list
<__u32
> disks
= dev_filt
.get_mm_list();
422 list
<__u32
> host_ids
= dev_filt
.get_host_id_list();
425 for (list
<__u32
>::const_iterator i
= host_ids
.begin();
426 i
!= host_ids
.end(); ++i
) {
428 frmt
= "HBA/CHPID: 0.0.%04x/%x\n";
432 frmt
= " 0.0.%04x/%x\n";
433 rc
+= fprintf(fp
, frmt
, cfg
.get_devno_by_host_id(*i
, &lrc
),
434 cfg
.get_chpid_by_host_id(*i
, &lrc
));
440 for (list
<__u32
>::const_iterator i
= disks
.begin();
441 i
!= disks
.end(); ++i
) {
443 frmt
= "WWPN/LUN (dev): 0x%016Lx/0x%016Lx (%s)\n";
447 frmt
= " 0x%016Lx/0x%016Lx (%s)\n";
448 rc
+= fprintf(fp
, frmt
, cfg
.get_wwpn_by_mm_internal(*i
, &lrc
),
449 cfg
.get_lun_by_mm_internal(*i
, &lrc
),
450 cfg
.get_dev_by_mm_internal(*i
, &lrc
));
458 /* Calculates seconds since 1970 _without_ caring for daylight
459 savings time (comtrary to mktime() et al).
460 It does not care for leap years and the like, which is OK,
461 since we use it in a very narrow scenario: To calculate any
462 daylight savings time related shifts.
463 Hence: Dont't use if you're not sure what you are doing... */
464 static __u64
secs_since_1970(const struct tm
*t
) {
467 res
+= 60 * t
->tm_min
;
468 res
+= 3600 * t
->tm_hour
;
469 res
+= 86400 * t
->tm_yday
;
470 res
+= 86400 * 365 * t
->tm_year
;
476 int get_datetime_val(const char *str
, __u64
*tgt
)
481 // strptime only sets
482 memset(&t
, 0, sizeof(struct tm
));
483 ret
= strptime(str
, "%Y-%m-%d %H:%M", &t
);
484 if (ret
== NULL
|| *ret
!= '\0') {
485 ret
= strptime(str
, "%Y-%m-%d %H:%M:%S", &t
);
486 if (ret
== NULL
|| *ret
!= '\0') {
487 fprintf(stderr
, "%s: Could not parse date %s."
488 " Please use format as specified in"
489 " man-page\n", toolname
, str
);
495 // if daylight savings time applies, 't' has been adjusted,
496 // so we have to correct
497 if (t_old
.tm_hour
!= t
.tm_hour
)
498 *tgt
-= secs_since_1970(&t
) - secs_since_1970(&t_old
);
499 verbose_msg("datetime value from user after translation: %s", ctime((const time_t *)tgt
));
505 int parse_topline_arg(char *str
, __u64
*arg
)
509 *arg
= strtoull(str
, &p
, 0);
510 if (*arg
== ULLONG_MAX
) {
511 fprintf(stderr
, "%s: Cannot convert"
512 " %s, over/underflow occured. Try a smaller/larger"
513 " value.\n", toolname
, str
);
517 fprintf(stderr
, "%s: Non-numeric"
518 " characters in argument '%s' to option '-t'. Make"
519 " sure to use only numeric characters.\n", toolname
,
527 FILE* open_csv_output_file(const char *filename
, const char *extension
,
535 tmp
= (char*)malloc(strlen(filename
) + strlen(extension
) + 1);
537 sprintf(tmp
, "%s%s", filename
, extension
);
538 fp
= fopen(tmp
, "w");
541 fprintf(stdout
, "%s: Could not open file %s. Make sure that you"
542 " have sufficient permissions and try again.\n", toolname
, tmp
);
546 fprintf(stdout
, "Exporting data in CSV format to %s\n", tmp
);