// ----------------------------------------------------------------------
// File: com_report.cc
// Author: Andreas-Joachim Peters - CERN
// ----------------------------------------------------------------------
/************************************************************************
* EOS - the CERN Disk Storage System *
* Copyright (C) 2021 CERN/Switzerland *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see .*
************************************************************************/
#include "common/StringTokenizer.hh"
#include "console/ConsoleMain.hh"
#include "common/Statistics.hh"
#include "common/Path.hh"
#include
#include
#include
#include
/* Change working directory &*/
int
com_report(char* arg1)
{
eos::common::StringTokenizer subtokenizer(arg1);
subtokenizer.GetLine();
XrdOucString arg;
XrdOucString path;
std::string sregex;
size_t max_reports = 2000000000;
bool silent = false;
bool ec = false;
XrdOucString squash;
time_t start_time = 0;
time_t stop_time = 0;
time_t first_ts = 0;
time_t last_ts = 0;
do {
arg = subtokenizer.GetToken();
if (!arg.length() && path.length()) {
break;
}
if (arg == "--regex") {
arg = subtokenizer.GetToken();
if (!arg.length()) {
goto com_report_usage;
} else {
sregex = arg.c_str();
}
continue;
}
if (arg == "-n") {
arg = subtokenizer.GetToken();
if (!arg.length()) {
goto com_report_usage;
} else {
max_reports = std::strtoul(arg.c_str(), 0, 10);
}
continue;
}
if (arg == "--ec") {
ec = true;
continue;
}
if (arg == "--squash") {
arg = subtokenizer.GetToken();
if (!arg.beginswith("/") && !arg.endswith("/")) {
goto com_report_usage;
}
squash = arg;
continue;
}
if (arg == "--start") {
arg = subtokenizer.GetToken();
if (!arg.length()) {
goto com_report_usage;
} else {
start_time = std::strtoul(arg.c_str(), 0, 10);
}
continue;
}
if (arg == "--stop") {
arg = subtokenizer.GetToken();
if (!arg.length()) {
goto com_report_usage;
} else {
stop_time = std::strtoul(arg.c_str(), 0, 10);
}
continue;
}
if (arg == "-s") {
silent = true;
continue;
}
if ((!arg.length()) || (arg.beginswith("--help")) || (arg.beginswith("-h"))) {
goto com_report_usage;
}
path = arg;
} while (arg.length());
{
std::string reportfile = path.c_str();
std::ifstream file(reportfile);
if (file.is_open()) {
std::map map;
std::string line;
std::vector keys;
std::multiset r_t;
std::multiset w_t;
uint64_t sum_w, sum_r;
sum_w = sum_r = 0;
std::string sizestring;
size_t n_reports = 0;
regex_t regex;
if (sregex.length()) {
// Compile regex
int regexErrorCode = regcomp(®ex, sregex.c_str(), REG_EXTENDED);
if (regexErrorCode) {
fprintf(stderr, "error: regular expression is invalid regex-rc=%d\n",
regexErrorCode);
global_retc = EINVAL;
return (0);
}
}
while (std::getline(file, line)) {
if (sregex.length()) {
// Execute regex
int result = regexec(®ex, line.c_str(), 0, NULL, 0);
// Check the result
if (result == REG_NOMATCH) {
// next entry
continue;
} else if (result != 0) { // REG_BADPAT, REG_ESPACE, etc...
fprintf(stderr, "error: invalid regex\n");
global_retc = EINVAL;
return (0);
} else {
}
}
std::map map;
if (eos::common::StringConversion::GetKeyValueMap(line.c_str(),
map,
"=",
"&",
&keys)) {
if (!sregex.length() && map["td"].substr(0, 6) == "daemon") {
continue;
}
if (!map.count("rb") && !map.count("wb")) {
continue;
}
if (map["sec.app"] == "deletion") {
continue;
}
bool found = false;
time_t start_ots = std::stoul(map["ots"]);
if (!first_ts) {
first_ts = start_ots;
}
last_ts = start_ots;
if (start_time) {
if (start_ots < start_time) {
continue;
}
}
if (stop_time) {
if (start_ots > stop_time) {
continue;
}
}
ssize_t wsize = ec ? std::stol(map["csize"]) : std::stol(map["wb"]);
ssize_t rsize = ec ? std::stol(map["csize"]) : std::stol(map["rb"]);
// classify write or read
if (std::stol(map["wb"]) > 0) {
sum_w += wsize;
double tt = std::stoul(map["cts"]) - std::stoul(map["ots"]) +
(0.001 * std::stoul(map["ctms"])) - (0.001 * std::stoul(map["otms"]));
float rate = wsize / tt / 1000000.0;
if (!silent) {
fprintf(stdout, "W %-16s t=%03.02f [s] r=%03.02f [MB/s] path=%64s\n",
eos::common::StringConversion::GetReadableSizeString(sizestring, wsize, ""), tt,
rate, map["path"].c_str());
}
w_t.insert(tt);
found = true;
}
if (std::stol(map["rb"]) > 0) {
sum_r += rsize;
double tt = std::stoul(map["cts"]) - std::stoul(map["ots"]) +
(0.001 * std::stoul(map["ctms"])) - (0.001 * std::stoul(map["otms"]));
float rate = rsize / tt / 1000000.0;
if (!silent && !squash.length()) {
fprintf(stdout, "R %-16s t=%03.02f [s] r=%03.02f [MB/s] path=%64s\n",
eos::common::StringConversion::GetReadableSizeString(sizestring, rsize, ""), tt,
rate, map["path"].c_str());
}
r_t.insert(tt);
found = true;
}
if (found) {
n_reports++;
}
} else {
fprintf(stderr, "error: failed to parse '%s'\n", line.c_str());
}
if (n_reports >= max_reports) {
break;
}
if (squash.length()) {
std::string rpath = squash.c_str();
rpath += map["path"].c_str();
eos::common::Path cPath(rpath.c_str());
fprintf(stderr, "info: squash %s\n", cPath.GetFullPath().c_str());
cPath.MakeParentPath(0644);
int fd = open(rpath.c_str(), O_CREAT | O_RDWR | O_APPEND, S_IRWXU | S_IRWXG);
if (fd < 0) {
fprintf(stderr, "error:failed to create\n");
} else {
(void) ::write(fd, line.c_str(), line.length() + 1);
(void) ::close(fd);
}
}
}
if (1) {
std::string sizestring1, sizestring2;
fprintf(stdout,
"---------------------------------------------------------------------\n");
fprintf(stdout, "- n(r): %lu vol(r): %s n(w): %lu vol(w): %s\n",
r_t.size(),
eos::common::StringConversion::GetReadableSizeString(sizestring1, sum_r, "B"),
w_t.size(),
eos::common::StringConversion::GetReadableSizeString(sizestring2, sum_w, "B"));
fprintf(stdout,
"---------------------------------------------------------------------\n");
fprintf(stdout, "- r:t avg: %s +- %s 95-perc: %s 99-perc: %s max: %s \n",
eos::common::StringConversion::GetFixedDouble(eos::common::Statistics::avg(r_t),
6, 2).c_str(),
eos::common::StringConversion::GetFixedDouble(eos::common::Statistics::sig(r_t),
6, 2).c_str(),
eos::common::StringConversion::GetFixedDouble(eos::common::Statistics::nperc(
r_t, 95), 6, 2).c_str(),
eos::common::StringConversion::GetFixedDouble(eos::common::Statistics::nperc(
r_t, 99), 6, 2).c_str(),
eos::common::StringConversion::GetFixedDouble(eos::common::Statistics::max(r_t),
6, 2).c_str()
);
fprintf(stdout, "- w:t avg: %s +- %s 95-perc: %s 99-perc: %s max: %s \n",
eos::common::StringConversion::GetFixedDouble(eos::common::Statistics::avg(w_t),
6, 2).c_str(),
eos::common::StringConversion::GetFixedDouble(eos::common::Statistics::sig(w_t),
6, 2).c_str(),
eos::common::StringConversion::GetFixedDouble(eos::common::Statistics::nperc(
w_t, 95), 6, 2).c_str(),
eos::common::StringConversion::GetFixedDouble(eos::common::Statistics::nperc(
w_t, 99), 6, 2).c_str(),
eos::common::StringConversion::GetFixedDouble(eos::common::Statistics::max(w_t),
6, 2).c_str()
);
fprintf(stdout,
"---------------------------------------------------------------------\n");
XrdOucString agestring;
fprintf(stdout, "- first-ts:%ld last-ts:%ld time-span:%ld s [ %s ] \n",
first_ts,
last_ts,
last_ts - first_ts,
eos::common::StringConversion::GetReadableAgeString(agestring,
last_ts - first_ts));
fprintf(stdout, "- r:rate avg: %.02f MB/s ",
(last_ts - first_ts) ? sum_r / 1000000.0 / (last_ts - first_ts) : 0);
fprintf(stdout, "- w:rate avg: %.02f MB/s\n",
(last_ts - first_ts) ? sum_w / 1000000.0 / (last_ts - first_ts) : 0);
fprintf(stdout,
"---------------------------------------------------------------------\n");
file.close();
if (sregex.length()) {
regfree(®ex);
}
}
} else {
fprintf(stderr, "error: unable to open file!\n");
global_retc = EIO;
return (0);
}
}
return (0);
com_report_usage:
fprintf(stdout,
"'[eos] report [-n ] [--regex ] [-s] [--start ] [--stop ] [--ec] \n");
fprintf(stdout, "Usage: report \n");
fprintf(stdout, "Options:\n");
fprintf(stdout,
" -s : show only the summary with N(r) [number of files read] N(w) [number of files written] VOL(r) [data volume read] VOL (w) [data volume written],\n");
fprintf(stdout,
" + timings avg [average transfer time], 95-perc [95 percentile], 99-perc [99 percentila] max [maximal transfer time]\n");
fprintf(stdout,
" -n : stop after n records are accepted for the statistics\n");
fprintf(stdout,
" --ec : consider records as EC file streaming reads/writes\n");
fprintf(stdout,
" --regex : apply for filtering the records\n");
fprintf(stdout,
" --start : only take records starting after \n");
fprintf(stdout,
" --stop : only take records starting before \n\n");
fprintf(stdout,
"Example: bash> eos report /var/eos/report/2021/05/20210530.eosreport\n");
fprintf(stdout,
" bash> zcat /var/eos/report/2021/05/20210530.eosreport.gz | eos report /dev/stdin -s\n");
fprintf(stdout,
" bash> eos report /var/eos/report/2021/05/20210530.eosreport --regex \"sec.app=fuse\" -s\n");
global_retc = EINVAL;
return (0);
}