scc  2022.4.0
SystemC components library
scv_tr_sqlite.cpp
1 /*******************************************************************************
2  * Copyright 2014, 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 "sqlite3.h"
17 #include <array>
18 #include <cstdio>
19 #include <iostream>
20 #include <sstream>
21 #include <stdexcept>
22 #include <string>
23 #include <unordered_map>
24 #include <vector>
25 #ifdef HAS_SCV
26 #include <scv.h>
27 #else
28 #include <scv-tr.h>
29 namespace scv_tr {
30 #endif
31 // ----------------------------------------------------------------------------
32 constexpr auto SQLITEWRAPPER_ERROR = 1000;
33 constexpr auto with_transactions = false;
34 // ----------------------------------------------------------------------------
35 using namespace std;
36 
37 class SQLiteDB {
38 public:
39  class SQLiteException : public runtime_error {
40  public:
41  SQLiteException(const int nErrCode, const char* msg, bool doFree = true)
42  : runtime_error(msg)
43  , mnErrCode(0) {
44  if(doFree && msg)
45  sqlite3_free(const_cast<char*>(msg));
46  }
47  const int errorCode() { return mnErrCode; }
48  const char* errorMessage() { return what(); }
49 
50  private:
51  int mnErrCode;
52  };
53 
54  SQLiteDB() = default;
55 
56  void open(const string szFile) {
57  int nRet = sqlite3_open(szFile.c_str(), &db);
58  if(nRet != SQLITE_OK)
59  throw SQLiteException(nRet, sqlite3_errmsg(db), false);
60  sqlite3_busy_timeout(db, busyTimeoutMs);
61  sqlite3_config(SQLITE_CONFIG_MMAP_SIZE, 1ULL << 26, 1ULL << 30);
62  char* zSql = sqlite3_mprintf("PRAGMA journal_mode=OFF;\n"
63  "PRAGMA synchronous=OFF;\n");
64  char* zErrMsg = nullptr;
65  nRet = sqlite3_exec(db, zSql, 0, 0, &zErrMsg);
66  if(nRet != SQLITE_OK)
67  throw SQLiteException(nRet, sqlite3_errmsg(db), false);
68  sqlite3_free(zSql);
69  }
70 
71  inline bool isOpen() { return db != nullptr; }
72 
73  void close() {
74  if(db) {
75  int nRet = sqlite3_close(db);
76  if(nRet == SQLITE_OK)
77  db = nullptr;
78  else if(nRet == SQLITE_BUSY) {
79  while(nRet == SQLITE_BUSY) { // maybe include _LOCKED
80  sqlite3_stmt* stmt = sqlite3_next_stmt(db, nullptr);
81  if(stmt)
82  sqlite3_finalize(stmt); // don't trap, can't handle it anyway
83  nRet = sqlite3_close(db);
84  }
85  if(nRet != SQLITE_OK)
86  throw SQLiteException(SQLITEWRAPPER_ERROR, "Unable to close database", false);
87  db = nullptr;
88  } else
89  throw SQLiteException(SQLITEWRAPPER_ERROR, "Unable to close database", false);
90  }
91  }
92 
93  inline int exec(const string szSQL) { return exec(szSQL.c_str()); }
94  inline sqlite3_stmt* prepare(const string szSQL) {
95  sqlite3_stmt* ret = nullptr;
96  const char* tail;
97  sqlite3_prepare_v2(db, szSQL.c_str(), szSQL.size(), &ret, &tail);
98  return ret;
99  }
100 
101  int exec(const char* szSQL) {
102  checkDB();
103  char* szError = nullptr;
104  int nRet = sqlite3_exec(db, szSQL, nullptr, nullptr, &szError);
105  if(nRet == SQLITE_OK)
106  return sqlite3_changes(db);
107  else
108  throw SQLiteException(nRet, szError);
109  }
110 
111  int exec(sqlite3_stmt* stmt) {
112  checkDB();
113  int nRet = sqlite3_step(stmt);
114  if(nRet == SQLITE_OK || nRet == SQLITE_DONE) {
115  sqlite3_reset(stmt);
116  return sqlite3_changes(db);
117  } else
118  throw SQLiteException(nRet, sqlite3_errmsg(db));
119  }
120 
121 protected:
122  inline void checkDB() {
123  if(!db)
124  throw SQLiteException(SQLITEWRAPPER_ERROR, "Database not open", false);
125  }
126 
127 private:
128  int busyTimeoutMs{60000};
129  sqlite3* db{nullptr};
130 };
131 // ----------------------------------------------------------------------------
132 static SQLiteDB db;
133 static vector<vector<uint64_t>*> concurrencyLevel;
134 static sqlite3_stmt *string_stmt, *stream_stmt, *gen_stmt, *tx_stmt, *evt_stmt, *attr_stmt, *rel_stmt;
135 
136 // ----------------------------------------------------------------------------
137 enum EventType { BEGIN, RECORD, END };
138 using data_type = scv_extensions_if::data_type;
139 // ----------------------------------------------------------------------------
140 #define SIM_PROPS "ScvSimProps"
141 #define STRING_TABLE "ScvStrings"
142 #define STREAM_TABLE "ScvStream"
143 #define GENERATOR_TABLE "ScvGenerator"
144 #define TX_TABLE "ScvTx"
145 #define TX_EVENT_TABLE "ScvTxEvent"
146 #define TX_ATTRIBUTE_TABLE "ScvTxAttribute"
147 #define TX_RELATION_TABLE "ScvTxRelation"
148 
149 static void dbCb(const scv_tr_db& _scv_tr_db, scv_tr_db::callback_reason reason, void* data) {
150  char* tail = nullptr;
151  // This is called from the scv_tr_db ctor.
152  static string fName("DEFAULT_scv_tr_sqlite");
153  switch(reason) {
154  case scv_tr_db::CREATE:
155  if((_scv_tr_db.get_name() != nullptr) && (strlen(_scv_tr_db.get_name()) != 0))
156  fName = _scv_tr_db.get_name();
157  try {
158  remove(fName.c_str());
159  db.open(fName);
160  // performance related according to
161  // http://blog.quibb.org/2010/08/fast-bulk-inserts-into-sqlite/
162  db.exec("PRAGMA synchronous=OFF");
163  db.exec("PRAGMA count_changes=OFF");
164  db.exec("PRAGMA journal_mode=OFF");
165  db.exec("PRAGMA temp_store=MEMORY");
166  // scv_out << "TB Transaction Recording has started, file = " <<
167  // my_sqlite_file_name << endl;
168  db.exec("CREATE TABLE IF NOT EXISTS " STRING_TABLE "("
169  "id INTEGER NOT null PRIMARY KEY, "
170  "value TEXT"
171  ");");
172  db.exec("CREATE TABLE IF NOT EXISTS " STREAM_TABLE "("
173  "id INTEGER NOT null PRIMARY KEY, "
174  "name INTEGER REFERENCES " STRING_TABLE "(id), "
175  "kind INTEGER REFERENCES " STRING_TABLE "(id)"
176  ");");
177  db.exec("CREATE TABLE IF NOT EXISTS " GENERATOR_TABLE "("
178  "id INTEGER NOT null PRIMARY KEY, "
179  "stream INTEGER REFERENCES " STREAM_TABLE "(id), "
180  "name INTEGER REFERENCES " STRING_TABLE "(id), "
181  "begin_attr INTEGER, "
182  "end_attr INTEGER"
183  ");");
184  db.exec("CREATE TABLE IF NOT EXISTS " TX_TABLE "("
185  "id INTEGER NOT null PRIMARY KEY, "
186  "generator INTEGER REFERENCES " GENERATOR_TABLE "(id), "
187  "stream INTEGER REFERENCES " STREAM_TABLE "(id), "
188  "concurrencyLevel INTEGER"
189  ");");
190  db.exec("CREATE TABLE IF NOT EXISTS " TX_EVENT_TABLE "("
191  "tx INTEGER REFERENCES " TX_TABLE "(id), "
192  "type INTEGER, "
193  "time INTEGER"
194  ");");
195  db.exec("CREATE TABLE IF NOT EXISTS " TX_ATTRIBUTE_TABLE "("
196  "tx INTEGER REFERENCES " TX_TABLE "(id), "
197  "type INTEGER, "
198  "name INTEGER REFERENCES " STRING_TABLE "(id), "
199  "data_type INTEGER, "
200  "data_value TEXT"
201  ");");
202  db.exec("CREATE TABLE IF NOT EXISTS " TX_RELATION_TABLE "("
203  "name INTEGER REFERENCES " STRING_TABLE "(id), "
204  "src INTEGER REFERENCES " TX_TABLE "(id), "
205  "sink INTEGER REFERENCES " TX_TABLE "(id)"
206  ");");
207  db.exec("CREATE TABLE IF NOT EXISTS " SIM_PROPS "(time_resolution INTEGER);");
208  if(with_transactions)
209  db.exec("BEGIN TRANSACTION");
210  std::ostringstream ss;
211  ss << "INSERT INTO " SIM_PROPS " (time_resolution) values (" << (long)(sc_core::sc_get_time_resolution().to_seconds() * 1e15)
212  << ");";
213  db.exec(ss.str().c_str());
214  string_stmt = db.prepare("INSERT INTO " STRING_TABLE " (id, value) values (@ID,@VALUE);");
215  stream_stmt = db.prepare("INSERT INTO " STREAM_TABLE " (id, name, kind) values (@ID,@NAME,@KIND);");
216  gen_stmt = db.prepare("INSERT INTO " GENERATOR_TABLE " (id,stream, name)"
217  " values (@ID,@STRM_DI,@NAME);");
218  tx_stmt = db.prepare("INSERT INTO " TX_TABLE " (id,generator,stream, concurrencyLevel)"
219  " values (@ID,@GEN_ID,@STREAM_ID,@CONC_LEVEL);");
220  evt_stmt = db.prepare("INSERT INTO " TX_EVENT_TABLE " (tx,type,time)"
221  " values (@TX_ID,@TYPE,@TIMESTAMP);");
222  attr_stmt = db.prepare("INSERT INTO " TX_ATTRIBUTE_TABLE " (tx,type,name,data_type,data_value) "
223  "values (@ID,@EVENTID,@NAME,@TYPE,@VALUE);");
224  rel_stmt = db.prepare("INSERT INTO " TX_RELATION_TABLE " (name,sink,src)"
225  "values (@NAME,@ID1,@ID2);");
226  } catch(SQLiteDB::SQLiteException& e) {
227  _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Can't open recording file");
228  }
229  break;
230  case scv_tr_db::DELETE:
231  try {
232  // scv_out << "Transaction Recording is closing file: " <<
233  // my_sqlite_file_name << endl;
234  if(with_transactions)
235  db.exec("COMMIT TRANSACTION");
236  db.close();
237  } catch(SQLiteDB::SQLiteException& e) {
238  _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Can't close recording file");
239  }
240  break;
241  default:
242  _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Unknown reason in scv_tr_db callback");
243  }
244 }
245 // ----------------------------------------------------------------------------
246 static std::unordered_map<std::string, uint64_t> str_map;
247 uint64_t getStringId(std::string const& s) {
248  auto it = str_map.find(s);
249  if(it != std::end(str_map))
250  return it->second;
251  auto id = str_map.size();
252  str_map.insert({s, id});
253  try {
254  sqlite3_bind_int64(string_stmt, 1, id);
255  sqlite3_bind_text(string_stmt, 2, s.c_str(), -1, SQLITE_TRANSIENT);
256  db.exec(string_stmt);
257  } catch(SQLiteDB::SQLiteException& e) {
258  _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Can't create string entry");
259  }
260  return id;
261 }
262 // ----------------------------------------------------------------------------
263 static void streamCb(const scv_tr_stream& s, scv_tr_stream::callback_reason reason, void* data) {
264  if(reason == scv_tr_stream::CREATE && db.isOpen()) {
265  try {
266  sqlite3_bind_int64(stream_stmt, 1, s.get_id());
267  sqlite3_bind_int64(stream_stmt, 2, getStringId(s.get_name()));
268  sqlite3_bind_int64(stream_stmt, 3, getStringId(s.get_stream_kind() ? s.get_stream_kind() : "<unnamed>"));
269  db.exec(stream_stmt);
270  if(concurrencyLevel.size() <= s.get_id())
271  concurrencyLevel.resize(s.get_id() + 1);
272  concurrencyLevel[s.get_id()] = new vector<uint64_t>();
273  } catch(SQLiteDB::SQLiteException& e) {
274  _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Can't create stream");
275  }
276  }
277 }
278 // ----------------------------------------------------------------------------
279 void recordAttribute(uint64_t id, EventType event, const string& name, data_type type, const string& value) {
280  try {
281  sqlite3_bind_int64(attr_stmt, 1, id);
282  sqlite3_bind_int64(attr_stmt, 2, event);
283  sqlite3_bind_int64(attr_stmt, 3, getStringId(name));
284  sqlite3_bind_int64(attr_stmt, 4, type);
285  sqlite3_bind_int64(attr_stmt, 5, getStringId(value));
286  db.exec(attr_stmt);
287  } catch(SQLiteDB::SQLiteException& e) {
288  _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Can't create attribute entry");
289  }
290 }
291 // ----------------------------------------------------------------------------
292 inline void recordAttribute(uint64_t id, EventType event, const string& name, data_type type, long long value) {
293  recordAttribute(id, event, name, type, std::to_string(value));
294 }
295 // ----------------------------------------------------------------------------
296 inline void recordAttribute(uint64_t id, EventType event, const string& name, data_type type, double value) {
297  recordAttribute(id, event, name, type, std::to_string(value));
298 }
299 // ----------------------------------------------------------------------------
300 static void recordAttributes(uint64_t id, EventType eventType, string& prefix, const scv_extensions_if* my_exts_p) {
301  if(my_exts_p == nullptr)
302  return;
303  string name;
304  if(prefix == "") {
305  name = my_exts_p->get_name();
306  } else {
307  if((my_exts_p->get_name() == nullptr) || (strlen(my_exts_p->get_name()) == 0)) {
308  name = prefix;
309  } else {
310  name = prefix + "." + my_exts_p->get_name();
311  }
312  }
313  if(name == "")
314  name = "<unnamed>";
315  switch(my_exts_p->get_type()) {
316  case scv_extensions_if::RECORD: {
317  int num_fields = my_exts_p->get_num_fields();
318  if(num_fields > 0) {
319  for(int field_counter = 0; field_counter < num_fields; field_counter++) {
320  const scv_extensions_if* field_data_p = my_exts_p->get_field(field_counter);
321  recordAttributes(id, eventType, prefix, field_data_p);
322  }
323  }
324  } break;
325  case scv_extensions_if::ENUMERATION:
326  recordAttribute(id, eventType, name, scv_extensions_if::ENUMERATION, my_exts_p->get_enum_string((int)(my_exts_p->get_integer())));
327  break;
328  case scv_extensions_if::BOOLEAN:
329  recordAttribute(id, eventType, name, scv_extensions_if::BOOLEAN, my_exts_p->get_bool() ? "TRUE" : "FALSE");
330  break;
331  case scv_extensions_if::INTEGER:
332  case scv_extensions_if::FIXED_POINT_INTEGER:
333  recordAttribute(id, eventType, name, scv_extensions_if::INTEGER, my_exts_p->get_integer());
334  break;
335  case scv_extensions_if::UNSIGNED:
336  recordAttribute(id, eventType, name, scv_extensions_if::UNSIGNED, my_exts_p->get_integer());
337  break;
338  case scv_extensions_if::POINTER:
339  recordAttribute(id, eventType, name, scv_extensions_if::POINTER, (long long)my_exts_p->get_pointer());
340  break;
341  case scv_extensions_if::STRING:
342  recordAttribute(id, eventType, name, scv_extensions_if::STRING, my_exts_p->get_string());
343  break;
344  case scv_extensions_if::FLOATING_POINT_NUMBER:
345  recordAttribute(id, eventType, name, scv_extensions_if::FLOATING_POINT_NUMBER, my_exts_p->get_double());
346  break;
347  case scv_extensions_if::BIT_VECTOR: {
348  sc_bv_base tmp_bv(my_exts_p->get_bitwidth());
349  my_exts_p->get_value(tmp_bv);
350  recordAttribute(id, eventType, name, scv_extensions_if::BIT_VECTOR, tmp_bv.to_string());
351  } break;
352  case scv_extensions_if::LOGIC_VECTOR: {
353  sc_lv_base tmp_lv(my_exts_p->get_bitwidth());
354  my_exts_p->get_value(tmp_lv);
355  recordAttribute(id, eventType, name, scv_extensions_if::LOGIC_VECTOR, tmp_lv.to_string());
356  } break;
357  case scv_extensions_if::ARRAY:
358  for(int array_elt_index = 0; array_elt_index < my_exts_p->get_array_size(); array_elt_index++) {
359  const scv_extensions_if* field_data_p = my_exts_p->get_array_elt(array_elt_index);
360  recordAttributes(id, eventType, prefix, field_data_p);
361  }
362  break;
363  default: {
364  std::array<char, 100> tmpString{};
365  sprintf(tmpString.data(), "Unsupported attribute type = %d", my_exts_p->get_type());
366  _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, tmpString.data());
367  }
368  }
369 }
370 // ----------------------------------------------------------------------------
371 static void generatorCb(const scv_tr_generator_base& g, scv_tr_generator_base::callback_reason reason, void* data) {
372  if(reason == scv_tr_generator_base::CREATE && db.isOpen()) {
373  try {
374  sqlite3_bind_int64(gen_stmt, 1, g.get_id());
375  sqlite3_bind_int64(gen_stmt, 2, g.get_scv_tr_stream().get_id());
376  sqlite3_bind_int64(gen_stmt, 3, getStringId(g.get_name()));
377  db.exec(gen_stmt);
378  } catch(SQLiteDB::SQLiteException& e) {
379  _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Can't create generator entry");
380  }
381  }
382 }
383 // ----------------------------------------------------------------------------
384 static void transactionCb(const scv_tr_handle& t, scv_tr_handle::callback_reason reason, void* data) {
385  if(!db.isOpen())
386  return;
387  if(t.get_scv_tr_stream().get_scv_tr_db() == nullptr)
388  return;
389  if(t.get_scv_tr_stream().get_scv_tr_db()->get_recording() == false)
390  return;
391 
392  uint64_t id = t.get_id();
393  uint64_t streamId = t.get_scv_tr_stream().get_id();
394  vector<uint64_t>::size_type concurrencyIdx;
395  const scv_extensions_if* my_exts_p;
396  switch(reason) {
397  case scv_tr_handle::BEGIN: {
398  try {
399  if(concurrencyLevel.size() <= streamId)
400  concurrencyLevel.resize(streamId + 1);
401  vector<uint64_t>* levels = concurrencyLevel[streamId];
402  if(levels == nullptr) {
403  levels = new vector<uint64_t>();
404  concurrencyLevel[id] = levels;
405  }
406  for(concurrencyIdx = 0; concurrencyIdx < levels->size(); ++concurrencyIdx)
407  if((*levels)[concurrencyIdx] == 0)
408  break;
409  if(concurrencyIdx == levels->size())
410  levels->push_back(id);
411  else
412  (*levels)[concurrencyIdx] = id;
413 
414  sqlite3_bind_int64(tx_stmt, 1, id);
415  sqlite3_bind_int64(tx_stmt, 2, t.get_scv_tr_generator_base().get_id());
416  sqlite3_bind_int64(tx_stmt, 3, t.get_scv_tr_stream().get_id());
417  sqlite3_bind_int64(tx_stmt, 3, concurrencyIdx);
418  db.exec(tx_stmt);
419 
420  sqlite3_bind_int64(evt_stmt, 1, id);
421  sqlite3_bind_int(evt_stmt, 2, BEGIN);
422  sqlite3_bind_int64(evt_stmt, 3, t.get_begin_sc_time().value());
423  db.exec(evt_stmt);
424 
425  } catch(SQLiteDB::SQLiteException& e) {
426  _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, e.errorMessage());
427  }
428  my_exts_p = t.get_begin_exts_p();
429  if(my_exts_p == nullptr) {
430  my_exts_p = t.get_scv_tr_generator_base().get_begin_exts_p();
431  }
432  string tmp_str =
433  t.get_scv_tr_generator_base().get_begin_attribute_name() ? t.get_scv_tr_generator_base().get_begin_attribute_name() : "";
434  recordAttributes(id, BEGIN, tmp_str, my_exts_p);
435  } break;
436  case scv_tr_handle::END: {
437  try {
438  vector<uint64_t>* levels = concurrencyLevel[streamId];
439  for(concurrencyIdx = 0; concurrencyIdx < levels->size(); ++concurrencyIdx)
440  if((*levels)[concurrencyIdx] == id)
441  break;
442  if(concurrencyIdx == levels->size())
443  levels->push_back(id);
444  else
445  levels->at(concurrencyIdx) = id;
446 
447  sqlite3_bind_int64(evt_stmt, 1, t.get_id());
448  sqlite3_bind_int(evt_stmt, 2, END);
449  sqlite3_bind_int64(evt_stmt, 3, t.get_end_sc_time().value());
450  db.exec(evt_stmt);
451 
452  } catch(SQLiteDB::SQLiteException& e) {
453  _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Can't create transaction end");
454  }
455  my_exts_p = t.get_end_exts_p();
456  if(my_exts_p == nullptr) {
457  my_exts_p = t.get_scv_tr_generator_base().get_end_exts_p();
458  }
459  string tmp_str =
460  t.get_scv_tr_generator_base().get_end_attribute_name() ? t.get_scv_tr_generator_base().get_end_attribute_name() : "";
461  recordAttributes(t.get_id(), END, tmp_str, my_exts_p);
462  } break;
463  default:;
464  }
465 }
466 // ----------------------------------------------------------------------------
467 static void attributeCb(const scv_tr_handle& t, const char* name, const scv_extensions_if* ext, void* data) {
468  if(!db.isOpen())
469  return;
470  if(t.get_scv_tr_stream().get_scv_tr_db() == nullptr)
471  return;
472  if(t.get_scv_tr_stream().get_scv_tr_db()->get_recording() == false)
473  return;
474  string tmp_str(name == nullptr ? "" : name);
475  recordAttributes(t.get_id(), RECORD, tmp_str, ext);
476 }
477 // ----------------------------------------------------------------------------
478 static void relationCb(const scv_tr_handle& tr_1, const scv_tr_handle& tr_2, void* data, scv_tr_relation_handle_t relation_handle) {
479  if(!db.isOpen())
480  return;
481  if(tr_1.get_scv_tr_stream().get_scv_tr_db() == nullptr)
482  return;
483  if(tr_1.get_scv_tr_stream().get_scv_tr_db()->get_recording() == false)
484  return;
485  try {
486  sqlite3_bind_int64(rel_stmt, 1, getStringId(tr_1.get_scv_tr_stream().get_scv_tr_db()->get_relation_name(relation_handle)));
487  sqlite3_bind_int64(rel_stmt, 2, tr_1.get_id());
488  sqlite3_bind_int64(rel_stmt, 3, tr_2.get_id());
489  db.exec(rel_stmt);
490  } catch(SQLiteDB::SQLiteException& e) {
491  _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Can't create transaction relation");
492  }
493 }
494 // ----------------------------------------------------------------------------
496  scv_tr_db::register_class_cb(dbCb);
497  scv_tr_stream::register_class_cb(streamCb);
498  scv_tr_generator_base::register_class_cb(generatorCb);
499  scv_tr_handle::register_class_cb(transactionCb);
500  scv_tr_handle::register_record_attribute_cb(attributeCb);
501  scv_tr_handle::register_relation_cb(relationCb);
502 }
503 // ----------------------------------------------------------------------------
504 #ifndef HAS_SCV
505 }
506 #endif
SystemC Verification Library (SCV) Transaction Recording.
void scv_tr_sqlite_init()
initializes the infrastructure to use a SQLite based transaction recording database