souffle  2.0.2-371-g6315b36
ProfileDatabase.h
Go to the documentation of this file.
1 #pragma once
2 
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 
21 namespace souffle {
22 namespace profile {
23 
24 class DirectoryEntry;
25 class DurationEntry;
26 class SizeEntry;
27 class TextEntry;
28 class TimeEntry;
29 
30 /**
31  * Visitor Interface
32  */
33 class Visitor {
34 public:
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  */
52 class Entry {
53 private:
54  // entry key
55  std::string key;
56 
57 public:
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  */
76 class DirectoryEntry : public Entry {
77 private:
78  std::map<std::string, Own<Entry>> entries;
79  mutable std::mutex lock;
80 
81 public:
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 dynamic_cast<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  */
146 class SizeEntry : public Entry {
147 private:
148  size_t size; // size
149 public:
150  SizeEntry(const std::string& key, size_t size) : Entry(key), size(size) {}
151 
152  // get size
153  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  */
171 class TextEntry : public Entry {
172 private:
173  // entry text
174  std::string text;
175 
176 public:
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  */
198 class DurationEntry : public Entry {
199 private:
200  // duration start
201  microseconds start;
202 
203  // duration end
204  microseconds end;
205 
206 public:
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  */
239 class TimeEntry : public Entry {
240 private:
241  // time since start
242  microseconds time;
243 
244 public:
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 
266 inline 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 
274 class Counter : public Visitor {
275 private:
276  size_t ctr{0};
277  std::string key;
278 
279 public:
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  size_t getCounter() const {
288  return ctr;
289  }
290 };
291 
292 /**
293  * Hierarchical databas
294  */
295 class ProfileDatabase {
296 private:
297  Own<DirectoryEntry> root;
298 
299 protected:
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 = dynamic_cast<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 
347 public:
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, 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 = std::make_unique<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 = std::make_unique<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 = std::make_unique<TimeEntry>(key, time);
405  dir->writeEntry(std::move(entry));
406  }
407 
408  // compute sum
409  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 = dynamic_cast<DirectoryEntry*>(lookupEntry(path));
442  if (parent == nullptr) {
443  return kvps;
444  }
445 
446  for (const auto& key : parent->getKeys()) {
447  auto* text = dynamic_cast<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
TCB_SPAN_NAMESPACE_NAME::detail::size
constexpr auto size(const C &c) -> decltype(c.size())
Definition: span.h:198
err
std::string & err
Definition: json11.h:664
json11::Json::parse
static Json parse(const std::string &in, std::string &err, JsonParse strategy=JsonParse::STANDARD)
Definition: json11.h:1071
e
l j a showGridBackground &&c b raw series this eventEmitter e
Definition: htmlJsChartistMin.h:15
MiscUtil.h
json11.h
json11::Json::object_items
const object & object_items() const
Definition: json11.h:557
souffle::mk
Own< A > mk(Args &&... xs)
Definition: ContainerUtil.h:48
ContainerUtil.h
json11::Json
Definition: json11.h:87
souffle::test::time
long time(const std::string &name, const Op &operation)
Definition: btree_multiset_test.cpp:411
std
Definition: Brie.h:3053
souffle::test::Entry
std::tuple< int, int > Entry
Definition: btree_multiset_test.cpp:385
souffle
Definition: AggregateOp.h:25