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