2006-09-03 00:23:48 +08:00
|
|
|
/*
|
|
|
|
* GIT - The information manager from hell
|
|
|
|
*
|
|
|
|
* Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
|
|
|
|
* Copyright (C) 2002-2004 Oswald Buddenhagen <ossi@users.sf.net>
|
|
|
|
* Copyright (C) 2004 Theodore Y. Ts'o <tytso@mit.edu>
|
|
|
|
* Copyright (C) 2006 Mike McCormack
|
|
|
|
* Copyright (C) 2006 Christian Couder
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "cache.h"
|
|
|
|
#include "quote.h"
|
|
|
|
|
2016-08-04 06:56:57 +08:00
|
|
|
/*
|
|
|
|
* "Normalize" a key argument by converting NULL to our trace_default,
|
|
|
|
* and otherwise passing through the value. All caller-facing functions
|
|
|
|
* should normalize their inputs in this way, though most get it
|
|
|
|
* for free by calling get_trace_fd() (directly or indirectly).
|
|
|
|
*/
|
|
|
|
static void normalize_trace_key(struct trace_key **key)
|
|
|
|
{
|
|
|
|
static struct trace_key trace_default = { "GIT_TRACE" };
|
|
|
|
if (!*key)
|
|
|
|
*key = &trace_default;
|
|
|
|
}
|
|
|
|
|
2011-02-24 22:28:41 +08:00
|
|
|
/* Get a trace file descriptor from "key" env variable. */
|
2014-07-12 08:00:06 +08:00
|
|
|
static int get_trace_fd(struct trace_key *key)
|
2006-09-03 00:23:48 +08:00
|
|
|
{
|
2014-07-12 08:00:06 +08:00
|
|
|
const char *trace;
|
|
|
|
|
2016-08-04 06:56:57 +08:00
|
|
|
normalize_trace_key(&key);
|
2014-07-12 08:00:06 +08:00
|
|
|
|
|
|
|
/* don't open twice */
|
|
|
|
if (key->initialized)
|
|
|
|
return key->fd;
|
|
|
|
|
|
|
|
trace = getenv(key->key);
|
2006-09-03 00:23:48 +08:00
|
|
|
|
2006-10-14 22:05:25 +08:00
|
|
|
if (!trace || !strcmp(trace, "") ||
|
|
|
|
!strcmp(trace, "0") || !strcasecmp(trace, "false"))
|
2014-07-12 08:00:06 +08:00
|
|
|
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)) {
|
2006-09-03 00:23:48 +08:00
|
|
|
int fd = open(trace, O_WRONLY | O_APPEND | O_CREAT, 0666);
|
|
|
|
if (fd == -1) {
|
2016-08-04 07:00:23 +08:00
|
|
|
warning("could not open '%s' for tracing: %s\n"
|
|
|
|
" Defaulting to tracing on stderr...",
|
2006-09-03 00:23:48 +08:00
|
|
|
trace, strerror(errno));
|
2014-07-12 08:00:06 +08:00
|
|
|
key->fd = STDERR_FILENO;
|
|
|
|
} else {
|
|
|
|
key->fd = fd;
|
|
|
|
key->need_close = 1;
|
2006-09-03 00:23:48 +08:00
|
|
|
}
|
2014-07-12 08:00:06 +08:00
|
|
|
} else {
|
2016-08-04 07:00:23 +08:00
|
|
|
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 /)\n"
|
|
|
|
" Defaulting to tracing on stderr...",
|
|
|
|
key->key, trace, key->key);
|
2014-07-12 08:00:06 +08:00
|
|
|
key->fd = STDERR_FILENO;
|
2006-09-03 00:23:48 +08:00
|
|
|
}
|
|
|
|
|
2014-07-12 08:00:06 +08:00
|
|
|
key->initialized = 1;
|
|
|
|
return key->fd;
|
|
|
|
}
|
2006-09-03 00:23:48 +08:00
|
|
|
|
2014-07-12 08:00:06 +08:00
|
|
|
void trace_disable(struct trace_key *key)
|
|
|
|
{
|
2016-08-04 06:56:57 +08:00
|
|
|
normalize_trace_key(&key);
|
|
|
|
|
2014-07-12 08:00:06 +08:00
|
|
|
if (key->need_close)
|
|
|
|
close(key->fd);
|
|
|
|
key->fd = 0;
|
|
|
|
key->initialized = 1;
|
|
|
|
key->need_close = 0;
|
2006-09-03 00:23:48 +08:00
|
|
|
}
|
|
|
|
|
2016-08-04 07:00:23 +08:00
|
|
|
static const char err_msg[] = "could not trace into fd given by "
|
2006-09-03 00:23:48 +08:00
|
|
|
"GIT_TRACE environment variable";
|
|
|
|
|
2014-07-12 08:05:03 +08:00
|
|
|
static int prepare_trace_line(const char *file, int line,
|
|
|
|
struct trace_key *key, struct strbuf *buf)
|
trace: add infrastructure to augment trace output with additional info
To be able to add a common prefix or suffix to all trace output (e.g.
a timestamp or file:line of the caller), factor out common setup and
cleanup tasks of the trace* functions.
When adding a common prefix, it makes sense that the output of each trace
call starts on a new line. Add '\n' in case the caller forgot.
Note that this explicitly limits trace output to line-by-line, it is no
longer possible to trace-print just part of a line. Until now, this was
just an implicit assumption (trace-printing part of a line worked, but
messed up the trace file if multiple threads or processes were involved).
Thread-safety / inter-process-safety is also the reason why we need to do
the prefixing and suffixing in memory rather than issuing multiple write()
calls. Write_or_whine_pipe() / xwrite() is atomic unless the size exceeds
MAX_IO_SIZE (8MB, see wrapper.c). In case of trace_strbuf, this costs an
additional string copy (which should be irrelevant for performance in light
of actual file IO).
While we're at it, rename trace_strbuf's 'buf' argument, which suggests
that the function is modifying the buffer. Trace_strbuf() currently is the
only trace API that can print arbitrary binary data (without barfing on
'%' or stopping at '\0'), so 'data' seems more appropriate.
Signed-off-by: Karsten Blees <blees@dcon.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-07-12 08:02:18 +08:00
|
|
|
{
|
2014-07-12 08:03:01 +08:00
|
|
|
static struct trace_key trace_bare = TRACE_KEY_INIT(BARE);
|
2014-07-12 08:03:50 +08:00
|
|
|
struct timeval tv;
|
|
|
|
struct tm tm;
|
|
|
|
time_t secs;
|
2014-07-12 08:03:01 +08:00
|
|
|
|
trace: add infrastructure to augment trace output with additional info
To be able to add a common prefix or suffix to all trace output (e.g.
a timestamp or file:line of the caller), factor out common setup and
cleanup tasks of the trace* functions.
When adding a common prefix, it makes sense that the output of each trace
call starts on a new line. Add '\n' in case the caller forgot.
Note that this explicitly limits trace output to line-by-line, it is no
longer possible to trace-print just part of a line. Until now, this was
just an implicit assumption (trace-printing part of a line worked, but
messed up the trace file if multiple threads or processes were involved).
Thread-safety / inter-process-safety is also the reason why we need to do
the prefixing and suffixing in memory rather than issuing multiple write()
calls. Write_or_whine_pipe() / xwrite() is atomic unless the size exceeds
MAX_IO_SIZE (8MB, see wrapper.c). In case of trace_strbuf, this costs an
additional string copy (which should be irrelevant for performance in light
of actual file IO).
While we're at it, rename trace_strbuf's 'buf' argument, which suggests
that the function is modifying the buffer. Trace_strbuf() currently is the
only trace API that can print arbitrary binary data (without barfing on
'%' or stopping at '\0'), so 'data' seems more appropriate.
Signed-off-by: Karsten Blees <blees@dcon.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-07-12 08:02:18 +08:00
|
|
|
if (!trace_want(key))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
set_try_to_free_routine(NULL); /* is never reset */
|
|
|
|
|
2014-07-12 08:03:01 +08:00
|
|
|
/* unit tests may want to disable additional trace output */
|
|
|
|
if (trace_want(&trace_bare))
|
|
|
|
return 1;
|
|
|
|
|
2014-07-12 08:03:50 +08:00
|
|
|
/* print current timestamp */
|
|
|
|
gettimeofday(&tv, NULL);
|
|
|
|
secs = tv.tv_sec;
|
|
|
|
localtime_r(&secs, &tm);
|
|
|
|
strbuf_addf(buf, "%02d:%02d:%02d.%06ld ", tm.tm_hour, tm.tm_min,
|
|
|
|
tm.tm_sec, (long) tv.tv_usec);
|
trace: add infrastructure to augment trace output with additional info
To be able to add a common prefix or suffix to all trace output (e.g.
a timestamp or file:line of the caller), factor out common setup and
cleanup tasks of the trace* functions.
When adding a common prefix, it makes sense that the output of each trace
call starts on a new line. Add '\n' in case the caller forgot.
Note that this explicitly limits trace output to line-by-line, it is no
longer possible to trace-print just part of a line. Until now, this was
just an implicit assumption (trace-printing part of a line worked, but
messed up the trace file if multiple threads or processes were involved).
Thread-safety / inter-process-safety is also the reason why we need to do
the prefixing and suffixing in memory rather than issuing multiple write()
calls. Write_or_whine_pipe() / xwrite() is atomic unless the size exceeds
MAX_IO_SIZE (8MB, see wrapper.c). In case of trace_strbuf, this costs an
additional string copy (which should be irrelevant for performance in light
of actual file IO).
While we're at it, rename trace_strbuf's 'buf' argument, which suggests
that the function is modifying the buffer. Trace_strbuf() currently is the
only trace API that can print arbitrary binary data (without barfing on
'%' or stopping at '\0'), so 'data' seems more appropriate.
Signed-off-by: Karsten Blees <blees@dcon.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-07-12 08:02:18 +08:00
|
|
|
|
2014-07-12 08:05:03 +08:00
|
|
|
#ifdef HAVE_VARIADIC_MACROS
|
|
|
|
/* print file:line */
|
|
|
|
strbuf_addf(buf, "%s:%d ", file, line);
|
|
|
|
/* align trace output (column 40 catches most files names in git) */
|
|
|
|
while (buf->len < 40)
|
|
|
|
strbuf_addch(buf, ' ');
|
|
|
|
#endif
|
|
|
|
|
trace: add infrastructure to augment trace output with additional info
To be able to add a common prefix or suffix to all trace output (e.g.
a timestamp or file:line of the caller), factor out common setup and
cleanup tasks of the trace* functions.
When adding a common prefix, it makes sense that the output of each trace
call starts on a new line. Add '\n' in case the caller forgot.
Note that this explicitly limits trace output to line-by-line, it is no
longer possible to trace-print just part of a line. Until now, this was
just an implicit assumption (trace-printing part of a line worked, but
messed up the trace file if multiple threads or processes were involved).
Thread-safety / inter-process-safety is also the reason why we need to do
the prefixing and suffixing in memory rather than issuing multiple write()
calls. Write_or_whine_pipe() / xwrite() is atomic unless the size exceeds
MAX_IO_SIZE (8MB, see wrapper.c). In case of trace_strbuf, this costs an
additional string copy (which should be irrelevant for performance in light
of actual file IO).
While we're at it, rename trace_strbuf's 'buf' argument, which suggests
that the function is modifying the buffer. Trace_strbuf() currently is the
only trace API that can print arbitrary binary data (without barfing on
'%' or stopping at '\0'), so 'data' seems more appropriate.
Signed-off-by: Karsten Blees <blees@dcon.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-07-12 08:02:18 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
trace: stop using write_or_whine_pipe()
The write_or_whine_pipe function does two things:
1. it checks for EPIPE and converts it into a signal death
2. it prints a message to stderr on error
The first thing does not help us, and actively hurts.
Generally we would simply die from SIGPIPE in this case,
unless somebody has taken the time to ignore SIGPIPE for the
whole process. And if they _did_ do that, it seems rather
silly for the trace code, which otherwise takes pains to
continue even in the face of errors (e.g., by not using
write_or_die!), to take down the whole process for one
specific type of error.
Nor does the second thing help us; it just makes it harder
to write our error message, because we have to feed bits of
it as an argument to write_or_whine_pipe(). Translators
never get to see the full message, and it's hard for us to
customize it.
Let's switch to just using write_in_full() and writing our
own error string. For now, the error is identical to what
write_or_whine_pipe() would say, but now that it's more
under our control, we can improve it in future patches.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-08-04 06:58:00 +08:00
|
|
|
static void trace_write(struct trace_key *key, const void *buf, unsigned len)
|
|
|
|
{
|
|
|
|
if (write_in_full(get_trace_fd(key), buf, len) < 0)
|
2016-08-05 15:56:00 +08:00
|
|
|
warning("%s: write error (%s)", err_msg, strerror(errno));
|
trace: stop using write_or_whine_pipe()
The write_or_whine_pipe function does two things:
1. it checks for EPIPE and converts it into a signal death
2. it prints a message to stderr on error
The first thing does not help us, and actively hurts.
Generally we would simply die from SIGPIPE in this case,
unless somebody has taken the time to ignore SIGPIPE for the
whole process. And if they _did_ do that, it seems rather
silly for the trace code, which otherwise takes pains to
continue even in the face of errors (e.g., by not using
write_or_die!), to take down the whole process for one
specific type of error.
Nor does the second thing help us; it just makes it harder
to write our error message, because we have to feed bits of
it as an argument to write_or_whine_pipe(). Translators
never get to see the full message, and it's hard for us to
customize it.
Let's switch to just using write_in_full() and writing our
own error string. For now, the error is identical to what
write_or_whine_pipe() would say, but now that it's more
under our control, we can improve it in future patches.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-08-04 06:58:00 +08:00
|
|
|
}
|
|
|
|
|
2015-06-17 01:23:20 +08:00
|
|
|
void trace_verbatim(struct trace_key *key, const void *buf, unsigned len)
|
|
|
|
{
|
|
|
|
if (!trace_want(key))
|
|
|
|
return;
|
trace: stop using write_or_whine_pipe()
The write_or_whine_pipe function does two things:
1. it checks for EPIPE and converts it into a signal death
2. it prints a message to stderr on error
The first thing does not help us, and actively hurts.
Generally we would simply die from SIGPIPE in this case,
unless somebody has taken the time to ignore SIGPIPE for the
whole process. And if they _did_ do that, it seems rather
silly for the trace code, which otherwise takes pains to
continue even in the face of errors (e.g., by not using
write_or_die!), to take down the whole process for one
specific type of error.
Nor does the second thing help us; it just makes it harder
to write our error message, because we have to feed bits of
it as an argument to write_or_whine_pipe(). Translators
never get to see the full message, and it's hard for us to
customize it.
Let's switch to just using write_in_full() and writing our
own error string. For now, the error is identical to what
write_or_whine_pipe() would say, but now that it's more
under our control, we can improve it in future patches.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-08-04 06:58:00 +08:00
|
|
|
trace_write(key, buf, len);
|
2015-06-17 01:23:20 +08:00
|
|
|
}
|
|
|
|
|
trace: add infrastructure to augment trace output with additional info
To be able to add a common prefix or suffix to all trace output (e.g.
a timestamp or file:line of the caller), factor out common setup and
cleanup tasks of the trace* functions.
When adding a common prefix, it makes sense that the output of each trace
call starts on a new line. Add '\n' in case the caller forgot.
Note that this explicitly limits trace output to line-by-line, it is no
longer possible to trace-print just part of a line. Until now, this was
just an implicit assumption (trace-printing part of a line worked, but
messed up the trace file if multiple threads or processes were involved).
Thread-safety / inter-process-safety is also the reason why we need to do
the prefixing and suffixing in memory rather than issuing multiple write()
calls. Write_or_whine_pipe() / xwrite() is atomic unless the size exceeds
MAX_IO_SIZE (8MB, see wrapper.c). In case of trace_strbuf, this costs an
additional string copy (which should be irrelevant for performance in light
of actual file IO).
While we're at it, rename trace_strbuf's 'buf' argument, which suggests
that the function is modifying the buffer. Trace_strbuf() currently is the
only trace API that can print arbitrary binary data (without barfing on
'%' or stopping at '\0'), so 'data' seems more appropriate.
Signed-off-by: Karsten Blees <blees@dcon.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-07-12 08:02:18 +08:00
|
|
|
static void print_trace_line(struct trace_key *key, struct strbuf *buf)
|
|
|
|
{
|
2014-12-13 03:16:38 +08:00
|
|
|
strbuf_complete_line(buf);
|
trace: stop using write_or_whine_pipe()
The write_or_whine_pipe function does two things:
1. it checks for EPIPE and converts it into a signal death
2. it prints a message to stderr on error
The first thing does not help us, and actively hurts.
Generally we would simply die from SIGPIPE in this case,
unless somebody has taken the time to ignore SIGPIPE for the
whole process. And if they _did_ do that, it seems rather
silly for the trace code, which otherwise takes pains to
continue even in the face of errors (e.g., by not using
write_or_die!), to take down the whole process for one
specific type of error.
Nor does the second thing help us; it just makes it harder
to write our error message, because we have to feed bits of
it as an argument to write_or_whine_pipe(). Translators
never get to see the full message, and it's hard for us to
customize it.
Let's switch to just using write_in_full() and writing our
own error string. For now, the error is identical to what
write_or_whine_pipe() would say, but now that it's more
under our control, we can improve it in future patches.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-08-04 06:58:00 +08:00
|
|
|
trace_write(key, buf->buf, buf->len);
|
trace: add infrastructure to augment trace output with additional info
To be able to add a common prefix or suffix to all trace output (e.g.
a timestamp or file:line of the caller), factor out common setup and
cleanup tasks of the trace* functions.
When adding a common prefix, it makes sense that the output of each trace
call starts on a new line. Add '\n' in case the caller forgot.
Note that this explicitly limits trace output to line-by-line, it is no
longer possible to trace-print just part of a line. Until now, this was
just an implicit assumption (trace-printing part of a line worked, but
messed up the trace file if multiple threads or processes were involved).
Thread-safety / inter-process-safety is also the reason why we need to do
the prefixing and suffixing in memory rather than issuing multiple write()
calls. Write_or_whine_pipe() / xwrite() is atomic unless the size exceeds
MAX_IO_SIZE (8MB, see wrapper.c). In case of trace_strbuf, this costs an
additional string copy (which should be irrelevant for performance in light
of actual file IO).
While we're at it, rename trace_strbuf's 'buf' argument, which suggests
that the function is modifying the buffer. Trace_strbuf() currently is the
only trace API that can print arbitrary binary data (without barfing on
'%' or stopping at '\0'), so 'data' seems more appropriate.
Signed-off-by: Karsten Blees <blees@dcon.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-07-12 08:02:18 +08:00
|
|
|
strbuf_release(buf);
|
|
|
|
}
|
|
|
|
|
2014-07-12 08:05:03 +08:00
|
|
|
static void trace_vprintf_fl(const char *file, int line, struct trace_key *key,
|
|
|
|
const char *format, va_list ap)
|
2006-09-03 00:23:48 +08:00
|
|
|
{
|
2011-02-26 13:08:53 +08:00
|
|
|
struct strbuf buf = STRBUF_INIT;
|
2006-09-03 00:23:48 +08:00
|
|
|
|
2014-07-12 08:05:03 +08:00
|
|
|
if (!prepare_trace_line(file, line, key, &buf))
|
2006-09-03 00:23:48 +08:00
|
|
|
return;
|
|
|
|
|
2014-06-11 15:57:23 +08:00
|
|
|
strbuf_vaddf(&buf, format, ap);
|
trace: add infrastructure to augment trace output with additional info
To be able to add a common prefix or suffix to all trace output (e.g.
a timestamp or file:line of the caller), factor out common setup and
cleanup tasks of the trace* functions.
When adding a common prefix, it makes sense that the output of each trace
call starts on a new line. Add '\n' in case the caller forgot.
Note that this explicitly limits trace output to line-by-line, it is no
longer possible to trace-print just part of a line. Until now, this was
just an implicit assumption (trace-printing part of a line worked, but
messed up the trace file if multiple threads or processes were involved).
Thread-safety / inter-process-safety is also the reason why we need to do
the prefixing and suffixing in memory rather than issuing multiple write()
calls. Write_or_whine_pipe() / xwrite() is atomic unless the size exceeds
MAX_IO_SIZE (8MB, see wrapper.c). In case of trace_strbuf, this costs an
additional string copy (which should be irrelevant for performance in light
of actual file IO).
While we're at it, rename trace_strbuf's 'buf' argument, which suggests
that the function is modifying the buffer. Trace_strbuf() currently is the
only trace API that can print arbitrary binary data (without barfing on
'%' or stopping at '\0'), so 'data' seems more appropriate.
Signed-off-by: Karsten Blees <blees@dcon.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-07-12 08:02:18 +08:00
|
|
|
print_trace_line(key, &buf);
|
2006-09-03 00:23:48 +08:00
|
|
|
}
|
|
|
|
|
2014-07-12 08:05:03 +08:00
|
|
|
static void trace_argv_vprintf_fl(const char *file, int line,
|
|
|
|
const char **argv, const char *format,
|
|
|
|
va_list ap)
|
2011-02-24 22:28:41 +08:00
|
|
|
{
|
2014-07-12 08:04:29 +08:00
|
|
|
struct strbuf buf = STRBUF_INIT;
|
2011-02-24 22:28:41 +08:00
|
|
|
|
2014-07-12 08:05:03 +08:00
|
|
|
if (!prepare_trace_line(file, line, NULL, &buf))
|
2014-07-12 08:04:29 +08:00
|
|
|
return;
|
|
|
|
|
|
|
|
strbuf_vaddf(&buf, format, ap);
|
|
|
|
|
|
|
|
sq_quote_argv(&buf, argv, 0);
|
|
|
|
print_trace_line(NULL, &buf);
|
2011-02-24 22:28:15 +08:00
|
|
|
}
|
|
|
|
|
2014-07-12 08:05:03 +08:00
|
|
|
void trace_strbuf_fl(const char *file, int line, struct trace_key *key,
|
|
|
|
const struct strbuf *data)
|
2011-02-24 22:29:50 +08:00
|
|
|
{
|
trace: add infrastructure to augment trace output with additional info
To be able to add a common prefix or suffix to all trace output (e.g.
a timestamp or file:line of the caller), factor out common setup and
cleanup tasks of the trace* functions.
When adding a common prefix, it makes sense that the output of each trace
call starts on a new line. Add '\n' in case the caller forgot.
Note that this explicitly limits trace output to line-by-line, it is no
longer possible to trace-print just part of a line. Until now, this was
just an implicit assumption (trace-printing part of a line worked, but
messed up the trace file if multiple threads or processes were involved).
Thread-safety / inter-process-safety is also the reason why we need to do
the prefixing and suffixing in memory rather than issuing multiple write()
calls. Write_or_whine_pipe() / xwrite() is atomic unless the size exceeds
MAX_IO_SIZE (8MB, see wrapper.c). In case of trace_strbuf, this costs an
additional string copy (which should be irrelevant for performance in light
of actual file IO).
While we're at it, rename trace_strbuf's 'buf' argument, which suggests
that the function is modifying the buffer. Trace_strbuf() currently is the
only trace API that can print arbitrary binary data (without barfing on
'%' or stopping at '\0'), so 'data' seems more appropriate.
Signed-off-by: Karsten Blees <blees@dcon.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-07-12 08:02:18 +08:00
|
|
|
struct strbuf buf = STRBUF_INIT;
|
|
|
|
|
2014-07-12 08:05:03 +08:00
|
|
|
if (!prepare_trace_line(file, line, key, &buf))
|
2011-02-24 22:29:50 +08:00
|
|
|
return;
|
|
|
|
|
trace: add infrastructure to augment trace output with additional info
To be able to add a common prefix or suffix to all trace output (e.g.
a timestamp or file:line of the caller), factor out common setup and
cleanup tasks of the trace* functions.
When adding a common prefix, it makes sense that the output of each trace
call starts on a new line. Add '\n' in case the caller forgot.
Note that this explicitly limits trace output to line-by-line, it is no
longer possible to trace-print just part of a line. Until now, this was
just an implicit assumption (trace-printing part of a line worked, but
messed up the trace file if multiple threads or processes were involved).
Thread-safety / inter-process-safety is also the reason why we need to do
the prefixing and suffixing in memory rather than issuing multiple write()
calls. Write_or_whine_pipe() / xwrite() is atomic unless the size exceeds
MAX_IO_SIZE (8MB, see wrapper.c). In case of trace_strbuf, this costs an
additional string copy (which should be irrelevant for performance in light
of actual file IO).
While we're at it, rename trace_strbuf's 'buf' argument, which suggests
that the function is modifying the buffer. Trace_strbuf() currently is the
only trace API that can print arbitrary binary data (without barfing on
'%' or stopping at '\0'), so 'data' seems more appropriate.
Signed-off-by: Karsten Blees <blees@dcon.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-07-12 08:02:18 +08:00
|
|
|
strbuf_addbuf(&buf, data);
|
|
|
|
print_trace_line(key, &buf);
|
2011-02-24 22:29:50 +08:00
|
|
|
}
|
|
|
|
|
2014-07-12 08:06:28 +08:00
|
|
|
static struct trace_key trace_perf_key = TRACE_KEY_INIT(PERFORMANCE);
|
|
|
|
|
|
|
|
static void trace_performance_vprintf_fl(const char *file, int line,
|
|
|
|
uint64_t nanos, const char *format,
|
|
|
|
va_list ap)
|
|
|
|
{
|
|
|
|
struct strbuf buf = STRBUF_INIT;
|
|
|
|
|
|
|
|
if (!prepare_trace_line(file, line, &trace_perf_key, &buf))
|
|
|
|
return;
|
|
|
|
|
|
|
|
strbuf_addf(&buf, "performance: %.9f s", (double) nanos / 1000000000);
|
|
|
|
|
|
|
|
if (format && *format) {
|
|
|
|
strbuf_addstr(&buf, ": ");
|
|
|
|
strbuf_vaddf(&buf, format, ap);
|
|
|
|
}
|
|
|
|
|
|
|
|
print_trace_line(&trace_perf_key, &buf);
|
|
|
|
}
|
|
|
|
|
2014-07-12 08:05:03 +08:00
|
|
|
#ifndef HAVE_VARIADIC_MACROS
|
|
|
|
|
2014-07-12 08:04:29 +08:00
|
|
|
void trace_printf(const char *format, ...)
|
2006-09-03 00:23:48 +08:00
|
|
|
{
|
nfv?asprintf are broken without va_copy, workaround them.
* drop nfasprintf.
* move nfvasprintf into imap-send.c back, and let it work on a 8k buffer,
and die() in case of overflow. It should be enough for imap commands, if
someone cares about imap-send, he's welcomed to fix it properly.
* replace nfvasprintf use in merge-recursive with a copy of the strbuf_addf
logic, it's one place, we'll live with it.
To ease the change, output_buffer string list is replaced with a strbuf ;)
* rework trace.c to call vsnprintf itself. It's used to format strerror()s
and git command names, it should never be more than a few octets long, let
it work on a 8k static buffer with vsnprintf or die loudly.
Signed-off-by: Pierre Habouzit <madcoder@debian.org>
2007-09-20 16:43:11 +08:00
|
|
|
va_list ap;
|
2014-06-11 15:57:23 +08:00
|
|
|
va_start(ap, format);
|
2014-07-12 08:05:03 +08:00
|
|
|
trace_vprintf_fl(NULL, 0, NULL, format, ap);
|
nfv?asprintf are broken without va_copy, workaround them.
* drop nfasprintf.
* move nfvasprintf into imap-send.c back, and let it work on a 8k buffer,
and die() in case of overflow. It should be enough for imap commands, if
someone cares about imap-send, he's welcomed to fix it properly.
* replace nfvasprintf use in merge-recursive with a copy of the strbuf_addf
logic, it's one place, we'll live with it.
To ease the change, output_buffer string list is replaced with a strbuf ;)
* rework trace.c to call vsnprintf itself. It's used to format strerror()s
and git command names, it should never be more than a few octets long, let
it work on a 8k static buffer with vsnprintf or die loudly.
Signed-off-by: Pierre Habouzit <madcoder@debian.org>
2007-09-20 16:43:11 +08:00
|
|
|
va_end(ap);
|
2014-07-12 08:04:29 +08:00
|
|
|
}
|
2006-09-03 00:23:48 +08:00
|
|
|
|
2014-07-12 08:04:29 +08:00
|
|
|
void trace_printf_key(struct trace_key *key, const char *format, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
va_start(ap, format);
|
2014-07-12 08:05:03 +08:00
|
|
|
trace_vprintf_fl(NULL, 0, key, format, ap);
|
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
|
|
|
|
void trace_argv_printf(const char **argv, const char *format, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
va_start(ap, format);
|
|
|
|
trace_argv_vprintf_fl(NULL, 0, argv, format, ap);
|
2014-07-12 08:04:29 +08:00
|
|
|
va_end(ap);
|
2006-09-03 00:23:48 +08:00
|
|
|
}
|
2010-11-26 23:31:57 +08:00
|
|
|
|
2014-09-07 04:53:03 +08:00
|
|
|
void trace_strbuf(struct trace_key *key, const struct strbuf *data)
|
2014-07-12 08:05:03 +08:00
|
|
|
{
|
|
|
|
trace_strbuf_fl(NULL, 0, key, data);
|
|
|
|
}
|
|
|
|
|
2014-07-12 08:06:28 +08:00
|
|
|
void trace_performance(uint64_t nanos, const char *format, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
va_start(ap, format);
|
|
|
|
trace_performance_vprintf_fl(NULL, 0, nanos, format, ap);
|
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
|
|
|
|
void trace_performance_since(uint64_t start, const char *format, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
va_start(ap, format);
|
|
|
|
trace_performance_vprintf_fl(NULL, 0, getnanotime() - start,
|
|
|
|
format, ap);
|
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
|
2014-07-12 08:05:03 +08:00
|
|
|
#else
|
|
|
|
|
|
|
|
void trace_printf_key_fl(const char *file, int line, struct trace_key *key,
|
|
|
|
const char *format, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
va_start(ap, format);
|
|
|
|
trace_vprintf_fl(file, line, key, format, ap);
|
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
|
|
|
|
void trace_argv_printf_fl(const char *file, int line, const char **argv,
|
|
|
|
const char *format, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
va_start(ap, format);
|
|
|
|
trace_argv_vprintf_fl(file, line, argv, format, ap);
|
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
|
2014-07-12 08:06:28 +08:00
|
|
|
void trace_performance_fl(const char *file, int line, uint64_t nanos,
|
|
|
|
const char *format, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
va_start(ap, format);
|
|
|
|
trace_performance_vprintf_fl(file, line, nanos, format, ap);
|
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
|
2014-07-12 08:05:03 +08:00
|
|
|
#endif /* HAVE_VARIADIC_MACROS */
|
|
|
|
|
|
|
|
|
2010-11-26 23:31:57 +08:00
|
|
|
static const char *quote_crnl(const char *path)
|
|
|
|
{
|
2015-09-25 05:05:54 +08:00
|
|
|
static struct strbuf new_path = STRBUF_INIT;
|
2010-11-26 23:31:57 +08:00
|
|
|
|
|
|
|
if (!path)
|
|
|
|
return NULL;
|
|
|
|
|
2015-09-25 05:05:54 +08:00
|
|
|
strbuf_reset(&new_path);
|
|
|
|
|
|
|
|
while (*path) {
|
|
|
|
switch (*path) {
|
|
|
|
case '\\': strbuf_addstr(&new_path, "\\\\"); break;
|
|
|
|
case '\n': strbuf_addstr(&new_path, "\\n"); break;
|
|
|
|
case '\r': strbuf_addstr(&new_path, "\\r"); break;
|
2010-11-26 23:31:57 +08:00
|
|
|
default:
|
2015-09-25 05:05:54 +08:00
|
|
|
strbuf_addch(&new_path, *path);
|
2010-11-26 23:31:57 +08:00
|
|
|
}
|
2015-09-25 05:05:54 +08:00
|
|
|
path++;
|
2010-11-26 23:31:57 +08:00
|
|
|
}
|
2015-09-25 05:05:54 +08:00
|
|
|
return new_path.buf;
|
2010-11-26 23:31:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* FIXME: move prefix to startup_info struct and get rid of this arg */
|
|
|
|
void trace_repo_setup(const char *prefix)
|
|
|
|
{
|
2014-07-12 08:00:06 +08:00
|
|
|
static struct trace_key key = TRACE_KEY_INIT(SETUP);
|
2011-01-06 08:30:01 +08:00
|
|
|
const char *git_work_tree;
|
2014-07-29 02:30:39 +08:00
|
|
|
char *cwd;
|
2010-11-26 23:31:57 +08:00
|
|
|
|
2014-07-12 08:00:06 +08:00
|
|
|
if (!trace_want(&key))
|
2010-11-26 23:31:57 +08:00
|
|
|
return;
|
|
|
|
|
2014-07-29 02:30:39 +08:00
|
|
|
cwd = xgetcwd();
|
2010-11-26 23:31:57 +08:00
|
|
|
|
2011-01-06 08:30:01 +08:00
|
|
|
if (!(git_work_tree = get_git_work_tree()))
|
|
|
|
git_work_tree = "(null)";
|
|
|
|
|
|
|
|
if (!prefix)
|
|
|
|
prefix = "(null)";
|
|
|
|
|
2014-07-12 08:00:06 +08:00
|
|
|
trace_printf_key(&key, "setup: git_dir: %s\n", quote_crnl(get_git_dir()));
|
2014-11-30 16:24:44 +08:00
|
|
|
trace_printf_key(&key, "setup: git_common_dir: %s\n", quote_crnl(get_git_common_dir()));
|
2014-07-12 08:00:06 +08:00
|
|
|
trace_printf_key(&key, "setup: worktree: %s\n", quote_crnl(git_work_tree));
|
|
|
|
trace_printf_key(&key, "setup: cwd: %s\n", quote_crnl(cwd));
|
|
|
|
trace_printf_key(&key, "setup: prefix: %s\n", quote_crnl(prefix));
|
2014-07-29 02:30:39 +08:00
|
|
|
|
|
|
|
free(cwd);
|
2010-11-26 23:31:57 +08:00
|
|
|
}
|
2011-02-24 22:28:59 +08:00
|
|
|
|
2014-07-12 08:00:06 +08:00
|
|
|
int trace_want(struct trace_key *key)
|
2011-02-24 22:28:59 +08:00
|
|
|
{
|
2014-07-12 08:00:06 +08:00
|
|
|
return !!get_trace_fd(key);
|
2011-02-24 22:28:59 +08:00
|
|
|
}
|
2014-07-12 08:05:42 +08:00
|
|
|
|
2015-01-09 04:00:56 +08:00
|
|
|
#if defined(HAVE_CLOCK_GETTIME) && defined(HAVE_CLOCK_MONOTONIC)
|
2014-07-12 08:05:42 +08:00
|
|
|
|
|
|
|
static inline uint64_t highres_nanos(void)
|
|
|
|
{
|
|
|
|
struct timespec ts;
|
|
|
|
if (clock_gettime(CLOCK_MONOTONIC, &ts))
|
|
|
|
return 0;
|
|
|
|
return (uint64_t) ts.tv_sec * 1000000000 + ts.tv_nsec;
|
|
|
|
}
|
|
|
|
|
|
|
|
#elif defined (GIT_WINDOWS_NATIVE)
|
|
|
|
|
|
|
|
static inline uint64_t highres_nanos(void)
|
|
|
|
{
|
|
|
|
static uint64_t high_ns, scaled_low_ns;
|
|
|
|
static int scale;
|
|
|
|
LARGE_INTEGER cnt;
|
|
|
|
|
|
|
|
if (!scale) {
|
|
|
|
if (!QueryPerformanceFrequency(&cnt))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* high_ns = number of ns per cnt.HighPart */
|
|
|
|
high_ns = (1000000000LL << 32) / (uint64_t) cnt.QuadPart;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Number of ns per cnt.LowPart is 10^9 / frequency (or
|
|
|
|
* high_ns >> 32). For maximum precision, we scale this factor
|
|
|
|
* so that it just fits within 32 bit (i.e. won't overflow if
|
|
|
|
* multiplied with cnt.LowPart).
|
|
|
|
*/
|
|
|
|
scaled_low_ns = high_ns;
|
|
|
|
scale = 32;
|
|
|
|
while (scaled_low_ns >= 0x100000000LL) {
|
|
|
|
scaled_low_ns >>= 1;
|
|
|
|
scale--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* if QPF worked on initialization, we expect QPC to work as well */
|
|
|
|
QueryPerformanceCounter(&cnt);
|
|
|
|
|
|
|
|
return (high_ns * cnt.HighPart) +
|
|
|
|
((scaled_low_ns * cnt.LowPart) >> scale);
|
|
|
|
}
|
|
|
|
|
|
|
|
#else
|
|
|
|
# define highres_nanos() 0
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static inline uint64_t gettimeofday_nanos(void)
|
|
|
|
{
|
|
|
|
struct timeval tv;
|
|
|
|
gettimeofday(&tv, NULL);
|
|
|
|
return (uint64_t) tv.tv_sec * 1000000000 + tv.tv_usec * 1000;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns nanoseconds since the epoch (01/01/1970), for performance tracing
|
|
|
|
* (i.e. favoring high precision over wall clock time accuracy).
|
|
|
|
*/
|
2014-09-28 15:50:26 +08:00
|
|
|
uint64_t getnanotime(void)
|
2014-07-12 08:05:42 +08:00
|
|
|
{
|
|
|
|
static uint64_t offset;
|
|
|
|
if (offset > 1) {
|
|
|
|
/* initialization succeeded, return offset + high res time */
|
|
|
|
return offset + highres_nanos();
|
|
|
|
} else if (offset == 1) {
|
|
|
|
/* initialization failed, fall back to gettimeofday */
|
|
|
|
return gettimeofday_nanos();
|
|
|
|
} else {
|
|
|
|
/* initialize offset if high resolution timer works */
|
|
|
|
uint64_t now = gettimeofday_nanos();
|
|
|
|
uint64_t highres = highres_nanos();
|
|
|
|
if (highres)
|
|
|
|
offset = now - highres;
|
|
|
|
else
|
|
|
|
offset = 1;
|
|
|
|
return now;
|
|
|
|
}
|
|
|
|
}
|
2014-07-12 08:07:01 +08:00
|
|
|
|
|
|
|
static uint64_t command_start_time;
|
|
|
|
static struct strbuf command_line = STRBUF_INIT;
|
|
|
|
|
|
|
|
static void print_command_performance_atexit(void)
|
|
|
|
{
|
|
|
|
trace_performance_since(command_start_time, "git command:%s",
|
|
|
|
command_line.buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
void trace_command_performance(const char **argv)
|
|
|
|
{
|
|
|
|
if (!trace_want(&trace_perf_key))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!command_start_time)
|
|
|
|
atexit(print_command_performance_atexit);
|
|
|
|
|
|
|
|
strbuf_reset(&command_line);
|
|
|
|
sq_quote_argv(&command_line, argv, 0);
|
|
|
|
command_start_time = getnanotime();
|
|
|
|
}
|