HAL  v4.5.0-83-g30c8f0afc
The Hardware Analyzer - a comprehensive reverse engineering and manipulation framework for gate-level netlists.
saleae.cpp
Go to the documentation of this file.
1 #include <iostream>
2 #include <iomanip>
10 #include <QFileInfo>
11 #include <QString>
12 #include <sys/resource.h>
13 
14 using namespace hal;
15 
16 // checks if a file exists
17 bool file_exists (const std::string& path)
18 {
19  if (FILE *ff = fopen(path.c_str(), "rb"))
20  {
21  fclose(ff);
22  return true;
23  }
24  return false;
25 }
26 
27 // template for space saving printing
28 template<typename T> void print_element(T t, const int& width, bool align)
29 {
30  if (align)
31  {
32  std::cout << std::left << std::setw(width) << std::setfill(' ') << t << " | ";
33  }
34  else
35  {
36  std::cout << std::right << std::setw(width) << std::setfill(' ') << t << " | ";
37  }
38 }
39 
40 // check, given the size, whether an entry may be printed
41 bool check_size(bool necessary, char op, int size_val, int compare_val)
42 {
43  if (!necessary) return true;
44  switch (op)
45  {
46  case '+':
47  return (compare_val > size_val);
48  case '-':
49  return (compare_val < size_val);
50  default:
51  return (compare_val == size_val);
52  }
53 }
54 
55 // check, given an id list, whether an entry may be printed
56 bool check_ids(bool necessary, std::unordered_set<int> id_set, int id_to_check)
57 {
58  return ((id_set.count(id_to_check)) || (!necessary));
59 }
60 
61 // parses a string containing ids to a set of ids
62 std::unordered_set<int> parse_list_of_ids(std::string list_of_ids)
63 {
64  std::unordered_set<int> id_set;
65  std::stringstream ss(list_of_ids);
66  std::vector<std::string> splited_ids;
67  while (ss.good())
68  {
69  std::string substr;
70  getline(ss, substr, ',');
71  splited_ids.push_back(substr);
72  }
73 
74  for (std::string id_entry : splited_ids)
75  {
76  if (id_entry.find('-') != std::string::npos)
77  {
78  std::stringstream range_stream(id_entry);
79  std::vector<std::string> range;
80  while (range_stream.good())
81  {
82  std::string substr;
83  getline(range_stream, substr, '-');
84  range.push_back(substr);
85  }
86  int tmp_id = std::stoi(range.front());
87  while (tmp_id <= std::stoi(range.back()))
88  {
89  id_set.insert(tmp_id);
90  tmp_id ++;
91  }
92  }
93  else
94  {
95  id_set.insert(std::stoi(id_entry));
96  }
97  }
98  return id_set;
99 }
100 
101 
102 // parses a string containing a timerange to a timerange tuple
103 std::tuple<int,int> parse_timerange(std::string timerange) {
104  size_t del_pos = timerange.find(',');
105  int time_1 = std::stoi(timerange.substr(0, del_pos));
106  int time_2 = std::stoi(timerange.substr(del_pos + 1, std::string::npos));
107  return std::make_tuple(time_1, time_2);
108 }
109 
110 
111 int time_within_tolerance(const std::vector<uint64_t>& time_vec, int cur_time, int tolerance)
112 {
113  // find closest time
114  int closest_time;
115  auto i = std::lower_bound(
116  time_vec.begin(),
117  time_vec.end(),
118  cur_time
119  );
120 
121  if (i == time_vec.begin())
122  {
123  closest_time = cur_time;
124  }
125  else
126  {
127  int s_time = *(i - 1); // smaller time
128  int g_time = *(i); // greater time
129  if (abs(cur_time - s_time) <= abs(cur_time - g_time))
130  {
131  closest_time = time_vec[i - time_vec.begin() - 1];
132  }
133  else
134  {
135  closest_time = time_vec[i - time_vec.begin()];
136  }
137  }
138 
139  // is closest time in tolerance range
140  int suitable_time = -1;
141  if (abs(cur_time - closest_time) <= tolerance)
142  {
143  suitable_time = closest_time;
144  }
145 
146  return suitable_time;
147 }
148 
149 
150 // saleae ls-tool
151 void saleae_ls(std::string p_path, std::string size, std::string ids, bool validate)
152 {
153  // handle --dir option
154  std::string path = (p_path == "") ? "./saleae.json" : p_path + "/saleae.json";
155  if (!file_exists(path))
156  {
157  std::cout << "Cannot open file: " << path << std::endl;
158  exit (1);
159  }
160 
161  // handle --size option
162  bool size_necessary = false;
163  char size_op = '=';
164  int size_val = 0;
165  if (size != "")
166  {
167  size_necessary = true;
168  if (size[0] == '+' || size[0] == '-')
169  {
170  size_op = size[0];
171  size_val = std::stoi(size.substr(1));
172  }
173  else
174  {
175  size_val = std::stoi(size);
176  }
177  }
178 
179  // handle --validate option (counter)
180  int val_cnt = 0;
181 
182  // handle --id option
183  bool ids_necessary = false;
184  std::unordered_set<int> id_set;
185  if (ids != "")
186  {
187  ids_necessary = true;
188  id_set = parse_list_of_ids(ids);
189  }
190 
191  SaleaeDirectory *sd = new SaleaeDirectory(path, false);
192  std::vector<SaleaeDirectoryNetEntry> net_entries = sd->dump();
193 
194  // collect length for better formatting
195  int format_length [6] = {7, 8, 19, 11, 10, 15}; // length of the column titles
196  if (validate)
197  format_length[0] = 6;
198  for (const SaleaeDirectoryNetEntry& sdne : net_entries)
199  {
200  for (const SaleaeDirectoryFileIndex& sdfi : sdne.indexes())
201  {
202  if (check_size(size_necessary, size_op, size_val, sdfi.numberValues()) && check_ids(ids_necessary, id_set, sdne.id()))
203  {
204  format_length[0] = (format_length[0] < std::to_string(sdne.id()).length()) ? std::to_string(sdne.id()).length() : format_length[0];
205  format_length[1] = (format_length[1] < sdne.name().length()) ? sdne.name().length() : format_length[1];
206  format_length[2] = (format_length[2] < std::to_string(sdfi.numberValues()).length()) ? std::to_string(sdfi.numberValues()).length() : format_length[2];
207  format_length[3] = (format_length[3] < std::to_string(sdfi.beginTime()).length()) ? std::to_string(sdfi.beginTime()).length() : format_length[3];
208  format_length[4] = (format_length[4] < std::to_string(sdfi.endTime()).length()) ? std::to_string(sdfi.endTime()).length() : format_length[4];
209  format_length[5] = (format_length[5] < std::to_string(sdfi.index()).length() + 12) ? std::to_string(sdfi.index()).length() + 12: format_length[5];
210  }
211  }
212  }
213  int abs_length = format_length[0] + format_length[1] + format_length[2] + format_length[3] + format_length[4] + format_length[5] + 16;
214  if (validate)
215  abs_length += 5;
216 
217  // print saleae-dir content
218  std::cout << std::string(abs_length + 2, '-') << std::endl;
219 
220  if (validate)
221  {
222  print_element("| | Net ID", format_length[0] + 4, true);
223  }
224  else
225  {
226  print_element("| Net ID", format_length[0], true);
227  }
228 
229  print_element("Net Name", format_length[1], true);
230  print_element("Total Number Values", format_length[2], true);
231  print_element("First Event", format_length[3], true);
232  print_element("Last Event", format_length[4], true);
233  print_element("Binary filename", format_length[5], true);
234  std::cout << std::endl;
235  std::cout << '|' << std::string(abs_length, '-') << '|' << std::endl;
236  for (const SaleaeDirectoryNetEntry& sdne : net_entries)
237  {
238  for (const SaleaeDirectoryFileIndex& sdfi : sdne.indexes())
239  {
240  if (check_size(size_necessary, size_op, size_val, sdfi.numberValues()) && check_ids(ids_necessary, id_set, sdne.id()))
241  {
242  std::cout << '|';
243  if (validate)
244  {
245  std::string valid_char = " ";
246 
247  p_path = (p_path == "") ? "" : p_path + "/";
248  std::string bin_path = p_path + "digital_" + std::to_string(sdfi.index()) + ".bin";
249  if (!file_exists(bin_path))
250  {
251  valid_char = "*";
252  val_cnt++;
253  }
254  else
255  {
256  SaleaeInputFile *sf = new SaleaeInputFile(bin_path);
258  if ((sdfi.beginTime() != sf->header()->mBeginTime) || (sdfi.endTime() != sf->header()->mEndTime) || (sdfi.numberValues() - 1 != sf->header()->mNumTransitions) || (QFileInfo(QString::fromStdString(bin_path)).size() != 44 + (sdfi.numberValues() - 1) * 8))
259  {
260  valid_char = "*";
261  val_cnt++;
262  }
263  }
264 
265  print_element(valid_char, 2, false);
266  }
267  print_element(sdne.id(), format_length[0], false);
268  print_element(sdne.name(), format_length[1], true);
269  print_element(sdfi.numberValues(), format_length[2], false);
270  print_element(sdfi.beginTime(), format_length[3], false);
271  print_element(sdfi.endTime(), format_length[4], false);
272  print_element("digital_" + std::to_string(sdfi.index()) + ".bin", format_length[5], true);
273  std::cout << std::endl;
274  }
275  }
276  }
277  std::cout << std::string(abs_length + 2, '-') << std::endl;
278  if (validate)
279  {
280  if (val_cnt > 0)
281  {
282  std::cout << "Number of non-matching directory entries: " << val_cnt << std::endl;
283  exit (1);
284  }
285  else
286  {
287  std::cout << "validation OK" << std::endl;
288  exit (0);
289  }
290  }
291  exit (0);
292 }
293 
294 
295 // saleae cat-tool
296 void saleae_cat(std::string path, std::string file_name, bool dump_header, bool dump_data)
297 {
298  // handle --dir option
299  path = (path == "") ? "./" + file_name : path + "/" + file_name;
300  if (!file_exists(path))
301  {
302  std::cout << "Cannot open file: " << path << std::endl;
303  exit (1);
304  }
305 
306  // handle --only-data and --only-header option
307  if (!dump_header && !dump_data)
308  {
309  dump_header = true;
310  dump_data = true;
311  }
312 
313  SaleaeInputFile *sf = new SaleaeInputFile(path);
314  uint64_t num_transitions = sf->header()->mNumTransitions;
315 
316  // dump header
317  if (dump_header)
318  {
319  // get header content
320  uint64_t begin_time = sf->header()->mBeginTime;
321  uint64_t end_time = sf->header()->mEndTime;
322  int32_t start_val = sf->header()->mValue;
323  std::string data_format;
324  switch (sf->header()->storageFormat())
325  {
327  data_format = "Double";
328  break;
330  data_format = "Uint64";
331  break;
332  case SaleaeHeader::Coded:
333  data_format = "Coded";
334  break;
335  }
336 
337  // collect length for better formatting
338  int format_length [5] = {12, 10, 8, 11, 21}; // length of the column titles
339  format_length[0] = (format_length[0] < data_format.length()) ? data_format.length() : format_length[0];
340  format_length[1] = (format_length[1] < std::to_string(begin_time).length()) ? std::to_string(begin_time).length() : format_length[1];
341  format_length[1] = (format_length[1] < std::to_string(end_time).length()) ? std::to_string(end_time).length() : format_length[1];
342  format_length[2] = (format_length[2] < std::to_string(start_val).length()) ? std::to_string(start_val).length() : format_length[2];
343  format_length[3] = (format_length[3] < std::to_string(num_transitions).length()) ? std::to_string(num_transitions).length() : format_length[3];
344  int abs_length = format_length[0] + format_length[1] + format_length[2] + format_length[3] + format_length[4] + 13;
345 
346  // print saleae-file header
347  std::cout << std::string(abs_length + 2, '-') << std::endl;
348  std::cout << '|';
349  print_element(" Data Format", format_length[0], true);
350  print_element("Start Time", format_length[1], true);
351  print_element("End Time", format_length[2], true);
352  print_element("Start Value", format_length[3], true);
353  print_element("Number of Transitions", format_length[4], true);
354  std::cout << std::endl;
355  std::cout << '|' << std::string(abs_length, '-') << '|' << std::endl;
356  std::cout << "| ";
357  print_element(data_format, format_length[0] - 1, true);
358  print_element(begin_time, format_length[1], false);
359  print_element(end_time, format_length[2], false);
360  print_element(start_val, format_length[3], false);
361  print_element(num_transitions, format_length[4], false);
362  std::cout << std::endl;
363  std::cout << std::string(abs_length + 2, '-') << std::endl;
364  }
365 
366  // dump data
367  if (dump_data && (num_transitions > 0))
368  {
369  SaleaeDataBuffer *db = sf->get_buffered_data(num_transitions + 1);
370 
371  // get data
372  uint64_t* time_array = db->mTimeArray;
373  int* value_array = db->mValueArray;
374 
375  // collect length for better formatting
376  int format_length [3] = {4, 4, 5}; // length of the column titles
377  for (int i = 0; i <= num_transitions; i++)
378  {
379  format_length[0] = (format_length[0] < std::to_string(i).length()) ? std::to_string(i).length() : format_length[0];
380  format_length[1] = (format_length[1] < std::to_string(time_array[i]).length()) ? std::to_string(time_array[i]).length() : format_length[1];
381  format_length[2] = (format_length[2] < std::to_string(value_array[i]).length()) ? std::to_string(time_array[i]).length() : format_length[2];
382  }
383  int abs_length = format_length[0] + format_length[1] + format_length[2] + 7;
384 
385  // print saleae-file data
386  std::cout << std::string(abs_length + 2, '-') << std::endl;
387  std::cout << '|';
388  print_element(" No.", format_length[0], true);
389  print_element("Time", format_length[1], true);
390  print_element("Value", format_length[2], true);
391  std::cout << std::endl;
392  std::cout << '|' << std::string(abs_length, '-') << '|' << std::endl;
393  for (int i = 0; i <= num_transitions; i++)
394  {
395  std::cout << '|';
396  print_element(i, format_length[0], false);
397  print_element(time_array[i], format_length[1], false);
398  print_element(value_array[i], format_length[2], false);
399  std::cout << std::endl;
400  }
401  std::cout << std::string(abs_length + 2, '-') << std::endl;
402  }
403 
404  exit (0);
405 }
406 
407 
408 // saleae diff-tool
409 void saleae_diff(std::string path_1, std::string path_2, std::string ids, bool only_diff, int tolerance)
410 {
411  // handle --dir option
412  std::string path_1_json = (path_1 == "") ? "./saleae.json" : path_1 + "/saleae.json";
413  if (!file_exists(path_1_json))
414  {
415  std::cout << "Cannot open file: " << path_1_json << std::endl;
416  exit (1);
417  }
418  std::string path_2_json = path_2 + "/saleae.json";
419  if (!file_exists(path_2_json))
420  {
421  std::cout << "Cannot open file: " << path_2_json << std::endl;
422  exit (1);
423  }
424 
425  // handle --id option
426  bool ids_necessary = false;
427  std::unordered_set<int> id_set;
428  if (ids != "")
429  {
430  ids_necessary = true;
431  id_set = parse_list_of_ids(ids);
432  }
433 
434  struct row_t
435  {
436  int alt_time;
437  int val_1;
438  bool val_1_avail;
439  int val_2;
440  bool val_2_avail;
441  bool diff;
442  };
443 
444  struct net_t
445  {
446  int id;
447  std::string name_1;
448  std::string name_2;
449  bool name_diff;
450  bool data_diff;
451  std::map<uint64_t, row_t> net_data;
452  int format_length[4];
453  };
454 
455  std::vector<int> ids_not_in_2;
456  std::vector<int> ids_not_in_1;
457  std::vector<net_t> diff_vec;
458  bool diff_found = false;
459 
460  SaleaeDirectory *sd_1 = new SaleaeDirectory(path_1_json, false);
461  std::vector<SaleaeDirectoryNetEntry> net_entries_1 = sd_1->dump();
462  SaleaeDirectory *sd_2 = new SaleaeDirectory(path_2_json, false);
463  std::vector<SaleaeDirectoryNetEntry> net_entries_2 = sd_2->dump();
464  for (const SaleaeDirectoryNetEntry& sdne_1 : net_entries_1)
465  {
466  if (!check_ids(ids_necessary, id_set, sdne_1.id()))
467  {
468  continue;
469  }
470  bool id_found = false;
471  for (const SaleaeDirectoryNetEntry& sdne_2 : net_entries_2)
472  {
473  if (sdne_1.id() != sdne_2.id())
474  {
475  continue;
476  }
477  id_found = true;
478 
479  // save net info
480  struct net_t cur_net;
481  cur_net.id = sdne_1.id();
482  cur_net.name_1 = sdne_1.name();
483  cur_net.name_2 = sdne_2.name();
484 
485  // format len
486  cur_net.format_length[0] = 2;
487  cur_net.format_length[1] = 4;
488  cur_net.format_length[2] = cur_net.name_1.length();
489  cur_net.format_length[3] = cur_net.name_2.length();
490 
491  // name diff?
492  cur_net.name_diff = cur_net.name_1 != cur_net.name_2;
493 
494  // compare data
495  int diff_cnt = 0;
496  std::map<uint64_t, row_t> net_data; // key=time, value=row struct
497  std::vector<uint64_t> time_vec;
498  for (const SaleaeDirectoryFileIndex& sdfi : sdne_1.indexes())
499  {
500  path_1 = (path_1 == "") ? "." : path_1;
501  std::string bin_path = path_1 + "/digital_" + std::to_string(sdfi.index()) + ".bin";
502  if (!file_exists(bin_path))
503  {
504  std::cout << "Error in database: " << path_1 << "\nCannot open file: " << bin_path << std::endl;
505  exit (1);
506  }
507  SaleaeInputFile sf(bin_path);
509  if (!db)
510  {
511  std::cout << "db nullptr: <" << bin_path << ">" << std::endl;
512  continue;
513  }
514  // save first net_data times in map
515  for (int i = 0; i < db->mCount; i++)
516  {
517  uint64_t t = db->mTimeArray[i];
518  time_vec.push_back(t);
519  net_data[t] = row_t{.val_1 = db->mValueArray[i], .val_1_avail = true, .val_2_avail = false, .diff = true};
520  diff_cnt++;
521  // update format len
522  cur_net.format_length[1] = (cur_net.format_length[1] < std::to_string(t).length()) ? std::to_string(t).length() : cur_net.format_length[1];
523  cur_net.format_length[2] = (cur_net.format_length[2] < std::to_string(net_data[t].val_1).length()) ? std::to_string(net_data[t].val_1).length() : cur_net.format_length[2];
524  }
525  delete db;
526  }
527 
528  for (const SaleaeDirectoryFileIndex& sdfi : sdne_2.indexes())
529  {
530  std::string bin_path = path_2 + "/digital_" + std::to_string(sdfi.index()) + ".bin";
531  if (!file_exists(bin_path))
532  {
533  std::cout << "Error in database: " << path_2 << "\nCannot open file: " << bin_path << std::endl;
534  exit (1);
535  }
536  SaleaeInputFile sf(bin_path);
538  if (!db)
539  {
540  std::cout << "db nullptr: <" << bin_path << ">" << std::endl;
541  continue;
542  }
543  // save second net_data times in map
544  for (int i = 0; i < db->mCount; i++)
545  {
546  uint64_t t = db->mTimeArray[i];
547  // is there a timestamp within tolerance range?
548  int twt = time_within_tolerance(time_vec, t, tolerance);
549  if (twt >= 0)
550  {
551  net_data[twt].alt_time = t;
552  net_data[twt].val_2 = db->mValueArray[i];
553  net_data[twt].val_2_avail = true;
554  net_data[twt].diff = (net_data[twt].val_1 != net_data[twt].val_2);
555  diff_cnt = net_data[twt].diff ? diff_cnt : diff_cnt - 1;
556  // update format len
557  cur_net.format_length[3] = (cur_net.format_length[3] < std::to_string(net_data[twt].val_2).length()) ? std::to_string(net_data[twt].val_2).length() : cur_net.format_length[3];
558  }
559  else
560  {
561  net_data[t] = row_t{.val_1_avail = false, .val_2 = db->mValueArray[i], .val_2_avail = true, .diff = true};
562  diff_cnt++;
563  // update format len
564  cur_net.format_length[1] = (cur_net.format_length[1] < std::to_string(t).length()) ? std::to_string(t).length() : cur_net.format_length[1];
565  cur_net.format_length[3] = (cur_net.format_length[3] < std::to_string(net_data[t].val_2).length()) ? std::to_string(net_data[t].val_2).length() : cur_net.format_length[3];
566  }
567  }
568  // data diff?
569  cur_net.data_diff = diff_cnt > 0;
570  if (cur_net.data_diff)
571  {
572  cur_net.net_data = net_data;
573  }
574  // save net struct if there is a difference
575  if (cur_net.data_diff || cur_net.name_diff)
576  {
577  diff_vec.push_back(cur_net);
578  diff_found = true;
579  }
580  delete db;
581  }
582 
583  }
584  if (!id_found)
585  {
586  ids_not_in_2.push_back(sdne_1.id());
587  diff_found = true;
588  }
589  }
590  for (const SaleaeDirectoryNetEntry& sdne_2 : net_entries_2)
591  {
592  if (!check_ids(ids_necessary, id_set, sdne_2.id()))
593  {
594  continue;
595  }
596  bool id_found = false;
597  for (const SaleaeDirectoryNetEntry& sdne_1 : net_entries_1)
598  {
599  if (sdne_2.id() != sdne_1.id())
600  {
601  continue;
602  }
603  id_found = true;
604  }
605  if (!id_found)
606  {
607  ids_not_in_1.push_back(sdne_2.id());
608  diff_found = true;
609  }
610  }
611 
612  // output
613  if (!diff_found) {
614  std::cout << "- Content of waveform database is the same" << std::endl;
615  exit (0);
616  }
617  std::cout << "=> Database 1: " << path_1 << "\n=> Database 2: " << path_2 << "\n\n" << std::endl;
618  // id not found
619  for (int id : ids_not_in_2)
620  {
621  std::cout << "- Waveform ID " << id << " found in database 1 but not in database 2\n" << std::endl;
622  }
623  for (int id : ids_not_in_1)
624  {
625  std::cout << "- Waveform ID " << id << " found in database 2 but not in database 1\n" << std::endl;
626  }
627  for (net_t cur_net : diff_vec)
628  {
629  bool name_diff_bool = cur_net.name_diff && !cur_net.data_diff;
630  if (name_diff_bool)
631  {
632  // only diffrent name
633  std::cout << "- Waveform ID " << cur_net.id << " is named \"" << cur_net.name_1 << "\" in database 1 but \"" << cur_net.name_2 << "\" in database 2\n" << std::endl;
634  }
635  else
636  {
637  // diffrent data
638  // header row
639  if (name_diff_bool) {
640  std::cout << "\n- Waveform ID " << cur_net.id << " has a data difference" << std::endl;
641  }
642  else {
643  std::cout << "\n- Waveform ID " << " ("<< cur_net.name_1 << ") " << cur_net.id << " has a data difference" << std::endl;
644  }
645  int abs_length = cur_net.format_length[0] + cur_net.format_length[1] + cur_net.format_length[2] + cur_net.format_length[3] + 10;
646  std::cout << std::string(abs_length + 2, '-') << std::endl;
647  std::string diff_char = cur_net.name_diff ? "*" : " ";
648 
649  print_element("| " + diff_char, cur_net.format_length[0], true);
650  print_element("Time", cur_net.format_length[1], true);
651  print_element(cur_net.name_1, cur_net.format_length[2], true);
652  print_element(cur_net.name_2, cur_net.format_length[3], true);
653  std::cout << std::endl;
654  std::cout << '|' << std::string(abs_length, '-') << '|' << std::endl;
655 
656  // data rows
657  for (auto &item : cur_net.net_data)
658  {
659  struct row_t cur_row = item.second;
660  // handle --only-differences option
661  if (only_diff && !cur_row.diff)
662  {
663  continue;
664  }
665  std::string diff_char = cur_row.diff ? "*" : " ";
666  std::string val_1 = cur_row.val_1_avail ? std::to_string(cur_row.val_1) : "-";
667  std::string val_2 = cur_row.val_2_avail ? std::to_string(cur_row.val_2) : "-";
668  int main_time = item.first;
669  int alt_time = (cur_row.val_1_avail && cur_row.val_2_avail) ? cur_row.alt_time : main_time;
670 
671  std::cout << '|';
672  print_element(diff_char, cur_net.format_length[0], true);
673 
674  if (alt_time < main_time)
675  {
676  print_element(alt_time, cur_net.format_length[1], false);
677  }
678  else
679  {
680  print_element(main_time, cur_net.format_length[1], false);
681  }
682 
683 
684  print_element(val_1, cur_net.format_length[2], true);
685  print_element(val_2, cur_net.format_length[3], true);
686  std::cout << std::endl;
687  }
688  std::cout << std::string(abs_length + 2, '-') << std::endl;
689  std::cout << "\n" << std::endl;
690  }
691  }
692  exit (1);
693 }
694 
695 
696 // saleae export tool
697 void saleae_export(std::string path_1, std::string path_2, std::string ids, std::string timerange)
698 {
699  // handle --dir option
700  path_2 = (path_2 == "") ? "." : path_2;
701 
702  // handle --id option
703  bool ids_necessary = false;
704  std::unordered_set<int> id_set;
705  if (ids != "")
706  {
707  ids_necessary = true;
708  id_set = parse_list_of_ids(ids);
709  }
710 
711  // handle --time-range option
712  bool tr_necessary = false;
713  int time_shift, last_time;
714  if (timerange != "")
715  {
716  tr_necessary = true;
717  auto [t1, t2] = parse_timerange(timerange);
718  time_shift = t1;
719  last_time = t2;
720  if (time_shift > last_time) {
721  std::cout << "Invalid timerange. First time must be smaller then second time!" << std::endl;
722  exit (1);
723  }
724  }
725 
726  VcdSerializer *vcd_s = new VcdSerializer(QString::fromStdString(path_2), true);
727  std::string saleae_fp= path_2 + "/saleae.json";
728  WaveDataList *wave_data_list = new WaveDataList(QString::fromStdString(saleae_fp));
729  wave_data_list->updateFromSaleae();
730 
731  QList<const WaveData*> wave_data_qlist;
732  if (ids_necessary) {
733  for (int id : id_set) {
734  const WaveData* wd = wave_data_list->waveDataById(id);
735  if (wd != nullptr) {
736  wave_data_qlist.append(wd);
737  }
738  }
739  }
740  else {
741  for (const WaveData* wd : *wave_data_list) {
742  wave_data_qlist.append(wd);
743  }
744  }
745  if (!tr_necessary) {
746  time_shift = 0;
747  last_time = wave_data_list->timeFrame().sceneMaxTime();
748  }
749 
750 
751  // add new waveform, might need to increase ulimit
752  struct rlimit rlim;
753  getrlimit(RLIMIT_NOFILE, &rlim);
754 
755  unsigned int required = wave_data_qlist.size() + 256;
756  Q_ASSERT(rlim.rlim_max >= required);
757  if (rlim.rlim_cur < required)
758  {
759  rlim.rlim_cur = required;
760  setrlimit(RLIMIT_NOFILE, &rlim);
761  }
762 
763 
765  bool ret = false;
766 
767  if (ext == "vcd")
768  ret = vcd_s->exportVcd(QString::fromStdString(path_1), wave_data_qlist, wave_data_list->timeFrame().sceneMinTime(), last_time, time_shift);
769  else if (ext == "csv")
770  ret = vcd_s->exportCsv(QString::fromStdString(path_1), wave_data_qlist);
771  else
772  std::cout << "Export file format not supported, must be either vcd or csv." << std::endl;
773 
774  if (ret) {
775  exit (0);
776  }
777  else {
778  exit (1);
779  }
780 }
781 
782 
783 int main(int argc, const char* argv[])
784 {
785  std::cout << std::endl;
786  // initialize logging
790 
791  // initialize and parse options
792  ProgramOptions generic_options("generic options");
793  generic_options.add("--help", "print help messages");
794 
795  ProgramOptions tool_options("tools");
796  tool_options.add("ls", "Lists content of saleae directory file saleae.json");
797  tool_options.add("cat", "Dump content of binary file <arg> into console", {""});
798  tool_options.add("diff", "Compares content of database in current directory with other saleae database at <arg>", {""});
799  tool_options.add("export", "Exports waveforms from database in current SALEAE directory to .VCD or .CSV file <arg>", {""});
800 
801  ProgramOptions ls_options("ls options");
802  ls_options.add({"-d", "--dir"}, "lists saleae directory from directory given by absolute or relative path name <ARG>", {ProgramOptions::A_REQUIRED_PARAMETER});
803  ls_options.add({"-s", "--size"}, "lists only entries with given number <ARG> of waveform events", {ProgramOptions::A_REQUIRED_PARAMETER});
804  ls_options.add({"-i", "--id"}, "list only entries where ID matches entry in list <ARG>. Entries are separated by comma. A single entry can be either an ID or a range sepearated by hyphen", {ProgramOptions::A_REQUIRED_PARAMETER});
805  ls_options.add({"-v", "--validate"}, "validates time stamps and number of waveform events by comparing directory information with binary file header and binary file sizes.");
806 
807  ProgramOptions cat_options("cat options");
808  cat_options.add({"-d", "--dir"}, "binary file is not in current directory but in directory given by path name <ARG>", {ProgramOptions::A_REQUIRED_PARAMETER});
809  cat_options.add({"-h", "--only-header"}, "dump only header");
810  cat_options.add({"-b", "--only-data"}, "dump only data including start value");
811 
812  ProgramOptions diff_options("diff options");
813  diff_options.add({"-d", "--dir"}, "current database not in current directory but in directory given by path name <ARG>", {ProgramOptions::A_REQUIRED_PARAMETER});
814  diff_options.add({"-i", "--id"}, "compares only entries where ID matches entry in list <ARG>. Entries are separated by comma. A single entry can be either an ID or a range sepearated by hyphen", {ProgramOptions::A_REQUIRED_PARAMETER});
815  diff_options.add({"-x", "--only-differences"}, "when dumping waveform data values all rows without differences are suppressed (except header row)");
816  diff_options.add({"-t", "--max-tolerance"}, "the integer value <ARG> sets the maximum tolerance when comparing waveform data. On default (zero tolerance) two waveforms A,B with transition values A=[0,12000,16000] B=[0,12010,16010] are considered to be different. However, when tolerance is set to 10 or higher the comparison will not find any differences", {ProgramOptions::A_REQUIRED_PARAMETER});
817 
818  ProgramOptions export_options("export options");
819  export_options.add({"-d", "--dir"}, "binary file is not in current directory but in directory given by path name <ARG>", {ProgramOptions::A_REQUIRED_PARAMETER});
820  export_options.add({"-i", "--id"}, "export only entries where ID matches entry in list <ARG>. Entries are separated by comma. A single entry can be either an ID or a range sepearated by hyphen", {ProgramOptions::A_REQUIRED_PARAMETER});
821  export_options.add({"-r", "--time-range"}, "exports only events within given time range <ARG>. The value in <ARG> gets subtracted from every exported time stamp so that exported time starts at zero. A start value (last value before entering the time range) must be provided for each exported waveform.", {ProgramOptions::A_REQUIRED_PARAMETER});
822 
823  ProgramArguments args = tool_options.parse(argc, argv);
824 
825  if (args.is_option_set("ls"))
826  {
827  ls_options.add(generic_options);
828  ProgramArguments args = ls_options.parse(argc, argv);
829 
830  bool unknown_option_exists = false;
831  for (std::string opt : ls_options.get_unknown_arguments())
832  {
833  unknown_option_exists = (opt != "ls") ? true : unknown_option_exists;
834  }
835 
836  if (args.is_option_set("--help") || unknown_option_exists)
837  {
838  std::cout << ls_options.get_options_string() << std::endl;
839  }
840  else
841  {
842  saleae_ls(args.get_parameter("--dir"), args.get_parameter("--size"), args.get_parameter("--id"), args.is_option_set("--validate"));
843  }
844  }
845  else if (args.is_option_set("cat"))
846  {
847  std::string filename = args.get_parameter("cat");
848  cat_options.add(generic_options);
849  ProgramArguments args = cat_options.parse(argc, argv);
850 
851  bool unknown_option_exists = false;
852  for (std::string opt : cat_options.get_unknown_arguments())
853  {
854  unknown_option_exists = ((opt != "cat") && (opt != filename)) ? true : unknown_option_exists;
855  }
856 
857  if (filename == "")
858  {
859  std::cout << tool_options.get_options_string();
860  std::cout << ls_options.get_options_string();
861  std::cout << cat_options.get_options_string();
862  std::cout << diff_options.get_options_string() << std::endl;
863  }
864  else if (args.is_option_set("--help") || unknown_option_exists)
865  {
866  std::cout << cat_options.get_options_string() << std::endl;
867  }
868  else
869  {
870  saleae_cat(args.get_parameter("--dir"), filename, args.is_option_set("--only-header"), args.is_option_set("--only-data"));
871  }
872  }
873  else if (args.is_option_set("diff"))
874  {
875  std::string diff_path = args.get_parameter("diff");
876  diff_options.add(generic_options);
877  ProgramArguments args = diff_options.parse(argc, argv);
878 
879  bool unknown_option_exists = false;
880  for (std::string opt : diff_options.get_unknown_arguments())
881  {
882  unknown_option_exists = ((opt != "diff") && (opt != diff_path)) ? true : unknown_option_exists;
883  }
884  int tolerance = 0;
885  if (args.is_option_set("--max-tolerance"))
886  {
887  tolerance = std::stoi(args.get_parameter("--max-tolerance"));
888  if (tolerance < 0)
889  {
890  unknown_option_exists = true;
891  }
892  }
893  if (diff_path == "")
894  {
895  std::cout << tool_options.get_options_string();
896  std::cout << ls_options.get_options_string();
897  std::cout << cat_options.get_options_string();
898  std::cout << diff_options.get_options_string() << std::endl;
899  }
900  else if (args.is_option_set("--help") || unknown_option_exists)
901  {
902  std::cout << diff_options.get_options_string() << std::endl;
903  }
904  else
905  {
906  saleae_diff(args.get_parameter("--dir"), diff_path, args.get_parameter("--id"), args.is_option_set("--only-differences"), tolerance);
907  }
908  }
909  else if (args.is_option_set("export"))
910  {
911  std::string export_path = args.get_parameter("export");
912  export_options.add(generic_options);
913  ProgramArguments args = export_options.parse(argc, argv);
914 
915  bool unknown_option_exists = false;
916  for (std::string opt : export_options.get_unknown_arguments())
917  {
918  unknown_option_exists = ((opt != "export") && (opt != export_path)) ? true : unknown_option_exists;
919  }
920  if (export_path == "")
921  {
922  std::cout << tool_options.get_options_string();
923  std::cout << ls_options.get_options_string();
924  std::cout << cat_options.get_options_string();
925  std::cout << diff_options.get_options_string();
926  std::cout << export_options.get_options_string() << std::endl;
927  }
928  else if (args.is_option_set("--help") || unknown_option_exists)
929  {
930  std::cout << export_options.get_options_string() << std::endl;
931  }
932  else
933  {
934  saleae_export(export_path, args.get_parameter("--dir"), args.get_parameter("--id"), args.get_parameter("--time-range"));
935  }
936  }
937  else
938  {
939  tool_options.add(generic_options);
940 
941  std::cout << tool_options.get_options_string();
942  std::cout << ls_options.get_options_string();
943  std::cout << cat_options.get_options_string();
944  std::cout << diff_options.get_options_string();
945  std::cout << export_options.get_options_string() << std::endl;
946  }
947 }
u32 size
std::shared_ptr< spdlog::logger > add_channel(const std::string &channel_name, const std::vector< std::shared_ptr< log_sink >> &sinks, const std::string &level="info")
Definition: log.cpp:100
void deactivate_all_channels()
Definition: log.cpp:196
static std::shared_ptr< log_sink > create_gui_sink()
Definition: log.cpp:274
static std::shared_ptr< log_sink > create_file_sink(const std::filesystem::path &file_name="", const bool truncate=false)
Definition: log.cpp:247
static std::shared_ptr< log_sink > create_stdout_sink(const bool colored=true)
Definition: log.cpp:216
static LogManager * get_instance(const std::filesystem::path &file_name="")
Definition: log.cpp:61
std::string get_parameter(const std::string &flag) const
bool is_option_set(const std::string &flag) const
std::string get_options_string() const
ProgramArguments parse(int argc, const char *argv[])
bool add(const std::string &flag, const std::string &description, const std::initializer_list< std::string > &parameters={})
std::vector< std::string > get_unknown_arguments()
static const std::string A_REQUIRED_PARAMETER
constant to specify that a parameter is required and does not have a default value.
uint64_t mCount
Number of elements in buffer.
Definition: saleae_file.h:137
uint64_t * mTimeArray
Buffer for mCount transition time values.
Definition: saleae_file.h:140
int * mValueArray
Buffer for mCount data values.
Definition: saleae_file.h:143
The SaleaeDirectoryFileIndex class represents a single SALEAE data file. The class comprises the inde...
The SaleaeDirectory class provides the API for the SALEAE directory file. The SALEAE directory file w...
std::vector< SaleaeDirectoryNetEntry > dump() const
Dump content of class instance to console for debugging purpose.
The SaleaeDirectoryNetEntry class represents a regular waveform for a single simulated net....
uint64_t mNumTransitions
Definition: saleae_file.h:70
@ Uint64
Double values,.
Definition: saleae_file.h:62
uint64_t mEndTime
Definition: saleae_file.h:69
StorageFormat storageFormat() const
Getter for storage format, see above.
Definition: saleae_file.h:91
uint64_t mBeginTime
Definition: saleae_file.h:68
SaleaeDataBuffer * get_buffered_data(uint64_t nread)
Returns pointer to data buffer reading from current file position up to nread events....
const SaleaeHeader * header() const
Getter for header information.
Definition: saleae_file.h:214
bool exportVcd(const QString &filename, const QList< const WaveData * > &waves, u32 startTime, u32 endTime, u32 timeShift=0)
bool exportCsv(const QString &filename, const QList< const WaveData * > &waves)
WaveData * waveDataById(const int id)
Definition: wave_data.cpp:1673
const WaveDataTimeframe & timeFrame() const
Definition: wave_data.h:233
u64 sceneMaxTime() const
Definition: wave_data.cpp:1228
u64 sceneMinTime() const
Definition: wave_data.cpp:1242
Definition: defines.h:45
i32 id
QString suffix() const const
void append(const T &value)
int size() const const
QString fromStdString(const std::string &str)
QString toLower() const const
int time_within_tolerance(const std::vector< uint64_t > &time_vec, int cur_time, int tolerance)
Definition: saleae.cpp:111
void saleae_export(std::string path_1, std::string path_2, std::string ids, std::string timerange)
Definition: saleae.cpp:697
void print_element(T t, const int &width, bool align)
Definition: saleae.cpp:28
bool check_size(bool necessary, char op, int size_val, int compare_val)
Definition: saleae.cpp:41
std::unordered_set< int > parse_list_of_ids(std::string list_of_ids)
Definition: saleae.cpp:62
void saleae_diff(std::string path_1, std::string path_2, std::string ids, bool only_diff, int tolerance)
Definition: saleae.cpp:409
bool check_ids(bool necessary, std::unordered_set< int > id_set, int id_to_check)
Definition: saleae.cpp:56
std::tuple< int, int > parse_timerange(std::string timerange)
Definition: saleae.cpp:103
bool file_exists(const std::string &path)
Definition: saleae.cpp:17
void saleae_cat(std::string path, std::string file_name, bool dump_header, bool dump_data)
Definition: saleae.cpp:296
int main(int argc, const char *argv[])
Definition: saleae.cpp:783
void saleae_ls(std::string p_path, std::string size, std::string ids, bool validate)
Definition: saleae.cpp:151