Import version 1.8.3
[s390-tools.git] / ziomon / ziorep_utils.cpp
blob8877c164d33d34c004ae1c648836a5a3a31f0c8e
1 /*
2 * FCP report generators
4 * Utility functions
6 * Copyright IBM Corp. 2008
7 * Author(s): Stefan Raspl <raspl@linux.vnet.ibm.com>
8 */
10 #define __STDC_LIMIT_MACROS
11 #include <stdint.h>
12 #include <assert.h>
13 #include <stdlib.h>
14 #include <limits.h>
16 #include "ziorep_utils.hpp"
17 #include "ziorep_cfgreader.hpp"
19 extern "C" {
20 #include "ziomon_msg_tools.h"
23 extern const char *toolname;
24 extern int verbose;
28 /**
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)
38 FILE *fp;
39 int rc = 0;
40 __u64 begin;
42 if (open_data_files(&fp, filename, f_hdr, agg))
43 return -1;
44 close_data_files(fp);
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
56 // as well
57 if (*agg)
58 begin = (*agg)->end_time;
59 else
60 begin = f_hdr->begin_time;
62 Framer framer(begin,
63 begin + f_hdr->interval_length,
64 f_hdr->interval_length, &type_flt,
65 (DeviceFilter*)NULL, filename, &rc);
66 vector <struct ioerr_cnt*> ioerrs;
67 do {
68 if ( framer.get_next_frameset(frameset) != 0 ) {
69 fprintf(stderr, "%s: Could not read"
70 " any frames in %s%s\n", toolname, filename,
71 DACC_FILE_EXT_LOG);
72 return -2;
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();
81 rc = 0;
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);
89 if (rc)
90 return -1;
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",
97 toolname);
98 return -1;
101 verbose_msg("retrieve initial data FINISHED\n");
103 return 0;
107 int add_all_devices(ConfigReader &cfg, DeviceFilter &dev_filt)
109 int rc = 0;
110 list<__u32> lst;
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));
116 assert(rc == 0);
119 return 0;
123 int get_all_devices(const char *filename, DeviceFilter &dev_filt,
124 ConfigReader &cfg)
126 struct file_header f_hdr;
127 struct aggr_data *agg;
128 int rc = 0;
130 rc = get_initial_data(filename, &f_hdr, &agg, dev_filt, cfg);
131 discard_aggr_data_struct(agg);
132 free(agg);
134 return rc;
138 #define BUF_SZ 256
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));
147 return buf;
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));
158 return buf;
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;
168 if (tmp)
169 time += (interval_length - tmp);
171 return time;
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;
181 if (tmp)
182 time -= tmp;
184 return time;
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
193 * (a) take all of it
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
201 * interval length.
202 * */
203 int adjust_timeframe(const char *filename, __u64 *begin, __u64 *end,
204 __u32 *interval)
206 struct file_header f_hdr;
207 struct aggr_data *agg = NULL;
208 FILE *fp;
209 time_t t;
210 int rc = 0;
212 verbose_msg("adjust timeframe:\n");
214 // check begin and end time
215 if (*begin > *end) {
216 fprintf(stderr, "%s: Start time must be"
217 " prior to end time.\n", toolname);
218 rc = -1;
219 goto out;
222 if (open_data_files(&fp, filename, &f_hdr, &agg)) {
223 rc = -1;
224 goto out;
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));
253 rc = -1;
254 goto out;
256 else {
257 verbose_msg(" begin time: round to nearest boundary\n");
258 *begin = round_lower_boundary(*begin, f_hdr.begin_time, f_hdr.interval_length);
260 t = *begin;
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));
271 rc = -1;
272 goto out;
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));
282 rc = -1;
283 goto out;
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");
292 else {
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");
297 t = *end;
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;
311 t = *end;
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));
322 rc = -1;
323 goto out;
326 out:
327 if (agg) {
328 discard_aggr_data_struct(agg);
329 free(agg);
332 return rc;
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,
340 Printer &printer)
342 int frames_printed = 0;
343 bool first_time = true;
344 time_t t;
345 int rc = 0;
346 Frameset frameset(&col);
347 Framer framer(begin, end, interval,
348 filter_types, &dev_filter,
349 filename, &rc);
351 if (rc)
352 return -1;
354 if (topline && printer.print_csv()) {
355 fprintf(stderr, "%s: Warning: Cannot use '-t' with CSV mode,"
356 " ignoring\n", toolname);
357 topline = 0;
360 verbose_msg("print report for:\n");
361 t = (time_t)begin;
362 verbose_msg(" begin : %s", ctime(&t));
363 t = (time_t)end;
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)) {
372 first_time = false;
373 printer.print_topline(fp);
375 if (printer.print_frame(fp, frameset, dev_filter) < 0)
376 return -1;
377 ++frames_printed;
380 if (rc > 0)
381 return frames_printed;
383 return rc;
387 int print_summary_report(FILE *fp, char *filename, ConfigReader &cfg)
389 int rc = 0;
390 int lrc=0;
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))
396 return -1;
398 rc += fprintf(fp, "Data Summary\n");
399 rc += fprintf(fp, "------------\n");
401 rc += fprintf(fp, "Aggregated range: ");
402 if (a_hdr) {
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));
408 else
409 rc += fprintf(fp, "none\n");
411 discard_aggr_data_struct(a_hdr);
412 free(a_hdr);
413 a_hdr = NULL;
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();
423 int first = 1;
424 const char *frmt;
425 for (list<__u32>::const_iterator i = host_ids.begin();
426 i != host_ids.end(); ++i) {
427 if (first) {
428 frmt = "HBA/CHPID: 0.0.%04x/%x\n";
429 first = 0;
431 else
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));
435 if (lrc)
436 return -1;
439 first = 1;
440 for (list<__u32>::const_iterator i = disks.begin();
441 i != disks.end(); ++i) {
442 if (first) {
443 frmt = "WWPN/LUN (dev): 0x%016Lx/0x%016Lx (%s)\n";
444 first = 0;
446 else
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));
451 if (lrc)
452 return -1;
455 return rc;
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) {
465 __u64 res = 0;
466 res += t->tm_sec;
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;
472 return res;
476 int get_datetime_val(const char *str, __u64 *tgt)
478 struct tm t, t_old;
479 char *ret;
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);
490 return -1;
493 t_old = t;
494 *tgt = mktime(&t);
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));
501 return 0;
505 int parse_topline_arg(char *str, __u64 *arg)
507 char *p;
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);
514 return -1;
516 if (*p != '\0') {
517 fprintf(stderr, "%s: Non-numeric"
518 " characters in argument '%s' to option '-t'. Make"
519 " sure to use only numeric characters.\n", toolname,
520 str);
521 return -1;
524 return 0;
527 FILE* open_csv_output_file(const char *filename, const char *extension,
528 int *rc)
530 char *tmp;
531 FILE *fp = NULL;
533 *rc = 0;
535 tmp = (char*)malloc(strlen(filename) + strlen(extension) + 1);
537 sprintf(tmp, "%s%s", filename, extension);
538 fp = fopen(tmp, "w");
540 if (!fp) {
541 fprintf(stdout, "%s: Could not open file %s. Make sure that you"
542 " have sufficient permissions and try again.\n", toolname, tmp);
543 *rc = -1;
545 else
546 fprintf(stdout, "Exporting data in CSV format to %s\n", tmp);
548 free(tmp);
550 return fp;