Merge branch 'kb/perf-trace'
* kb/perf-trace: api-trace.txt: add trace API documentation progress: simplify performance measurement by using getnanotime() wt-status: simplify performance measurement by using getnanotime() git: add performance tracing for git's main() function to debug scripts trace: add trace_performance facility to debug performance issues trace: add high resolution timer function to debug performance issues trace: add 'file:line' to all trace output trace: move code around, in preparation to file:line output trace: add current timestamp to all trace output trace: disable additional trace output for unit tests trace: add infrastructure to augment trace output with additional info sha1_file: change GIT_TRACE_PACK_ACCESS logging to use trace API Documentation/git.txt: improve documentation of 'GIT_TRACE*' variables trace: improve trace performance trace: remove redundant printf format attribute trace: consistently name the format parameter trace: move trace declarations from cache.h to new trace.hmaint
commit
9f2de9c121
|
@ -906,31 +906,54 @@ for further details.
|
||||||
based on whether stdout appears to be redirected to a file or not.
|
based on whether stdout appears to be redirected to a file or not.
|
||||||
|
|
||||||
'GIT_TRACE'::
|
'GIT_TRACE'::
|
||||||
If this variable is set to "1", "2" or "true" (comparison
|
Enables general trace messages, e.g. alias expansion, built-in
|
||||||
is case insensitive), Git will print `trace:` messages on
|
command execution and external command execution.
|
||||||
stderr telling about alias expansion, built-in command
|
+
|
||||||
execution and external command execution.
|
If this variable is set to "1", "2" or "true" (comparison
|
||||||
If this variable is set to an integer value greater than 1
|
is case insensitive), trace messages will be printed to
|
||||||
and lower than 10 (strictly) then Git will interpret this
|
stderr.
|
||||||
value as an open file descriptor and will try to write the
|
+
|
||||||
trace messages into this file descriptor.
|
If the variable is set to an integer value greater than 2
|
||||||
Alternatively, if this variable is set to an absolute path
|
and lower than 10 (strictly) then Git will interpret this
|
||||||
(starting with a '/' character), Git will interpret this
|
value as an open file descriptor and will try to write the
|
||||||
as a file path and will try to write the trace messages
|
trace messages into this file descriptor.
|
||||||
into it.
|
+
|
||||||
|
Alternatively, if the variable is set to an absolute path
|
||||||
|
(starting with a '/' character), Git will interpret this
|
||||||
|
as a file path and will try to write the trace messages
|
||||||
|
into it.
|
||||||
|
+
|
||||||
|
Unsetting the variable, or setting it to empty, "0" or
|
||||||
|
"false" (case insensitive) disables trace messages.
|
||||||
|
|
||||||
'GIT_TRACE_PACK_ACCESS'::
|
'GIT_TRACE_PACK_ACCESS'::
|
||||||
If this variable is set to a path, a file will be created at
|
Enables trace messages for all accesses to any packs. For each
|
||||||
the given path logging all accesses to any packs. For each
|
|
||||||
access, the pack file name and an offset in the pack is
|
access, the pack file name and an offset in the pack is
|
||||||
recorded. This may be helpful for troubleshooting some
|
recorded. This may be helpful for troubleshooting some
|
||||||
pack-related performance problems.
|
pack-related performance problems.
|
||||||
|
See 'GIT_TRACE' for available trace output options.
|
||||||
|
|
||||||
'GIT_TRACE_PACKET'::
|
'GIT_TRACE_PACKET'::
|
||||||
If this variable is set, it shows a trace of all packets
|
Enables trace messages for all packets coming in or out of a
|
||||||
coming in or out of a given program. This can help with
|
given program. This can help with debugging object negotiation
|
||||||
debugging object negotiation or other protocol issues. Tracing
|
or other protocol issues. Tracing is turned off at a packet
|
||||||
is turned off at a packet starting with "PACK".
|
starting with "PACK".
|
||||||
|
See 'GIT_TRACE' for available trace output options.
|
||||||
|
|
||||||
|
'GIT_TRACE_PERFORMANCE'::
|
||||||
|
Enables performance related trace messages, e.g. total execution
|
||||||
|
time of each Git command.
|
||||||
|
See 'GIT_TRACE' for available trace output options.
|
||||||
|
|
||||||
|
'GIT_TRACE_SETUP'::
|
||||||
|
Enables trace messages printing the .git, working tree and current
|
||||||
|
working directory after Git has completed its setup phase.
|
||||||
|
See 'GIT_TRACE' for available trace output options.
|
||||||
|
|
||||||
|
'GIT_TRACE_SHALLOW'::
|
||||||
|
Enables trace messages that can help debugging fetching /
|
||||||
|
cloning of shallow repositories.
|
||||||
|
See 'GIT_TRACE' for available trace output options.
|
||||||
|
|
||||||
GIT_LITERAL_PATHSPECS::
|
GIT_LITERAL_PATHSPECS::
|
||||||
Setting this variable to `1` will cause Git to treat all
|
Setting this variable to `1` will cause Git to treat all
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
trace API
|
||||||
|
=========
|
||||||
|
|
||||||
|
The trace API can be used to print debug messages to stderr or a file. Trace
|
||||||
|
code is inactive unless explicitly enabled by setting `GIT_TRACE*` environment
|
||||||
|
variables.
|
||||||
|
|
||||||
|
The trace implementation automatically adds `timestamp file:line ... \n` to
|
||||||
|
all trace messages. E.g.:
|
||||||
|
|
||||||
|
------------
|
||||||
|
23:59:59.123456 git.c:312 trace: built-in: git 'foo'
|
||||||
|
00:00:00.000001 builtin/foo.c:99 foo: some message
|
||||||
|
------------
|
||||||
|
|
||||||
|
Data Structures
|
||||||
|
---------------
|
||||||
|
|
||||||
|
`struct trace_key`::
|
||||||
|
|
||||||
|
Defines a trace key (or category). The default (for API functions that
|
||||||
|
don't take a key) is `GIT_TRACE`.
|
||||||
|
+
|
||||||
|
E.g. to define a trace key controlled by environment variable `GIT_TRACE_FOO`:
|
||||||
|
+
|
||||||
|
------------
|
||||||
|
static struct trace_key trace_foo = TRACE_KEY_INIT(FOO);
|
||||||
|
|
||||||
|
static void trace_print_foo(const char *message)
|
||||||
|
{
|
||||||
|
trace_print_key(&trace_foo, message);
|
||||||
|
}
|
||||||
|
------------
|
||||||
|
+
|
||||||
|
Note: don't use `const` as the trace implementation stores internal state in
|
||||||
|
the `trace_key` structure.
|
||||||
|
|
||||||
|
Functions
|
||||||
|
---------
|
||||||
|
|
||||||
|
`int trace_want(struct trace_key *key)`::
|
||||||
|
|
||||||
|
Checks whether the trace key is enabled. Used to prevent expensive
|
||||||
|
string formatting before calling one of the printing APIs.
|
||||||
|
|
||||||
|
`void trace_disable(struct trace_key *key)`::
|
||||||
|
|
||||||
|
Disables tracing for the specified key, even if the environment
|
||||||
|
variable was set.
|
||||||
|
|
||||||
|
`void trace_printf(const char *format, ...)`::
|
||||||
|
`void trace_printf_key(struct trace_key *key, const char *format, ...)`::
|
||||||
|
|
||||||
|
Prints a formatted message, similar to printf.
|
||||||
|
|
||||||
|
`void trace_argv_printf(const char **argv, const char *format, ...)``::
|
||||||
|
|
||||||
|
Prints a formatted message, followed by a quoted list of arguments.
|
||||||
|
|
||||||
|
`void trace_strbuf(struct trace_key *key, const struct strbuf *data)`::
|
||||||
|
|
||||||
|
Prints the strbuf, without additional formatting (i.e. doesn't
|
||||||
|
choke on `%` or even `\0`).
|
||||||
|
|
||||||
|
`uint64_t getnanotime(void)`::
|
||||||
|
|
||||||
|
Returns nanoseconds since the epoch (01/01/1970), typically used
|
||||||
|
for performance measurements.
|
||||||
|
+
|
||||||
|
Currently there are high precision timer implementations for Linux (using
|
||||||
|
`clock_gettime(CLOCK_MONOTONIC)`) and Windows (`QueryPerformanceCounter`).
|
||||||
|
Other platforms use `gettimeofday` as time source.
|
||||||
|
|
||||||
|
`void trace_performance(uint64_t nanos, const char *format, ...)`::
|
||||||
|
`void trace_performance_since(uint64_t start, const char *format, ...)`::
|
||||||
|
|
||||||
|
Prints the elapsed time (in nanoseconds), or elapsed time since
|
||||||
|
`start`, followed by a formatted message. Enabled via environment
|
||||||
|
variable `GIT_TRACE_PERFORMANCE`. Used for manual profiling, e.g.:
|
||||||
|
+
|
||||||
|
------------
|
||||||
|
uint64_t start = getnanotime();
|
||||||
|
/* code section to measure */
|
||||||
|
trace_performance_since(start, "foobar");
|
||||||
|
------------
|
||||||
|
+
|
||||||
|
------------
|
||||||
|
uint64_t t = 0;
|
||||||
|
for (;;) {
|
||||||
|
/* ignore */
|
||||||
|
t -= getnanotime();
|
||||||
|
/* code section to measure */
|
||||||
|
t += getnanotime();
|
||||||
|
/* ignore */
|
||||||
|
}
|
||||||
|
trace_performance(t, "frotz");
|
||||||
|
------------
|
7
Makefile
7
Makefile
|
@ -340,6 +340,8 @@ all::
|
||||||
#
|
#
|
||||||
# Define GMTIME_UNRELIABLE_ERRORS if your gmtime() function does not
|
# Define GMTIME_UNRELIABLE_ERRORS if your gmtime() function does not
|
||||||
# return NULL when it receives a bogus time_t.
|
# return NULL when it receives a bogus time_t.
|
||||||
|
#
|
||||||
|
# Define HAVE_CLOCK_GETTIME if your platform has clock_gettime in librt.
|
||||||
|
|
||||||
GIT-VERSION-FILE: FORCE
|
GIT-VERSION-FILE: FORCE
|
||||||
@$(SHELL_PATH) ./GIT-VERSION-GEN
|
@$(SHELL_PATH) ./GIT-VERSION-GEN
|
||||||
|
@ -1500,6 +1502,11 @@ ifdef GMTIME_UNRELIABLE_ERRORS
|
||||||
BASIC_CFLAGS += -DGMTIME_UNRELIABLE_ERRORS
|
BASIC_CFLAGS += -DGMTIME_UNRELIABLE_ERRORS
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifdef HAVE_CLOCK_GETTIME
|
||||||
|
BASIC_CFLAGS += -DHAVE_CLOCK_GETTIME
|
||||||
|
EXTLIBS += -lrt
|
||||||
|
endif
|
||||||
|
|
||||||
ifeq ($(TCLTK_PATH),)
|
ifeq ($(TCLTK_PATH),)
|
||||||
NO_TCLTK = NoThanks
|
NO_TCLTK = NoThanks
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -438,7 +438,7 @@ static int update_shallow_ref(struct command *cmd, struct shallow_info *si)
|
||||||
uint32_t mask = 1 << (cmd->index % 32);
|
uint32_t mask = 1 << (cmd->index % 32);
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
trace_printf_key("GIT_TRACE_SHALLOW",
|
trace_printf_key(&trace_shallow,
|
||||||
"shallow: update_shallow_ref %s\n", cmd->ref_name);
|
"shallow: update_shallow_ref %s\n", cmd->ref_name);
|
||||||
for (i = 0; i < si->shallow->nr; i++)
|
for (i = 0; i < si->shallow->nr; i++)
|
||||||
if (si->used_shallow[i] &&
|
if (si->used_shallow[i] &&
|
||||||
|
|
13
cache.h
13
cache.h
|
@ -7,6 +7,7 @@
|
||||||
#include "advice.h"
|
#include "advice.h"
|
||||||
#include "gettext.h"
|
#include "gettext.h"
|
||||||
#include "convert.h"
|
#include "convert.h"
|
||||||
|
#include "trace.h"
|
||||||
|
|
||||||
#include SHA1_HEADER
|
#include SHA1_HEADER
|
||||||
#ifndef git_SHA_CTX
|
#ifndef git_SHA_CTX
|
||||||
|
@ -1402,17 +1403,7 @@ extern void *alloc_tag_node(void);
|
||||||
extern void *alloc_object_node(void);
|
extern void *alloc_object_node(void);
|
||||||
extern void alloc_report(void);
|
extern void alloc_report(void);
|
||||||
|
|
||||||
/* trace.c */
|
/* pkt-line.c */
|
||||||
__attribute__((format (printf, 1, 2)))
|
|
||||||
extern void trace_printf(const char *format, ...);
|
|
||||||
__attribute__((format (printf, 2, 3)))
|
|
||||||
extern void trace_argv_printf(const char **argv, const char *format, ...);
|
|
||||||
extern void trace_repo_setup(const char *prefix);
|
|
||||||
extern int trace_want(const char *key);
|
|
||||||
__attribute__((format (printf, 2, 3)))
|
|
||||||
extern void trace_printf_key(const char *key, const char *fmt, ...);
|
|
||||||
extern void trace_strbuf(const char *key, const struct strbuf *buf);
|
|
||||||
|
|
||||||
void packet_trace_identity(const char *prog);
|
void packet_trace_identity(const char *prog);
|
||||||
|
|
||||||
/* add */
|
/* add */
|
||||||
|
|
1
commit.h
1
commit.h
|
@ -271,6 +271,7 @@ extern void assign_shallow_commits_to_refs(struct shallow_info *info,
|
||||||
int *ref_status);
|
int *ref_status);
|
||||||
extern int delayed_reachability_test(struct shallow_info *si, int c);
|
extern int delayed_reachability_test(struct shallow_info *si, int c);
|
||||||
extern void prune_shallow(int show_only);
|
extern void prune_shallow(int show_only);
|
||||||
|
extern struct trace_key trace_shallow;
|
||||||
|
|
||||||
int is_descendant_of(struct commit *, struct commit_list *);
|
int is_descendant_of(struct commit *, struct commit_list *);
|
||||||
int in_merge_bases(struct commit *, struct commit *);
|
int in_merge_bases(struct commit *, struct commit *);
|
||||||
|
|
|
@ -34,6 +34,7 @@ ifeq ($(uname_S),Linux)
|
||||||
HAVE_PATHS_H = YesPlease
|
HAVE_PATHS_H = YesPlease
|
||||||
LIBC_CONTAINS_LIBINTL = YesPlease
|
LIBC_CONTAINS_LIBINTL = YesPlease
|
||||||
HAVE_DEV_TTY = YesPlease
|
HAVE_DEV_TTY = YesPlease
|
||||||
|
HAVE_CLOCK_GETTIME = YesPlease
|
||||||
endif
|
endif
|
||||||
ifeq ($(uname_S),GNU/kFreeBSD)
|
ifeq ($(uname_S),GNU/kFreeBSD)
|
||||||
HAVE_ALLOCA_H = YesPlease
|
HAVE_ALLOCA_H = YesPlease
|
||||||
|
|
|
@ -761,6 +761,10 @@ void git_qsort(void *base, size_t nmemb, size_t size,
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(__GNUC__) || (_MSC_VER >= 1400)
|
||||||
|
#define HAVE_VARIADIC_MACROS 1
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Preserves errno, prints a message, but gives no warning for ENOENT.
|
* Preserves errno, prints a message, but gives no warning for ENOENT.
|
||||||
* Always returns the return value of unlink(2).
|
* Always returns the return value of unlink(2).
|
||||||
|
|
2
git.c
2
git.c
|
@ -613,6 +613,8 @@ int main(int argc, char **av)
|
||||||
|
|
||||||
git_setup_gettext();
|
git_setup_gettext();
|
||||||
|
|
||||||
|
trace_command_performance(argv);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* "git-xxxx" is the same as "git xxxx", but we obviously:
|
* "git-xxxx" is the same as "git xxxx", but we obviously:
|
||||||
*
|
*
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
char packet_buffer[LARGE_PACKET_MAX];
|
char packet_buffer[LARGE_PACKET_MAX];
|
||||||
static const char *packet_trace_prefix = "git";
|
static const char *packet_trace_prefix = "git";
|
||||||
static const char trace_key[] = "GIT_TRACE_PACKET";
|
static struct trace_key trace_packet = TRACE_KEY_INIT(PACKET);
|
||||||
|
|
||||||
void packet_trace_identity(const char *prog)
|
void packet_trace_identity(const char *prog)
|
||||||
{
|
{
|
||||||
|
@ -15,7 +15,7 @@ static void packet_trace(const char *buf, unsigned int len, int write)
|
||||||
int i;
|
int i;
|
||||||
struct strbuf out;
|
struct strbuf out;
|
||||||
|
|
||||||
if (!trace_want(trace_key))
|
if (!trace_want(&trace_packet))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* +32 is just a guess for header + quoting */
|
/* +32 is just a guess for header + quoting */
|
||||||
|
@ -27,7 +27,7 @@ static void packet_trace(const char *buf, unsigned int len, int write)
|
||||||
if ((len >= 4 && starts_with(buf, "PACK")) ||
|
if ((len >= 4 && starts_with(buf, "PACK")) ||
|
||||||
(len >= 5 && starts_with(buf+1, "PACK"))) {
|
(len >= 5 && starts_with(buf+1, "PACK"))) {
|
||||||
strbuf_addstr(&out, "PACK ...");
|
strbuf_addstr(&out, "PACK ...");
|
||||||
unsetenv(trace_key);
|
trace_disable(&trace_packet);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* XXX we should really handle printable utf8 */
|
/* XXX we should really handle printable utf8 */
|
||||||
|
@ -43,7 +43,7 @@ static void packet_trace(const char *buf, unsigned int len, int write)
|
||||||
}
|
}
|
||||||
|
|
||||||
strbuf_addch(&out, '\n');
|
strbuf_addch(&out, '\n');
|
||||||
trace_strbuf(trace_key, &out);
|
trace_strbuf(&trace_packet, &out);
|
||||||
strbuf_release(&out);
|
strbuf_release(&out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
71
progress.c
71
progress.c
|
@ -12,13 +12,14 @@
|
||||||
#include "gettext.h"
|
#include "gettext.h"
|
||||||
#include "progress.h"
|
#include "progress.h"
|
||||||
#include "strbuf.h"
|
#include "strbuf.h"
|
||||||
|
#include "trace.h"
|
||||||
|
|
||||||
#define TP_IDX_MAX 8
|
#define TP_IDX_MAX 8
|
||||||
|
|
||||||
struct throughput {
|
struct throughput {
|
||||||
off_t curr_total;
|
off_t curr_total;
|
||||||
off_t prev_total;
|
off_t prev_total;
|
||||||
struct timeval prev_tv;
|
uint64_t prev_ns;
|
||||||
unsigned int avg_bytes;
|
unsigned int avg_bytes;
|
||||||
unsigned int avg_misecs;
|
unsigned int avg_misecs;
|
||||||
unsigned int last_bytes[TP_IDX_MAX];
|
unsigned int last_bytes[TP_IDX_MAX];
|
||||||
|
@ -127,65 +128,65 @@ static void throughput_string(struct strbuf *buf, off_t total,
|
||||||
void display_throughput(struct progress *progress, off_t total)
|
void display_throughput(struct progress *progress, off_t total)
|
||||||
{
|
{
|
||||||
struct throughput *tp;
|
struct throughput *tp;
|
||||||
struct timeval tv;
|
uint64_t now_ns;
|
||||||
unsigned int misecs;
|
unsigned int misecs, count, rate;
|
||||||
|
struct strbuf buf = STRBUF_INIT;
|
||||||
|
|
||||||
if (!progress)
|
if (!progress)
|
||||||
return;
|
return;
|
||||||
tp = progress->throughput;
|
tp = progress->throughput;
|
||||||
|
|
||||||
gettimeofday(&tv, NULL);
|
now_ns = getnanotime();
|
||||||
|
|
||||||
if (!tp) {
|
if (!tp) {
|
||||||
progress->throughput = tp = calloc(1, sizeof(*tp));
|
progress->throughput = tp = calloc(1, sizeof(*tp));
|
||||||
if (tp) {
|
if (tp) {
|
||||||
tp->prev_total = tp->curr_total = total;
|
tp->prev_total = tp->curr_total = total;
|
||||||
tp->prev_tv = tv;
|
tp->prev_ns = now_ns;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
tp->curr_total = total;
|
tp->curr_total = total;
|
||||||
|
|
||||||
|
/* only update throughput every 0.5 s */
|
||||||
|
if (now_ns - tp->prev_ns <= 500000000)
|
||||||
|
return;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We have x = bytes and y = microsecs. We want z = KiB/s:
|
* We have x = bytes and y = nanosecs. We want z = KiB/s:
|
||||||
*
|
*
|
||||||
* z = (x / 1024) / (y / 1000000)
|
* z = (x / 1024) / (y / 1000000000)
|
||||||
* z = x / y * 1000000 / 1024
|
* z = x / y * 1000000000 / 1024
|
||||||
* z = x / (y * 1024 / 1000000)
|
* z = x / (y * 1024 / 1000000000)
|
||||||
* z = x / y'
|
* z = x / y'
|
||||||
*
|
*
|
||||||
* To simplify things we'll keep track of misecs, or 1024th of a sec
|
* To simplify things we'll keep track of misecs, or 1024th of a sec
|
||||||
* obtained with:
|
* obtained with:
|
||||||
*
|
*
|
||||||
* y' = y * 1024 / 1000000
|
* y' = y * 1024 / 1000000000
|
||||||
* y' = y / (1000000 / 1024)
|
* y' = y * (2^10 / 2^42) * (2^42 / 1000000000)
|
||||||
* y' = y / 977
|
* y' = y / 2^32 * 4398
|
||||||
|
* y' = (y * 4398) >> 32
|
||||||
*/
|
*/
|
||||||
misecs = (tv.tv_sec - tp->prev_tv.tv_sec) * 1024;
|
misecs = ((now_ns - tp->prev_ns) * 4398) >> 32;
|
||||||
misecs += (int)(tv.tv_usec - tp->prev_tv.tv_usec) / 977;
|
|
||||||
|
|
||||||
if (misecs > 512) {
|
count = total - tp->prev_total;
|
||||||
struct strbuf buf = STRBUF_INIT;
|
tp->prev_total = total;
|
||||||
unsigned int count, rate;
|
tp->prev_ns = now_ns;
|
||||||
|
tp->avg_bytes += count;
|
||||||
|
tp->avg_misecs += misecs;
|
||||||
|
rate = tp->avg_bytes / tp->avg_misecs;
|
||||||
|
tp->avg_bytes -= tp->last_bytes[tp->idx];
|
||||||
|
tp->avg_misecs -= tp->last_misecs[tp->idx];
|
||||||
|
tp->last_bytes[tp->idx] = count;
|
||||||
|
tp->last_misecs[tp->idx] = misecs;
|
||||||
|
tp->idx = (tp->idx + 1) % TP_IDX_MAX;
|
||||||
|
|
||||||
count = total - tp->prev_total;
|
throughput_string(&buf, total, rate);
|
||||||
tp->prev_total = total;
|
strncpy(tp->display, buf.buf, sizeof(tp->display));
|
||||||
tp->prev_tv = tv;
|
strbuf_release(&buf);
|
||||||
tp->avg_bytes += count;
|
if (progress->last_value != -1 && progress_update)
|
||||||
tp->avg_misecs += misecs;
|
display(progress, progress->last_value, NULL);
|
||||||
rate = tp->avg_bytes / tp->avg_misecs;
|
|
||||||
tp->avg_bytes -= tp->last_bytes[tp->idx];
|
|
||||||
tp->avg_misecs -= tp->last_misecs[tp->idx];
|
|
||||||
tp->last_bytes[tp->idx] = count;
|
|
||||||
tp->last_misecs[tp->idx] = misecs;
|
|
||||||
tp->idx = (tp->idx + 1) % TP_IDX_MAX;
|
|
||||||
|
|
||||||
throughput_string(&buf, total, rate);
|
|
||||||
strncpy(tp->display, buf.buf, sizeof(tp->display));
|
|
||||||
strbuf_release(&buf);
|
|
||||||
if (progress->last_value != -1 && progress_update)
|
|
||||||
display(progress, progress->last_value, NULL);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int display_progress(struct progress *progress, unsigned n)
|
int display_progress(struct progress *progress, unsigned n)
|
||||||
|
|
30
sha1_file.c
30
sha1_file.c
|
@ -36,9 +36,6 @@ static inline uintmax_t sz_fmt(size_t s) { return s; }
|
||||||
|
|
||||||
const unsigned char null_sha1[20];
|
const unsigned char null_sha1[20];
|
||||||
|
|
||||||
static const char *no_log_pack_access = "no_log_pack_access";
|
|
||||||
static const char *log_pack_access;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is meant to hold a *small* number of objects that you would
|
* This is meant to hold a *small* number of objects that you would
|
||||||
* want read_sha1_file() to be able to return, but yet you do not want
|
* want read_sha1_file() to be able to return, but yet you do not want
|
||||||
|
@ -2086,27 +2083,9 @@ static void *read_object(const unsigned char *sha1, enum object_type *type,
|
||||||
|
|
||||||
static void write_pack_access_log(struct packed_git *p, off_t obj_offset)
|
static void write_pack_access_log(struct packed_git *p, off_t obj_offset)
|
||||||
{
|
{
|
||||||
static FILE *log_file;
|
static struct trace_key pack_access = TRACE_KEY_INIT(PACK_ACCESS);
|
||||||
|
trace_printf_key(&pack_access, "%s %"PRIuMAX"\n",
|
||||||
if (!log_pack_access)
|
p->pack_name, (uintmax_t)obj_offset);
|
||||||
log_pack_access = getenv("GIT_TRACE_PACK_ACCESS");
|
|
||||||
if (!log_pack_access)
|
|
||||||
log_pack_access = no_log_pack_access;
|
|
||||||
if (log_pack_access == no_log_pack_access)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!log_file) {
|
|
||||||
log_file = fopen(log_pack_access, "w");
|
|
||||||
if (!log_file) {
|
|
||||||
error("cannot open pack access log '%s' for writing: %s",
|
|
||||||
log_pack_access, strerror(errno));
|
|
||||||
log_pack_access = no_log_pack_access;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fprintf(log_file, "%s %"PRIuMAX"\n",
|
|
||||||
p->pack_name, (uintmax_t)obj_offset);
|
|
||||||
fflush(log_file);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int do_check_packed_object_crc;
|
int do_check_packed_object_crc;
|
||||||
|
@ -2131,8 +2110,7 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset,
|
||||||
int delta_stack_nr = 0, delta_stack_alloc = UNPACK_ENTRY_STACK_PREALLOC;
|
int delta_stack_nr = 0, delta_stack_alloc = UNPACK_ENTRY_STACK_PREALLOC;
|
||||||
int base_from_cache = 0;
|
int base_from_cache = 0;
|
||||||
|
|
||||||
if (log_pack_access != no_log_pack_access)
|
write_pack_access_log(p, obj_offset);
|
||||||
write_pack_access_log(p, obj_offset);
|
|
||||||
|
|
||||||
/* PHASE 1: drill down to the innermost base object */
|
/* PHASE 1: drill down to the innermost base object */
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
|
10
shallow.c
10
shallow.c
|
@ -325,7 +325,7 @@ void prune_shallow(int show_only)
|
||||||
strbuf_release(&sb);
|
strbuf_release(&sb);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define TRACE_KEY "GIT_TRACE_SHALLOW"
|
struct trace_key trace_shallow = TRACE_KEY_INIT(SHALLOW);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Step 1, split sender shallow commits into "ours" and "theirs"
|
* Step 1, split sender shallow commits into "ours" and "theirs"
|
||||||
|
@ -334,7 +334,7 @@ void prune_shallow(int show_only)
|
||||||
void prepare_shallow_info(struct shallow_info *info, struct sha1_array *sa)
|
void prepare_shallow_info(struct shallow_info *info, struct sha1_array *sa)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
trace_printf_key(TRACE_KEY, "shallow: prepare_shallow_info\n");
|
trace_printf_key(&trace_shallow, "shallow: prepare_shallow_info\n");
|
||||||
memset(info, 0, sizeof(*info));
|
memset(info, 0, sizeof(*info));
|
||||||
info->shallow = sa;
|
info->shallow = sa;
|
||||||
if (!sa)
|
if (!sa)
|
||||||
|
@ -365,7 +365,7 @@ void remove_nonexistent_theirs_shallow(struct shallow_info *info)
|
||||||
{
|
{
|
||||||
unsigned char (*sha1)[20] = info->shallow->sha1;
|
unsigned char (*sha1)[20] = info->shallow->sha1;
|
||||||
int i, dst;
|
int i, dst;
|
||||||
trace_printf_key(TRACE_KEY, "shallow: remove_nonexistent_theirs_shallow\n");
|
trace_printf_key(&trace_shallow, "shallow: remove_nonexistent_theirs_shallow\n");
|
||||||
for (i = dst = 0; i < info->nr_theirs; i++) {
|
for (i = dst = 0; i < info->nr_theirs; i++) {
|
||||||
if (i != dst)
|
if (i != dst)
|
||||||
info->theirs[dst] = info->theirs[i];
|
info->theirs[dst] = info->theirs[i];
|
||||||
|
@ -516,7 +516,7 @@ void assign_shallow_commits_to_refs(struct shallow_info *info,
|
||||||
int *shallow, nr_shallow = 0;
|
int *shallow, nr_shallow = 0;
|
||||||
struct paint_info pi;
|
struct paint_info pi;
|
||||||
|
|
||||||
trace_printf_key(TRACE_KEY, "shallow: assign_shallow_commits_to_refs\n");
|
trace_printf_key(&trace_shallow, "shallow: assign_shallow_commits_to_refs\n");
|
||||||
shallow = xmalloc(sizeof(*shallow) * (info->nr_ours + info->nr_theirs));
|
shallow = xmalloc(sizeof(*shallow) * (info->nr_ours + info->nr_theirs));
|
||||||
for (i = 0; i < info->nr_ours; i++)
|
for (i = 0; i < info->nr_ours; i++)
|
||||||
shallow[nr_shallow++] = info->ours[i];
|
shallow[nr_shallow++] = info->ours[i];
|
||||||
|
@ -622,7 +622,7 @@ static void post_assign_shallow(struct shallow_info *info,
|
||||||
int bitmap_nr = (info->ref->nr + 31) / 32;
|
int bitmap_nr = (info->ref->nr + 31) / 32;
|
||||||
struct commit_array ca;
|
struct commit_array ca;
|
||||||
|
|
||||||
trace_printf_key(TRACE_KEY, "shallow: post_assign_shallow\n");
|
trace_printf_key(&trace_shallow, "shallow: post_assign_shallow\n");
|
||||||
if (ref_status)
|
if (ref_status)
|
||||||
memset(ref_status, 0, sizeof(*ref_status) * info->ref->nr);
|
memset(ref_status, 0, sizeof(*ref_status) * info->ref->nr);
|
||||||
|
|
||||||
|
|
|
@ -109,6 +109,10 @@ export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME
|
||||||
export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME
|
export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME
|
||||||
export EDITOR
|
export EDITOR
|
||||||
|
|
||||||
|
# Tests using GIT_TRACE typically don't want <timestamp> <file>:<line> output
|
||||||
|
GIT_TRACE_BARE=1
|
||||||
|
export GIT_TRACE_BARE
|
||||||
|
|
||||||
if test -n "${TEST_GIT_INDEX_VERSION:+isset}"
|
if test -n "${TEST_GIT_INDEX_VERSION:+isset}"
|
||||||
then
|
then
|
||||||
GIT_INDEX_VERSION="$TEST_GIT_INDEX_VERSION"
|
GIT_INDEX_VERSION="$TEST_GIT_INDEX_VERSION"
|
||||||
|
|
397
trace.c
397
trace.c
|
@ -26,110 +26,250 @@
|
||||||
#include "quote.h"
|
#include "quote.h"
|
||||||
|
|
||||||
/* Get a trace file descriptor from "key" env variable. */
|
/* Get a trace file descriptor from "key" env variable. */
|
||||||
static int get_trace_fd(const char *key, int *need_close)
|
static int get_trace_fd(struct trace_key *key)
|
||||||
{
|
{
|
||||||
char *trace = getenv(key);
|
static struct trace_key trace_default = { "GIT_TRACE" };
|
||||||
|
const char *trace;
|
||||||
|
|
||||||
|
/* use default "GIT_TRACE" if NULL */
|
||||||
|
if (!key)
|
||||||
|
key = &trace_default;
|
||||||
|
|
||||||
|
/* don't open twice */
|
||||||
|
if (key->initialized)
|
||||||
|
return key->fd;
|
||||||
|
|
||||||
|
trace = getenv(key->key);
|
||||||
|
|
||||||
if (!trace || !strcmp(trace, "") ||
|
if (!trace || !strcmp(trace, "") ||
|
||||||
!strcmp(trace, "0") || !strcasecmp(trace, "false"))
|
!strcmp(trace, "0") || !strcasecmp(trace, "false"))
|
||||||
return 0;
|
key->fd = 0;
|
||||||
if (!strcmp(trace, "1") || !strcasecmp(trace, "true"))
|
else if (!strcmp(trace, "1") || !strcasecmp(trace, "true"))
|
||||||
return STDERR_FILENO;
|
key->fd = STDERR_FILENO;
|
||||||
if (strlen(trace) == 1 && isdigit(*trace))
|
else if (strlen(trace) == 1 && isdigit(*trace))
|
||||||
return atoi(trace);
|
key->fd = atoi(trace);
|
||||||
if (is_absolute_path(trace)) {
|
else if (is_absolute_path(trace)) {
|
||||||
int fd = open(trace, O_WRONLY | O_APPEND | O_CREAT, 0666);
|
int fd = open(trace, O_WRONLY | O_APPEND | O_CREAT, 0666);
|
||||||
if (fd == -1) {
|
if (fd == -1) {
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"Could not open '%s' for tracing: %s\n"
|
"Could not open '%s' for tracing: %s\n"
|
||||||
"Defaulting to tracing on stderr...\n",
|
"Defaulting to tracing on stderr...\n",
|
||||||
trace, strerror(errno));
|
trace, strerror(errno));
|
||||||
return STDERR_FILENO;
|
key->fd = STDERR_FILENO;
|
||||||
|
} else {
|
||||||
|
key->fd = fd;
|
||||||
|
key->need_close = 1;
|
||||||
}
|
}
|
||||||
*need_close = 1;
|
} else {
|
||||||
return fd;
|
fprintf(stderr, "What does '%s' for %s mean?\n"
|
||||||
|
"If you want to trace into a file, then please set "
|
||||||
|
"%s to an absolute pathname (starting with /).\n"
|
||||||
|
"Defaulting to tracing on stderr...\n",
|
||||||
|
trace, key->key, key->key);
|
||||||
|
key->fd = STDERR_FILENO;
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stderr, "What does '%s' for %s mean?\n", trace, key);
|
key->initialized = 1;
|
||||||
fprintf(stderr, "If you want to trace into a file, "
|
return key->fd;
|
||||||
"then please set %s to an absolute pathname "
|
}
|
||||||
"(starting with /).\n", key);
|
|
||||||
fprintf(stderr, "Defaulting to tracing on stderr...\n");
|
|
||||||
|
|
||||||
return STDERR_FILENO;
|
void trace_disable(struct trace_key *key)
|
||||||
|
{
|
||||||
|
if (key->need_close)
|
||||||
|
close(key->fd);
|
||||||
|
key->fd = 0;
|
||||||
|
key->initialized = 1;
|
||||||
|
key->need_close = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char err_msg[] = "Could not trace into fd given by "
|
static const char err_msg[] = "Could not trace into fd given by "
|
||||||
"GIT_TRACE environment variable";
|
"GIT_TRACE environment variable";
|
||||||
|
|
||||||
static void trace_vprintf(const char *key, const char *fmt, va_list ap)
|
static int prepare_trace_line(const char *file, int line,
|
||||||
|
struct trace_key *key, struct strbuf *buf)
|
||||||
{
|
{
|
||||||
struct strbuf buf = STRBUF_INIT;
|
static struct trace_key trace_bare = TRACE_KEY_INIT(BARE);
|
||||||
|
struct timeval tv;
|
||||||
|
struct tm tm;
|
||||||
|
time_t secs;
|
||||||
|
|
||||||
if (!trace_want(key))
|
if (!trace_want(key))
|
||||||
return;
|
return 0;
|
||||||
|
|
||||||
set_try_to_free_routine(NULL); /* is never reset */
|
set_try_to_free_routine(NULL); /* is never reset */
|
||||||
strbuf_vaddf(&buf, fmt, ap);
|
|
||||||
trace_strbuf(key, &buf);
|
/* unit tests may want to disable additional trace output */
|
||||||
strbuf_release(&buf);
|
if (trace_want(&trace_bare))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
|
||||||
|
#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
|
||||||
|
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((format (printf, 2, 3)))
|
static void print_trace_line(struct trace_key *key, struct strbuf *buf)
|
||||||
void trace_printf_key(const char *key, const char *fmt, ...)
|
|
||||||
{
|
{
|
||||||
va_list ap;
|
/* append newline if missing */
|
||||||
va_start(ap, fmt);
|
if (buf->len && buf->buf[buf->len - 1] != '\n')
|
||||||
trace_vprintf(key, fmt, ap);
|
strbuf_addch(buf, '\n');
|
||||||
va_end(ap);
|
|
||||||
|
write_or_whine_pipe(get_trace_fd(key), buf->buf, buf->len, err_msg);
|
||||||
|
strbuf_release(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
void trace_printf(const char *fmt, ...)
|
static void trace_vprintf_fl(const char *file, int line, struct trace_key *key,
|
||||||
{
|
const char *format, va_list ap)
|
||||||
va_list ap;
|
|
||||||
va_start(ap, fmt);
|
|
||||||
trace_vprintf("GIT_TRACE", fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
}
|
|
||||||
|
|
||||||
void trace_strbuf(const char *key, const struct strbuf *buf)
|
|
||||||
{
|
|
||||||
int fd, need_close = 0;
|
|
||||||
|
|
||||||
fd = get_trace_fd(key, &need_close);
|
|
||||||
if (!fd)
|
|
||||||
return;
|
|
||||||
|
|
||||||
write_or_whine_pipe(fd, buf->buf, buf->len, err_msg);
|
|
||||||
|
|
||||||
if (need_close)
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
void trace_argv_printf(const char **argv, const char *fmt, ...)
|
|
||||||
{
|
{
|
||||||
struct strbuf buf = STRBUF_INIT;
|
struct strbuf buf = STRBUF_INIT;
|
||||||
va_list ap;
|
|
||||||
int fd, need_close = 0;
|
|
||||||
|
|
||||||
fd = get_trace_fd("GIT_TRACE", &need_close);
|
if (!prepare_trace_line(file, line, key, &buf))
|
||||||
if (!fd)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
set_try_to_free_routine(NULL); /* is never reset */
|
strbuf_vaddf(&buf, format, ap);
|
||||||
va_start(ap, fmt);
|
print_trace_line(key, &buf);
|
||||||
strbuf_vaddf(&buf, fmt, ap);
|
}
|
||||||
va_end(ap);
|
|
||||||
|
static void trace_argv_vprintf_fl(const char *file, int line,
|
||||||
|
const char **argv, const char *format,
|
||||||
|
va_list ap)
|
||||||
|
{
|
||||||
|
struct strbuf buf = STRBUF_INIT;
|
||||||
|
|
||||||
|
if (!prepare_trace_line(file, line, NULL, &buf))
|
||||||
|
return;
|
||||||
|
|
||||||
|
strbuf_vaddf(&buf, format, ap);
|
||||||
|
|
||||||
sq_quote_argv(&buf, argv, 0);
|
sq_quote_argv(&buf, argv, 0);
|
||||||
strbuf_addch(&buf, '\n');
|
print_trace_line(NULL, &buf);
|
||||||
write_or_whine_pipe(fd, buf.buf, buf.len, err_msg);
|
|
||||||
strbuf_release(&buf);
|
|
||||||
|
|
||||||
if (need_close)
|
|
||||||
close(fd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void trace_strbuf_fl(const char *file, int line, struct trace_key *key,
|
||||||
|
const struct strbuf *data)
|
||||||
|
{
|
||||||
|
struct strbuf buf = STRBUF_INIT;
|
||||||
|
|
||||||
|
if (!prepare_trace_line(file, line, key, &buf))
|
||||||
|
return;
|
||||||
|
|
||||||
|
strbuf_addbuf(&buf, data);
|
||||||
|
print_trace_line(key, &buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef HAVE_VARIADIC_MACROS
|
||||||
|
|
||||||
|
void trace_printf(const char *format, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, format);
|
||||||
|
trace_vprintf_fl(NULL, 0, NULL, format, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
void trace_printf_key(struct trace_key *key, const char *format, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, format);
|
||||||
|
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);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
void trace_strbuf(const char *key, const struct strbuf *data)
|
||||||
|
{
|
||||||
|
trace_strbuf_fl(NULL, 0, key, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* HAVE_VARIADIC_MACROS */
|
||||||
|
|
||||||
|
|
||||||
static const char *quote_crnl(const char *path)
|
static const char *quote_crnl(const char *path)
|
||||||
{
|
{
|
||||||
static char new_path[PATH_MAX];
|
static char new_path[PATH_MAX];
|
||||||
|
@ -156,11 +296,11 @@ static const char *quote_crnl(const char *path)
|
||||||
/* FIXME: move prefix to startup_info struct and get rid of this arg */
|
/* FIXME: move prefix to startup_info struct and get rid of this arg */
|
||||||
void trace_repo_setup(const char *prefix)
|
void trace_repo_setup(const char *prefix)
|
||||||
{
|
{
|
||||||
static const char *key = "GIT_TRACE_SETUP";
|
static struct trace_key key = TRACE_KEY_INIT(SETUP);
|
||||||
const char *git_work_tree;
|
const char *git_work_tree;
|
||||||
char cwd[PATH_MAX];
|
char cwd[PATH_MAX];
|
||||||
|
|
||||||
if (!trace_want(key))
|
if (!trace_want(&key))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!getcwd(cwd, PATH_MAX))
|
if (!getcwd(cwd, PATH_MAX))
|
||||||
|
@ -172,18 +312,117 @@ void trace_repo_setup(const char *prefix)
|
||||||
if (!prefix)
|
if (!prefix)
|
||||||
prefix = "(null)";
|
prefix = "(null)";
|
||||||
|
|
||||||
trace_printf_key(key, "setup: git_dir: %s\n", quote_crnl(get_git_dir()));
|
trace_printf_key(&key, "setup: git_dir: %s\n", quote_crnl(get_git_dir()));
|
||||||
trace_printf_key(key, "setup: worktree: %s\n", quote_crnl(git_work_tree));
|
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: cwd: %s\n", quote_crnl(cwd));
|
||||||
trace_printf_key(key, "setup: prefix: %s\n", quote_crnl(prefix));
|
trace_printf_key(&key, "setup: prefix: %s\n", quote_crnl(prefix));
|
||||||
}
|
}
|
||||||
|
|
||||||
int trace_want(const char *key)
|
int trace_want(struct trace_key *key)
|
||||||
{
|
{
|
||||||
const char *trace = getenv(key);
|
return !!get_trace_fd(key);
|
||||||
|
}
|
||||||
if (!trace || !strcmp(trace, "") ||
|
|
||||||
!strcmp(trace, "0") || !strcasecmp(trace, "false"))
|
#ifdef HAVE_CLOCK_GETTIME
|
||||||
return 0;
|
|
||||||
return 1;
|
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).
|
||||||
|
*/
|
||||||
|
inline uint64_t getnanotime(void)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,113 @@
|
||||||
|
#ifndef TRACE_H
|
||||||
|
#define TRACE_H
|
||||||
|
|
||||||
|
#include "git-compat-util.h"
|
||||||
|
#include "strbuf.h"
|
||||||
|
|
||||||
|
struct trace_key {
|
||||||
|
const char * const key;
|
||||||
|
int fd;
|
||||||
|
unsigned int initialized : 1;
|
||||||
|
unsigned int need_close : 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define TRACE_KEY_INIT(name) { "GIT_TRACE_" #name, 0, 0, 0 }
|
||||||
|
|
||||||
|
extern void trace_repo_setup(const char *prefix);
|
||||||
|
extern int trace_want(struct trace_key *key);
|
||||||
|
extern void trace_disable(struct trace_key *key);
|
||||||
|
extern uint64_t getnanotime(void);
|
||||||
|
extern void trace_command_performance(const char **argv);
|
||||||
|
|
||||||
|
#ifndef HAVE_VARIADIC_MACROS
|
||||||
|
|
||||||
|
__attribute__((format (printf, 1, 2)))
|
||||||
|
extern void trace_printf(const char *format, ...);
|
||||||
|
|
||||||
|
__attribute__((format (printf, 2, 3)))
|
||||||
|
extern void trace_printf_key(struct trace_key *key, const char *format, ...);
|
||||||
|
|
||||||
|
__attribute__((format (printf, 2, 3)))
|
||||||
|
extern void trace_argv_printf(const char **argv, const char *format, ...);
|
||||||
|
|
||||||
|
extern void trace_strbuf(struct trace_key *key, const struct strbuf *data);
|
||||||
|
|
||||||
|
/* Prints elapsed time (in nanoseconds) if GIT_TRACE_PERFORMANCE is enabled. */
|
||||||
|
__attribute__((format (printf, 2, 3)))
|
||||||
|
extern void trace_performance(uint64_t nanos, const char *format, ...);
|
||||||
|
|
||||||
|
/* Prints elapsed time since 'start' if GIT_TRACE_PERFORMANCE is enabled. */
|
||||||
|
__attribute__((format (printf, 2, 3)))
|
||||||
|
extern void trace_performance_since(uint64_t start, const char *format, ...);
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Macros to add file:line - see above for C-style declarations of how these
|
||||||
|
* should be used.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TRACE_CONTEXT may be set to __FUNCTION__ if the compiler supports it. The
|
||||||
|
* default is __FILE__, as it is consistent with assert(), and static function
|
||||||
|
* names are not necessarily unique.
|
||||||
|
*
|
||||||
|
* __FILE__ ":" __FUNCTION__ doesn't work with GNUC, as __FILE__ is supplied
|
||||||
|
* by the preprocessor as a string literal, and __FUNCTION__ is filled in by
|
||||||
|
* the compiler as a string constant.
|
||||||
|
*/
|
||||||
|
#ifndef TRACE_CONTEXT
|
||||||
|
# define TRACE_CONTEXT __FILE__
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note: with C99 variadic macros, __VA_ARGS__ must include the last fixed
|
||||||
|
* parameter ('format' in this case). Otherwise, a call without variable
|
||||||
|
* arguments will have a surplus ','. E.g.:
|
||||||
|
*
|
||||||
|
* #define foo(format, ...) bar(format, __VA_ARGS__)
|
||||||
|
* foo("test");
|
||||||
|
*
|
||||||
|
* will expand to
|
||||||
|
*
|
||||||
|
* bar("test",);
|
||||||
|
*
|
||||||
|
* which is invalid (note the ',)'). With GNUC, '##__VA_ARGS__' drops the
|
||||||
|
* comma, but this is non-standard.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define trace_printf(...) \
|
||||||
|
trace_printf_key_fl(TRACE_CONTEXT, __LINE__, NULL, __VA_ARGS__)
|
||||||
|
|
||||||
|
#define trace_printf_key(key, ...) \
|
||||||
|
trace_printf_key_fl(TRACE_CONTEXT, __LINE__, key, __VA_ARGS__)
|
||||||
|
|
||||||
|
#define trace_argv_printf(argv, ...) \
|
||||||
|
trace_argv_printf_fl(TRACE_CONTEXT, __LINE__, argv, __VA_ARGS__)
|
||||||
|
|
||||||
|
#define trace_strbuf(key, data) \
|
||||||
|
trace_strbuf_fl(TRACE_CONTEXT, __LINE__, key, data)
|
||||||
|
|
||||||
|
#define trace_performance(nanos, ...) \
|
||||||
|
trace_performance_fl(TRACE_CONTEXT, __LINE__, nanos, __VA_ARGS__)
|
||||||
|
|
||||||
|
#define trace_performance_since(start, ...) \
|
||||||
|
trace_performance_fl(TRACE_CONTEXT, __LINE__, getnanotime() - (start), \
|
||||||
|
__VA_ARGS__)
|
||||||
|
|
||||||
|
/* backend functions, use non-*fl macros instead */
|
||||||
|
__attribute__((format (printf, 4, 5)))
|
||||||
|
extern void trace_printf_key_fl(const char *file, int line, struct trace_key *key,
|
||||||
|
const char *format, ...);
|
||||||
|
__attribute__((format (printf, 4, 5)))
|
||||||
|
extern void trace_argv_printf_fl(const char *file, int line, const char **argv,
|
||||||
|
const char *format, ...);
|
||||||
|
extern void trace_strbuf_fl(const char *file, int line, struct trace_key *key,
|
||||||
|
const struct strbuf *data);
|
||||||
|
__attribute__((format (printf, 4, 5)))
|
||||||
|
extern void trace_performance_fl(const char *file, int line,
|
||||||
|
uint64_t nanos, const char *fmt, ...);
|
||||||
|
|
||||||
|
#endif /* HAVE_VARIADIC_MACROS */
|
||||||
|
|
||||||
|
#endif /* TRACE_H */
|
14
wt-status.c
14
wt-status.c
|
@ -574,14 +574,11 @@ static void wt_status_collect_untracked(struct wt_status *s)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
struct dir_struct dir;
|
struct dir_struct dir;
|
||||||
struct timeval t_begin;
|
uint64_t t_begin = getnanotime();
|
||||||
|
|
||||||
if (!s->show_untracked_files)
|
if (!s->show_untracked_files)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (advice_status_u_option)
|
|
||||||
gettimeofday(&t_begin, NULL);
|
|
||||||
|
|
||||||
memset(&dir, 0, sizeof(dir));
|
memset(&dir, 0, sizeof(dir));
|
||||||
if (s->show_untracked_files != SHOW_ALL_UNTRACKED_FILES)
|
if (s->show_untracked_files != SHOW_ALL_UNTRACKED_FILES)
|
||||||
dir.flags |=
|
dir.flags |=
|
||||||
|
@ -612,13 +609,8 @@ static void wt_status_collect_untracked(struct wt_status *s)
|
||||||
free(dir.ignored);
|
free(dir.ignored);
|
||||||
clear_directory(&dir);
|
clear_directory(&dir);
|
||||||
|
|
||||||
if (advice_status_u_option) {
|
if (advice_status_u_option)
|
||||||
struct timeval t_end;
|
s->untracked_in_ms = (getnanotime() - t_begin) / 1000000;
|
||||||
gettimeofday(&t_end, NULL);
|
|
||||||
s->untracked_in_ms =
|
|
||||||
(uint64_t)t_end.tv_sec * 1000 + t_end.tv_usec / 1000 -
|
|
||||||
((uint64_t)t_begin.tv_sec * 1000 + t_begin.tv_usec / 1000);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void wt_status_collect(struct wt_status *s)
|
void wt_status_collect(struct wt_status *s)
|
||||||
|
|
Loading…
Reference in New Issue