scc 2025.09
SystemC components library
scv_tr_binary.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 <array>
17#include <boost/filesystem.hpp>
18#include <cmath>
19#include <cstdio>
20#include <cstring>
21#include <fcntl.h>
22#include <iostream>
23#include <sstream>
24#include <stdexcept>
25#include <string>
26#include <unordered_map>
27#include <unordered_set>
28#include <vector>
29// clang-format off
30#ifdef HAS_SCV
31#include <scv.h>
32#else
33#include <scv-tr.h>
34namespace scv_tr {
35#endif
36// clang-format on
37// ----------------------------------------------------------------------------
38using namespace std;
71// ----------------------------------------------------------------------------
72#ifdef _MSC_VER
73#define scv_tr_TEXT_LLU "%I64u"
74#define scv_tr_TEXT_LLX "%I64x"
75#else
76#define scv_tr_TEXT_LLU "%llu"
77#define scv_tr_TEXT_LLX "%llx"
78#endif
79
80// ----------------------------------------------------------------------------
81enum EventType { BEGIN, RECORD, END };
82using data_type = scv_extensions_if::data_type;
83// ----------------------------------------------------------------------------
84namespace {
85
86struct 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)
91 buf.push_back(*ptr);
92 return *this;
93 }
94
95 size_t length() { return buf.size(); }
96
97 unsigned char* operator()() { return buf.data(); }
98
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"); // TODO: implement error handling
103 }
104
105private:
106 std::vector<unsigned char> buf;
107};
108
109template <> ByteBufferWriter& ByteBufferWriter::append<std::string>(const std::string& v) {
110 auto it = buf.end();
111 buf.resize(buf.size() + v.length());
112 std::copy(v.begin(), v.end(), it);
113 return *this;
114}
115
116const int open_flags{O_WRONLY | O_CREAT | O_TRUNC};
117const auto open_mode{00644};
118
119struct ControlBuffer {
120 ControlBuffer(const boost::filesystem::path& name) { file_des = open(name.string().c_str(), open_flags, open_mode); }
121
122 ~ControlBuffer() { close(file_des); }
123
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);
130 }
131 return strid;
132 }
133
134 void writeStream(uint64_t id, std::string& name, std::string& kind) {
135 ByteBufferWriter bw;
136 bw.append<uint32_t>(2U).append(id).append(getIdOf(name)).append(getIdOf(kind)).write(file_des);
137 }
138
139 void writeGenerator(uint64_t id, std::string& name, uint64_t stream) {
140 ByteBufferWriter bw;
141 bw.append<uint32_t>(3U).append(id).append(getIdOf(name)).append(stream).write(file_des);
142 }
143
144private:
145 int file_des = 0;
146 std::unordered_set<uint64_t> lookup;
147};
148
149class DataBuffer {
150public:
151 DataBuffer(const boost::filesystem::path& name) { file_des = open(name.string().c_str(), open_flags, open_mode); }
152
153 ~DataBuffer() {
154 if(bufTail > 0) {
155 std::fill(buf.data() + bufTail, buf.data() + buf.size(), 0);
156 write(file_des, buf.data(), buf.size());
157 }
158 close(file_des);
159 }
160
161 uint64_t writeTx(uint64_t id, uint64_t generator, uint64_t concurrencyLevel) {
162 // type(4)=1, id(8), generator(8),concurrencyLevel(4)
163 ByteBufferWriter bw;
164 bw.append<uint32_t>(1U).append(id).append(generator).append(concurrencyLevel);
165 return append(bw(), bw.length());
166 }
167
168 void writeAttribute(uint64_t id, EventType event, uint64_t name, data_type typ, uint64_t value) {
169 // type(4)=2, tx_id(8),type(2),name(8),data_type(2),data_value(8)
170 ByteBufferWriter bw;
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());
173 }
174
175 void writeAttribute(uint64_t id, EventType event, uint64_t name, data_type typ, uint64_t value0, uint32_t value1) {
176 // type(4)=3, tx_id(8),type(2),name(8),data_type(2),data_value(8)
177 ByteBufferWriter bw;
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());
180 }
181
182 void writeRelation(uint64_t name, uint64_t src, uint64_t sink) {
183 // type(4)=4, id(8), src(8), tgt(8)
184 ByteBufferWriter bw;
185 bw.append<uint32_t>(4U).append(name).append(src).append(sink);
186 append(bw(), bw.length());
187 }
191 template <typename T> uint64_t append(const T& val) { return append(reinterpret_cast<const unsigned char*>(&val), sizeof(T)); }
192
193 uint64_t getActualFilePos() { return blockCount * buf.size() + bufTail; }
194
195private:
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"); // TODO: implement error handling
202 blockCount++;
203 bufTail = 0;
204 }
205 uint64_t ret = blockCount * buf.size() + bufTail;
206 std::copy(p, p + len, buf.data() + bufTail);
207 bufTail += len;
208 return ret;
209 }
210
211 int file_des = 0;
212 size_t blockCount = 0;
213 size_t bufTail = 0;
214 std::array<unsigned char, 1024 * 1024> buf;
215};
216
217struct TimingBuffer {
218 TimingBuffer(const boost::filesystem::path& name) { file_des = open(name.string().c_str(), open_flags, open_mode); }
219
220 ~TimingBuffer() {
221 if(bufTail > 0) {
222 std::fill(buf.data() + bufTail, buf.data() + buf.size(), 0);
223 write(file_des, buf.data(), buf.size());
224 }
225 close(file_des);
226 }
227
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"); // TODO: implement error handling
235 bufTail = 0;
236 }
237 ByteBufferWriter bw(len);
238 bw.append(type).append(time).append(file_offset);
239 std::copy(bw(), bw() + bw.length(), buf.data() + bufTail);
240 bufTail += len;
241 }
242
243private:
244 int file_des = 0;
245 size_t bufTail = 0;
246 std::array<unsigned char, 20 * 1024> buf;
247};
248
249class Base {
250protected:
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);
257 }
258};
259
260struct Database : Base {
261 ControlBuffer c;
262 DataBuffer d;
263 TimingBuffer t;
264
265 Database(const std::string& name)
266 : Base(name)
267 , c(dir / "c")
268 , d(dir / "d")
269 , t(dir / "t") {}
270
271 inline uint64_t getIdOf(const std::string& str) { return c.getIdOf(str); }
272
273 inline void writeStream(uint64_t id, std::string name, std::string kind) { c.writeStream(id, name, kind); }
274
275 inline void writeGenerator(uint64_t id, std::string name, uint64_t stream) { c.writeGenerator(id, name, stream); }
276
277 inline uint64_t writeTransaction(uint64_t id, uint64_t generator, uint64_t concurrencyLevel) {
278 return d.writeTx(id, generator, concurrencyLevel);
279 }
280
281 inline void writeTxTimepoint(uint64_t id, int type, uint64_t time, uint64_t file_offset) { t.append(type, time, file_offset); }
282
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));
285 }
286
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);
289 }
290
291 inline void writeAttribute(uint64_t id, EventType event, const string& name, data_type type, double value) {
292 // int exponent;
293 // const double mantissa = frexp(value, &exponent);
294 }
295
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);
298 }
299};
300
301vector<vector<uint64_t>*> concurrencyLevel;
302Database* db;
303std::unordered_map<uint64_t, uint64_t> id2offset;
304
305void dbCb(const scv_tr_db& _scv_tr_db, scv_tr_db::callback_reason reason, void* data) {
306 // This is called from the scv_tr_db ctor.
307 static string fName("DEFAULT_scv_tr_sqlite");
308 switch(reason) {
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();
312 try {
313 db = new Database(fName);
314 } catch(...) {
315 _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Can't open recording file");
316 }
317 break;
318 case scv_tr_db::DELETE:
319 try {
320 delete db;
321 } catch(...) {
322 _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Can't close recording file");
323 }
324 break;
325 default:
326 _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Unknown reason in scv_tr_db callback");
327 }
328}
329// ----------------------------------------------------------------------------
330void streamCb(const scv_tr_stream& s, scv_tr_stream::callback_reason reason, void* data) {
331 if(reason == scv_tr_stream::CREATE) {
332 try {
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");
336 }
337 }
338}
339// ----------------------------------------------------------------------------
340void recordAttribute(uint64_t id, EventType event, const string& name, data_type type, const string& value) {
341 try {
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");
345 }
346}
347// ----------------------------------------------------------------------------
348void recordAttribute(uint64_t id, EventType event, const string& name, data_type type, long long value) {
349 try {
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");
353 }
354}
355// ----------------------------------------------------------------------------
356inline void recordAttribute(uint64_t id, EventType event, const string& name, data_type type, double value) {
357 try {
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");
361 }
362}
363// ----------------------------------------------------------------------------
364void recordAttributes(uint64_t id, EventType eventType, string& prefix, const scv_extensions_if* my_exts_p) {
365 if(my_exts_p == nullptr)
366 return;
367 string name;
368 if(prefix == "") {
369 name = my_exts_p->get_name();
370 } else {
371 if((my_exts_p->get_name() == nullptr) || (strlen(my_exts_p->get_name()) == 0)) {
372 name = prefix;
373 } else {
374 name = prefix + "." + my_exts_p->get_name();
375 }
376 }
377 if(name == "")
378 name = "<unnamed>";
379 switch(my_exts_p->get_type()) {
380 case scv_extensions_if::RECORD: {
381 int num_fields = my_exts_p->get_num_fields();
382 if(num_fields > 0) {
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);
386 }
387 }
388 } break;
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())));
391 break;
392 case scv_extensions_if::BOOLEAN:
393 recordAttribute(id, eventType, name, scv_extensions_if::BOOLEAN, my_exts_p->get_bool() ? "TRUE" : "FALSE");
394 break;
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());
398 break;
399 case scv_extensions_if::UNSIGNED:
400 recordAttribute(id, eventType, name, scv_extensions_if::UNSIGNED, my_exts_p->get_integer());
401 break;
402 case scv_extensions_if::POINTER:
403 recordAttribute(id, eventType, name, scv_extensions_if::POINTER, (long long)my_exts_p->get_pointer());
404 break;
405 case scv_extensions_if::STRING:
406 recordAttribute(id, eventType, name, scv_extensions_if::STRING, my_exts_p->get_string());
407 break;
408 case scv_extensions_if::FLOATING_POINT_NUMBER:
409 recordAttribute(id, eventType, name, scv_extensions_if::FLOATING_POINT_NUMBER, my_exts_p->get_double());
410 break;
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());
415 } break;
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());
420 } break;
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);
425 }
426 break;
427 default: {
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());
431 }
432 }
433}
434// ----------------------------------------------------------------------------
435void 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) {
437 try {
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");
441 }
442 }
443}
444// ----------------------------------------------------------------------------
445void transactionCb(const scv_tr_handle& t, scv_tr_handle::callback_reason reason, void* data) {
446 if(!db)
447 return;
448 if(t.get_scv_tr_stream().get_scv_tr_db() == nullptr)
449 return;
450 if(t.get_scv_tr_stream().get_scv_tr_db()->get_recording() == false)
451 return;
452
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;
457 switch(reason) {
458 case scv_tr_handle::BEGIN: {
459 try {
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;
466 }
467 for(concurrencyIdx = 0; concurrencyIdx < levels->size(); ++concurrencyIdx)
468 if((*levels)[concurrencyIdx] == 0)
469 break;
470 if(concurrencyIdx == levels->size())
471 levels->push_back(id);
472 else
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());
479 }
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();
483 if(my_exts_p) {
484 string tmp_str =
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);
487 }
488 } break;
489 case scv_tr_handle::END: {
490 try {
491 vector<uint64_t>* levels = concurrencyLevel[streamId];
492 for(concurrencyIdx = 0; concurrencyIdx < levels->size(); ++concurrencyIdx)
493 if((*levels)[concurrencyIdx] == id)
494 break;
495 if(concurrencyIdx == levels->size())
496 levels->push_back(id);
497 else
498 (*levels)[concurrencyIdx] = id;
499 db->writeTxTimepoint(id, END, t.get_begin_sc_time().value(), id2offset[id]);
500 id2offset.erase(id);
501 } catch(std::runtime_error& e) {
502 _scv_message::message(_scv_message::TRANSACTION_RECORDING_INTERNAL, "Can't create transaction end");
503 }
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();
507 if(my_exts_p) {
508 string tmp_str =
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);
511 }
512 } break;
513 default:;
514 }
515}
516// ----------------------------------------------------------------------------
517void attributeCb(const scv_tr_handle& t, const char* name, const scv_extensions_if* ext, void* data) {
518 if(!db)
519 return;
520 if(t.get_scv_tr_stream().get_scv_tr_db() == nullptr)
521 return;
522 if(t.get_scv_tr_stream().get_scv_tr_db()->get_recording() == false)
523 return;
524 string tmp_str(name == nullptr ? "" : name);
525 recordAttributes(t.get_id(), RECORD, tmp_str, ext);
526}
527// ----------------------------------------------------------------------------
528void relationCb(const scv_tr_handle& tr_1, const scv_tr_handle& tr_2, void* data, scv_tr_relation_handle_t relation_handle) {
529 if(!db)
530 return;
531 if(tr_1.get_scv_tr_stream().get_scv_tr_db() == nullptr)
532 return;
533 if(tr_1.get_scv_tr_stream().get_scv_tr_db()->get_recording() == false)
534 return;
535 try {
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");
539 }
540}
541} // namespace
542// ----------------------------------------------------------------------------
543void 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);
550}
551// ----------------------------------------------------------------------------
552#ifndef HAS_SCV
553}
554#endif
SystemC Verification Library (SCV) Transaction Recording.