diff options
Diffstat (limited to 'trace2')
-rw-r--r-- | trace2/tr2_cfg.c | 7 | ||||
-rw-r--r-- | trace2/tr2_dst.c | 91 | ||||
-rw-r--r-- | trace2/tr2_dst.h | 3 | ||||
-rw-r--r-- | trace2/tr2_sid.c | 53 | ||||
-rw-r--r-- | trace2/tr2_sysenv.c | 127 | ||||
-rw-r--r-- | trace2/tr2_sysenv.h | 36 | ||||
-rw-r--r-- | trace2/tr2_tbuf.c | 19 | ||||
-rw-r--r-- | trace2/tr2_tbuf.h | 5 | ||||
-rw-r--r-- | trace2/tr2_tgt.h | 1 | ||||
-rw-r--r-- | trace2/tr2_tgt_event.c | 53 | ||||
-rw-r--r-- | trace2/tr2_tgt_normal.c | 19 | ||||
-rw-r--r-- | trace2/tr2_tgt_perf.c | 23 | ||||
-rw-r--r-- | trace2/tr2_tls.c | 38 | ||||
-rw-r--r-- | trace2/tr2_tls.h | 8 |
14 files changed, 392 insertions, 91 deletions
diff --git a/trace2/tr2_cfg.c b/trace2/tr2_cfg.c index b329921ac5..caa7f06948 100644 --- a/trace2/tr2_cfg.c +++ b/trace2/tr2_cfg.c @@ -1,8 +1,7 @@ #include "cache.h" #include "config.h" -#include "tr2_cfg.h" - -#define TR2_ENVVAR_CFG_PARAM "GIT_TR2_CONFIG_PARAMS" +#include "trace2/tr2_cfg.h" +#include "trace2/tr2_sysenv.h" static struct strbuf **tr2_cfg_patterns; static int tr2_cfg_count_patterns; @@ -21,7 +20,7 @@ static int tr2_cfg_load_patterns(void) return tr2_cfg_count_patterns; tr2_cfg_loaded = 1; - envvar = getenv(TR2_ENVVAR_CFG_PARAM); + envvar = tr2_sysenv_get(TR2_SYSENV_CFG_PARAM); if (!envvar || !*envvar) return tr2_cfg_count_patterns; diff --git a/trace2/tr2_dst.c b/trace2/tr2_dst.c index fd490a43ad..5dda0ca1cd 100644 --- a/trace2/tr2_dst.c +++ b/trace2/tr2_dst.c @@ -1,23 +1,19 @@ #include "cache.h" #include "trace2/tr2_dst.h" +#include "trace2/tr2_sid.h" +#include "trace2/tr2_sysenv.h" /* - * If a Trace2 target cannot be opened for writing, we should issue a - * warning to stderr, but this is very annoying if the target is a pipe - * or socket and beyond the user's control -- especially since every - * git command (and sub-command) will print the message. So we silently - * eat these warnings and just discard the trace data. - * - * Enable the following environment variable to see these warnings. + * How many attempts we will make at creating an automatically-named trace file. */ -#define TR2_ENVVAR_DST_DEBUG "GIT_TR2_DST_DEBUG" +#define MAX_AUTO_ATTEMPTS 10 static int tr2_dst_want_warning(void) { static int tr2env_dst_debug = -1; if (tr2env_dst_debug == -1) { - const char *env_value = getenv(TR2_ENVVAR_DST_DEBUG); + const char *env_value = tr2_sysenv_get(TR2_SYSENV_DST_DEBUG); if (!env_value || !*env_value) tr2env_dst_debug = 0; else @@ -36,13 +32,65 @@ void tr2_dst_trace_disable(struct tr2_dst *dst) dst->need_close = 0; } +static int tr2_dst_try_auto_path(struct tr2_dst *dst, const char *tgt_prefix) +{ + int fd; + const char *last_slash, *sid = tr2_sid_get(); + struct strbuf path = STRBUF_INIT; + size_t base_path_len; + unsigned attempt_count; + + last_slash = strrchr(sid, '/'); + if (last_slash) + sid = last_slash + 1; + + strbuf_addstr(&path, tgt_prefix); + if (!is_dir_sep(path.buf[path.len - 1])) + strbuf_addch(&path, '/'); + strbuf_addstr(&path, sid); + base_path_len = path.len; + + for (attempt_count = 0; attempt_count < MAX_AUTO_ATTEMPTS; attempt_count++) { + if (attempt_count > 0) { + strbuf_setlen(&path, base_path_len); + strbuf_addf(&path, ".%d", attempt_count); + } + + fd = open(path.buf, O_WRONLY | O_CREAT | O_EXCL, 0666); + if (fd != -1) + break; + } + + if (fd == -1) { + if (tr2_dst_want_warning()) + warning("trace2: could not open '%.*s' for '%s' tracing: %s", + (int) base_path_len, path.buf, + tr2_sysenv_display_name(dst->sysenv_var), + strerror(errno)); + + tr2_dst_trace_disable(dst); + strbuf_release(&path); + return 0; + } + + strbuf_release(&path); + + dst->fd = fd; + dst->need_close = 1; + dst->initialized = 1; + + return dst->fd; +} + static int tr2_dst_try_path(struct tr2_dst *dst, const char *tgt_value) { int fd = open(tgt_value, O_WRONLY | O_APPEND | O_CREAT, 0666); if (fd == -1) { if (tr2_dst_want_warning()) warning("trace2: could not open '%s' for '%s' tracing: %s", - tgt_value, dst->env_var_name, strerror(errno)); + tgt_value, + tr2_sysenv_display_name(dst->sysenv_var), + strerror(errno)); tr2_dst_trace_disable(dst); return 0; @@ -116,7 +164,8 @@ static int tr2_dst_try_unix_domain_socket(struct tr2_dst *dst, if (!path || !*path) { if (tr2_dst_want_warning()) warning("trace2: invalid AF_UNIX value '%s' for '%s' tracing", - tgt_value, dst->env_var_name); + tgt_value, + tr2_sysenv_display_name(dst->sysenv_var)); tr2_dst_trace_disable(dst); return 0; @@ -126,7 +175,7 @@ static int tr2_dst_try_unix_domain_socket(struct tr2_dst *dst, strlen(path) >= sizeof(((struct sockaddr_un *)0)->sun_path)) { if (tr2_dst_want_warning()) warning("trace2: invalid AF_UNIX path '%s' for '%s' tracing", - path, dst->env_var_name); + path, tr2_sysenv_display_name(dst->sysenv_var)); tr2_dst_trace_disable(dst); return 0; @@ -148,7 +197,8 @@ static int tr2_dst_try_unix_domain_socket(struct tr2_dst *dst, error: if (tr2_dst_want_warning()) warning("trace2: could not connect to socket '%s' for '%s' tracing: %s", - path, dst->env_var_name, strerror(e)); + path, tr2_sysenv_display_name(dst->sysenv_var), + strerror(e)); tr2_dst_trace_disable(dst); return 0; @@ -168,7 +218,7 @@ static void tr2_dst_malformed_warning(struct tr2_dst *dst, struct strbuf buf = STRBUF_INIT; strbuf_addf(&buf, "trace2: unknown value for '%s': '%s'", - dst->env_var_name, tgt_value); + tr2_sysenv_display_name(dst->sysenv_var), tgt_value); warning("%s", buf.buf); strbuf_release(&buf); @@ -184,7 +234,7 @@ int tr2_dst_get_trace_fd(struct tr2_dst *dst) dst->initialized = 1; - tgt_value = getenv(dst->env_var_name); + tgt_value = tr2_sysenv_get(dst->sysenv_var); if (!tgt_value || !strcmp(tgt_value, "") || !strcmp(tgt_value, "0") || !strcasecmp(tgt_value, "false")) { @@ -202,8 +252,12 @@ int tr2_dst_get_trace_fd(struct tr2_dst *dst) return dst->fd; } - if (is_absolute_path(tgt_value)) - return tr2_dst_try_path(dst, tgt_value); + if (is_absolute_path(tgt_value)) { + if (is_directory(tgt_value)) + return tr2_dst_try_auto_path(dst, tgt_value); + else + return tr2_dst_try_path(dst, tgt_value); + } #ifndef NO_UNIX_SOCKETS if (starts_with(tgt_value, PREFIX_AF_UNIX)) @@ -246,7 +300,8 @@ void tr2_dst_write_line(struct tr2_dst *dst, struct strbuf *buf_line) return; if (tr2_dst_want_warning()) - warning("unable to write trace to '%s': %s", dst->env_var_name, + warning("unable to write trace to '%s': %s", + tr2_sysenv_display_name(dst->sysenv_var), strerror(errno)); tr2_dst_trace_disable(dst); } diff --git a/trace2/tr2_dst.h b/trace2/tr2_dst.h index 9a64f05b02..3adf3bac13 100644 --- a/trace2/tr2_dst.h +++ b/trace2/tr2_dst.h @@ -2,9 +2,10 @@ #define TR2_DST_H struct strbuf; +#include "trace2/tr2_sysenv.h" struct tr2_dst { - const char *const env_var_name; + enum tr2_sysenv_variable sysenv_var; int fd; unsigned int initialized : 1; unsigned int need_close : 1; diff --git a/trace2/tr2_sid.c b/trace2/tr2_sid.c index 984524a43c..5047095478 100644 --- a/trace2/tr2_sid.c +++ b/trace2/tr2_sid.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "trace2/tr2_tbuf.h" #include "trace2/tr2_sid.h" #define TR2_ENVVAR_PARENT_SID "GIT_TR2_PARENT_SID" @@ -7,6 +8,53 @@ static struct strbuf tr2sid_buf = STRBUF_INIT; static int tr2sid_nr_git_parents; /* + * Compute the final component of the SID representing the current process. + * This should uniquely identify the process and be a valid filename (to + * allow writing trace2 data to per-process files). It should also be fixed + * length for possible use as a database key. + * + * "<yyyymmdd>T<hhmmss>.<fraction>Z-<host>-<process>" + * + * where <host> is a 9 character string: + * "H<first_8_chars_of_sha1_of_hostname>" + * "Localhost" when no hostname. + * + * where <process> is a 9 character string containing the least signifcant + * 32 bits in the process-id. + * "P<pid>" + * (This is an abribrary choice. On most systems pid_t is a 32 bit value, + * so limit doesn't matter. On larger systems, a truncated value is fine + * for our purposes here.) + */ +static void tr2_sid_append_my_sid_component(void) +{ + const struct git_hash_algo *algo = &hash_algos[GIT_HASH_SHA1]; + struct tr2_tbuf tb_now; + git_hash_ctx ctx; + pid_t pid = getpid(); + unsigned char hash[GIT_MAX_RAWSZ + 1]; + char hex[GIT_MAX_HEXSZ + 1]; + char hostname[HOST_NAME_MAX + 1]; + + tr2_tbuf_utc_datetime(&tb_now); + strbuf_addstr(&tr2sid_buf, tb_now.buf); + + strbuf_addch(&tr2sid_buf, '-'); + if (xgethostname(hostname, sizeof(hostname))) + strbuf_add(&tr2sid_buf, "Localhost", 9); + else { + algo->init_fn(&ctx); + algo->update_fn(&ctx, hostname, strlen(hostname)); + algo->final_fn(hash, &ctx); + hash_to_hex_algop_r(hex, hash, algo); + strbuf_addch(&tr2sid_buf, 'H'); + strbuf_add(&tr2sid_buf, hex, 8); + } + + strbuf_addf(&tr2sid_buf, "-P%08"PRIx32, (uint32_t)pid); +} + +/* * Compute a "unique" session id (SID) for the current process. This allows * all events from this process to have a single label (much like a PID). * @@ -20,7 +68,6 @@ static int tr2sid_nr_git_parents; */ static void tr2_sid_compute(void) { - uint64_t us_now; const char *parent_sid; if (tr2sid_buf.len) @@ -38,9 +85,7 @@ static void tr2_sid_compute(void) tr2sid_nr_git_parents++; } - us_now = getnanotime() / 1000; - strbuf_addf(&tr2sid_buf, "%" PRIuMAX "-%" PRIdMAX, (uintmax_t)us_now, - (intmax_t)getpid()); + tr2_sid_append_my_sid_component(); setenv(TR2_ENVVAR_PARENT_SID, tr2sid_buf.buf, 1); } diff --git a/trace2/tr2_sysenv.c b/trace2/tr2_sysenv.c new file mode 100644 index 0000000000..9025b86303 --- /dev/null +++ b/trace2/tr2_sysenv.c @@ -0,0 +1,127 @@ +#include "cache.h" +#include "config.h" +#include "dir.h" +#include "tr2_sysenv.h" + +/* + * Each entry represents a trace2 setting. + * See Documentation/technical/api-trace2.txt + */ +struct tr2_sysenv_entry { + const char *env_var_name; + const char *git_config_name; + + char *value; + unsigned int getenv_called : 1; +}; + +/* + * This table must match "enum tr2_sysenv_variable" in tr2_sysenv.h. + * + * The strings in this table are constant and must match the published + * config and environment variable names as described in the documentation. + * + * We do not define entries for the GIT_TR2_PARENT_* environment + * variables because they are transient and used to pass information + * from parent to child git processes, rather than settings. + */ +/* clang-format off */ +static struct tr2_sysenv_entry tr2_sysenv_settings[] = { + [TR2_SYSENV_CFG_PARAM] = { "GIT_TR2_CONFIG_PARAMS", + "trace2.configparams" }, + + [TR2_SYSENV_DST_DEBUG] = { "GIT_TR2_DST_DEBUG", + "trace2.destinationdebug" }, + + [TR2_SYSENV_NORMAL] = { "GIT_TR2", + "trace2.normaltarget" }, + [TR2_SYSENV_NORMAL_BRIEF] = { "GIT_TR2_BRIEF", + "trace2.normalbrief" }, + + [TR2_SYSENV_EVENT] = { "GIT_TR2_EVENT", + "trace2.eventtarget" }, + [TR2_SYSENV_EVENT_BRIEF] = { "GIT_TR2_EVENT_BRIEF", + "trace2.eventbrief" }, + [TR2_SYSENV_EVENT_NESTING] = { "GIT_TR2_EVENT_NESTING", + "trace2.eventnesting" }, + + [TR2_SYSENV_PERF] = { "GIT_TR2_PERF", + "trace2.perftarget" }, + [TR2_SYSENV_PERF_BRIEF] = { "GIT_TR2_PERF_BRIEF", + "trace2.perfbrief" }, +}; +/* clang-format on */ + +static int tr2_sysenv_cb(const char *key, const char *value, void *d) +{ + int k; + + if (!starts_with(key, "trace2.")) + return 0; + + for (k = 0; k < ARRAY_SIZE(tr2_sysenv_settings); k++) { + if (!strcmp(key, tr2_sysenv_settings[k].git_config_name)) { + free(tr2_sysenv_settings[k].value); + tr2_sysenv_settings[k].value = xstrdup(value); + return 0; + } + } + + return 0; +} + +/* + * Load Trace2 settings from the system config (usually "/etc/gitconfig" + * unless we were built with a runtime-prefix). These are intended to + * define the default values for Trace2 as requested by the administrator. + * + * Then override with the Trace2 settings from the global config. + */ +void tr2_sysenv_load(void) +{ + if (ARRAY_SIZE(tr2_sysenv_settings) != TR2_SYSENV_MUST_BE_LAST) + BUG("tr2_sysenv_settings size is wrong"); + + read_very_early_config(tr2_sysenv_cb, NULL); +} + +/* + * Return the value for the requested Trace2 setting from these sources: + * the system config, the global config, and the environment. + */ +const char *tr2_sysenv_get(enum tr2_sysenv_variable var) +{ + if (var >= TR2_SYSENV_MUST_BE_LAST) + BUG("tr2_sysenv_get invalid var '%d'", var); + + if (!tr2_sysenv_settings[var].getenv_called) { + const char *v = getenv(tr2_sysenv_settings[var].env_var_name); + if (v && *v) { + free(tr2_sysenv_settings[var].value); + tr2_sysenv_settings[var].value = xstrdup(v); + } + tr2_sysenv_settings[var].getenv_called = 1; + } + + return tr2_sysenv_settings[var].value; +} + +/* + * Return a friendly name for this setting that is suitable for printing + * in an error messages. + */ +const char *tr2_sysenv_display_name(enum tr2_sysenv_variable var) +{ + if (var >= TR2_SYSENV_MUST_BE_LAST) + BUG("tr2_sysenv_get invalid var '%d'", var); + + return tr2_sysenv_settings[var].env_var_name; +} + +void tr2_sysenv_release(void) +{ + int k; + + for (k = 0; k < ARRAY_SIZE(tr2_sysenv_settings); k++) + free(tr2_sysenv_settings[k].value); +} diff --git a/trace2/tr2_sysenv.h b/trace2/tr2_sysenv.h new file mode 100644 index 0000000000..369b20bd87 --- /dev/null +++ b/trace2/tr2_sysenv.h @@ -0,0 +1,36 @@ +#ifndef TR2_SYSENV_H +#define TR2_SYSENV_H + +/* + * The Trace2 settings that can be loaded from /etc/gitconfig + * and/or user environment variables. + * + * Note that this set does not contain any of the transient + * environment variables used to pass information from parent + * to child git processes, such "GIT_TR2_PARENT_SID". + */ +enum tr2_sysenv_variable { + TR2_SYSENV_CFG_PARAM = 0, + + TR2_SYSENV_DST_DEBUG, + + TR2_SYSENV_NORMAL, + TR2_SYSENV_NORMAL_BRIEF, + + TR2_SYSENV_EVENT, + TR2_SYSENV_EVENT_BRIEF, + TR2_SYSENV_EVENT_NESTING, + + TR2_SYSENV_PERF, + TR2_SYSENV_PERF_BRIEF, + + TR2_SYSENV_MUST_BE_LAST +}; + +void tr2_sysenv_load(void); + +const char *tr2_sysenv_get(enum tr2_sysenv_variable); +const char *tr2_sysenv_display_name(enum tr2_sysenv_variable var); +void tr2_sysenv_release(void); + +#endif /* TR2_SYSENV_H */ diff --git a/trace2/tr2_tbuf.c b/trace2/tr2_tbuf.c index 0844910423..2498482d9a 100644 --- a/trace2/tr2_tbuf.c +++ b/trace2/tr2_tbuf.c @@ -15,7 +15,7 @@ void tr2_tbuf_local_time(struct tr2_tbuf *tb) tm.tm_min, tm.tm_sec, (long)tv.tv_usec); } -void tr2_tbuf_utc_time(struct tr2_tbuf *tb) +void tr2_tbuf_utc_datetime_extended(struct tr2_tbuf *tb) { struct timeval tv; struct tm tm; @@ -26,7 +26,22 @@ void tr2_tbuf_utc_time(struct tr2_tbuf *tb) gmtime_r(&secs, &tm); xsnprintf(tb->buf, sizeof(tb->buf), - "%4d-%02d-%02d %02d:%02d:%02d.%06ld", tm.tm_year + 1900, + "%4d-%02d-%02dT%02d:%02d:%02d.%06ldZ", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, (long)tv.tv_usec); } + +void tr2_tbuf_utc_datetime(struct tr2_tbuf *tb) +{ + struct timeval tv; + struct tm tm; + time_t secs; + + gettimeofday(&tv, NULL); + secs = tv.tv_sec; + gmtime_r(&secs, &tm); + + xsnprintf(tb->buf, sizeof(tb->buf), "%4d%02d%02dT%02d%02d%02d.%06ldZ", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, + tm.tm_min, tm.tm_sec, (long)tv.tv_usec); +} diff --git a/trace2/tr2_tbuf.h b/trace2/tr2_tbuf.h index 9cdefa3957..fa853d8f42 100644 --- a/trace2/tr2_tbuf.h +++ b/trace2/tr2_tbuf.h @@ -16,8 +16,9 @@ struct tr2_tbuf { void tr2_tbuf_local_time(struct tr2_tbuf *tb); /* - * Fill buffer with formatted UTC time string. + * Fill buffer with formatted UTC datatime string. */ -void tr2_tbuf_utc_time(struct tr2_tbuf *tb); +void tr2_tbuf_utc_datetime_extended(struct tr2_tbuf *tb); +void tr2_tbuf_utc_datetime(struct tr2_tbuf *tb); #endif /* TR2_TBUF_H */ diff --git a/trace2/tr2_tgt.h b/trace2/tr2_tgt.h index 297bb8ffbe..7b90469212 100644 --- a/trace2/tr2_tgt.h +++ b/trace2/tr2_tgt.h @@ -15,6 +15,7 @@ typedef void(tr2_tgt_term_t)(void); typedef void(tr2_tgt_evt_version_fl_t)(const char *file, int line); typedef void(tr2_tgt_evt_start_fl_t)(const char *file, int line, + uint64_t us_elapsed_absolute, const char **argv); typedef void(tr2_tgt_evt_exit_fl_t)(const char *file, int line, uint64_t us_elapsed_absolute, int code); diff --git a/trace2/tr2_tgt_event.c b/trace2/tr2_tgt_event.c index 1cf4f62441..c2852d1bd2 100644 --- a/trace2/tr2_tgt_event.c +++ b/trace2/tr2_tgt_event.c @@ -6,10 +6,11 @@ #include "trace2/tr2_dst.h" #include "trace2/tr2_tbuf.h" #include "trace2/tr2_sid.h" +#include "trace2/tr2_sysenv.h" #include "trace2/tr2_tgt.h" #include "trace2/tr2_tls.h" -static struct tr2_dst tr2dst_event = { "GIT_TR2_EVENT", 0, 0, 0 }; +static struct tr2_dst tr2dst_event = { TR2_SYSENV_EVENT, 0, 0, 0 }; /* * The version number of the JSON data generated by the EVENT target @@ -28,37 +29,36 @@ static struct tr2_dst tr2dst_event = { "GIT_TR2_EVENT", 0, 0, 0 }; * 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. + * event target. Use the TR2_SYSENV_EVENT_NESTING setting to increase + * region details in the event target. */ -#define TR2_ENVVAR_EVENT_NESTING "GIT_TR2_EVENT_NESTING" -static int tr2env_event_nesting_wanted = 2; +static int tr2env_event_max_nesting_levels = 2; /* - * Set this environment variable to true to omit the <time>, <file>, and + * Use the TR2_SYSENV_EVENT_BRIEF to omit the <time>, <file>, and * <line> fields from most events. */ -#define TR2_ENVVAR_EVENT_BRIEF "GIT_TR2_EVENT_BRIEF" -static int tr2env_event_brief; +static int tr2env_event_be_brief; static int fn_init(void) { int want = tr2_dst_trace_want(&tr2dst_event); - int want_nesting; + int max_nesting; int want_brief; - char *nesting; - char *brief; + const char *nesting; + const char *brief; if (!want) return want; - nesting = getenv(TR2_ENVVAR_EVENT_NESTING); - if (nesting && ((want_nesting = atoi(nesting)) > 0)) - tr2env_event_nesting_wanted = want_nesting; + nesting = tr2_sysenv_get(TR2_SYSENV_EVENT_NESTING); + if (nesting && *nesting && ((max_nesting = atoi(nesting)) > 0)) + tr2env_event_max_nesting_levels = max_nesting; - brief = getenv(TR2_ENVVAR_EVENT_BRIEF); - if (brief && ((want_brief = atoi(brief)) > 0)) - tr2env_event_brief = want_brief; + brief = tr2_sysenv_get(TR2_SYSENV_EVENT_BRIEF); + if (brief && *brief && + ((want_brief = git_parse_maybe_bool(brief)) != -1)) + tr2env_event_be_brief = want_brief; return want; } @@ -92,13 +92,13 @@ static void event_fmt_prepare(const char *event_name, const char *file, /* * In brief mode, only emit <time> on these 2 event types. */ - if (!tr2env_event_brief || !strcmp(event_name, "version") || + if (!tr2env_event_be_brief || !strcmp(event_name, "version") || !strcmp(event_name, "atexit")) { - tr2_tbuf_utc_time(&tb_now); + tr2_tbuf_utc_datetime_extended(&tb_now); jw_object_string(jw, "time", tb_now.buf); } - if (!tr2env_event_brief && file && *file) { + if (!tr2env_event_be_brief && file && *file) { jw_object_string(jw, "file", file); jw_object_intmax(jw, "line", line); } @@ -122,13 +122,16 @@ static void fn_version_fl(const char *file, int line) jw_release(&jw); } -static void fn_start_fl(const char *file, int line, const char **argv) +static void fn_start_fl(const char *file, int line, + uint64_t us_elapsed_absolute, const char **argv) { const char *event_name = "start"; struct json_writer jw = JSON_WRITER_INIT; + double t_abs = (double)us_elapsed_absolute / 1000000.0; jw_object_begin(&jw, 0); event_fmt_prepare(event_name, file, line, NULL, &jw); + jw_object_double(&jw, "t_abs", 6, t_abs); jw_object_inline_begin_array(&jw, "argv"); jw_array_argv(&jw, argv); jw_end(&jw); @@ -456,7 +459,7 @@ static void fn_region_enter_printf_va_fl(const char *file, int line, { const char *event_name = "region_enter"; struct tr2tls_thread_ctx *ctx = tr2tls_get_self(); - if (ctx->nr_open_regions <= tr2env_event_nesting_wanted) { + if (ctx->nr_open_regions <= tr2env_event_max_nesting_levels) { struct json_writer jw = JSON_WRITER_INIT; jw_object_begin(&jw, 0); @@ -481,7 +484,7 @@ static void fn_region_leave_printf_va_fl( { const char *event_name = "region_leave"; struct tr2tls_thread_ctx *ctx = tr2tls_get_self(); - if (ctx->nr_open_regions <= tr2env_event_nesting_wanted) { + if (ctx->nr_open_regions <= tr2env_event_max_nesting_levels) { struct json_writer jw = JSON_WRITER_INIT; double t_rel = (double)us_elapsed_region / 1000000.0; @@ -508,7 +511,7 @@ static void fn_data_fl(const char *file, int line, uint64_t us_elapsed_absolute, { const char *event_name = "data"; struct tr2tls_thread_ctx *ctx = tr2tls_get_self(); - if (ctx->nr_open_regions <= tr2env_event_nesting_wanted) { + if (ctx->nr_open_regions <= tr2env_event_max_nesting_levels) { struct json_writer jw = JSON_WRITER_INIT; double t_abs = (double)us_elapsed_absolute / 1000000.0; double t_rel = (double)us_elapsed_region / 1000000.0; @@ -536,7 +539,7 @@ static void fn_data_json_fl(const char *file, int line, { const char *event_name = "data_json"; struct tr2tls_thread_ctx *ctx = tr2tls_get_self(); - if (ctx->nr_open_regions <= tr2env_event_nesting_wanted) { + if (ctx->nr_open_regions <= tr2env_event_max_nesting_levels) { struct json_writer jw = JSON_WRITER_INIT; double t_abs = (double)us_elapsed_absolute / 1000000.0; double t_rel = (double)us_elapsed_region / 1000000.0; diff --git a/trace2/tr2_tgt_normal.c b/trace2/tr2_tgt_normal.c index 1a07d70abd..00b116d797 100644 --- a/trace2/tr2_tgt_normal.c +++ b/trace2/tr2_tgt_normal.c @@ -4,20 +4,20 @@ #include "quote.h" #include "version.h" #include "trace2/tr2_dst.h" +#include "trace2/tr2_sysenv.h" #include "trace2/tr2_tbuf.h" #include "trace2/tr2_tgt.h" #include "trace2/tr2_tls.h" -static struct tr2_dst tr2dst_normal = { "GIT_TR2", 0, 0, 0 }; +static struct tr2_dst tr2dst_normal = { TR2_SYSENV_NORMAL, 0, 0, 0 }; /* - * Set this environment variable to true to omit the "<time> <file>:<line>" + * Use the TR2_SYSENV_NORMAL_BRIEF setting to omit the "<time> <file>:<line>" * fields from each line written to the builtin normal target. * * Unit tests may want to use this to help with testing. */ -#define TR2_ENVVAR_NORMAL_BRIEF "GIT_TR2_BRIEF" -static int tr2env_normal_brief; +static int tr2env_normal_be_brief; #define TR2FMT_NORMAL_FL_WIDTH (50) @@ -25,15 +25,15 @@ static int fn_init(void) { int want = tr2_dst_trace_want(&tr2dst_normal); int want_brief; - char *brief; + const char *brief; if (!want) return want; - brief = getenv(TR2_ENVVAR_NORMAL_BRIEF); + brief = tr2_sysenv_get(TR2_SYSENV_NORMAL_BRIEF); if (brief && *brief && ((want_brief = git_parse_maybe_bool(brief)) != -1)) - tr2env_normal_brief = want_brief; + tr2env_normal_be_brief = want_brief; return want; } @@ -47,7 +47,7 @@ static void normal_fmt_prepare(const char *file, int line, struct strbuf *buf) { strbuf_setlen(buf, 0); - if (!tr2env_normal_brief) { + if (!tr2env_normal_be_brief) { struct tr2_tbuf tb_now; tr2_tbuf_local_time(&tb_now); @@ -81,7 +81,8 @@ static void fn_version_fl(const char *file, int line) strbuf_release(&buf_payload); } -static void fn_start_fl(const char *file, int line, const char **argv) +static void fn_start_fl(const char *file, int line, + uint64_t us_elapsed_absolute, const char **argv) { struct strbuf buf_payload = STRBUF_INIT; diff --git a/trace2/tr2_tgt_perf.c b/trace2/tr2_tgt_perf.c index 2a866d701b..ea0cbbe13e 100644 --- a/trace2/tr2_tgt_perf.c +++ b/trace2/tr2_tgt_perf.c @@ -6,20 +6,20 @@ #include "json-writer.h" #include "trace2/tr2_dst.h" #include "trace2/tr2_sid.h" +#include "trace2/tr2_sysenv.h" #include "trace2/tr2_tbuf.h" #include "trace2/tr2_tgt.h" #include "trace2/tr2_tls.h" -static struct tr2_dst tr2dst_perf = { "GIT_TR2_PERF", 0, 0, 0 }; +static struct tr2_dst tr2dst_perf = { TR2_SYSENV_PERF, 0, 0, 0 }; /* - * Set this environment variable to true to omit the "<time> <file>:<line>" + * Use TR2_SYSENV_PERF_BRIEF to omit the "<time> <file>:<line>" * fields from each line written to the builtin performance target. * * Unit tests may want to use this to help with testing. */ -#define TR2_ENVVAR_PERF_BRIEF "GIT_TR2_PERF_BRIEF" -static int tr2env_perf_brief; +static int tr2env_perf_be_brief; #define TR2FMT_PERF_FL_WIDTH (50) #define TR2FMT_PERF_MAX_EVENT_NAME (12) @@ -36,17 +36,17 @@ static int fn_init(void) { int want = tr2_dst_trace_want(&tr2dst_perf); int want_brief; - char *brief; + const char *brief; if (!want) return want; strbuf_addchars(&dots, '.', TR2_DOTS_BUFFER_SIZE); - brief = getenv(TR2_ENVVAR_PERF_BRIEF); + brief = tr2_sysenv_get(TR2_SYSENV_PERF_BRIEF); if (brief && *brief && ((want_brief = git_parse_maybe_bool(brief)) != -1)) - tr2env_perf_brief = want_brief; + tr2env_perf_be_brief = want_brief; return want; } @@ -77,7 +77,7 @@ static void perf_fmt_prepare(const char *event_name, strbuf_setlen(buf, 0); - if (!tr2env_perf_brief) { + if (!tr2env_perf_be_brief) { struct tr2_tbuf tb_now; tr2_tbuf_local_time(&tb_now); @@ -159,15 +159,16 @@ static void fn_version_fl(const char *file, int line) strbuf_release(&buf_payload); } -static void fn_start_fl(const char *file, int line, const char **argv) +static void fn_start_fl(const char *file, int line, + uint64_t us_elapsed_absolute, const char **argv) { const char *event_name = "start"; struct strbuf buf_payload = STRBUF_INIT; sq_quote_argv_pretty(&buf_payload, argv); - perf_io_write_fl(file, line, event_name, NULL, NULL, NULL, NULL, - &buf_payload); + perf_io_write_fl(file, line, event_name, NULL, &us_elapsed_absolute, + NULL, NULL, &buf_payload); strbuf_release(&buf_payload); } diff --git a/trace2/tr2_tls.c b/trace2/tr2_tls.c index 8e65b0361d..e76d8c5d92 100644 --- a/trace2/tr2_tls.c +++ b/trace2/tr2_tls.c @@ -10,16 +10,30 @@ #define TR2_REGION_NESTING_INITIAL_SIZE (100) static struct tr2tls_thread_ctx *tr2tls_thread_main; -static uint64_t tr2tls_us_start_main; +static uint64_t tr2tls_us_start_process; static pthread_mutex_t tr2tls_mutex; static pthread_key_t tr2tls_key; static int tr2_next_thread_id; /* modify under lock */ -struct tr2tls_thread_ctx *tr2tls_create_self(const char *thread_name) +void tr2tls_start_process_clock(void) +{ + if (tr2tls_us_start_process) + return; + + /* + * Keep the absolute start time of the process (i.e. the main + * process) in a fixed variable since other threads need to + * access it. This allows them to do that without a lock on + * main thread's array data (because of reallocs). + */ + tr2tls_us_start_process = getnanotime() / 1000; +} + +struct tr2tls_thread_ctx *tr2tls_create_self(const char *thread_name, + uint64_t us_thread_start) { - uint64_t us_now = getnanotime() / 1000; struct tr2tls_thread_ctx *ctx = xcalloc(1, sizeof(*ctx)); /* @@ -29,7 +43,7 @@ struct tr2tls_thread_ctx *tr2tls_create_self(const char *thread_name) */ ctx->alloc = TR2_REGION_NESTING_INITIAL_SIZE; ctx->array_us_start = (uint64_t *)xcalloc(ctx->alloc, sizeof(uint64_t)); - ctx->array_us_start[ctx->nr_open_regions++] = us_now; + ctx->array_us_start[ctx->nr_open_regions++] = us_thread_start; ctx->thread_id = tr2tls_locked_increment(&tr2_next_thread_id); @@ -55,7 +69,7 @@ struct tr2tls_thread_ctx *tr2tls_get_self(void) * here and silently continue. */ if (!ctx) - ctx = tr2tls_create_self("unknown"); + ctx = tr2tls_create_self("unknown", getnanotime() / 1000); return ctx; } @@ -124,22 +138,18 @@ uint64_t tr2tls_absolute_elapsed(uint64_t us) if (!tr2tls_thread_main) return 0; - return us - tr2tls_us_start_main; + return us - tr2tls_us_start_process; } void tr2tls_init(void) { + tr2tls_start_process_clock(); + pthread_key_create(&tr2tls_key, NULL); init_recursive_mutex(&tr2tls_mutex); - tr2tls_thread_main = tr2tls_create_self("main"); - /* - * Keep a copy of the absolute start time of the main thread - * in a fixed variable since other threads need to access it. - * This also eliminates the need to lock accesses to the main - * thread's array (because of reallocs). - */ - tr2tls_us_start_main = tr2tls_thread_main->array_us_start[0]; + tr2tls_thread_main = + tr2tls_create_self("main", tr2tls_us_start_process); } void tr2tls_release(void) diff --git a/trace2/tr2_tls.h b/trace2/tr2_tls.h index bb80e3f8e7..b1e327a928 100644 --- a/trace2/tr2_tls.h +++ b/trace2/tr2_tls.h @@ -31,7 +31,8 @@ struct tr2tls_thread_ctx { * In this and all following functions the term "self" refers to the * current thread. */ -struct tr2tls_thread_ctx *tr2tls_create_self(const char *thread_name); +struct tr2tls_thread_ctx *tr2tls_create_self(const char *thread_name, + uint64_t us_thread_start); /* * Get our TLS data. @@ -94,4 +95,9 @@ void tr2tls_release(void); */ int tr2tls_locked_increment(int *p); +/* + * Capture the process start time and do nothing else. + */ +void tr2tls_start_process_clock(void); + #endif /* TR2_TLS_H */ |