A SystemC productivity library for virtual platform development utilizing SCV and TLM2.0 https://www.minres.com/#opensource

scv_tr_sqlite.cpp 20KB

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