17 #include <boost/filesystem.hpp>
26 #include <unordered_map>
27 #include <unordered_set>
73 #define scv_tr_TEXT_LLU "%I64u"
74 #define scv_tr_TEXT_LLX "%I64x"
76 #define scv_tr_TEXT_LLU "%llu"
77 #define scv_tr_TEXT_LLX "%llx"
81 enum EventType { BEGIN, RECORD, END };
82 using data_type = scv_extensions_if::data_type;
86 struct ByteBufferWriter {
87 ByteBufferWriter(
size_t reserve = 32) { buf.reserve(reserve); }
88 template <
typename T> ByteBufferWriter& append(
const T& v) {
89 const auto* ptr =
reinterpret_cast<const unsigned char*
>(&v);
90 for(
size_t i = 0; i <
sizeof(T); ++i, ++ptr)
95 size_t length() {
return buf.size(); }
97 unsigned char* operator()() {
return buf.data(); }
99 void write(
int file_des) {
100 ssize_t written = ::write(file_des, buf.data(), buf.size());
101 if(written != buf.size())
102 throw std::runtime_error(
"not written");
106 std::vector<unsigned char> buf;
109 template <> ByteBufferWriter& ByteBufferWriter::append<std::string>(
const std::string& v) {
111 buf.resize(buf.size() + v.length());
112 std::copy(v.begin(), v.end(), it);
116 const int open_flags{O_WRONLY | O_CREAT | O_TRUNC};
117 const auto open_mode{00644};
119 struct ControlBuffer {
120 ControlBuffer(
const boost::filesystem::path& name) { file_des = open(name.string().c_str(), open_flags, open_mode); }
122 ~ControlBuffer() { close(file_des); }
124 uint64_t getIdOf(
const std::string& str) {
125 auto strid = std::hash<std::string>{}(str);
126 if(lookup.find(strid) == lookup.end()) {
127 ByteBufferWriter bw(
sizeof(uint32_t) +
sizeof(strid) +
sizeof(uint32_t) + str.length());
128 bw.append<uint32_t>(1U).append(strid).append<uint32_t>(str.length()).append(str).write(file_des);
129 lookup.insert(strid);
134 void writeStream(uint64_t
id, std::string& name, std::string& kind) {
136 bw.append<uint32_t>(2U).append(
id).append(getIdOf(name)).append(getIdOf(kind)).write(file_des);
139 void writeGenerator(uint64_t
id, std::string& name, uint64_t stream) {
141 bw.append<uint32_t>(3U).append(
id).append(getIdOf(name)).append(stream).write(file_des);
146 std::unordered_set<uint64_t> lookup;
151 DataBuffer(
const boost::filesystem::path& name) { file_des = open(name.string().c_str(), open_flags, open_mode); }
155 std::fill(buf.data() + bufTail, buf.data() + buf.size(), 0);
156 write(file_des, buf.data(), buf.size());
161 uint64_t writeTx(uint64_t
id, uint64_t generator, uint64_t concurrencyLevel) {
164 bw.append<uint32_t>(1U).append(
id).append(generator).append(concurrencyLevel);
165 return append(bw(), bw.length());
168 void writeAttribute(uint64_t
id, EventType event, uint64_t name, data_type typ, uint64_t value) {
171 bw.append<uint32_t>(2U).append(
id).append(event).append(name).append(
static_cast<uint16_t
>(typ)).append(value);
172 append(bw(), bw.length());
175 void writeAttribute(uint64_t
id, EventType event, uint64_t name, data_type typ, uint64_t value0, uint32_t value1) {
178 bw.append<uint32_t>(3U).append(
id).append(event).append(name).append(
static_cast<uint16_t
>(typ)).append(value0).append(value1);
179 append(bw(), bw.length());
182 void writeRelation(uint64_t name, uint64_t src, uint64_t sink) {
185 bw.append<uint32_t>(4U).append(name).append(src).append(sink);
186 append(bw(), bw.length());
191 template <
typename T> uint64_t append(
const T& val) {
return append(
reinterpret_cast<const unsigned char*
>(&val),
sizeof(T)); }
193 uint64_t getActualFilePos() {
return blockCount * buf.size() + bufTail; }
196 uint64_t append(
const unsigned char* p,
size_t len) {
197 if((bufTail + len) > buf.size()) {
198 std::fill(buf.data() + bufTail, buf.data() + buf.size(), 0);
199 auto written = write(file_des, buf.data(), buf.size());
200 if(written != buf.size())
201 throw std::runtime_error(
"not written");
205 uint64_t ret = blockCount * buf.size() + bufTail;
206 std::copy(p, p + len, buf.data() + bufTail);
212 size_t blockCount = 0;
214 std::array<unsigned char, 1024 * 1024> buf;
217 struct TimingBuffer {
218 TimingBuffer(
const boost::filesystem::path& name) { file_des = open(name.string().c_str(), open_flags, open_mode); }
222 std::fill(buf.data() + bufTail, buf.data() + buf.size(), 0);
223 write(file_des, buf.data(), buf.size());
228 void append(uint32_t type, uint64_t time, uint64_t file_offset) {
229 const size_t len =
sizeof(type) +
sizeof(time) +
sizeof(file_offset);
230 if((bufTail + len) > buf.size()) {
231 std::fill(buf.data() + bufTail, buf.data() + buf.size(), 0);
232 ssize_t written = write(file_des, buf.data(), buf.size());
233 if(written != buf.size())
234 throw std::runtime_error(
"not written");
237 ByteBufferWriter bw(len);
238 bw.append(type).append(time).append(file_offset);
239 std::copy(bw(), bw() + bw.length(), buf.data() + bufTail);
246 std::array<unsigned char, 20 * 1024> buf;
251 boost::filesystem::path dir;
252 Base(
const std::string& name)
253 : dir(name.c_str()) {
254 if(boost::filesystem::exists(dir))
255 boost::filesystem::remove_all(dir);
256 boost::filesystem::create_directory(dir);
260 struct Database : Base {
265 Database(
const std::string& name)
271 inline uint64_t getIdOf(
const std::string& str) {
return c.getIdOf(str); }
273 inline void writeStream(uint64_t
id, std::string name, std::string kind) { c.writeStream(
id, name, kind); }
275 inline void writeGenerator(uint64_t
id, std::string name, uint64_t stream) { c.writeGenerator(
id, name, stream); }
277 inline uint64_t writeTransaction(uint64_t
id, uint64_t generator, uint64_t concurrencyLevel) {
278 return d.writeTx(
id, generator, concurrencyLevel);
281 inline void writeTxTimepoint(uint64_t
id,
int type, uint64_t time, uint64_t file_offset) { t.append(type, time, file_offset); }
283 inline void writeAttribute(uint64_t
id, EventType event,
const string& name, data_type type,
const string& value) {
284 d.writeAttribute(
id, event, c.getIdOf(name), type, c.getIdOf(value));
287 inline void writeAttribute(uint64_t
id, EventType event,
const string& name, data_type type, uint64_t value) {
288 d.writeAttribute(
id, event, c.getIdOf(name), type, value);
291 inline void writeAttribute(uint64_t
id, EventType event,
const string& name, data_type type,
double value) {
296 inline void writeRelation(
const std::string& name, uint64_t sink_id, uint64_t src_id) {
297 d.writeRelation(c.getIdOf(name), src_id, sink_id);
301 vector<vector<uint64_t>*> concurrencyLevel;
303 std::unordered_map<uint64_t, uint64_t> id2offset;
305 void dbCb(
const scv_tr_db& _scv_tr_db, scv_tr_db::callback_reason reason,
void* data) {
307 static string fName(
"DEFAULT_scv_tr_sqlite");
309 case scv_tr_db::CREATE:
310 if((_scv_tr_db.get_name() !=
nullptr) && (strlen(_scv_tr_db.get_name()) != 0))
311 fName = _scv_tr_db.get_name();
313 db =
new Database(fName);
315 _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL,
"Can't open recording file");
318 case scv_tr_db::DELETE:
322 _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL,
"Can't close recording file");
326 _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL,
"Unknown reason in scv_tr_db callback");
330 void streamCb(
const scv_tr_stream& s, scv_tr_stream::callback_reason reason,
void* data) {
331 if(reason == scv_tr_stream::CREATE) {
333 db->writeStream(s.get_id(), s.get_name(), s.get_stream_kind());
334 }
catch(std::runtime_error& e) {
335 _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL,
"Can't create stream");
340 void recordAttribute(uint64_t
id, EventType event,
const string& name, data_type type,
const string& value) {
342 db->writeAttribute(
id, event, name, type, value);
343 }
catch(std::runtime_error& e) {
344 _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL,
"Can't create attribute entry");
348 void recordAttribute(uint64_t
id, EventType event,
const string& name, data_type type,
long long value) {
350 db->writeAttribute(
id, event, name, type,
static_cast<uint64_t
>(value));
351 }
catch(std::runtime_error& e) {
352 _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL,
"Can't create attribute entry");
356 inline void recordAttribute(uint64_t
id, EventType event,
const string& name, data_type type,
double value) {
358 db->writeAttribute(
id, event, name, type, value);
359 }
catch(std::runtime_error& e) {
360 _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL,
"Can't create attribute entry");
364 void recordAttributes(uint64_t
id, EventType eventType,
string& prefix,
const scv_extensions_if* my_exts_p) {
365 if(my_exts_p ==
nullptr)
369 name = my_exts_p->get_name();
371 if((my_exts_p->get_name() ==
nullptr) || (strlen(my_exts_p->get_name()) == 0)) {
374 name = prefix +
"." + my_exts_p->get_name();
379 switch(my_exts_p->get_type()) {
380 case scv_extensions_if::RECORD: {
381 int num_fields = my_exts_p->get_num_fields();
383 for(
int field_counter = 0; field_counter < num_fields; field_counter++) {
384 const scv_extensions_if* field_data_p = my_exts_p->get_field(field_counter);
385 recordAttributes(
id, eventType, prefix, field_data_p);
389 case scv_extensions_if::ENUMERATION:
390 recordAttribute(
id, eventType, name, scv_extensions_if::ENUMERATION, my_exts_p->get_enum_string((
int)(my_exts_p->get_integer())));
392 case scv_extensions_if::BOOLEAN:
393 recordAttribute(
id, eventType, name, scv_extensions_if::BOOLEAN, my_exts_p->get_bool() ?
"TRUE" :
"FALSE");
395 case scv_extensions_if::INTEGER:
396 case scv_extensions_if::FIXED_POINT_INTEGER:
397 recordAttribute(
id, eventType, name, scv_extensions_if::INTEGER, my_exts_p->get_integer());
399 case scv_extensions_if::UNSIGNED:
400 recordAttribute(
id, eventType, name, scv_extensions_if::UNSIGNED, my_exts_p->get_integer());
402 case scv_extensions_if::POINTER:
403 recordAttribute(
id, eventType, name, scv_extensions_if::POINTER, (
long long)my_exts_p->get_pointer());
405 case scv_extensions_if::STRING:
406 recordAttribute(
id, eventType, name, scv_extensions_if::STRING, my_exts_p->get_string());
408 case scv_extensions_if::FLOATING_POINT_NUMBER:
409 recordAttribute(
id, eventType, name, scv_extensions_if::FLOATING_POINT_NUMBER, my_exts_p->get_double());
411 case scv_extensions_if::BIT_VECTOR: {
412 sc_bv_base tmp_bv(my_exts_p->get_bitwidth());
413 my_exts_p->get_value(tmp_bv);
414 recordAttribute(
id, eventType, name, scv_extensions_if::BIT_VECTOR, tmp_bv.to_string());
416 case scv_extensions_if::LOGIC_VECTOR: {
417 sc_lv_base tmp_lv(my_exts_p->get_bitwidth());
418 my_exts_p->get_value(tmp_lv);
419 recordAttribute(
id, eventType, name, scv_extensions_if::LOGIC_VECTOR, tmp_lv.to_string());
421 case scv_extensions_if::ARRAY:
422 for(
int array_elt_index = 0; array_elt_index < my_exts_p->get_array_size(); array_elt_index++) {
423 const scv_extensions_if* field_data_p = my_exts_p->get_array_elt(array_elt_index);
424 recordAttributes(
id, eventType, prefix, field_data_p);
428 std::array<char, 100> tmpString;
429 sprintf(tmpString.data(),
"Unsupported attribute type = %d", my_exts_p->get_type());
430 _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, tmpString.data());
435 void generatorCb(
const scv_tr_generator_base& g, scv_tr_generator_base::callback_reason reason,
void* data) {
436 if(reason == scv_tr_generator_base::CREATE && db) {
438 db->writeGenerator(g.get_id(), g.get_name(), g.get_scv_tr_stream().get_id());
439 }
catch(std::runtime_error& e) {
440 _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL,
"Can't create generator entry");
445 void transactionCb(
const scv_tr_handle& t, scv_tr_handle::callback_reason reason,
void* data) {
448 if(t.get_scv_tr_stream().get_scv_tr_db() ==
nullptr)
450 if(t.get_scv_tr_stream().get_scv_tr_db()->get_recording() ==
false)
453 uint64_t
id = t.get_id();
454 uint64_t streamId = t.get_scv_tr_stream().get_id();
455 vector<uint64_t>::size_type concurrencyIdx;
456 const scv_extensions_if* my_exts_p;
458 case scv_tr_handle::BEGIN: {
460 if(concurrencyLevel.size() <= streamId)
461 concurrencyLevel.resize(streamId + 1);
462 vector<uint64_t>* levels = concurrencyLevel.at(streamId);
463 if(levels ==
nullptr) {
464 levels =
new vector<uint64_t>();
465 concurrencyLevel[id] = levels;
467 for(concurrencyIdx = 0; concurrencyIdx < levels->size(); ++concurrencyIdx)
468 if((*levels)[concurrencyIdx] == 0)
470 if(concurrencyIdx == levels->size())
471 levels->push_back(
id);
473 (*levels)[concurrencyIdx] = id;
474 auto offset = db->writeTransaction(
id, t.get_scv_tr_generator_base().get_id(), concurrencyIdx);
475 db->writeTxTimepoint(
id, BEGIN, t.get_begin_sc_time().value(), offset);
476 id2offset[id] = offset;
477 }
catch(std::runtime_error& e) {
478 _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, e.what());
480 my_exts_p = t.get_begin_exts_p();
481 if(my_exts_p ==
nullptr)
482 my_exts_p = t.get_scv_tr_generator_base().get_begin_exts_p();
485 t.get_scv_tr_generator_base().get_begin_attribute_name() ? t.get_scv_tr_generator_base().get_begin_attribute_name() :
"";
486 recordAttributes(
id, BEGIN, tmp_str, my_exts_p);
489 case scv_tr_handle::END: {
491 vector<uint64_t>* levels = concurrencyLevel[streamId];
492 for(concurrencyIdx = 0; concurrencyIdx < levels->size(); ++concurrencyIdx)
493 if((*levels)[concurrencyIdx] ==
id)
495 if(concurrencyIdx == levels->size())
496 levels->push_back(
id);
498 (*levels)[concurrencyIdx] = id;
499 db->writeTxTimepoint(
id, END, t.get_begin_sc_time().value(), id2offset[
id]);
501 }
catch(std::runtime_error& e) {
502 _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL,
"Can't create transaction end");
504 my_exts_p = t.get_end_exts_p();
505 if(my_exts_p ==
nullptr)
506 my_exts_p = t.get_scv_tr_generator_base().get_end_exts_p();
509 t.get_scv_tr_generator_base().get_end_attribute_name() ? t.get_scv_tr_generator_base().get_end_attribute_name() :
"";
510 recordAttributes(t.get_id(), END, tmp_str, my_exts_p);
517 void attributeCb(
const scv_tr_handle& t,
const char* name,
const scv_extensions_if* ext,
void* data) {
520 if(t.get_scv_tr_stream().get_scv_tr_db() ==
nullptr)
522 if(t.get_scv_tr_stream().get_scv_tr_db()->get_recording() ==
false)
524 string tmp_str(name ==
nullptr ?
"" : name);
525 recordAttributes(t.get_id(), RECORD, tmp_str, ext);
528 void relationCb(
const scv_tr_handle& tr_1,
const scv_tr_handle& tr_2,
void* data, scv_tr_relation_handle_t relation_handle) {
531 if(tr_1.get_scv_tr_stream().get_scv_tr_db() ==
nullptr)
533 if(tr_1.get_scv_tr_stream().get_scv_tr_db()->get_recording() ==
false)
536 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());
537 }
catch(std::runtime_error& e) {
538 _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL,
"Can't create transaction relation");
543 void scv_tr_binary_init() {
544 scv_tr_db::register_class_cb(dbCb);
545 scv_tr_stream::register_class_cb(streamCb);
546 scv_tr_generator_base::register_class_cb(generatorCb);
547 scv_tr_handle::register_class_cb(transactionCb);
548 scv_tr_handle::register_record_attribute_cb(attributeCb);
549 scv_tr_handle::register_relation_cb(relationCb);
SystemC Verification Library (SCV) Transaction Recording.