Commit 60cd37eb authored by Ingo Molnar's avatar Ingo Molnar

Merge tag 'perf-core-for-mingo' of...

Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core

Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo:

User visible changes:

  - Take tracefs into account when reporting errors about accessing
    tracepoint information in tools like 'perf trace' (Arnaldo Carvalho de Melo)

  - Let user have timestamps with per-thread recording in 'perf record' (Adrian Hunter)

Infrastructure changes:

  - Introduce series of functions to build event filters so that we
    can set them in just one ioctl call, useful to set up common_pid,
    raw_syscalls:sys_{enter,exit}'s "id" filters to use with
    'perf trace' (Arnaldo Carvalho de Melo)

  - Delete an unnecessary check before calling strfilter__delete() (Markus Elfring)
Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents ebf2d268 ab85785a
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include "debugfs.h" #include "debugfs.h"
#include "tracefs.h"
#ifndef DEBUGFS_DEFAULT_PATH #ifndef DEBUGFS_DEFAULT_PATH
#define DEBUGFS_DEFAULT_PATH "/sys/kernel/debug" #define DEBUGFS_DEFAULT_PATH "/sys/kernel/debug"
...@@ -94,11 +95,21 @@ int debugfs__strerror_open(int err, char *buf, size_t size, const char *filename ...@@ -94,11 +95,21 @@ int debugfs__strerror_open(int err, char *buf, size_t size, const char *filename
"Hint:\tIs the debugfs filesystem mounted?\n" "Hint:\tIs the debugfs filesystem mounted?\n"
"Hint:\tTry 'sudo mount -t debugfs nodev /sys/kernel/debug'"); "Hint:\tTry 'sudo mount -t debugfs nodev /sys/kernel/debug'");
break; break;
case EACCES: case EACCES: {
const char *mountpoint = debugfs_mountpoint;
if (!access(debugfs_mountpoint, R_OK) && strncmp(filename, "tracing/", 8) == 0) {
const char *tracefs_mntpoint = tracefs_find_mountpoint();
if (tracefs_mntpoint)
mountpoint = tracefs_mntpoint;
}
snprintf(buf, size, snprintf(buf, size,
"Error:\tNo permissions to read %s/%s\n" "Error:\tNo permissions to read %s/%s\n"
"Hint:\tTry 'sudo mount -o remount,mode=755 %s'\n", "Hint:\tTry 'sudo mount -o remount,mode=755 %s'\n",
debugfs_mountpoint, filename, debugfs_mountpoint); debugfs_mountpoint, filename, mountpoint);
}
break; break;
default: default:
snprintf(buf, size, "%s", strerror_r(err, sbuf, sizeof(sbuf))); snprintf(buf, size, "%s", strerror_r(err, sbuf, sizeof(sbuf)));
......
...@@ -297,8 +297,7 @@ static void cleanup_params(void) ...@@ -297,8 +297,7 @@ static void cleanup_params(void)
clear_perf_probe_event(params.events + i); clear_perf_probe_event(params.events + i);
line_range__clear(&params.line_range); line_range__clear(&params.line_range);
free(params.target); free(params.target);
if (params.filter) strfilter__delete(params.filter);
strfilter__delete(params.filter);
memset(&params, 0, sizeof(params)); memset(&params, 0, sizeof(params));
} }
......
...@@ -1030,7 +1030,9 @@ struct option __record_options[] = { ...@@ -1030,7 +1030,9 @@ struct option __record_options[] = {
OPT_BOOLEAN('s', "stat", &record.opts.inherit_stat, OPT_BOOLEAN('s', "stat", &record.opts.inherit_stat,
"per thread counts"), "per thread counts"),
OPT_BOOLEAN('d', "data", &record.opts.sample_address, "Record the sample addresses"), OPT_BOOLEAN('d', "data", &record.opts.sample_address, "Record the sample addresses"),
OPT_BOOLEAN('T', "timestamp", &record.opts.sample_time, "Record the sample timestamps"), OPT_BOOLEAN_SET('T', "timestamp", &record.opts.sample_time,
&record.opts.sample_time_set,
"Record the sample timestamps"),
OPT_BOOLEAN('P', "period", &record.opts.period, "Record the sample period"), OPT_BOOLEAN('P', "period", &record.opts.period, "Record the sample period"),
OPT_BOOLEAN('n', "no-samples", &record.opts.no_samples, OPT_BOOLEAN('n', "no-samples", &record.opts.no_samples,
"don't sample"), "don't sample"),
......
...@@ -247,42 +247,6 @@ static struct perf_evsel *perf_evsel__syscall_newtp(const char *direction, void ...@@ -247,42 +247,6 @@ static struct perf_evsel *perf_evsel__syscall_newtp(const char *direction, void
({ struct syscall_tp *fields = evsel->priv; \ ({ struct syscall_tp *fields = evsel->priv; \
fields->name.pointer(&fields->name, sample); }) fields->name.pointer(&fields->name, sample); })
static int perf_evlist__add_syscall_newtp(struct perf_evlist *evlist,
void *sys_enter_handler,
void *sys_exit_handler)
{
int ret = -1;
struct perf_evsel *sys_enter, *sys_exit;
sys_enter = perf_evsel__syscall_newtp("sys_enter", sys_enter_handler);
if (sys_enter == NULL)
goto out;
if (perf_evsel__init_sc_tp_ptr_field(sys_enter, args))
goto out_delete_sys_enter;
sys_exit = perf_evsel__syscall_newtp("sys_exit", sys_exit_handler);
if (sys_exit == NULL)
goto out_delete_sys_enter;
if (perf_evsel__init_sc_tp_uint_field(sys_exit, ret))
goto out_delete_sys_exit;
perf_evlist__add(evlist, sys_enter);
perf_evlist__add(evlist, sys_exit);
ret = 0;
out:
return ret;
out_delete_sys_exit:
perf_evsel__delete_priv(sys_exit);
out_delete_sys_enter:
perf_evsel__delete_priv(sys_enter);
goto out;
}
struct syscall_arg { struct syscall_arg {
unsigned long val; unsigned long val;
struct thread *thread; struct thread *thread;
...@@ -1223,7 +1187,6 @@ struct syscall { ...@@ -1223,7 +1187,6 @@ struct syscall {
int nr_args; int nr_args;
struct format_field *args; struct format_field *args;
const char *name; const char *name;
bool filtered;
bool is_exit; bool is_exit;
struct syscall_fmt *fmt; struct syscall_fmt *fmt;
size_t (**arg_scnprintf)(char *bf, size_t size, struct syscall_arg *arg); size_t (**arg_scnprintf)(char *bf, size_t size, struct syscall_arg *arg);
...@@ -1307,6 +1270,10 @@ struct trace { ...@@ -1307,6 +1270,10 @@ struct trace {
struct { struct {
int max; int max;
struct syscall *table; struct syscall *table;
struct {
struct perf_evsel *sys_enter,
*sys_exit;
} events;
} syscalls; } syscalls;
struct record_opts opts; struct record_opts opts;
struct perf_evlist *evlist; struct perf_evlist *evlist;
...@@ -1316,6 +1283,10 @@ struct trace { ...@@ -1316,6 +1283,10 @@ struct trace {
FILE *output; FILE *output;
unsigned long nr_events; unsigned long nr_events;
struct strlist *ev_qualifier; struct strlist *ev_qualifier;
struct {
size_t nr;
int *entries;
} ev_qualifier_ids;
const char *last_vfs_getname; const char *last_vfs_getname;
struct intlist *tid_list; struct intlist *tid_list;
struct intlist *pid_list; struct intlist *pid_list;
...@@ -1578,19 +1549,6 @@ static int trace__read_syscall_info(struct trace *trace, int id) ...@@ -1578,19 +1549,6 @@ static int trace__read_syscall_info(struct trace *trace, int id)
sc = trace->syscalls.table + id; sc = trace->syscalls.table + id;
sc->name = name; sc->name = name;
if (trace->ev_qualifier) {
bool in = strlist__find(trace->ev_qualifier, name) != NULL;
if (!(in ^ trace->not_ev_qualifier)) {
sc->filtered = true;
/*
* No need to do read tracepoint information since this will be
* filtered out.
*/
return 0;
}
}
sc->fmt = syscall_fmt__find(sc->name); sc->fmt = syscall_fmt__find(sc->name);
snprintf(tp_name, sizeof(tp_name), "sys_enter_%s", sc->name); snprintf(tp_name, sizeof(tp_name), "sys_enter_%s", sc->name);
...@@ -1619,13 +1577,27 @@ static int trace__read_syscall_info(struct trace *trace, int id) ...@@ -1619,13 +1577,27 @@ static int trace__read_syscall_info(struct trace *trace, int id)
static int trace__validate_ev_qualifier(struct trace *trace) static int trace__validate_ev_qualifier(struct trace *trace)
{ {
int err = 0; int err = 0, i;
struct str_node *pos; struct str_node *pos;
trace->ev_qualifier_ids.nr = strlist__nr_entries(trace->ev_qualifier);
trace->ev_qualifier_ids.entries = malloc(trace->ev_qualifier_ids.nr *
sizeof(trace->ev_qualifier_ids.entries[0]));
if (trace->ev_qualifier_ids.entries == NULL) {
fputs("Error:\tNot enough memory for allocating events qualifier ids\n",
trace->output);
err = -EINVAL;
goto out;
}
i = 0;
strlist__for_each(pos, trace->ev_qualifier) { strlist__for_each(pos, trace->ev_qualifier) {
const char *sc = pos->s; const char *sc = pos->s;
int id = audit_name_to_syscall(sc, trace->audit.machine);
if (audit_name_to_syscall(sc, trace->audit.machine) < 0) { if (id < 0) {
if (err == 0) { if (err == 0) {
fputs("Error:\tInvalid syscall ", trace->output); fputs("Error:\tInvalid syscall ", trace->output);
err = -EINVAL; err = -EINVAL;
...@@ -1635,13 +1607,17 @@ static int trace__validate_ev_qualifier(struct trace *trace) ...@@ -1635,13 +1607,17 @@ static int trace__validate_ev_qualifier(struct trace *trace)
fputs(sc, trace->output); fputs(sc, trace->output);
} }
trace->ev_qualifier_ids.entries[i++] = id;
} }
if (err < 0) { if (err < 0) {
fputs("\nHint:\ttry 'perf list syscalls:sys_enter_*'" fputs("\nHint:\ttry 'perf list syscalls:sys_enter_*'"
"\nHint:\tand: 'man syscalls'\n", trace->output); "\nHint:\tand: 'man syscalls'\n", trace->output);
zfree(&trace->ev_qualifier_ids.entries);
trace->ev_qualifier_ids.nr = 0;
} }
out:
return err; return err;
} }
...@@ -1833,9 +1809,6 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, ...@@ -1833,9 +1809,6 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel,
if (sc == NULL) if (sc == NULL)
return -1; return -1;
if (sc->filtered)
return 0;
thread = machine__findnew_thread(trace->host, sample->pid, sample->tid); thread = machine__findnew_thread(trace->host, sample->pid, sample->tid);
ttrace = thread__trace(thread, trace->output); ttrace = thread__trace(thread, trace->output);
if (ttrace == NULL) if (ttrace == NULL)
...@@ -1891,9 +1864,6 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, ...@@ -1891,9 +1864,6 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel,
if (sc == NULL) if (sc == NULL)
return -1; return -1;
if (sc->filtered)
return 0;
thread = machine__findnew_thread(trace->host, sample->pid, sample->tid); thread = machine__findnew_thread(trace->host, sample->pid, sample->tid);
ttrace = thread__trace(thread, trace->output); ttrace = thread__trace(thread, trace->output);
if (ttrace == NULL) if (ttrace == NULL)
...@@ -2283,9 +2253,68 @@ static void trace__handle_event(struct trace *trace, union perf_event *event, st ...@@ -2283,9 +2253,68 @@ static void trace__handle_event(struct trace *trace, union perf_event *event, st
} }
} }
static int trace__add_syscall_newtp(struct trace *trace)
{
int ret = -1;
struct perf_evlist *evlist = trace->evlist;
struct perf_evsel *sys_enter, *sys_exit;
sys_enter = perf_evsel__syscall_newtp("sys_enter", trace__sys_enter);
if (sys_enter == NULL)
goto out;
if (perf_evsel__init_sc_tp_ptr_field(sys_enter, args))
goto out_delete_sys_enter;
sys_exit = perf_evsel__syscall_newtp("sys_exit", trace__sys_exit);
if (sys_exit == NULL)
goto out_delete_sys_enter;
if (perf_evsel__init_sc_tp_uint_field(sys_exit, ret))
goto out_delete_sys_exit;
perf_evlist__add(evlist, sys_enter);
perf_evlist__add(evlist, sys_exit);
trace->syscalls.events.sys_enter = sys_enter;
trace->syscalls.events.sys_exit = sys_exit;
ret = 0;
out:
return ret;
out_delete_sys_exit:
perf_evsel__delete_priv(sys_exit);
out_delete_sys_enter:
perf_evsel__delete_priv(sys_enter);
goto out;
}
static int trace__set_ev_qualifier_filter(struct trace *trace)
{
int err = -1;
char *filter = asprintf_expr_inout_ints("id", !trace->not_ev_qualifier,
trace->ev_qualifier_ids.nr,
trace->ev_qualifier_ids.entries);
if (filter == NULL)
goto out_enomem;
if (!perf_evsel__append_filter(trace->syscalls.events.sys_enter, "&&", filter))
err = perf_evsel__append_filter(trace->syscalls.events.sys_exit, "&&", filter);
free(filter);
out:
return err;
out_enomem:
errno = ENOMEM;
goto out;
}
static int trace__run(struct trace *trace, int argc, const char **argv) static int trace__run(struct trace *trace, int argc, const char **argv)
{ {
struct perf_evlist *evlist = trace->evlist; struct perf_evlist *evlist = trace->evlist;
struct perf_evsel *evsel;
int err = -1, i; int err = -1, i;
unsigned long before; unsigned long before;
const bool forks = argc > 0; const bool forks = argc > 0;
...@@ -2293,9 +2322,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv) ...@@ -2293,9 +2322,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
trace->live = true; trace->live = true;
if (trace->trace_syscalls && if (trace->trace_syscalls && trace__add_syscall_newtp(trace))
perf_evlist__add_syscall_newtp(evlist, trace__sys_enter,
trace__sys_exit))
goto out_error_raw_syscalls; goto out_error_raw_syscalls;
if (trace->trace_syscalls) if (trace->trace_syscalls)
...@@ -2356,11 +2383,21 @@ static int trace__run(struct trace *trace, int argc, const char **argv) ...@@ -2356,11 +2383,21 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
else if (thread_map__pid(evlist->threads, 0) == -1) else if (thread_map__pid(evlist->threads, 0) == -1)
err = perf_evlist__set_filter_pid(evlist, getpid()); err = perf_evlist__set_filter_pid(evlist, getpid());
if (err < 0) { if (err < 0)
printf("err=%d,%s\n", -err, strerror(-err)); goto out_error_mem;
exit(1);
if (trace->ev_qualifier_ids.nr > 0) {
err = trace__set_ev_qualifier_filter(trace);
if (err < 0)
goto out_errno;
} }
pr_debug("%s\n", trace->syscalls.events.sys_exit->filter);
err = perf_evlist__apply_filters(evlist, &evsel);
if (err < 0)
goto out_error_apply_filters;
err = perf_evlist__mmap(evlist, trace->opts.mmap_pages, false); err = perf_evlist__mmap(evlist, trace->opts.mmap_pages, false);
if (err < 0) if (err < 0)
goto out_error_mmap; goto out_error_mmap;
...@@ -2462,10 +2499,21 @@ static int trace__run(struct trace *trace, int argc, const char **argv) ...@@ -2462,10 +2499,21 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
out_error: out_error:
fprintf(trace->output, "%s\n", errbuf); fprintf(trace->output, "%s\n", errbuf);
goto out_delete_evlist; goto out_delete_evlist;
out_error_apply_filters:
fprintf(trace->output,
"Failed to set filter \"%s\" on event %s with %d (%s)\n",
evsel->filter, perf_evsel__name(evsel), errno,
strerror_r(errno, errbuf, sizeof(errbuf)));
goto out_delete_evlist;
} }
out_error_mem: out_error_mem:
fprintf(trace->output, "Not enough memory to run!\n"); fprintf(trace->output, "Not enough memory to run!\n");
goto out_delete_evlist; goto out_delete_evlist;
out_errno:
fprintf(trace->output, "errno=%d,%s\n", errno, strerror(errno));
goto out_delete_evlist;
} }
static int trace__replay(struct trace *trace) static int trace__replay(struct trace *trace)
......
...@@ -51,6 +51,7 @@ struct record_opts { ...@@ -51,6 +51,7 @@ struct record_opts {
bool sample_address; bool sample_address;
bool sample_weight; bool sample_weight;
bool sample_time; bool sample_time;
bool sample_time_set;
bool period; bool period;
bool sample_intr_regs; bool sample_intr_regs;
bool running_time; bool running_time;
......
...@@ -1161,7 +1161,7 @@ int perf_evlist__apply_filters(struct perf_evlist *evlist, struct perf_evsel **e ...@@ -1161,7 +1161,7 @@ int perf_evlist__apply_filters(struct perf_evlist *evlist, struct perf_evsel **e
if (evsel->filter == NULL) if (evsel->filter == NULL)
continue; continue;
err = perf_evsel__set_filter(evsel, ncpus, nthreads, evsel->filter); err = perf_evsel__apply_filter(evsel, ncpus, nthreads, evsel->filter);
if (err) { if (err) {
*err_evsel = evsel; *err_evsel = evsel;
break; break;
...@@ -1175,11 +1175,9 @@ int perf_evlist__set_filter(struct perf_evlist *evlist, const char *filter) ...@@ -1175,11 +1175,9 @@ int perf_evlist__set_filter(struct perf_evlist *evlist, const char *filter)
{ {
struct perf_evsel *evsel; struct perf_evsel *evsel;
int err = 0; int err = 0;
const int ncpus = cpu_map__nr(evlist->cpus),
nthreads = thread_map__nr(evlist->threads);
evlist__for_each(evlist, evsel) { evlist__for_each(evlist, evsel) {
err = perf_evsel__set_filter(evsel, ncpus, nthreads, filter); err = perf_evsel__set_filter(evsel, filter);
if (err) if (err)
break; break;
} }
......
...@@ -707,7 +707,8 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts) ...@@ -707,7 +707,8 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts)
*/ */
if (opts->sample_time && if (opts->sample_time &&
(!perf_missing_features.sample_id_all && (!perf_missing_features.sample_id_all &&
(!opts->no_inherit || target__has_cpu(&opts->target) || per_cpu))) (!opts->no_inherit || target__has_cpu(&opts->target) || per_cpu ||
opts->sample_time_set)))
perf_evsel__set_sample_bit(evsel, TIME); perf_evsel__set_sample_bit(evsel, TIME);
if (opts->raw_samples && !evsel->no_aux_samples) { if (opts->raw_samples && !evsel->no_aux_samples) {
...@@ -815,14 +816,44 @@ static int perf_evsel__run_ioctl(struct perf_evsel *evsel, int ncpus, int nthrea ...@@ -815,14 +816,44 @@ static int perf_evsel__run_ioctl(struct perf_evsel *evsel, int ncpus, int nthrea
return 0; return 0;
} }
int perf_evsel__set_filter(struct perf_evsel *evsel, int ncpus, int nthreads, int perf_evsel__apply_filter(struct perf_evsel *evsel, int ncpus, int nthreads,
const char *filter) const char *filter)
{ {
return perf_evsel__run_ioctl(evsel, ncpus, nthreads, return perf_evsel__run_ioctl(evsel, ncpus, nthreads,
PERF_EVENT_IOC_SET_FILTER, PERF_EVENT_IOC_SET_FILTER,
(void *)filter); (void *)filter);
} }
int perf_evsel__set_filter(struct perf_evsel *evsel, const char *filter)
{
char *new_filter = strdup(filter);
if (new_filter != NULL) {
free(evsel->filter);
evsel->filter = new_filter;
return 0;
}
return -1;
}
int perf_evsel__append_filter(struct perf_evsel *evsel,
const char *op, const char *filter)
{
char *new_filter;
if (evsel->filter == NULL)
return perf_evsel__set_filter(evsel, filter);
if (asprintf(&new_filter,"(%s) %s (%s)", evsel->filter, op, filter) > 0) {
free(evsel->filter);
evsel->filter = new_filter;
return 0;
}
return -1;
}
int perf_evsel__enable(struct perf_evsel *evsel, int ncpus, int nthreads) int perf_evsel__enable(struct perf_evsel *evsel, int ncpus, int nthreads)
{ {
return perf_evsel__run_ioctl(evsel, ncpus, nthreads, return perf_evsel__run_ioctl(evsel, ncpus, nthreads,
......
...@@ -182,8 +182,11 @@ void __perf_evsel__reset_sample_bit(struct perf_evsel *evsel, ...@@ -182,8 +182,11 @@ void __perf_evsel__reset_sample_bit(struct perf_evsel *evsel,
void perf_evsel__set_sample_id(struct perf_evsel *evsel, void perf_evsel__set_sample_id(struct perf_evsel *evsel,
bool use_sample_identifier); bool use_sample_identifier);
int perf_evsel__set_filter(struct perf_evsel *evsel, int ncpus, int nthreads, int perf_evsel__set_filter(struct perf_evsel *evsel, const char *filter);
const char *filter); int perf_evsel__append_filter(struct perf_evsel *evsel,
const char *op, const char *filter);
int perf_evsel__apply_filter(struct perf_evsel *evsel, int ncpus, int nthreads,
const char *filter);
int perf_evsel__enable(struct perf_evsel *evsel, int ncpus, int nthreads); int perf_evsel__enable(struct perf_evsel *evsel, int ncpus, int nthreads);
int perf_evsel__open_per_cpu(struct perf_evsel *evsel, int perf_evsel__open_per_cpu(struct perf_evsel *evsel,
......
...@@ -1177,8 +1177,7 @@ int parse_filter(const struct option *opt, const char *str, ...@@ -1177,8 +1177,7 @@ int parse_filter(const struct option *opt, const char *str,
return -1; return -1;
} }
last->filter = strdup(str); if (perf_evsel__set_filter(last, str) < 0) {
if (last->filter == NULL) {
fprintf(stderr, "not enough memory to hold filter string\n"); fprintf(stderr, "not enough memory to hold filter string\n");
return -1; return -1;
} }
......
...@@ -357,3 +357,42 @@ void *memdup(const void *src, size_t len) ...@@ -357,3 +357,42 @@ void *memdup(const void *src, size_t len)
return p; return p;
} }
char *asprintf_expr_inout_ints(const char *var, bool in, size_t nints, int *ints)
{
/*
* FIXME: replace this with an expression using log10() when we
* find a suitable implementation, maybe the one in the dvb drivers...
*
* "%s == %d || " = log10(MAXINT) * 2 + 8 chars for the operators
*/
size_t size = nints * 28 + 1; /* \0 */
size_t i, printed = 0;
char *expr = malloc(size);
if (expr) {
const char *or_and = "||", *eq_neq = "==";
char *e = expr;
if (!in) {
or_and = "&&";
eq_neq = "!=";
}
for (i = 0; i < nints; ++i) {
if (printed == size)
goto out_err_overflow;
if (i > 0)
printed += snprintf(e + printed, size - printed, " %s ", or_and);
printed += scnprintf(e + printed, size - printed,
"%s %s %d", var, eq_neq, ints[i]);
}
}
return expr;
out_err_overflow:
free(expr);
return NULL;
}
...@@ -339,4 +339,16 @@ int gzip_decompress_to_file(const char *input, int output_fd); ...@@ -339,4 +339,16 @@ int gzip_decompress_to_file(const char *input, int output_fd);
int lzma_decompress_to_file(const char *input, int output_fd); int lzma_decompress_to_file(const char *input, int output_fd);
#endif #endif
char *asprintf_expr_inout_ints(const char *var, bool in, size_t nints, int *ints);
static inline char *asprintf_expr_in_ints(const char *var, size_t nints, int *ints)
{
return asprintf_expr_inout_ints(var, true, nints, ints);
}
static inline char *asprintf_expr_not_in_ints(const char *var, size_t nints, int *ints)
{
return asprintf_expr_inout_ints(var, false, nints, ints);
}
#endif /* GIT_COMPAT_UTIL_H */ #endif /* GIT_COMPAT_UTIL_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment