Files
s390-tools/ziomon/ziorep_utils.cpp
Michael Holzheu b627b8d8e1 Initial s390-tools-2.0.0 import
This commit is based on the s390-tools-1.39.0 version.

Changes on top of s390-tools-1.39.0:

 - Add MIT license to all source files
 - Add LICENSE file
 - Transform REAMDE to README.md (markdown)
 - Add AUTHORS.md file
 - Add CONTRIBUTING.md file
 - Move changelog from README to CHANGELOG.md file

Reviewed-by: Stefan Haberland <sth@linux.vnet.ibm.com>
Signed-off-by: Michael Holzheu <holzheu@linux.vnet.ibm.com>
2017-08-21 10:55:40 +02:00

558 lines
14 KiB
C++

/*
* FCP report generators
*
* Utility functions
*
* Copyright IBM Corp. 2008, 2017
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#define __STDC_LIMIT_MACROS
#include <stdint.h>
#include <assert.h>
#include <stdlib.h>
#include <limits.h>
#include "ziorep_utils.hpp"
#include "ziorep_cfgreader.hpp"
extern "C" {
#include "ziomon_msg_tools.h"
}
extern const char *toolname;
extern int verbose;
/**
* Read all essential data from the files,
* including the headers, timestamp of first message
* and a DeviceFilter for all available devices.
* Note that 'agg' is NULL if not available and must
* be free()'d otherwise. */
static int get_initial_data(const char *filename, struct file_header *f_hdr,
struct aggr_data **agg,
DeviceFilter &dev_filt, ConfigReader &cfg)
{
FILE *fp;
int rc = 0;
__u64 begin;
if (open_data_files(&fp, filename, f_hdr, agg))
return -1;
close_data_files(fp);
/*
* Retrieve first real frame
*/
MsgTypeFilter msgtype_filter;
NoopCollapser nop_col;
Frameset frameset(&nop_col);
list<MsgTypes> type_flt;
type_flt.push_back(ioerr);
// we retrieve the first interval only, and eventually the .agg data
// as well
if (*agg)
begin = (*agg)->end_time;
else
begin = f_hdr->begin_time;
Framer framer(begin,
begin + f_hdr->interval_length,
f_hdr->interval_length, &type_flt,
(DeviceFilter*)NULL, filename, &rc);
vector <struct ioerr_cnt*> ioerrs;
do {
if ( framer.get_next_frameset(frameset) != 0 ) {
fprintf(stderr, "%s: Could not read"
" any frames in %s%s\n", toolname, filename,
DACC_FILE_EXT_LOG);
return -2;
}
/*
* Construct a DeviceFilter with all devices.
* NOTE: The very first ioerr msg might already have been moved to the .agg
* file - hence we have to consider the .agg data as well!
*/
ioerrs = frameset.get_ioerr_stats();
rc = 0;
for (vector<struct ioerr_cnt*>::const_iterator i = ioerrs.begin();
i != ioerrs.end(); ++i) {
vverbose_msg(" add device: hctl=[%d:%d:%d:%d], mm=%d\n",
(*i)->identifier.host, (*i)->identifier.channel,
(*i)->identifier.target, (*i)->identifier.lun,
cfg.get_mm_by_ident(&(*i)->identifier, &rc));
dev_filt.add_device(cfg.get_mm_by_ident(&(*i)->identifier, &rc), &(*i)->identifier);
if (rc)
return -1;
}
} while ( frameset.is_aggregated() && !ioerrs.size());
if (dev_filt.get_host_id_list().size() == 0 || dev_filt.get_mm_list().size() == 0) {
fprintf(stderr, "%s: Could not retrieve initial data"
" - data files corrupted or broken, or the .agg file is missing.\n",
toolname);
return -1;
}
verbose_msg("retrieve initial data FINISHED\n");
return 0;
}
int add_all_devices(ConfigReader &cfg, DeviceFilter &dev_filt)
{
int rc = 0;
list<__u32> lst;
cfg.get_unique_mms(lst);
for (list<__u32>::const_iterator i = lst.begin();
i != lst.end(); ++i) {
dev_filt.add_device(*i, cfg.get_ident_by_mm_internal(*i, &rc));
assert(rc == 0);
}
return 0;
}
int get_all_devices(const char *filename, DeviceFilter &dev_filt,
ConfigReader &cfg)
{
struct file_header f_hdr;
struct aggr_data *agg;
int rc = 0;
rc = get_initial_data(filename, &f_hdr, &agg, dev_filt, cfg);
discard_aggr_data_struct(agg);
free(agg);
return rc;
}
#define BUF_SZ 256
const char* print_time_formatted(__u64 timestamp)
{
time_t t = timestamp;
static char buf[BUF_SZ];
strftime(buf, BUF_SZ - 1, "%Y-%m-%d %H:%M:%S", localtime(&t));
return buf;
}
const char* print_time_formatted_short(__u64 timestamp)
{
time_t t = timestamp;
static char buf[BUF_SZ];
strftime(buf, BUF_SZ - 1, "%H:%M:%S", localtime(&t));
return buf;
}
// adjust lower timestamp to last frame boundary that was touched
static __u64 round_lower_boundary(__u64 time, __u64 begin,
__u32 interval_length)
{
__u64 tmp = (time - begin)%interval_length;
if (tmp)
time += (interval_length - tmp);
return time;
}
// adjust upper timestamp to last frame boundary that was touched
static __u64 round_upper_boundary(__u64 time, __u64 begin,
__u32 interval_length)
{
__u64 tmp = (time - begin)%interval_length;
if (tmp)
time -= tmp;
return time;
}
/**
* The times will be adjusted to respective exakt frame boundaries.
* The end time is only ever used to determine whether we are done - we stop
* in case the begin of the timeframe is later than the end date.
* Note that in case we scratch into the .agg data, we
* (a) take all of it
* (b) synonymously associate the .agg data with the timestamp of its
* final frame. This is _utterly_ important, especially in context
* with the interval: If it is user adjusted, and the date range touches
* the .agg data, we adjust the begin to the .agg data's final frame's
* timestamp, and make sure that when we process the agg data, the next
* frame is 1 unit of the original interval length away. Only after we
* have shifted into the 'regular' .log data can we apply any user-set
* interval length.
* */
int adjust_timeframe(const char *filename, __u64 *begin, __u64 *end,
__u32 *interval)
{
struct file_header f_hdr;
struct aggr_data *agg = NULL;
FILE *fp;
time_t t;
int rc = 0;
verbose_msg("adjust timeframe:\n");
// check begin and end time
if (*begin > *end) {
fprintf(stderr, "%s: Start time must be"
" prior to end time.\n", toolname);
rc = -1;
goto out;
}
if (open_data_files(&fp, filename, &f_hdr, &agg)) {
rc = -1;
goto out;
}
close_data_files(fp);
// check if begin scratches into .agg data
// if so, we use the _end_ time of the .agg frame
if (*begin == 0 && agg) {
verbose_msg(" begin time: take from .agg data\n");
*begin = agg->end_time;
}
else if (agg && *begin < agg->begin_time) {
fprintf(stderr, "%s: Warning: Begin of timeframe is before"
" earliest available data, which is %s.\n", toolname,
print_time_formatted(agg->begin_time));
*begin = agg->end_time;
verbose_msg(" begin time: adjust to begin from .agg data\n");
}
else if (agg && *begin <= agg->end_time) {
*begin = agg->end_time;
verbose_msg(" begin time: set to end of .agg data\n");
}
else if (*begin < f_hdr.begin_time) {
*begin = f_hdr.begin_time;
verbose_msg(" begin time: adjust to begin of .log data\n");
}
else if (*begin > f_hdr.end_time) {
fprintf(stderr, "%s: Begin of timeframe is past the"
" end of available data, which is %s.\n",
toolname, print_time_formatted(f_hdr.end_time));
rc = -1;
goto out;
}
else {
verbose_msg(" begin time: round to nearest boundary\n");
*begin = round_lower_boundary(*begin, f_hdr.begin_time, f_hdr.interval_length);
}
t = *begin;
verbose_msg(" begin time set to: %s", ctime(&t));
if (*end == UINT64_MAX) {
*end = f_hdr.end_time;
verbose_msg(" end time : take from .log data\n");
}
else if (agg && *end < agg->begin_time) {
fprintf(stderr, "%s: End of timeframe is prior to earliest"
" available data, which is %s.\n", toolname,
print_time_formatted(agg->begin_time));
rc = -1;
goto out;
}
else if (agg && *end < agg->end_time) {
*end = agg->end_time;
verbose_msg(" end time : take from .agg data\n");
}
else if (!agg && *end < f_hdr.begin_time) {
fprintf(stderr, "%s: End of timeframe is prior to earliest"
" available data, which is %s.\n", toolname,
print_time_formatted(f_hdr.begin_time));
rc = -1;
goto out;
}
else if (*end > f_hdr.end_time) {
fprintf(stderr, "%s: Warning: End of timeframe is after"
" latest available data, which is %s.\n", toolname,
print_time_formatted(f_hdr.end_time));
*end = f_hdr.end_time;
verbose_msg(" end time : adjust to end of .log data\n");
}
else {
*end = round_upper_boundary(*end, f_hdr.begin_time,
f_hdr.interval_length);
verbose_msg(" end time : round to nearest boundary\n");
}
t = *end;
verbose_msg(" end time set to : %s", ctime(&t));
assert((*end - *begin) % f_hdr.interval_length == 0);
if (*interval == UINT32_MAX) {
*interval = f_hdr.interval_length;
verbose_msg("using original interval length: %lus\n",
(long unsigned int)*interval);
}
/* the exact frame boundaries don't include the length of the very
first interval, so we have to add one more to our calculations */
if (*interval && (*end - *begin + f_hdr.interval_length) % *interval != 0) {
// cut off rest in case of user-set interval
*end -= (*end - *begin) % *interval + f_hdr.interval_length;
t = *end;
verbose_msg(" cut off at : %s", ctime(&t));
}
// check if the interval is correct
if (*interval % f_hdr.interval_length) {
fprintf(stderr, "%s: Data aggregation interval %lu"
" is incompatible with source data. Please use"
" a multiple of %lu and try again.\n", toolname,
(long unsigned int)*interval,
(long unsigned int)(f_hdr.interval_length));
rc = -1;
goto out;
}
out:
if (agg) {
discard_aggr_data_struct(agg);
free(agg);
}
return rc;
}
int print_report(FILE *fp, __u64 begin, __u64 end, __u32 interval,
char *filename, __u64 topline,
list<MsgTypes> *filter_types,
DeviceFilter &dev_filter, Collapser &col,
Printer &printer)
{
int frames_printed = 0;
bool first_time = true;
time_t t;
int rc = 0;
Frameset frameset(&col);
Framer framer(begin, end, interval,
filter_types, &dev_filter,
filename, &rc);
if (rc)
return -1;
if (topline && printer.print_csv()) {
fprintf(stderr, "%s: Warning: Cannot use '-t' with CSV mode,"
" ignoring\n", toolname);
topline = 0;
}
verbose_msg("print report for:\n");
t = (time_t)begin;
verbose_msg(" begin : %s", ctime(&t));
t = (time_t)end;
verbose_msg(" end : %s", (end == UINT64_MAX ? "-\n" : ctime(&t)));
verbose_msg(" interval : %lu\n", (long unsigned int)interval);
verbose_msg(" topline : %llu\n", (long long unsigned int)topline);
verbose_msg(" csv mode : %d\n", printer.print_csv());
while ( (rc = framer.get_next_frameset(frameset, true)) == 0 ) {
vverbose_msg("printing frameset %d\n", frames_printed);
if (first_time || (topline && frames_printed % topline == 0)) {
first_time = false;
printer.print_topline(fp);
}
if (printer.print_frame(fp, frameset, dev_filter) < 0)
return -1;
++frames_printed;
}
if (rc > 0)
return frames_printed;
return rc;
}
int print_summary_report(FILE *fp, char *filename, ConfigReader &cfg)
{
int rc = 0;
int lrc=0;
struct file_header f_hdr;
struct aggr_data *a_hdr;
DeviceFilter dev_filt;
if (get_initial_data(filename, &f_hdr, &a_hdr, dev_filt, cfg))
return -1;
rc += fprintf(fp, "Data Summary\n");
rc += fprintf(fp, "------------\n");
rc += fprintf(fp, "Aggregated range: ");
if (a_hdr) {
rc += fprintf(fp, "%s to ",
print_time_formatted(a_hdr->begin_time - f_hdr.interval_length));
rc += fprintf(fp, "%s\n",
print_time_formatted(a_hdr->end_time));
}
else
rc += fprintf(fp, "none\n");
discard_aggr_data_struct(a_hdr);
free(a_hdr);
a_hdr = NULL;
rc += fprintf(fp, "Detailed range: %s to ",
print_time_formatted(f_hdr.begin_time - f_hdr.interval_length));
rc += fprintf(fp, "%s\n", print_time_formatted(f_hdr.end_time));
rc += fprintf(fp, "Interval length: %d seconds\n",
f_hdr.interval_length);
list<__u32> disks = dev_filt.get_mm_list();
list<__u32> host_ids = dev_filt.get_host_id_list();
int first = 1;
const char *frmt;
for (list<__u32>::const_iterator i = host_ids.begin();
i != host_ids.end(); ++i) {
if (first) {
frmt = "HBA/CHPID: %x.%x.%04x/%x\n";
first = 0;
}
else
frmt = " %x.%x.%04x/%x\n";
rc += fprintf(fp, frmt, ZIOREP_BUSID_UNPACKED(cfg.get_devno_by_host_id(*i, &lrc)),
cfg.get_chpid_by_host_id(*i, &lrc));
if (lrc)
return -1;
}
first = 1;
for (list<__u32>::const_iterator i = disks.begin();
i != disks.end(); ++i) {
if (first) {
frmt = "WWPN/LUN (dev): 0x%016Lx/0x%016Lx (%s)\n";
first = 0;
}
else
frmt = " 0x%016Lx/0x%016Lx (%s)\n";
rc += fprintf(fp, frmt, cfg.get_wwpn_by_mm_internal(*i, &lrc),
cfg.get_lun_by_mm_internal(*i, &lrc),
cfg.get_dev_by_mm_internal(*i, &lrc));
if (lrc)
return -1;
}
return rc;
}
/* Calculates seconds since 1970 _without_ caring for daylight
savings time (comtrary to mktime() et al).
It does not care for leap years and the like, which is OK,
since we use it in a very narrow scenario: To calculate any
daylight savings time related shifts.
Hence: Dont't use if you're not sure what you are doing... */
static __u64 secs_since_1970(const struct tm *t) {
__u64 res = 0;
res += t->tm_sec;
res += 60 * t->tm_min;
res += 3600 * t->tm_hour;
res += 86400 * t->tm_yday;
res += 86400 * 365 * t->tm_year;
return res;
}
int get_datetime_val(const char *str, __u64 *tgt)
{
struct tm t, t_old;
char *ret;
// strptime only sets
memset(&t, 0, sizeof(struct tm));
ret = strptime(str, "%Y-%m-%d %H:%M", &t);
if (ret == NULL || *ret != '\0') {
ret = strptime(str, "%Y-%m-%d %H:%M:%S", &t);
if (ret == NULL || *ret != '\0') {
fprintf(stderr, "%s: Could not parse date %s."
" Please use format as specified in"
" man-page\n", toolname, str);
return -1;
}
}
t_old = t;
*tgt = mktime(&t);
// if daylight savings time applies, 't' has been adjusted,
// so we have to correct
if (t_old.tm_hour != t.tm_hour)
*tgt -= secs_since_1970(&t) - secs_since_1970(&t_old);
verbose_msg("datetime value from user after translation: %s", ctime((const time_t *)tgt));
return 0;
}
int parse_topline_arg(char *str, __u64 *arg)
{
char *p;
*arg = strtoull(str, &p, 0);
if (*arg == ULLONG_MAX) {
fprintf(stderr, "%s: Cannot convert"
" %s, over/underflow occurred. Try a smaller/larger"
" value.\n", toolname, str);
return -1;
}
if (*p != '\0') {
fprintf(stderr, "%s: Non-numeric"
" characters in argument '%s' to option '-t'. Make"
" sure to use only numeric characters.\n", toolname,
str);
return -1;
}
return 0;
}
FILE* open_csv_output_file(const char *filename, const char *extension,
int *rc)
{
char *tmp;
FILE *fp = NULL;
*rc = 0;
tmp = (char*)malloc(strlen(filename) + strlen(extension) + 1);
sprintf(tmp, "%s%s", filename, extension);
fp = fopen(tmp, "w");
if (!fp) {
fprintf(stdout, "%s: Could not open file %s. Make sure that you"
" have sufficient permissions and try again.\n", toolname, tmp);
*rc = -1;
}
else
fprintf(stdout, "Exporting data in CSV format to %s\n", tmp);
free(tmp);
return fp;
}