| 1 | #define DISABLE_SIGN_COMPARE_WARNINGS |
| 2 | |
| 3 | #include "git-compat-util.h" |
| 4 | #include "config.h" |
| 5 | #include "json-writer.h" |
| 6 | #include "repository.h" |
| 7 | #include "run-command.h" |
| 8 | #include "version.h" |
| 9 | #include "trace2/tr2_dst.h" |
| 10 | #include "trace2/tr2_tbuf.h" |
| 11 | #include "trace2/tr2_sid.h" |
| 12 | #include "trace2/tr2_sysenv.h" |
| 13 | #include "trace2/tr2_tgt.h" |
| 14 | #include "trace2/tr2_tls.h" |
| 15 | #include "trace2/tr2_tmr.h" |
| 16 | |
| 17 | static struct tr2_dst tr2dst_event = { |
| 18 | .sysenv_var = TR2_SYSENV_EVENT, |
| 19 | }; |
| 20 | |
| 21 | /* |
| 22 | * The version number of the JSON data generated by the EVENT target in this |
| 23 | * source file. The version should be incremented if new event types are added, |
| 24 | * if existing fields are removed, or if there are significant changes in |
| 25 | * interpretation of existing events or fields. Smaller changes, such as adding |
| 26 | * a new field to an existing event, do not require an increment to the EVENT |
| 27 | * format version. |
| 28 | */ |
| 29 | #define TR2_EVENT_VERSION "4" |
| 30 | |
| 31 | /* |
| 32 | * Region nesting limit for messages written to the event target. |
| 33 | * |
| 34 | * The "region_enter" and "region_leave" messages (especially recursive |
| 35 | * messages such as those produced while diving the worktree or index) |
| 36 | * are primarily intended for the performance target during debugging. |
| 37 | * |
| 38 | * Some of the outer-most messages, however, may be of interest to the |
| 39 | * event target. Use the TR2_SYSENV_EVENT_NESTING setting to increase |
| 40 | * region details in the event target. |
| 41 | */ |
| 42 | static int tr2env_event_max_nesting_levels = 2; |
| 43 | |
| 44 | /* |
| 45 | * Use the TR2_SYSENV_EVENT_BRIEF to omit the <time>, <file>, and |
| 46 | * <line> fields from most events. |
| 47 | */ |
| 48 | static int tr2env_event_be_brief; |
| 49 | |
| 50 | static int fn_init(void) |
| 51 | { |
| 52 | int want = tr2_dst_trace_want(&tr2dst_event); |
| 53 | int max_nesting; |
| 54 | int want_brief; |
| 55 | const char *nesting; |
| 56 | const char *brief; |
| 57 | |
| 58 | if (!want) |
| 59 | return want; |
| 60 | |
| 61 | nesting = tr2_sysenv_get(TR2_SYSENV_EVENT_NESTING); |
| 62 | if (nesting && *nesting && ((max_nesting = atoi(nesting)) > 0)) |
| 63 | tr2env_event_max_nesting_levels = max_nesting; |
| 64 | |
| 65 | brief = tr2_sysenv_get(TR2_SYSENV_EVENT_BRIEF); |
| 66 | if (brief && *brief && |
| 67 | ((want_brief = git_parse_maybe_bool(brief)) != -1)) |
| 68 | tr2env_event_be_brief = want_brief; |
| 69 | |
| 70 | return want; |
| 71 | } |
| 72 | |
| 73 | static void fn_term(void) |
| 74 | { |
| 75 | tr2_dst_trace_disable(&tr2dst_event); |
| 76 | } |
| 77 | |
| 78 | /* |
| 79 | * Append common key-value pairs to the currently open JSON object. |
| 80 | * "event:"<event_name>" |
| 81 | * "sid":"<sid>" |
| 82 | * "thread":"<thread_name>" |
| 83 | * "time":"<time>" |
| 84 | * "file":"<filename>" |
| 85 | * "line":<line_number> |
| 86 | * "repo":<repo_id> |
| 87 | */ |
| 88 | static void event_fmt_prepare(const char *event_name, const char *file, |
| 89 | int line, const struct repository *repo, |
| 90 | struct json_writer *jw) |
| 91 | { |
| 92 | struct tr2tls_thread_ctx *ctx = tr2tls_get_self(); |
| 93 | struct tr2_tbuf tb_now; |
| 94 | |
| 95 | jw_object_string(jw, "event", event_name); |
| 96 | jw_object_string(jw, "sid", tr2_sid_get()); |
| 97 | jw_object_string(jw, "thread", ctx->thread_name); |
| 98 | |
| 99 | /* |
| 100 | * In brief mode, only emit <time> on these 2 event types. |
| 101 | */ |
| 102 | if (!tr2env_event_be_brief || !strcmp(event_name, "version") || |
| 103 | !strcmp(event_name, "atexit")) { |
| 104 | tr2_tbuf_utc_datetime_extended(&tb_now); |
| 105 | jw_object_string(jw, "time", tb_now.buf); |
| 106 | } |
| 107 | |
| 108 | if (!tr2env_event_be_brief && file && *file) { |
| 109 | jw_object_string(jw, "file", file); |
| 110 | jw_object_intmax(jw, "line", line); |
| 111 | } |
| 112 | |
| 113 | if (repo) |
| 114 | jw_object_intmax(jw, "repo", repo->trace2_repo_id); |
| 115 | } |
| 116 | |
| 117 | static void fn_too_many_files_fl(const char *file, int line) |
| 118 | { |
| 119 | const char *event_name = "too_many_files"; |
| 120 | struct json_writer jw = JSON_WRITER_INIT; |
| 121 | |
| 122 | jw_object_begin(&jw, 0); |
| 123 | event_fmt_prepare(event_name, file, line, NULL, &jw); |
| 124 | jw_end(&jw); |
| 125 | |
| 126 | tr2_dst_write_line(&tr2dst_event, &jw.json); |
| 127 | jw_release(&jw); |
| 128 | } |
| 129 | |
| 130 | static void fn_version_fl(const char *file, int line) |
| 131 | { |
| 132 | const char *event_name = "version"; |
| 133 | struct json_writer jw = JSON_WRITER_INIT; |
| 134 | |
| 135 | jw_object_begin(&jw, 0); |
| 136 | event_fmt_prepare(event_name, file, line, NULL, &jw); |
| 137 | jw_object_string(&jw, "evt", TR2_EVENT_VERSION); |
| 138 | jw_object_string(&jw, "exe", git_version_string); |
| 139 | jw_end(&jw); |
| 140 | |
| 141 | tr2_dst_write_line(&tr2dst_event, &jw.json); |
| 142 | jw_release(&jw); |
| 143 | |
| 144 | if (tr2dst_event.too_many_files) |
| 145 | fn_too_many_files_fl(file, line); |
| 146 | } |
| 147 | |
| 148 | static void fn_start_fl(const char *file, int line, |
| 149 | uint64_t us_elapsed_absolute, const char **argv) |
| 150 | { |
| 151 | const char *event_name = "start"; |
| 152 | struct json_writer jw = JSON_WRITER_INIT; |
| 153 | double t_abs = (double)us_elapsed_absolute / 1000000.0; |
| 154 | |
| 155 | jw_object_begin(&jw, 0); |
| 156 | event_fmt_prepare(event_name, file, line, NULL, &jw); |
| 157 | jw_object_double(&jw, "t_abs", 6, t_abs); |
| 158 | jw_object_inline_begin_array(&jw, "argv"); |
| 159 | jw_array_argv(&jw, argv); |
| 160 | jw_end(&jw); |
| 161 | jw_end(&jw); |
| 162 | |
| 163 | tr2_dst_write_line(&tr2dst_event, &jw.json); |
| 164 | jw_release(&jw); |
| 165 | } |
| 166 | |
| 167 | static void fn_exit_fl(const char *file, int line, uint64_t us_elapsed_absolute, |
| 168 | int code) |
| 169 | { |
| 170 | const char *event_name = "exit"; |
| 171 | struct json_writer jw = JSON_WRITER_INIT; |
| 172 | double t_abs = (double)us_elapsed_absolute / 1000000.0; |
| 173 | |
| 174 | jw_object_begin(&jw, 0); |
| 175 | event_fmt_prepare(event_name, file, line, NULL, &jw); |
| 176 | jw_object_double(&jw, "t_abs", 6, t_abs); |
| 177 | jw_object_intmax(&jw, "code", code); |
| 178 | jw_end(&jw); |
| 179 | |
| 180 | tr2_dst_write_line(&tr2dst_event, &jw.json); |
| 181 | jw_release(&jw); |
| 182 | } |
| 183 | |
| 184 | static void fn_signal(uint64_t us_elapsed_absolute, int signo) |
| 185 | { |
| 186 | const char *event_name = "signal"; |
| 187 | struct json_writer jw = JSON_WRITER_INIT; |
| 188 | double t_abs = (double)us_elapsed_absolute / 1000000.0; |
| 189 | |
| 190 | jw_object_begin(&jw, 0); |
| 191 | event_fmt_prepare(event_name, __FILE__, __LINE__, NULL, &jw); |
| 192 | jw_object_double(&jw, "t_abs", 6, t_abs); |
| 193 | jw_object_intmax(&jw, "signo", signo); |
| 194 | jw_end(&jw); |
| 195 | |
| 196 | tr2_dst_write_line(&tr2dst_event, &jw.json); |
| 197 | jw_release(&jw); |
| 198 | } |
| 199 | |
| 200 | static void fn_atexit(uint64_t us_elapsed_absolute, int code) |
| 201 | { |
| 202 | const char *event_name = "atexit"; |
| 203 | struct json_writer jw = JSON_WRITER_INIT; |
| 204 | double t_abs = (double)us_elapsed_absolute / 1000000.0; |
| 205 | |
| 206 | jw_object_begin(&jw, 0); |
| 207 | event_fmt_prepare(event_name, __FILE__, __LINE__, NULL, &jw); |
| 208 | jw_object_double(&jw, "t_abs", 6, t_abs); |
| 209 | jw_object_intmax(&jw, "code", code); |
| 210 | jw_end(&jw); |
| 211 | |
| 212 | tr2_dst_write_line(&tr2dst_event, &jw.json); |
| 213 | jw_release(&jw); |
| 214 | } |
| 215 | |
| 216 | static void maybe_add_string_va(struct json_writer *jw, const char *field_name, |
| 217 | const char *fmt, va_list ap) |
| 218 | { |
| 219 | if (fmt && *fmt) { |
| 220 | va_list copy_ap; |
| 221 | struct strbuf buf = STRBUF_INIT; |
| 222 | |
| 223 | va_copy(copy_ap, ap); |
| 224 | strbuf_vaddf(&buf, fmt, copy_ap); |
| 225 | va_end(copy_ap); |
| 226 | |
| 227 | jw_object_string(jw, field_name, buf.buf); |
| 228 | strbuf_release(&buf); |
| 229 | return; |
| 230 | } |
| 231 | } |
| 232 | |
| 233 | static void fn_error_va_fl(const char *file, int line, const char *fmt, |
| 234 | va_list ap) |
| 235 | { |
| 236 | const char *event_name = "error"; |
| 237 | struct json_writer jw = JSON_WRITER_INIT; |
| 238 | |
| 239 | jw_object_begin(&jw, 0); |
| 240 | event_fmt_prepare(event_name, file, line, NULL, &jw); |
| 241 | maybe_add_string_va(&jw, "msg", fmt, ap); |
| 242 | /* |
| 243 | * Also emit the format string as a field in case |
| 244 | * post-processors want to aggregate common error |
| 245 | * messages by type without argument fields (such |
| 246 | * as pathnames or branch names) cluttering it up. |
| 247 | */ |
| 248 | if (fmt && *fmt) |
| 249 | jw_object_string(&jw, "fmt", fmt); |
| 250 | jw_end(&jw); |
| 251 | |
| 252 | tr2_dst_write_line(&tr2dst_event, &jw.json); |
| 253 | jw_release(&jw); |
| 254 | } |
| 255 | |
| 256 | static void fn_command_path_fl(const char *file, int line, const char *pathname) |
| 257 | { |
| 258 | const char *event_name = "cmd_path"; |
| 259 | struct json_writer jw = JSON_WRITER_INIT; |
| 260 | |
| 261 | jw_object_begin(&jw, 0); |
| 262 | event_fmt_prepare(event_name, file, line, NULL, &jw); |
| 263 | jw_object_string(&jw, "path", pathname); |
| 264 | jw_end(&jw); |
| 265 | |
| 266 | tr2_dst_write_line(&tr2dst_event, &jw.json); |
| 267 | jw_release(&jw); |
| 268 | } |
| 269 | |
| 270 | static void fn_command_ancestry_fl(const char *file, int line, const char **parent_names) |
| 271 | { |
| 272 | const char *event_name = "cmd_ancestry"; |
| 273 | const char *parent_name = NULL; |
| 274 | struct json_writer jw = JSON_WRITER_INIT; |
| 275 | |
| 276 | jw_object_begin(&jw, 0); |
| 277 | event_fmt_prepare(event_name, file, line, NULL, &jw); |
| 278 | jw_object_inline_begin_array(&jw, "ancestry"); |
| 279 | |
| 280 | while ((parent_name = *parent_names++)) |
| 281 | jw_array_string(&jw, parent_name); |
| 282 | |
| 283 | jw_end(&jw); /* 'ancestry' array */ |
| 284 | jw_end(&jw); /* event object */ |
| 285 | |
| 286 | tr2_dst_write_line(&tr2dst_event, &jw.json); |
| 287 | jw_release(&jw); |
| 288 | } |
| 289 | |
| 290 | static void fn_command_name_fl(const char *file, int line, const char *name, |
| 291 | const char *hierarchy) |
| 292 | { |
| 293 | const char *event_name = "cmd_name"; |
| 294 | struct json_writer jw = JSON_WRITER_INIT; |
| 295 | |
| 296 | jw_object_begin(&jw, 0); |
| 297 | event_fmt_prepare(event_name, file, line, NULL, &jw); |
| 298 | jw_object_string(&jw, "name", name); |
| 299 | if (hierarchy && *hierarchy) |
| 300 | jw_object_string(&jw, "hierarchy", hierarchy); |
| 301 | jw_end(&jw); |
| 302 | |
| 303 | tr2_dst_write_line(&tr2dst_event, &jw.json); |
| 304 | jw_release(&jw); |
| 305 | } |
| 306 | |
| 307 | static void fn_command_mode_fl(const char *file, int line, const char *mode) |
| 308 | { |
| 309 | const char *event_name = "cmd_mode"; |
| 310 | struct json_writer jw = JSON_WRITER_INIT; |
| 311 | |
| 312 | jw_object_begin(&jw, 0); |
| 313 | event_fmt_prepare(event_name, file, line, NULL, &jw); |
| 314 | jw_object_string(&jw, "name", mode); |
| 315 | jw_end(&jw); |
| 316 | |
| 317 | tr2_dst_write_line(&tr2dst_event, &jw.json); |
| 318 | jw_release(&jw); |
| 319 | } |
| 320 | |
| 321 | static void fn_alias_fl(const char *file, int line, const char *alias, |
| 322 | const char **argv) |
| 323 | { |
| 324 | const char *event_name = "alias"; |
| 325 | struct json_writer jw = JSON_WRITER_INIT; |
| 326 | |
| 327 | jw_object_begin(&jw, 0); |
| 328 | event_fmt_prepare(event_name, file, line, NULL, &jw); |
| 329 | jw_object_string(&jw, "alias", alias); |
| 330 | jw_object_inline_begin_array(&jw, "argv"); |
| 331 | jw_array_argv(&jw, argv); |
| 332 | jw_end(&jw); |
| 333 | jw_end(&jw); |
| 334 | |
| 335 | tr2_dst_write_line(&tr2dst_event, &jw.json); |
| 336 | jw_release(&jw); |
| 337 | } |
| 338 | |
| 339 | static void fn_child_start_fl(const char *file, int line, |
| 340 | uint64_t us_elapsed_absolute UNUSED, |
| 341 | const struct child_process *cmd) |
| 342 | { |
| 343 | const char *event_name = "child_start"; |
| 344 | struct json_writer jw = JSON_WRITER_INIT; |
| 345 | |
| 346 | jw_object_begin(&jw, 0); |
| 347 | event_fmt_prepare(event_name, file, line, NULL, &jw); |
| 348 | jw_object_intmax(&jw, "child_id", cmd->trace2_child_id); |
| 349 | if (cmd->trace2_hook_name) { |
| 350 | jw_object_string(&jw, "child_class", "hook"); |
| 351 | jw_object_string(&jw, "hook_name", cmd->trace2_hook_name); |
| 352 | } else { |
| 353 | const char *child_class = |
| 354 | cmd->trace2_child_class ? cmd->trace2_child_class : "?"; |
| 355 | jw_object_string(&jw, "child_class", child_class); |
| 356 | } |
| 357 | if (cmd->dir) |
| 358 | jw_object_string(&jw, "cd", cmd->dir); |
| 359 | jw_object_bool(&jw, "use_shell", cmd->use_shell); |
| 360 | jw_object_inline_begin_array(&jw, "argv"); |
| 361 | if (cmd->git_cmd) |
| 362 | jw_array_string(&jw, "git"); |
| 363 | jw_array_argv(&jw, cmd->args.v); |
| 364 | jw_end(&jw); |
| 365 | jw_end(&jw); |
| 366 | |
| 367 | tr2_dst_write_line(&tr2dst_event, &jw.json); |
| 368 | jw_release(&jw); |
| 369 | } |
| 370 | |
| 371 | static void fn_child_exit_fl(const char *file, int line, |
| 372 | uint64_t us_elapsed_absolute UNUSED, |
| 373 | int cid, int pid, |
| 374 | int code, uint64_t us_elapsed_child) |
| 375 | { |
| 376 | const char *event_name = "child_exit"; |
| 377 | struct json_writer jw = JSON_WRITER_INIT; |
| 378 | double t_rel = (double)us_elapsed_child / 1000000.0; |
| 379 | |
| 380 | jw_object_begin(&jw, 0); |
| 381 | event_fmt_prepare(event_name, file, line, NULL, &jw); |
| 382 | jw_object_intmax(&jw, "child_id", cid); |
| 383 | jw_object_intmax(&jw, "pid", pid); |
| 384 | jw_object_intmax(&jw, "code", code); |
| 385 | jw_object_double(&jw, "t_rel", 6, t_rel); |
| 386 | jw_end(&jw); |
| 387 | |
| 388 | tr2_dst_write_line(&tr2dst_event, &jw.json); |
| 389 | |
| 390 | jw_release(&jw); |
| 391 | } |
| 392 | |
| 393 | static void fn_child_ready_fl(const char *file, int line, |
| 394 | uint64_t us_elapsed_absolute UNUSED, |
| 395 | int cid, int pid, |
| 396 | const char *ready, uint64_t us_elapsed_child) |
| 397 | { |
| 398 | const char *event_name = "child_ready"; |
| 399 | struct json_writer jw = JSON_WRITER_INIT; |
| 400 | double t_rel = (double)us_elapsed_child / 1000000.0; |
| 401 | |
| 402 | jw_object_begin(&jw, 0); |
| 403 | event_fmt_prepare(event_name, file, line, NULL, &jw); |
| 404 | jw_object_intmax(&jw, "child_id", cid); |
| 405 | jw_object_intmax(&jw, "pid", pid); |
| 406 | jw_object_string(&jw, "ready", ready); |
| 407 | jw_object_double(&jw, "t_rel", 6, t_rel); |
| 408 | jw_end(&jw); |
| 409 | |
| 410 | tr2_dst_write_line(&tr2dst_event, &jw.json); |
| 411 | |
| 412 | jw_release(&jw); |
| 413 | } |
| 414 | |
| 415 | static void fn_thread_start_fl(const char *file, int line, |
| 416 | uint64_t us_elapsed_absolute UNUSED) |
| 417 | { |
| 418 | const char *event_name = "thread_start"; |
| 419 | struct json_writer jw = JSON_WRITER_INIT; |
| 420 | |
| 421 | jw_object_begin(&jw, 0); |
| 422 | event_fmt_prepare(event_name, file, line, NULL, &jw); |
| 423 | jw_end(&jw); |
| 424 | |
| 425 | tr2_dst_write_line(&tr2dst_event, &jw.json); |
| 426 | jw_release(&jw); |
| 427 | } |
| 428 | |
| 429 | static void fn_thread_exit_fl(const char *file, int line, |
| 430 | uint64_t us_elapsed_absolute UNUSED, |
| 431 | uint64_t us_elapsed_thread) |
| 432 | { |
| 433 | const char *event_name = "thread_exit"; |
| 434 | struct json_writer jw = JSON_WRITER_INIT; |
| 435 | double t_rel = (double)us_elapsed_thread / 1000000.0; |
| 436 | |
| 437 | jw_object_begin(&jw, 0); |
| 438 | event_fmt_prepare(event_name, file, line, NULL, &jw); |
| 439 | jw_object_double(&jw, "t_rel", 6, t_rel); |
| 440 | jw_end(&jw); |
| 441 | |
| 442 | tr2_dst_write_line(&tr2dst_event, &jw.json); |
| 443 | jw_release(&jw); |
| 444 | } |
| 445 | |
| 446 | static void fn_exec_fl(const char *file, int line, |
| 447 | uint64_t us_elapsed_absolute UNUSED, |
| 448 | int exec_id, const char *exe, const char **argv) |
| 449 | { |
| 450 | const char *event_name = "exec"; |
| 451 | struct json_writer jw = JSON_WRITER_INIT; |
| 452 | |
| 453 | jw_object_begin(&jw, 0); |
| 454 | event_fmt_prepare(event_name, file, line, NULL, &jw); |
| 455 | jw_object_intmax(&jw, "exec_id", exec_id); |
| 456 | if (exe) |
| 457 | jw_object_string(&jw, "exe", exe); |
| 458 | jw_object_inline_begin_array(&jw, "argv"); |
| 459 | jw_array_argv(&jw, argv); |
| 460 | jw_end(&jw); |
| 461 | jw_end(&jw); |
| 462 | |
| 463 | tr2_dst_write_line(&tr2dst_event, &jw.json); |
| 464 | jw_release(&jw); |
| 465 | } |
| 466 | |
| 467 | static void fn_exec_result_fl(const char *file, int line, |
| 468 | uint64_t us_elapsed_absolute UNUSED, |
| 469 | int exec_id, int code) |
| 470 | { |
| 471 | const char *event_name = "exec_result"; |
| 472 | struct json_writer jw = JSON_WRITER_INIT; |
| 473 | |
| 474 | jw_object_begin(&jw, 0); |
| 475 | event_fmt_prepare(event_name, file, line, NULL, &jw); |
| 476 | jw_object_intmax(&jw, "exec_id", exec_id); |
| 477 | jw_object_intmax(&jw, "code", code); |
| 478 | jw_end(&jw); |
| 479 | |
| 480 | tr2_dst_write_line(&tr2dst_event, &jw.json); |
| 481 | jw_release(&jw); |
| 482 | } |
| 483 | |
| 484 | static void fn_param_fl(const char *file, int line, const char *param, |
| 485 | const char *value, const struct key_value_info *kvi) |
| 486 | { |
| 487 | const char *event_name = "def_param"; |
| 488 | struct json_writer jw = JSON_WRITER_INIT; |
| 489 | enum config_scope scope = kvi->scope; |
| 490 | const char *scope_name = config_scope_name(scope); |
| 491 | |
| 492 | jw_object_begin(&jw, 0); |
| 493 | event_fmt_prepare(event_name, file, line, NULL, &jw); |
| 494 | jw_object_string(&jw, "scope", scope_name); |
| 495 | jw_object_string(&jw, "param", param); |
| 496 | if (value) |
| 497 | jw_object_string(&jw, "value", value); |
| 498 | jw_end(&jw); |
| 499 | |
| 500 | tr2_dst_write_line(&tr2dst_event, &jw.json); |
| 501 | jw_release(&jw); |
| 502 | } |
| 503 | |
| 504 | static void fn_repo_fl(const char *file, int line, |
| 505 | const struct repository *repo) |
| 506 | { |
| 507 | const char *event_name = "def_repo"; |
| 508 | struct json_writer jw = JSON_WRITER_INIT; |
| 509 | |
| 510 | jw_object_begin(&jw, 0); |
| 511 | event_fmt_prepare(event_name, file, line, repo, &jw); |
| 512 | jw_object_string(&jw, "worktree", repo->worktree); |
| 513 | jw_end(&jw); |
| 514 | |
| 515 | tr2_dst_write_line(&tr2dst_event, &jw.json); |
| 516 | jw_release(&jw); |
| 517 | } |
| 518 | |
| 519 | static void fn_region_enter_printf_va_fl(const char *file, int line, |
| 520 | uint64_t us_elapsed_absolute UNUSED, |
| 521 | const char *category, |
| 522 | const char *label, |
| 523 | const struct repository *repo, |
| 524 | const char *fmt, va_list ap) |
| 525 | { |
| 526 | const char *event_name = "region_enter"; |
| 527 | struct tr2tls_thread_ctx *ctx = tr2tls_get_self(); |
| 528 | if (ctx->nr_open_regions <= tr2env_event_max_nesting_levels) { |
| 529 | struct json_writer jw = JSON_WRITER_INIT; |
| 530 | |
| 531 | jw_object_begin(&jw, 0); |
| 532 | event_fmt_prepare(event_name, file, line, repo, &jw); |
| 533 | jw_object_intmax(&jw, "nesting", ctx->nr_open_regions); |
| 534 | if (category) |
| 535 | jw_object_string(&jw, "category", category); |
| 536 | if (label) |
| 537 | jw_object_string(&jw, "label", label); |
| 538 | maybe_add_string_va(&jw, "msg", fmt, ap); |
| 539 | jw_end(&jw); |
| 540 | |
| 541 | tr2_dst_write_line(&tr2dst_event, &jw.json); |
| 542 | jw_release(&jw); |
| 543 | } |
| 544 | } |
| 545 | |
| 546 | static void fn_region_leave_printf_va_fl( |
| 547 | const char *file, int line, uint64_t us_elapsed_absolute UNUSED, |
| 548 | uint64_t us_elapsed_region, const char *category, const char *label, |
| 549 | const struct repository *repo, const char *fmt, va_list ap) |
| 550 | { |
| 551 | const char *event_name = "region_leave"; |
| 552 | struct tr2tls_thread_ctx *ctx = tr2tls_get_self(); |
| 553 | if (ctx->nr_open_regions <= tr2env_event_max_nesting_levels) { |
| 554 | struct json_writer jw = JSON_WRITER_INIT; |
| 555 | double t_rel = (double)us_elapsed_region / 1000000.0; |
| 556 | |
| 557 | jw_object_begin(&jw, 0); |
| 558 | event_fmt_prepare(event_name, file, line, repo, &jw); |
| 559 | jw_object_double(&jw, "t_rel", 6, t_rel); |
| 560 | jw_object_intmax(&jw, "nesting", ctx->nr_open_regions); |
| 561 | if (category) |
| 562 | jw_object_string(&jw, "category", category); |
| 563 | if (label) |
| 564 | jw_object_string(&jw, "label", label); |
| 565 | maybe_add_string_va(&jw, "msg", fmt, ap); |
| 566 | jw_end(&jw); |
| 567 | |
| 568 | tr2_dst_write_line(&tr2dst_event, &jw.json); |
| 569 | jw_release(&jw); |
| 570 | } |
| 571 | } |
| 572 | |
| 573 | static void fn_data_fl(const char *file, int line, uint64_t us_elapsed_absolute, |
| 574 | uint64_t us_elapsed_region, const char *category, |
| 575 | const struct repository *repo, const char *key, |
| 576 | const char *value) |
| 577 | { |
| 578 | const char *event_name = "data"; |
| 579 | struct tr2tls_thread_ctx *ctx = tr2tls_get_self(); |
| 580 | if (ctx->nr_open_regions <= tr2env_event_max_nesting_levels) { |
| 581 | struct json_writer jw = JSON_WRITER_INIT; |
| 582 | double t_abs = (double)us_elapsed_absolute / 1000000.0; |
| 583 | double t_rel = (double)us_elapsed_region / 1000000.0; |
| 584 | |
| 585 | jw_object_begin(&jw, 0); |
| 586 | event_fmt_prepare(event_name, file, line, repo, &jw); |
| 587 | jw_object_double(&jw, "t_abs", 6, t_abs); |
| 588 | jw_object_double(&jw, "t_rel", 6, t_rel); |
| 589 | jw_object_intmax(&jw, "nesting", ctx->nr_open_regions); |
| 590 | jw_object_string(&jw, "category", category); |
| 591 | jw_object_string(&jw, "key", key); |
| 592 | jw_object_string(&jw, "value", value); |
| 593 | jw_end(&jw); |
| 594 | |
| 595 | tr2_dst_write_line(&tr2dst_event, &jw.json); |
| 596 | jw_release(&jw); |
| 597 | } |
| 598 | } |
| 599 | |
| 600 | static void fn_data_json_fl(const char *file, int line, |
| 601 | uint64_t us_elapsed_absolute, |
| 602 | uint64_t us_elapsed_region, const char *category, |
| 603 | const struct repository *repo, const char *key, |
| 604 | const struct json_writer *value) |
| 605 | { |
| 606 | const char *event_name = "data_json"; |
| 607 | struct tr2tls_thread_ctx *ctx = tr2tls_get_self(); |
| 608 | if (ctx->nr_open_regions <= tr2env_event_max_nesting_levels) { |
| 609 | struct json_writer jw = JSON_WRITER_INIT; |
| 610 | double t_abs = (double)us_elapsed_absolute / 1000000.0; |
| 611 | double t_rel = (double)us_elapsed_region / 1000000.0; |
| 612 | |
| 613 | jw_object_begin(&jw, 0); |
| 614 | event_fmt_prepare(event_name, file, line, repo, &jw); |
| 615 | jw_object_double(&jw, "t_abs", 6, t_abs); |
| 616 | jw_object_double(&jw, "t_rel", 6, t_rel); |
| 617 | jw_object_intmax(&jw, "nesting", ctx->nr_open_regions); |
| 618 | jw_object_string(&jw, "category", category); |
| 619 | jw_object_string(&jw, "key", key); |
| 620 | jw_object_sub_jw(&jw, "value", value); |
| 621 | jw_end(&jw); |
| 622 | |
| 623 | tr2_dst_write_line(&tr2dst_event, &jw.json); |
| 624 | jw_release(&jw); |
| 625 | } |
| 626 | } |
| 627 | |
| 628 | static void fn_printf_va_fl(const char *file, int line, |
| 629 | uint64_t us_elapsed_absolute, |
| 630 | const char *fmt, va_list ap) |
| 631 | { |
| 632 | const char *event_name = "printf"; |
| 633 | struct json_writer jw = JSON_WRITER_INIT; |
| 634 | double t_abs = (double)us_elapsed_absolute / 1000000.0; |
| 635 | |
| 636 | jw_object_begin(&jw, 0); |
| 637 | event_fmt_prepare(event_name, file, line, NULL, &jw); |
| 638 | jw_object_double(&jw, "t_abs", 6, t_abs); |
| 639 | maybe_add_string_va(&jw, "msg", fmt, ap); |
| 640 | jw_end(&jw); |
| 641 | |
| 642 | tr2_dst_write_line(&tr2dst_event, &jw.json); |
| 643 | jw_release(&jw); |
| 644 | } |
| 645 | |
| 646 | static void fn_timer(const struct tr2_timer_metadata *meta, |
| 647 | const struct tr2_timer *timer, |
| 648 | int is_final_data) |
| 649 | { |
| 650 | const char *event_name = is_final_data ? "timer" : "th_timer"; |
| 651 | struct json_writer jw = JSON_WRITER_INIT; |
| 652 | double t_total = NS_TO_SEC(timer->total_ns); |
| 653 | double t_min = NS_TO_SEC(timer->min_ns); |
| 654 | double t_max = NS_TO_SEC(timer->max_ns); |
| 655 | |
| 656 | jw_object_begin(&jw, 0); |
| 657 | event_fmt_prepare(event_name, __FILE__, __LINE__, NULL, &jw); |
| 658 | jw_object_string(&jw, "category", meta->category); |
| 659 | jw_object_string(&jw, "name", meta->name); |
| 660 | jw_object_intmax(&jw, "intervals", timer->interval_count); |
| 661 | jw_object_double(&jw, "t_total", 6, t_total); |
| 662 | jw_object_double(&jw, "t_min", 6, t_min); |
| 663 | jw_object_double(&jw, "t_max", 6, t_max); |
| 664 | jw_end(&jw); |
| 665 | |
| 666 | tr2_dst_write_line(&tr2dst_event, &jw.json); |
| 667 | jw_release(&jw); |
| 668 | } |
| 669 | |
| 670 | static void fn_counter(const struct tr2_counter_metadata *meta, |
| 671 | const struct tr2_counter *counter, |
| 672 | int is_final_data) |
| 673 | { |
| 674 | const char *event_name = is_final_data ? "counter" : "th_counter"; |
| 675 | struct json_writer jw = JSON_WRITER_INIT; |
| 676 | |
| 677 | jw_object_begin(&jw, 0); |
| 678 | event_fmt_prepare(event_name, __FILE__, __LINE__, NULL, &jw); |
| 679 | jw_object_string(&jw, "category", meta->category); |
| 680 | jw_object_string(&jw, "name", meta->name); |
| 681 | jw_object_intmax(&jw, "count", counter->value); |
| 682 | jw_end(&jw); |
| 683 | |
| 684 | tr2_dst_write_line(&tr2dst_event, &jw.json); |
| 685 | jw_release(&jw); |
| 686 | } |
| 687 | |
| 688 | struct tr2_tgt tr2_tgt_event = { |
| 689 | .pdst = &tr2dst_event, |
| 690 | |
| 691 | .pfn_init = fn_init, |
| 692 | .pfn_term = fn_term, |
| 693 | |
| 694 | .pfn_version_fl = fn_version_fl, |
| 695 | .pfn_start_fl = fn_start_fl, |
| 696 | .pfn_exit_fl = fn_exit_fl, |
| 697 | .pfn_signal = fn_signal, |
| 698 | .pfn_atexit = fn_atexit, |
| 699 | .pfn_error_va_fl = fn_error_va_fl, |
| 700 | .pfn_command_path_fl = fn_command_path_fl, |
| 701 | .pfn_command_ancestry_fl = fn_command_ancestry_fl, |
| 702 | .pfn_command_name_fl = fn_command_name_fl, |
| 703 | .pfn_command_mode_fl = fn_command_mode_fl, |
| 704 | .pfn_alias_fl = fn_alias_fl, |
| 705 | .pfn_child_start_fl = fn_child_start_fl, |
| 706 | .pfn_child_exit_fl = fn_child_exit_fl, |
| 707 | .pfn_child_ready_fl = fn_child_ready_fl, |
| 708 | .pfn_thread_start_fl = fn_thread_start_fl, |
| 709 | .pfn_thread_exit_fl = fn_thread_exit_fl, |
| 710 | .pfn_exec_fl = fn_exec_fl, |
| 711 | .pfn_exec_result_fl = fn_exec_result_fl, |
| 712 | .pfn_param_fl = fn_param_fl, |
| 713 | .pfn_repo_fl = fn_repo_fl, |
| 714 | .pfn_region_enter_printf_va_fl = fn_region_enter_printf_va_fl, |
| 715 | .pfn_region_leave_printf_va_fl = fn_region_leave_printf_va_fl, |
| 716 | .pfn_data_fl = fn_data_fl, |
| 717 | .pfn_data_json_fl = fn_data_json_fl, |
| 718 | .pfn_printf_va_fl = fn_printf_va_fl, |
| 719 | .pfn_timer = fn_timer, |
| 720 | .pfn_counter = fn_counter, |
| 721 | }; |