From: "Jeff Hostetler via GitGitGadget" <gitgitgadget@gmail.com>
To: git@vger.kernel.org
Cc: jeffhost@microsoft.com, Junio C Hamano <gitster@pobox.com>,
Jeff Hostetler <jeffhost@microsoft.com>
Subject: [PATCH 1/8] trace2: create new combined trace facility
Date: Fri, 31 Aug 2018 09:49:55 -0700 (PDT) [thread overview]
Message-ID: <82885700379efe6d6a83629cac4d943b99b393bf.1535734192.git.gitgitgadget@gmail.com> (raw)
In-Reply-To: <pull.29.git.gitgitgadget@gmail.com>
From: Jeff Hostetler <jeffhost@microsoft.com>
Create trace2 API allowing event-based tracing. This will hopefully
eventually replace the existing trace API.
Create GIT_TR2 trace-key to replace GIT_TRACE, GIT_TR2_PERFORMANCE to
replace GIT_TRACE_PERFORMANCE, and a new trace-key GIT_TR2_EVENT to
generate JSON data for telemetry purposes. Other structured formats
can easily be added later using this new existing model.
Define a higher-level event API that selectively writes to all of the
new GIT_TR2_* targets (depending on event type) without needing to call
different trace_printf*() or trace_performance_*() routines.
The API defines both fixed-field and printf-style functions.
The trace2 performance tracing includes thread-specific function
nesting and timings.
Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>
---
Makefile | 1 +
cache.h | 1 +
trace2.c | 1592 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
trace2.h | 214 ++++++++
4 files changed, 1808 insertions(+)
create mode 100644 trace2.c
create mode 100644 trace2.h
diff --git a/Makefile b/Makefile
index 5a969f5830..88ae7afdd4 100644
--- a/Makefile
+++ b/Makefile
@@ -974,6 +974,7 @@ LIB_OBJS += tag.o
LIB_OBJS += tempfile.o
LIB_OBJS += tmp-objdir.o
LIB_OBJS += trace.o
+LIB_OBJS += trace2.o
LIB_OBJS += trailer.o
LIB_OBJS += transport.o
LIB_OBJS += transport-helper.o
diff --git a/cache.h b/cache.h
index 4d014541ab..38f0cd2ba0 100644
--- a/cache.h
+++ b/cache.h
@@ -9,6 +9,7 @@
#include "gettext.h"
#include "convert.h"
#include "trace.h"
+#include "trace2.h"
#include "string-list.h"
#include "pack-revindex.h"
#include "hash.h"
diff --git a/trace2.c b/trace2.c
new file mode 100644
index 0000000000..13d5c85366
--- /dev/null
+++ b/trace2.c
@@ -0,0 +1,1592 @@
+#include "cache.h"
+#include "config.h"
+#include "json-writer.h"
+#include "quote.h"
+#include "run-command.h"
+#include "sigchain.h"
+#include "thread-utils.h"
+#include "version.h"
+
+/*****************************************************************
+ * TODO remove this section header
+ *****************************************************************/
+
+static struct strbuf tr2sid_buf = STRBUF_INIT;
+static int tr2sid_nr_git_parents;
+
+/*
+ * Compute a "unique" session id (SID) for the current process. All events
+ * from this process will have this label. If we were started by another
+ * git instance, use our parent's SID as a prefix and count the number of
+ * nested git processes. (This lets us track parent/child relationships
+ * even if there is an intermediate shell process.)
+ */
+static void tr2sid_compute(void)
+{
+ uint64_t us_now;
+ const char *parent_sid;
+
+ if (tr2sid_buf.len)
+ return;
+
+ parent_sid = getenv("SLOG_PARENT_SID");
+ if (parent_sid && *parent_sid) {
+ const char *p;
+ for (p = parent_sid; *p; p++)
+ if (*p == '/')
+ tr2sid_nr_git_parents++;
+
+ strbuf_addstr(&tr2sid_buf, parent_sid);
+ strbuf_addch(&tr2sid_buf, '/');
+ tr2sid_nr_git_parents++;
+ }
+
+ us_now = getnanotime() / 1000;
+ strbuf_addf(&tr2sid_buf, "%"PRIuMAX"-%"PRIdMAX,
+ (uintmax_t)us_now, (intmax_t)getpid());
+
+ setenv("SLOG_PARENT_SID", tr2sid_buf.buf, 1);
+}
+
+/*****************************************************************
+ * TODO remove this section header
+ *****************************************************************/
+
+/*
+ * Each thread (including the main thread) has a stack of nested regions.
+ * This is used to indent messages and provide nested perf times. The
+ * limit here is for simplicity. Note that the region stack is per-thread
+ * and not per-trace_key.
+ */
+#define TR2_MAX_REGION_NESTING (100)
+#define TR2_INDENT (2)
+#define TR2_INDENT_LENGTH(ctx) (((ctx)->nr_open_regions - 1) * TR2_INDENT)
+
+#define TR2_MAX_THREAD_NAME (24)
+
+struct tr2tls_thread_ctx {
+ struct strbuf thread_name;
+ uint64_t us_start[TR2_MAX_REGION_NESTING];
+ int nr_open_regions;
+ int thread_id;
+};
+
+static struct tr2tls_thread_ctx *tr2tls_thread_main;
+
+static pthread_mutex_t tr2tls_mutex;
+static pthread_key_t tr2tls_key;
+static int tr2tls_initialized;
+
+static struct strbuf tr2_dots = STRBUF_INIT;
+
+static int tr2_next_child_id;
+static int tr2_next_thread_id;
+
+/*
+ * Create TLS data for the current thread. This gives us a place to
+ * put per-thread data, such as thread start time, function nesting
+ * and a per-thread label for our messages.
+ *
+ * We assume the first thread is "main". Other threads are given
+ * non-zero thread-ids to help distinguish messages from concurrent
+ * threads.
+ *
+ * Truncate the thread name if necessary to help with column alignment
+ * in printf-style messages.
+ */
+static struct tr2tls_thread_ctx *tr2tls_create_self(const char *thread_name)
+{
+ struct tr2tls_thread_ctx *ctx = xcalloc(1, sizeof(*ctx));
+
+ /*
+ * Implicitly "tr2tls_push_self()" to capture the thread's start
+ * time in us_start[0]. For the main thread this gives us the
+ * application run time.
+ */
+ ctx->us_start[ctx->nr_open_regions++] = getnanotime() / 1000;
+
+ pthread_mutex_lock(&tr2tls_mutex);
+ ctx->thread_id = tr2_next_thread_id++;
+ pthread_mutex_unlock(&tr2tls_mutex);
+
+ strbuf_init(&ctx->thread_name, 0);
+ if (ctx->thread_id)
+ strbuf_addf(&ctx->thread_name, "th%02d:", ctx->thread_id);
+ strbuf_addstr(&ctx->thread_name, thread_name);
+ if (ctx->thread_name.len > TR2_MAX_THREAD_NAME)
+ strbuf_setlen(&ctx->thread_name, TR2_MAX_THREAD_NAME);
+
+ pthread_setspecific(tr2tls_key, ctx);
+
+ return ctx;
+}
+
+static struct tr2tls_thread_ctx *tr2tls_get_self(void)
+{
+ struct tr2tls_thread_ctx * ctx = pthread_getspecific(tr2tls_key);
+
+ /*
+ * If the thread-proc did not call trace2_thread_start(), we won't
+ * have any TLS data associated with the current thread. Fix it
+ * here and silently continue.
+ */
+ if (!ctx)
+ ctx = tr2tls_create_self("unknown");
+
+ return ctx;
+}
+
+static void tr2tls_unset_self(void)
+{
+ struct tr2tls_thread_ctx * ctx;
+
+ ctx = tr2tls_get_self();
+
+ pthread_setspecific(tr2tls_key, NULL);
+
+ free(ctx);
+}
+
+/*
+ * Begin a new nested region and remember the start time.
+ */
+static void tr2tls_push_self(void)
+{
+ struct tr2tls_thread_ctx *ctx = tr2tls_get_self();
+
+ if (ctx->nr_open_regions == TR2_MAX_REGION_NESTING)
+ BUG("exceeded max region nesting '%d' in thread '%s'",
+ ctx->nr_open_regions, ctx->thread_name.buf);
+
+ ctx->us_start[ctx->nr_open_regions++] = getnanotime() / 1000;
+}
+
+/*
+ * End the innermost nested region.
+ */
+static void tr2tls_pop_self(void)
+{
+ struct tr2tls_thread_ctx *ctx = tr2tls_get_self();
+
+ if (!ctx->nr_open_regions)
+ BUG("no open regions in thread '%s'", ctx->thread_name.buf);
+
+ ctx->nr_open_regions--;
+}
+
+/*
+ * Compute elapsed time in innermost nested region.
+ */
+static uint64_t tr2tls_region_elapsed_self(void)
+{
+ struct tr2tls_thread_ctx *ctx;
+ uint64_t us_start;
+ uint64_t us_now;
+
+ ctx = tr2tls_get_self();
+ if (!ctx->nr_open_regions)
+ return 0;
+
+ us_now = getnanotime() / 1000;
+ us_start = ctx->us_start[ctx->nr_open_regions - 1];
+
+ return us_now - us_start;
+}
+
+/*
+ * Compute the elapsed run time since the main thread started.
+ */
+static uint64_t tr2tls_elapsed_main(void)
+{
+ uint64_t us_start;
+ uint64_t us_now;
+ uint64_t us_elapsed;
+
+ if (!tr2tls_thread_main)
+ return 0;
+
+ us_now = getnanotime() / 1000;
+ us_start = tr2tls_thread_main->us_start[0];
+ us_elapsed = us_now - us_start;
+
+ return us_elapsed;
+}
+
+static void tr2tls_init(void)
+{
+ pthread_key_create(&tr2tls_key, NULL);
+ init_recursive_mutex(&tr2tls_mutex);
+
+ tr2tls_thread_main = tr2tls_create_self("main");
+ tr2tls_initialized = 1;
+}
+
+static void tr2tls_release(void)
+{
+ tr2tls_unset_self();
+ tr2tls_thread_main = NULL;
+
+ tr2tls_initialized = 0;
+
+ pthread_mutex_destroy(&tr2tls_mutex);
+ pthread_key_delete(tr2tls_key);
+}
+
+/*****************************************************************
+ * TODO remove this section header
+ *****************************************************************/
+
+static struct trace_key tr2key_default = { "GIT_TR2", 0, 0, 0 };
+static struct trace_key tr2key_perf = { "GIT_TR2_PERFORMANCE", 0, 0, 0 };
+static struct trace_key tr2key_event = { "GIT_TR2_EVENT", 0, 0, 0 };
+
+/*
+ * Region depth limit for messages written to the event target.
+ *
+ * The "region_enter" and "region_leave" messages (especially recursive
+ * messages such as those produced while diving the worktree or index)
+ * are primarily intended for the performance target during debugging.
+ *
+ * Some of the outer-most messages, however, may be of interest to the
+ * event target. Set this environment variable to a larger integer for
+ * more detail in the event target.
+ */
+#define GIT_TR2_EVENT_DEPTH "GIT_TR2_EVENT_DEPTH"
+static int tr2env_event_depth_wanted = 2;
+
+/*
+ * Set this environment variable to true to omit the "<time> <file>:<line>"
+ * fields from each line written to the default and performance targets.
+ *
+ * Unit tests may want to use this to help with testing.
+ */
+#define GIT_TR2_BARE "GIT_TR2_BARE"
+static int tr2env_bare_wanted;
+
+static void tr2key_trace_disable(struct trace_key *key)
+{
+ if (key->need_close)
+ close(key->fd);
+ key->fd = 0;
+ key->initialized = 1;
+ key->need_close = 0;
+}
+
+static int tr2key_get_trace_fd(struct trace_key *key)
+{
+ const char *trace;
+
+ /* don't open twice */
+ if (key->initialized)
+ return key->fd;
+
+ trace = getenv(key->key);
+
+ if (!trace || !strcmp(trace, "") ||
+ !strcmp(trace, "0") || !strcasecmp(trace, "false"))
+ key->fd = 0;
+ else if (!strcmp(trace, "1") || !strcasecmp(trace, "true"))
+ key->fd = STDERR_FILENO;
+ else if (strlen(trace) == 1 && isdigit(*trace))
+ key->fd = atoi(trace);
+ else if (is_absolute_path(trace)) {
+ int fd = open(trace, O_WRONLY | O_APPEND | O_CREAT, 0666);
+ if (fd == -1) {
+ warning("could not open '%s' for tracing: %s",
+ trace, strerror(errno));
+ tr2key_trace_disable(key);
+ } else {
+ key->fd = fd;
+ key->need_close = 1;
+ }
+ } else {
+ warning("unknown trace value for '%s': %s\n"
+ " If you want to trace into a file, then please set %s\n"
+ " to an absolute pathname (starting with /)",
+ key->key, trace, key->key);
+ tr2key_trace_disable(key);
+ }
+
+ key->initialized = 1;
+ return key->fd;
+}
+
+static int tr2key_trace_want(struct trace_key *key)
+{
+ return !!tr2key_get_trace_fd(key);
+}
+
+/*
+ * Force (rather than lazily) initialize any of the requested
+ * primary/builtin trace targets at startup (and before we've
+ * seen an actual trace event call) so we can see if we need
+ * to setup the TR2 and TLS machinery.
+ *
+ * Other ad hoc trace targets can just use compatibility mode
+ * and and printf-based messages.
+ */
+static int tr2key_want_builtins(void)
+{
+ int sum = 0;
+ int v;
+ char *p;
+
+ sum += tr2key_trace_want(&tr2key_default);
+ sum += tr2key_trace_want(&tr2key_perf);
+ sum += tr2key_trace_want(&tr2key_event);
+
+ p = getenv(GIT_TR2_EVENT_DEPTH);
+ if (p && ((v = atoi(p)) > 0))
+ tr2env_event_depth_wanted = v;
+
+ p = getenv(GIT_TR2_BARE);
+ if (p && *p && ((v = git_parse_maybe_bool(p)) != -1))
+ tr2env_bare_wanted = v;
+
+ return sum;
+}
+
+static void tr2key_disable_builtins(void)
+{
+ tr2key_trace_disable(&tr2key_default);
+ tr2key_trace_disable(&tr2key_perf);
+ tr2key_trace_disable(&tr2key_event);
+}
+
+/*****************************************************************
+ * TODO remove this section header
+ *****************************************************************/
+
+/*
+ * A simple wrapper around a fixed buffer to avoid C syntax
+ * quirks and the need to pass around an additional size_t
+ * argument.
+ */
+struct tr2time_buf {
+ char buf[24];
+};
+
+static void tr2time_current_time(struct tr2time_buf *tb)
+{
+ struct timeval tv;
+ struct tm tm;
+ time_t secs;
+
+ gettimeofday(&tv, NULL);
+ secs = tv.tv_sec;
+ localtime_r(&secs, &tm);
+
+ xsnprintf(tb->buf, sizeof(tb->buf), "%02d:%02d:%02d.%06ld ",
+ tm.tm_hour, tm.tm_min, tm.tm_sec, (long)tv.tv_usec);
+}
+
+/*****************************************************************
+ * TODO remove this section header
+ *****************************************************************/
+
+#define TR2FMT_MAX_EVENT_NAME (12)
+
+/*
+ * Format trace line prefix in human-readable classic format for
+ * the default target:
+ * "<time> <file>:<line> <bar> <thread_name> <bar> <event_name> <bar> "
+ */
+static void tr2fmt_prepare_default(
+ const char *event_name,
+ struct tr2tls_thread_ctx *ctx,
+ const char *file, int line,
+ struct strbuf *buf)
+{
+ strbuf_setlen(buf,0);
+
+ if (!tr2env_bare_wanted) {
+ struct tr2time_buf tb_now;
+
+ tr2time_current_time(&tb_now);
+ strbuf_addstr(buf, tb_now.buf);
+
+ if (file && *file)
+ strbuf_addf(buf, "%s:%d ", file, line);
+ while (buf->len < 40)
+ strbuf_addch(buf, ' ');
+
+ strbuf_addstr(buf, "| ");
+ }
+
+ strbuf_addf(buf, "%-*s | %-*s | ",
+ TR2_MAX_THREAD_NAME, ctx->thread_name.buf,
+ TR2FMT_MAX_EVENT_NAME, event_name);
+}
+
+/*
+ * Format trace line prefix in human-readable classic format for
+ * the performance target:
+ * "[<time>] [<file>:<line>] [<thread_name>] [<event_name>] <bar> <indent>"
+ */
+static void tr2fmt_prepare_perf(
+ const char *event_name,
+ struct tr2tls_thread_ctx *ctx,
+ const char *file, int line,
+ uint64_t *p_us_elapsed,
+ struct strbuf *buf)
+{
+ strbuf_setlen(buf,0);
+
+ if (!tr2env_bare_wanted) {
+
+ struct tr2time_buf tb_now;
+
+ tr2time_current_time(&tb_now);
+ strbuf_addstr(buf, tb_now.buf);
+
+ if (file && *file)
+ strbuf_addf(buf, "%s:%d ", file, line);
+ while (buf->len < 40)
+ strbuf_addch(buf, ' ');
+
+ strbuf_addstr(buf, "| ");
+ }
+
+ strbuf_addf(buf, "%-*s | %-*s | ",
+ TR2_MAX_THREAD_NAME, ctx->thread_name.buf,
+ TR2FMT_MAX_EVENT_NAME, event_name);
+
+ if (p_us_elapsed)
+ strbuf_addf(buf, "%9.6f | ",
+ ((double)(*p_us_elapsed)) / 1000000.0);
+ else
+ strbuf_addf(buf, "%9s | ", " ");
+
+ if (ctx->nr_open_regions > 0)
+ strbuf_addf(buf, "%.*s", TR2_INDENT_LENGTH(ctx), tr2_dots.buf);
+}
+
+/*
+ * Append common key-value pairs to the currently open JSON object.
+ * "event:"<event_name>"
+ * "sid":"<sid>"
+ * "thread":"<thread_name>"
+ * "file":"<filename>"
+ * "line":<line_number>
+ */
+static void tr2fmt_prepare_json_trace_line(
+ const char *event_name,
+ const char *file, int line,
+ struct json_writer *jw)
+{
+ struct tr2tls_thread_ctx *ctx = tr2tls_get_self();
+
+ jw_object_string(jw, "event", event_name);
+ jw_object_string(jw, "sid", tr2sid_buf.buf);
+ jw_object_string(jw, "thread", ctx->thread_name.buf);
+
+ if (file && *file) {
+ jw_object_string(jw, "file", file);
+ jw_object_intmax(jw, "line", line);
+ }
+}
+
+/*****************************************************************
+ * TODO remove this section header
+ *****************************************************************/
+
+static void tr2io_write_line(struct trace_key *key, struct strbuf *buf_line)
+{
+ strbuf_complete_line(buf_line); /* ensure final NL on buffer */
+
+ // TODO we don't really want short-writes to try again when
+ // TODO we are in append mode...
+
+ if (write_in_full(tr2key_get_trace_fd(key),
+ buf_line->buf, buf_line->len) < 0) {
+ warning("unable to write trace for '%s': %s",
+ key->key, strerror(errno));
+ tr2key_trace_disable(key);
+ }
+}
+
+static void tr2io_write_default_fl(const char *file, int line,
+ const char *event_name,
+ const struct strbuf *buf_payload)
+{
+ struct tr2tls_thread_ctx *ctx = tr2tls_get_self();
+ struct strbuf buf_line = STRBUF_INIT;
+
+ tr2fmt_prepare_default(event_name, ctx, file, line, &buf_line);
+
+ strbuf_addbuf(&buf_line, buf_payload);
+
+ tr2io_write_line(&tr2key_default, &buf_line);
+
+ strbuf_release(&buf_line);
+}
+
+static void tr2io_write_perf_fl(const char *file, int line,
+ const char *event_name,
+ uint64_t *p_us_elapsed,
+ const struct strbuf *buf_payload)
+{
+ struct tr2tls_thread_ctx *ctx = tr2tls_get_self();
+ struct strbuf buf_line = STRBUF_INIT;
+
+ tr2fmt_prepare_perf(event_name, ctx, file, line,
+ p_us_elapsed, &buf_line);
+
+ strbuf_addbuf(&buf_line, buf_payload);
+
+ tr2io_write_line(&tr2key_perf, &buf_line);
+
+ strbuf_release(&buf_line);
+}
+
+/*****************************************************************
+ * TODO remove this section header
+ *****************************************************************/
+
+/*
+ * Write 'version' event message to the builtin targets.
+ */
+static void tr2evt_version_fl(const char *file, int line)
+{
+ const char *event_name = "version";
+
+ if (tr2key_trace_want(&tr2key_default)) {
+ struct strbuf buf_payload = STRBUF_INIT;
+
+ strbuf_addf(&buf_payload, "%s", git_version_string);
+
+ tr2io_write_default_fl(file, line, event_name, &buf_payload);
+ strbuf_release(&buf_payload);
+ }
+
+ if (tr2key_trace_want(&tr2key_perf)) {
+ struct strbuf buf_payload = STRBUF_INIT;
+
+ strbuf_addf(&buf_payload, "%s", git_version_string);
+
+ tr2io_write_perf_fl(file, line,
+ event_name, NULL, &buf_payload);
+ strbuf_release(&buf_payload);
+ }
+
+ if (tr2key_trace_want(&tr2key_event)) {
+ struct json_writer jw = JSON_WRITER_INIT;
+
+ jw_object_begin(&jw, 0);
+ tr2fmt_prepare_json_trace_line(
+ event_name, file, line, &jw);
+ jw_object_string(&jw, "git", git_version_string);
+ jw_end(&jw);
+
+ tr2io_write_line(&tr2key_event, &jw.json);
+ jw_release(&jw);
+ }
+}
+
+/*
+ * Write 'start' event message to the builtin targets.
+ */
+static void tr2evt_start_fl(const char *file, int line,
+ const char **argv)
+{
+ const char *event_name = "start";
+
+ if (tr2key_trace_want(&tr2key_default)) {
+ struct strbuf buf_payload = STRBUF_INIT;
+
+ sq_quote_argv_pretty(&buf_payload, argv);
+
+ tr2io_write_default_fl(file, line, event_name, &buf_payload);
+ strbuf_release(&buf_payload);
+ }
+
+ if (tr2key_trace_want(&tr2key_perf)) {
+ struct strbuf buf_payload = STRBUF_INIT;
+
+ sq_quote_argv_pretty(&buf_payload, argv);
+
+ tr2io_write_perf_fl(file, line,
+ event_name, NULL, &buf_payload);
+ strbuf_release(&buf_payload);
+ }
+
+ if (tr2key_trace_want(&tr2key_event)) {
+ struct json_writer jw = JSON_WRITER_INIT;
+
+ jw_object_begin(&jw, 0);
+ tr2fmt_prepare_json_trace_line(
+ event_name, file, line, &jw);
+ jw_object_inline_begin_array(&jw, "argv");
+ jw_array_argv(&jw, argv);
+ jw_end(&jw);
+ jw_end(&jw);
+
+ tr2io_write_line(&tr2key_event, &jw.json);
+ jw_release(&jw);
+ }
+}
+
+/*
+ * Write 'exit' event message to the builtin targets.
+ */
+static void tr2evt_exit_fl(const char *file, int line,
+ int code, uint64_t us_elapsed)
+{
+ const char *event_name = "exit";
+
+ if (tr2key_trace_want(&tr2key_default)) {
+ struct strbuf buf_payload = STRBUF_INIT;
+ double elapsed = (double)us_elapsed / 1000000.0;
+
+ strbuf_addf(&buf_payload, "elapsed:%.6f code:%d",
+ elapsed, code);
+
+ tr2io_write_default_fl(file, line, event_name, &buf_payload);
+ strbuf_release(&buf_payload);
+ }
+
+ if (tr2key_trace_want(&tr2key_perf)) {
+ struct strbuf buf_payload = STRBUF_INIT;
+
+ strbuf_addf(&buf_payload, "code:%d", code);
+
+ tr2io_write_perf_fl(file, line, event_name,
+ &us_elapsed, &buf_payload);
+ strbuf_release(&buf_payload);
+ }
+
+ if (tr2key_trace_want(&tr2key_event)) {
+ struct json_writer jw = JSON_WRITER_INIT;
+ double elapsed = (double)us_elapsed / 1000000.0;
+
+ jw_object_begin(&jw, 0);
+ tr2fmt_prepare_json_trace_line(
+ event_name, file, line, &jw);
+ jw_object_double(&jw, "elapsed", 6, elapsed);
+ jw_object_intmax(&jw, "code", code);
+ jw_end(&jw);
+
+ tr2io_write_line(&tr2key_event, &jw.json);
+ jw_release(&jw);
+ }
+}
+
+/*
+ * Write 'signal' event message to the builtin targets.
+ */
+static void tr2evt_signal(int signo, uint64_t us_elapsed)
+{
+ const char *event_name = "signal";
+
+ if (tr2key_trace_want(&tr2key_default)) {
+ struct strbuf buf_payload = STRBUF_INIT;
+ double elapsed = (double)us_elapsed / 1000000.0;
+
+ strbuf_addf(&buf_payload, "elapsed:%.6f signo:%d",
+ elapsed, signo);
+
+ tr2io_write_default_fl(__FILE__, __LINE__,
+ event_name, &buf_payload);
+ strbuf_release(&buf_payload);
+ }
+
+ if (tr2key_trace_want(&tr2key_perf)) {
+ struct strbuf buf_payload = STRBUF_INIT;
+
+ strbuf_addf(&buf_payload, "signo:%d", signo);
+
+ tr2io_write_perf_fl(__FILE__, __LINE__,
+ event_name, &us_elapsed, &buf_payload);
+ strbuf_release(&buf_payload);
+ }
+
+ if (tr2key_trace_want(&tr2key_event)) {
+ struct json_writer jw = JSON_WRITER_INIT;
+ double elapsed = (double)us_elapsed / 1000000.0;
+
+ jw_object_begin(&jw, 0);
+ tr2fmt_prepare_json_trace_line(
+ event_name, __FILE__, __LINE__, &jw);
+ jw_object_double(&jw, "elapsed", 6, elapsed);
+ jw_object_intmax(&jw, "signo", signo);
+ jw_end(&jw);
+
+ tr2io_write_line(&tr2key_event, &jw.json);
+ jw_release(&jw);
+ }
+}
+
+/*
+ * Write 'atexit' event message to the builtin targets.
+ */
+static void tr2evt_atexit(int code, uint64_t us_elapsed)
+{
+ const char *event_name = "atexit";
+
+ if (tr2key_trace_want(&tr2key_default)) {
+ struct strbuf buf_payload = STRBUF_INIT;
+ double elapsed = (double)us_elapsed / 1000000.0;
+
+ strbuf_addf(&buf_payload, "elapsed:%.6f code:%d",
+ elapsed, code);
+
+ tr2io_write_default_fl(__FILE__, __LINE__,
+ event_name, &buf_payload);
+ strbuf_release(&buf_payload);
+ }
+
+ if (tr2key_trace_want(&tr2key_perf)) {
+ struct strbuf buf_payload = STRBUF_INIT;
+
+ strbuf_addf(&buf_payload, "code:%d", code);
+
+ tr2io_write_perf_fl(__FILE__, __LINE__,
+ event_name, &us_elapsed, &buf_payload);
+ strbuf_release(&buf_payload);
+ }
+
+ if (tr2key_trace_want(&tr2key_event)) {
+ struct json_writer jw = JSON_WRITER_INIT;
+ double elapsed = (double)us_elapsed / 1000000.0;
+
+ jw_object_begin(&jw, 0);
+ tr2fmt_prepare_json_trace_line(
+ event_name, __FILE__, __LINE__, &jw);
+ jw_object_double(&jw, "elapsed", 6, elapsed);
+ jw_object_intmax(&jw, "code", code);
+ jw_end(&jw);
+
+ tr2io_write_line(&tr2key_event, &jw.json);
+ jw_release(&jw);
+ }
+}
+
+static void tr2evt_error_va_fl(const char *file, int line,
+ const char *fmt, va_list ap)
+{
+ const char *event_name = "error";
+
+ if (tr2key_trace_want(&tr2key_default)) {
+ struct strbuf buf_payload = STRBUF_INIT;
+
+ strbuf_vaddf(&buf_payload, fmt, ap);
+
+ tr2io_write_default_fl(file, line, event_name, &buf_payload);
+ strbuf_release(&buf_payload);
+ }
+
+ if (tr2key_trace_want(&tr2key_perf)) {
+ struct strbuf buf_payload = STRBUF_INIT;
+
+ strbuf_vaddf(&buf_payload, fmt, ap);
+
+ tr2io_write_perf_fl(file, line,
+ event_name, NULL, &buf_payload);
+ strbuf_release(&buf_payload);
+ }
+
+ if (tr2key_trace_want(&tr2key_event)) {
+ struct json_writer jw = JSON_WRITER_INIT;
+ struct strbuf buf_msg = STRBUF_INIT;
+
+ strbuf_vaddf(&buf_msg, fmt, ap);
+
+ jw_object_begin(&jw, 0);
+ tr2fmt_prepare_json_trace_line(
+ event_name, file, line, &jw);
+ jw_object_string(&jw, "msg", buf_msg.buf);
+ jw_end(&jw);
+
+ tr2io_write_line(&tr2key_event, &jw.json);
+ jw_release(&jw);
+ strbuf_release(&buf_msg);
+ }
+}
+
+static void tr2evt_command_fl(const char *file, int line,
+ const char *command_name)
+{
+ const char *event_name = "command";
+
+ if (tr2key_trace_want(&tr2key_default)) {
+ struct strbuf buf_payload = STRBUF_INIT;
+
+ strbuf_addstr(&buf_payload, command_name);
+
+ tr2io_write_default_fl(file, line, event_name, &buf_payload);
+ strbuf_release(&buf_payload);
+ }
+
+ if (tr2key_trace_want(&tr2key_perf)) {
+ struct strbuf buf_payload = STRBUF_INIT;
+
+ strbuf_addstr(&buf_payload, command_name);
+
+ tr2io_write_perf_fl(file, line,
+ event_name, NULL, &buf_payload);
+ strbuf_release(&buf_payload);
+ }
+
+ if (tr2key_trace_want(&tr2key_event)) {
+ struct json_writer jw = JSON_WRITER_INIT;
+
+ jw_object_begin(&jw, 0);
+ tr2fmt_prepare_json_trace_line(
+ event_name, file, line, &jw);
+ jw_object_string(&jw, "name", command_name);
+ jw_end(&jw);
+
+ tr2io_write_line(&tr2key_event, &jw.json);
+ jw_release(&jw);
+ }
+}
+
+static void tr2evt_worktree_fl(const char *file, int line,
+ const char *path)
+{
+ const char *event_name = "worktree";
+
+ if (tr2key_trace_want(&tr2key_default)) {
+ struct strbuf buf_payload = STRBUF_INIT;
+
+ strbuf_addstr(&buf_payload, path);
+
+ tr2io_write_default_fl(file, line, event_name, &buf_payload);
+ strbuf_release(&buf_payload);
+ }
+
+ if (tr2key_trace_want(&tr2key_perf)) {
+ struct strbuf buf_payload = STRBUF_INIT;
+
+ strbuf_addstr(&buf_payload, path);
+
+ tr2io_write_perf_fl(file, line,
+ event_name, NULL, &buf_payload);
+ strbuf_release(&buf_payload);
+ }
+
+ if (tr2key_trace_want(&tr2key_event)) {
+ struct json_writer jw = JSON_WRITER_INIT;
+
+ jw_object_begin(&jw, 0);
+ tr2fmt_prepare_json_trace_line(
+ event_name, file, line, &jw);
+ jw_object_string(&jw, "path", path);
+ jw_end(&jw);
+
+ tr2io_write_line(&tr2key_event, &jw.json);
+ jw_release(&jw);
+ }
+}
+
+static void tr2evt_alias_fl(const char *file, int line,
+ const char *alias, const char **argv)
+{
+ const char *event_name = "alias";
+
+ if (tr2key_trace_want(&tr2key_default)) {
+ struct strbuf buf_payload = STRBUF_INIT;
+
+ strbuf_addf(&buf_payload, "alias:%s argv:", alias);
+ sq_quote_argv_pretty(&buf_payload, argv);
+
+ tr2io_write_default_fl(file, line, event_name, &buf_payload);
+ strbuf_release(&buf_payload);
+ }
+
+ if (tr2key_trace_want(&tr2key_perf)) {
+ struct strbuf buf_payload = STRBUF_INIT;
+
+ strbuf_addf(&buf_payload, "alias:%s argv:", alias);
+ sq_quote_argv_pretty(&buf_payload, argv);
+
+ tr2io_write_perf_fl(file, line,
+ event_name, NULL, &buf_payload);
+ strbuf_release(&buf_payload);
+ }
+
+ if (tr2key_trace_want(&tr2key_event)) {
+ struct json_writer jw = JSON_WRITER_INIT;
+
+ jw_object_begin(&jw, 0);
+ tr2fmt_prepare_json_trace_line(
+ event_name, file, line, &jw);
+ jw_object_string(&jw, "alias", alias);
+ jw_object_inline_begin_array(&jw, "argv");
+ jw_array_argv(&jw, argv);
+ jw_end(&jw);
+ jw_end(&jw);
+
+ tr2io_write_line(&tr2key_event, &jw.json);
+ jw_release(&jw);
+ }
+}
+
+static void tr2evt_param_fl(const char *file, int line,
+ const char *param, const char *value)
+{
+ const char *event_name = "param";
+
+ if (tr2key_trace_want(&tr2key_default)) {
+ struct strbuf buf_payload = STRBUF_INIT;
+
+ strbuf_addf(&buf_payload, "%s:%s", param, value);
+
+ tr2io_write_default_fl(file, line, event_name, &buf_payload);
+ strbuf_release(&buf_payload);
+ }
+
+ if (tr2key_trace_want(&tr2key_perf)) {
+ struct strbuf buf_payload = STRBUF_INIT;
+
+ strbuf_addf(&buf_payload, "%s:%s", param, value);
+
+ tr2io_write_perf_fl(file, line,
+ event_name, NULL, &buf_payload);
+ strbuf_release(&buf_payload);
+ }
+
+ if (tr2key_trace_want(&tr2key_event)) {
+ struct json_writer jw = JSON_WRITER_INIT;
+
+ jw_object_begin(&jw, 0);
+ tr2fmt_prepare_json_trace_line(
+ event_name, file, line, &jw);
+ jw_object_string(&jw, "param", param);
+ jw_object_string(&jw, "value", value);
+ jw_end(&jw);
+
+ tr2io_write_line(&tr2key_event, &jw.json);
+ jw_release(&jw);
+ }
+}
+
+/*
+ * Write a common 'child_start' message to the builtin targets.
+ */
+static void tr2evt_child_start_fl(const char *file, int line,
+ int child_id, const char *child_class,
+ const char **argv)
+{
+ const char *event_name = "child_start";
+
+ if (!child_class)
+ child_class = "?";
+
+ if (tr2key_trace_want(&tr2key_default)) {
+ struct strbuf buf_payload = STRBUF_INIT;
+
+ strbuf_addf(&buf_payload, "[ch%d] class:%s argv:",
+ child_id, child_class);
+ sq_quote_argv_pretty(&buf_payload, argv);
+
+ tr2io_write_default_fl(file, line, event_name, &buf_payload);
+ strbuf_release(&buf_payload);
+ }
+
+ if (tr2key_trace_want(&tr2key_perf)) {
+ struct strbuf buf_payload = STRBUF_INIT;
+
+ strbuf_addf(&buf_payload, "[ch%d] class:%s argv:",
+ child_id, child_class);
+ sq_quote_argv_pretty(&buf_payload, argv);
+
+ tr2io_write_perf_fl(file, line,
+ event_name, NULL, &buf_payload);
+ strbuf_release(&buf_payload);
+ }
+
+ if (tr2key_trace_want(&tr2key_event)) {
+ struct json_writer jw = JSON_WRITER_INIT;
+
+ jw_object_begin(&jw, 0);
+ tr2fmt_prepare_json_trace_line(
+ event_name, file, line, &jw);
+ jw_object_intmax(&jw, "child_id", child_id);
+ jw_object_string(&jw, "child_class", child_class);
+ jw_object_inline_begin_array(&jw, "argv");
+ jw_array_argv(&jw, argv);
+ jw_end(&jw);
+ jw_end(&jw);
+
+ tr2io_write_line(&tr2key_event, &jw.json);
+ jw_release(&jw);
+ }
+}
+
+static void tr2evt_child_exit_fl(const char *file, int line,
+ int child_id,
+ int child_exit_code,
+ uint64_t us_elapsed)
+{
+ const char *event_name = "child_exit";
+
+ if (tr2key_trace_want(&tr2key_default)) {
+ struct strbuf buf_payload = STRBUF_INIT;
+ double elapsed = (double)us_elapsed / 1000000.0;
+
+ strbuf_addf(&buf_payload, "[ch%d] code:%d elapsed:%.6f",
+ child_id, child_exit_code, elapsed);
+
+ tr2io_write_default_fl(file, line, event_name, &buf_payload);
+ strbuf_release(&buf_payload);
+ }
+
+ if (tr2key_trace_want(&tr2key_perf)) {
+ struct strbuf buf_payload = STRBUF_INIT;
+
+ strbuf_addf(&buf_payload, "[ch%d] code:%d",
+ child_id, child_exit_code);
+
+ tr2io_write_perf_fl(file, line,
+ event_name, &us_elapsed, &buf_payload);
+ strbuf_release(&buf_payload);
+ }
+
+ if (tr2key_trace_want(&tr2key_event)) {
+ struct json_writer jw = JSON_WRITER_INIT;
+ double elapsed = (double)us_elapsed / 1000000.0;
+
+ jw_object_begin(&jw, 0);
+ tr2fmt_prepare_json_trace_line(
+ event_name, file, line, &jw);
+ jw_object_intmax(&jw, "child_id", child_id);
+ jw_object_intmax(&jw, "code", child_exit_code);
+ jw_object_double(&jw, "elapsed", 6, elapsed);
+ jw_end(&jw);
+
+ tr2io_write_line(&tr2key_event, &jw.json);
+
+ jw_release(&jw);
+ }
+}
+
+static void tr2evt_thread_start_fl(const char *file, int line)
+{
+ const char *event_name = "thread_start";
+
+ /* Default target does not care about threads. */
+
+ if (tr2key_trace_want(&tr2key_perf)) {
+ struct strbuf buf_payload = STRBUF_INIT;
+
+ tr2io_write_perf_fl(file, line,
+ event_name, NULL, &buf_payload);
+ strbuf_release(&buf_payload);
+ }
+
+ if (tr2key_trace_want(&tr2key_event)) {
+ struct json_writer jw = JSON_WRITER_INIT;
+
+ jw_object_begin(&jw, 0);
+ tr2fmt_prepare_json_trace_line(
+ event_name, file, line, &jw);
+ jw_end(&jw);
+
+ tr2io_write_line(&tr2key_event, &jw.json);
+ jw_release(&jw);
+ }
+}
+
+static void tr2evt_thread_exit_fl(const char *file, int line,
+ uint64_t us_elapsed)
+{
+ const char *event_name = "thread_exit";
+
+ /* Default target does not care about threads. */
+
+ if (tr2key_trace_want(&tr2key_perf)) {
+ struct strbuf buf_payload = STRBUF_INIT;
+
+ tr2io_write_perf_fl(file, line,
+ event_name, &us_elapsed, &buf_payload);
+ strbuf_release(&buf_payload);
+ }
+
+ if (tr2key_trace_want(&tr2key_event)) {
+ struct json_writer jw = JSON_WRITER_INIT;
+ double elapsed = (double)us_elapsed / 1000000.0;
+
+ jw_object_begin(&jw, 0);
+ tr2fmt_prepare_json_trace_line(
+ event_name, file, line, &jw);
+ jw_object_double(&jw, "elapsed", 6, elapsed);
+ jw_end(&jw);
+
+ tr2io_write_line(&tr2key_event, &jw.json);
+ jw_release(&jw);
+ }
+}
+
+static void tr2evt_region_enter_va_fl(const char *file, int line,
+ const char *fmt, va_list ap)
+{
+ const char *event_name = "region_enter";
+
+ /* Default target does not care about nesting. */
+
+ if (tr2key_trace_want(&tr2key_perf)) {
+ struct strbuf buf_payload = STRBUF_INIT;
+
+ strbuf_vaddf(&buf_payload, fmt, ap);
+
+ tr2io_write_perf_fl(file, line,
+ event_name, NULL, &buf_payload);
+ strbuf_release(&buf_payload);
+ }
+
+ if (tr2key_trace_want(&tr2key_event)) {
+ struct tr2tls_thread_ctx *ctx = tr2tls_get_self();
+ if (ctx->nr_open_regions <= tr2env_event_depth_wanted) {
+ struct json_writer jw = JSON_WRITER_INIT;
+ struct strbuf buf = STRBUF_INIT;
+
+ strbuf_vaddf(&buf, fmt, ap);
+
+ jw_object_begin(&jw, 0);
+ tr2fmt_prepare_json_trace_line(
+ event_name, file, line, &jw);
+ jw_object_intmax(&jw, "depth", ctx->nr_open_regions);
+ jw_object_string(&jw, "msg", buf.buf);
+ jw_end(&jw);
+
+ tr2io_write_line(&tr2key_event, &jw.json);
+ jw_release(&jw);
+ strbuf_release(&buf);
+ }
+ }
+}
+
+static void tr2evt_region_leave_va_fl(const char *file, int line,
+ uint64_t us_elapsed,
+ const char *fmt, va_list ap)
+{
+ const char *event_name = "region_leave";
+
+ /* Default target does not care about nesting. */
+
+ if (tr2key_trace_want(&tr2key_perf)) {
+ struct strbuf buf_payload = STRBUF_INIT;
+
+ strbuf_vaddf(&buf_payload, fmt, ap);
+
+ tr2io_write_perf_fl(file, line,
+ event_name, &us_elapsed, &buf_payload);
+ strbuf_release(&buf_payload);
+ }
+
+ if (tr2key_trace_want(&tr2key_event)) {
+ struct tr2tls_thread_ctx *ctx = tr2tls_get_self();
+ if (ctx->nr_open_regions <= tr2env_event_depth_wanted) {
+ struct json_writer jw = JSON_WRITER_INIT;
+ struct strbuf buf = STRBUF_INIT;
+ double elapsed = (double)us_elapsed / 1000000.0;
+
+ strbuf_vaddf(&buf, fmt, ap);
+
+ jw_object_begin(&jw, 0);
+ tr2fmt_prepare_json_trace_line(
+ event_name, file, line, &jw);
+ jw_object_double(&jw, "elapsed", 6, elapsed);
+ jw_object_intmax(&jw, "depth", ctx->nr_open_regions);
+ jw_object_string(&jw, "msg", buf.buf);
+ jw_end(&jw);
+
+ tr2io_write_line(&tr2key_event, &jw.json);
+ jw_release(&jw);
+ strbuf_release(&buf);
+ }
+ }
+}
+
+static void tr2evt_printf_va_fl(const char *file, int line,
+ const char *fmt, va_list ap)
+{
+ const char *event_name = "printf";
+
+ if (tr2key_trace_want(&tr2key_default)) {
+ struct strbuf buf_payload = STRBUF_INIT;
+
+ strbuf_vaddf(&buf_payload, fmt, ap);
+
+ tr2io_write_default_fl(file, line, event_name, &buf_payload);
+ strbuf_release(&buf_payload);
+ }
+
+ if (tr2key_trace_want(&tr2key_perf)) {
+ struct strbuf buf_payload = STRBUF_INIT;
+
+ strbuf_vaddf(&buf_payload, fmt, ap);
+
+ tr2io_write_perf_fl(file, line,
+ event_name, NULL, &buf_payload);
+ strbuf_release(&buf_payload);
+ }
+
+ /* Event target does not care about printf messages. */
+}
+
+static void tr2evt_data_fl(const char *file, int line,
+ const char *category,
+ const char *key,
+ const char *value)
+{
+ const char *event_name = "data";
+
+ /* Default target does not care about data messages. */
+
+ if (tr2key_trace_want(&tr2key_perf)) {
+ struct strbuf buf_payload = STRBUF_INIT;
+
+ strbuf_addf(&buf_payload, "category:%s key:%s value:%s",
+ category, key, value);
+
+ tr2io_write_perf_fl(file, line,
+ event_name, NULL, &buf_payload);
+ strbuf_release(&buf_payload);
+ }
+
+ if (tr2key_trace_want(&tr2key_event)) {
+ struct json_writer jw = JSON_WRITER_INIT;
+
+ jw_object_begin(&jw, 0);
+ tr2fmt_prepare_json_trace_line(
+ event_name, file, line, &jw);
+ jw_object_string(&jw, "category", category);
+ jw_object_string(&jw, "key", key);
+ jw_object_string(&jw, "value", value);
+ jw_end(&jw);
+
+ tr2io_write_line(&tr2key_event, &jw.json);
+ jw_release(&jw);
+ }
+}
+
+/*****************************************************************
+ * TODO remove this section header
+ *****************************************************************/
+
+static int tr2main_exit_code;
+
+/*
+ * Our atexit routine should run after everything has finished.
+ * The system should call it on the main thread.
+ *
+ * Note that events generated here might not actually appear if
+ * we are writing to fd 1 or 2 and our atexit routine runs after
+ * the pager's atexit routine (since it closes them to shutdown
+ * the pipes).
+ */
+static void tr2main_atexit_handler(void)
+{
+ assert(tr2tls_get_self() == tr2tls_thread_main);
+
+ /*
+ * Pop any extra open regions on the main thread and discard.
+ * Normally, we should only have the region[0] that was pushed
+ * in trace2_start(), but there may be more if someone calls
+ * die() for example.
+ */
+ while (tr2tls_thread_main->nr_open_regions > 1)
+ tr2tls_pop_self();
+
+ /*
+ * Issue single 'atexit' event with the elapsed time since
+ * the main thread was started.
+ */
+ tr2evt_atexit(tr2main_exit_code, tr2tls_elapsed_main());
+
+ tr2key_disable_builtins();
+
+ tr2tls_release();
+
+ strbuf_release(&tr2sid_buf);
+ strbuf_release(&tr2_dots);
+}
+
+static void tr2main_signal_handler(int signo)
+{
+ tr2evt_signal(signo, tr2tls_elapsed_main());
+
+ sigchain_pop(signo);
+ raise(signo);
+}
+
+/*****************************************************************
+ * TODO remove this section header
+ *****************************************************************/
+
+void trace2_start_fl(const char *file, int line, const char **argv)
+{
+ if (!tr2key_want_builtins())
+ return;
+
+ tr2sid_compute();
+ strbuf_addchars(&tr2_dots, '.',
+ TR2_MAX_REGION_NESTING * TR2_INDENT);
+
+ atexit(tr2main_atexit_handler);
+ sigchain_push(SIGPIPE, tr2main_signal_handler);
+
+ tr2tls_init();
+
+ tr2evt_version_fl(file, line);
+ tr2evt_start_fl(file, line, argv);
+}
+
+int trace2_exit_fl(const char *file, int line, int code)
+{
+ code &= 0xff;
+
+ if (!tr2tls_initialized)
+ return code;
+
+ tr2main_exit_code = code;
+
+ tr2evt_exit_fl(file, line, code, tr2tls_elapsed_main());
+
+ return code;
+}
+
+void trace2_error_va_fl(const char *file, int line,
+ const char *fmt, va_list ap)
+{
+ if (!tr2tls_initialized)
+ return;
+
+ tr2evt_error_va_fl(file, line, fmt, ap);
+}
+
+void trace2_command_fl(const char *file, int line,
+ const char *command_name)
+{
+ if (!tr2tls_initialized)
+ return;
+
+ tr2evt_command_fl(file, line, command_name);
+}
+
+void trace2_worktree_fl(const char *file, int line,
+ const char *path)
+{
+ if (!tr2tls_initialized)
+ return;
+
+ tr2evt_worktree_fl(file, line, path);
+}
+
+void trace2_alias_fl(const char *file, int line,
+ const char *alias, const char **argv)
+{
+ if (!tr2tls_initialized)
+ return;
+
+ tr2evt_alias_fl(file, line, alias, argv);
+}
+
+void trace2_param_fl(const char *file, int line,
+ const char *param, const char *value)
+{
+ if (!tr2tls_initialized)
+ return;
+
+ tr2evt_param_fl(file, line, param, value);
+}
+
+void trace2_child_start_fl(const char *file, int line,
+ struct child_process *cmd)
+{
+ if (!tr2tls_initialized)
+ return;
+
+ pthread_mutex_lock(&tr2tls_mutex);
+ cmd->trace2_child_id = tr2_next_child_id++;
+ pthread_mutex_unlock(&tr2tls_mutex);
+
+ cmd->trace2_child_us_start = getnanotime() / 1000;
+
+ tr2evt_child_start_fl(file, line,
+ cmd->trace2_child_id,
+ cmd->trace2_child_class,
+ cmd->argv);
+}
+
+void trace2_child_exit_fl(const char *file, int line,
+ struct child_process *cmd,
+ int child_exit_code)
+{
+ uint64_t us_elapsed;
+
+ if (!tr2tls_initialized)
+ return;
+
+ if (cmd->trace2_child_us_start)
+ us_elapsed = (getnanotime() / 1000) - cmd->trace2_child_us_start;
+ else
+ us_elapsed = 0;
+
+ tr2evt_child_exit_fl(file, line,
+ cmd->trace2_child_id,
+ child_exit_code,
+ us_elapsed);
+}
+
+void trace2_thread_start_fl(const char *file, int line,
+ const char *thread_name)
+{
+ if (!tr2tls_initialized)
+ return;
+
+ tr2tls_create_self(thread_name);
+
+ tr2evt_thread_start_fl(file, line);
+}
+
+void trace2_thread_exit_fl(const char *file, int line)
+{
+ struct tr2tls_thread_ctx *ctx;
+
+ if (!tr2tls_initialized)
+ return;
+
+ /*
+ * Pop any extra open regions on the current thread and discard.
+ * Normally, we should only have the region[0] that was pushed
+ * in trace2_thread_start() if the thread exits normally.
+ */
+ ctx = tr2tls_get_self();
+ while (ctx->nr_open_regions > 1)
+ tr2tls_pop_self();
+
+ tr2evt_thread_exit_fl(file, line, tr2tls_region_elapsed_self());
+
+ tr2tls_unset_self();
+}
+
+void trace2_region_enter_va_fl(const char *file, int line,
+ const char *fmt, va_list ap)
+{
+ if (!tr2tls_initialized)
+ return;
+
+ if (fmt && *fmt)
+ tr2evt_region_enter_va_fl(file, line, fmt, ap);
+
+ tr2tls_push_self();
+}
+
+void trace2_region_enter_fl(const char *file, int line,
+ const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ trace2_region_enter_va_fl(file, line, fmt, ap);
+ va_end(ap);
+}
+
+#ifndef HAVE_VARIADIC_MACROS
+void trace2_region_enter(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ trace2_region_enter_va_fl(NULL, 0, fmt, ap);
+ va_end(ap);
+}
+#endif
+
+void trace2_region_leave_va_fl(const char *file, int line,
+ const char *fmt, va_list ap)
+{
+ uint64_t us_elapsed;
+
+ if (!tr2tls_initialized)
+ return;
+
+ /*
+ * Get the elapsed time in the current region before we
+ * pop it off the stack. Pop the stack. And then print
+ * the perf message at the new (shallower) level so that
+ * it lines up with the corresponding push/enter.
+ */
+ us_elapsed = tr2tls_region_elapsed_self();
+
+ tr2tls_pop_self();
+
+ if (fmt && *fmt)
+ tr2evt_region_leave_va_fl(file, line, us_elapsed, fmt, ap);
+}
+
+void trace2_region_leave_fl(const char *file, int line,
+ const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ trace2_region_leave_va_fl(file, line, fmt, ap);
+ va_end(ap);
+}
+
+#ifndef HAVE_VARIADIC_MACROS
+void trace2_region_leave(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ trace2_region_leave_va_fl(NULL, 0, fmt, ap);
+ va_end(ap);
+}
+#endif
+
+void trace2_printf_va_fl(const char *file, int line,
+ const char *fmt, va_list ap)
+{
+ if (!tr2tls_initialized)
+ return;
+
+ tr2evt_printf_va_fl(file, line, fmt, ap);
+}
+
+void trace2_printf_fl(const char *file, int line,
+ const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ trace2_printf_va_fl(file, line, fmt, ap);
+ va_end(ap);
+}
+
+#ifndef HAVE_VARIADIC_MACROS
+void trace2_printf(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ trace2_printf_va_fl(NULL, 0, fmt, ap);
+ va_end(ap);
+}
+#endif
+
+void trace2_data_intmax_fl(const char *file, int line,
+ const char *category,
+ const char *key,
+ intmax_t value)
+{
+ struct strbuf buf_string = STRBUF_INIT;
+
+ if (!tr2tls_initialized)
+ return;
+
+ strbuf_addf(&buf_string, "%"PRIdMAX, value);
+ tr2evt_data_fl(file, line, category, key, buf_string.buf);
+ strbuf_release(&buf_string);
+}
+
+void trace2_data_string_fl(const char *file, int line,
+ const char *category,
+ const char *key,
+ const char *value)
+{
+ if (!tr2tls_initialized)
+ return;
+
+ tr2evt_data_fl(file, line, category, key, value);
+}
diff --git a/trace2.h b/trace2.h
new file mode 100644
index 0000000000..0ef08c1253
--- /dev/null
+++ b/trace2.h
@@ -0,0 +1,214 @@
+#ifndef TRACE2_H
+#define TRACE2_H
+
+/*
+ * Begin TRACE2 tracing (if any of the builtin TRACE2 targets are
+ * enabled in the environment).
+ *
+ * Emit a 'start' event.
+ */
+void trace2_start_fl(const char *file, int line,
+ const char **argv);
+
+#define trace2_start(argv) trace2_start_fl(__FILE__, __LINE__, argv)
+
+/*
+ * Emit an 'exit' event.
+ *
+ * Write the exit-code that will be passed to exit() or returned
+ * from main(). Use this prior to actually calling exit().
+ * See "#define exit()" in git-compat-util.h
+ */
+int trace2_exit_fl(const char *file, int line, int code);
+
+#define trace2_exit(code) (trace2_exit_fl(__FILE__, __LINE__, (code)))
+
+/*
+ * Emit an 'error' event.
+ *
+ * Write an error message to the TRACE2 targets.
+ */
+void trace2_error_va_fl(const char *file, int line,
+ const char *fmt, va_list ap);
+
+#define trace2_error_va(fmt, ap) \
+ trace2_error_va_fl(__FILE__, __LINE__, (fmt), (ap))
+
+void trace2_command_fl(const char *file, int line,
+ const char *command_name);
+
+#define trace2_command(n) \
+ trace2_command_fl(__FILE__, __LINE__, (n))
+
+/*
+ * Emit a 'worktree' event giving the absolute path of the worktree.
+ */
+void trace2_worktree_fl(const char *file, int line,
+ const char *path);
+
+#define trace2_worktree(path) \
+ trace2_worktree_fl(__FILE__, __LINE__, path)
+
+/*
+ * Emit an 'alias' expansion event.
+ */
+void trace2_alias_fl(const char *file, int line,
+ const char *alias, const char **argv);
+
+#define trace2_alias(alias, argv) \
+ trace2_alias_fl(__FILE__, __LINE__, (alias), (argv))
+
+/*
+ * Emit a 'param' event.
+ *
+ * Write a (key, value) pair describing some aspect of the run
+ * such as an important configuration setting.
+ */
+void trace2_param_fl(const char *file, int line,
+ const char *param, const char *value);
+
+#define trace2_param(param, value) \
+ trace2_param_fl(__FILE__, __LINE__, (param), (value))
+
+struct child_process;
+
+/*
+ * Emit a 'child_start' event prior to spawning a child process.
+ *
+ * Before calling optionally set cmd.trace2_child_class to a string
+ * describing the type of the child process. For example, "editor"
+ * or "pager".
+ */
+void trace2_child_start_fl(const char *file, int line,
+ struct child_process *cmd);
+
+#define trace2_child_start(cmd) \
+ trace2_child_start_fl(__FILE__, __LINE__, (cmd))
+
+/*
+ * Emit a 'child_exit' event after the child process completes.
+ */
+void trace2_child_exit_fl(const char *file, int line,
+ struct child_process *cmd,
+ int child_exit_code);
+
+#define trace2_child_exit(cmd, code) \
+ trace2_child_exit_fl(__FILE__, __LINE__, (cmd), (code))
+
+/*
+ * Emit a 'thread_start' event. This must be called from inside the
+ * thread-proc to set up TLS data for the thread.
+ *
+ * Thread names should be descriptive, like "preload_index".
+ * Thread names will be decorated with an instance number automatically.
+ */
+void trace2_thread_start_fl(const char *file, int line,
+ const char *thread_name);
+
+#define trace2_thread_start(thread_name) \
+ trace2_thread_start_fl(__FILE__, __LINE__, (thread_name))
+
+/*
+ * Emit a 'thead_exit' event. This must be called from inside the
+ * thread-proc to report thread-specific data and cleanup TLS data
+ * for the thread.
+ */
+void trace2_thread_exit_fl(const char *file, int line);
+
+#define trace2_thread_exit() trace2_thread_exit_fl(__FILE__, __LINE__)
+
+/*
+ * Emit a 'printf' event.
+ *
+ * Write an arbitrary formatted message to the TRACE2 targets. These
+ * text messages should be considered as human-readable strings without
+ * any formatting guidelines. Post-processors may choose to ignore
+ * them.
+ */
+void trace2_printf_va_fl(const char *file, int line,
+ const char *fmt, va_list ap);
+
+#define trace2_printf_va(fmt, ap) \
+ trace2_printf_va_fl(__FILE__, __LINE__, (fmt), (ap))
+
+void trace2_printf_fl(const char *file, int line, const char *fmt, ...);
+
+#ifdef HAVE_VARIADIC_MACROS
+#define trace2_printf(...) \
+ trace2_printf_fl(__FILE__, __LINE__, __VA_ARGS__)
+#else
+__attribute__((format (printf, 1, 2)))
+void trace2_printf(const char *fmt, ...);
+#endif
+
+/*
+ * Emit a 'region_enter' event.
+ *
+ * Enter a new nesting level on the current thread and remember the
+ * current time. This controls the indenting of subsequent thread
+ * events.
+ */
+void trace2_region_enter_va_fl(const char *file, int line,
+ const char *fmt, va_list ap);
+
+#define trace2_region_enter_va(fmt, ap) \
+ trace2_region_enter_va_fl(__FILE__, __LINE__, (fmt), (ap))
+
+void trace2_region_enter_fl(const char *file, int line, const char *fmt, ...);
+
+#ifdef HAVE_VARIADIC_MACROS
+#define trace2_region_enter(...) \
+ trace2_region_enter_fl(__FILE__, __LINE__, __VA_ARGS__)
+#else
+__attribute__((format (printf, 1, 2)))
+void trace2_region_enter(const char *fmt, ...);
+#endif
+
+/*
+ * Emit a 'region_leave' event.
+ *
+ * Leave current nesting level and report the elapsed time spent
+ * in this nesting level.
+ */
+void trace2_region_leave_va_fl(const char *file, int line,
+ const char *fmt, va_list ap);
+
+#define trace2_region_enter_va(fmt, ap) \
+ trace2_region_enter_va_fl(__FILE__, __LINE__, (fmt), (ap))
+
+void trace2_region_leave_fl(const char *file, int line, const char *fmt, ...);
+
+#ifdef HAVE_VARIADIC_MACROS
+#define trace2_region_leave(...) \
+ trace2_region_leave_fl(__FILE__, __LINE__, __VA_ARGS__)
+#else
+__attribute__((format (printf, 1, 2)))
+void trace2_region_leave(const char *fmt, ...);
+#endif
+
+/*
+ * Emit a 'data' event to report a <category>.<key> = <value> pair.
+ * This can be used to report global data, such as the size of the index,
+ * or by a thread to report TLS data, such as their assigned subset.
+ *
+ * On event-based TRACE2 targets, this generates a 'data' event suitable
+ * for post-processing. On printf-based TRACE2 targets, this is converted
+ * into a fixed-format printf message.
+ */
+void trace2_data_intmax_fl(const char *file, int line,
+ const char *category,
+ const char *key,
+ intmax_t value);
+
+#define trace2_data_intmax(c, k, v) \
+ trace2_data_intmax_fl(__FILE__, __LINE__, (c), (k), (v))
+
+void trace2_data_string_fl(const char *file, int line,
+ const char *category,
+ const char *key,
+ const char *value);
+
+#define trace2_data_string(c, k, v) \
+ trace2_data_string_fl(__FILE__, __LINE__, (c), (k), (v))
+
+#endif /* TRACE2_H */
--
gitgitgadget
next prev parent reply other threads:[~2018-08-31 16:50 UTC|newest]
Thread overview: 26+ messages / expand[flat|nested] mbox.gz Atom feed top
2018-08-31 16:49 [PATCH 0/8] WIP: trace2: a new trace facility Jeff Hostetler via GitGitGadget
2018-08-31 16:49 ` Jeff Hostetler via GitGitGadget [this message]
2018-08-31 17:19 ` [PATCH 1/8] trace2: create new combined " Derrick Stolee
2018-09-04 22:12 ` Stefan Beller
2018-09-04 22:30 ` Junio C Hamano
2018-09-05 15:51 ` Jeff Hostetler
2018-09-05 15:01 ` Jeff Hostetler
2018-08-31 16:49 ` [PATCH 2/8] trace2: add trace2 to main Jeff Hostetler via GitGitGadget
2018-08-31 16:49 ` [PATCH 3/8] trace2: demonstrate trace2 regions in wt-status Jeff Hostetler via GitGitGadget
2018-08-31 16:49 ` [PATCH 4/8] trace2: demonstrate trace2 child process classification Jeff Hostetler via GitGitGadget
2018-08-31 16:50 ` [PATCH 5/8] trace2: demonstrate instrumenting do_read_index Jeff Hostetler via GitGitGadget
2018-08-31 16:50 ` [PATCH 6/8] trace2: demonstrate instrumenting threaded preload_index Jeff Hostetler via GitGitGadget
2018-08-31 16:50 ` [PATCH 7/8] trace2: demonstrate setting sub-command parameter in checkout Jeff Hostetler via GitGitGadget
2018-08-31 16:50 ` [PATCH 8/8] trace2: demonstrate use of regions in read_directory_recursive Jeff Hostetler via GitGitGadget
2018-08-31 17:19 ` [PATCH 0/8] WIP: trace2: a new trace facility Derrick Stolee
2018-09-06 15:13 ` [RFC PATCH 0/6] Use trace2 in commit-reach Derrick Stolee
2018-09-06 15:13 ` [RFC PATCH 1/6] commit-reach: add trace2 telemetry and walk count Derrick Stolee
2018-09-06 15:13 ` [RFC PATCH 2/6] comit-reach: use trace2 for commit_contains_tag_algo Derrick Stolee
2018-09-06 15:13 ` [RFC PATCH 3/6] commit-reach: use trace2 in can_all_from_reach Derrick Stolee
2018-09-06 15:13 ` [RFC PATCH 4/6] test-tool: start trace2 environment Derrick Stolee
2018-09-06 15:13 ` [RFC PATCH 5/6] test-lib: add run_and_check_trace2 Derrick Stolee
2018-09-06 15:13 ` [RFC PATCH 6/6] commit-reach: fix first-parent heuristic Derrick Stolee
2018-10-11 1:50 ` Jonathan Nieder
2018-10-11 11:00 ` Derrick Stolee
2019-01-15 1:05 ` [PATCH 0/8] WIP: trace2: a new trace facility Jonathan Nieder
2019-01-15 17:03 ` Jeff Hostetler
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=82885700379efe6d6a83629cac4d943b99b393bf.1535734192.git.gitgitgadget@gmail.com \
--to=gitgitgadget@gmail.com \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=jeffhost@microsoft.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).