XRootD
XrdPfcPrint.cc
Go to the documentation of this file.
1 //----------------------------------------------------------------------------------
2 // Copyright (c) 2014 by Board of Trustees of the Leland Stanford, Jr., University
3 // Author: Alja Mrak-Tadel
4 //----------------------------------------------------------------------------------
5 // XRootD is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU Lesser General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // XRootD is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public License
16 // along with XRootD. If not, see <http://www.gnu.org/licenses/>.
17 //----------------------------------------------------------------------------------
18 
19 #include <iostream>
20 #include <fcntl.h>
21 #include <vector>
22 #include "XrdPfcPrint.hh"
23 #include "XrdPfcInfo.hh"
24 #include "XrdOuc/XrdOucEnv.hh"
25 #include "XrdOuc/XrdOucStream.hh"
26 #include "XrdOuc/XrdOucArgs.hh"
27 #include "XrdOuc/XrdOucJson.hh"
28 #include "XrdSys/XrdSysTrace.hh"
29 #include "XrdOfs/XrdOfsConfigPI.hh"
30 #include "XrdSys/XrdSysLogger.hh"
31 #include "XrdOss/XrdOss.hh"
32 
33 using namespace XrdPfc;
34 
35 Print::Print(XrdOss* oss, char u, bool v, bool j, int i, const char* path) :
36  m_oss(oss), m_verbose(v), m_json(j), m_indent(i), m_ossUser("nobody")
37 {
38  if (u == 'k') {
39  m_unit_shift = 10;
40  m_unit_width = 12;
41  m_unit[0] = u; m_unit[1] = 'B'; m_unit[2] = 0;
42  } else if (u == 'M') {
43  m_unit_shift = 20;
44  m_unit_width = 12; // need 12 chars for header
45  m_unit[0] = u; m_unit[1] = 'B'; m_unit[2] = 0;
46  } else {
47  m_unit_shift = 0;
48  m_unit_width = 15;
49  m_unit[0] = 'B'; m_unit[1] = 0;
50  }
51 
52  if (isInfoFile(path))
53  {
54  if (m_json) printFileJson(std::string(path));
55  else printFile(std::string(path));
56  }
57  else
58  {
59  XrdOssDF* dh = m_oss->newDir(m_ossUser);
60  if ( dh->Opendir(path, m_env) >= 0 )
61  {
62  printDir(dh, path);
63  }
64  delete dh;
65  }
66 }
67 
68 bool Print::isInfoFile(const char* path)
69 {
70  if (strncmp(&path[strlen(path)-6], ".cinfo", 6))
71  {
72  // printf("%s is not cinfo file.\n\n", path);
73  return false;
74  }
75  return true;
76 }
77 
78 
79 void Print::printFileJson(const std::string& path)
80 {
81  XrdOssDF* fh = m_oss->newFile(m_ossUser);
82  fh->Open((path).c_str(),O_RDONLY, 0600, m_env);
83 
84  XrdSysTrace tr("XrdPfcPrint"); tr.What = 2;
85  Info cfi(&tr);
86 
87  if ( ! cfi.Read(fh, path.c_str()))
88  {
89  return;
90  }
91 
92  int cntd = 0;
93  for (int i = 0; i < cfi.GetNBlocks(); ++i)
94  {
95  if (cfi.TestBitWritten(i)) cntd++;
96  }
97  const Info::Store& store = cfi.RefStoredData();
98  char timeBuff[128];
99  strftime(timeBuff, 128, "%c", localtime(&store.m_creationTime));
100 
101  nlohmann::json jobj = {
102  { "file", path },
103  { "version", cfi.GetVersion() },
104  { "created", timeBuff },
105  { "cksum", cfi.GetCkSumStateAsText() },
106  { "file_size", cfi.GetFileSize() },
107  { "buffer_size", cfi.GetBufferSize() },
108  { "n_blocks", cfi.GetNBlocks() },
109  { "state_complete", cntd < cfi.GetNBlocks() ? "incomplete" : "complete" },
110  { "state_percentage", 100.0 * cntd / cfi.GetNBlocks() },
111  { "n_acc_total", store.m_accessCnt }
112  };
113 
114  if (cfi.HasNoCkSumTime()) {
115  strftime(timeBuff, 128, "%c", localtime(&store.m_noCkSumTime));
116  jobj["no-cksum-time"] = timeBuff;
117  }
118 
119  // verbose needed in json?
120  if (m_verbose)
121  {
122  nlohmann::json jarr = nlohmann::json::array();
123  for (int i = 0; i < cfi.GetNBlocks(); ++i)
124  {
125  jarr.push_back(cfi.TestBitWritten(i) ? 1 : 0);
126  }
127  jobj["block_array"] = jarr;
128  }
129 
130  int idx = 1;
131  const std::vector<Info::AStat> &astats = cfi.RefAStats();
132  nlohmann::json acc_arr = nlohmann::json::array();
133  for (std::vector<Info::AStat>::const_iterator it = astats.begin(); it != astats.end(); ++it)
134  {
135  const int MM = 128;
136  char s[MM];
137 
138  strftime(s, MM, "%y%m%d:%H%M%S", localtime(&(it->AttachTime)));
139  std::string at = s;
140  strftime(s, MM, "%y%m%d:%H%M%S", localtime(&(it->DetachTime)));
141  std::string dt = it->DetachTime > 0 ? s : "------:------";
142  {
143  int hours = it->Duration/3600;
144  int min = (it->Duration - hours * 3600)/60;
145  int sec = it->Duration % 60;
146  snprintf(s, MM, "%d:%02d:%02d", hours, min, sec);
147  }
148  std::string dur = s;
149 
150  nlohmann::json acc = {
151  { "record", idx++ },
152  { "attach", at },
153  { "detach", dt },
154  { "duration", dur },
155  { "n_ios", it->NumIos },
156  { "n_mrg", it->NumMerged },
157  { "B_hit", it->BytesHit },
158  { "B_miss", it->BytesMissed },
159  { "B_bypass", it->BytesBypassed }
160  };
161  acc_arr.push_back(acc);
162  }
163  jobj["accesses"] = acc_arr;
164 
165  std::cout << jobj.dump(m_indent) << "\n";
166 
167  delete fh;
168 }
169 
170 
171 void Print::printFile(const std::string& path)
172 {
173  printf("FILE: %s\n", path.c_str());
174  XrdOssDF* fh = m_oss->newFile(m_ossUser);
175  fh->Open((path).c_str(),O_RDONLY, 0600, m_env);
176 
177  XrdSysTrace tr("XrdPfcPrint"); tr.What = 2;
178  Info cfi(&tr);
179 
180  if ( ! cfi.Read(fh, path.c_str()))
181  {
182  return;
183  }
184 
185  int cntd = 0;
186  for (int i = 0; i < cfi.GetNBlocks(); ++i)
187  {
188  if (cfi.TestBitWritten(i)) cntd++;
189  }
190  const Info::Store& store = cfi.RefStoredData();
191  char timeBuff[128];
192  strftime(timeBuff, 128, "%c", localtime(&store.m_creationTime));
193 
194  printf("version %d, created %s; cksum %s", cfi.GetVersion(), timeBuff, cfi.GetCkSumStateAsText());
195  if (cfi.HasNoCkSumTime()) {
196  strftime(timeBuff, 128, "%c", localtime(&store.m_noCkSumTime));
197  printf(", no-cksum-time %s\n", timeBuff);
198  } else printf("\n");
199 
200  printf("file_size %lld %s, buffer_size %lld %s, n_blocks %d, n_downloaded %d, state %scomplete [%.3f%%]\n",
201  cfi.GetFileSize() >> m_unit_shift, m_unit,
202  cfi.GetBufferSize() >> (m_unit[0] == 'M' ? 10 : m_unit_shift), m_unit[0] == 'M' ? "kB" : m_unit,
203  cfi.GetNBlocks(), cntd,
204  (cntd < cfi.GetNBlocks()) ? "in" : "", 100.0 * cntd / cfi.GetNBlocks());
205 
206  if (m_verbose)
207  {
208  int8_t n_db = 0;
209  { int x = cfi.GetNBlocks(); while (x)
210  {
211  x /= 10; ++n_db;
212  }
213  }
214  static const char *nums = "0123456789";
215  printf("printing %d blocks:\n", cfi.GetNBlocks());
216  printf("%*s %10d%10d%10d%10d%10d%10d\n", n_db, "", 1, 2, 3, 4, 5, 6);
217  printf("%*s %s%s%s%s%s%s0123", n_db, "", nums, nums, nums, nums, nums, nums);
218  for (int i = 0; i < cfi.GetNBlocks(); ++i)
219  {
220  if (i % 64 == 0)
221  printf("\n%*d ", n_db, i);
222  printf("%c", cfi.TestBitWritten(i) ? 'x' : '.');
223  }
224  printf("\n");
225  }
226 
227  int ww = m_unit_width - 2 - strlen(m_unit);
228  printf("Access records (N_acc_total=%llu):\n"
229  "%-6s %-13s %-13s %-12s %-5s %-5s %*s[%s] %*s[%s] %*s[%s]\n",
230  (unsigned long long) store.m_accessCnt,
231  "Record", "Attach", "Detach", "Duration", "N_ios", "N_mrg",
232  ww, "B_hit", m_unit, ww, "B_miss", m_unit, ww, "B_bypass", m_unit);
233 
234  int idx = 1;
235  const std::vector<Info::AStat> &astats = cfi.RefAStats();
236  for (std::vector<Info::AStat>::const_iterator it = astats.begin(); it != astats.end(); ++it)
237  {
238  const int MM = 128;
239  char s[MM];
240 
241  strftime(s, MM, "%y%m%d:%H%M%S", localtime(&(it->AttachTime)));
242  std::string at = s;
243  strftime(s, MM, "%y%m%d:%H%M%S", localtime(&(it->DetachTime)));
244  std::string dt = it->DetachTime > 0 ? s : "------:------";
245  {
246  int hours = it->Duration/3600;
247  int min = (it->Duration - hours * 3600)/60;
248  int sec = it->Duration % 60;
249  snprintf(s, MM, "%d:%02d:%02d", hours, min, sec);
250  }
251  std::string dur = s;
252 
253  printf("%-6d %-13s %-13s %-12s %5d %5d %*lld %*lld %*lld\n", idx++,
254  at.c_str(), dt.c_str(), dur.c_str(), it->NumIos, it->NumMerged,
255  m_unit_width, it->BytesHit >> m_unit_shift,
256  m_unit_width, it->BytesMissed >> m_unit_shift,
257  m_unit_width, it->BytesBypassed >> m_unit_shift);
258  }
259 
260  delete fh;
261 }
262 
263 void Print::printDir(XrdOssDF* iOssDF, const std::string& path)
264 {
265  // printf("---------> print dir %s \n", path.c_str());
266 
267  const int MM = 1024;
268  char buff[MM];
269  int rdr;
270  bool first = true;
271 
272  while ((rdr = iOssDF->Readdir(&buff[0], MM)) >= 0)
273  {
274  if (strncmp("..", &buff[0], 2) && strncmp(".", &buff[0], 1))
275  {
276  if (strlen(buff) == 0)
277  {
278  break; // end of readdir
279  }
280  std::string np = path + "/" + std::string(&buff[0]);
281  if (isInfoFile(buff))
282  {
283  if (m_json) {
284  printFileJson(np);
285  } else {
286  if (first) first = false;
287  else printf("\n");
288  printFile(np);
289  }
290  }
291  else
292  {
293  XrdOssDF* dh = m_oss->newDir(m_ossUser);
294  if (dh->Opendir(np.c_str(), m_env) >= 0)
295  {
296  printDir(dh, np);
297  }
298  delete dh;
299  }
300  }
301  }
302 }
303 
304 
305 //------------------------------------------------------------------------------
306 
307 int main(int argc, char *argv[])
308 {
309  static const char* usage = "Usage: pfc_print [-h] [-c config_file] [-u B|kB|MB] [-v] [-j] [-i indent] path ...\n";
310  char unit = 'k';
311  bool verbose = false;
312  bool json = false;
313  int indent = -1;
314  const char* cfgn = 0;
315 
316  XrdOucEnv myEnv;
317 
318  XrdSysLogger log;
319  XrdSysError err(&log);
320 
321  XrdOucStream Config(&err, getenv("XRDINSTANCE"), &myEnv, "=====> ");
322  XrdOucArgs Spec(&err, "xrdpfc_print: ", "",
323  "help", 1, "h",
324  "config", 1, "c:",
325  "unit", 1, "u:",
326  "verbose", 1, "v",
327  "json", 1, "j",
328  "indent", 1, "i:",
329  (const char *) 0);
330 
331  Spec.Set(argc-1, &argv[1]);
332  char theOpt;
333 
334  while ((theOpt = Spec.getopt()) != (char)-1)
335  {
336  // printf("GETOPT %c -- arg=%s\n", theOpt, Spec.argval);
337  switch (theOpt)
338  {
339  case 'c': {
340  cfgn = Spec.argval;
341  int fd = open(cfgn, O_RDONLY, 0);
342  Config.Attach(fd);
343  break;
344  }
345  case 'u': {
346  if (strcmp(Spec.argval,"B") && strcmp(Spec.argval,"kB") && strcmp(Spec.argval,"MB")) {
347  printf("%s Error: -unit argument can only be B, kB or MB\n", usage);
348  exit(1);
349  }
350  unit = Spec.argval[0];
351  break;
352  }
353  case 'v': {
354  verbose = true;
355  break;
356  }
357  case 'j': {
358  json = true;
359  break;
360  }
361  case 'i': {
362  indent = std::stoi(Spec.argval);
363  break;
364  }
365  case 'h':
366  default: {
367  printf("%s", usage);
368  exit(1);
369  }
370  }
371  }
372 
373  // suppress oss init messages
374  int efs = open("/dev/null",O_RDWR, 0);
375  XrdSysLogger ossLog(efs);
376  XrdSysError ossErr(&ossLog, "print");
377  XrdOss *oss;
378  XrdOfsConfigPI *ofsCfg = XrdOfsConfigPI::New(cfgn,&Config,&ossErr);
379  bool ossSucc = ofsCfg->Load(XrdOfsConfigPI::theOssLib);
380  if ( ! ossSucc)
381  {
382  printf("can't load oss\n");
383  exit(1);
384  }
385  ofsCfg->Plugin(oss);
386 
387  const char* path;
388  while ((path = Spec.getarg()))
389  {
390  if ( ! path)
391  {
392  printf("%s", usage);
393  exit(1);
394  }
395 
396  // append oss.localroot if path starts with 'root://'
397  if ( ! strncmp(&path[0], "root:/", 6))
398  {
399  if (Config.FDNum() < 0)
400  {
401  printf("Configuration file not specified.\n");
402  exit(1);
403  }
404  char *var;
405  while((var = Config.GetFirstWord()))
406  {
407  // printf("var %s \n", var);
408  if ( ! strncmp(var,"oss.localroot", strlen("oss.localroot")))
409  {
410  std::string tmp = Config.GetWord();
411  tmp += &path[6];
412  // printf("Absolute path %s \n", tmp.c_str());
413  XrdPfc::Print p(oss, unit, verbose, json, indent, tmp.c_str());
414  }
415  }
416  }
417  else
418  {
419  XrdPfc::Print p(oss, unit, verbose, json, indent, path);
420  }
421  }
422 
423  return 0;
424 }
void usage()
nlohmann::json json
int main(int argc, char *argv[])
Definition: XrdPfcPrint.cc:307
int open(const char *path, int oflag,...)
bool Plugin(XrdAccAuthorize *&piP)
Get Authorization plugin.
static XrdOfsConfigPI * New(const char *cfn, XrdOucStream *cfgP, XrdSysError *errP, XrdVersionInfo *verP=0, XrdSfsFileSystem *sfsP=0)
bool Load(int what, XrdOucEnv *envP=0)
@ theOssLib
Oss plugin.
virtual int Opendir(const char *path, XrdOucEnv &env)
Definition: XrdOss.hh:79
virtual int Readdir(char *buff, int blen)
Definition: XrdOss.hh:92
virtual int Open(const char *path, int Oflag, mode_t Mode, XrdOucEnv &env)
Definition: XrdOss.hh:200
virtual XrdOssDF * newDir(const char *tident)=0
virtual XrdOssDF * newFile(const char *tident)=0
char getopt()
Definition: XrdOucArgs.cc:151
char * getarg()
Definition: XrdOucArgs.cc:136
void Set(char *arglist)
Definition: XrdOucArgs.cc:236
char * argval
Definition: XrdOucArgs.hh:102
Status of cached file. Can be read from and written into a binary file.
Definition: XrdPfcInfo.hh:41
Print(XrdOss *oss, char u, bool v, bool j, int i, const char *path)
Constructor.
Definition: XrdPfcPrint.cc:35
XrdCmsConfig Config
Definition: XrdPfc.hh:41
time_t m_noCkSumTime
time when first non-cksummed block was detected
Definition: XrdPfcInfo.hh:81
size_t m_accessCnt
total access count for the file
Definition: XrdPfcInfo.hh:82
time_t m_creationTime
time the info file was created
Definition: XrdPfcInfo.hh:80