Browse Source

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: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Ingo Molnar 10 years ago
parent
commit
60cd37eb10

+ 13 - 2
tools/lib/api/fs/debugfs.c

@@ -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
 			 "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)));

+ 1 - 2
tools/perf/builtin-probe.c

@@ -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));
 }
 }
 
 

+ 3 - 1
tools/perf/builtin-record.c

@@ -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"),

+ 113 - 65
tools/perf/builtin-trace.c

@@ -247,42 +247,6 @@ out_delete:
 	({ 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 {
 	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 {
 	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 {
 	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)
 	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)
 
 
 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)
 
 
 			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,
 	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,
 	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
 	}
 	}
 }
 }
 
 
+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)
 
 
 	trace->live = true;
 	trace->live = true;
 
 
-	if (trace->trace_syscalls &&
-	    perf_evlist__add_syscall_newtp(evlist, trace__sys_enter,
-					   trace__sys_exit))
+	if (trace->trace_syscalls && trace__add_syscall_newtp(trace))
 		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)
 	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) {
-		printf("err=%d,%s\n", -err, strerror(-err));
-		exit(1);
+	if (err < 0)
+		goto out_error_mem;
+
+	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 @@ out_error_open:
 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)

+ 1 - 0
tools/perf/perf.h

@@ -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;

+ 2 - 4
tools/perf/util/evlist.c

@@ -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)
 {
 {
 	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;
 	}
 	}

+ 34 - 3
tools/perf/util/evsel.c

@@ -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
 	return 0;
 	return 0;
 }
 }
 
 
-int perf_evsel__set_filter(struct perf_evsel *evsel, int ncpus, int nthreads,
-			   const char *filter)
+int perf_evsel__apply_filter(struct perf_evsel *evsel, int ncpus, int nthreads,
+			     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,

+ 5 - 2
tools/perf/util/evsel.h

@@ -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,
-			   const char *filter);
+int perf_evsel__set_filter(struct perf_evsel *evsel, 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,

+ 1 - 2
tools/perf/util/parse-events.c

@@ -1177,8 +1177,7 @@ int parse_filter(const struct option *opt, const char *str,
 		return -1;
 		return -1;
 	}
 	}
 
 
-	last->filter = strdup(str);
-	if (last->filter == NULL) {
+	if (perf_evsel__set_filter(last, str) < 0) {
 		fprintf(stderr, "not enough memory to hold filter string\n");
 		fprintf(stderr, "not enough memory to hold filter string\n");
 		return -1;
 		return -1;
 	}
 	}

+ 39 - 0
tools/perf/util/string.c

@@ -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;
+}

+ 12 - 0
tools/perf/util/util.h

@@ -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 */