mirror of
https://github.com/FEX-Emu/linux.git
synced 2024-12-28 12:25:31 +00:00
perf/core improvements and fixes:
New features: - Allow filtering perf's pid via 'perf record --exclude-perf' (Wang Nan) - 'perf trace' now supports syscall groups, like strace, i.e: $ trace -e file touch file Will expand 'file' into multiple, file related, syscalls. More work needed to add extra groups for other syscall groups, and also to complement what was added for the 'file' group, included as a proof of concept. (Arnaldo Carvalho de Melo) - Add lock_pi stresser to 'perf bench futex', to test the kernel code related to FUTEX_(UN)LOCK_PI (Davidlohr Bueso) User visible fixes: - Apply --filter to all events in a glob matching, not just the last one (Wang Nan) Documentation: - Document setting '-e pmu/period=N/' in the 'perf record' man page (Kan Liang) Infrastructure: - 'perf probe' code simplifications and movements to separate files (Masami Hiramatsu) - Fix makefile generation under 'dash' (Sergei Trofimovich) Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJVrWBvAAoJENZQFvNTUqpAPRsQAIuTZfE/2nJne2bTbEMBcK6e 86z+lqRVmyS0xKwrStao804XtgYLBb5x2kxCsIO8ADVPK+N1EZ5LGgl53AZE62ZY bvTz74rTDVue0ZdsnqrG8hVlkq+GDT//HU8DvJ0vB+rPsrysc5kEDnAgqCMY9ZzR pFgp8NQjE8Ue5R4s10fXglK58eWmHxF6SDpYUQuZVdfc8RLd+kB3HBOqdcQ82VLj PKOhtY+2KyWGYHeDoywF4m7qlUaqP8ezpPQNdL8Wp8+0aMF/OQpLw7gwct2Kokd8 1G/7gukY0z+iWnOSMxyiUXHAP0bMTQKNYLHsjsb4LU16zrGLYBJGvzb22xjMzpoi uzbDEGn4paaKAdxZcUL9x45Gx4JOrmQD103VB4qTj0d8jKg1o32V7O/InYuPVFFc mHuJxDmxm2QkXSNRtob3U6rgEAWDXcj0i/e23s2P3PEYllw4e8dAjOmKAHIV8nPZ xOxyZyerfGciGYjKBx2cBWjW6kXljQDu1wqmI6sNFTKQYFs1zg7k7sikgF/nsoo8 LLEqGmqfwo2Y45lVAXEhPYQYy2Fvfop2mPX3uYWl2rnnwnOgEBNcT+ovr0/4/WFv RRUSGaqFSyzlqzSZTZXxz0YEIEJvfw1HOZIUrAsY3xTTXXbR09bPNSUkG2ycugqv O5pTv8rktLO8qfXFY22S =EM3X -----END PGP SIGNATURE----- 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: New features: - Allow filtering out of perf's PID via 'perf record --exclude-perf'. (Wang Nan) - 'perf trace' now supports syscall groups, like strace, i.e: $ trace -e file touch file Will expand 'file' into multiple, file related, syscalls. More work needed to add extra groups for other syscall groups, and also to complement what was added for the 'file' group, included as a proof of concept. (Arnaldo Carvalho de Melo) - Add lock_pi stresser to 'perf bench futex', to test the kernel code related to FUTEX_(UN)LOCK_PI. (Davidlohr Bueso) User visible fixes: - Apply --filter to all events in a glob matching, not just the last one. (Wang Nan) Documentation changes: - Document setting '-e pmu/period=N/' in the 'perf record' man page. (Kan Liang) Infrastructure changes: - 'perf probe' code simplifications and movements to separate files. (Masami Hiramatsu) - Fix makefile generation under 'dash'. (Sergei Trofimovich) Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
commit
a11c51acc5
@ -35,6 +35,7 @@ paths += -DPERF_MAN_PATH="BUILD_STR($(mandir_SQ))"
|
||||
CFLAGS_builtin-help.o += $(paths)
|
||||
CFLAGS_builtin-timechart.o += $(paths)
|
||||
CFLAGS_perf.o += -DPERF_HTML_PATH="BUILD_STR($(htmldir_SQ))" -include $(OUTPUT)PERF-VERSION-FILE
|
||||
CFLAGS_builtin-trace.o += -DSTRACE_GROUPS_DIR="BUILD_STR($(STRACE_GROUPS_DIR_SQ))"
|
||||
|
||||
libperf-y += util/
|
||||
libperf-y += arch/
|
||||
|
@ -216,6 +216,10 @@ Suite for evaluating parallel wake calls.
|
||||
*requeue*::
|
||||
Suite for evaluating requeue calls.
|
||||
|
||||
*lock-pi*::
|
||||
Suite for evaluating futex lock_pi calls.
|
||||
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf[1]
|
||||
|
@ -45,6 +45,14 @@ OPTIONS
|
||||
param1 and param2 are defined as formats for the PMU in:
|
||||
/sys/bus/event_sources/devices/<pmu>/format/*
|
||||
|
||||
There are also some params which are not defined in .../<pmu>/format/*.
|
||||
These params can be used to set event defaults.
|
||||
Here is a list of the params.
|
||||
- 'period': Set event sampling period
|
||||
|
||||
Note: If user explicitly sets options which conflict with the params,
|
||||
the value set by the params will be overridden.
|
||||
|
||||
- a hardware breakpoint event in the form of '\mem:addr[/len][:access]'
|
||||
where addr is the address in memory you want to break in.
|
||||
Access is the memory access type (read, write, execute) it can
|
||||
@ -61,7 +69,16 @@ OPTIONS
|
||||
"perf report" to view group events together.
|
||||
|
||||
--filter=<filter>::
|
||||
Event filter.
|
||||
Event filter. This option should follow a event selector (-e) which
|
||||
selects tracepoint event(s). Multiple '--filter' options are combined
|
||||
using '&&'.
|
||||
|
||||
--exclude-perf::
|
||||
Don't record events issued by perf itself. This option should follow
|
||||
a event selector (-e) which selects tracepoint event(s). It adds a
|
||||
filter expression 'common_pid != $PERFPID' to filters. If other
|
||||
'--filter' exists, the new filter expression will be combined with
|
||||
them by '&&'.
|
||||
|
||||
-a::
|
||||
--all-cpus::
|
||||
|
@ -507,6 +507,11 @@ endif
|
||||
$(INSTALL) $(OUTPUT)perf-archive -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
|
||||
$(call QUIET_INSTALL, perf-with-kcore) \
|
||||
$(INSTALL) $(OUTPUT)perf-with-kcore -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
|
||||
ifndef NO_LIBAUDIT
|
||||
$(call QUIET_INSTALL, strace/groups) \
|
||||
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(STRACE_GROUPS_INSTDIR_SQ)'; \
|
||||
$(INSTALL) trace/strace/groups/* -t '$(DESTDIR_SQ)$(STRACE_GROUPS_INSTDIR_SQ)'
|
||||
endif
|
||||
ifndef NO_LIBPERL
|
||||
$(call QUIET_INSTALL, perl-scripts) \
|
||||
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace'; \
|
||||
|
@ -5,6 +5,7 @@ perf-y += futex-hash.o
|
||||
perf-y += futex-wake.o
|
||||
perf-y += futex-wake-parallel.o
|
||||
perf-y += futex-requeue.o
|
||||
perf-y += futex-lock-pi.o
|
||||
|
||||
perf-$(CONFIG_X86_64) += mem-memcpy-x86-64-asm.o
|
||||
perf-$(CONFIG_X86_64) += mem-memset-x86-64-asm.o
|
||||
|
@ -36,6 +36,8 @@ extern int bench_futex_wake(int argc, const char **argv, const char *prefix);
|
||||
extern int bench_futex_wake_parallel(int argc, const char **argv,
|
||||
const char *prefix);
|
||||
extern int bench_futex_requeue(int argc, const char **argv, const char *prefix);
|
||||
/* pi futexes */
|
||||
extern int bench_futex_lock_pi(int argc, const char **argv, const char *prefix);
|
||||
|
||||
#define BENCH_FORMAT_DEFAULT_STR "default"
|
||||
#define BENCH_FORMAT_DEFAULT 0
|
||||
|
219
tools/perf/bench/futex-lock-pi.c
Normal file
219
tools/perf/bench/futex-lock-pi.c
Normal file
@ -0,0 +1,219 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Davidlohr Bueso.
|
||||
*/
|
||||
|
||||
#include "../perf.h"
|
||||
#include "../util/util.h"
|
||||
#include "../util/stat.h"
|
||||
#include "../util/parse-options.h"
|
||||
#include "../util/header.h"
|
||||
#include "bench.h"
|
||||
#include "futex.h"
|
||||
|
||||
#include <err.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/time.h>
|
||||
#include <pthread.h>
|
||||
|
||||
struct worker {
|
||||
int tid;
|
||||
u_int32_t *futex;
|
||||
pthread_t thread;
|
||||
unsigned long ops;
|
||||
};
|
||||
|
||||
static u_int32_t global_futex = 0;
|
||||
static struct worker *worker;
|
||||
static unsigned int nsecs = 10;
|
||||
static bool silent = false, multi = false;
|
||||
static bool done = false, fshared = false;
|
||||
static unsigned int ncpus, nthreads = 0;
|
||||
static int futex_flag = 0;
|
||||
struct timeval start, end, runtime;
|
||||
static pthread_mutex_t thread_lock;
|
||||
static unsigned int threads_starting;
|
||||
static struct stats throughput_stats;
|
||||
static pthread_cond_t thread_parent, thread_worker;
|
||||
|
||||
static const struct option options[] = {
|
||||
OPT_UINTEGER('t', "threads", &nthreads, "Specify amount of threads"),
|
||||
OPT_UINTEGER('r', "runtime", &nsecs, "Specify runtime (in seconds)"),
|
||||
OPT_BOOLEAN( 'M', "multi", &multi, "Use multiple futexes"),
|
||||
OPT_BOOLEAN( 's', "silent", &silent, "Silent mode: do not display data/details"),
|
||||
OPT_BOOLEAN( 'S', "shared", &fshared, "Use shared futexes instead of private ones"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
static const char * const bench_futex_lock_pi_usage[] = {
|
||||
"perf bench futex requeue <options>",
|
||||
NULL
|
||||
};
|
||||
|
||||
static void print_summary(void)
|
||||
{
|
||||
unsigned long avg = avg_stats(&throughput_stats);
|
||||
double stddev = stddev_stats(&throughput_stats);
|
||||
|
||||
printf("%sAveraged %ld operations/sec (+- %.2f%%), total secs = %d\n",
|
||||
!silent ? "\n" : "", avg, rel_stddev_stats(stddev, avg),
|
||||
(int) runtime.tv_sec);
|
||||
}
|
||||
|
||||
static void toggle_done(int sig __maybe_unused,
|
||||
siginfo_t *info __maybe_unused,
|
||||
void *uc __maybe_unused)
|
||||
{
|
||||
/* inform all threads that we're done for the day */
|
||||
done = true;
|
||||
gettimeofday(&end, NULL);
|
||||
timersub(&end, &start, &runtime);
|
||||
}
|
||||
|
||||
static void *workerfn(void *arg)
|
||||
{
|
||||
struct worker *w = (struct worker *) arg;
|
||||
|
||||
pthread_mutex_lock(&thread_lock);
|
||||
threads_starting--;
|
||||
if (!threads_starting)
|
||||
pthread_cond_signal(&thread_parent);
|
||||
pthread_cond_wait(&thread_worker, &thread_lock);
|
||||
pthread_mutex_unlock(&thread_lock);
|
||||
|
||||
do {
|
||||
int ret;
|
||||
again:
|
||||
ret = futex_lock_pi(w->futex, NULL, 0, futex_flag);
|
||||
|
||||
if (ret) { /* handle lock acquisition */
|
||||
if (!silent)
|
||||
warn("thread %d: Could not lock pi-lock for %p (%d)",
|
||||
w->tid, w->futex, ret);
|
||||
if (done)
|
||||
break;
|
||||
|
||||
goto again;
|
||||
}
|
||||
|
||||
usleep(1);
|
||||
ret = futex_unlock_pi(w->futex, futex_flag);
|
||||
if (ret && !silent)
|
||||
warn("thread %d: Could not unlock pi-lock for %p (%d)",
|
||||
w->tid, w->futex, ret);
|
||||
w->ops++; /* account for thread's share of work */
|
||||
} while (!done);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void create_threads(struct worker *w, pthread_attr_t thread_attr)
|
||||
{
|
||||
cpu_set_t cpu;
|
||||
unsigned int i;
|
||||
|
||||
threads_starting = nthreads;
|
||||
|
||||
for (i = 0; i < nthreads; i++) {
|
||||
worker[i].tid = i;
|
||||
|
||||
if (multi) {
|
||||
worker[i].futex = calloc(1, sizeof(u_int32_t));
|
||||
if (!worker[i].futex)
|
||||
err(EXIT_FAILURE, "calloc");
|
||||
} else
|
||||
worker[i].futex = &global_futex;
|
||||
|
||||
CPU_ZERO(&cpu);
|
||||
CPU_SET(i % ncpus, &cpu);
|
||||
|
||||
if (pthread_attr_setaffinity_np(&thread_attr, sizeof(cpu_set_t), &cpu))
|
||||
err(EXIT_FAILURE, "pthread_attr_setaffinity_np");
|
||||
|
||||
if (pthread_create(&w[i].thread, &thread_attr, workerfn, &worker[i]))
|
||||
err(EXIT_FAILURE, "pthread_create");
|
||||
}
|
||||
}
|
||||
|
||||
int bench_futex_lock_pi(int argc, const char **argv,
|
||||
const char *prefix __maybe_unused)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned int i;
|
||||
struct sigaction act;
|
||||
pthread_attr_t thread_attr;
|
||||
|
||||
argc = parse_options(argc, argv, options, bench_futex_lock_pi_usage, 0);
|
||||
if (argc)
|
||||
goto err;
|
||||
|
||||
ncpus = sysconf(_SC_NPROCESSORS_ONLN);
|
||||
|
||||
sigfillset(&act.sa_mask);
|
||||
act.sa_sigaction = toggle_done;
|
||||
sigaction(SIGINT, &act, NULL);
|
||||
|
||||
if (!nthreads)
|
||||
nthreads = ncpus;
|
||||
|
||||
worker = calloc(nthreads, sizeof(*worker));
|
||||
if (!worker)
|
||||
err(EXIT_FAILURE, "calloc");
|
||||
|
||||
if (!fshared)
|
||||
futex_flag = FUTEX_PRIVATE_FLAG;
|
||||
|
||||
printf("Run summary [PID %d]: %d threads doing pi lock/unlock pairing for %d secs.\n\n",
|
||||
getpid(), nthreads, nsecs);
|
||||
|
||||
init_stats(&throughput_stats);
|
||||
pthread_mutex_init(&thread_lock, NULL);
|
||||
pthread_cond_init(&thread_parent, NULL);
|
||||
pthread_cond_init(&thread_worker, NULL);
|
||||
|
||||
threads_starting = nthreads;
|
||||
pthread_attr_init(&thread_attr);
|
||||
gettimeofday(&start, NULL);
|
||||
|
||||
create_threads(worker, thread_attr);
|
||||
pthread_attr_destroy(&thread_attr);
|
||||
|
||||
pthread_mutex_lock(&thread_lock);
|
||||
while (threads_starting)
|
||||
pthread_cond_wait(&thread_parent, &thread_lock);
|
||||
pthread_cond_broadcast(&thread_worker);
|
||||
pthread_mutex_unlock(&thread_lock);
|
||||
|
||||
sleep(nsecs);
|
||||
toggle_done(0, NULL, NULL);
|
||||
|
||||
for (i = 0; i < nthreads; i++) {
|
||||
ret = pthread_join(worker[i].thread, NULL);
|
||||
if (ret)
|
||||
err(EXIT_FAILURE, "pthread_join");
|
||||
}
|
||||
|
||||
/* cleanup & report results */
|
||||
pthread_cond_destroy(&thread_parent);
|
||||
pthread_cond_destroy(&thread_worker);
|
||||
pthread_mutex_destroy(&thread_lock);
|
||||
|
||||
for (i = 0; i < nthreads; i++) {
|
||||
unsigned long t = worker[i].ops/runtime.tv_sec;
|
||||
|
||||
update_stats(&throughput_stats, t);
|
||||
if (!silent)
|
||||
printf("[thread %3d] futex: %p [ %ld ops/sec ]\n",
|
||||
worker[i].tid, worker[i].futex, t);
|
||||
|
||||
if (multi)
|
||||
free(worker[i].futex);
|
||||
}
|
||||
|
||||
print_summary();
|
||||
|
||||
free(worker);
|
||||
return ret;
|
||||
err:
|
||||
usage_with_options(bench_futex_lock_pi_usage, options);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
@ -55,6 +55,26 @@ futex_wake(u_int32_t *uaddr, int nr_wake, int opflags)
|
||||
return futex(uaddr, FUTEX_WAKE, nr_wake, NULL, NULL, 0, opflags);
|
||||
}
|
||||
|
||||
/**
|
||||
* futex_lock_pi() - block on uaddr as a PI mutex
|
||||
* @detect: whether (1) or not (0) to perform deadlock detection
|
||||
*/
|
||||
static inline int
|
||||
futex_lock_pi(u_int32_t *uaddr, struct timespec *timeout, int detect,
|
||||
int opflags)
|
||||
{
|
||||
return futex(uaddr, FUTEX_LOCK_PI, detect, timeout, NULL, 0, opflags);
|
||||
}
|
||||
|
||||
/**
|
||||
* futex_unlock_pi() - release uaddr as a PI mutex, waking the top waiter
|
||||
*/
|
||||
static inline int
|
||||
futex_unlock_pi(u_int32_t *uaddr, int opflags)
|
||||
{
|
||||
return futex(uaddr, FUTEX_UNLOCK_PI, 0, NULL, NULL, 0, opflags);
|
||||
}
|
||||
|
||||
/**
|
||||
* futex_cmp_requeue() - requeue tasks from uaddr to uaddr2
|
||||
* @nr_wake: wake up to this many tasks
|
||||
|
@ -60,6 +60,8 @@ static struct bench futex_benchmarks[] = {
|
||||
{ "wake", "Benchmark for futex wake calls", bench_futex_wake },
|
||||
{ "wake-parallel", "Benchmark for parallel futex wake calls", bench_futex_wake_parallel },
|
||||
{ "requeue", "Benchmark for futex requeue calls", bench_futex_requeue },
|
||||
/* pi-futexes */
|
||||
{ "lock-pi", "Benchmark for futex lock_pi calls", bench_futex_lock_pi },
|
||||
{ "all", "Test all futex benchmarks", NULL },
|
||||
{ NULL, NULL, NULL }
|
||||
};
|
||||
|
@ -127,7 +127,7 @@ static int build_id_cache__kcore_existing(const char *from_dir, char *to_dir,
|
||||
|
||||
static int build_id_cache__add_kcore(const char *filename, bool force)
|
||||
{
|
||||
char dir[32], sbuildid[BUILD_ID_SIZE * 2 + 1];
|
||||
char dir[32], sbuildid[SBUILD_ID_SIZE];
|
||||
char from_dir[PATH_MAX], to_dir[PATH_MAX];
|
||||
char *p;
|
||||
|
||||
@ -184,7 +184,7 @@ static int build_id_cache__add_kcore(const char *filename, bool force)
|
||||
|
||||
static int build_id_cache__add_file(const char *filename)
|
||||
{
|
||||
char sbuild_id[BUILD_ID_SIZE * 2 + 1];
|
||||
char sbuild_id[SBUILD_ID_SIZE];
|
||||
u8 build_id[BUILD_ID_SIZE];
|
||||
int err;
|
||||
|
||||
@ -204,7 +204,7 @@ static int build_id_cache__add_file(const char *filename)
|
||||
static int build_id_cache__remove_file(const char *filename)
|
||||
{
|
||||
u8 build_id[BUILD_ID_SIZE];
|
||||
char sbuild_id[BUILD_ID_SIZE * 2 + 1];
|
||||
char sbuild_id[SBUILD_ID_SIZE];
|
||||
|
||||
int err;
|
||||
|
||||
@ -276,7 +276,7 @@ static int build_id_cache__fprintf_missing(struct perf_session *session, FILE *f
|
||||
static int build_id_cache__update_file(const char *filename)
|
||||
{
|
||||
u8 build_id[BUILD_ID_SIZE];
|
||||
char sbuild_id[BUILD_ID_SIZE * 2 + 1];
|
||||
char sbuild_id[SBUILD_ID_SIZE];
|
||||
|
||||
int err = 0;
|
||||
|
||||
@ -363,7 +363,7 @@ int cmd_buildid_cache(int argc, const char **argv,
|
||||
setup_pager();
|
||||
|
||||
if (add_name_list_str) {
|
||||
list = strlist__new(true, add_name_list_str);
|
||||
list = strlist__new(add_name_list_str, NULL);
|
||||
if (list) {
|
||||
strlist__for_each(pos, list)
|
||||
if (build_id_cache__add_file(pos->s)) {
|
||||
@ -381,7 +381,7 @@ int cmd_buildid_cache(int argc, const char **argv,
|
||||
}
|
||||
|
||||
if (remove_name_list_str) {
|
||||
list = strlist__new(true, remove_name_list_str);
|
||||
list = strlist__new(remove_name_list_str, NULL);
|
||||
if (list) {
|
||||
strlist__for_each(pos, list)
|
||||
if (build_id_cache__remove_file(pos->s)) {
|
||||
@ -399,7 +399,7 @@ int cmd_buildid_cache(int argc, const char **argv,
|
||||
}
|
||||
|
||||
if (purge_name_list_str) {
|
||||
list = strlist__new(true, purge_name_list_str);
|
||||
list = strlist__new(purge_name_list_str, NULL);
|
||||
if (list) {
|
||||
strlist__for_each(pos, list)
|
||||
if (build_id_cache__purge_path(pos->s)) {
|
||||
@ -420,7 +420,7 @@ int cmd_buildid_cache(int argc, const char **argv,
|
||||
ret = build_id_cache__fprintf_missing(session, stdout);
|
||||
|
||||
if (update_name_list_str) {
|
||||
list = strlist__new(true, update_name_list_str);
|
||||
list = strlist__new(update_name_list_str, NULL);
|
||||
if (list) {
|
||||
strlist__for_each(pos, list)
|
||||
if (build_id_cache__update_file(pos->s)) {
|
||||
|
@ -20,7 +20,7 @@
|
||||
static int sysfs__fprintf_build_id(FILE *fp)
|
||||
{
|
||||
u8 kallsyms_build_id[BUILD_ID_SIZE];
|
||||
char sbuild_id[BUILD_ID_SIZE * 2 + 1];
|
||||
char sbuild_id[SBUILD_ID_SIZE];
|
||||
|
||||
if (sysfs__read_build_id("/sys/kernel/notes", kallsyms_build_id,
|
||||
sizeof(kallsyms_build_id)) != 0)
|
||||
@ -35,7 +35,7 @@ static int sysfs__fprintf_build_id(FILE *fp)
|
||||
static int filename__fprintf_build_id(const char *name, FILE *fp)
|
||||
{
|
||||
u8 build_id[BUILD_ID_SIZE];
|
||||
char sbuild_id[BUILD_ID_SIZE * 2 + 1];
|
||||
char sbuild_id[SBUILD_ID_SIZE];
|
||||
|
||||
if (filename__read_build_id(name, build_id,
|
||||
sizeof(build_id)) != sizeof(build_id))
|
||||
|
@ -992,6 +992,9 @@ struct option __record_options[] = {
|
||||
parse_events_option),
|
||||
OPT_CALLBACK(0, "filter", &record.evlist, "filter",
|
||||
"event filter", parse_filter),
|
||||
OPT_CALLBACK_NOOPT(0, "exclude-perf", &record.evlist,
|
||||
NULL, "don't record events from perf itself",
|
||||
exclude_perf),
|
||||
OPT_STRING('p', "pid", &record.opts.target.pid, "pid",
|
||||
"record events on existing process id"),
|
||||
OPT_STRING('t', "tid", &record.opts.target.tid, "tid",
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "util/color.h"
|
||||
#include "util/debug.h"
|
||||
#include "util/evlist.h"
|
||||
#include "util/exec_cmd.h"
|
||||
#include "util/machine.h"
|
||||
#include "util/session.h"
|
||||
#include "util/thread.h"
|
||||
@ -2927,11 +2928,14 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
|
||||
if (ev_qualifier_str != NULL) {
|
||||
const char *s = ev_qualifier_str;
|
||||
struct strlist_config slist_config = {
|
||||
.dirname = system_path(STRACE_GROUPS_DIR),
|
||||
};
|
||||
|
||||
trace.not_ev_qualifier = *s == '!';
|
||||
if (trace.not_ev_qualifier)
|
||||
++s;
|
||||
trace.ev_qualifier = strlist__new(true, s);
|
||||
trace.ev_qualifier = strlist__new(s, &slist_config);
|
||||
if (trace.ev_qualifier == NULL) {
|
||||
fputs("Not enough memory to parse event qualifier",
|
||||
trace.output);
|
||||
|
@ -11,7 +11,7 @@ ifneq ($(obj-perf),)
|
||||
obj-perf := $(abspath $(obj-perf))/
|
||||
endif
|
||||
|
||||
$(shell echo -n > $(OUTPUT).config-detected)
|
||||
$(shell printf "" > $(OUTPUT).config-detected)
|
||||
detected = $(shell echo "$(1)=y" >> $(OUTPUT).config-detected)
|
||||
detected_var = $(shell echo "$(1)=$($(1))" >> $(OUTPUT).config-detected)
|
||||
|
||||
@ -644,6 +644,7 @@ infodir = share/info
|
||||
perfexecdir = libexec/perf-core
|
||||
sharedir = $(prefix)/share
|
||||
template_dir = share/perf-core/templates
|
||||
STRACE_GROUPS_DIR = share/perf-core/strace/groups
|
||||
htmldir = share/doc/perf-doc
|
||||
ifeq ($(prefix),/usr)
|
||||
sysconfdir = /etc
|
||||
@ -663,6 +664,7 @@ libdir = $(prefix)/$(lib)
|
||||
|
||||
# Shell quote (do not use $(call) to accommodate ancient setups);
|
||||
ETC_PERFCONFIG_SQ = $(subst ','\'',$(ETC_PERFCONFIG))
|
||||
STRACE_GROUPS_DIR_SQ = $(subst ','\'',$(STRACE_GROUPS_DIR))
|
||||
DESTDIR_SQ = $(subst ','\'',$(DESTDIR))
|
||||
bindir_SQ = $(subst ','\'',$(bindir))
|
||||
mandir_SQ = $(subst ','\'',$(mandir))
|
||||
@ -676,10 +678,13 @@ libdir_SQ = $(subst ','\'',$(libdir))
|
||||
|
||||
ifneq ($(filter /%,$(firstword $(perfexecdir))),)
|
||||
perfexec_instdir = $(perfexecdir)
|
||||
STRACE_GROUPS_INSTDIR = $(STRACE_GROUPS_DIR)
|
||||
else
|
||||
perfexec_instdir = $(prefix)/$(perfexecdir)
|
||||
STRACE_GROUPS_INSTDIR = $(prefix)/$(STRACE_GROUPS_DIR)
|
||||
endif
|
||||
perfexec_instdir_SQ = $(subst ','\'',$(perfexec_instdir))
|
||||
STRACE_GROUPS_INSTDIR_SQ = $(subst ','\'',$(STRACE_GROUPS_INSTDIR))
|
||||
|
||||
# If we install to $(HOME) we keep the traceevent default:
|
||||
# $(HOME)/.traceevent/plugins
|
||||
@ -713,6 +718,7 @@ $(call detected_var,htmldir_SQ)
|
||||
$(call detected_var,infodir_SQ)
|
||||
$(call detected_var,mandir_SQ)
|
||||
$(call detected_var,ETC_PERFCONFIG_SQ)
|
||||
$(call detected_var,STRACE_GROUPS_DIR_SQ)
|
||||
$(call detected_var,prefix_SQ)
|
||||
$(call detected_var,perfexecdir_SQ)
|
||||
$(call detected_var,LIBDIR)
|
||||
|
18
tools/perf/trace/strace/groups/file
Normal file
18
tools/perf/trace/strace/groups/file
Normal file
@ -0,0 +1,18 @@
|
||||
access
|
||||
chmod
|
||||
creat
|
||||
execve
|
||||
faccessat
|
||||
getcwd
|
||||
lstat
|
||||
mkdir
|
||||
open
|
||||
openat
|
||||
quotactl
|
||||
readlink
|
||||
rename
|
||||
rmdir
|
||||
stat
|
||||
statfs
|
||||
symlink
|
||||
unlink
|
@ -79,6 +79,7 @@ libperf-$(CONFIG_AUXTRACE) += auxtrace.o
|
||||
libperf-y += parse-branch-options.o
|
||||
|
||||
libperf-$(CONFIG_LIBELF) += symbol-elf.o
|
||||
libperf-$(CONFIG_LIBELF) += probe-file.o
|
||||
libperf-$(CONFIG_LIBELF) += probe-event.o
|
||||
|
||||
ifndef CONFIG_LIBELF
|
||||
|
@ -124,7 +124,7 @@ static char *build_id__filename(const char *sbuild_id, char *bf, size_t size)
|
||||
|
||||
char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size)
|
||||
{
|
||||
char build_id_hex[BUILD_ID_SIZE * 2 + 1];
|
||||
char build_id_hex[SBUILD_ID_SIZE];
|
||||
|
||||
if (!dso->has_build_id)
|
||||
return NULL;
|
||||
@ -291,7 +291,7 @@ int build_id_cache__list_build_ids(const char *pathname,
|
||||
struct dirent *d;
|
||||
int ret = 0;
|
||||
|
||||
list = strlist__new(true, NULL);
|
||||
list = strlist__new(NULL, NULL);
|
||||
dir_name = build_id_cache__dirname_from_path(pathname, false, false);
|
||||
if (!list || !dir_name) {
|
||||
ret = -ENOMEM;
|
||||
@ -384,7 +384,7 @@ static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size,
|
||||
const char *name, bool is_kallsyms,
|
||||
bool is_vdso)
|
||||
{
|
||||
char sbuild_id[BUILD_ID_SIZE * 2 + 1];
|
||||
char sbuild_id[SBUILD_ID_SIZE];
|
||||
|
||||
build_id__sprintf(build_id, build_id_size, sbuild_id);
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
#ifndef PERF_BUILD_ID_H_
|
||||
#define PERF_BUILD_ID_H_ 1
|
||||
|
||||
#define BUILD_ID_SIZE 20
|
||||
#define BUILD_ID_SIZE 20
|
||||
#define SBUILD_ID_SIZE (BUILD_ID_SIZE * 2 + 1)
|
||||
|
||||
#include "tool.h"
|
||||
#include "strlist.h"
|
||||
|
@ -210,6 +210,7 @@ void perf_evsel__init(struct perf_evsel *evsel,
|
||||
perf_evsel__object.init(evsel);
|
||||
evsel->sample_size = __perf_evsel__sample_size(attr->sample_type);
|
||||
perf_evsel__calc_id_pos(evsel);
|
||||
evsel->cmdline_group_boundary = false;
|
||||
}
|
||||
|
||||
struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx)
|
||||
|
@ -86,6 +86,7 @@ struct perf_evsel {
|
||||
unsigned long *per_pkg_mask;
|
||||
struct perf_evsel *leader;
|
||||
char *group_name;
|
||||
bool cmdline_group_boundary;
|
||||
};
|
||||
|
||||
union u64_swap {
|
||||
|
@ -250,7 +250,7 @@ struct machine *machines__findnew(struct machines *machines, pid_t pid)
|
||||
static struct strlist *seen;
|
||||
|
||||
if (!seen)
|
||||
seen = strlist__new(true, NULL);
|
||||
seen = strlist__new(NULL, NULL);
|
||||
|
||||
if (!strlist__has_entry(seen, path)) {
|
||||
pr_err("Can't access file %s\n", path);
|
||||
|
@ -1065,8 +1065,13 @@ int parse_events(struct perf_evlist *evlist, const char *str,
|
||||
perf_pmu__parse_cleanup();
|
||||
if (!ret) {
|
||||
int entries = data.idx - evlist->nr_entries;
|
||||
struct perf_evsel *last;
|
||||
|
||||
perf_evlist__splice_list_tail(evlist, &data.list, entries);
|
||||
evlist->nr_groups += data.nr_groups;
|
||||
last = perf_evlist__last(evlist);
|
||||
last->cmdline_group_boundary = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1162,29 +1167,93 @@ int parse_events_option(const struct option *opt, const char *str,
|
||||
return ret;
|
||||
}
|
||||
|
||||
int parse_filter(const struct option *opt, const char *str,
|
||||
int unset __maybe_unused)
|
||||
static int
|
||||
foreach_evsel_in_last_glob(struct perf_evlist *evlist,
|
||||
int (*func)(struct perf_evsel *evsel,
|
||||
const void *arg),
|
||||
const void *arg)
|
||||
{
|
||||
struct perf_evlist *evlist = *(struct perf_evlist **)opt->value;
|
||||
struct perf_evsel *last = NULL;
|
||||
int err;
|
||||
|
||||
if (evlist->nr_entries > 0)
|
||||
last = perf_evlist__last(evlist);
|
||||
|
||||
if (last == NULL || last->attr.type != PERF_TYPE_TRACEPOINT) {
|
||||
do {
|
||||
err = (*func)(last, arg);
|
||||
if (err)
|
||||
return -1;
|
||||
if (!last)
|
||||
return 0;
|
||||
|
||||
if (last->node.prev == &evlist->entries)
|
||||
return 0;
|
||||
last = list_entry(last->node.prev, struct perf_evsel, node);
|
||||
} while (!last->cmdline_group_boundary);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_filter(struct perf_evsel *evsel, const void *arg)
|
||||
{
|
||||
const char *str = arg;
|
||||
|
||||
if (evsel == NULL || evsel->attr.type != PERF_TYPE_TRACEPOINT) {
|
||||
fprintf(stderr,
|
||||
"--filter option should follow a -e tracepoint option\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (perf_evsel__set_filter(last, str) < 0) {
|
||||
fprintf(stderr, "not enough memory to hold filter string\n");
|
||||
if (perf_evsel__append_filter(evsel, "&&", str) < 0) {
|
||||
fprintf(stderr,
|
||||
"not enough memory to hold filter string\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int parse_filter(const struct option *opt, const char *str,
|
||||
int unset __maybe_unused)
|
||||
{
|
||||
struct perf_evlist *evlist = *(struct perf_evlist **)opt->value;
|
||||
|
||||
return foreach_evsel_in_last_glob(evlist, set_filter,
|
||||
(const void *)str);
|
||||
}
|
||||
|
||||
static int add_exclude_perf_filter(struct perf_evsel *evsel,
|
||||
const void *arg __maybe_unused)
|
||||
{
|
||||
char new_filter[64];
|
||||
|
||||
if (evsel == NULL || evsel->attr.type != PERF_TYPE_TRACEPOINT) {
|
||||
fprintf(stderr,
|
||||
"--exclude-perf option should follow a -e tracepoint option\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
snprintf(new_filter, sizeof(new_filter), "common_pid != %d", getpid());
|
||||
|
||||
if (perf_evsel__append_filter(evsel, "&&", new_filter) < 0) {
|
||||
fprintf(stderr,
|
||||
"not enough memory to hold filter string\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int exclude_perf(const struct option *opt,
|
||||
const char *arg __maybe_unused,
|
||||
int unset __maybe_unused)
|
||||
{
|
||||
struct perf_evlist *evlist = *(struct perf_evlist **)opt->value;
|
||||
|
||||
return foreach_evsel_in_last_glob(evlist, add_exclude_perf_filter,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static const char * const event_type_descriptors[] = {
|
||||
"Hardware event",
|
||||
"Software event",
|
||||
|
@ -34,6 +34,7 @@ extern int parse_events(struct perf_evlist *evlist, const char *str,
|
||||
struct parse_events_error *error);
|
||||
extern int parse_events_terms(struct list_head *terms, const char *str);
|
||||
extern int parse_filter(const struct option *opt, const char *str, int unset);
|
||||
extern int exclude_perf(const struct option *opt, const char *arg, int unset);
|
||||
|
||||
#define EVENTS_HELP_MAX (128*1024)
|
||||
|
||||
|
@ -45,6 +45,7 @@
|
||||
#include "trace-event.h" /* For __maybe_unused */
|
||||
#include "probe-event.h"
|
||||
#include "probe-finder.h"
|
||||
#include "probe-file.h"
|
||||
#include "session.h"
|
||||
|
||||
#define MAX_CMDLEN 256
|
||||
@ -55,11 +56,7 @@ struct probe_conf probe_conf;
|
||||
|
||||
#define semantic_error(msg ...) pr_err("Semantic error :" msg)
|
||||
|
||||
/* If there is no space to write, returns -E2BIG. */
|
||||
static int e_snprintf(char *str, size_t size, const char *format, ...)
|
||||
__attribute__((format(printf, 3, 4)));
|
||||
|
||||
static int e_snprintf(char *str, size_t size, const char *format, ...)
|
||||
int e_snprintf(char *str, size_t size, const char *format, ...)
|
||||
{
|
||||
int ret;
|
||||
va_list ap;
|
||||
@ -72,7 +69,6 @@ static int e_snprintf(char *str, size_t size, const char *format, ...)
|
||||
}
|
||||
|
||||
static char *synthesize_perf_probe_point(struct perf_probe_point *pp);
|
||||
static void clear_probe_trace_event(struct probe_trace_event *tev);
|
||||
static struct machine *host_machine;
|
||||
|
||||
/* Initialize symbol maps and path of vmlinux/modules */
|
||||
@ -1467,8 +1463,7 @@ bool perf_probe_event_need_dwarf(struct perf_probe_event *pev)
|
||||
}
|
||||
|
||||
/* Parse probe_events event into struct probe_point */
|
||||
static int parse_probe_trace_command(const char *cmd,
|
||||
struct probe_trace_event *tev)
|
||||
int parse_probe_trace_command(const char *cmd, struct probe_trace_event *tev)
|
||||
{
|
||||
struct probe_trace_point *tp = &tev->point;
|
||||
char pr;
|
||||
@ -1951,7 +1946,7 @@ void clear_perf_probe_event(struct perf_probe_event *pev)
|
||||
memset(pev, 0, sizeof(*pev));
|
||||
}
|
||||
|
||||
static void clear_probe_trace_event(struct probe_trace_event *tev)
|
||||
void clear_probe_trace_event(struct probe_trace_event *tev)
|
||||
{
|
||||
struct probe_trace_arg_ref *ref, *next;
|
||||
int i;
|
||||
@ -1976,119 +1971,6 @@ static void clear_probe_trace_event(struct probe_trace_event *tev)
|
||||
memset(tev, 0, sizeof(*tev));
|
||||
}
|
||||
|
||||
static void print_open_warning(int err, bool is_kprobe)
|
||||
{
|
||||
char sbuf[STRERR_BUFSIZE];
|
||||
|
||||
if (err == -ENOENT) {
|
||||
const char *config;
|
||||
|
||||
if (!is_kprobe)
|
||||
config = "CONFIG_UPROBE_EVENTS";
|
||||
else
|
||||
config = "CONFIG_KPROBE_EVENTS";
|
||||
|
||||
pr_warning("%cprobe_events file does not exist"
|
||||
" - please rebuild kernel with %s.\n",
|
||||
is_kprobe ? 'k' : 'u', config);
|
||||
} else if (err == -ENOTSUP)
|
||||
pr_warning("Tracefs or debugfs is not mounted.\n");
|
||||
else
|
||||
pr_warning("Failed to open %cprobe_events: %s\n",
|
||||
is_kprobe ? 'k' : 'u',
|
||||
strerror_r(-err, sbuf, sizeof(sbuf)));
|
||||
}
|
||||
|
||||
static void print_both_open_warning(int kerr, int uerr)
|
||||
{
|
||||
/* Both kprobes and uprobes are disabled, warn it. */
|
||||
if (kerr == -ENOTSUP && uerr == -ENOTSUP)
|
||||
pr_warning("Tracefs or debugfs is not mounted.\n");
|
||||
else if (kerr == -ENOENT && uerr == -ENOENT)
|
||||
pr_warning("Please rebuild kernel with CONFIG_KPROBE_EVENTS "
|
||||
"or/and CONFIG_UPROBE_EVENTS.\n");
|
||||
else {
|
||||
char sbuf[STRERR_BUFSIZE];
|
||||
pr_warning("Failed to open kprobe events: %s.\n",
|
||||
strerror_r(-kerr, sbuf, sizeof(sbuf)));
|
||||
pr_warning("Failed to open uprobe events: %s.\n",
|
||||
strerror_r(-uerr, sbuf, sizeof(sbuf)));
|
||||
}
|
||||
}
|
||||
|
||||
static int open_probe_events(const char *trace_file, bool readwrite)
|
||||
{
|
||||
char buf[PATH_MAX];
|
||||
const char *__debugfs;
|
||||
const char *tracing_dir = "";
|
||||
int ret;
|
||||
|
||||
__debugfs = tracefs_find_mountpoint();
|
||||
if (__debugfs == NULL) {
|
||||
tracing_dir = "tracing/";
|
||||
|
||||
__debugfs = debugfs_find_mountpoint();
|
||||
if (__debugfs == NULL)
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
ret = e_snprintf(buf, PATH_MAX, "%s/%s%s",
|
||||
__debugfs, tracing_dir, trace_file);
|
||||
if (ret >= 0) {
|
||||
pr_debug("Opening %s write=%d\n", buf, readwrite);
|
||||
if (readwrite && !probe_event_dry_run)
|
||||
ret = open(buf, O_RDWR | O_APPEND, 0);
|
||||
else
|
||||
ret = open(buf, O_RDONLY, 0);
|
||||
|
||||
if (ret < 0)
|
||||
ret = -errno;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int open_kprobe_events(bool readwrite)
|
||||
{
|
||||
return open_probe_events("kprobe_events", readwrite);
|
||||
}
|
||||
|
||||
static int open_uprobe_events(bool readwrite)
|
||||
{
|
||||
return open_probe_events("uprobe_events", readwrite);
|
||||
}
|
||||
|
||||
/* Get raw string list of current kprobe_events or uprobe_events */
|
||||
static struct strlist *get_probe_trace_command_rawlist(int fd)
|
||||
{
|
||||
int ret, idx;
|
||||
FILE *fp;
|
||||
char buf[MAX_CMDLEN];
|
||||
char *p;
|
||||
struct strlist *sl;
|
||||
|
||||
sl = strlist__new(true, NULL);
|
||||
|
||||
fp = fdopen(dup(fd), "r");
|
||||
while (!feof(fp)) {
|
||||
p = fgets(buf, MAX_CMDLEN, fp);
|
||||
if (!p)
|
||||
break;
|
||||
|
||||
idx = strlen(p) - 1;
|
||||
if (p[idx] == '\n')
|
||||
p[idx] = '\0';
|
||||
ret = strlist__add(sl, buf);
|
||||
if (ret < 0) {
|
||||
pr_debug("strlist__add failed (%d)\n", ret);
|
||||
strlist__delete(sl);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
return sl;
|
||||
}
|
||||
|
||||
struct kprobe_blacklist_node {
|
||||
struct list_head list;
|
||||
unsigned long start;
|
||||
@ -2284,7 +2166,7 @@ static int __show_perf_probe_events(int fd, bool is_kprobe,
|
||||
memset(&tev, 0, sizeof(tev));
|
||||
memset(&pev, 0, sizeof(pev));
|
||||
|
||||
rawlist = get_probe_trace_command_rawlist(fd);
|
||||
rawlist = probe_file__get_rawlist(fd);
|
||||
if (!rawlist)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -2325,89 +2207,20 @@ int show_perf_probe_events(struct strfilter *filter)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
kp_fd = open_kprobe_events(false);
|
||||
if (kp_fd >= 0) {
|
||||
ret = probe_file__open_both(&kp_fd, &up_fd, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (kp_fd >= 0)
|
||||
ret = __show_perf_probe_events(kp_fd, true, filter);
|
||||
close(kp_fd);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
up_fd = open_uprobe_events(false);
|
||||
if (kp_fd < 0 && up_fd < 0) {
|
||||
print_both_open_warning(kp_fd, up_fd);
|
||||
ret = kp_fd;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (up_fd >= 0) {
|
||||
if (up_fd >= 0 && ret >= 0)
|
||||
ret = __show_perf_probe_events(up_fd, false, filter);
|
||||
if (kp_fd > 0)
|
||||
close(kp_fd);
|
||||
if (up_fd > 0)
|
||||
close(up_fd);
|
||||
}
|
||||
out:
|
||||
exit_symbol_maps();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Get current perf-probe event names */
|
||||
static struct strlist *get_probe_trace_event_names(int fd, bool include_group)
|
||||
{
|
||||
char buf[128];
|
||||
struct strlist *sl, *rawlist;
|
||||
struct str_node *ent;
|
||||
struct probe_trace_event tev;
|
||||
int ret = 0;
|
||||
|
||||
memset(&tev, 0, sizeof(tev));
|
||||
rawlist = get_probe_trace_command_rawlist(fd);
|
||||
if (!rawlist)
|
||||
return NULL;
|
||||
sl = strlist__new(true, NULL);
|
||||
strlist__for_each(ent, rawlist) {
|
||||
ret = parse_probe_trace_command(ent->s, &tev);
|
||||
if (ret < 0)
|
||||
break;
|
||||
if (include_group) {
|
||||
ret = e_snprintf(buf, 128, "%s:%s", tev.group,
|
||||
tev.event);
|
||||
if (ret >= 0)
|
||||
ret = strlist__add(sl, buf);
|
||||
} else
|
||||
ret = strlist__add(sl, tev.event);
|
||||
clear_probe_trace_event(&tev);
|
||||
if (ret < 0)
|
||||
break;
|
||||
}
|
||||
strlist__delete(rawlist);
|
||||
|
||||
if (ret < 0) {
|
||||
strlist__delete(sl);
|
||||
return NULL;
|
||||
}
|
||||
return sl;
|
||||
}
|
||||
|
||||
static int write_probe_trace_event(int fd, struct probe_trace_event *tev)
|
||||
{
|
||||
int ret = 0;
|
||||
char *buf = synthesize_probe_trace_command(tev);
|
||||
char sbuf[STRERR_BUFSIZE];
|
||||
|
||||
if (!buf) {
|
||||
pr_debug("Failed to synthesize probe trace event.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pr_debug("Writing event: %s\n", buf);
|
||||
if (!probe_event_dry_run) {
|
||||
ret = write(fd, buf, strlen(buf));
|
||||
if (ret <= 0) {
|
||||
ret = -errno;
|
||||
pr_warning("Failed to write event: %s\n",
|
||||
strerror_r(errno, sbuf, sizeof(sbuf)));
|
||||
}
|
||||
}
|
||||
free(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -2478,36 +2291,67 @@ out:
|
||||
free(buf);
|
||||
}
|
||||
|
||||
/* Set new name from original perf_probe_event and namelist */
|
||||
static int probe_trace_event__set_name(struct probe_trace_event *tev,
|
||||
struct perf_probe_event *pev,
|
||||
struct strlist *namelist,
|
||||
bool allow_suffix)
|
||||
{
|
||||
const char *event, *group;
|
||||
char buf[64];
|
||||
int ret;
|
||||
|
||||
if (pev->event)
|
||||
event = pev->event;
|
||||
else
|
||||
if (pev->point.function && !strisglob(pev->point.function))
|
||||
event = pev->point.function;
|
||||
else
|
||||
event = tev->point.realname;
|
||||
if (pev->group)
|
||||
group = pev->group;
|
||||
else
|
||||
group = PERFPROBE_GROUP;
|
||||
|
||||
/* Get an unused new event name */
|
||||
ret = get_new_event_name(buf, 64, event,
|
||||
namelist, allow_suffix);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
event = buf;
|
||||
|
||||
tev->event = strdup(event);
|
||||
tev->group = strdup(group);
|
||||
if (tev->event == NULL || tev->group == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Add added event name to namelist */
|
||||
strlist__add(namelist, event);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __add_probe_trace_events(struct perf_probe_event *pev,
|
||||
struct probe_trace_event *tevs,
|
||||
int ntevs, bool allow_suffix)
|
||||
{
|
||||
int i, fd, ret;
|
||||
struct probe_trace_event *tev = NULL;
|
||||
char buf[64];
|
||||
const char *event = NULL, *group = NULL;
|
||||
struct strlist *namelist;
|
||||
bool safename;
|
||||
|
||||
if (pev->uprobes)
|
||||
fd = open_uprobe_events(true);
|
||||
else
|
||||
fd = open_kprobe_events(true);
|
||||
|
||||
if (fd < 0) {
|
||||
print_open_warning(fd, !pev->uprobes);
|
||||
fd = probe_file__open(PF_FL_RW | (pev->uprobes ? PF_FL_UPROBE : 0));
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
}
|
||||
|
||||
/* Get current event names */
|
||||
namelist = get_probe_trace_event_names(fd, false);
|
||||
namelist = probe_file__get_namelist(fd);
|
||||
if (!namelist) {
|
||||
pr_debug("Failed to get current event list.\n");
|
||||
ret = -ENOMEM;
|
||||
goto close_out;
|
||||
}
|
||||
|
||||
safename = (pev->point.function && !strisglob(pev->point.function));
|
||||
ret = 0;
|
||||
pr_info("Added new event%s\n", (ntevs > 1) ? "s:" : ":");
|
||||
for (i = 0; i < ntevs; i++) {
|
||||
@ -2516,36 +2360,15 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
|
||||
if (!tev->point.symbol)
|
||||
continue;
|
||||
|
||||
if (pev->event)
|
||||
event = pev->event;
|
||||
else
|
||||
if (safename)
|
||||
event = pev->point.function;
|
||||
else
|
||||
event = tev->point.realname;
|
||||
if (pev->group)
|
||||
group = pev->group;
|
||||
else
|
||||
group = PERFPROBE_GROUP;
|
||||
|
||||
/* Get an unused new event name */
|
||||
ret = get_new_event_name(buf, 64, event,
|
||||
namelist, allow_suffix);
|
||||
/* Set new name for tev (and update namelist) */
|
||||
ret = probe_trace_event__set_name(tev, pev, namelist,
|
||||
allow_suffix);
|
||||
if (ret < 0)
|
||||
break;
|
||||
event = buf;
|
||||
|
||||
tev->event = strdup(event);
|
||||
tev->group = strdup(group);
|
||||
if (tev->event == NULL || tev->group == NULL) {
|
||||
ret = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
ret = write_probe_trace_event(fd, tev);
|
||||
ret = probe_file__add_event(fd, tev);
|
||||
if (ret < 0)
|
||||
break;
|
||||
/* Add added event name to namelist */
|
||||
strlist__add(namelist, event);
|
||||
|
||||
/* We use tev's name for showing new events */
|
||||
show_perf_probe_event(tev->group, tev->event, pev,
|
||||
@ -2838,68 +2661,9 @@ end:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __del_trace_probe_event(int fd, struct str_node *ent)
|
||||
{
|
||||
char *p;
|
||||
char buf[128];
|
||||
int ret;
|
||||
|
||||
/* Convert from perf-probe event to trace-probe event */
|
||||
ret = e_snprintf(buf, 128, "-:%s", ent->s);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
p = strchr(buf + 2, ':');
|
||||
if (!p) {
|
||||
pr_debug("Internal error: %s should have ':' but not.\n",
|
||||
ent->s);
|
||||
ret = -ENOTSUP;
|
||||
goto error;
|
||||
}
|
||||
*p = '/';
|
||||
|
||||
pr_debug("Writing event: %s\n", buf);
|
||||
ret = write(fd, buf, strlen(buf));
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
goto error;
|
||||
}
|
||||
|
||||
pr_info("Removed event: %s\n", ent->s);
|
||||
return 0;
|
||||
error:
|
||||
pr_warning("Failed to delete event: %s\n",
|
||||
strerror_r(-ret, buf, sizeof(buf)));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int del_trace_probe_events(int fd, struct strfilter *filter,
|
||||
struct strlist *namelist)
|
||||
{
|
||||
struct str_node *ent;
|
||||
const char *p;
|
||||
int ret = -ENOENT;
|
||||
|
||||
if (!namelist)
|
||||
return -ENOENT;
|
||||
|
||||
strlist__for_each(ent, namelist) {
|
||||
p = strchr(ent->s, ':');
|
||||
if ((p && strfilter__compare(filter, p + 1)) ||
|
||||
strfilter__compare(filter, ent->s)) {
|
||||
ret = __del_trace_probe_event(fd, ent);
|
||||
if (ret < 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int del_perf_probe_events(struct strfilter *filter)
|
||||
{
|
||||
int ret, ret2, ufd = -1, kfd = -1;
|
||||
struct strlist *namelist = NULL, *unamelist = NULL;
|
||||
char *str = strfilter__string(filter);
|
||||
|
||||
if (!str)
|
||||
@ -2908,25 +2672,15 @@ int del_perf_probe_events(struct strfilter *filter)
|
||||
pr_debug("Delete filter: \'%s\'\n", str);
|
||||
|
||||
/* Get current event names */
|
||||
kfd = open_kprobe_events(true);
|
||||
if (kfd >= 0)
|
||||
namelist = get_probe_trace_event_names(kfd, true);
|
||||
ret = probe_file__open_both(&kfd, &ufd, PF_FL_RW);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ufd = open_uprobe_events(true);
|
||||
if (ufd >= 0)
|
||||
unamelist = get_probe_trace_event_names(ufd, true);
|
||||
|
||||
if (kfd < 0 && ufd < 0) {
|
||||
print_both_open_warning(kfd, ufd);
|
||||
ret = kfd;
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = del_trace_probe_events(kfd, filter, namelist);
|
||||
ret = probe_file__del_events(kfd, filter);
|
||||
if (ret < 0 && ret != -ENOENT)
|
||||
goto error;
|
||||
|
||||
ret2 = del_trace_probe_events(ufd, filter, unamelist);
|
||||
ret2 = probe_file__del_events(ufd, filter);
|
||||
if (ret2 < 0 && ret2 != -ENOENT) {
|
||||
ret = ret2;
|
||||
goto error;
|
||||
@ -2937,15 +2691,11 @@ int del_perf_probe_events(struct strfilter *filter)
|
||||
ret = 0;
|
||||
|
||||
error:
|
||||
if (kfd >= 0) {
|
||||
strlist__delete(namelist);
|
||||
if (kfd >= 0)
|
||||
close(kfd);
|
||||
}
|
||||
|
||||
if (ufd >= 0) {
|
||||
strlist__delete(unamelist);
|
||||
if (ufd >= 0)
|
||||
close(ufd);
|
||||
}
|
||||
out:
|
||||
free(str);
|
||||
|
||||
return ret;
|
||||
|
@ -109,6 +109,8 @@ struct variable_list {
|
||||
/* Command string to events */
|
||||
extern int parse_perf_probe_command(const char *cmd,
|
||||
struct perf_probe_event *pev);
|
||||
extern int parse_probe_trace_command(const char *cmd,
|
||||
struct probe_trace_event *tev);
|
||||
|
||||
/* Events to command string */
|
||||
extern char *synthesize_perf_probe_command(struct perf_probe_event *pev);
|
||||
@ -121,6 +123,7 @@ extern bool perf_probe_event_need_dwarf(struct perf_probe_event *pev);
|
||||
|
||||
/* Release event contents */
|
||||
extern void clear_perf_probe_event(struct perf_probe_event *pev);
|
||||
extern void clear_probe_trace_event(struct probe_trace_event *tev);
|
||||
|
||||
/* Command string to line-range */
|
||||
extern int parse_line_range_desc(const char *cmd, struct line_range *lr);
|
||||
@ -144,6 +147,10 @@ bool arch__prefers_symtab(void);
|
||||
void arch__fix_tev_from_maps(struct perf_probe_event *pev,
|
||||
struct probe_trace_event *tev, struct map *map);
|
||||
|
||||
/* If there is no space to write, returns -E2BIG. */
|
||||
int e_snprintf(char *str, size_t size, const char *format, ...)
|
||||
__attribute__((format(printf, 3, 4)));
|
||||
|
||||
/* Maximum index number of event-name postfix */
|
||||
#define MAX_EVENT_INDEX 1024
|
||||
|
||||
|
301
tools/perf/util/probe-file.c
Normal file
301
tools/perf/util/probe-file.c
Normal file
@ -0,0 +1,301 @@
|
||||
/*
|
||||
* probe-file.c : operate ftrace k/uprobe events files
|
||||
*
|
||||
* Written by Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
#include "util.h"
|
||||
#include "event.h"
|
||||
#include "strlist.h"
|
||||
#include "debug.h"
|
||||
#include "cache.h"
|
||||
#include "color.h"
|
||||
#include "symbol.h"
|
||||
#include "thread.h"
|
||||
#include <api/fs/debugfs.h>
|
||||
#include <api/fs/tracefs.h>
|
||||
#include "probe-event.h"
|
||||
#include "probe-file.h"
|
||||
#include "session.h"
|
||||
|
||||
#define MAX_CMDLEN 256
|
||||
|
||||
static void print_open_warning(int err, bool uprobe)
|
||||
{
|
||||
char sbuf[STRERR_BUFSIZE];
|
||||
|
||||
if (err == -ENOENT) {
|
||||
const char *config;
|
||||
|
||||
if (uprobe)
|
||||
config = "CONFIG_UPROBE_EVENTS";
|
||||
else
|
||||
config = "CONFIG_KPROBE_EVENTS";
|
||||
|
||||
pr_warning("%cprobe_events file does not exist"
|
||||
" - please rebuild kernel with %s.\n",
|
||||
uprobe ? 'u' : 'k', config);
|
||||
} else if (err == -ENOTSUP)
|
||||
pr_warning("Tracefs or debugfs is not mounted.\n");
|
||||
else
|
||||
pr_warning("Failed to open %cprobe_events: %s\n",
|
||||
uprobe ? 'u' : 'k',
|
||||
strerror_r(-err, sbuf, sizeof(sbuf)));
|
||||
}
|
||||
|
||||
static void print_both_open_warning(int kerr, int uerr)
|
||||
{
|
||||
/* Both kprobes and uprobes are disabled, warn it. */
|
||||
if (kerr == -ENOTSUP && uerr == -ENOTSUP)
|
||||
pr_warning("Tracefs or debugfs is not mounted.\n");
|
||||
else if (kerr == -ENOENT && uerr == -ENOENT)
|
||||
pr_warning("Please rebuild kernel with CONFIG_KPROBE_EVENTS "
|
||||
"or/and CONFIG_UPROBE_EVENTS.\n");
|
||||
else {
|
||||
char sbuf[STRERR_BUFSIZE];
|
||||
pr_warning("Failed to open kprobe events: %s.\n",
|
||||
strerror_r(-kerr, sbuf, sizeof(sbuf)));
|
||||
pr_warning("Failed to open uprobe events: %s.\n",
|
||||
strerror_r(-uerr, sbuf, sizeof(sbuf)));
|
||||
}
|
||||
}
|
||||
|
||||
static int open_probe_events(const char *trace_file, bool readwrite)
|
||||
{
|
||||
char buf[PATH_MAX];
|
||||
const char *__debugfs;
|
||||
const char *tracing_dir = "";
|
||||
int ret;
|
||||
|
||||
__debugfs = tracefs_find_mountpoint();
|
||||
if (__debugfs == NULL) {
|
||||
tracing_dir = "tracing/";
|
||||
|
||||
__debugfs = debugfs_find_mountpoint();
|
||||
if (__debugfs == NULL)
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
ret = e_snprintf(buf, PATH_MAX, "%s/%s%s",
|
||||
__debugfs, tracing_dir, trace_file);
|
||||
if (ret >= 0) {
|
||||
pr_debug("Opening %s write=%d\n", buf, readwrite);
|
||||
if (readwrite && !probe_event_dry_run)
|
||||
ret = open(buf, O_RDWR | O_APPEND, 0);
|
||||
else
|
||||
ret = open(buf, O_RDONLY, 0);
|
||||
|
||||
if (ret < 0)
|
||||
ret = -errno;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int open_kprobe_events(bool readwrite)
|
||||
{
|
||||
return open_probe_events("kprobe_events", readwrite);
|
||||
}
|
||||
|
||||
static int open_uprobe_events(bool readwrite)
|
||||
{
|
||||
return open_probe_events("uprobe_events", readwrite);
|
||||
}
|
||||
|
||||
int probe_file__open(int flag)
|
||||
{
|
||||
int fd;
|
||||
|
||||
if (flag & PF_FL_UPROBE)
|
||||
fd = open_uprobe_events(flag & PF_FL_RW);
|
||||
else
|
||||
fd = open_kprobe_events(flag & PF_FL_RW);
|
||||
if (fd < 0)
|
||||
print_open_warning(fd, flag & PF_FL_UPROBE);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
int probe_file__open_both(int *kfd, int *ufd, int flag)
|
||||
{
|
||||
if (!kfd || !ufd)
|
||||
return -EINVAL;
|
||||
|
||||
*kfd = open_kprobe_events(flag & PF_FL_RW);
|
||||
*ufd = open_uprobe_events(flag & PF_FL_RW);
|
||||
if (*kfd < 0 && *ufd < 0) {
|
||||
print_both_open_warning(*kfd, *ufd);
|
||||
return *kfd;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get raw string list of current kprobe_events or uprobe_events */
|
||||
struct strlist *probe_file__get_rawlist(int fd)
|
||||
{
|
||||
int ret, idx;
|
||||
FILE *fp;
|
||||
char buf[MAX_CMDLEN];
|
||||
char *p;
|
||||
struct strlist *sl;
|
||||
|
||||
sl = strlist__new(NULL, NULL);
|
||||
|
||||
fp = fdopen(dup(fd), "r");
|
||||
while (!feof(fp)) {
|
||||
p = fgets(buf, MAX_CMDLEN, fp);
|
||||
if (!p)
|
||||
break;
|
||||
|
||||
idx = strlen(p) - 1;
|
||||
if (p[idx] == '\n')
|
||||
p[idx] = '\0';
|
||||
ret = strlist__add(sl, buf);
|
||||
if (ret < 0) {
|
||||
pr_debug("strlist__add failed (%d)\n", ret);
|
||||
strlist__delete(sl);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
return sl;
|
||||
}
|
||||
|
||||
static struct strlist *__probe_file__get_namelist(int fd, bool include_group)
|
||||
{
|
||||
char buf[128];
|
||||
struct strlist *sl, *rawlist;
|
||||
struct str_node *ent;
|
||||
struct probe_trace_event tev;
|
||||
int ret = 0;
|
||||
|
||||
memset(&tev, 0, sizeof(tev));
|
||||
rawlist = probe_file__get_rawlist(fd);
|
||||
if (!rawlist)
|
||||
return NULL;
|
||||
sl = strlist__new(NULL, NULL);
|
||||
strlist__for_each(ent, rawlist) {
|
||||
ret = parse_probe_trace_command(ent->s, &tev);
|
||||
if (ret < 0)
|
||||
break;
|
||||
if (include_group) {
|
||||
ret = e_snprintf(buf, 128, "%s:%s", tev.group,
|
||||
tev.event);
|
||||
if (ret >= 0)
|
||||
ret = strlist__add(sl, buf);
|
||||
} else
|
||||
ret = strlist__add(sl, tev.event);
|
||||
clear_probe_trace_event(&tev);
|
||||
if (ret < 0)
|
||||
break;
|
||||
}
|
||||
strlist__delete(rawlist);
|
||||
|
||||
if (ret < 0) {
|
||||
strlist__delete(sl);
|
||||
return NULL;
|
||||
}
|
||||
return sl;
|
||||
}
|
||||
|
||||
/* Get current perf-probe event names */
|
||||
struct strlist *probe_file__get_namelist(int fd)
|
||||
{
|
||||
return __probe_file__get_namelist(fd, false);
|
||||
}
|
||||
|
||||
int probe_file__add_event(int fd, struct probe_trace_event *tev)
|
||||
{
|
||||
int ret = 0;
|
||||
char *buf = synthesize_probe_trace_command(tev);
|
||||
char sbuf[STRERR_BUFSIZE];
|
||||
|
||||
if (!buf) {
|
||||
pr_debug("Failed to synthesize probe trace event.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pr_debug("Writing event: %s\n", buf);
|
||||
if (!probe_event_dry_run) {
|
||||
ret = write(fd, buf, strlen(buf));
|
||||
if (ret <= 0) {
|
||||
ret = -errno;
|
||||
pr_warning("Failed to write event: %s\n",
|
||||
strerror_r(errno, sbuf, sizeof(sbuf)));
|
||||
}
|
||||
}
|
||||
free(buf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __del_trace_probe_event(int fd, struct str_node *ent)
|
||||
{
|
||||
char *p;
|
||||
char buf[128];
|
||||
int ret;
|
||||
|
||||
/* Convert from perf-probe event to trace-probe event */
|
||||
ret = e_snprintf(buf, 128, "-:%s", ent->s);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
p = strchr(buf + 2, ':');
|
||||
if (!p) {
|
||||
pr_debug("Internal error: %s should have ':' but not.\n",
|
||||
ent->s);
|
||||
ret = -ENOTSUP;
|
||||
goto error;
|
||||
}
|
||||
*p = '/';
|
||||
|
||||
pr_debug("Writing event: %s\n", buf);
|
||||
ret = write(fd, buf, strlen(buf));
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
goto error;
|
||||
}
|
||||
|
||||
pr_info("Removed event: %s\n", ent->s);
|
||||
return 0;
|
||||
error:
|
||||
pr_warning("Failed to delete event: %s\n",
|
||||
strerror_r(-ret, buf, sizeof(buf)));
|
||||
return ret;
|
||||
}
|
||||
|
||||
int probe_file__del_events(int fd, struct strfilter *filter)
|
||||
{
|
||||
struct strlist *namelist;
|
||||
struct str_node *ent;
|
||||
const char *p;
|
||||
int ret = -ENOENT;
|
||||
|
||||
namelist = __probe_file__get_namelist(fd, true);
|
||||
if (!namelist)
|
||||
return -ENOENT;
|
||||
|
||||
strlist__for_each(ent, namelist) {
|
||||
p = strchr(ent->s, ':');
|
||||
if ((p && strfilter__compare(filter, p + 1)) ||
|
||||
strfilter__compare(filter, ent->s)) {
|
||||
ret = __del_trace_probe_event(fd, ent);
|
||||
if (ret < 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
strlist__delete(namelist);
|
||||
|
||||
return ret;
|
||||
}
|
18
tools/perf/util/probe-file.h
Normal file
18
tools/perf/util/probe-file.h
Normal file
@ -0,0 +1,18 @@
|
||||
#ifndef __PROBE_FILE_H
|
||||
#define __PROBE_FILE_H
|
||||
|
||||
#include "strlist.h"
|
||||
#include "strfilter.h"
|
||||
#include "probe-event.h"
|
||||
|
||||
#define PF_FL_UPROBE 1
|
||||
#define PF_FL_RW 2
|
||||
|
||||
int probe_file__open(int flag);
|
||||
int probe_file__open_both(int *kfd, int *ufd, int flag);
|
||||
struct strlist *probe_file__get_namelist(int fd);
|
||||
struct strlist *probe_file__get_rawlist(int fd);
|
||||
int probe_file__add_event(int fd, struct probe_trace_event *tev);
|
||||
int probe_file__del_events(int fd, struct strfilter *filter);
|
||||
|
||||
#endif
|
@ -1355,7 +1355,7 @@ static int add_available_vars(Dwarf_Die *sc_die, struct probe_finder *pf)
|
||||
vl->point.offset);
|
||||
|
||||
/* Find local variables */
|
||||
vl->vars = strlist__new(true, NULL);
|
||||
vl->vars = strlist__new(NULL, NULL);
|
||||
if (vl->vars == NULL)
|
||||
return -ENOMEM;
|
||||
af->child = true;
|
||||
|
@ -72,7 +72,7 @@ int strlist__load(struct strlist *slist, const char *filename)
|
||||
FILE *fp = fopen(filename, "r");
|
||||
|
||||
if (fp == NULL)
|
||||
return errno;
|
||||
return -errno;
|
||||
|
||||
while (fgets(entry, sizeof(entry), fp) != NULL) {
|
||||
const size_t len = strlen(entry);
|
||||
@ -108,43 +108,70 @@ struct str_node *strlist__find(struct strlist *slist, const char *entry)
|
||||
return snode;
|
||||
}
|
||||
|
||||
static int strlist__parse_list_entry(struct strlist *slist, const char *s)
|
||||
static int strlist__parse_list_entry(struct strlist *slist, const char *s,
|
||||
const char *subst_dir)
|
||||
{
|
||||
int err;
|
||||
char *subst = NULL;
|
||||
|
||||
if (strncmp(s, "file://", 7) == 0)
|
||||
return strlist__load(slist, s + 7);
|
||||
|
||||
return strlist__add(slist, s);
|
||||
if (subst_dir) {
|
||||
err = -ENOMEM;
|
||||
if (asprintf(&subst, "%s/%s", subst_dir, s) < 0)
|
||||
goto out;
|
||||
|
||||
if (access(subst, F_OK) == 0) {
|
||||
err = strlist__load(slist, subst);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
err = strlist__add(slist, s);
|
||||
out:
|
||||
free(subst);
|
||||
return err;
|
||||
}
|
||||
|
||||
int strlist__parse_list(struct strlist *slist, const char *s)
|
||||
static int strlist__parse_list(struct strlist *slist, const char *s, const char *subst_dir)
|
||||
{
|
||||
char *sep;
|
||||
int err;
|
||||
|
||||
while ((sep = strchr(s, ',')) != NULL) {
|
||||
*sep = '\0';
|
||||
err = strlist__parse_list_entry(slist, s);
|
||||
err = strlist__parse_list_entry(slist, s, subst_dir);
|
||||
*sep = ',';
|
||||
if (err != 0)
|
||||
return err;
|
||||
s = sep + 1;
|
||||
}
|
||||
|
||||
return *s ? strlist__parse_list_entry(slist, s) : 0;
|
||||
return *s ? strlist__parse_list_entry(slist, s, subst_dir) : 0;
|
||||
}
|
||||
|
||||
struct strlist *strlist__new(bool dupstr, const char *list)
|
||||
struct strlist *strlist__new(const char *list, const struct strlist_config *config)
|
||||
{
|
||||
struct strlist *slist = malloc(sizeof(*slist));
|
||||
|
||||
if (slist != NULL) {
|
||||
bool dupstr = true;
|
||||
const char *dirname = NULL;
|
||||
|
||||
if (config) {
|
||||
dupstr = !config->dont_dupstr;
|
||||
dirname = config->dirname;
|
||||
}
|
||||
|
||||
rblist__init(&slist->rblist);
|
||||
slist->rblist.node_cmp = strlist__node_cmp;
|
||||
slist->rblist.node_new = strlist__node_new;
|
||||
slist->rblist.node_delete = strlist__node_delete;
|
||||
|
||||
slist->dupstr = dupstr;
|
||||
if (list && strlist__parse_list(slist, list) != 0)
|
||||
|
||||
if (list && strlist__parse_list(slist, list, dirname) != 0)
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,12 @@ struct strlist {
|
||||
bool dupstr;
|
||||
};
|
||||
|
||||
struct strlist *strlist__new(bool dupstr, const char *slist);
|
||||
struct strlist_config {
|
||||
bool dont_dupstr;
|
||||
const char *dirname;
|
||||
};
|
||||
|
||||
struct strlist *strlist__new(const char *slist, const struct strlist_config *config);
|
||||
void strlist__delete(struct strlist *slist);
|
||||
|
||||
void strlist__remove(struct strlist *slist, struct str_node *sn);
|
||||
@ -74,6 +79,4 @@ static inline struct str_node *strlist__next(struct str_node *sn)
|
||||
#define strlist__for_each_safe(pos, n, slist) \
|
||||
for (pos = strlist__first(slist), n = strlist__next(pos); pos;\
|
||||
pos = n, n = strlist__next(n))
|
||||
|
||||
int strlist__parse_list(struct strlist *slist, const char *s);
|
||||
#endif /* __PERF_STRLIST_H */
|
||||
|
@ -1906,7 +1906,7 @@ int setup_list(struct strlist **list, const char *list_str,
|
||||
if (list_str == NULL)
|
||||
return 0;
|
||||
|
||||
*list = strlist__new(true, list_str);
|
||||
*list = strlist__new(list_str, NULL);
|
||||
if (!*list) {
|
||||
pr_err("problems parsing %s list\n", list_name);
|
||||
return -1;
|
||||
|
@ -195,7 +195,8 @@ static struct thread_map *thread_map__new_by_pid_str(const char *pid_str)
|
||||
pid_t pid, prev_pid = INT_MAX;
|
||||
char *end_ptr;
|
||||
struct str_node *pos;
|
||||
struct strlist *slist = strlist__new(false, pid_str);
|
||||
struct strlist_config slist_config = { .dont_dupstr = true, };
|
||||
struct strlist *slist = strlist__new(pid_str, &slist_config);
|
||||
|
||||
if (!slist)
|
||||
return NULL;
|
||||
@ -265,13 +266,14 @@ static struct thread_map *thread_map__new_by_tid_str(const char *tid_str)
|
||||
pid_t tid, prev_tid = INT_MAX;
|
||||
char *end_ptr;
|
||||
struct str_node *pos;
|
||||
struct strlist_config slist_config = { .dont_dupstr = true, };
|
||||
struct strlist *slist;
|
||||
|
||||
/* perf-stat expects threads to be generated even if tid not given */
|
||||
if (!tid_str)
|
||||
return thread_map__new_dummy();
|
||||
|
||||
slist = strlist__new(false, tid_str);
|
||||
slist = strlist__new(tid_str, &slist_config);
|
||||
if (!slist)
|
||||
return NULL;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user