https://github.com/akkartik/mu/blob/main/003trace.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71 :(before "End Globals")
72 trace_stream* Trace_stream = NULL;
73
74 :(before "End Types")
75 struct trace_stream {
76 vector<trace_line> past_lines;
77
78
79 trace_stream() {
80
81 }
82 ~trace_stream() {
83
84 }
85
86 };
87
88
89
90
91
92
93 :(before "End Includes")
94
95
96 #define trace(...) !Trace_stream ? cerr : Trace_stream->stream(__VA_ARGS__)
97
98 :(before "End trace_stream Fields")
99
100 ostringstream* curr_stream;
101 string curr_label;
102 int curr_depth;
103
104 int collect_depth;
105 ofstream null_stream;
106
107
108 :(before "struct trace_stream")
109 const int Max_depth = 9999;
110 :(before "End trace_stream Constructor")
111 curr_stream = NULL;
112 curr_depth = Max_depth;
113 collect_depth = Max_depth;
114
115 :(before "struct trace_stream")
116 struct trace_line {
117 string contents;
118 string label;
119 int depth;
120 trace_line(string c, string l) {
121 contents = c;
122 label = l;
123 depth = 0;
124 }
125 trace_line(string c, string l, int d) {
126 contents = c;
127 label = l;
128 depth = d;
129 }
130 };
131
132 string unescape_newline(string& s) {
133 std::stringstream ss;
134 for (int i = 0; i < SIZE(s); ++i) {
135 if (s.at(i) == '\n')
136 ss << "\\n";
137 else
138 ss << s.at(i);
139 }
140 return ss.str();
141 }
142
143 void dump_trace_line(ostream& s, trace_line& t) {
144 s << std::setw(2) << t.depth << ' ' << t.label << ": " << unescape_newline(t.contents) << '\n';
145 }
146
147
148 :(before "End trace_stream Methods")
149 ostream& stream(string label) {
150 return stream(Max_depth, label);
151 }
152
153 ostream& stream(int depth, string label) {
154 if (depth > collect_depth) return null_stream;
155 curr_stream = new ostringstream;
156 curr_label = label;
157 curr_depth = depth;
158 (*curr_stream) << std::hex;
159 return *curr_stream;
160 }
161
162
163 :(before "End Types")
164 struct end {};
165 :(code)
166 ostream& operator<<(ostream& os, end ) {
167 if (Trace_stream) Trace_stream->newline();
168 return os;
169 }
170
171
172 :(before "End Types")
173 struct die {};
174 :(code)
175 ostream& operator<<(ostream& , die ) {
176 if (Trace_stream) Trace_stream->newline();
177 exit(1);
178 }
179
180 :(before "End trace_stream Methods")
181 void newline();
182 :(code)
183 void trace_stream::newline() {
184 if (!curr_stream) return;
185 string curr_contents = curr_stream->str();
186 if (!curr_contents.empty()) {
187 past_lines.push_back(trace_line(curr_contents, trim(curr_label), curr_depth));
188
189 trace_line& t = past_lines.back();
190 if (should_incrementally_print_trace()) {
191 dump_trace_line(cerr, t);
192 }
193
194 }
195
196
197 delete curr_stream;
198 curr_stream = NULL;
199 curr_label.clear();
200 curr_depth = Max_depth;
201 }
202
203
204
205 :(before "End Includes")
206 #define START_TRACING_UNTIL_END_OF_SCOPE lease_tracer leased_tracer;
207 :(before "End Test Setup")
208 START_TRACING_UNTIL_END_OF_SCOPE
209
210
211 :(before "End Types")
212 struct lease_tracer {
213 lease_tracer();
214 ~lease_tracer();
215 };
216 :(code)
217 lease_tracer::lease_tracer() { Trace_stream = new trace_stream; }
218 lease_tracer::~lease_tracer() {
219 delete Trace_stream;
220 Trace_stream = NULL;
221 }
222
223
224
225 :(before "End Includes")
226 #define raise (!Trace_stream ? (++Trace_errors,cerr) : Trace_stream->stream(Error_depth, "error"))
227 #define warn (!Trace_stream ? (++Trace_errors,cerr) : Trace_stream->stream(Warn_depth, "warn"))
228
229
230 :(before "struct trace_stream")
231 const int Error_depth = 0;
232 const int Warn_depth = 1;
233 :(before "End Globals")
234 int Hide_errors = false;
235 int Hide_warnings = false;
236 :(before "End Reset")
237 Hide_errors = false;
238 Hide_warnings = false;
239
240 :(before "End Test Setup")
241 Hide_warnings = true;
242 :(code)
243 bool trace_stream::should_incrementally_print_trace() {
244 if (!Hide_errors && curr_depth == Error_depth) return true;
245 if (!Hide_warnings && !Hide_errors && curr_depth == Warn_depth) return true;
246
247 return false;
248 }
249 :(before "End trace_stream Methods")
250 bool should_incrementally_print_trace();
251
252 :(before "End Globals")
253 int Trace_errors = 0;
254
255
256
257 :(before "End Test Teardown")
258 if (Passed && !Hide_errors && trace_contains_errors()) {
259 Passed = false;
260 }
261 :(code)
262 bool trace_contains_errors() {
263 return Trace_errors > 0 || trace_count("error") > 0;
264 }
265
266 :(before "End Includes")
267
268
269 #define assert_for_now assert
270 #define raise_for_now raise
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289 :(before "End Includes")
290 #define CHECK_TRACE_CONTENTS(...) check_trace_contents(__FUNCTION__, __FILE__, __LINE__, __VA_ARGS__)
291
292 #define CHECK_TRACE_DOESNT_CONTAIN(...) CHECK(trace_doesnt_contain(__VA_ARGS__))
293
294 #define CHECK_TRACE_COUNT(label, count) \
295 if (Passed && trace_count(label) != (count)) { \
296 cerr << "\nF - " << __FUNCTION__ << "(" << __FILE__ << ":" << __LINE__ << "): trace_count of " << label << " should be " << count << '\n'; \
297 cerr << " got " << trace_count(label) << '\n'; \
298 DUMP(label); \
299 Passed = false; \
300 return; \
301 }
302
303 #define CHECK_TRACE_CONTAINS_ERRORS() CHECK(trace_contains_errors())
304 #define CHECK_TRACE_DOESNT_CONTAIN_ERRORS() \
305 if (Passed && trace_contains_errors()) { \
306 cerr << "\nF - " << __FUNCTION__ << "(" << __FILE__ << ":" << __LINE__ << "): unexpected errors\n"; \
307 DUMP("error"); \
308 Passed = false; \
309 return; \
310 }
311
312
313 #define CLEAR_TRACE delete Trace_stream, Trace_stream = new trace_stream
314
315 :(code)
316 bool check_trace_contents(string FUNCTION, string FILE, int LINE, string expected) {
317 if (!Passed) return false;
318 if (!Trace_stream) return false;
319 vector<string> expected_lines = split(expected, "\n");
320 int curr_expected_line = 0;
321 while (curr_expected_line < SIZE(expected_lines) && expected_lines.at(curr_expected_line).empty())
322 ++curr_expected_line;
323 if (curr_expected_line == SIZE(expected_lines)) return true;
324 string label, contents;
325 split_label_contents(expected_lines.at(curr_expected_line), &label, &contents);
326 for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) {
327 if (label != p->label) continue;
328 string t = trim(p->contents);
329 if (contents != unescape_newline(t)) continue;
330 ++curr_expected_line;
331 while (curr_expected_line < SIZE(expected_lines) && expected_lines.at(curr_expected_line).empty())
332 ++curr_expected_line;
333 if (curr_expected_line == SIZE(expected_lines)) return true;
334 split_label_contents(expected_lines.at(curr_expected_line), &label, &contents);
335 }
336
337 if (line_exists_anywhere(label, contents)) {
338 cerr << "\nF - " << FUNCTION << "(" << FILE << ":" << LINE << "): line [" << label << ": " << contents << "] out of order in trace:\n";
339 DUMP("");
340 }
341 else {
342 cerr << "\nF - " << FUNCTION << "(" << FILE << ":" << LINE << "): missing [" << contents << "] in trace:\n";
343 DUMP(label);
344 }
345 Passed = false;
346 return false;
347 }
348
349 bool trace_doesnt_contain(string expected) {
350 vector<string> tmp = split_first(expected, ": ");
351 if (SIZE(tmp) == 1) {
352 raise << expected << ": missing label or contents in trace line\n" << end();
353 assert(false);
354 }
355 return trace_count(tmp.at(0), tmp.at(1)) == 0;
356 }
357
358 int trace_count(string label) {
359 return trace_count(label, "");
360 }
361
362 int trace_count(string label, string line) {
363 if (!Trace_stream) return 0;
364 long result = 0;
365 for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) {
366 if (label == p->label) {
367 if (line == "" || trim(line) == trim(p->contents))
368 ++result;
369 }
370 }
371 return result;
372 }
373
374 int trace_count_prefix(string label, string prefix) {
375 if (!Trace_stream) return 0;
376 long result = 0;
377 for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) {
378 if (label == p->label) {
379 if (starts_with(trim(p->contents), trim(prefix)))
380 ++result;
381 }
382 }
383 return result;
384 }
385
386 void split_label_contents(const string& s, string* label, string* contents) {
387 static const string delim(": ");
388 size_t pos = s.find(delim);
389 if (pos == string::npos) {
390 *label = "";
391 *contents = trim(s);
392 }
393 else {
394 *label = trim(s.substr(0, pos));
395 *contents = trim(s.substr(pos+SIZE(delim)));
396 }
397 }
398
399 bool line_exists_anywhere(const string& label, const string& contents) {
400 for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) {
401 if (label != p->label) continue;
402 if (contents == trim(p->contents)) return true;
403 }
404 return false;
405 }
406
407 vector<string> split(string s, string delim) {
408 vector<string> result;
409 size_t begin=0, end=s.find(delim);
410 while (true) {
411 if (end == string::npos) {
412 result.push_back(string(s, begin, string::npos));
413 break;
414 }
415 result.push_back(string(s, begin, end-begin));
416 begin = end+SIZE(delim);
417 end = s.find(delim, begin);
418 }
419 return result;
420 }
421
422 vector<string> split_first(string s, string delim) {
423 vector<string> result;
424 size_t end=s.find(delim);
425 result.push_back(string(s, 0, end));
426 if (end != string::npos)
427 result.push_back(string(s, end+SIZE(delim), string::npos));
428 return result;
429 }
430
431
432
433 :(before "End Includes")
434
435 #define DUMP(label) if (Trace_stream) cerr << Trace_stream->readable_contents(label);
436
437
438
439 #define dbg trace(0, "a")
440
441
442
443
444
445 :(before "End Globals")
446 ofstream Trace_file;
447 :(before "End Commandline Options(*arg)")
448 else if (is_equal(*arg, "--trace")) {
449 cerr << "saving trace to 'last_run'\n";
450 Trace_file.open("last_run");
451
452
453 Trace_file << " 0 dummy: start\n";
454
455 }
456 :(before "End trace Commit")
457 if (Trace_file.is_open()) {
458 dump_trace_line(Trace_file, t);
459 Trace_file.flush();
460 past_lines.pop_back();
461 }
462 :(before "End One-time Setup")
463 atexit(cleanup_main);
464 :(code)
465 void cleanup_main() {
466 if (Trace_file.is_open()) Trace_file.close();
467
468 }
469
470 :(before "End trace_stream Methods")
471 string readable_contents(string label) {
472 string trim(const string& s);
473 ostringstream output;
474 label = trim(label);
475 for (vector<trace_line>::iterator p = past_lines.begin(); p != past_lines.end(); ++p)
476 if (label.empty() || label == p->label)
477 dump_trace_line(output, *p);
478 return output.str();
479 }
480
481
482
483
484 :(before "End Globals")
485 bool Dump_trace = false;
486 :(before "End Commandline Options(*arg)")
487 else if (is_equal(*arg, "--dump")) {
488 Dump_trace = true;
489 }
490 :(before "End Incremental Trace Print Conditions")
491 if (Dump_trace) return true;
492
493
494
495 :(code)
496 string trim(const string& s) {
497 string::const_iterator first = s.begin();
498 while (first != s.end() && isspace(*first))
499 ++first;
500 if (first == s.end()) return "";
501
502 string::const_iterator last = --s.end();
503 while (last != s.begin() && isspace(*last))
504 --last;
505 ++last;
506 return string(first, last);
507 }
508
509 :(before "End Includes")
510 #include <vector>
511 using std::vector;
512 #include <list>
513 using std::list;
514 #include <set>
515 using std::set;
516
517 #include <sstream>
518 using std::istringstream;
519 using std::ostringstream;
520
521 #include <fstream>
522 using std::ifstream;
523 using std::ofstream;