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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.