trace2: write to directory targets
authorJosh Steadmon <steadmon@google.com>
Thu, 21 Mar 2019 21:09:51 +0000 (14:09 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 22 Mar 2019 05:27:02 +0000 (14:27 +0900)
When the value of a trace2 environment variable is an absolute path
referring to an existing directory, write output to files (one per
process) underneath the given directory. Files will be named according
to the final component of the trace2 SID, followed by a counter to avoid
potential collisions.

This makes it more convenient to collect traces for every git invocation
by unconditionally setting the relevant trace2 envvar to a constant
directory name.

Signed-off-by: Josh Steadmon <steadmon@google.com>
Reviewed-by: Jeff Hostetler <jeffhost@microsoft.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Documentation/technical/api-trace2.txt
t/t0210-trace2-normal.sh
trace2/tr2_dst.c
index 2de565fa3d5a15c4a7179d73b112cd83de0beb09..d0948ba250933ed7e5c3f06bdf70cb7a1e6e5814 100644 (file)
@@ -109,6 +109,11 @@ values are recognized.
 
        Enables the target, opens and writes to the file in append mode.
 
+       If the target already exists and is a directory, the traces will be
+       written to files (one per process) underneath the given directory. They
+       will be named according to the last component of the SID (optionally
+       followed by a counter to avoid filename collisions).
+
 `af_unix:[<socket_type>:]<absolute-pathname>`::
 
        Enables the target, opens and writes to a Unix Domain Socket
index 03a0aedb1d2bd11e1621e048232e22f35494acd0..819430658be65dca58cc4f6f7aec0c2f4ecefb70 100755 (executable)
@@ -80,6 +80,21 @@ test_expect_success 'normal stream, return code 1' '
        test_cmp expect actual
 '
 
+test_expect_success 'automatic filename' '
+       test_when_finished "rm -r traces actual expect" &&
+       mkdir traces &&
+       GIT_TR2="$(pwd)/traces" test-tool trace2 001return 0 &&
+       perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <"$(ls traces/*)" >actual &&
+       cat >expect <<-EOF &&
+               version $V
+               start _EXE_ trace2 001return 0
+               cmd_name trace2 (trace2)
+               exit elapsed:_TIME_ code:0
+               atexit elapsed:_TIME_ code:0
+       EOF
+       test_cmp expect actual
+'
+
 # Verb 002exit
 #
 # Explicit exit(code) from within cmd_<verb> propagates <code>.
index fd490a43ad923d2c40c7f0139e71a3d2259f8075..c3d82ca6a4bd852ed175c0800f31ce08bfb32ea3 100644 (file)
@@ -1,5 +1,6 @@
 #include "cache.h"
 #include "trace2/tr2_dst.h"
+#include "trace2/tr2_sid.h"
 
 /*
  * If a Trace2 target cannot be opened for writing, we should issue a
  */
 #define TR2_ENVVAR_DST_DEBUG "GIT_TR2_DST_DEBUG"
 
+/*
+ * How many attempts we will make at creating an automatically-named trace file.
+ */
+#define MAX_AUTO_ATTEMPTS 10
+
 static int tr2_dst_want_warning(void)
 {
        static int tr2env_dst_debug = -1;
@@ -36,6 +42,55 @@ 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,
+                               dst->env_var_name, 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);
@@ -202,8 +257,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))