@@ -176,183 +176,189 @@ fn void Generator.writeCalls(Generator* gen, string_buffer.Buf* out) {
176176 }
177177 out.add("};\n\n");
178178
179- const char[] begin =
180- ```c
181- struct c2_func_t {
182- unsigned count;
183- unsigned short filename_idx;
184- unsigned short line;
185- const char *funcname;
186- };
187- static struct c2_func_t c2_func_data[] = {
188- ```;
189- out.add(begin);
179+ out.add(
180+ ```c
181+ struct c2_func_t {
182+ unsigned count;
183+ unsigned short filename_idx;
184+ unsigned short line;
185+ const char *funcname;
186+ };
187+ static struct c2_func_t c2_func_data[] = {
188+ ```);
190189 // TODO: store function definition location
191190 n = gen.funcnames.length();
192191 for (u32 i = 0; i < n; i++) {
193192 out.print(" { 0, 0, 0, \"%s\" },\n", gen.funcnames.get(i));
194193 }
195- out.add("};\n\n");
194+ out.add(
195+ ```c
196+ };
196197
197- out.add(" struct c2_trace_t {\n"
198- " unsigned count;\n"
199- " unsigned char filename_idx;\n"
200- " unsigned char column;\n"
201- " unsigned short line;\n"
202- " unsigned short caller_idx;\n"
203- " unsigned short callee_idx;\n"
204- "};\n"
205- " static struct c2_trace_t c2_trace_data[] = {\n"
206- );
198+ struct c2_trace_t {
199+ unsigned count;
200+ unsigned char filename_idx;
201+ unsigned char column;
202+ unsigned short line;
203+ unsigned short caller_idx;
204+ unsigned short callee_idx;
205+ };
206+ static struct c2_trace_t c2_trace_data[] = {
207+ ``` );
207208 for (u32 i = 0; i < gen.calls.count; i++) {
208209 out.print(" { 0, %d, %d, %d, %d, %d },\n",
209210 gen.calls.array[i].filename_idx, gen.calls.array[i].column, gen.calls.array[i].line,
210211 gen.calls.array[i].caller_idx, gen.calls.array[i].callee_idx);
211212 }
212- out.add("};\n\n");
213213
214- out.add("static const unsigned c2_trace_length = sizeof(c2_trace_data) / sizeof(c2_trace_data[0]);\n"
215- "unsigned c2_trace_counts[sizeof(c2_trace_data) / sizeof(c2_trace_data[0])];\n\n"
216- );
217- out.add("char *getenv(const char *);\n"
218- "int dprintf(int fd, const char *format, ...);\n"
219- "int strcmp(const char *s1, const char *s2);\n"
220- "typedef unsigned long size_t;\n"
221- "void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));\n"
222- "int sscanf(const char *, const char *, ...);\n\n"
223- );
224- out.add("static int c2_match_name(const char *name, const char *pattern) {\n"
225- " for (;;) {\n"
226- " char c1, c2;\n"
227- " while ((c1 = *name++) == (c2 = *pattern++)) {\n"
228- " if (!c1) return 1;\n"
229- " }\n"
230- " if (c2 == '?') {\n"
231- " if (c1) continue;\n"
232- " return 0;\n"
233- " }\n"
234- " if (c2 == '*') {\n"
235- " c2 = *pattern++;\n"
236- " if (!c2 || c2 == ',' || c2 == ';') return 1;\n"
237- " for (; c1; c1 = *name++) {\n"
238- " if (c1 == c2 && c2_match_name(name, pattern)) return 1;\n"
239- " }\n"
240- " return 0;\n"
241- " }\n"
242- " return (!c1 && (c2 == ',' || c2 == ';'));\n"
243- " }\n"
244- "}\n\n"
245- );
246- out.add("static int c2_match_pattern(const char *name, const char *pattern) {\n"
247- " if (!pattern || !*pattern) return 1;\n"
248- " for (const char *p = pattern; *p;) {\n"
249- " if (c2_match_name(name, p)) return 1;\n"
250- " char c;\n"
251- " while ((c = *p++) != 0 && c != ',' && c != ';') continue;\n"
252- " if (c != ',') break;\n"
253- " }\n"
254- " return 0;\n"
255- "}\n\n"
256- );
257- out.add("static int c2_cmp_funcs(const void *a, const void *b) {\n"
258- " const struct c2_trace_t *aa = a;\n"
259- " const struct c2_trace_t *bb = b;\n"
260- " struct c2_func_t *fa = &c2_func_data[aa->callee_idx];\n"
261- " struct c2_func_t *fb = &c2_func_data[bb->callee_idx];\n"
262- " if (fa->count != fb->count) return fa->count < fb->count ? 1 : -1;\n"
263- " if (fa != fb) return strcmp(fa->funcname, fb->funcname);\n"
264- " return (aa->count < bb->count) - (aa->count > bb->count);\n"
265- "}\n\n"
266- );
267- out.add("static int c2_cmp_calls(const void *a, const void *b) {\n"
268- " const struct c2_trace_t *aa = a;\n"
269- " const struct c2_trace_t *bb = b;\n"
270- " return (aa->count < bb->count) - (aa->count > bb->count);\n"
271- "}\n\n"
272- );
273- out.add("void __attribute__((destructor)) c2_trace_calls(void) {\n"
274- " const char *p = getenv(\"C2_TRACE\");\n"
275- " const char *pattern = 0;\n"
276- " const char *filename = 0;\n"
277- " const char *caller = 0;\n"
278- " if (!p || !*p) return;\n"
279- " unsigned min = 1, min2 = 1;\n"
280- " int pos = 0, mode = 3, fd = 1, indent = 2;\n"
281- " for (; *p; p += pos) {\n"
282- " for (pos = 0;;) {\n"
283- " sscanf(p, \" min%*1[=]%n%u\", &pos, &min);\n"
284- " if (pos) break;\n"
285- " sscanf(p, \" min2%*1[=]%n%u\", &pos, &min2);\n"
286- " if (pos) break;\n"
287- " sscanf(p, \" indent%*1[=]%n%u\", &pos, &indent);\n"
288- " if (pos) break;\n"
289- " sscanf(p, \" mode%*1[=]%n%d\", &pos, &mode);\n"
290- " if (pos) break;\n"
291- " sscanf(p, \" fd%*1[=]%n%d\", &pos, &fd);\n"
292- " if (pos) break;\n"
293- " sscanf(p, \" name%*1[=]%n\", &pos);\n"
294- " if (pos) { pattern = p + pos; break; }\n"
295- " sscanf(p, \" filename%*1[=]%n\", &pos);\n"
296- " if (pos) { filename = p + pos; break; }\n"
297- " sscanf(p, \" caller%*1[=]%n\", &pos);\n"
298- " if (pos) { caller = p + pos; break; }\n"
299- " sscanf(p, \"%*[^;=]%*1[=]%n%\", &pos);\n"
300- " break;\n"
301- " }\n"
302- " if (!pos) pattern = p;\n"
303- " while (p[pos] && p[pos++] != ';') continue;\n"
304- " }\n"
305- " if (!mode) return;\n"
306- " unsigned *counts = c2_trace_counts;\n"
307- " struct c2_trace_t* data = c2_trace_data;\n"
308- " unsigned n = c2_trace_length;\n"
309- " for (unsigned i = 0; i < n; i++) {\n"
310- " struct c2_trace_t *cp = &data[i];\n"
311- " if (c2_match_pattern(c2_func_data[cp->callee_idx].funcname, pattern)\n"
312- " && c2_match_pattern(c2_filenames[cp->filename_idx], filename)\n"
313- " && c2_match_pattern(c2_func_data[cp->caller_idx].funcname, caller)) {\n"
314- " cp->count = counts[i];\n"
315- " c2_func_data[cp->callee_idx].count += counts[i];\n"
316- " }\n"
317- " }\n"
318- " if (mode == 2) {\n"
319- " qsort(data, n, sizeof(*data), c2_cmp_calls);\n"
320- " indent = 0;\n"
321- " min2 = min;\n"
322- " } else {\n"
323- " qsort(data, n, sizeof(*data), c2_cmp_funcs);\n"
324- " }\n"
325- " struct c2_func_t *last = 0;\n"
326- " int show = 0;\n"
327- " for (unsigned i = 0; i < n; i++) {\n"
328- " struct c2_trace_t *cp = &data[i];\n"
329- " struct c2_func_t *func = &c2_func_data[cp->callee_idx];\n"
330- " unsigned count1 = func->count;\n"
331- " unsigned count2 = cp->count;\n"
332- " if (count1 < min) continue;\n"
333- " if (func != last) {\n"
334- " show = mode & 2;\n"
335- " if (mode & 1) {\n"
336- " dprintf(fd, \"%.*s%s: %u call%.*s\\n\", show, \"\\n\",\n"
337- " func->funcname, count1, count1 != 1, \"s\");\n"
338- " }\n"
339- " last = func;\n"
340- " }\n"
341- " if (show && count2 >= min2) {\n"
342- " dprintf(fd, \"%*s%s:%d:%d: %s: %u call%.*s from %s\\n\",\n"
343- " indent, \"\",\n"
344- " c2_filenames[cp->filename_idx], cp->line, cp->column,\n"
345- " func->funcname, count2, count2 != 1, \"s\",\n"
346- " c2_func_data[cp->caller_idx].funcname);\n"
347- " }\n"
348- " }\n"
349- "}\n"
350- );
214+ out.add(
215+ ```c
216+ };
217+
218+ static const unsigned c2_trace_length = sizeof(c2_trace_data) / sizeof(c2_trace_data[0]);
219+ unsigned c2_trace_counts[sizeof(c2_trace_data) / sizeof(c2_trace_data[0])];
220+
221+ char *getenv(const char *);
222+ int dprintf(int fd, const char *format, ...);
223+ int strcmp(const char *s1, const char *s2);
224+ typedef unsigned long size_t;
225+ void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));
226+ int sscanf(const char *, const char *, ...);
227+
228+ static int c2_match_name(const char *name, const char *pattern) {
229+ for (;;) {
230+ char c1, c2;
231+ while ((c1 = *name++) == (c2 = *pattern++)) {
232+ if (!c1) return 1;
233+ }
234+ if (c2 == '?') {
235+ if (c1) continue;
236+ return 0;
237+ }
238+ if (c2 == '*') {
239+ c2 = *pattern++;
240+ if (!c2 || c2 == ',' || c2 == ';') return 1;
241+ for (; c1; c1 = *name++) {
242+ if (c1 == c2 && c2_match_name(name, pattern)) return 1;
243+ }
244+ return 0;
245+ }
246+ return (!c1 && (c2 == ',' || c2 == ';'));
247+ }
248+ }
249+
250+ static int c2_match_pattern(const char *name, const char *pattern) {
251+ if (!pattern || !*pattern) return 1;
252+ for (const char *p = pattern; *p;) {
253+ if (c2_match_name(name, p)) return 1;
254+ char c;
255+ while ((c = *p++) != 0 && c != ',' && c != ';') continue;
256+ if (c != ',') break;
257+ }
258+ return 0;
259+ }
260+
261+ static int c2_cmp_funcs(const void *a, const void *b) {
262+ const struct c2_trace_t *aa = a;
263+ const struct c2_trace_t *bb = b;
264+ struct c2_func_t *fa = &c2_func_data[aa->callee_idx];
265+ struct c2_func_t *fb = &c2_func_data[bb->callee_idx];
266+ if (fa->count != fb->count) return fa->count < fb->count ? 1 : -1;
267+ if (fa != fb) return strcmp(fa->funcname, fb->funcname);
268+ return (aa->count < bb->count) - (aa->count > bb->count);
269+ }
270+
271+ static int c2_cmp_calls(const void *a, const void *b) {
272+ const struct c2_trace_t *aa = a;
273+ const struct c2_trace_t *bb = b;
274+ return (aa->count < bb->count) - (aa->count > bb->count);
275+ }
276+
277+ void __attribute__((destructor)) c2_trace_calls(void) {
278+ const char *p = getenv("C2_TRACE");
279+ const char *pattern = 0;
280+ const char *filename = 0;
281+ const char *caller = 0;
282+ if (!p || !*p) return;
283+ unsigned min = 1, min2 = 1;
284+ int pos = 0, mode = 3, fd = 1, indent = 2;
285+ for (; *p; p += pos) {
286+ for (pos = 0;;) {
287+ sscanf(p, " min%*1[=]%n%u", &pos, &min);
288+ if (pos) break;
289+ sscanf(p, " min2%*1[=]%n%u", &pos, &min2);
290+ if (pos) break;
291+ sscanf(p, " indent%*1[=]%n%u", &pos, &indent);
292+ if (pos) break;
293+ sscanf(p, " mode%*1[=]%n%d", &pos, &mode);
294+ if (pos) break;
295+ sscanf(p, " fd%*1[=]%n%d", &pos, &fd);
296+ if (pos) break;
297+ sscanf(p, " name%*1[=]%n", &pos);
298+ if (pos) { pattern = p + pos; break; }
299+ sscanf(p, " filename%*1[=]%n", &pos);
300+ if (pos) { filename = p + pos; break; }
301+ sscanf(p, " caller%*1[=]%n", &pos);
302+ if (pos) { caller = p + pos; break; }
303+ sscanf(p, "%*[^;=]%*1[=]%n%", &pos);
304+ break;
305+ }
306+ if (!pos) pattern = p;
307+ while (p[pos] && p[pos++] != ';') continue;
308+ }
309+ if (!mode) return;
310+ unsigned *counts = c2_trace_counts;
311+ struct c2_trace_t* data = c2_trace_data;
312+ unsigned n = c2_trace_length;
313+ for (unsigned i = 0; i < n; i++) {
314+ struct c2_trace_t *cp = &data[i];
315+ if (c2_match_pattern(c2_func_data[cp->callee_idx].funcname, pattern)
316+ && c2_match_pattern(c2_filenames[cp->filename_idx], filename)
317+ && c2_match_pattern(c2_func_data[cp->caller_idx].funcname, caller)) {
318+ cp->count = counts[i];
319+ c2_func_data[cp->callee_idx].count += counts[i];
320+ }
321+ }
322+ if (mode == 2) {
323+ qsort(data, n, sizeof(*data), c2_cmp_calls);
324+ indent = 0;
325+ min2 = min;
326+ } else {
327+ qsort(data, n, sizeof(*data), c2_cmp_funcs);
328+ }
329+ struct c2_func_t *last = 0;
330+ int show = 0;
331+ for (unsigned i = 0; i < n; i++) {
332+ struct c2_trace_t *cp = &data[i];
333+ struct c2_func_t *func = &c2_func_data[cp->callee_idx];
334+ unsigned count1 = func->count;
335+ unsigned count2 = cp->count;
336+ if (count1 < min) continue;
337+ if (func != last) {
338+ show = mode & 2;
339+ if (mode & 1) {
340+ dprintf(fd, "%.*s%s: %u call%.*s\n", show, "\n",
341+ func->funcname, count1, count1 != 1, "s");
342+ }
343+ last = func;
344+ }
345+ if (show && count2 >= min2) {
346+ dprintf(fd, "%*s%s:%d:%d: %s: %u call%.*s from %s\n",
347+ indent, "",
348+ c2_filenames[cp->filename_idx], cp->line, cp->column,
349+ func->funcname, count2, count2 != 1, "s",
350+ c2_func_data[cp->caller_idx].funcname);
351+ }
352+ }
353+ }
354+ ```);
351355}
352356
353357fn void Generator.writeCallExterns(Generator* gen, string_buffer.Buf* out) {
354- out.add("extern unsigned c2_trace_counts[];\n"
355- "extern void c2_trace_calls(void);\n"
356- "extern int atexit(void (*func)(void));\n"
357- );
358+ out.add(
359+ ```c
360+ extern unsigned c2_trace_counts[];
361+ extern void c2_trace_calls(void);
362+ extern int atexit(void (*func)(void));
363+ ```);
358364}
0 commit comments