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:
commit
4da14b574f
@ -1,12 +1,34 @@
|
||||
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.
|
||||
|
||||
- `BUG` is for failed internal assertions that should never happen,
|
||||
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
|
||||
the user and exits with status 128.
|
||||
|
||||
|
@ -465,8 +465,8 @@ completed.)
|
||||
------------
|
||||
|
||||
`"error"`::
|
||||
This event is emitted when one of the `BUG()`, `error()`, `die()`,
|
||||
`warning()`, or `usage()` functions are called.
|
||||
This event is emitted when one of the `BUG()`, `bug()`, `error()`,
|
||||
`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;
|
||||
}
|
||||
|
||||
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 command *cmd;
|
||||
int checked_connectivity = 1;
|
||||
|
||||
for (cmd = commands; cmd; cmd = cmd->next) {
|
||||
if (should_process_cmd(cmd) && si->shallow_ref[cmd->index]) {
|
||||
error("BUG: connectivity check has not been run on ref %s",
|
||||
cmd->ref_name);
|
||||
checked_connectivity = 0;
|
||||
}
|
||||
if (should_process_cmd(cmd) && si->shallow_ref[cmd->index])
|
||||
bug("connectivity check has not been run on ref %s",
|
||||
cmd->ref_name);
|
||||
}
|
||||
if (!checked_connectivity)
|
||||
BUG("connectivity check skipped???");
|
||||
BUG_if_bug("connectivity check skipped???");
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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,
|
||||
|
@ -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);
|
||||
if (ret == WRITE_TREE_UNMERGED_INDEX) {
|
||||
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++) {
|
||||
const struct cache_entry *ce = index_state->cache[i];
|
||||
if (ce_stage(ce))
|
||||
fprintf(stderr, "BUG: %d %.*s\n", ce_stage(ce),
|
||||
(int)ce_namelen(ce), ce->name);
|
||||
bug("%d %.*s", ce_stage(ce),
|
||||
(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);
|
||||
|
@ -55,10 +55,30 @@ int main(int argc, const char **argv)
|
||||
|
||||
result = cmd_main(argc, argv);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
/* Not exit(3), but a wrapper calling our common_exit() */
|
||||
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) */
|
||||
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
|
||||
void BUG_fl(const char *file, int line, const char *fmt, ...);
|
||||
#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
|
||||
#ifdef __APPLE__
|
||||
@ -1459,8 +1469,8 @@ int cmd_main(int, const char **);
|
||||
* Intercept all calls to exit() and route them to trace2 to
|
||||
* optionally emit a message before calling the real exit().
|
||||
*/
|
||||
int trace2_cmd_exit_fl(const char *file, int line, int code);
|
||||
#define exit(code) exit(trace2_cmd_exit_fl(__FILE__, __LINE__, (code)))
|
||||
int common_exit(const char *file, int line, int code);
|
||||
#define exit(code) exit(common_exit(__FILE__, __LINE__, (code)))
|
||||
|
||||
/*
|
||||
* You can mark a stack variable with UNLEAK(var) to avoid it being
|
||||
|
@ -14,15 +14,15 @@ enum opt_parsed {
|
||||
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->short_name)
|
||||
return error("BUG: switch '%c' (--%s) %s",
|
||||
opt->short_name, opt->long_name, reason);
|
||||
return error("BUG: option '%s' %s", opt->long_name, reason);
|
||||
}
|
||||
return error("BUG: switch '%c' %s", opt->short_name, reason);
|
||||
if (opt->long_name && opt->short_name)
|
||||
bug("switch '%c' (--%s) %s", opt->short_name,
|
||||
opt->long_name, reason);
|
||||
else if (opt->long_name)
|
||||
bug("option '%s' %s", opt->long_name, reason);
|
||||
else
|
||||
bug("switch '%c' %s", opt->short_name, reason);
|
||||
}
|
||||
|
||||
static const char *optname(const struct option *opt, enum opt_parsed flags)
|
||||
@ -441,28 +441,27 @@ static void check_typos(const char *arg, const struct option *options)
|
||||
|
||||
static void parse_options_check(const struct option *opts)
|
||||
{
|
||||
int err = 0;
|
||||
char short_opts[128];
|
||||
|
||||
memset(short_opts, '\0', sizeof(short_opts));
|
||||
for (; opts->type != OPTION_END; opts++) {
|
||||
if ((opts->flags & PARSE_OPT_LASTARG_DEFAULT) &&
|
||||
(opts->flags & PARSE_OPT_OPTARG))
|
||||
err |= optbug(opts, "uses incompatible flags "
|
||||
"LASTARG_DEFAULT and OPTARG");
|
||||
optbug(opts, "uses incompatible flags "
|
||||
"LASTARG_DEFAULT and OPTARG");
|
||||
if (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]++)
|
||||
err |= optbug(opts, "short name already used");
|
||||
optbug(opts, "short name already used");
|
||||
}
|
||||
if (opts->flags & PARSE_OPT_NODASH &&
|
||||
((opts->flags & PARSE_OPT_OPTARG) ||
|
||||
!(opts->flags & PARSE_OPT_NOARG) ||
|
||||
!(opts->flags & PARSE_OPT_NONEG) ||
|
||||
opts->long_name))
|
||||
err |= optbug(opts, "uses feature "
|
||||
"not supported for dashless options");
|
||||
optbug(opts, "uses feature "
|
||||
"not supported for dashless options");
|
||||
switch (opts->type) {
|
||||
case OPTION_COUNTUP:
|
||||
case OPTION_BIT:
|
||||
@ -471,33 +470,33 @@ static void parse_options_check(const struct option *opts)
|
||||
case OPTION_NUMBER:
|
||||
if ((opts->flags & PARSE_OPT_OPTARG) ||
|
||||
!(opts->flags & PARSE_OPT_NOARG))
|
||||
err |= optbug(opts, "should not accept an argument");
|
||||
optbug(opts, "should not accept an argument");
|
||||
break;
|
||||
case OPTION_CALLBACK:
|
||||
if (!opts->callback && !opts->ll_callback)
|
||||
BUG("OPTION_CALLBACK needs one callback");
|
||||
if (opts->callback && opts->ll_callback)
|
||||
BUG("OPTION_CALLBACK can't have two callbacks");
|
||||
optbug(opts, "OPTION_CALLBACK needs one callback");
|
||||
else if (opts->callback && opts->ll_callback)
|
||||
optbug(opts, "OPTION_CALLBACK can't have two callbacks");
|
||||
break;
|
||||
case OPTION_LOWLEVEL_CALLBACK:
|
||||
if (!opts->ll_callback)
|
||||
BUG("OPTION_LOWLEVEL_CALLBACK needs a callback");
|
||||
optbug(opts, "OPTION_LOWLEVEL_CALLBACK needs a callback");
|
||||
if (opts->callback)
|
||||
BUG("OPTION_LOWLEVEL_CALLBACK needs no high level callback");
|
||||
optbug(opts, "OPTION_LOWLEVEL_CALLBACK needs no high level callback");
|
||||
break;
|
||||
case OPTION_ALIAS:
|
||||
BUG("OPT_ALIAS() should not remain at this point. "
|
||||
"Are you using parse_options_step() directly?\n"
|
||||
"That case is not supported yet.");
|
||||
optbug(opts, "OPT_ALIAS() should not remain at this point. "
|
||||
"Are you using parse_options_step() directly?\n"
|
||||
"That case is not supported yet.");
|
||||
break;
|
||||
default:
|
||||
; /* ok. (usually accepts an argument) */
|
||||
}
|
||||
if (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)
|
||||
exit(128);
|
||||
BUG_if_bug("invalid 'struct option'");
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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.
|
||||
@ -206,6 +206,28 @@ static int ut_007bug(int argc, const char **argv)
|
||||
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:
|
||||
* 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_005exec, "005exec", "<git_command_args>" },
|
||||
{ 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 */
|
||||
|
||||
|
@ -168,6 +168,82 @@ test_expect_success 'BUG messages are written to trace2' '
|
||||
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
|
||||
|
||||
# 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);
|
||||
}
|
||||
|
||||
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;
|
||||
int j;
|
||||
uint64_t us_now;
|
||||
uint64_t us_elapsed_absolute;
|
||||
|
||||
code &= 0xff;
|
||||
|
||||
if (!trace2_enabled)
|
||||
return code;
|
||||
return;
|
||||
|
||||
trace_git_fsync_stats();
|
||||
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)
|
||||
tgt_j->pfn_exit_fl(file, line, us_elapsed_absolute,
|
||||
code);
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
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.
|
||||
*
|
||||
* 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)))
|
||||
|
||||
|
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. */
|
||||
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];
|
||||
va_list params_copy;
|
||||
static int in_bug;
|
||||
|
||||
va_copy(params_copy, params);
|
||||
|
||||
/* truncation via snprintf is OK here */
|
||||
snprintf(prefix, sizeof(prefix), "BUG: %s:%d: ", file, line);
|
||||
|
||||
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)
|
||||
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, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
bug_called_must_BUG = 0;
|
||||
|
||||
va_start(ap, fmt);
|
||||
BUG_vfl(file, line, fmt, 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
|
||||
void unleak_memory(const void *ptr, size_t len)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user