OILS / vendor / souffle / profile / Tui.h View on Github | oilshell.org

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