23 #include <unordered_map>
32 constexpr
auto SQLITEWRAPPER_ERROR = 1000;
33 constexpr
auto with_transactions =
false;
41 SQLiteException(
const int nErrCode,
const char* msg,
bool doFree =
true)
45 sqlite3_free(
const_cast<char*
>(msg));
47 const int errorCode() {
return mnErrCode; }
48 const char* errorMessage() {
return what(); }
56 void open(
const string szFile) {
57 int nRet = sqlite3_open(szFile.c_str(), &db);
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);
71 inline bool isOpen() {
return db !=
nullptr; }
75 int nRet = sqlite3_close(db);
78 else if(nRet == SQLITE_BUSY) {
79 while(nRet == SQLITE_BUSY) {
80 sqlite3_stmt* stmt = sqlite3_next_stmt(db,
nullptr);
82 sqlite3_finalize(stmt);
83 nRet = sqlite3_close(db);
86 throw SQLiteException(SQLITEWRAPPER_ERROR,
"Unable to close database",
false);
89 throw SQLiteException(SQLITEWRAPPER_ERROR,
"Unable to close database",
false);
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;
97 sqlite3_prepare_v2(db, szSQL.c_str(), szSQL.size(), &ret, &tail);
101 int exec(
const char* szSQL) {
103 char* szError =
nullptr;
104 int nRet = sqlite3_exec(db, szSQL,
nullptr,
nullptr, &szError);
105 if(nRet == SQLITE_OK)
106 return sqlite3_changes(db);
108 throw SQLiteException(nRet, szError);
111 int exec(sqlite3_stmt* stmt) {
113 int nRet = sqlite3_step(stmt);
114 if(nRet == SQLITE_OK || nRet == SQLITE_DONE) {
116 return sqlite3_changes(db);
118 throw SQLiteException(nRet, sqlite3_errmsg(db));
122 inline void checkDB() {
124 throw SQLiteException(SQLITEWRAPPER_ERROR,
"Database not open",
false);
128 int busyTimeoutMs{60000};
129 sqlite3* db{
nullptr};
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;
137 enum EventType { BEGIN, RECORD, END };
138 using data_type = scv_extensions_if::data_type;
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"
149 static void dbCb(
const scv_tr_db& _scv_tr_db, scv_tr_db::callback_reason reason,
void* data) {
150 char* tail =
nullptr;
152 static string fName(
"DEFAULT_scv_tr_sqlite");
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();
158 remove(fName.c_str());
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");
168 db.exec(
"CREATE TABLE IF NOT EXISTS " STRING_TABLE
"("
169 "id INTEGER NOT null PRIMARY KEY, "
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)"
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, "
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"
190 db.exec(
"CREATE TABLE IF NOT EXISTS " TX_EVENT_TABLE
"("
191 "tx INTEGER REFERENCES " TX_TABLE
"(id), "
195 db.exec(
"CREATE TABLE IF NOT EXISTS " TX_ATTRIBUTE_TABLE
"("
196 "tx INTEGER REFERENCES " TX_TABLE
"(id), "
198 "name INTEGER REFERENCES " STRING_TABLE
"(id), "
199 "data_type INTEGER, "
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)"
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)
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");
230 case scv_tr_db::DELETE:
234 if(with_transactions)
235 db.exec(
"COMMIT TRANSACTION");
237 }
catch(SQLiteDB::SQLiteException& e) {
238 _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL,
"Can't close recording file");
242 _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL,
"Unknown reason in scv_tr_db callback");
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))
251 auto id = str_map.size();
252 str_map.insert({s,
id});
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");
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()) {
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");
279 void recordAttribute(uint64_t
id, EventType event,
const string& name, data_type type,
const string& value) {
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));
287 }
catch(SQLiteDB::SQLiteException& e) {
288 _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL,
"Can't create attribute entry");
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));
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));
300 static void recordAttributes(uint64_t
id, EventType eventType,
string& prefix,
const scv_extensions_if* my_exts_p) {
301 if(my_exts_p ==
nullptr)
305 name = my_exts_p->get_name();
307 if((my_exts_p->get_name() ==
nullptr) || (strlen(my_exts_p->get_name()) == 0)) {
310 name = prefix +
"." + my_exts_p->get_name();
315 switch(my_exts_p->get_type()) {
316 case scv_extensions_if::RECORD: {
317 int num_fields = my_exts_p->get_num_fields();
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);
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())));
328 case scv_extensions_if::BOOLEAN:
329 recordAttribute(
id, eventType, name, scv_extensions_if::BOOLEAN, my_exts_p->get_bool() ?
"TRUE" :
"FALSE");
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());
335 case scv_extensions_if::UNSIGNED:
336 recordAttribute(
id, eventType, name, scv_extensions_if::UNSIGNED, my_exts_p->get_integer());
338 case scv_extensions_if::POINTER:
339 recordAttribute(
id, eventType, name, scv_extensions_if::POINTER, (
long long)my_exts_p->get_pointer());
341 case scv_extensions_if::STRING:
342 recordAttribute(
id, eventType, name, scv_extensions_if::STRING, my_exts_p->get_string());
344 case scv_extensions_if::FLOATING_POINT_NUMBER:
345 recordAttribute(
id, eventType, name, scv_extensions_if::FLOATING_POINT_NUMBER, my_exts_p->get_double());
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());
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());
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);
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());
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()) {
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()));
378 }
catch(SQLiteDB::SQLiteException& e) {
379 _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL,
"Can't create generator entry");
384 static void transactionCb(
const scv_tr_handle& t, scv_tr_handle::callback_reason reason,
void* data) {
387 if(t.get_scv_tr_stream().get_scv_tr_db() ==
nullptr)
389 if(t.get_scv_tr_stream().get_scv_tr_db()->get_recording() ==
false)
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;
397 case scv_tr_handle::BEGIN: {
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;
406 for(concurrencyIdx = 0; concurrencyIdx < levels->size(); ++concurrencyIdx)
407 if((*levels)[concurrencyIdx] == 0)
409 if(concurrencyIdx == levels->size())
410 levels->push_back(
id);
412 (*levels)[concurrencyIdx] = id;
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);
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());
425 }
catch(SQLiteDB::SQLiteException& e) {
426 _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, e.errorMessage());
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();
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);
436 case scv_tr_handle::END: {
438 vector<uint64_t>* levels = concurrencyLevel[streamId];
439 for(concurrencyIdx = 0; concurrencyIdx < levels->size(); ++concurrencyIdx)
440 if((*levels)[concurrencyIdx] ==
id)
442 if(concurrencyIdx == levels->size())
443 levels->push_back(
id);
445 levels->at(concurrencyIdx) = id;
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());
452 }
catch(SQLiteDB::SQLiteException& e) {
453 _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL,
"Can't create transaction end");
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();
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);
467 static void attributeCb(
const scv_tr_handle& t,
const char* name,
const scv_extensions_if* ext,
void* data) {
470 if(t.get_scv_tr_stream().get_scv_tr_db() ==
nullptr)
472 if(t.get_scv_tr_stream().get_scv_tr_db()->get_recording() ==
false)
474 string tmp_str(name ==
nullptr ?
"" : name);
475 recordAttributes(t.get_id(), RECORD, tmp_str, ext);
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) {
481 if(tr_1.get_scv_tr_stream().get_scv_tr_db() ==
nullptr)
483 if(tr_1.get_scv_tr_stream().get_scv_tr_db()->get_recording() ==
false)
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());
490 }
catch(SQLiteDB::SQLiteException& e) {
491 _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL,
"Can't create transaction relation");
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);
SystemC Verification Library (SCV) Transaction Recording.
void scv_tr_sqlite_init()
initializes the infrastructure to use a SQLite based transaction recording database