scc  2024.06
SystemC components library
scv_tr_ldb.cpp
1 /*******************************************************************************
2  * Copyright 2018 MINRES Technologies GmbH
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *******************************************************************************/
16 #include "leveldb/db.h"
17 #include <json/json.h>
18 
19 #include <array>
20 #include <cassert>
21 #include <cmath>
22 #include <cstdio>
23 #include <cstring>
24 #include <fcntl.h>
25 #include <iomanip>
26 #include <iostream>
27 #include <sstream>
28 #include <stdexcept>
29 #include <string>
30 #include <unordered_map>
31 #include <unordered_set>
32 #include <vector>
33 // clang-format off
34 #include "scv/scv_util.h"
35 #include "scv/scv_introspection.h"
36 #include "scv/scv_tr.h"
37 // clang-format on
38 // ----------------------------------------------------------------------------
39 // ----------------------------------------------------------------------------
40 using namespace std;
41 using namespace leveldb;
42 using namespace Json;
43 
44 #ifdef _MSC_VER
45 #define scv_tr_TEXT_LLU "%I64u"
46 #define scv_tr_TEXT_LLX "%I64x"
47 #define scv_tr_TEXT_16LLX "%016I64x"
48 #else
49 #define scv_tr_TEXT_LLU "%llu"
50 #define scv_tr_TEXT_LLX "%llx"
51 #define scv_tr_TEXT_16LLX "%016lx"
52 #endif
53 
54 // ----------------------------------------------------------------------------
55 enum EventType { BEGIN, RECORD, END };
56 const char* EventTypeStr[] = {"BEGIN", "RECORD", "END"};
57 using data_type = scv_extensions_if::data_type;
58 // ----------------------------------------------------------------------------
59 namespace {
60 
61 struct Database {
62 
63  Database(const string& name)
64  : key_len(1024) {
65  wbuilder.settings_["indentation"] = "";
66  CharReaderBuilder::strictMode(&rbuilder.settings_);
67  Options options;
68  options.create_if_missing = true;
69  options.compression = kSnappyCompression;
70  DestroyDB(name, options);
71  if(!DB::Open(options, name, &db).ok())
72  throw runtime_error("Could not create database");
73  key_buf = new char[key_len];
74  }
75 
76  ~Database() {
77  delete db;
78  delete key_buf;
79  }
85  inline bool writeEntry(string& key, Value& val) {
86  return db->Put(write_options, Slice(key.c_str(), key.size()), writeString(wbuilder, val)).ok();
87  }
93  inline bool writeEntry(string&& key, Value& val) {
94  return db->Put(write_options, Slice(key.c_str(), key.size()), writeString(wbuilder, val)).ok();
95  }
102  inline void writeStream(uint64_t id, string name, string kind) {
103  auto len = sprintf(key_buf, "s~" scv_tr_TEXT_16LLX, id);
104  Value node{objectValue};
105  node["id"] = id;
106  node["name"] = name;
107  node["kind"] = kind;
108  db->Put(write_options, Slice(key_buf, len), writeString(wbuilder, node));
109  }
116  inline void writeGenerator(uint64_t id, string name, uint64_t stream) {
117  auto len = sprintf(key_buf, "sg~" scv_tr_TEXT_16LLX "~" scv_tr_TEXT_16LLX, stream, id);
118  Value node{objectValue};
119  node["id"] = id;
120  node["name"] = name;
121  node["stream"] = stream;
122  db->Put(write_options, Slice(key_buf, len), writeString(wbuilder, node));
123  }
130  inline void writeTransaction(uint64_t id, uint64_t stream_id, uint64_t generator_id, uint64_t concurrencyLevel) {
131  Value val{objectValue};
132  val["id"] = id;
133  val["s"] = stream_id;
134  val["g"] = generator_id;
135  val["conc"] = concurrencyLevel;
136  db->Put(write_options,
137  Slice(key_buf,
138  sprintf(key_buf, "sgx~" scv_tr_TEXT_16LLX "~" scv_tr_TEXT_16LLX "~" scv_tr_TEXT_16LLX, stream_id, generator_id, id)),
139  "");
140  tx_lut[id] = val;
141  // db->Put(write_options, Slice(key, sprintf(key, "x~" scv_tr_TEXT_16LLX, id)), writeString(wbuilder, val));
142  }
150  inline void writeTxTimepoint(uint64_t id, uint64_t streamid, EventType type, uint64_t time) {
151  string value;
152  Value node{arrayValue};
153  auto len = sprintf(key_buf, "st~" scv_tr_TEXT_16LLX "~" scv_tr_TEXT_16LLX "~%s", streamid, time, EventTypeStr[type]);
154  if(db->Get(read_options, key_buf, &value).ok() &&
155  rbuilder.newCharReader()->parse(value.data(), value.data() + value.size(), &node, nullptr)) {
156  node[node.size()] = Value(id);
157  } else {
158  node[0u] = Value(id);
159  }
160  db->Put(write_options, Slice(key_buf, len), writeString(wbuilder, node));
161  updateTx(id, type, time);
162  }
163 
164  inline void updateTx(uint64_t id, EventType type, uint64_t time) {
165  static const char* typeStr[] = {"START_TIME", "", "END_TIME"};
166  auto& node = tx_lut[id];
167  node[typeStr[type]] = time;
168  if(type == END) {
169  db->Put(write_options, Slice(key_buf, sprintf(key_buf, "x~" scv_tr_TEXT_16LLX, id)), writeString(wbuilder, node));
170  tx_lut.erase(id);
171  }
172  }
173 
174  inline void updateTx(uint64_t id, Value&& val) {
175  auto len = sprintf(key_buf, "x~" scv_tr_TEXT_16LLX, id);
176  auto& node = tx_lut[id];
177  auto& arrNode = node["attr"];
178  if(arrNode.isNull()) {
179  Value newNode{arrayValue};
180  newNode.append(val);
181  node["attr"] = newNode;
182  } else {
183  arrNode.append(val);
184  }
185  }
186 
195  inline void writeAttribute(uint64_t id, EventType event, const string& name, data_type type, const string& value) {
196  Value val;
197  val["name"] = name;
198  val["type"] = type;
199  val["value"] = value;
200  val["assoc"] = event;
201  updateTx(id, move(val));
202  }
211  inline void writeAttribute(uint64_t id, EventType event, const string& name, data_type type, uint64_t value) {
212  Value val;
213  val["name"] = name;
214  val["type"] = type;
215  val["value"] = value;
216  val["assoc"] = event;
217  updateTx(id, move(val));
218  }
227  inline void writeAttribute(uint64_t id, EventType event, const string& name, data_type type, double value) {
228  Value val;
229  val["name"] = name;
230  val["type"] = type;
231  val["value"] = value;
232  val["assoc"] = event;
233  updateTx(id, move(val));
234  }
241  inline void writeRelation(const string& name, uint64_t sink_id, uint64_t src_id) {
242  if(key_len < (name.size() + 32 + 5)) { // reallocate buffer if needed, making sure no buffer overflow
243  delete key_buf;
244  key_len = name.size() + 32 + 5;
245  key_buf = new char[key_len];
246  }
247  db->Put(write_options,
248  Slice(key_buf, sprintf(key_buf, "ro~" scv_tr_TEXT_16LLX "~" scv_tr_TEXT_16LLX "~%s", src_id, sink_id, name.c_str())), "");
249  db->Put(write_options,
250  Slice(key_buf, sprintf(key_buf, "ri~" scv_tr_TEXT_16LLX "~" scv_tr_TEXT_16LLX "~%s", sink_id, src_id, name.c_str())), "");
251  }
252 
253 private:
254  DB* db;
255  ReadOptions read_options;
256  WriteOptions write_options;
257  char* key_buf;
258  size_t key_len;
259  StreamWriterBuilder wbuilder;
260  CharReaderBuilder rbuilder;
261  unordered_map<uint64_t, Value> tx_lut;
262 };
263 
264 vector<vector<uint64_t>> concurrencyLevel;
265 
266 Database* db;
267 
268 void dbCb(const scv_tr_db& _scv_tr_db, scv_tr_db::callback_reason reason, void* data) {
269  // This is called from the scv_tr_db ctor.
270  static string fName("DEFAULT_scv_tr_sqlite");
271  switch(reason) {
272  case scv_tr_db::CREATE:
273  if((_scv_tr_db.get_name() != nullptr) && (strlen(_scv_tr_db.get_name()) != 0))
274  fName = _scv_tr_db.get_name();
275  try {
276  db = new Database(fName);
277  Value val{objectValue};
278  val["resolution"] = (long)(sc_get_time_resolution().to_seconds() * 1e15);
279  db->writeEntry("__config", val);
280  } catch(runtime_error& e) {
281  _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, e.what());
282  } catch(...) {
283  _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Can't open recording file");
284  }
285  break;
286  case scv_tr_db::DELETE:
287  try {
288  delete db;
289  } catch(...) {
290  _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Can't close recording file");
291  }
292  break;
293  default:
294  _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Unknown reason in scv_tr_db callback");
295  }
296 }
297 // ----------------------------------------------------------------------------
298 void streamCb(const scv_tr_stream& s, scv_tr_stream::callback_reason reason, void* data) {
299  if(reason == scv_tr_stream::CREATE) {
300  try {
301  db->writeStream(s.get_id(), s.get_name(), s.get_stream_kind());
302  } catch(runtime_error& e) {
303  _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Can't create stream");
304  }
305  }
306 }
307 // ----------------------------------------------------------------------------
308 void recordAttribute(uint64_t id, EventType event, const string& name, data_type type, const string& value) {
309  try {
310  db->writeAttribute(id, event, name, type, value);
311  } catch(runtime_error& e) {
312  _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Can't create attribute entry");
313  }
314 }
315 // ----------------------------------------------------------------------------
316 void recordAttribute(uint64_t id, EventType event, const string& name, data_type type, long long value) {
317  try {
318  db->writeAttribute(id, event, name, type, static_cast<uint64_t>(value));
319  } catch(runtime_error& e) {
320  _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Can't create attribute entry");
321  }
322 }
323 // ----------------------------------------------------------------------------
324 inline void recordAttribute(uint64_t id, EventType event, const string& name, data_type type, double value) {
325  try {
326  db->writeAttribute(id, event, name, type, value);
327  } catch(runtime_error& e) {
328  _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Can't create attribute entry");
329  }
330 }
331 // ----------------------------------------------------------------------------
332 void recordAttributes(uint64_t id, EventType eventType, string& prefix, const scv_extensions_if* my_exts_p) {
333  if(my_exts_p == nullptr)
334  return;
335  string name;
336  if(prefix == "") {
337  name = my_exts_p->get_name();
338  } else {
339  if((my_exts_p->get_name() == nullptr) || (strlen(my_exts_p->get_name()) == 0)) {
340  name = prefix;
341  } else {
342  name = prefix + "." + my_exts_p->get_name();
343  }
344  }
345  if(name == "")
346  name = "<unnamed>";
347  switch(my_exts_p->get_type()) {
348  case scv_extensions_if::RECORD: {
349  int num_fields = my_exts_p->get_num_fields();
350  if(num_fields > 0) {
351  for(int field_counter = 0; field_counter < num_fields; field_counter++) {
352  const scv_extensions_if* field_data_p = my_exts_p->get_field(field_counter);
353  recordAttributes(id, eventType, prefix, field_data_p);
354  }
355  }
356  } break;
357  case scv_extensions_if::ENUMERATION:
358  recordAttribute(id, eventType, name, scv_extensions_if::ENUMERATION, my_exts_p->get_enum_string((int)(my_exts_p->get_integer())));
359  break;
360  case scv_extensions_if::BOOLEAN:
361  recordAttribute(id, eventType, name, scv_extensions_if::BOOLEAN, my_exts_p->get_bool() ? "TRUE" : "FALSE");
362  break;
363  case scv_extensions_if::INTEGER:
364  case scv_extensions_if::FIXED_POINT_INTEGER:
365  recordAttribute(id, eventType, name, scv_extensions_if::INTEGER, my_exts_p->get_integer());
366  break;
367  case scv_extensions_if::UNSIGNED:
368  recordAttribute(id, eventType, name, scv_extensions_if::UNSIGNED, my_exts_p->get_integer());
369  break;
370  case scv_extensions_if::POINTER:
371  recordAttribute(id, eventType, name, scv_extensions_if::POINTER, (long long)my_exts_p->get_pointer());
372  break;
373  case scv_extensions_if::STRING:
374  recordAttribute(id, eventType, name, scv_extensions_if::STRING, my_exts_p->get_string());
375  break;
376  case scv_extensions_if::FLOATING_POINT_NUMBER:
377  recordAttribute(id, eventType, name, scv_extensions_if::FLOATING_POINT_NUMBER, my_exts_p->get_double());
378  break;
379  case scv_extensions_if::BIT_VECTOR: {
380  sc_bv_base tmp_bv(my_exts_p->get_bitwidth());
381  my_exts_p->get_value(tmp_bv);
382  recordAttribute(id, eventType, name, scv_extensions_if::BIT_VECTOR, tmp_bv.to_string());
383  } break;
384  case scv_extensions_if::LOGIC_VECTOR: {
385  sc_lv_base tmp_lv(my_exts_p->get_bitwidth());
386  my_exts_p->get_value(tmp_lv);
387  recordAttribute(id, eventType, name, scv_extensions_if::LOGIC_VECTOR, tmp_lv.to_string());
388  } break;
389  case scv_extensions_if::ARRAY:
390  for(int array_elt_index = 0; array_elt_index < my_exts_p->get_array_size(); array_elt_index++) {
391  const scv_extensions_if* field_data_p = my_exts_p->get_array_elt(array_elt_index);
392  recordAttributes(id, eventType, prefix, field_data_p);
393  }
394  break;
395  default: {
396  array<char, 100> tmpString;
397  sprintf(tmpString.data(), "Unsupported attribute type = %d", my_exts_p->get_type());
398  _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, tmpString.data());
399  }
400  }
401 }
402 // ----------------------------------------------------------------------------
403 void generatorCb(const scv_tr_generator_base& g, scv_tr_generator_base::callback_reason reason, void* data) {
404  if(reason == scv_tr_generator_base::CREATE && db) {
405  try {
406  db->writeGenerator(g.get_id(), g.get_name(), g.get_scv_tr_stream().get_id());
407  } catch(runtime_error& e) {
408  _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Can't create generator entry");
409  }
410  }
411 }
412 // ----------------------------------------------------------------------------
413 void transactionCb(const scv_tr_handle& t, scv_tr_handle::callback_reason reason, void* data) {
414  if(!db)
415  return;
416  if(t.get_scv_tr_stream().get_scv_tr_db() == nullptr)
417  return;
418  if(t.get_scv_tr_stream().get_scv_tr_db()->get_recording() == false)
419  return;
420 
421  uint64_t id = t.get_id();
422  uint64_t streamId = t.get_scv_tr_stream().get_id();
423  vector<uint64_t>::size_type concurrencyIdx;
424  const scv_extensions_if* my_exts_p;
425  switch(reason) {
426  case scv_tr_handle::BEGIN: {
427  try {
428  if(concurrencyLevel.size() <= streamId)
429  concurrencyLevel.resize(streamId + 1);
430  vector<uint64_t>& levels = concurrencyLevel.at(streamId);
431  for(concurrencyIdx = 0; concurrencyIdx < levels.size(); ++concurrencyIdx) // find a free slot
432  if(levels[concurrencyIdx] == 0)
433  break;
434  if(concurrencyIdx == levels.size())
435  levels.push_back(id);
436  else
437  levels[concurrencyIdx] = id;
438  db->writeTransaction(id, t.get_scv_tr_stream().get_id(), t.get_scv_tr_generator_base().get_id(), concurrencyIdx);
439  db->writeTxTimepoint(id, t.get_scv_tr_stream().get_id(), BEGIN, t.get_begin_sc_time().value());
440  } catch(runtime_error& e) {
441  _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, e.what());
442  }
443  my_exts_p = t.get_begin_exts_p();
444  if(my_exts_p == nullptr)
445  my_exts_p = t.get_scv_tr_generator_base().get_begin_exts_p();
446  if(my_exts_p) {
447  string tmp_str =
448  t.get_scv_tr_generator_base().get_begin_attribute_name() ? t.get_scv_tr_generator_base().get_begin_attribute_name() : "";
449  recordAttributes(id, BEGIN, tmp_str, my_exts_p);
450  }
451  } break;
452  case scv_tr_handle::END: {
453  my_exts_p = t.get_end_exts_p();
454  if(my_exts_p == nullptr)
455  my_exts_p = t.get_scv_tr_generator_base().get_end_exts_p();
456  if(my_exts_p) {
457  string tmp_str =
458  t.get_scv_tr_generator_base().get_end_attribute_name() ? t.get_scv_tr_generator_base().get_end_attribute_name() : "";
459  recordAttributes(t.get_id(), END, tmp_str, my_exts_p);
460  }
461  try {
462  db->writeTxTimepoint(id, t.get_scv_tr_stream().get_id(), END, t.get_end_sc_time().value());
463  vector<uint64_t>& levels = concurrencyLevel[streamId];
464  for(concurrencyIdx = 0; concurrencyIdx < levels.size(); ++concurrencyIdx)
465  if(levels[concurrencyIdx] == id)
466  break;
467  if(concurrencyIdx == levels.size())
468  levels.push_back(0);
469  else
470  levels[concurrencyIdx] = 0;
471  } catch(runtime_error& e) {
472  _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Can't create transaction end");
473  }
474  } break;
475  default:;
476  }
477 }
478 // ----------------------------------------------------------------------------
479 void attributeCb(const scv_tr_handle& t, const char* name, const scv_extensions_if* ext, void* data) {
480  if(!db)
481  return;
482  if(t.get_scv_tr_stream().get_scv_tr_db() == nullptr)
483  return;
484  if(t.get_scv_tr_stream().get_scv_tr_db()->get_recording() == false)
485  return;
486  string tmp_str(name == nullptr ? "" : name);
487  recordAttributes(t.get_id(), RECORD, tmp_str, ext);
488 }
489 // ----------------------------------------------------------------------------
490 void relationCb(const scv_tr_handle& tr_1, const scv_tr_handle& tr_2, void* data, scv_tr_relation_handle_t relation_handle) {
491  if(!db)
492  return;
493  if(tr_1.get_scv_tr_stream().get_scv_tr_db() == nullptr)
494  return;
495  if(tr_1.get_scv_tr_stream().get_scv_tr_db()->get_recording() == false)
496  return;
497  try {
498  db->writeRelation(tr_1.get_scv_tr_stream().get_scv_tr_db()->get_relation_name(relation_handle), tr_1.get_id(), tr_2.get_id());
499  } catch(runtime_error& e) {
500  _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Can't create transaction relation");
501  }
502 }
503 } // namespace
504 // ----------------------------------------------------------------------------
505 void scv_tr_ldb_init() {
506  scv_tr_db::register_class_cb(dbCb);
507  scv_tr_stream::register_class_cb(streamCb);
508  scv_tr_generator_base::register_class_cb(generatorCb);
509  scv_tr_handle::register_class_cb(transactionCb);
510  scv_tr_handle::register_record_attribute_cb(attributeCb);
511  scv_tr_handle::register_relation_cb(relationCb);
512 }
513 // ----------------------------------------------------------------------------