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

465 lines, 276 significant
1#pragma once
2
3#include "souffle/utility/ContainerUtil.h"
4#include "souffle/utility/MiscUtil.h"
5#include "souffle/utility/json11.h"
6#include <cassert>
7#include <chrono>
8#include <cstddef>
9#include <fstream>
10#include <iostream>
11#include <iterator>
12#include <map>
13#include <memory>
14#include <mutex>
15#include <set>
16#include <stdexcept>
17#include <string>
18#include <utility>
19#include <vector>
20
21namespace souffle {
22namespace profile {
23
24class DirectoryEntry;
25class DurationEntry;
26class SizeEntry;
27class TextEntry;
28class TimeEntry;
29
30/**
31 * Visitor Interface
32 */
33class Visitor {
34public:
35 virtual ~Visitor() = default;
36
37 // visit entries in a directory
38 virtual void visit(DirectoryEntry& e);
39
40 // visit entries
41 virtual void visit(DurationEntry&) {}
42 virtual void visit(SizeEntry&) {}
43 virtual void visit(TextEntry&) {}
44 virtual void visit(TimeEntry&) {}
45};
46
47/**
48 * Entry class
49 *
50 * abstract class for a key/value entry in a hierarchical database
51 */
52class Entry {
53private:
54 // entry key
55 std::string key;
56
57public:
58 Entry(std::string key) : key(std::move(key)) {}
59 virtual ~Entry() = default;
60
61 // get key
62 const std::string& getKey() const {
63 return key;
64 };
65
66 // accept visitor
67 virtual void accept(Visitor& v) = 0;
68
69 // print
70 virtual void print(std::ostream& os, int tabpos) const = 0;
71};
72
73/**
74 * DirectoryEntry entry
75 */
76class DirectoryEntry : public Entry {
77private:
78 std::map<std::string, Own<Entry>> entries;
79 mutable std::mutex lock;
80
81public:
82 DirectoryEntry(const std::string& name) : Entry(name) {}
83
84 // get keys
85 const std::set<std::string> getKeys() const {
86 std::set<std::string> result;
87 std::lock_guard<std::mutex> guard(lock);
88 for (auto const& cur : entries) {
89 result.insert(cur.first);
90 }
91 return result;
92 }
93
94 // write entry
95 Entry* writeEntry(Own<Entry> entry) {
96 assert(entry != nullptr && "null entry");
97 std::lock_guard<std::mutex> guard(lock);
98 const std::string& keyToWrite = entry->getKey();
99 // Don't rewrite an existing entry
100 if (entries.count(keyToWrite) == 0) {
101 entries[keyToWrite] = std::move(entry);
102 }
103 return entries[keyToWrite].get();
104 }
105
106 // read entry
107 Entry* readEntry(const std::string& keyToRead) const {
108 std::lock_guard<std::mutex> guard(lock);
109 auto it = entries.find(keyToRead);
110 if (it != entries.end()) {
111 return (*it).second.get();
112 } else {
113 return nullptr;
114 }
115 }
116
117 // read directory
118 DirectoryEntry* readDirectoryEntry(const std::string& keyToRead) const {
119 return as<DirectoryEntry>(readEntry(keyToRead));
120 }
121
122 // accept visitor
123 void accept(Visitor& v) override {
124 v.visit(*this);
125 }
126
127 // print directory
128 void print(std::ostream& os, int tabpos) const override {
129 os << std::string(tabpos, ' ') << '"' << getKey() << "\": {" << std::endl;
130 bool first{true};
131 for (auto const& cur : entries) {
132 if (!first) {
133 os << ',' << std::endl;
134 } else {
135 first = false;
136 }
137 cur.second->print(os, tabpos + 1);
138 }
139 os << std::endl << std::string(tabpos, ' ') << '}';
140 }
141};
142
143/**
144 * SizeEntry
145 */
146class SizeEntry : public Entry {
147private:
148 std::size_t size; // size
149public:
150 SizeEntry(const std::string& key, std::size_t size) : Entry(key), size(size) {}
151
152 // get size
153 std::size_t getSize() const {
154 return size;
155 }
156
157 // accept visitor
158 void accept(Visitor& v) override {
159 v.visit(*this);
160 }
161
162 // print entry
163 void print(std::ostream& os, int tabpos) const override {
164 os << std::string(tabpos, ' ') << "\"" << getKey() << "\": " << size;
165 }
166};
167
168/**
169 * TextEntry
170 */
171class TextEntry : public Entry {
172private:
173 // entry text
174 std::string text;
175
176public:
177 TextEntry(const std::string& key, std::string text) : Entry(key), text(std::move(text)) {}
178
179 // get text
180 const std::string& getText() const {
181 return text;
182 }
183
184 // accept visitor
185 void accept(Visitor& v) override {
186 v.visit(*this);
187 }
188
189 // write size entry
190 void print(std::ostream& os, int tabpos) const override {
191 os << std::string(tabpos, ' ') << "\"" << getKey() << "\": \"" << text << "\"";
192 }
193};
194
195/**
196 * Duration Entry
197 */
198class DurationEntry : public Entry {
199private:
200 // duration start
201 microseconds start;
202
203 // duration end
204 microseconds end;
205
206public:
207 DurationEntry(const std::string& key, microseconds start, microseconds end)
208 : Entry(key), start(start), end(end) {}
209
210 // get start
211 microseconds getStart() const {
212 return start;
213 }
214
215 // get end
216 microseconds getEnd() const {
217 return end;
218 }
219
220 // accept visitor
221 void accept(Visitor& v) override {
222 v.visit(*this);
223 }
224
225 // write size entry
226 void print(std::ostream& os, int tabpos) const override {
227 os << std::string(tabpos, ' ') << '"' << getKey();
228 os << R"_(": { "start": )_";
229 os << start.count();
230 os << ", \"end\": ";
231 os << end.count();
232 os << '}';
233 }
234};
235
236/**
237 * Time Entry
238 */
239class TimeEntry : public Entry {
240private:
241 // time since start
242 microseconds time;
243
244public:
245 TimeEntry(const std::string& key, microseconds time) : Entry(key), time(time) {}
246
247 // get start
248 microseconds getTime() const {
249 return time;
250 }
251
252 // accept visitor
253 void accept(Visitor& v) override {
254 v.visit(*this);
255 }
256
257 // write size entry
258 void print(std::ostream& os, int tabpos) const override {
259 os << std::string(tabpos, ' ') << '"' << getKey();
260 os << R"_(": { "time": )_";
261 os << time.count();
262 os << '}';
263 }
264};
265
266inline void Visitor::visit(DirectoryEntry& e) {
267 std::cout << "Dir " << e.getKey() << "\n";
268 for (const auto& cur : e.getKeys()) {
269 std::cout << "\t :" << cur << "\n";
270 e.readEntry(cur)->accept(*this);
271 }
272}
273
274class Counter : public Visitor {
275private:
276 std::size_t ctr{0};
277 std::string key;
278
279public:
280 Counter(std::string key) : key(std::move(key)) {}
281 void visit(SizeEntry& e) override {
282 std::cout << "Size entry : " << e.getKey() << " " << e.getSize() << "\n";
283 if (e.getKey() == key) {
284 ctr += e.getSize();
285 }
286 }
287 std::size_t getCounter() const {
288 return ctr;
289 }
290};
291
292/**
293 * Hierarchical databas
294 */
295class ProfileDatabase {
296private:
297 Own<DirectoryEntry> root;
298
299protected:
300 /**
301 * Find path: if directories along the path do not exist, create them.
302 */
303 DirectoryEntry* lookupPath(const std::vector<std::string>& path) {
304 DirectoryEntry* dir = root.get();
305 for (const std::string& key : path) {
306 assert(!key.empty() && "Key is empty!");
307 DirectoryEntry* newDir = dir->readDirectoryEntry(key);
308 if (newDir == nullptr) {
309 newDir = as<DirectoryEntry>(dir->writeEntry(mk<DirectoryEntry>(key)));
310 }
311 assert(newDir != nullptr && "Attempting to overwrite an existing entry");
312 dir = newDir;
313 }
314 return dir;
315 }
316
317 void parseJson(const json11::Json& json, Own<DirectoryEntry>& node) {
318 for (auto& cur : json.object_items()) {
319 if (cur.second.is_object()) {
320 std::string err;
321 // Duration entries are also maps
322 if (cur.second.has_shape(
323 {{"start", json11::Json::NUMBER}, {"end", json11::Json::NUMBER}}, err)) {
324 auto start = std::chrono::microseconds(cur.second["start"].long_value());
325 auto end = std::chrono::microseconds(cur.second["end"].long_value());
326 node->writeEntry(mk<DurationEntry>(cur.first, start, end));
327 } else if (cur.second.has_shape({{"time", json11::Json::NUMBER}}, err)) {
328 auto time = std::chrono::microseconds(cur.second["time"].long_value());
329 node->writeEntry(mk<TimeEntry>(cur.first, time));
330 } else {
331 auto dir = mk<DirectoryEntry>(cur.first);
332 parseJson(cur.second, dir);
333 node->writeEntry(std::move(dir));
334 }
335 } else if (cur.second.is_string()) {
336 node->writeEntry(mk<TextEntry>(cur.first, cur.second.string_value()));
337 } else if (cur.second.is_number()) {
338 node->writeEntry(mk<SizeEntry>(cur.first, cur.second.long_value()));
339 } else {
340 std::string err;
341 cur.second.dump(err);
342 std::cerr << "Unknown types in profile log: " << cur.first << ": " << err << std::endl;
343 }
344 }
345 }
346
347public:
348 ProfileDatabase() : root(mk<DirectoryEntry>("root")) {}
349
350 ProfileDatabase(const std::string& filename) : root(mk<DirectoryEntry>("root")) {
351 std::ifstream file(filename);
352 if (!file.is_open()) {
353 throw std::runtime_error("Log file could not be opened.");
354 }
355 std::string jsonString((std::istreambuf_iterator<char>(file)), (std::istreambuf_iterator<char>()));
356 std::string error;
357 json11::Json json = json11::Json::parse(jsonString, error);
358 if (!error.empty()) {
359 throw std::runtime_error("Parse error: " + error);
360 }
361 parseJson(json["root"], root);
362 }
363
364 // add size entry
365 void addSizeEntry(std::vector<std::string> qualifier, std::size_t size) {
366 assert(qualifier.size() > 0 && "no qualifier");
367 std::vector<std::string> path(qualifier.begin(), qualifier.end() - 1);
368 DirectoryEntry* dir = lookupPath(path);
369
370 const std::string& key = qualifier.back();
371 Own<SizeEntry> entry = mk<SizeEntry>(key, size);
372 dir->writeEntry(std::move(entry));
373 }
374
375 // add text entry
376 void addTextEntry(std::vector<std::string> qualifier, const std::string& text) {
377 assert(qualifier.size() > 0 && "no qualifier");
378 std::vector<std::string> path(qualifier.begin(), qualifier.end() - 1);
379 DirectoryEntry* dir = lookupPath(path);
380
381 const std::string& key = qualifier.back();
382 Own<TextEntry> entry = mk<TextEntry>(key, text);
383 dir->writeEntry(std::move(entry));
384 }
385
386 // add duration entry
387 void addDurationEntry(std::vector<std::string> qualifier, microseconds start, microseconds end) {
388 assert(qualifier.size() > 0 && "no qualifier");
389 std::vector<std::string> path(qualifier.begin(), qualifier.end() - 1);
390 DirectoryEntry* dir = lookupPath(path);
391
392 const std::string& key = qualifier.back();
393 Own<DurationEntry> entry = mk<DurationEntry>(key, start, end);
394 dir->writeEntry(std::move(entry));
395 }
396
397 // add time entry
398 void addTimeEntry(std::vector<std::string> qualifier, microseconds time) {
399 assert(qualifier.size() > 0 && "no qualifier");
400 std::vector<std::string> path(qualifier.begin(), qualifier.end() - 1);
401 DirectoryEntry* dir = lookupPath(path);
402
403 const std::string& key = qualifier.back();
404 Own<TimeEntry> entry = mk<TimeEntry>(key, time);
405 dir->writeEntry(std::move(entry));
406 }
407
408 // compute sum
409 std::size_t computeSum(const std::vector<std::string>& qualifier) {
410 assert(qualifier.size() > 0 && "no qualifier");
411 std::vector<std::string> path(qualifier.begin(), qualifier.end() - 1);
412 DirectoryEntry* dir = lookupPath(path);
413
414 const std::string& key = qualifier.back();
415 std::cout << "Key: " << key << std::endl;
416 Counter ctr(key);
417 dir->accept(ctr);
418 return ctr.getCounter();
419 }
420
421 /**
422 * Return the entry at the given path.
423 */
424 Entry* lookupEntry(const std::vector<std::string>& path) const {
425 DirectoryEntry* dir = root.get();
426 auto last = --path.end();
427 for (auto it = path.begin(); it != last; ++it) {
428 dir = dir->readDirectoryEntry(*it);
429 if (dir == nullptr) {
430 return nullptr;
431 }
432 }
433 return dir->readEntry(*last);
434 }
435
436 /**
437 * Return a map of string keys to string values.
438 */
439 std::map<std::string, std::string> getStringMap(const std::vector<std::string>& path) const {
440 std::map<std::string, std::string> kvps;
441 auto* parent = as<DirectoryEntry>(lookupEntry(path));
442 if (parent == nullptr) {
443 return kvps;
444 }
445
446 for (const auto& key : parent->getKeys()) {
447 auto* text = as<TextEntry>(parent->readEntry(key));
448 if (text != nullptr) {
449 kvps[key] = text->getText();
450 }
451 }
452
453 return kvps;
454 }
455
456 // print database
457 void print(std::ostream& os) const {
458 os << '{' << std::endl;
459 root->print(os, 1);
460 os << std::endl << '}' << std::endl;
461 };
462};
463
464} // namespace profile
465} // namespace souffle