trace.con commit Merge branch 'rs/use-argv-array-in-child-process' (0956eaa)
   1/*
   2 * GIT - The information manager from hell
   3 *
   4 * Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
   5 * Copyright (C) 2002-2004 Oswald Buddenhagen <ossi@users.sf.net>
   6 * Copyright (C) 2004 Theodore Y. Ts'o <tytso@mit.edu>
   7 * Copyright (C) 2006 Mike McCormack
   8 * Copyright (C) 2006 Christian Couder
   9 *
  10 *  This program is free software; you can redistribute it and/or modify
  11 *  it under the terms of the GNU General Public License as published by
  12 *  the Free Software Foundation; either version 2 of the License, or
  13 *  (at your option) any later version.
  14 *
  15 *  This program is distributed in the hope that it will be useful,
  16 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  17 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18 *  GNU General Public License for more details.
  19 *
  20 *  You should have received a copy of the GNU General Public License
  21 *  along with this program; if not, see <http://www.gnu.org/licenses/>.
  22 */
  23
  24#include "cache.h"
  25#include "quote.h"
  26
  27struct trace_key trace_default_key = { "GIT_TRACE", 0, 0, 0 };
  28struct trace_key trace_perf_key = TRACE_KEY_INIT(PERFORMANCE);
  29
  30/* Get a trace file descriptor from "key" env variable. */
  31static int get_trace_fd(struct trace_key *key)
  32{
  33        const char *trace;
  34
  35        /* don't open twice */
  36        if (key->initialized)
  37                return key->fd;
  38
  39        trace = getenv(key->key);
  40
  41        if (!trace || !strcmp(trace, "") ||
  42            !strcmp(trace, "0") || !strcasecmp(trace, "false"))
  43                key->fd = 0;
  44        else if (!strcmp(trace, "1") || !strcasecmp(trace, "true"))
  45                key->fd = STDERR_FILENO;
  46        else if (strlen(trace) == 1 && isdigit(*trace))
  47                key->fd = atoi(trace);
  48        else if (is_absolute_path(trace)) {
  49                int fd = open(trace, O_WRONLY | O_APPEND | O_CREAT, 0666);
  50                if (fd == -1) {
  51                        warning("could not open '%s' for tracing: %s",
  52                                trace, strerror(errno));
  53                        trace_disable(key);
  54                } else {
  55                        key->fd = fd;
  56                        key->need_close = 1;
  57                }
  58        } else {
  59                warning("unknown trace value for '%s': %s\n"
  60                        "         If you want to trace into a file, then please set %s\n"
  61                        "         to an absolute pathname (starting with /)",
  62                        key->key, trace, key->key);
  63                trace_disable(key);
  64        }
  65
  66        key->initialized = 1;
  67        return key->fd;
  68}
  69
  70void trace_disable(struct trace_key *key)
  71{
  72        if (key->need_close)
  73                close(key->fd);
  74        key->fd = 0;
  75        key->initialized = 1;
  76        key->need_close = 0;
  77}
  78
  79static int prepare_trace_line(const char *file, int line,
  80                              struct trace_key *key, struct strbuf *buf)
  81{
  82        static struct trace_key trace_bare = TRACE_KEY_INIT(BARE);
  83        struct timeval tv;
  84        struct tm tm;
  85        time_t secs;
  86
  87        if (!trace_want(key))
  88                return 0;
  89
  90        set_try_to_free_routine(NULL);  /* is never reset */
  91
  92        /* unit tests may want to disable additional trace output */
  93        if (trace_want(&trace_bare))
  94                return 1;
  95
  96        /* print current timestamp */
  97        gettimeofday(&tv, NULL);
  98        secs = tv.tv_sec;
  99        localtime_r(&secs, &tm);
 100        strbuf_addf(buf, "%02d:%02d:%02d.%06ld ", tm.tm_hour, tm.tm_min,
 101                    tm.tm_sec, (long) tv.tv_usec);
 102
 103#ifdef HAVE_VARIADIC_MACROS
 104        /* print file:line */
 105        strbuf_addf(buf, "%s:%d ", file, line);
 106        /* align trace output (column 40 catches most files names in git) */
 107        while (buf->len < 40)
 108                strbuf_addch(buf, ' ');
 109#endif
 110
 111        return 1;
 112}
 113
 114static void trace_write(struct trace_key *key, const void *buf, unsigned len)
 115{
 116        if (write_in_full(get_trace_fd(key), buf, len) < 0) {
 117                warning("unable to write trace for %s: %s",
 118                        key->key, strerror(errno));
 119                trace_disable(key);
 120        }
 121}
 122
 123void trace_verbatim(struct trace_key *key, const void *buf, unsigned len)
 124{
 125        if (!trace_want(key))
 126                return;
 127        trace_write(key, buf, len);
 128}
 129
 130static void print_trace_line(struct trace_key *key, struct strbuf *buf)
 131{
 132        strbuf_complete_line(buf);
 133        trace_write(key, buf->buf, buf->len);
 134        strbuf_release(buf);
 135}
 136
 137static void trace_vprintf_fl(const char *file, int line, struct trace_key *key,
 138                             const char *format, va_list ap)
 139{
 140        struct strbuf buf = STRBUF_INIT;
 141
 142        if (!prepare_trace_line(file, line, key, &buf))
 143                return;
 144
 145        strbuf_vaddf(&buf, format, ap);
 146        print_trace_line(key, &buf);
 147}
 148
 149static void trace_argv_vprintf_fl(const char *file, int line,
 150                                  const char **argv, const char *format,
 151                                  va_list ap)
 152{
 153        struct strbuf buf = STRBUF_INIT;
 154
 155        if (!prepare_trace_line(file, line, &trace_default_key, &buf))
 156                return;
 157
 158        strbuf_vaddf(&buf, format, ap);
 159
 160        sq_quote_argv(&buf, argv, 0);
 161        print_trace_line(&trace_default_key, &buf);
 162}
 163
 164void trace_strbuf_fl(const char *file, int line, struct trace_key *key,
 165                     const struct strbuf *data)
 166{
 167        struct strbuf buf = STRBUF_INIT;
 168
 169        if (!prepare_trace_line(file, line, key, &buf))
 170                return;
 171
 172        strbuf_addbuf(&buf, data);
 173        print_trace_line(key, &buf);
 174}
 175
 176static void trace_performance_vprintf_fl(const char *file, int line,
 177                                         uint64_t nanos, const char *format,
 178                                         va_list ap)
 179{
 180        struct strbuf buf = STRBUF_INIT;
 181
 182        if (!prepare_trace_line(file, line, &trace_perf_key, &buf))
 183                return;
 184
 185        strbuf_addf(&buf, "performance: %.9f s", (double) nanos / 1000000000);
 186
 187        if (format && *format) {
 188                strbuf_addstr(&buf, ": ");
 189                strbuf_vaddf(&buf, format, ap);
 190        }
 191
 192        print_trace_line(&trace_perf_key, &buf);
 193}
 194
 195#ifndef HAVE_VARIADIC_MACROS
 196
 197void trace_printf(const char *format, ...)
 198{
 199        va_list ap;
 200        va_start(ap, format);
 201        trace_vprintf_fl(NULL, 0, &trace_default_key, format, ap);
 202        va_end(ap);
 203}
 204
 205void trace_printf_key(struct trace_key *key, const char *format, ...)
 206{
 207        va_list ap;
 208        va_start(ap, format);
 209        trace_vprintf_fl(NULL, 0, key, format, ap);
 210        va_end(ap);
 211}
 212
 213void trace_argv_printf(const char **argv, const char *format, ...)
 214{
 215        va_list ap;
 216        va_start(ap, format);
 217        trace_argv_vprintf_fl(NULL, 0, argv, format, ap);
 218        va_end(ap);
 219}
 220
 221void trace_strbuf(struct trace_key *key, const struct strbuf *data)
 222{
 223        trace_strbuf_fl(NULL, 0, key, data);
 224}
 225
 226void trace_performance(uint64_t nanos, const char *format, ...)
 227{
 228        va_list ap;
 229        va_start(ap, format);
 230        trace_performance_vprintf_fl(NULL, 0, nanos, format, ap);
 231        va_end(ap);
 232}
 233
 234void trace_performance_since(uint64_t start, const char *format, ...)
 235{
 236        va_list ap;
 237        va_start(ap, format);
 238        trace_performance_vprintf_fl(NULL, 0, getnanotime() - start,
 239                                     format, ap);
 240        va_end(ap);
 241}
 242
 243#else
 244
 245void trace_printf_key_fl(const char *file, int line, struct trace_key *key,
 246                         const char *format, ...)
 247{
 248        va_list ap;
 249        va_start(ap, format);
 250        trace_vprintf_fl(file, line, key, format, ap);
 251        va_end(ap);
 252}
 253
 254void trace_argv_printf_fl(const char *file, int line, const char **argv,
 255                          const char *format, ...)
 256{
 257        va_list ap;
 258        va_start(ap, format);
 259        trace_argv_vprintf_fl(file, line, argv, format, ap);
 260        va_end(ap);
 261}
 262
 263void trace_performance_fl(const char *file, int line, uint64_t nanos,
 264                              const char *format, ...)
 265{
 266        va_list ap;
 267        va_start(ap, format);
 268        trace_performance_vprintf_fl(file, line, nanos, format, ap);
 269        va_end(ap);
 270}
 271
 272#endif /* HAVE_VARIADIC_MACROS */
 273
 274
 275static const char *quote_crnl(const char *path)
 276{
 277        static struct strbuf new_path = STRBUF_INIT;
 278
 279        if (!path)
 280                return NULL;
 281
 282        strbuf_reset(&new_path);
 283
 284        while (*path) {
 285                switch (*path) {
 286                case '\\': strbuf_addstr(&new_path, "\\\\"); break;
 287                case '\n': strbuf_addstr(&new_path, "\\n"); break;
 288                case '\r': strbuf_addstr(&new_path, "\\r"); break;
 289                default:
 290                        strbuf_addch(&new_path, *path);
 291                }
 292                path++;
 293        }
 294        return new_path.buf;
 295}
 296
 297/* FIXME: move prefix to startup_info struct and get rid of this arg */
 298void trace_repo_setup(const char *prefix)
 299{
 300        static struct trace_key key = TRACE_KEY_INIT(SETUP);
 301        const char *git_work_tree;
 302        char *cwd;
 303
 304        if (!trace_want(&key))
 305                return;
 306
 307        cwd = xgetcwd();
 308
 309        if (!(git_work_tree = get_git_work_tree()))
 310                git_work_tree = "(null)";
 311
 312        if (!prefix)
 313                prefix = "(null)";
 314
 315        trace_printf_key(&key, "setup: git_dir: %s\n", quote_crnl(get_git_dir()));
 316        trace_printf_key(&key, "setup: git_common_dir: %s\n", quote_crnl(get_git_common_dir()));
 317        trace_printf_key(&key, "setup: worktree: %s\n", quote_crnl(git_work_tree));
 318        trace_printf_key(&key, "setup: cwd: %s\n", quote_crnl(cwd));
 319        trace_printf_key(&key, "setup: prefix: %s\n", quote_crnl(prefix));
 320
 321        free(cwd);
 322}
 323
 324int trace_want(struct trace_key *key)
 325{
 326        return !!get_trace_fd(key);
 327}
 328
 329#if defined(HAVE_CLOCK_GETTIME) && defined(HAVE_CLOCK_MONOTONIC)
 330
 331static inline uint64_t highres_nanos(void)
 332{
 333        struct timespec ts;
 334        if (clock_gettime(CLOCK_MONOTONIC, &ts))
 335                return 0;
 336        return (uint64_t) ts.tv_sec * 1000000000 + ts.tv_nsec;
 337}
 338
 339#elif defined (GIT_WINDOWS_NATIVE)
 340
 341static inline uint64_t highres_nanos(void)
 342{
 343        static uint64_t high_ns, scaled_low_ns;
 344        static int scale;
 345        LARGE_INTEGER cnt;
 346
 347        if (!scale) {
 348                if (!QueryPerformanceFrequency(&cnt))
 349                        return 0;
 350
 351                /* high_ns = number of ns per cnt.HighPart */
 352                high_ns = (1000000000LL << 32) / (uint64_t) cnt.QuadPart;
 353
 354                /*
 355                 * Number of ns per cnt.LowPart is 10^9 / frequency (or
 356                 * high_ns >> 32). For maximum precision, we scale this factor
 357                 * so that it just fits within 32 bit (i.e. won't overflow if
 358                 * multiplied with cnt.LowPart).
 359                 */
 360                scaled_low_ns = high_ns;
 361                scale = 32;
 362                while (scaled_low_ns >= 0x100000000LL) {
 363                        scaled_low_ns >>= 1;
 364                        scale--;
 365                }
 366        }
 367
 368        /* if QPF worked on initialization, we expect QPC to work as well */
 369        QueryPerformanceCounter(&cnt);
 370
 371        return (high_ns * cnt.HighPart) +
 372               ((scaled_low_ns * cnt.LowPart) >> scale);
 373}
 374
 375#else
 376# define highres_nanos() 0
 377#endif
 378
 379static inline uint64_t gettimeofday_nanos(void)
 380{
 381        struct timeval tv;
 382        gettimeofday(&tv, NULL);
 383        return (uint64_t) tv.tv_sec * 1000000000 + tv.tv_usec * 1000;
 384}
 385
 386/*
 387 * Returns nanoseconds since the epoch (01/01/1970), for performance tracing
 388 * (i.e. favoring high precision over wall clock time accuracy).
 389 */
 390uint64_t getnanotime(void)
 391{
 392        static uint64_t offset;
 393        if (offset > 1) {
 394                /* initialization succeeded, return offset + high res time */
 395                return offset + highres_nanos();
 396        } else if (offset == 1) {
 397                /* initialization failed, fall back to gettimeofday */
 398                return gettimeofday_nanos();
 399        } else {
 400                /* initialize offset if high resolution timer works */
 401                uint64_t now = gettimeofday_nanos();
 402                uint64_t highres = highres_nanos();
 403                if (highres)
 404                        offset = now - highres;
 405                else
 406                        offset = 1;
 407                return now;
 408        }
 409}
 410
 411static uint64_t command_start_time;
 412static struct strbuf command_line = STRBUF_INIT;
 413
 414static void print_command_performance_atexit(void)
 415{
 416        trace_performance_since(command_start_time, "git command:%s",
 417                                command_line.buf);
 418}
 419
 420void trace_command_performance(const char **argv)
 421{
 422        if (!trace_want(&trace_perf_key))
 423                return;
 424
 425        if (!command_start_time)
 426                atexit(print_command_performance_atexit);
 427
 428        strbuf_reset(&command_line);
 429        sq_quote_argv(&command_line, argv, 0);
 430        command_start_time = getnanotime();
 431}