https://github.com/akkartik/mu/blob/master/subx/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
133 :(before "End trace_stream Methods")
134 ostream& stream(string label) {
135 return stream(Max_depth, label);
136 }
137
138 ostream& stream(int depth, string label) {
139 if (depth > collect_depth) return null_stream;
140 curr_stream = new ostringstream;
141 curr_label = label;
142 curr_depth = depth;
143 (*curr_stream) << std::hex;
144 return *curr_stream;
145 }
146
147
148 :(before "End Types")
149 struct end {};
150 :(code)
151 ostream& operator<<(ostream& os, end ) {
152 if (Trace_stream) Trace_stream->newline();
153 return os;
154 }
155
156 :(before "End trace_stream Methods")
157 void newline();
158 :(code)
159 void trace_stream::newline() {
160 if (!curr_stream) return;
161 string curr_contents = curr_stream->str();
162 if (!curr_contents.empty()) {
163 past_lines.push_back(trace_line(curr_contents, trim(curr_label), curr_depth));
164
165 trace_line& t = past_lines.back();
166 if (should_incrementally_print_trace()) {
167 cerr << std::setw(4) << t.depth << ' ' << t.label << ": " << t.contents << '\n';
168 }
169
170 }
171
172
173 delete curr_stream;
174 curr_stream = NULL;
175 curr_label.clear();
176 curr_depth = Max_depth;
177 }
178
179
180
181 :(before "End Includes")
182 #define START_TRACING_UNTIL_END_OF_SCOPE lease_tracer leased_tracer;
183 :(before "End Test Setup")
184 START_TRACING_UNTIL_END_OF_SCOPE
185
186
187 :(before "End Types")
188 struct lease_tracer {
189 lease_tracer();
190 ~lease_tracer();
191 };
192 :(code)
193 lease_tracer::lease_tracer() { Trace_stream = new trace_stream; }
194 lease_tracer::~lease_tracer() {
195 delete Trace_stream;
196 Trace_stream = NULL;
197 }
198
199
200
201 :(before "End Includes")
202 #define raise (!Trace_stream ? (++Trace_errors,cerr) : Trace_stream->stream(Error_depth, "error"))
203 #define warn (!Trace_stream ? (++Trace_errors,cerr) : Trace_stream->stream(Warn_depth, "warn"))
204
205
206 :(before "struct trace_stream")
207 const int Error_depth = 0;
208 const int Warn_depth = 1;
209 :(before "End Globals")
210 int Hide_errors = false;
211 int Hide_warnings = false;
212 :(before "End Reset")
213 Hide_errors = false;
214 Hide_warnings = false;
215
216 :(before "End Test Setup")
217 Hide_warnings = true;
218 :(code)
219 bool trace_stream::should_incrementally_print_trace() {
220 if (!Hide_errors && curr_depth == Error_depth) return true;
221 if (!Hide_warnings && !Hide_errors && curr_depth == Warn_depth) return true;
222
223 return false;
224 }
225 :(before "End trace_stream Methods")
226 bool should_incrementally_print_trace();
227
228 :(before "End Globals")
229 int Trace_errors = 0;
230
231
232
233 :(before "End Test Teardown")
234 if (Passed && !Hide_errors && trace_contains_errors()) {
235 Passed = false;
236 }
237 :(code)
238 bool trace_contains_errors() {
239 return Trace_errors > 0 || trace_count("error") > 0;
240 }
241
242 :(before "End Includes")
243
244
245 #define assert_for_now assert
246 #define raise_for_now raise
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265 :(before "End Includes")
266 #define CHECK_TRACE_CONTENTS(...) check_trace_contents(__FUNCTION__, __FILE__, __LINE__, __VA_ARGS__)
267
268 #define CHECK_TRACE_DOESNT_CONTAIN(...) CHECK(trace_doesnt_contain(__VA_ARGS__))
269
270 #define CHECK_TRACE_COUNT(label, count) \
271 if (Passed && trace_count(label) != (count)) { \
272 cerr << "\nF - " << __FUNCTION__ << "(" << __FILE__ << ":" << __LINE__ << "): trace_count of " << label << " should be " << count << '\n'; \
273 cerr << " got " << trace_count(label) << '\n'; \
274 DUMP(label); \
275 Passed = false; \
276 return; \
277 }
278
279 #define CHECK_TRACE_CONTAINS_ERRORS() CHECK(trace_contains_errors())
280 #define CHECK_TRACE_DOESNT_CONTAIN_ERRORS() \
281 if (Passed && trace_contains_errors()) { \
282 cerr << "\nF - " << __FUNCTION__ << "(" << __FILE__ << ":" << __LINE__ << "): unexpected errors\n"; \
283 DUMP("error"); \
284 Passed = false; \
285 return; \
286 }
287
288
289 #define CLEAR_TRACE delete Trace_stream, Trace_stream = new trace_stream
290
291 :(code)
292 bool check_trace_contents(string FUNCTION, string FILE, int LINE, string expected) {
293 if (!Passed) return false;
294 if (!Trace_stream) return false;
295 vector<string> expected_lines = split(expected, "\n");
296 int curr_expected_line = 0;
297 while (curr_expected_line < SIZE(expected_lines) && expected_lines.at(curr_expected_line).empty())
298 ++curr_expected_line;
299 if (curr_expected_line == SIZE(expected_lines)) return true;
300 string label, contents;
301 split_label_contents(expected_lines.at(curr_expected_line), &label, &contents);
302 for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) {
303 if (label != p->label) continue;
304 if (contents != trim(p->contents)) continue;
305 ++curr_expected_line;
306 while (curr_expected_line < SIZE(expected_lines) && expected_lines.at(curr_expected_line).empty())
307 ++curr_expected_line;
308 if (curr_expected_line == SIZE(expected_lines)) return true;
309 split_label_contents(expected_lines.at(curr_expected_line), &label, &contents);
310 }
311
312 if (line_exists_anywhere(label, contents)) {
313 cerr << "\nF - " << FUNCTION << "(" << FILE << ":" << LINE << "): line [" << label << ": " << contents << "] out of order in trace:\n";
314 DUMP("");
315 }
316 else {
317 cerr << "\nF - " << FUNCTION << "(" << FILE << ":" << LINE << "): missing [" << contents << "] in trace:\n";
318 DUMP(label);
319 }
320 Passed = false;
321 return false;
322 }
323
324 bool trace_doesnt_contain(string expected) {
325 vector<string> tmp = split_first(expected, ": ");
326 if (SIZE(tmp) == 1) {
327 raise << expected << ": missing label or contents in trace line\n" << end();
328 assert(false);
329 }
330 return trace_count(tmp.at(0), tmp.at(1)) == 0;
331 }
332
333 int trace_count(string label) {
334 return trace_count(label, "");
335 }
336
337 int trace_count(string label, string line) {
338 if (!Trace_stream) return 0;
339 long result = 0;
340 for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) {
341 if (label == p->label) {
342 if (line == "" || trim(line) == trim(p->contents))
343 ++result;
344 }
345 }
346 return result;
347 }
348
349 int trace_count_prefix(string label, string prefix) {
350 if (!Trace_stream) return 0;
351 long result = 0;
352 for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) {
353 if (label == p->label) {
354 if (starts_with(trim(p->contents), trim(prefix)))
355 ++result;
356 }
357 }
358 return result;
359 }
360
361 void split_label_contents(const string& s, string* label, string* contents) {
362 static const string delim(": ");
363 size_t pos = s.find(delim);
364 if (pos == string::npos) {
365 *label = "";
366 *contents = trim(s);
367 }
368 else {
369 *label = trim(s.substr(0, pos));
370 *contents = trim(s.substr(pos+SIZE(delim)));
371 }
372 }
373
374 bool line_exists_anywhere(const string& label, const string& contents) {
375 for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) {
376 if (label != p->label) continue;
377 if (contents == trim(p->contents)) return true;
378 }
379 return false;
380 }
381
382 vector<string> split(string s, string delim) {
383 vector<string> result;
384 size_t begin=0, end=s.find(delim);
385 while (true) {
386 if (end == string::npos) {
387 result.push_back(string(s, begin, string::npos));
388 break;
389 }
390 result.push_back(string(s, begin, end-begin));
391 begin = end+SIZE(delim);
392 end = s.find(delim, begin);
393 }
394 return result;
395 }
396
397 vector<string> split_first(string s, string delim) {
398 vector<string> result;
399 size_t end=s.find(delim);
400 result.push_back(string(s, 0, end));
401 if (end != string::npos)
402 result.push_back(string(s, end+SIZE(delim), string::npos));
403 return result;
404 }
405
406
407
408 :(before "End Includes")
409
410 #define DUMP(label) if (Trace_stream) cerr << Trace_stream->readable_contents(label);
411
412
413
414 #define dbg trace(0, "a")
415
416
417
418
419
420 :(before "End Globals")
421 ofstream Trace_file;
422 :(before "End Commandline Options(*arg)")
423 else if (is_equal(*arg, "--trace")) {
424 cerr << "saving trace to 'last_run'\n";
425 Trace_file.open("last_run");
426
427
428 Trace_file << " 0 dummy: start\n";
429 }
430 :(before "End trace Commit")
431 if (Trace_file) {
432 Trace_file << std::setw(4) << t.depth << ' ' << t.label << ": " << t.contents << '\n';
433 }
434 :(before "End One-time Setup")
435 atexit(cleanup_main);
436 :(code)
437 void cleanup_main() {
438 if (Trace_file) Trace_file.close();
439
440 }
441
442 :(before "End trace_stream Methods")
443 string readable_contents(string label) {
444 string trim(const string& s);
445 ostringstream output;
446 label = trim(label);
447 for (vector<trace_line>::iterator p = past_lines.begin(); p != past_lines.end(); ++p)
448 if (label.empty() || label == p->label)
449 output << std::setw(4) << p->depth << ' ' << p->label << ": " << p->contents << '\n';
450 return output.str();
451 }
452
453
454
455
456 :(before "End Globals")
457 bool Dump_trace = false;
458 :(before "End Commandline Options(*arg)")
459 else if (is_equal(*arg, "--dump")) {
460 Dump_trace = true;
461 }
462 :(before "End Incremental Trace Print Conditions")
463 if (Dump_trace) return true;
464
465
466
467 :(code)
468 string trim(const string& s) {
469 string::const_iterator first = s.begin();
470 while (first != s.end() && isspace(*first))
471 ++first;
472 if (first == s.end()) return "";
473
474 string::const_iterator last = --s.end();
475 while (last != s.begin() && isspace(*last))
476 --last;
477 ++last;
478 return string(first, last);
479 }
480
481 :(before "End Includes")
482 #include <vector>
483 using std::vector;
484 #include <list>
485 using std::list;
486 #include <set>
487 using std::set;
488
489 #include <sstream>
490 using std::istringstream;
491 using std::ostringstream;
492
493 #include <fstream>
494 using std::ifstream;
495 using std::ofstream;