Merge branch 'ab/bug-if-bug'
A new bug() and BUG_if_bug() API is introduced to make it easier to uniformly log "detect multiple bugs and abort in the end" pattern. * ab/bug-if-bug: cache-tree.c: use bug() and BUG_if_bug() receive-pack: use bug() and BUG_if_bug() parse-options.c: use optbug() instead of BUG() "opts" check parse-options.c: use new bug() API for optbug() usage.c: add a non-fatal bug() function to go with BUG() common-main.c: move non-trace2 exit() behavior out of trace2.c
This commit is contained in:
@ -1,12 +1,34 @@
|
|||||||
Error reporting in git
|
Error reporting in git
|
||||||
======================
|
======================
|
||||||
|
|
||||||
`BUG`, `die`, `usage`, `error`, and `warning` report errors of
|
`BUG`, `bug`, `die`, `usage`, `error`, and `warning` report errors of
|
||||||
various kinds.
|
various kinds.
|
||||||
|
|
||||||
- `BUG` is for failed internal assertions that should never happen,
|
- `BUG` is for failed internal assertions that should never happen,
|
||||||
i.e. a bug in git itself.
|
i.e. a bug in git itself.
|
||||||
|
|
||||||
|
- `bug` (lower-case, not `BUG`) is supposed to be used like `BUG` but
|
||||||
|
prints a "BUG" message instead of calling `abort()`.
|
||||||
|
+
|
||||||
|
A call to `bug()` will then result in a "real" call to the `BUG()`
|
||||||
|
function, either explicitly by invoking `BUG_if_bug()` after call(s)
|
||||||
|
to `bug()`, or implicitly at `exit()` time where we'll check if we
|
||||||
|
encountered any outstanding `bug()` invocations.
|
||||||
|
+
|
||||||
|
If there were no prior calls to `bug()` before invoking `BUG_if_bug()`
|
||||||
|
the latter is a NOOP. The `BUG_if_bug()` function takes the same
|
||||||
|
arguments as `BUG()` itself. Calling `BUG_if_bug()` explicitly isn't
|
||||||
|
necessary, but ensures that we die as soon as possible.
|
||||||
|
+
|
||||||
|
If you know you had prior calls to `bug()` then calling `BUG()` itself
|
||||||
|
is equivalent to calling `BUG_if_bug()`, the latter being a wrapper
|
||||||
|
calling `BUG()` if we've set a flag indicating that we've called
|
||||||
|
`bug()`.
|
||||||
|
+
|
||||||
|
This is for the convenience of APIs who'd like to potentially report
|
||||||
|
more than one "bug", such as the optbug() validation in
|
||||||
|
parse-options.c.
|
||||||
|
|
||||||
- `die` is for fatal application errors. It prints a message to
|
- `die` is for fatal application errors. It prints a message to
|
||||||
the user and exits with status 128.
|
the user and exits with status 128.
|
||||||
|
|
||||||
|
@ -465,8 +465,8 @@ completed.)
|
|||||||
------------
|
------------
|
||||||
|
|
||||||
`"error"`::
|
`"error"`::
|
||||||
This event is emitted when one of the `BUG()`, `error()`, `die()`,
|
This event is emitted when one of the `BUG()`, `bug()`, `error()`,
|
||||||
`warning()`, or `usage()` functions are called.
|
`die()`, `warning()`, or `usage()` functions are called.
|
||||||
+
|
+
|
||||||
------------
|
------------
|
||||||
{
|
{
|
||||||
|
@ -1810,21 +1810,17 @@ static int should_process_cmd(struct command *cmd)
|
|||||||
return !cmd->error_string && !cmd->skip_update;
|
return !cmd->error_string && !cmd->skip_update;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void warn_if_skipped_connectivity_check(struct command *commands,
|
static void BUG_if_skipped_connectivity_check(struct command *commands,
|
||||||
struct shallow_info *si)
|
struct shallow_info *si)
|
||||||
{
|
{
|
||||||
struct command *cmd;
|
struct command *cmd;
|
||||||
int checked_connectivity = 1;
|
|
||||||
|
|
||||||
for (cmd = commands; cmd; cmd = cmd->next) {
|
for (cmd = commands; cmd; cmd = cmd->next) {
|
||||||
if (should_process_cmd(cmd) && si->shallow_ref[cmd->index]) {
|
if (should_process_cmd(cmd) && si->shallow_ref[cmd->index])
|
||||||
error("BUG: connectivity check has not been run on ref %s",
|
bug("connectivity check has not been run on ref %s",
|
||||||
cmd->ref_name);
|
cmd->ref_name);
|
||||||
checked_connectivity = 0;
|
|
||||||
}
|
}
|
||||||
}
|
BUG_if_bug("connectivity check skipped???");
|
||||||
if (!checked_connectivity)
|
|
||||||
BUG("connectivity check skipped???");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void execute_commands_non_atomic(struct command *commands,
|
static void execute_commands_non_atomic(struct command *commands,
|
||||||
@ -2005,7 +2001,7 @@ static void execute_commands(struct command *commands,
|
|||||||
execute_commands_non_atomic(commands, si);
|
execute_commands_non_atomic(commands, si);
|
||||||
|
|
||||||
if (shallow_update)
|
if (shallow_update)
|
||||||
warn_if_skipped_connectivity_check(commands, si);
|
BUG_if_skipped_connectivity_check(commands, si);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct command **queue_command(struct command **tail,
|
static struct command **queue_command(struct command **tail,
|
||||||
|
@ -722,14 +722,14 @@ struct tree* write_in_core_index_as_tree(struct repository *repo) {
|
|||||||
ret = write_index_as_tree_internal(&o, index_state, was_valid, 0, NULL);
|
ret = write_index_as_tree_internal(&o, index_state, was_valid, 0, NULL);
|
||||||
if (ret == WRITE_TREE_UNMERGED_INDEX) {
|
if (ret == WRITE_TREE_UNMERGED_INDEX) {
|
||||||
int i;
|
int i;
|
||||||
fprintf(stderr, "BUG: There are unmerged index entries:\n");
|
bug("there are unmerged index entries:");
|
||||||
for (i = 0; i < index_state->cache_nr; i++) {
|
for (i = 0; i < index_state->cache_nr; i++) {
|
||||||
const struct cache_entry *ce = index_state->cache[i];
|
const struct cache_entry *ce = index_state->cache[i];
|
||||||
if (ce_stage(ce))
|
if (ce_stage(ce))
|
||||||
fprintf(stderr, "BUG: %d %.*s\n", ce_stage(ce),
|
bug("%d %.*s", ce_stage(ce),
|
||||||
(int)ce_namelen(ce), ce->name);
|
(int)ce_namelen(ce), ce->name);
|
||||||
}
|
}
|
||||||
BUG("unmerged index entries when writing inmemory index");
|
BUG("unmerged index entries when writing in-core index");
|
||||||
}
|
}
|
||||||
|
|
||||||
return lookup_tree(repo, &index_state->cache_tree->oid);
|
return lookup_tree(repo, &index_state->cache_tree->oid);
|
||||||
|
@ -55,10 +55,30 @@ int main(int argc, const char **argv)
|
|||||||
|
|
||||||
result = cmd_main(argc, argv);
|
result = cmd_main(argc, argv);
|
||||||
|
|
||||||
/*
|
/* Not exit(3), but a wrapper calling our common_exit() */
|
||||||
* We define exit() to call trace2_cmd_exit_fl() in
|
|
||||||
* git-compat-util.h. Whether we reach this or exit()
|
|
||||||
* elsewhere we'll always run our trace2 exit handler.
|
|
||||||
*/
|
|
||||||
exit(result);
|
exit(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void check_bug_if_BUG(void)
|
||||||
|
{
|
||||||
|
if (!bug_called_must_BUG)
|
||||||
|
return;
|
||||||
|
BUG("on exit(): had bug() call(s) in this process without explicit BUG_if_bug()");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We wrap exit() to call common_exit() in git-compat-util.h */
|
||||||
|
int common_exit(const char *file, int line, int code)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* For non-POSIX systems: Take the lowest 8 bits of the "code"
|
||||||
|
* to e.g. turn -1 into 255. On a POSIX system this is
|
||||||
|
* redundant, see exit(3) and wait(2), but as it doesn't harm
|
||||||
|
* anything there we don't need to guard this with an "ifdef".
|
||||||
|
*/
|
||||||
|
code &= 0xff;
|
||||||
|
|
||||||
|
check_bug_if_BUG();
|
||||||
|
trace2_cmd_exit_fl(file, line, code);
|
||||||
|
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
@ -1326,9 +1326,19 @@ static inline int regexec_buf(const regex_t *preg, const char *buf, size_t size,
|
|||||||
/* usage.c: only to be used for testing BUG() implementation (see test-tool) */
|
/* usage.c: only to be used for testing BUG() implementation (see test-tool) */
|
||||||
extern int BUG_exit_code;
|
extern int BUG_exit_code;
|
||||||
|
|
||||||
|
/* usage.c: if bug() is called we should have a BUG_if_bug() afterwards */
|
||||||
|
extern int bug_called_must_BUG;
|
||||||
|
|
||||||
__attribute__((format (printf, 3, 4))) NORETURN
|
__attribute__((format (printf, 3, 4))) NORETURN
|
||||||
void BUG_fl(const char *file, int line, const char *fmt, ...);
|
void BUG_fl(const char *file, int line, const char *fmt, ...);
|
||||||
#define BUG(...) BUG_fl(__FILE__, __LINE__, __VA_ARGS__)
|
#define BUG(...) BUG_fl(__FILE__, __LINE__, __VA_ARGS__)
|
||||||
|
__attribute__((format (printf, 3, 4)))
|
||||||
|
void bug_fl(const char *file, int line, const char *fmt, ...);
|
||||||
|
#define bug(...) bug_fl(__FILE__, __LINE__, __VA_ARGS__)
|
||||||
|
#define BUG_if_bug(...) do { \
|
||||||
|
if (bug_called_must_BUG) \
|
||||||
|
BUG_fl(__FILE__, __LINE__, __VA_ARGS__); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
#ifndef FSYNC_METHOD_DEFAULT
|
#ifndef FSYNC_METHOD_DEFAULT
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
@ -1459,8 +1469,8 @@ int cmd_main(int, const char **);
|
|||||||
* Intercept all calls to exit() and route them to trace2 to
|
* Intercept all calls to exit() and route them to trace2 to
|
||||||
* optionally emit a message before calling the real exit().
|
* optionally emit a message before calling the real exit().
|
||||||
*/
|
*/
|
||||||
int trace2_cmd_exit_fl(const char *file, int line, int code);
|
int common_exit(const char *file, int line, int code);
|
||||||
#define exit(code) exit(trace2_cmd_exit_fl(__FILE__, __LINE__, (code)))
|
#define exit(code) exit(common_exit(__FILE__, __LINE__, (code)))
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* You can mark a stack variable with UNLEAK(var) to avoid it being
|
* You can mark a stack variable with UNLEAK(var) to avoid it being
|
||||||
|
@ -14,15 +14,15 @@ enum opt_parsed {
|
|||||||
OPT_UNSET = 1<<1,
|
OPT_UNSET = 1<<1,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int optbug(const struct option *opt, const char *reason)
|
static void optbug(const struct option *opt, const char *reason)
|
||||||
{
|
{
|
||||||
if (opt->long_name) {
|
if (opt->long_name && opt->short_name)
|
||||||
if (opt->short_name)
|
bug("switch '%c' (--%s) %s", opt->short_name,
|
||||||
return error("BUG: switch '%c' (--%s) %s",
|
opt->long_name, reason);
|
||||||
opt->short_name, opt->long_name, reason);
|
else if (opt->long_name)
|
||||||
return error("BUG: option '%s' %s", opt->long_name, reason);
|
bug("option '%s' %s", opt->long_name, reason);
|
||||||
}
|
else
|
||||||
return error("BUG: switch '%c' %s", opt->short_name, reason);
|
bug("switch '%c' %s", opt->short_name, reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *optname(const struct option *opt, enum opt_parsed flags)
|
static const char *optname(const struct option *opt, enum opt_parsed flags)
|
||||||
@ -441,27 +441,26 @@ static void check_typos(const char *arg, const struct option *options)
|
|||||||
|
|
||||||
static void parse_options_check(const struct option *opts)
|
static void parse_options_check(const struct option *opts)
|
||||||
{
|
{
|
||||||
int err = 0;
|
|
||||||
char short_opts[128];
|
char short_opts[128];
|
||||||
|
|
||||||
memset(short_opts, '\0', sizeof(short_opts));
|
memset(short_opts, '\0', sizeof(short_opts));
|
||||||
for (; opts->type != OPTION_END; opts++) {
|
for (; opts->type != OPTION_END; opts++) {
|
||||||
if ((opts->flags & PARSE_OPT_LASTARG_DEFAULT) &&
|
if ((opts->flags & PARSE_OPT_LASTARG_DEFAULT) &&
|
||||||
(opts->flags & PARSE_OPT_OPTARG))
|
(opts->flags & PARSE_OPT_OPTARG))
|
||||||
err |= optbug(opts, "uses incompatible flags "
|
optbug(opts, "uses incompatible flags "
|
||||||
"LASTARG_DEFAULT and OPTARG");
|
"LASTARG_DEFAULT and OPTARG");
|
||||||
if (opts->short_name) {
|
if (opts->short_name) {
|
||||||
if (0x7F <= opts->short_name)
|
if (0x7F <= opts->short_name)
|
||||||
err |= optbug(opts, "invalid short name");
|
optbug(opts, "invalid short name");
|
||||||
else if (short_opts[opts->short_name]++)
|
else if (short_opts[opts->short_name]++)
|
||||||
err |= optbug(opts, "short name already used");
|
optbug(opts, "short name already used");
|
||||||
}
|
}
|
||||||
if (opts->flags & PARSE_OPT_NODASH &&
|
if (opts->flags & PARSE_OPT_NODASH &&
|
||||||
((opts->flags & PARSE_OPT_OPTARG) ||
|
((opts->flags & PARSE_OPT_OPTARG) ||
|
||||||
!(opts->flags & PARSE_OPT_NOARG) ||
|
!(opts->flags & PARSE_OPT_NOARG) ||
|
||||||
!(opts->flags & PARSE_OPT_NONEG) ||
|
!(opts->flags & PARSE_OPT_NONEG) ||
|
||||||
opts->long_name))
|
opts->long_name))
|
||||||
err |= optbug(opts, "uses feature "
|
optbug(opts, "uses feature "
|
||||||
"not supported for dashless options");
|
"not supported for dashless options");
|
||||||
switch (opts->type) {
|
switch (opts->type) {
|
||||||
case OPTION_COUNTUP:
|
case OPTION_COUNTUP:
|
||||||
@ -471,33 +470,33 @@ static void parse_options_check(const struct option *opts)
|
|||||||
case OPTION_NUMBER:
|
case OPTION_NUMBER:
|
||||||
if ((opts->flags & PARSE_OPT_OPTARG) ||
|
if ((opts->flags & PARSE_OPT_OPTARG) ||
|
||||||
!(opts->flags & PARSE_OPT_NOARG))
|
!(opts->flags & PARSE_OPT_NOARG))
|
||||||
err |= optbug(opts, "should not accept an argument");
|
optbug(opts, "should not accept an argument");
|
||||||
break;
|
break;
|
||||||
case OPTION_CALLBACK:
|
case OPTION_CALLBACK:
|
||||||
if (!opts->callback && !opts->ll_callback)
|
if (!opts->callback && !opts->ll_callback)
|
||||||
BUG("OPTION_CALLBACK needs one callback");
|
optbug(opts, "OPTION_CALLBACK needs one callback");
|
||||||
if (opts->callback && opts->ll_callback)
|
else if (opts->callback && opts->ll_callback)
|
||||||
BUG("OPTION_CALLBACK can't have two callbacks");
|
optbug(opts, "OPTION_CALLBACK can't have two callbacks");
|
||||||
break;
|
break;
|
||||||
case OPTION_LOWLEVEL_CALLBACK:
|
case OPTION_LOWLEVEL_CALLBACK:
|
||||||
if (!opts->ll_callback)
|
if (!opts->ll_callback)
|
||||||
BUG("OPTION_LOWLEVEL_CALLBACK needs a callback");
|
optbug(opts, "OPTION_LOWLEVEL_CALLBACK needs a callback");
|
||||||
if (opts->callback)
|
if (opts->callback)
|
||||||
BUG("OPTION_LOWLEVEL_CALLBACK needs no high level callback");
|
optbug(opts, "OPTION_LOWLEVEL_CALLBACK needs no high level callback");
|
||||||
break;
|
break;
|
||||||
case OPTION_ALIAS:
|
case OPTION_ALIAS:
|
||||||
BUG("OPT_ALIAS() should not remain at this point. "
|
optbug(opts, "OPT_ALIAS() should not remain at this point. "
|
||||||
"Are you using parse_options_step() directly?\n"
|
"Are you using parse_options_step() directly?\n"
|
||||||
"That case is not supported yet.");
|
"That case is not supported yet.");
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
; /* ok. (usually accepts an argument) */
|
; /* ok. (usually accepts an argument) */
|
||||||
}
|
}
|
||||||
if (opts->argh &&
|
if (opts->argh &&
|
||||||
strcspn(opts->argh, " _") != strlen(opts->argh))
|
strcspn(opts->argh, " _") != strlen(opts->argh))
|
||||||
err |= optbug(opts, "multi-word argh should use dash to separate words");
|
optbug(opts, "multi-word argh should use dash to separate words");
|
||||||
}
|
}
|
||||||
if (err)
|
BUG_if_bug("invalid 'struct option'");
|
||||||
exit(128);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void parse_options_start_1(struct parse_opt_ctx_t *ctx,
|
static void parse_options_start_1(struct parse_opt_ctx_t *ctx,
|
||||||
|
@ -198,7 +198,7 @@ static int ut_006data(int argc, const char **argv)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ut_007bug(int argc, const char **argv)
|
static int ut_007BUG(int argc, const char **argv)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Exercise BUG() to ensure that the message is printed to trace2.
|
* Exercise BUG() to ensure that the message is printed to trace2.
|
||||||
@ -206,6 +206,28 @@ static int ut_007bug(int argc, const char **argv)
|
|||||||
BUG("the bug message");
|
BUG("the bug message");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ut_008bug(int argc, const char **argv)
|
||||||
|
{
|
||||||
|
bug("a bug message");
|
||||||
|
bug("another bug message");
|
||||||
|
BUG_if_bug("an explicit BUG_if_bug() following bug() call(s) is nice, but not required");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ut_009bug_BUG(int argc, const char **argv)
|
||||||
|
{
|
||||||
|
bug("a bug message");
|
||||||
|
bug("another bug message");
|
||||||
|
/* The BUG_if_bug(...) isn't here, but we'll spot bug() calls on exit()! */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ut_010bug_BUG(int argc, const char **argv)
|
||||||
|
{
|
||||||
|
bug("a bug message");
|
||||||
|
BUG("a BUG message");
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Usage:
|
* Usage:
|
||||||
* test-tool trace2 <ut_name_1> <ut_usage_1>
|
* test-tool trace2 <ut_name_1> <ut_usage_1>
|
||||||
@ -222,7 +244,10 @@ static struct unit_test ut_table[] = {
|
|||||||
{ ut_004child, "004child", "[<child_command_line>]" },
|
{ ut_004child, "004child", "[<child_command_line>]" },
|
||||||
{ ut_005exec, "005exec", "<git_command_args>" },
|
{ ut_005exec, "005exec", "<git_command_args>" },
|
||||||
{ ut_006data, "006data", "[<category> <key> <value>]+" },
|
{ ut_006data, "006data", "[<category> <key> <value>]+" },
|
||||||
{ ut_007bug, "007bug", "" },
|
{ ut_007BUG, "007bug", "" },
|
||||||
|
{ ut_008bug, "008bug", "" },
|
||||||
|
{ ut_009bug_BUG, "009bug_BUG","" },
|
||||||
|
{ ut_010bug_BUG, "010bug_BUG","" },
|
||||||
};
|
};
|
||||||
/* clang-format on */
|
/* clang-format on */
|
||||||
|
|
||||||
|
@ -168,6 +168,82 @@ test_expect_success 'BUG messages are written to trace2' '
|
|||||||
test_cmp expect actual
|
test_cmp expect actual
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'bug messages with BUG_if_bug() are written to trace2' '
|
||||||
|
test_when_finished "rm trace.normal actual expect" &&
|
||||||
|
test_expect_code 99 env GIT_TRACE2="$(pwd)/trace.normal" \
|
||||||
|
test-tool trace2 008bug 2>err &&
|
||||||
|
cat >expect <<-\EOF &&
|
||||||
|
a bug message
|
||||||
|
another bug message
|
||||||
|
an explicit BUG_if_bug() following bug() call(s) is nice, but not required
|
||||||
|
EOF
|
||||||
|
sed "s/^.*: //" <err >actual &&
|
||||||
|
test_cmp expect actual &&
|
||||||
|
|
||||||
|
perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual &&
|
||||||
|
cat >expect <<-EOF &&
|
||||||
|
version $V
|
||||||
|
start _EXE_ trace2 008bug
|
||||||
|
cmd_name trace2 (trace2)
|
||||||
|
error a bug message
|
||||||
|
error another bug message
|
||||||
|
error an explicit BUG_if_bug() following bug() call(s) is nice, but not required
|
||||||
|
exit elapsed:_TIME_ code:99
|
||||||
|
atexit elapsed:_TIME_ code:99
|
||||||
|
EOF
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'bug messages without explicit BUG_if_bug() are written to trace2' '
|
||||||
|
test_when_finished "rm trace.normal actual expect" &&
|
||||||
|
test_expect_code 99 env GIT_TRACE2="$(pwd)/trace.normal" \
|
||||||
|
test-tool trace2 009bug_BUG 2>err &&
|
||||||
|
cat >expect <<-\EOF &&
|
||||||
|
a bug message
|
||||||
|
another bug message
|
||||||
|
had bug() call(s) in this process without explicit BUG_if_bug()
|
||||||
|
EOF
|
||||||
|
sed "s/^.*: //" <err >actual &&
|
||||||
|
test_cmp expect actual &&
|
||||||
|
|
||||||
|
perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual &&
|
||||||
|
cat >expect <<-EOF &&
|
||||||
|
version $V
|
||||||
|
start _EXE_ trace2 009bug_BUG
|
||||||
|
cmd_name trace2 (trace2)
|
||||||
|
error a bug message
|
||||||
|
error another bug message
|
||||||
|
error on exit(): had bug() call(s) in this process without explicit BUG_if_bug()
|
||||||
|
exit elapsed:_TIME_ code:99
|
||||||
|
atexit elapsed:_TIME_ code:99
|
||||||
|
EOF
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'bug messages followed by BUG() are written to trace2' '
|
||||||
|
test_when_finished "rm trace.normal actual expect" &&
|
||||||
|
test_expect_code 99 env GIT_TRACE2="$(pwd)/trace.normal" \
|
||||||
|
test-tool trace2 010bug_BUG 2>err &&
|
||||||
|
cat >expect <<-\EOF &&
|
||||||
|
a bug message
|
||||||
|
a BUG message
|
||||||
|
EOF
|
||||||
|
sed "s/^.*: //" <err >actual &&
|
||||||
|
test_cmp expect actual &&
|
||||||
|
|
||||||
|
perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual &&
|
||||||
|
cat >expect <<-EOF &&
|
||||||
|
version $V
|
||||||
|
start _EXE_ trace2 010bug_BUG
|
||||||
|
cmd_name trace2 (trace2)
|
||||||
|
error a bug message
|
||||||
|
error a BUG message
|
||||||
|
exit elapsed:_TIME_ code:99
|
||||||
|
atexit elapsed:_TIME_ code:99
|
||||||
|
EOF
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
sane_unset GIT_TRACE2_BRIEF
|
sane_unset GIT_TRACE2_BRIEF
|
||||||
|
|
||||||
# Now test without environment variables and get all Trace2 settings
|
# Now test without environment variables and get all Trace2 settings
|
||||||
|
8
trace2.c
8
trace2.c
@ -202,17 +202,15 @@ void trace2_cmd_start_fl(const char *file, int line, const char **argv)
|
|||||||
argv);
|
argv);
|
||||||
}
|
}
|
||||||
|
|
||||||
int trace2_cmd_exit_fl(const char *file, int line, int code)
|
void trace2_cmd_exit_fl(const char *file, int line, int code)
|
||||||
{
|
{
|
||||||
struct tr2_tgt *tgt_j;
|
struct tr2_tgt *tgt_j;
|
||||||
int j;
|
int j;
|
||||||
uint64_t us_now;
|
uint64_t us_now;
|
||||||
uint64_t us_elapsed_absolute;
|
uint64_t us_elapsed_absolute;
|
||||||
|
|
||||||
code &= 0xff;
|
|
||||||
|
|
||||||
if (!trace2_enabled)
|
if (!trace2_enabled)
|
||||||
return code;
|
return;
|
||||||
|
|
||||||
trace_git_fsync_stats();
|
trace_git_fsync_stats();
|
||||||
trace2_collect_process_info(TRACE2_PROCESS_INFO_EXIT);
|
trace2_collect_process_info(TRACE2_PROCESS_INFO_EXIT);
|
||||||
@ -226,8 +224,6 @@ int trace2_cmd_exit_fl(const char *file, int line, int code)
|
|||||||
if (tgt_j->pfn_exit_fl)
|
if (tgt_j->pfn_exit_fl)
|
||||||
tgt_j->pfn_exit_fl(file, line, us_elapsed_absolute,
|
tgt_j->pfn_exit_fl(file, line, us_elapsed_absolute,
|
||||||
code);
|
code);
|
||||||
|
|
||||||
return code;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void trace2_cmd_error_va_fl(const char *file, int line, const char *fmt,
|
void trace2_cmd_error_va_fl(const char *file, int line, const char *fmt,
|
||||||
|
8
trace2.h
8
trace2.h
@ -101,14 +101,8 @@ void trace2_cmd_start_fl(const char *file, int line, const char **argv);
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Emit an 'exit' event.
|
* Emit an 'exit' event.
|
||||||
*
|
|
||||||
* Write the exit-code that will be passed to exit() or returned
|
|
||||||
* from main().
|
|
||||||
*
|
|
||||||
* Use this prior to actually calling exit().
|
|
||||||
* See "#define exit()" in git-compat-util.h
|
|
||||||
*/
|
*/
|
||||||
int trace2_cmd_exit_fl(const char *file, int line, int code);
|
void trace2_cmd_exit_fl(const char *file, int line, int code);
|
||||||
|
|
||||||
#define trace2_cmd_exit(code) (trace2_cmd_exit_fl(__FILE__, __LINE__, (code)))
|
#define trace2_cmd_exit(code) (trace2_cmd_exit_fl(__FILE__, __LINE__, (code)))
|
||||||
|
|
||||||
|
33
usage.c
33
usage.c
@ -290,18 +290,24 @@ void warning(const char *warn, ...)
|
|||||||
/* Only set this, ever, from t/helper/, when verifying that bugs are caught. */
|
/* Only set this, ever, from t/helper/, when verifying that bugs are caught. */
|
||||||
int BUG_exit_code;
|
int BUG_exit_code;
|
||||||
|
|
||||||
static NORETURN void BUG_vfl(const char *file, int line, const char *fmt, va_list params)
|
static void BUG_vfl_common(const char *file, int line, const char *fmt,
|
||||||
|
va_list params)
|
||||||
{
|
{
|
||||||
char prefix[256];
|
char prefix[256];
|
||||||
va_list params_copy;
|
|
||||||
static int in_bug;
|
|
||||||
|
|
||||||
va_copy(params_copy, params);
|
|
||||||
|
|
||||||
/* truncation via snprintf is OK here */
|
/* truncation via snprintf is OK here */
|
||||||
snprintf(prefix, sizeof(prefix), "BUG: %s:%d: ", file, line);
|
snprintf(prefix, sizeof(prefix), "BUG: %s:%d: ", file, line);
|
||||||
|
|
||||||
vreportf(prefix, fmt, params);
|
vreportf(prefix, fmt, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
static NORETURN void BUG_vfl(const char *file, int line, const char *fmt, va_list params)
|
||||||
|
{
|
||||||
|
va_list params_copy;
|
||||||
|
static int in_bug;
|
||||||
|
|
||||||
|
va_copy(params_copy, params);
|
||||||
|
BUG_vfl_common(file, line, fmt, params);
|
||||||
|
|
||||||
if (in_bug)
|
if (in_bug)
|
||||||
abort();
|
abort();
|
||||||
@ -317,11 +323,28 @@ static NORETURN void BUG_vfl(const char *file, int line, const char *fmt, va_lis
|
|||||||
NORETURN void BUG_fl(const char *file, int line, const char *fmt, ...)
|
NORETURN void BUG_fl(const char *file, int line, const char *fmt, ...)
|
||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
|
||||||
|
bug_called_must_BUG = 0;
|
||||||
|
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
BUG_vfl(file, line, fmt, ap);
|
BUG_vfl(file, line, fmt, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int bug_called_must_BUG;
|
||||||
|
void bug_fl(const char *file, int line, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap, cp;
|
||||||
|
|
||||||
|
bug_called_must_BUG = 1;
|
||||||
|
|
||||||
|
va_copy(cp, ap);
|
||||||
|
va_start(ap, fmt);
|
||||||
|
BUG_vfl_common(file, line, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
trace2_cmd_error_va(fmt, cp);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef SUPPRESS_ANNOTATED_LEAKS
|
#ifdef SUPPRESS_ANNOTATED_LEAKS
|
||||||
void unleak_memory(const void *ptr, size_t len)
|
void unleak_memory(const void *ptr, size_t len)
|
||||||
{
|
{
|
||||||
|
Reference in New Issue
Block a user