souffle  2.0.2-371-g6315b36
Tui.h
Go to the documentation of this file.
1 /*
2  * Souffle - A Datalog Compiler
3  * Copyright (c) 2016, The Souffle Developers. All rights reserved
4  * Licensed under the Universal Permissive License v 1.0 as shown at:
5  * - https://opensource.org/licenses/UPL
6  * - <souffle root>/licenses/SOUFFLE-UPL.txt
7  */
8 
9 #pragma once
10 
18 #include "souffle/profile/Reader.h"
20 #include "souffle/profile/Row.h"
21 #include "souffle/profile/Rule.h"
23 #include "souffle/profile/Table.h"
26 #include <algorithm>
27 #include <chrono>
28 #include <cstdint>
29 #include <cstdio>
30 #include <cstdlib>
31 #include <iostream>
32 #include <iterator>
33 #include <map>
34 #include <memory>
35 #include <ratio>
36 #include <set>
37 #include <string>
38 #include <thread>
39 #include <unordered_map>
40 #include <utility>
41 #include <vector>
42 #include <dirent.h>
43 #include <sys/ioctl.h>
44 #include <sys/stat.h>
45 
46 namespace souffle {
47 namespace profile {
48 
49 /*
50  * Text User interface for SouffleProf
51  * OutputProcessor creates a ProgramRun object
52  * ProgramRun -> Reader.h ProgramRun stores all the data
53  * OutputProcessor grabs the data and makes tables
54  * Tui displays the data
55  */
56 class Tui {
57 private:
59  bool loaded;
60  std::string f_name;
61  bool alive = false;
62  std::thread updater;
63  int sortColumn = 0;
64  int precision = 3;
67  std::shared_ptr<Reader> reader;
69  /// Limit results shown. Default value chosen to approximate unlimited
70  size_t resultLimit = 20000;
71 
72  struct Usage {
73  std::chrono::microseconds time;
74  uint64_t maxRSS;
75  std::chrono::microseconds systemtime;
76  std::chrono::microseconds usertime;
77  bool operator<(const Usage& other) const {
78  return time < other.time;
79  }
80  };
81 
82 public:
83  Tui(std::string filename, bool live, bool /* gui */) {
84  // Set a friendlier output size if we're being interacted with directly.
85  if (live) {
86  resultLimit = 20;
87  }
88  this->f_name = filename;
89 
90  const std::shared_ptr<ProgramRun>& run = out.getProgramRun();
91 
92  this->reader = std::make_shared<Reader>(filename, run);
93 
94  this->alive = false;
95  updateDB();
96  this->loaded = reader->isLoaded();
97  }
98 
99  Tui() {
100  const std::shared_ptr<ProgramRun>& run = out.getProgramRun();
101  this->reader = std::make_shared<Reader>(run);
102  this->loaded = true;
103  this->alive = true;
104  updateDB();
105  updater = std::thread([this]() {
106  // Update the display every 30s. Check for input every 0.5s
107  std::chrono::milliseconds interval(30000);
108  auto nextUpdateTime = std::chrono::high_resolution_clock::now();
109  do {
110  std::this_thread::sleep_for(std::chrono::milliseconds(500));
111  if (nextUpdateTime < std::chrono::high_resolution_clock::now()) {
112  runCommand({});
113  nextUpdateTime = std::chrono::high_resolution_clock::now() + interval;
114  }
115  } while (reader->isLive() && !linereader.hasReceivedInput());
116  });
117  }
118 
119  ~Tui() {
120  if (updater.joinable()) {
121  updater.join();
122  }
123  }
124 
125  void runCommand(std::vector<std::string> c) {
126  if (linereader.hasReceivedInput() && c.empty()) {
127  return;
128  }
129  if (!loaded) {
130  std::cout << "Error: File cannot be loaded\n";
131  return;
132  }
133 
134  if (alive) {
135  updateDB();
136  // remake tables to get new data
139 
141  }
142 
143  // If we have not received any input yet in live mode then run top.
144  if ((!linereader.hasReceivedInput() && c.empty())) {
145  // Move up n lines and overwrite the previous top output.
146  std::cout << "\x1b[3D";
147  std::cout << "\x1b[27A";
148  top();
149  std::cout << "\x1b[B> ";
150  } else if (c[0] == "top") {
151  top();
152  } else if (c[0] == "rel") {
153  if (c.size() == 2) {
154  relRul(c[1]);
155  } else if (c.size() == 1) {
156  rel(resultLimit);
157  } else {
158  std::cout << "Invalid parameters to rel command.\n";
159  }
160  } else if (c[0] == "rul") {
161  if (c.size() > 1) {
162  if (c.size() == 3 && c[1] == "id") {
163  std::printf("%7s%2s%s\n\n", "ID", "", "NAME");
164  id(c[2]);
165  } else if (c.size() == 2 && c[1] == "id") {
166  id("0");
167  } else if (c.size() == 2) {
168  verRul(c[1]);
169  } else {
170  std::cout << "Invalid parameters to rul command.\n";
171  }
172  } else {
173  rul(resultLimit);
174  }
175  } else if (c[0] == "graph") {
176  if (c.size() == 3 && c[1].find(".") == std::string::npos) {
177  iterRel(c[1], c[2]);
178  } else if (c.size() == 3 && c[1].at(0) == 'C') {
179  iterRul(c[1], c[2]);
180  } else if (c.size() == 4 && c[1] == "ver" && c[2].at(0) == 'C') {
181  verGraph(c[2], c[3]);
182  } else {
183  std::cout << "Invalid parameters to graph command.\n";
184  }
185  } else if (c[0] == "memory") {
186  memoryUsage();
187  } else if (c[0] == "usage") {
188  if (c.size() > 1) {
189  if (c[1][0] == 'R') {
190  usageRelation(c[1]);
191  } else {
192  usageRule(c[1]);
193  }
194  } else {
195  usage();
196  }
197  } else if (c[0] == "help") {
198  help();
199  } else if (c[0] == "limit") {
200  if (c.size() == 1) {
201  setResultLimit(20000);
202  } else {
203  try {
204  setResultLimit(std::stoul(c[1]));
205  } catch (...) {
206  std::cout << "Invalid parameters to limit command.\n";
207  }
208  }
209  } else if (c[0] == "configuration") {
210  configuration();
211  } else {
212  std::cout << "Unknown command. Use \"help\" for a list of commands.\n";
213  }
214  }
215 
216  void runProf() {
217  if (!loaded && !f_name.empty()) {
218  std::cout << "Error: File cannot be floaded\n";
219  return;
220  }
221  if (loaded) {
222  std::cout << "SouffleProf\n";
223  top();
224  }
225 
226  linereader.setPrompt("\n> ");
228 
229  while (true) {
230  std::string untrimmedInput = linereader.getInput();
231  std::string input = Tools::trimWhitespace(untrimmedInput);
232 
233  std::cout << std::endl;
234  if (input.empty()) {
235  std::cout << "Unknown command. Type help for a list of commands.\n";
236  continue;
237  }
238 
239  linereader.addHistory(input);
240 
241  std::vector<std::string> c = Tools::split(input, " ");
242 
243  if (c[0] == "q" || c[0] == "quit") {
244  quit();
245  break;
246  } else if (c[0] == "sort") {
247  if (c.size() == 2 && std::stoi(c[1]) < 7) {
248  sortColumn = std::stoi(c[1]);
249  } else {
250  std::cout << "Invalid column, please select a number between 0 and 6.\n";
251  }
252  } else {
253  runCommand(c);
254  }
255  }
256  }
257 
258  std::stringstream& genJsonTop(std::stringstream& ss) {
259  const std::shared_ptr<ProgramRun>& run = out.getProgramRun();
260 
261  auto beginTime = run->getStarttime();
262  auto endTime = run->getEndtime();
263  ss << R"_({"top":[)_" << (endTime - beginTime).count() / 1000000.0 << "," << run->getTotalSize()
264  << "," << run->getTotalLoadtime().count() / 1000000.0 << ","
265  << run->getTotalSavetime().count() / 1000000.0 << "]";
266  return ss;
267  }
268 
269  std::stringstream& genJsonRelations(std::stringstream& ss, const std::string& name, size_t maxRows) {
270  const std::shared_ptr<ProgramRun>& run = out.getProgramRun();
271 
272  auto comma = [&ss](bool& first, const std::string& delimiter = ", ") {
273  if (!first) {
274  ss << delimiter;
275  } else {
276  first = false;
277  }
278  };
279 
280  ss << '"' << name << R"_(":{)_";
281  bool firstRow = true;
282  auto rows = relationTable.getRows();
283  std::stable_sort(rows.begin(), rows.end(), [](std::shared_ptr<Row> left, std::shared_ptr<Row> right) {
284  return (*left)[0]->getDoubleVal() > (*right)[0]->getDoubleVal();
285  });
286  maxRows = std::min(rows.size(), maxRows);
287 
288  for (size_t i = 0; i < maxRows; ++i) {
289  comma(firstRow, ",\n");
290 
291  Row& row = *rows[i];
292  ss << '"' << row[6]->toString(0) << R"_(": [)_";
293  ss << '"' << Tools::cleanJsonOut(row[5]->toString(0)) << R"_(", )_";
294  ss << '"' << Tools::cleanJsonOut(row[6]->toString(0)) << R"_(", )_";
295  ss << row[0]->getDoubleVal() << ", ";
296  ss << row[1]->getDoubleVal() << ", ";
297  ss << row[2]->getDoubleVal() << ", ";
298  ss << row[3]->getDoubleVal() << ", ";
299  ss << row[4]->getLongVal() << ", ";
300  ss << row[12]->getLongVal() << ", ";
301  ss << '"' << Tools::cleanJsonOut(row[7]->toString(0)) << R"_(", [)_";
302 
303  bool firstCol = true;
304  for (auto& _rel_row : ruleTable.getRows()) {
305  Row rel_row = *_rel_row;
306  if (rel_row[7]->toString(0) == row[5]->toString(0)) {
307  comma(firstCol);
308  ss << '"' << rel_row[6]->toString(0) << '"';
309  }
310  }
311  ss << "], ";
312  std::vector<std::shared_ptr<Iteration>> iter =
313  run->getRelation(row[5]->toString(0))->getIterations();
314  ss << R"_({"tot_t": [)_";
315  firstCol = true;
316  for (auto& i : iter) {
317  comma(firstCol);
318  ss << i->getRuntime().count();
319  }
320  ss << R"_(], "copy_t": [)_";
321  firstCol = true;
322  for (auto& i : iter) {
323  comma(firstCol);
324  ss << i->getCopytime().count();
325  }
326  ss << R"_(], "tuples": [)_";
327  firstCol = true;
328  for (auto& i : iter) {
329  comma(firstCol);
330  ss << i->size();
331  }
332  ss << "]}]";
333  }
334  ss << "}";
335 
336  return ss;
337  }
338 
339  std::stringstream& genJsonRules(std::stringstream& ss, const std::string& name, size_t maxRows) {
340  const std::shared_ptr<ProgramRun>& run = out.getProgramRun();
341 
342  auto comma = [&ss](bool& first, const std::string& delimiter = ", ") {
343  if (!first) {
344  ss << delimiter;
345  } else {
346  first = false;
347  }
348  };
349 
350  ss << '"' << name << R"_(":{)_";
351 
352  bool firstRow = true;
353  auto rows = ruleTable.getRows();
354  std::stable_sort(rows.begin(), rows.end(), [](std::shared_ptr<Row> left, std::shared_ptr<Row> right) {
355  return (*left)[0]->getDoubleVal() > (*right)[0]->getDoubleVal();
356  });
357  maxRows = std::min(rows.size(), maxRows);
358 
359  for (size_t i = 0; i < maxRows; ++i) {
360  Row& row = *rows[i];
361 
362  std::vector<std::string> part = Tools::split(row[6]->toString(0), ".");
363  std::string strRel = "R" + part[0].substr(1);
364  Table versionTable = out.getVersions(strRel, row[6]->toString(0));
365 
366  std::string src;
367  if (versionTable.rows.size() > 0) {
368  if (versionTable.rows[0]->cells[9] != nullptr) {
369  src = (*versionTable.rows[0])[9]->toString(0);
370  } else {
371  src = "-";
372  }
373  } else {
374  src = row[10]->toString(-1);
375  }
376  comma(firstRow);
377  ss << "\n ";
378 
379  ss << '"' << row[6]->toString(0) << R"_(": [)_";
380  ss << '"' << Tools::cleanJsonOut(row[5]->toString(0)) << R"_(", )_";
381  ss << '"' << Tools::cleanJsonOut(row[6]->toString(0)) << R"_(", )_";
382  ss << row[0]->getDoubleVal() << ", ";
383  ss << row[1]->getDoubleVal() << ", ";
384  ss << row[2]->getDoubleVal() << ", ";
385  ss << row[4]->getLongVal() << ", ";
386 
387  ss << '"' << src << R"_(", )_";
388  ss << "[";
389 
390  bool has_ver = false;
391  bool firstCol = true;
392  for (auto& _ver_row : versionTable.getRows()) {
393  comma(firstCol);
394  has_ver = true;
395  Row ver_row = *_ver_row;
396  ss << '[';
397  ss << '"' << Tools::cleanJsonOut(ver_row[5]->toString(0)) << R"_(", )_";
398  ss << '"' << Tools::cleanJsonOut(ver_row[6]->toString(0)) << R"_(", )_";
399  ss << ver_row[0]->getDoubleVal() << ", ";
400  ss << ver_row[1]->getDoubleVal() << ", ";
401  ss << ver_row[2]->getDoubleVal() << ", ";
402  ss << ver_row[4]->getLongVal() << ", ";
403  ss << '"' << src << R"_(", )_";
404  ss << ver_row[8]->getLongVal();
405  ss << ']';
406  }
407 
408  ss << "], ";
409 
410  if (row[6]->toString(0).at(0) != 'C') {
411  ss << "{}, {}]";
412  } else {
413  ss << R"_({"tot_t": [)_";
414 
415  std::vector<uint64_t> iteration_tuples;
416  bool firstCol = true;
417  for (auto& i : run->getRelation(row[7]->toString(0))->getIterations()) {
418  bool add = false;
419  std::chrono::microseconds totalTime{};
420  uint64_t totalSize = 0L;
421  for (auto& rul : i->getRules()) {
422  if (rul.second->getId() == row[6]->toString(0)) {
423  totalTime += rul.second->getRuntime();
424 
425  totalSize += rul.second->size();
426  add = true;
427  }
428  }
429  if (add) {
430  comma(firstCol);
431  ss << totalTime.count();
432  iteration_tuples.push_back(totalSize);
433  }
434  }
435  ss << R"_(], "tuples": [)_";
436  firstCol = true;
437  for (auto& i : iteration_tuples) {
438  comma(firstCol);
439  ss << i;
440  }
441 
442  ss << "]}, {";
443 
444  if (has_ver) {
445  ss << R"_("tot_t": [)_";
446 
447  firstCol = true;
448  for (auto& row : versionTable.rows) {
449  comma(firstCol);
450  ss << (*row)[0]->getDoubleVal();
451  }
452  ss << R"_(], "tuples": [)_";
453 
454  firstCol = true;
455  for (auto& row : versionTable.rows) {
456  comma(firstCol);
457  ss << (*row)[4]->getLongVal();
458  }
459  ss << ']';
460  }
461  ss << "}]";
462  }
463  }
464  ss << "\n}";
465  return ss;
466  }
467 
468  std::stringstream& genJsonUsage(std::stringstream& ss) {
469  const std::shared_ptr<ProgramRun>& run = out.getProgramRun();
470 
471  auto comma = [&ss](bool& first, const std::string& delimiter = ", ") {
472  if (!first) {
473  ss << delimiter;
474  } else {
475  first = false;
476  }
477  };
478 
479  std::string source_loc = (*relationTable.getRows()[0])[7]->getStringVal();
480  std::string source_file_loc = Tools::split(source_loc, " ").at(0);
481  std::ifstream source_file(source_file_loc);
482  if (!source_file.is_open()) {
483  std::cout << "Error opening \"" << source_file_loc << "\", creating GUI without source locator."
484  << std::endl;
485  } else {
486  std::string str;
487  ss << R"_("code": [)_";
488  bool firstCol = true;
489  while (getline(source_file, str)) {
490  comma(firstCol, ",\n");
491  ss << '"' << Tools::cleanJsonOut(str) << '"';
492  }
493  ss << "],\n";
494  source_file.close();
495  }
496 
497  // Add usage statistics
498  auto usages = getUsageStats(100);
499  auto beginTime = run->getStarttime();
500 
501  ss << R"_("usage": [)_";
502  bool firstRow = true;
503  Usage previousUsage = *usages.begin();
504  previousUsage.time = beginTime;
505  for (auto usage : usages) {
506  comma(firstRow);
507  ss << '[';
508  ss << (usage.time - beginTime).count() / 1000000.0 << ", ";
509  ss << 100.0 * (usage.usertime - previousUsage.usertime) / (usage.time - previousUsage.time)
510  << ", ";
511  ss << 100.0 * (usage.systemtime - previousUsage.systemtime) / (usage.time - previousUsage.time)
512  << ", ";
513  ss << usage.maxRSS * 1024 << ", ";
514  ss << '"';
515  bool firstCol = true;
516  for (auto& cur : out.getProgramRun()->getRelationsAtTime(previousUsage.time, usage.time)) {
517  comma(firstCol);
518  ss << cur->getName();
519  }
520  ss << '"';
521  ss << ']';
522  previousUsage = usage;
523  }
524  ss << ']';
525  return ss;
526  }
527 
528  std::stringstream& genJsonConfiguration(std::stringstream& ss) {
529  auto comma = [&ss](bool& first, const std::string& delimiter = ", ") {
530  if (!first) {
531  ss << delimiter;
532  } else {
533  first = false;
534  }
535  };
536 
537  // Add configuration key-value pairs
538  ss << R"_("configuration": {)_";
539  bool firstRow = true;
540  for (auto& kvp :
541  ProfileEventSingleton::instance().getDB().getStringMap({"program", "configuration"})) {
542  comma(firstRow);
543  ss << '"' << kvp.first << R"_(": ")_" << Tools::cleanJsonOut(kvp.second) << '"';
544  }
545  ss << '}';
546  return ss;
547  }
548 
549  std::stringstream& genJsonAtoms(std::stringstream& ss) {
550  const std::shared_ptr<ProgramRun>& run = out.getProgramRun();
551 
552  auto comma = [&ss](bool& first, const std::string& delimiter = ", ") {
553  if (!first) {
554  ss << delimiter;
555  } else {
556  first = false;
557  }
558  };
559 
560  ss << R"_("atoms": {)_";
561 
562  bool firstRow = true;
563  for (auto& relation : run->getRelationMap()) {
564  // Get atoms for non-recursive rules
565  for (auto& rule : relation.second->getRuleMap()) {
566  comma(firstRow, ", \n");
567  ss << '"' << rule.second->getId() << R"_(": [)_";
568  bool firstCol = true;
569  for (auto& atom : rule.second->getAtoms()) {
570  comma(firstCol);
571  std::string relationName = atom.identifier;
572  relationName = relationName.substr(0, relationName.find('('));
573  auto* relation = out.getProgramRun()->getRelation(relationName);
574  std::string relationSize = relation == nullptr ? "" : std::to_string(relation->size());
575  ss << '[';
576  ss << '"' << Tools::cleanJsonOut(Tools::cleanString(atom.rule)) << R"_(", )_";
577  ss << '"' << Tools::cleanJsonOut(atom.identifier) << R"_(", )_";
578  ss << relationSize << ", ";
579  ss << atom.frequency << ']';
580  }
581  ss << "]";
582  }
583  // Get atoms for recursive rules
584  for (auto& iteration : relation.second->getIterations()) {
585  for (auto& rule : iteration->getRules()) {
586  comma(firstRow, ", \n");
587  ss << '"' << rule.second->getId() << R"_(": [)_";
588  bool firstCol = true;
589  for (auto& atom : rule.second->getAtoms()) {
590  comma(firstCol);
591  std::string relationName = atom.identifier;
592  relationName = relationName.substr(0, relationName.find('('));
593  auto* relation = out.getProgramRun()->getRelation(relationName);
594  std::string relationSize =
595  relation == nullptr ? "" : std::to_string(relation->size());
596  ss << '[';
597  ss << '"' << Tools::cleanJsonOut(Tools::cleanString(atom.rule)) << R"_(", )_";
598  ss << '"' << Tools::cleanJsonOut(atom.identifier) << R"_(", )_";
599  ss << relationSize << ", ";
600  ss << atom.frequency << ']';
601  }
602  ss << "]";
603  }
604  }
605  }
606 
607  ss << '}';
608  return ss;
609  }
610 
611  std::string genJson() {
612  std::stringstream ss;
613 
614  genJsonTop(ss);
615  ss << ",\n";
616  genJsonRelations(ss, "topRel", 3);
617  ss << ",\n";
618  genJsonRules(ss, "topRul", 3);
619  ss << ",\n";
620  genJsonRelations(ss, "rel", relationTable.rows.size());
621  ss << ",\n";
622  genJsonRules(ss, "rul", ruleTable.rows.size());
623  ss << ",\n";
624  genJsonUsage(ss);
625  ss << ",\n";
627  ss << ",\n";
628  genJsonAtoms(ss);
629  ss << '\n';
630 
631  ss << "};\n";
632 
633  return ss.str();
634  }
635 
636  void outputHtml(std::string filename = "profiler_html/") {
637  std::cout << "SouffleProf\n";
638  std::cout << "Generating HTML files...\n";
639 
640  DIR* dir;
641  bool exists = false;
642 
643  if (filename.find('/') != std::string::npos) {
644  std::string path = filename.substr(0, filename.find('/'));
645  if ((dir = opendir(path.c_str())) != nullptr) {
646  exists = true;
647  closedir(dir);
648  }
649  if (!exists) {
650  mode_t nMode = 0733; // UNIX style permissions
651  int nError = 0;
652  nError = mkdir(path.c_str(), nMode);
653  if (nError != 0) {
654  std::cerr << "directory " << path
655  << " could not be created. Please create it and try again.";
656  exit(2);
657  }
658  }
659  }
660  std::string filetype = ".html";
661  std::string newFile = filename;
662 
663  if (filename.size() <= filetype.size() ||
664  !std::equal(filetype.rbegin(), filetype.rend(), filename.rbegin())) {
665  int i = 0;
666  do {
667  ++i;
668  newFile = filename + std::to_string(i) + ".html";
669  } while (Tools::file_exists(newFile));
670  }
671 
672  std::ofstream outfile(newFile);
673 
674  outfile << HtmlGenerator::getHtml(genJson());
675 
676  std::cout << "file output to: " << newFile << std::endl;
677  }
678 
679  void quit() {
680  if (updater.joinable()) {
681  updater.join();
682  }
683  }
684 
685  static void help() {
686  std::cout << "\nAvailable profiling commands:" << std::endl;
687  std::printf(" %-30s%-5s %s\n", "rel", "-", "display relation table.");
688  std::printf(" %-30s%-5s %s\n", "rel <relation id>", "-", "display all rules of a given relation.");
689  std::printf(" %-30s%-5s %s\n", "rul", "-", "display rule table");
690  std::printf(" %-30s%-5s %s\n", "rul <rule id>", "-", "display all version of given rule.");
691  std::printf(" %-30s%-5s %s\n", "rul id", "-", "display all rules names and ids.");
692  std::printf(
693  " %-30s%-5s %s\n", "rul id <rule id>", "-", "display the rule name for the given rule id.");
694  std::printf(" %-30s%-5s %s\n", "graph <relation id> <type>", "-",
695  "graph a relation by type: (tot_t/copy_t/tuples).");
696  std::printf(" %-30s%-5s %s\n", "graph <rule id> <type>", "-",
697  "graph recursive(C) rule by type(tot_t/tuples).");
698  std::printf(" %-30s%-5s %s\n", "graph ver <rule id> <type>", "-",
699  "graph recursive(C) rule versions by type(tot_t/copy_t/tuples).");
700  std::printf(" %-30s%-5s %s\n", "top", "-", "display top-level summary of program run.");
701  std::printf(" %-30s%-5s %s\n", "configuration", "-", "display configuration settings for this run.");
702  std::printf(" %-30s%-5s %s\n", "usage [relation id|rule id]", "-",
703  "display CPU usage graphs for a relation or rule.");
704  std::printf(" %-30s%-5s %s\n", "memory", "-", "display memory usage.");
705  std::printf(" %-30s%-5s %s\n", "help", "-", "print this.");
706 
707  std::cout << "\nInteractive mode only commands:" << std::endl;
708  // if (alive) std::printf(" %-30s%-5s %s\n", "stop", "-",
709  // "stop the current live run.");
710  std::printf(" %-30s%-5s %s\n", "limit <row count>", "-", "limit number of results shown.");
711  std::printf(" %-30s%-5s %s\n", "sort <col number>", "-", "sort tables by given column number.");
712  std::printf(" %-30s%-5s %s\n", "q", "-", "exit program.");
713  }
714 
715  void usageRelation(std::string id) {
716  std::vector<std::vector<std::string>> formattedRelationTable =
718  std::string name = "";
719  bool found = false;
720  for (auto& row : formattedRelationTable) {
721  if (row[5] == id || row[6] == id) {
722  name = row[5];
723  found = true;
724  break;
725  }
726  }
727  if (!found) {
728  std::cout << "Relation does not exist.\n";
729  return;
730  }
731 
732  const Relation* rel = out.getProgramRun()->getRelation(name);
733  usage(rel->getEndtime(), rel->getStarttime());
734  }
735 
736  void usageRule(std::string id) {
737  std::vector<std::vector<std::string>> formattedRuleTable = Tools::formatTable(ruleTable, precision);
738  std::string relName = "";
739  std::string srcLocator = "";
740  bool found = false;
741  for (auto& row : formattedRuleTable) {
742  if (row[5] == id || row[6] == id) {
743  relName = row[7];
744  srcLocator = row[10];
745  found = true;
746  break;
747  }
748  }
749  if (!found) {
750  std::cout << "Rule does not exist.\n";
751  return;
752  }
753 
754  auto* rel = out.getProgramRun()->getRelation(relName);
755  if (rel == nullptr) {
756  std::cout << "Relation ceased to exist. Odd." << std::endl;
757  return;
758  }
759  if (rel->getRuleMap().count(srcLocator) == 0) {
760  std::cout << "Rule ceased to exist. Odd." << std::endl;
761  return;
762  }
763 
764  auto& rul = rel->getRuleMap().at(srcLocator);
765  usage(rul->getEndtime(), rul->getStarttime());
766  }
767 
768  std::set<Usage> getUsageStats(size_t width = size_t(-1)) {
769  std::set<Usage> usages;
770  DirectoryEntry* usageStats = dynamic_cast<DirectoryEntry*>(
771  ProfileEventSingleton::instance().getDB().lookupEntry({"program", "usage", "timepoint"}));
772  if (usageStats == nullptr || usageStats->getKeys().size() < 2) {
773  return usages;
774  }
775  std::chrono::microseconds endTime{};
776  std::chrono::microseconds startTime{};
777  std::chrono::microseconds timeStep{};
778  // Translate the string ordered text usage stats to a time ordered binary form.
779  std::set<Usage> allUsages;
780  for (auto& currentKey : usageStats->getKeys()) {
781  Usage currentUsage{};
782  uint64_t cur = std::stoul(currentKey);
783  currentUsage.time = std::chrono::duration<uint64_t, std::micro>(cur);
784  cur = dynamic_cast<SizeEntry*>(
785  usageStats->readDirectoryEntry(currentKey)->readEntry("systemtime"))
786  ->getSize();
787  currentUsage.systemtime = std::chrono::duration<uint64_t, std::micro>(cur);
788  cur = dynamic_cast<SizeEntry*>(usageStats->readDirectoryEntry(currentKey)->readEntry("usertime"))
789  ->getSize();
790  currentUsage.usertime = std::chrono::duration<uint64_t, std::micro>(cur);
791  currentUsage.maxRSS =
792  dynamic_cast<SizeEntry*>(usageStats->readDirectoryEntry(currentKey)->readEntry("maxRSS"))
793  ->getSize();
794 
795  // Duplicate times are possible
796  if (allUsages.find(currentUsage) != allUsages.end()) {
797  auto& existing = *allUsages.find(currentUsage);
798  currentUsage.systemtime = std::max(existing.systemtime, currentUsage.systemtime);
799  currentUsage.usertime = std::max(existing.usertime, currentUsage.usertime);
800  currentUsage.maxRSS = std::max(existing.maxRSS, currentUsage.maxRSS);
801  allUsages.erase(currentUsage);
802  }
803  allUsages.insert(currentUsage);
804  }
805 
806  // cpu times aren't quite recorded in a monotonic way, so skip the invalid ones.
807  for (auto it = ++allUsages.begin(); it != allUsages.end(); ++it) {
808  auto previous = std::prev(it);
809  if (it->usertime < previous->usertime || it->systemtime < previous->systemtime ||
810  it->time == previous->time) {
811  it = allUsages.erase(it);
812  --it;
813  }
814  }
815 
816  // Extract our overall stats
817  startTime = allUsages.begin()->time;
818  endTime = allUsages.rbegin()->time;
819 
820  // If we don't have enough records, just return what we can
821  if (allUsages.size() < width) {
822  return allUsages;
823  }
824 
825  timeStep = (endTime - startTime) / width;
826 
827  // Store the timepoints we need for the graph
828  for (uint32_t i = 1; i <= width; ++i) {
829  auto it = allUsages.upper_bound(Usage{startTime + timeStep * i, 0, {}, {}});
830  if (it != allUsages.begin()) {
831  --it;
832  }
833  usages.insert(*it);
834  }
835 
836  return usages;
837  }
838 
839  void usage(uint32_t height = 20) {
840  usage({}, {}, height);
841  }
842 
843  void usage(std::chrono::microseconds endTime, std::chrono::microseconds startTime, uint32_t height = 20) {
844  uint32_t width = getTermWidth() - 8;
845 
846  std::set<Usage> usages = getUsageStats(width);
847 
848  if (usages.size() < 2) {
849  for (uint8_t i = 0; i < height + 2; ++i) {
850  std::cout << std::endl;
851  }
852  std::cout << "Insufficient data for usage statistics." << std::endl;
853  return;
854  }
855 
856  double maxIntervalUsage = 0;
857 
858  // Extract our overall stats
859  if (startTime.count() == 0) {
860  startTime = usages.begin()->time;
861  }
862  if (endTime.count() == 0) {
863  endTime = usages.rbegin()->time;
864  }
865 
866  if (usages.size() < width) {
867  width = usages.size();
868  }
869 
870  // Find maximum so we can normalise the graph
871  Usage previousUsage{{}, 0, {}, {}};
872  for (auto& currentUsage : usages) {
873  double usageDiff = (currentUsage.systemtime - previousUsage.systemtime + currentUsage.usertime -
874  previousUsage.usertime)
875  .count();
876  usageDiff /= (currentUsage.time - previousUsage.time).count();
877  if (usageDiff > maxIntervalUsage) {
878  maxIntervalUsage = usageDiff;
879  }
880 
881  previousUsage = currentUsage;
882  }
883 
884  double intervalUsagePercent = 100.0 * maxIntervalUsage;
885  std::printf("%11s\n", "cpu total");
886  std::printf("%11s\n", Tools::formatTime(usages.rbegin()->usertime).c_str());
887 
888  // Add columns to the graph
889  char grid[height][width];
890  for (uint32_t i = 0; i < height; ++i) {
891  for (uint32_t j = 0; j < width; ++j) {
892  grid[i][j] = ' ';
893  }
894  }
895 
896  previousUsage = {{}, 0, {}, {}};
897  uint32_t col = 0;
898  for (const Usage& currentUsage : usages) {
899  uint64_t curHeight = 0;
900  uint64_t curSystemHeight = 0;
901  // Usage may be 0
902  if (maxIntervalUsage != 0) {
903  curHeight = (currentUsage.systemtime - previousUsage.systemtime + currentUsage.usertime -
904  previousUsage.usertime)
905  .count();
906  curHeight /= (currentUsage.time - previousUsage.time).count();
907  curHeight *= height / maxIntervalUsage;
908 
909  curSystemHeight = (currentUsage.systemtime - previousUsage.systemtime).count();
910  curSystemHeight /= (currentUsage.time - previousUsage.time).count();
911  curSystemHeight *= height / maxIntervalUsage;
912  }
913  for (uint32_t row = 0; row < curHeight; ++row) {
914  grid[row][col] = '*';
915  }
916  for (uint32_t row = curHeight - curSystemHeight; row < curHeight; ++row) {
917  grid[row][col] = '+';
918  }
919  previousUsage = currentUsage;
920  ++col;
921  }
922 
923  // Print array
924  for (int32_t row = height - 1; row >= 0; --row) {
925  printf("%6d%% ", uint32_t(intervalUsagePercent * (row + 1) / height));
926  for (uint32_t col = 0; col < width; ++col) {
927  std::cout << grid[row][col];
928  }
929  std::cout << std::endl;
930  }
931  for (uint32_t col = 0; col < 8; ++col) {
932  std::cout << ' ';
933  }
934  for (uint32_t col = 0; col < width; ++col) {
935  std::cout << '-';
936  }
937  std::cout << std::endl;
938  }
939 
940  void memoryUsage(uint32_t height = 20) {
941  memoryUsage({}, {}, height);
942  }
943 
944  void memoryUsage(std::chrono::microseconds /* endTime */, std::chrono::microseconds /* startTime */,
945  uint32_t height = 20) {
946  uint32_t width = getTermWidth() - 8;
947  uint64_t maxMaxRSS = 0;
948 
949  std::set<Usage> usages = getUsageStats(width);
950  char grid[height][width];
951  for (uint32_t i = 0; i < height; ++i) {
952  for (uint32_t j = 0; j < width; ++j) {
953  grid[i][j] = ' ';
954  }
955  }
956 
957  for (auto& usage : usages) {
958  maxMaxRSS = std::max(maxMaxRSS, usage.maxRSS);
959  }
960  size_t col = 0;
961  for (const Usage& currentUsage : usages) {
962  uint64_t curHeight = height * currentUsage.maxRSS / maxMaxRSS;
963  for (uint32_t row = 0; row < curHeight; ++row) {
964  grid[row][col] = '*';
965  }
966  ++col;
967  }
968 
969  // Print array
970  for (int32_t row = height - 1; row >= 0; --row) {
971  printf("%6s ", Tools::formatMemory(maxMaxRSS * (row + 1) / height).c_str());
972  for (uint32_t col = 0; col < width; ++col) {
973  std::cout << grid[row][col];
974  }
975  std::cout << std::endl;
976  }
977  for (uint32_t col = 0; col < 8; ++col) {
978  std::cout << ' ';
979  }
980  for (uint32_t col = 0; col < width; ++col) {
981  std::cout << '-';
982  }
983  std::cout << std::endl;
984  }
987 
997  linereader.appendTabCompletion("configuration");
998 
999  // add rel tab completes after the rest so users can see all commands first
1000  for (auto& row : Tools::formatTable(relationTable, precision)) {
1001  linereader.appendTabCompletion("rel " + row[5]);
1002  linereader.appendTabCompletion("graph " + row[5] + " tot_t");
1003  linereader.appendTabCompletion("graph " + row[5] + " copy_t");
1004  linereader.appendTabCompletion("graph " + row[5] + " tuples");
1005  linereader.appendTabCompletion("usage " + row[5]);
1006  }
1007  }
1008 
1009  void configuration() {
1010  std::cout << "Configuration" << '\n';
1011  printf("%30s %s", "Key", "Value\n\n");
1012  for (auto& kvp :
1013  ProfileEventSingleton::instance().getDB().getStringMap({"program", "configuration"})) {
1014  if (kvp.first == "") {
1015  printf("%30s %s\n", "Datalog input file", kvp.second.c_str());
1016  continue;
1017  }
1018  printf("%30s %s\n", kvp.first.c_str(), kvp.second.c_str());
1019  }
1020  std::cout << std::endl;
1021  }
1022 
1023  void top() {
1024  const std::shared_ptr<ProgramRun>& run = out.getProgramRun();
1025  auto* totalRelationsEntry =
1026  dynamic_cast<TextEntry*>(ProfileEventSingleton::instance().getDB().lookupEntry(
1027  {"program", "configuration", "relationCount"}));
1028  auto* totalRulesEntry =
1029  dynamic_cast<TextEntry*>(ProfileEventSingleton::instance().getDB().lookupEntry(
1030  {"program", "configuration", "ruleCount"}));
1031  size_t totalRelations = 0;
1032  if (totalRelationsEntry != nullptr) {
1033  totalRelations = std::stoul(totalRelationsEntry->getText());
1034  } else {
1035  totalRelations = run->getRelationMap().size();
1036  }
1037  size_t totalRules = 0;
1038  if (totalRulesEntry != nullptr) {
1039  totalRules = std::stoul(totalRulesEntry->getText());
1040  } else {
1041  totalRules = ruleTable.getRows().size();
1042  }
1043  std::printf("%11s%10s%10s%10s%10s%20s\n", "runtime", "loadtime", "savetime", "relations", "rules",
1044  "tuples generated");
1045 
1046  std::printf("%11s%10s%10s%10s%10s%14s\n", run->getRuntime().c_str(),
1047  run->formatTime(run->getTotalLoadtime()).c_str(),
1048  run->formatTime(run->getTotalSavetime()).c_str(), run->formatNum(0, totalRelations).c_str(),
1049  run->formatNum(0, totalRules).c_str(),
1050  run->formatNum(precision, run->getTotalSize()).c_str());
1051 
1052  // Progress bar
1053  // Determine number of relations processed
1054  size_t processedRelations = run->getRelationMap().size();
1055  size_t screenWidth = getTermWidth() - 10;
1056  if (alive && totalRelationsEntry != nullptr) {
1057  std::cout << "Progress ";
1058  for (size_t i = 0; i < screenWidth; ++i) {
1059  if (screenWidth * processedRelations / totalRelations > i) {
1060  std::cout << '#';
1061  } else {
1062  std::cout << '_';
1063  }
1064  }
1065  }
1066  std::cout << std::endl;
1067 
1068  std::cout << "Slowest relations to fully evaluate\n";
1069  rel(3, false);
1070  for (size_t i = relationTable.getRows().size(); i < 3; ++i) {
1071  std::cout << "\n";
1072  }
1073  std::cout << "Slowest rules to fully evaluate\n";
1074  rul(3, false);
1075  for (size_t i = ruleTable.getRows().size(); i < 3; ++i) {
1076  std::cout << "\n";
1077  }
1078 
1079  usage(10);
1080  }
1081 
1082  void setResultLimit(size_t limit) {
1083  resultLimit = limit;
1084  }
1085 
1086  void rel(size_t limit, bool showLimit = true) {
1088  std::cout << " ----- Relation Table -----\n";
1089  std::printf("%8s%8s%8s%8s%8s%8s%8s%8s%8s%6s %s\n\n", "TOT_T", "NREC_T", "REC_T", "COPY_T", "LOAD_T",
1090  "SAVE_T", "TUPLES", "READS", "TUP/s", "ID", "NAME");
1091  size_t count = 0;
1092  for (auto& row : Tools::formatTable(relationTable, precision)) {
1093  if (++count > limit) {
1094  if (showLimit) {
1095  std::cout << (relationTable.getRows().size() - resultLimit) << " rows not shown"
1096  << std::endl;
1097  }
1098  break;
1099  }
1100  std::printf("%8s%8s%8s%8s%8s%8s%8s%8s%8s%6s %s\n", row[0].c_str(), row[1].c_str(), row[2].c_str(),
1101  row[3].c_str(), row[9].c_str(), row[10].c_str(), row[4].c_str(), row[12].c_str(),
1102  row[8].c_str(), row[6].c_str(), row[5].c_str());
1103  }
1104  }
1105 
1106  void rul(size_t limit, bool showLimit = true) {
1108  std::cout << " ----- Rule Table -----\n";
1109  std::printf(
1110  "%8s%8s%8s%8s%8s%8s %s\n\n", "TOT_T", "NREC_T", "REC_T", "TUPLES", "TUP/s", "ID", "RELATION");
1111  size_t count = 0;
1112  for (auto& row : Tools::formatTable(ruleTable, precision)) {
1113  if (++count > limit) {
1114  if (showLimit) {
1115  std::cout << (ruleTable.getRows().size() - resultLimit) << " rows not shown" << std::endl;
1116  }
1117  break;
1118  }
1119  std::printf("%8s%8s%8s%8s%8s%8s %s\n", row[0].c_str(), row[1].c_str(), row[2].c_str(),
1120  row[4].c_str(), row[9].c_str(), row[6].c_str(), row[7].c_str());
1121  }
1122  }
1123 
1124  void id(std::string col) {
1125  ruleTable.sort(6);
1126  std::vector<std::vector<std::string>> table = Tools::formatTable(ruleTable, precision);
1127 
1128  if (col == "0") {
1129  std::printf("%7s%2s%s\n\n", "ID", "", "NAME");
1130  for (auto& row : table) {
1131  std::printf("%7s%2s%s\n", row[6].c_str(), "", row[5].c_str());
1132  }
1133  } else {
1134  for (auto& row : table) {
1135  if (row[6] == col) {
1136  std::printf("%7s%2s%s\n", row[6].c_str(), "", row[5].c_str());
1137  }
1138  }
1139  }
1140  }
1141 
1142  void relRul(std::string str) {
1144 
1145  std::vector<std::vector<std::string>> formattedRuleTable = Tools::formatTable(ruleTable, precision);
1146  std::vector<std::vector<std::string>> formattedRelationTable =
1148 
1149  std::cout << " ----- Rules of a Relation -----\n";
1150  std::printf("%8s%8s%8s%8s%8s %s\n\n", "TOT_T", "NREC_T", "REC_T", "TUPLES", "ID", "NAME");
1151  std::string name = "";
1152  for (auto& row : formattedRelationTable) {
1153  // Test for relation name or relation id
1154  if (row[5] == str || row[6] == str) {
1155  std::printf("%8s%8s%8s%8s%8s %s\n", row[0].c_str(), row[1].c_str(), row[2].c_str(),
1156  row[4].c_str(), row[6].c_str(), row[5].c_str());
1157  name = row[5];
1158  break;
1159  }
1160  }
1161  std::cout << " ---------------------------------------------------------\n";
1162  for (auto& row : formattedRuleTable) {
1163  if (row[7] == name) {
1164  std::printf("%8s%8s%8s%8s%8s %s\n", row[0].c_str(), row[1].c_str(), row[2].c_str(),
1165  row[4].c_str(), row[6].c_str(), row[7].c_str());
1166  }
1167  }
1168  std::string src = "";
1169  const std::shared_ptr<ProgramRun>& run = out.getProgramRun();
1170  if (run->getRelation(name) != nullptr) {
1171  src = run->getRelation(name)->getLocator();
1172  }
1173  std::cout << "\nSrc locator: " << src << "\n\n";
1174  for (auto& row : formattedRuleTable) {
1175  if (row[7] == name) {
1176  std::printf("%7s%2s%s\n", row[6].c_str(), "", row[5].c_str());
1177  }
1178  }
1179  }
1180 
1181  void verRul(std::string str) {
1182  if (str.find(".") == std::string::npos) {
1183  std::cout << "Rule does not exist\n";
1184  return;
1185  }
1186  std::vector<std::string> part = Tools::split(str, ".");
1187  std::string strRel = "R" + part[0].substr(1);
1188 
1189  Table versionTable = out.getVersions(strRel, str);
1190  versionTable.sort(sortColumn);
1191 
1192  ruleTable.sort(sortColumn); // why isnt it sorted in the original java?!?
1193 
1194  std::vector<std::vector<std::string>> formattedRuleTable = Tools::formatTable(ruleTable, precision);
1195 
1196  bool found = false;
1197  std::string ruleName;
1198  std::string srcLocator;
1199  // Check that the rule exists, and print it out if so.
1200  for (auto& row : formattedRuleTable) {
1201  if (row[6] == str) {
1202  std::cout << row[5] << std::endl;
1203  found = true;
1204  ruleName = row[5];
1205  srcLocator = row[10];
1206  }
1207  }
1208 
1209  // If the rule exists, print out the source locator.
1210  if (found) {
1211  if (versionTable.rows.size() > 0) {
1212  if (versionTable.rows[0]->cells[9] != nullptr) {
1213  std::cout << "Src locator-: " << (*versionTable.rows[0])[9]->getStringVal() << "\n\n";
1214  } else {
1215  std::cout << "Src locator-: -\n\n";
1216  }
1217  } else if (formattedRuleTable.size() > 0) {
1218  std::cout << "Src locator-: " << formattedRuleTable[0][10] << "\n\n";
1219  }
1220  }
1221 
1222  // Print out the versions of this rule.
1223  std::cout << " ----- Rule Versions Table -----\n";
1224  std::printf("%8s%8s%8s%16s%6s\n\n", "TOT_T", "NREC_T", "REC_T", "TUPLES", "VER");
1225  for (auto& row : formattedRuleTable) {
1226  if (row[6] == str) {
1227  std::printf("%8s%8s%8s%16s%6s\n", row[0].c_str(), row[1].c_str(), row[2].c_str(),
1228  row[4].c_str(), "");
1229  }
1230  }
1231  std::cout << " ---------------------------------------------\n";
1232  for (auto& _row : versionTable.rows) {
1233  Row row = *_row;
1234 
1235  std::printf("%8s%8s%8s%16s%6s\n", row[0]->toString(precision).c_str(),
1236  row[1]->toString(precision).c_str(), row[2]->toString(precision).c_str(),
1237  row[4]->toString(precision).c_str(), row[8]->toString(precision).c_str());
1238  Table atom_table = out.getVersionAtoms(strRel, srcLocator, row[8]->getLongVal());
1239  verAtoms(atom_table);
1240  }
1241 
1242  if (!versionTable.rows.empty()) {
1243  return;
1244  }
1245 
1246  Table atom_table = out.getAtomTable(strRel, str);
1247  verAtoms(atom_table, ruleName);
1248  }
1249 
1250  void iterRel(std::string c, std::string col) {
1251  const std::shared_ptr<ProgramRun>& run = out.getProgramRun();
1252  std::vector<std::vector<std::string>> table = Tools::formatTable(relationTable, -1);
1253  std::vector<std::shared_ptr<Iteration>> iter;
1254  for (auto& row : table) {
1255  if (row[6] == c) {
1256  std::printf("%4s%2s%s\n\n", row[6].c_str(), "", row[5].c_str());
1257  iter = run->getRelation(row[5])->getIterations();
1258  if (col == "tot_t") {
1259  std::vector<std::chrono::microseconds> list;
1260  for (auto& i : iter) {
1261  list.emplace_back(i->getRuntime());
1262  }
1263  std::printf("%4s %s\n\n", "NO", "RUNTIME");
1264  graphByTime(list);
1265  } else if (col == "copy_t") {
1266  std::vector<std::chrono::microseconds> list;
1267  for (auto& i : iter) {
1268  list.emplace_back(i->getCopytime());
1269  }
1270  std::printf("%4s %s\n\n", "NO", "COPYTIME");
1271  graphByTime(list);
1272  } else if (col == "tuples") {
1273  std::vector<size_t> list;
1274  for (auto& i : iter) {
1275  list.emplace_back(i->size());
1276  }
1277  std::printf("%4s %s\n\n", "NO", "TUPLES");
1278  graphBySize(list);
1279  }
1280  return;
1281  }
1282  }
1283  for (auto& row : table) {
1284  if (row[5] == c) {
1285  std::printf("%4s%2s%s\n\n", row[6].c_str(), "", row[5].c_str());
1286  const std::shared_ptr<ProgramRun>& run = out.getProgramRun();
1287  iter = run->getRelation(row[5])->getIterations();
1288  if (col == "tot_t") {
1289  std::vector<std::chrono::microseconds> list;
1290  for (auto& i : iter) {
1291  list.emplace_back(i->getRuntime());
1292  }
1293  std::printf("%4s %s\n\n", "NO", "RUNTIME");
1294  graphByTime(list);
1295  } else if (col == "copy_t") {
1296  std::vector<std::chrono::microseconds> list;
1297  for (auto& i : iter) {
1298  list.emplace_back(i->getCopytime());
1299  }
1300  std::printf("%4s %s\n\n", "NO", "COPYTIME");
1301  graphByTime(list);
1302  } else if (col == "tuples") {
1303  std::vector<size_t> list;
1304  for (auto& i : iter) {
1305  list.emplace_back(i->size());
1306  }
1307  std::printf("%4s %s\n\n", "NO", "TUPLES");
1308  graphBySize(list);
1309  }
1310  return;
1311  }
1312  }
1313  }
1314 
1315  void iterRul(std::string c, std::string col) {
1316  std::vector<std::vector<std::string>> table = Tools::formatTable(ruleTable, precision);
1317  std::vector<std::shared_ptr<Iteration>> iter;
1318  for (auto& row : table) {
1319  if (row[6] == c) {
1320  std::printf("%6s%2s%s\n\n", row[6].c_str(), "", row[5].c_str());
1321  const std::shared_ptr<ProgramRun>& run = out.getProgramRun();
1322  iter = run->getRelation(row[7])->getIterations();
1323  if (col == "tot_t") {
1324  std::vector<std::chrono::microseconds> list;
1325  for (auto& i : iter) {
1326  bool add = false;
1327  std::chrono::microseconds totalTime{};
1328  for (auto& rul : i->getRules()) {
1329  if (rul.second->getId() == c) {
1330  totalTime += rul.second->getRuntime();
1331  add = true;
1332  }
1333  }
1334  if (add) {
1335  list.emplace_back(totalTime);
1336  }
1337  }
1338  std::printf("%4s %s\n\n", "NO", "RUNTIME");
1339  graphByTime(list);
1340  } else if (col == "tuples") {
1341  std::vector<size_t> list;
1342  for (auto& i : iter) {
1343  bool add = false;
1344  size_t totalSize = 0L;
1345  for (auto& rul : i->getRules()) {
1346  if (rul.second->getId() == c) {
1347  totalSize += rul.second->size();
1348  add = true;
1349  }
1350  }
1351  if (add) {
1352  list.emplace_back(totalSize);
1353  }
1354  }
1355  std::printf("%4s %s\n\n", "NO", "TUPLES");
1356  graphBySize(list);
1357  }
1358  break;
1359  }
1360  }
1361  }
1362 
1363  void verGraph(std::string c, std::string col) {
1364  if (c.find('.') == std::string::npos) {
1365  std::cout << "Rule does not exist";
1366  return;
1367  }
1368 
1369  std::vector<std::string> part = Tools::split(c, ".");
1370  std::string strRel = "R" + part[0].substr(1);
1371 
1372  Table versionTable = out.getVersions(strRel, c);
1373  std::printf("%6s%2s%s\n\n", (*versionTable.rows[0])[6]->toString(0).c_str(), "",
1374  (*versionTable.rows[0])[5]->toString(0).c_str());
1375  if (col == "tot_t") {
1376  std::vector<std::chrono::microseconds> list;
1377  for (auto& row : versionTable.rows) {
1378  list.emplace_back((*row)[0]->getTimeVal());
1379  }
1380  std::printf("%4s %s\n\n", "NO", "RUNTIME");
1381  graphByTime(list);
1382  } else if (col == "copy_t") {
1383  std::vector<std::chrono::microseconds> list;
1384  for (auto& row : versionTable.rows) {
1385  list.emplace_back((*row)[3]->getTimeVal());
1386  }
1387  std::printf("%4s %s\n\n", "NO", "COPYTIME");
1388  graphByTime(list);
1389  } else if (col == "tuples") {
1390  std::vector<size_t> list;
1391  for (auto& row : versionTable.rows) {
1392  list.emplace_back((*row)[4]->getLongVal());
1393  }
1394  std::printf("%4s %s\n\n", "NO", "TUPLES");
1395  graphBySize(list);
1396  }
1397  }
1398 
1399  void graphByTime(std::vector<std::chrono::microseconds> list) {
1400  std::chrono::microseconds max{};
1401  for (auto& d : list) {
1402  if (d > max) {
1403  max = d;
1404  }
1405  }
1406 
1407  std::sort(list.begin(), list.end());
1408  std::reverse(list.begin(), list.end());
1409  int i = 0;
1410  for (auto& d : list) {
1411  uint32_t len = 67.0 * d.count() / max.count();
1412  std::string bar = "";
1413  for (uint32_t j = 0; j < len; j++) {
1414  bar += "*";
1415  }
1416 
1417  std::printf("%4d %10.8f | %s\n", i++, (d.count() / 1000000.0), bar.c_str());
1418  }
1419  }
1420 
1421  void graphBySize(std::vector<size_t> list) {
1422  size_t max = 0;
1423  for (auto& l : list) {
1424  if (l > max) {
1425  max = l;
1426  }
1427  }
1428  std::sort(list.begin(), list.end());
1429  std::reverse(list.begin(), list.end());
1430  uint32_t i = 0;
1431  for (auto& l : list) {
1432  size_t len = max == 0 ? 0 : 64.0 * l / max;
1433  std::string bar = "";
1434  for (uint32_t j = 0; j < len; j++) {
1435  bar += "*";
1436  }
1437 
1438  std::printf("%4d %8s | %s\n", i++, Tools::formatNum(precision, l).c_str(), bar.c_str());
1439  }
1440  }
1441 
1442 protected:
1443  void verAtoms(Table& atomTable, const std::string& ruleName = "") {
1444  // If there are no subrules then just print out any atoms found
1445  // If we do find subrules, then label atoms with their subrules.
1446  if (atomTable.rows.empty()) {
1447  return;
1448  }
1449  bool firstRun = true;
1450  std::string lastRule = ruleName;
1451  for (auto& _row : atomTable.rows) {
1452  Row& row = *_row;
1453  std::string rule = row[0]->toString(precision);
1454  if (rule != lastRule) {
1455  lastRule = rule;
1456  std::cout << " " << row[0]->toString(precision) << std::endl;
1457  firstRun = true;
1458  }
1459  if (firstRun) {
1460  std::printf(" %-16s%-16s%s\n", "FREQ", "RELSIZE", "ATOM");
1461  firstRun = false;
1462  }
1463  std::string relationName = row[1]->getStringVal();
1464  relationName = relationName.substr(0, relationName.find('('));
1465  auto* relation = out.getProgramRun()->getRelation(relationName);
1466  std::string relationSize = relation == nullptr ? "--" : std::to_string(relation->size());
1467  std::printf(" %-16s%-16s%s\n", row[3]->toString(precision).c_str(), relationSize.c_str(),
1468  row[1]->getStringVal().c_str());
1469  }
1470  std::cout << '\n';
1471  }
1472  void updateDB() {
1473  reader->processFile();
1476  }
1477 
1478  uint32_t getTermWidth() {
1479  struct winsize w {};
1480  ioctl(0, TIOCGWINSZ, &w);
1481  uint32_t width = w.ws_col > 0 ? w.ws_col : 80;
1482  return width;
1483  }
1484 };
1485 
1486 } // namespace profile
1487 } // namespace souffle
souffle::profile::OutputProcessor::getVersionAtoms
Table getVersionAtoms(std::string strRel, std::string strRul, int version) const
Definition: OutputProcessor.h:345
souffle::Table::size
std::size_t size() const
Definition: Table.h:115
souffle::profile::Tui::Usage::systemtime
std::chrono::microseconds systemtime
Definition: Tui.h:75
usageRule
void usageRule(std::string id)
Definition: Tui.h:736
Table.h
souffle::profile::Tui::Tui
Tui()
Definition: Tui.h:99
souffle::profile::OutputProcessor::getRulTable
Table getRulTable() const
Definition: OutputProcessor.h:121
souffle::profile::Tools::file_exists
bool file_exists(const std::string &name)
Definition: StringUtils.h:196
genJson
std::string genJson()
Definition: Tui.h:611
tinyformat::printf
void printf(const char *fmt)
Definition: tinyformat.h:1101
CellInterface.h
souffle::profile::InputReader::appendTabCompletion
void appendTabCompletion(std::vector< std::string > commands)
Definition: UserInputReader.h:145
souffle::profile::HtmlGenerator::getHtml
static std::string getHtml(std::string json)
Definition: HtmlGenerator.h:32
verAtoms
void verAtoms(Table &atomTable, const std::string &ruleName="")
Definition: Tui.h:1443
souffle::profile::OutputProcessor::getRelTable
Table getRelTable() const
Definition: OutputProcessor.h:77
souffle::profile::Tui::runProf
void runProf()
Definition: Tui.h:216
souffle::profile::Tools::cleanString
std::string cleanString(std::string val)
Remove and \t characters, and \t sequence of two chars, and wrapping quotes.
Definition: StringUtils.h:206
souffle::profile::Tools::formatTable
std::vector< std::vector< std::string > > formatTable(Table table, int precision)
Definition: StringUtils.h:145
souffle::profile::Tui::Usage::maxRSS
uint64_t maxRSS
Definition: Tui.h:74
souffle::profile::Tools::trimWhitespace
std::string trimWhitespace(std::string str)
Definition: StringUtils.h:182
souffle::profile::Tui::sortColumn
int sortColumn
Definition: Tui.h:63
souffle::profile::Table::rows
std::vector< std::shared_ptr< Row > > rows
Definition: Table.h:26
souffle::genJsonRules
std::stringstream & genJsonRules(std::stringstream &ss, const std::string &name, size_t maxRows)
Definition: Tui.h:339
souffle::profile::Tui::Tui
Tui(std::string filename, bool live, bool)
Definition: Tui.h:83
souffle::profile::Table::sort
void sort(int col_num)
Definition: Table.h:38
souffle::profile::Tui::updater
std::thread updater
Definition: Tui.h:62
souffle::profile::Tui::relationTable
Table relationTable
Definition: Tui.h:65
souffle::profile::Tui::reader
std::shared_ptr< Reader > reader
Definition: Tui.h:67
Reader.h
souffle::profile::Tui::Usage::time
std::chrono::microseconds time
Definition: Tui.h:73
verRul
void verRul(std::string str)
Definition: Tui.h:1181
souffle::ProfileEventSingleton::instance
static ProfileEventSingleton & instance()
get instance
Definition: ProfileEvent.h:70
souffle::genJsonUsage
std::stringstream & genJsonUsage(std::stringstream &ss)
Definition: Tui.h:468
iterRel
void iterRel(std::string c, std::string col)
Definition: Tui.h:1250
relation
Relation & relation
Definition: Reader.h:130
updateDB
void updateDB()
Definition: Tui.h:1472
help
static void help()
Definition: Tui.h:685
MiscUtil.h
UserInputReader.h
souffle::profile::Tools::formatMemory
std::string formatMemory(uint64_t kbytes)
Definition: StringUtils.h:97
HtmlGenerator.h
souffle::profile::InputReader::getInput
std::string getInput()
Definition: UserInputReader.h:80
souffle::profile::Tui::Usage::operator<
bool operator<(const Usage &other) const
Definition: Tui.h:77
setResultLimit
void setResultLimit(size_t limit)
Definition: Tui.h:1082
iteration
Iteration & iteration
Definition: Reader.h:129
top
void top()
Definition: Tui.h:1023
souffle::profile::Tui
Definition: Tui.h:56
j
var j
Definition: htmlJsChartistMin.h:15
ProfileEvent.h
OutputProcessor.h
str
const std::string & str
Definition: json11.h:662
ProgramRun.h
souffle::profile::Tui::genJsonTop
std::stringstream & genJsonTop(std::stringstream &ss)
Definition: Tui.h:258
souffle::toString
const std::string & toString(const std::string &str)
A generic function converting strings into strings (trivial case).
Definition: StringUtil.h:234
genJsonAtoms
std::stringstream & genJsonAtoms(std::stringstream &ss)
Definition: Tui.h:549
setupTabCompletion
void setupTabCompletion()
Definition: Tui.h:985
souffle::now
time_point now()
Definition: MiscUtil.h:89
l
var l
Definition: htmlJsChartistMin.h:15
souffle::profile::Tui::alive
bool alive
Definition: Tui.h:61
souffle::profile::Tools::formatNum
std::string formatNum(double amount)
Definition: StringUtils.h:40
iterRul
void iterRul(std::string c, std::string col)
Definition: Tui.h:1315
i
size_t i
Definition: json11.h:663
souffle::profile::OutputProcessor
Definition: OutputProcessor.h:34
souffle::profile::InputReader
Definition: UserInputReader.h:24
getUsageStats
std::set< Usage > getUsageStats(size_t width=size_t(-1))
Definition: Tui.h:768
relRul
void relRul(std::string str)
Definition: Tui.h:1142
souffle::profile::Tui::genJsonRelations
std::stringstream & genJsonRelations(std::stringstream &ss, const std::string &name, size_t maxRows)
Definition: Tui.h:269
souffle::profile::Tui::runCommand
void runCommand(std::vector< std::string > c)
Definition: Tui.h:125
souffle::profile::Tui::~Tui
~Tui()
Definition: Tui.h:119
souffle::profile::InputReader::setPrompt
void setPrompt(std::string prompt)
Definition: UserInputReader.h:142
souffle::profile::Row
Definition: Row.h:22
graphBySize
void graphBySize(std::vector< size_t > list)
Definition: Tui.h:1421
souffle::profile::Tui::out
OutputProcessor out
Definition: Tui.h:58
rul
void rul(size_t limit, bool showLimit=true)
Definition: Tui.h:1106
souffle::profile::Tools::cleanJsonOut
std::string cleanJsonOut(std::string value)
escape escapes and quotes, and remove surrounding quotes
Definition: StringUtils.h:232
Iteration.h
souffle::test::count
int count(const C &c)
Definition: table_test.cpp:40
souffle::profile::Tools::split
std::vector< std::string > split(std::string toSplit, std::string delimiter)
split on the delimiter
Definition: StringUtils.h:162
graphByTime
void graphByTime(std::vector< std::chrono::microseconds > list)
Definition: Tui.h:1399
souffle::profile::Tui::f_name
std::string f_name
Definition: Tui.h:60
configuration
void configuration()
Definition: Tui.h:1009
souffle::profile::InputReader::hasReceivedInput
bool hasReceivedInput()
Definition: UserInputReader.h:52
StringUtils.h
usageRelation
void usageRelation(std::string id)
Definition: Tui.h:715
souffle::profile::OutputProcessor::getVersions
Table getVersions(std::string strRel, std::string strRul) const
Definition: OutputProcessor.h:276
souffle::profile::Tui::resultLimit
size_t resultLimit
Limit results shown. Default value chosen to approximate unlimited.
Definition: Tui.h:70
souffle::Table
Definition: Table.h:33
souffle::profile::Tools::formatTime
std::string formatTime(std::chrono::microseconds number)
Definition: StringUtils.h:108
souffle::profile::OutputProcessor::getAtomTable
Table getAtomTable(std::string strRel, std::string strRul) const
Definition: OutputProcessor.h:199
souffle::profile::InputReader::addHistory
void addHistory(std::string hist)
Definition: UserInputReader.h:202
souffle::profile::Tui::ruleTable
Table ruleTable
Definition: Tui.h:66
souffle::profile::Tui::Usage
Definition: Tui.h:72
Rule.h
rule
Rule & rule
Definition: Reader.h:85
souffle::profile::OutputProcessor::getProgramRun
const std::shared_ptr< ProgramRun > & getProgramRun() const
Definition: OutputProcessor.h:43
souffle::profile::Tui::linereader
InputReader linereader
Definition: Tui.h:68
souffle::profile::Tui::loaded
bool loaded
Definition: Tui.h:59
souffle
Definition: AggregateOp.h:25
d
else d
Definition: htmlJsChartistMin.h:15
Row.h
souffle::profile::InputReader::clearTabCompletion
void clearTabCompletion()
Definition: UserInputReader.h:151
ProfileDatabase.h
souffle::profile::Tui::precision
int precision
Definition: Tui.h:64
verGraph
void verGraph(std::string c, std::string col)
Definition: Tui.h:1363
outputHtml
void outputHtml(std::string filename="profiler_html/")
Definition: Tui.h:636
memoryUsage
void memoryUsage(uint32_t height=20)
Definition: Tui.h:940
souffle::ProfileEventSingleton::getDB
const profile::ProfileDatabase & getDB() const
Definition: ProfileEvent.h:168
rel
void rel(size_t limit, bool showLimit=true)
Definition: Tui.h:1086
usage
void usage(uint32_t height=20)
Definition: Tui.h:839
id
void id(std::string col)
Definition: Tui.h:1124
quit
void quit()
Definition: Tui.h:679
Relation.h
genJsonConfiguration
std::stringstream & genJsonConfiguration(std::stringstream &ss)
Definition: Tui.h:528
souffle::profile::Table::getRows
std::vector< std::shared_ptr< Row > > getRows()
Definition: Table.h:34
getTermWidth
uint32_t getTermWidth()
Definition: Tui.h:1478
souffle::profile::Tui::Usage::usertime
std::chrono::microseconds usertime
Definition: Tui.h:76
souffle::profile::Table
Definition: Table.h:24
souffle::profile::ss
class souffle::profile::Tui ss
Definition: Tui.h:336