scc 2025.09
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// ----------------------------------------------------------------------------
40using namespace std;
41using namespace leveldb;
42using 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// ----------------------------------------------------------------------------
55enum EventType { BEGIN, RECORD, END };
56const char* EventTypeStr[] = {"BEGIN", "RECORD", "END"};
57using data_type = scv_extensions_if::data_type;
58// ----------------------------------------------------------------------------
59namespace {
60
61struct 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
253private:
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
264vector<vector<uint64_t>> concurrencyLevel;
265
266Database* db;
267
268void 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// ----------------------------------------------------------------------------
298void 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// ----------------------------------------------------------------------------
308void 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// ----------------------------------------------------------------------------
316void 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// ----------------------------------------------------------------------------
324inline 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// ----------------------------------------------------------------------------
332void 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// ----------------------------------------------------------------------------
403void 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// ----------------------------------------------------------------------------
413void 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// ----------------------------------------------------------------------------
479void 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// ----------------------------------------------------------------------------
490void 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// ----------------------------------------------------------------------------
505void 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// ----------------------------------------------------------------------------