scc  2022.4.0
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>
34 namespace scv_tr {
35 #endif
36 // clang-format on
37 // ----------------------------------------------------------------------------
38 using 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 // ----------------------------------------------------------------------------
81 enum EventType { BEGIN, RECORD, END };
82 using data_type = scv_extensions_if::data_type;
83 // ----------------------------------------------------------------------------
84 namespace {
85 
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)
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 
105 private:
106  std::vector<unsigned char> buf;
107 };
108 
109 template <> 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 
116 const int open_flags{O_WRONLY | O_CREAT | O_TRUNC};
117 const auto open_mode{00644};
118 
119 struct 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 
144 private:
145  int file_des = 0;
146  std::unordered_set<uint64_t> lookup;
147 };
148 
149 class DataBuffer {
150 public:
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 
195 private:
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 
217 struct 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 
243 private:
244  int file_des = 0;
245  size_t bufTail = 0;
246  std::array<unsigned char, 20 * 1024> buf;
247 };
248 
249 class Base {
250 protected:
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 
260 struct 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 
301 vector<vector<uint64_t>*> concurrencyLevel;
302 Database* db;
303 std::unordered_map<uint64_t, uint64_t> id2offset;
304 
305 void 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 // ----------------------------------------------------------------------------
330 void 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 // ----------------------------------------------------------------------------
340 void 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 // ----------------------------------------------------------------------------
348 void 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 // ----------------------------------------------------------------------------
356 inline 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 // ----------------------------------------------------------------------------
364 void 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 // ----------------------------------------------------------------------------
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) {
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 // ----------------------------------------------------------------------------
445 void 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 // ----------------------------------------------------------------------------
517 void 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 // ----------------------------------------------------------------------------
528 void 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 // ----------------------------------------------------------------------------
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);
550 }
551 // ----------------------------------------------------------------------------
552 #ifndef HAS_SCV
553 }
554 #endif
SystemC Verification Library (SCV) Transaction Recording.