Commit 09211e25 authored by Ingo Molnar's avatar Ingo Molnar

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

Merge tag 'perf-core-for-mingo-20160715' 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:

 - Allow reading from a backward ring buffer (one setup via sys_perf_event_open()
   with perf_event_attr.write_backward = 1) (Wang Nan)

Infrastructure changes:

 - Fix the build on Android NDK r12b (initially just for ARM), that is now port
   of my perf-build container collection and will get tested prior to sending
   patches upstream (Arnaldo Carvalho de Melo)

 - Add correct header for IPv6 definitions

 - Fix bitsperlong.h fallout (Arnaldo Carvalho de Melo, Peter Zijlstra)

 - Use base 0 (auto) in filename__read_ull(), so that we can handle hex values too (Jiri Olsa)
Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents b29c6574 b49364f3
...@@ -3,31 +3,12 @@ ...@@ -3,31 +3,12 @@
#include <uapi/asm-generic/bitsperlong.h> #include <uapi/asm-generic/bitsperlong.h>
/*
* In the kernel, where this file comes from, we can rely on CONFIG_64BIT,
* here we have to make amends with what the various compilers provides us
* to figure out if we're on a 64-bit machine...
*/
#ifdef __SIZEOF_LONG__ #ifdef __SIZEOF_LONG__
# if __SIZEOF_LONG__ == 8 #define BITS_PER_LONG (__CHAR_BIT__ * __SIZEOF_LONG__)
# define CONFIG_64BIT
# endif
#else #else
# ifdef __WORDSIZE #define BITS_PER_LONG __WORDSIZE
# if __WORDSIZE == 64
# define CONFIG_64BIT
# endif
# else
# error Failed to determine BITS_PER_LONG value
# endif
#endif #endif
#ifdef CONFIG_64BIT
#define BITS_PER_LONG 64
#else
#define BITS_PER_LONG 32
#endif /* CONFIG_64BIT */
#if BITS_PER_LONG != __BITS_PER_LONG #if BITS_PER_LONG != __BITS_PER_LONG
#error Inconsistent word size. Check asm/bitsperlong.h #error Inconsistent word size. Check asm/bitsperlong.h
#endif #endif
......
...@@ -9,6 +9,17 @@ ...@@ -9,6 +9,17 @@
# define __always_inline inline __attribute__((always_inline)) # define __always_inline inline __attribute__((always_inline))
#endif #endif
#ifdef __ANDROID__
/*
* FIXME: Big hammer to get rid of tons of:
* "warning: always_inline function might not be inlinable"
*
* At least on android-ndk-r12/platforms/android-24/arch-arm
*/
#undef __always_inline
#define __always_inline inline
#endif
#define __user #define __user
#ifndef __attribute_const__ #ifndef __attribute_const__
......
...@@ -22,6 +22,7 @@ struct fdarray { ...@@ -22,6 +22,7 @@ struct fdarray {
struct pollfd *entries; struct pollfd *entries;
union { union {
int idx; int idx;
void *ptr;
} *priv; } *priv;
}; };
......
...@@ -283,6 +283,11 @@ int filename__read_int(const char *filename, int *value) ...@@ -283,6 +283,11 @@ int filename__read_int(const char *filename, int *value)
return err; return err;
} }
/*
* Parses @value out of @filename with strtoull.
* By using 0 for base, the strtoull detects the
* base automatically (see man strtoull).
*/
int filename__read_ull(const char *filename, unsigned long long *value) int filename__read_ull(const char *filename, unsigned long long *value)
{ {
char line[64]; char line[64];
...@@ -292,7 +297,7 @@ int filename__read_ull(const char *filename, unsigned long long *value) ...@@ -292,7 +297,7 @@ int filename__read_ull(const char *filename, unsigned long long *value)
return -1; return -1;
if (read(fd, line, sizeof(line)) > 0) { if (read(fd, line, sizeof(line)) > 0) {
*value = strtoull(line, NULL, 10); *value = strtoull(line, NULL, 0);
if (*value != ULLONG_MAX) if (*value != ULLONG_MAX)
err = 0; err = 0;
} }
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
* Frederic Weisbecker gave his permission to relicense the code to * Frederic Weisbecker gave his permission to relicense the code to
* the Lesser General Public License. * the Lesser General Public License.
*/ */
#include <inttypes.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
...@@ -33,7 +34,7 @@ ...@@ -33,7 +34,7 @@
#include <limits.h> #include <limits.h>
#include <linux/string.h> #include <linux/string.h>
#include <netinet/ip6.h> #include <netinet/in.h>
#include "event-parse.h" #include "event-parse.h"
#include "event-utils.h" #include "event-utils.h"
......
...@@ -26,7 +26,7 @@ OBJTOOL_IN := $(OBJTOOL)-in.o ...@@ -26,7 +26,7 @@ OBJTOOL_IN := $(OBJTOOL)-in.o
all: $(OBJTOOL) all: $(OBJTOOL)
INCLUDES := -I$(srctree)/tools/include INCLUDES := -I$(srctree)/tools/include -I$(srctree)/tools/arch/$(ARCH)/include/uapi
CFLAGS += -Wall -Werror $(EXTRA_WARNINGS) -fomit-frame-pointer -O2 -g $(INCLUDES) CFLAGS += -Wall -Werror $(EXTRA_WARNINGS) -fomit-frame-pointer -O2 -g $(INCLUDES)
LDFLAGS += -lelf $(LIBSUBCMD) LDFLAGS += -lelf $(LIBSUBCMD)
......
...@@ -664,7 +664,7 @@ static int add_func_switch_tables(struct objtool_file *file, ...@@ -664,7 +664,7 @@ static int add_func_switch_tables(struct objtool_file *file,
struct symbol *func) struct symbol *func)
{ {
struct instruction *insn, *prev_jump; struct instruction *insn, *prev_jump;
struct rela *text_rela, *rodata_rela, *prev_rela; struct rela *text_rela, *rodata_rela, *prev_rela = NULL;
int ret; int ret;
prev_jump = NULL; prev_jump = NULL;
......
...@@ -367,6 +367,28 @@ options. ...@@ -367,6 +367,28 @@ options.
'perf record --dry-run -e' can act as a BPF script compiler if llvm.dump-obj 'perf record --dry-run -e' can act as a BPF script compiler if llvm.dump-obj
in config file is set to true. in config file is set to true.
--tail-synthesize::
Instead of collecting non-sample events (for example, fork, comm, mmap) at
the beginning of record, collect them during finalizing an output file.
The collected non-sample events reflects the status of the system when
record is finished.
--overwrite::
Makes all events use an overwritable ring buffer. An overwritable ring
buffer works like a flight recorder: when it gets full, the kernel will
overwrite the oldest records, that thus will never make it to the
perf.data file.
When '--overwrite' and '--switch-output' are used perf records and drops
events until it receives a signal, meaning that something unusual was
detected that warrants taking a snapshot of the most current events,
those fitting in the ring buffer at that moment.
'overwrite' attribute can also be set or canceled for an event using
config terms. For example: 'cycles/overwrite/' and 'instructions/no-overwrite/'.
Implies --tail-synthesize.
SEE ALSO SEE ALSO
-------- --------
linkperf:perf-stat[1], linkperf:perf-list[1] linkperf:perf-stat[1], linkperf:perf-list[1]
...@@ -119,11 +119,10 @@ backward_rb_find_range(void *buf, int mask, u64 head, u64 *start, u64 *end) ...@@ -119,11 +119,10 @@ backward_rb_find_range(void *buf, int mask, u64 head, u64 *start, u64 *end)
} }
static int static int
rb_find_range(struct perf_evlist *evlist, rb_find_range(void *data, int mask, u64 head, u64 old,
void *data, int mask, u64 head, u64 old, u64 *start, u64 *end, bool backward)
u64 *start, u64 *end)
{ {
if (!evlist->backward) { if (!backward) {
*start = old; *start = old;
*end = head; *end = head;
return 0; return 0;
...@@ -132,9 +131,10 @@ rb_find_range(struct perf_evlist *evlist, ...@@ -132,9 +131,10 @@ rb_find_range(struct perf_evlist *evlist,
return backward_rb_find_range(data, mask, head, start, end); return backward_rb_find_range(data, mask, head, start, end);
} }
static int record__mmap_read(struct record *rec, struct perf_evlist *evlist, int idx) static int
record__mmap_read(struct record *rec, struct perf_mmap *md,
bool overwrite, bool backward)
{ {
struct perf_mmap *md = &evlist->mmap[idx];
u64 head = perf_mmap__read_head(md); u64 head = perf_mmap__read_head(md);
u64 old = md->prev; u64 old = md->prev;
u64 end = head, start = old; u64 end = head, start = old;
...@@ -143,8 +143,8 @@ static int record__mmap_read(struct record *rec, struct perf_evlist *evlist, int ...@@ -143,8 +143,8 @@ static int record__mmap_read(struct record *rec, struct perf_evlist *evlist, int
void *buf; void *buf;
int rc = 0; int rc = 0;
if (rb_find_range(evlist, data, md->mask, head, if (rb_find_range(data, md->mask, head,
old, &start, &end)) old, &start, &end, backward))
return -1; return -1;
if (start == end) if (start == end)
...@@ -157,7 +157,7 @@ static int record__mmap_read(struct record *rec, struct perf_evlist *evlist, int ...@@ -157,7 +157,7 @@ static int record__mmap_read(struct record *rec, struct perf_evlist *evlist, int
WARN_ONCE(1, "failed to keep up with mmap data. (warn only once)\n"); WARN_ONCE(1, "failed to keep up with mmap data. (warn only once)\n");
md->prev = head; md->prev = head;
perf_evlist__mmap_consume(evlist, idx); perf_mmap__consume(md, overwrite || backward);
return 0; return 0;
} }
...@@ -182,7 +182,7 @@ static int record__mmap_read(struct record *rec, struct perf_evlist *evlist, int ...@@ -182,7 +182,7 @@ static int record__mmap_read(struct record *rec, struct perf_evlist *evlist, int
} }
md->prev = head; md->prev = head;
perf_evlist__mmap_consume(evlist, idx); perf_mmap__consume(md, overwrite || backward);
out: out:
return rc; return rc;
} }
...@@ -498,20 +498,30 @@ static struct perf_event_header finished_round_event = { ...@@ -498,20 +498,30 @@ static struct perf_event_header finished_round_event = {
.type = PERF_RECORD_FINISHED_ROUND, .type = PERF_RECORD_FINISHED_ROUND,
}; };
static int record__mmap_read_evlist(struct record *rec, struct perf_evlist *evlist) static int record__mmap_read_evlist(struct record *rec, struct perf_evlist *evlist,
bool backward)
{ {
u64 bytes_written = rec->bytes_written; u64 bytes_written = rec->bytes_written;
int i; int i;
int rc = 0; int rc = 0;
struct perf_mmap *maps;
if (!evlist) if (!evlist)
return 0; return 0;
maps = backward ? evlist->backward_mmap : evlist->mmap;
if (!maps)
return 0;
if (backward && evlist->bkw_mmap_state != BKW_MMAP_DATA_PENDING)
return 0;
for (i = 0; i < evlist->nr_mmaps; i++) { for (i = 0; i < evlist->nr_mmaps; i++) {
struct auxtrace_mmap *mm = &evlist->mmap[i].auxtrace_mmap; struct auxtrace_mmap *mm = &maps[i].auxtrace_mmap;
if (evlist->mmap[i].base) { if (maps[i].base) {
if (record__mmap_read(rec, evlist, i) != 0) { if (record__mmap_read(rec, &maps[i],
evlist->overwrite, backward) != 0) {
rc = -1; rc = -1;
goto out; goto out;
} }
...@@ -531,6 +541,8 @@ static int record__mmap_read_evlist(struct record *rec, struct perf_evlist *evli ...@@ -531,6 +541,8 @@ static int record__mmap_read_evlist(struct record *rec, struct perf_evlist *evli
if (bytes_written != rec->bytes_written) if (bytes_written != rec->bytes_written)
rc = record__write(rec, &finished_round_event, sizeof(finished_round_event)); rc = record__write(rec, &finished_round_event, sizeof(finished_round_event));
if (backward)
perf_evlist__toggle_bkw_mmap(evlist, BKW_MMAP_EMPTY);
out: out:
return rc; return rc;
} }
...@@ -539,11 +551,11 @@ static int record__mmap_read_all(struct record *rec) ...@@ -539,11 +551,11 @@ static int record__mmap_read_all(struct record *rec)
{ {
int err; int err;
err = record__mmap_read_evlist(rec, rec->evlist); err = record__mmap_read_evlist(rec, rec->evlist, false);
if (err) if (err)
return err; return err;
return err; return record__mmap_read_evlist(rec, rec->evlist, true);
} }
static void record__init_features(struct record *rec) static void record__init_features(struct record *rec)
...@@ -592,13 +604,16 @@ record__finish_output(struct record *rec) ...@@ -592,13 +604,16 @@ record__finish_output(struct record *rec)
return; return;
} }
static int record__synthesize_workload(struct record *rec) static int record__synthesize_workload(struct record *rec, bool tail)
{ {
struct { struct {
struct thread_map map; struct thread_map map;
struct thread_map_data map_data; struct thread_map_data map_data;
} thread_map; } thread_map;
if (rec->opts.tail_synthesize != tail)
return 0;
thread_map.map.nr = 1; thread_map.map.nr = 1;
thread_map.map.map[0].pid = rec->evlist->workload.pid; thread_map.map.map[0].pid = rec->evlist->workload.pid;
thread_map.map.map[0].comm = NULL; thread_map.map.map[0].comm = NULL;
...@@ -609,7 +624,7 @@ static int record__synthesize_workload(struct record *rec) ...@@ -609,7 +624,7 @@ static int record__synthesize_workload(struct record *rec)
rec->opts.proc_map_timeout); rec->opts.proc_map_timeout);
} }
static int record__synthesize(struct record *rec); static int record__synthesize(struct record *rec, bool tail);
static int static int
record__switch_output(struct record *rec, bool at_exit) record__switch_output(struct record *rec, bool at_exit)
...@@ -620,6 +635,10 @@ record__switch_output(struct record *rec, bool at_exit) ...@@ -620,6 +635,10 @@ record__switch_output(struct record *rec, bool at_exit)
/* Same Size: "2015122520103046"*/ /* Same Size: "2015122520103046"*/
char timestamp[] = "InvalidTimestamp"; char timestamp[] = "InvalidTimestamp";
record__synthesize(rec, true);
if (target__none(&rec->opts.target))
record__synthesize_workload(rec, true);
rec->samples = 0; rec->samples = 0;
record__finish_output(rec); record__finish_output(rec);
err = fetch_current_timestamp(timestamp, sizeof(timestamp)); err = fetch_current_timestamp(timestamp, sizeof(timestamp));
...@@ -642,7 +661,7 @@ record__switch_output(struct record *rec, bool at_exit) ...@@ -642,7 +661,7 @@ record__switch_output(struct record *rec, bool at_exit)
/* Output tracking events */ /* Output tracking events */
if (!at_exit) { if (!at_exit) {
record__synthesize(rec); record__synthesize(rec, false);
/* /*
* In 'perf record --switch-output' without -a, * In 'perf record --switch-output' without -a,
...@@ -654,7 +673,7 @@ record__switch_output(struct record *rec, bool at_exit) ...@@ -654,7 +673,7 @@ record__switch_output(struct record *rec, bool at_exit)
* perf_event__synthesize_thread_map() for those events. * perf_event__synthesize_thread_map() for those events.
*/ */
if (target__none(&rec->opts.target)) if (target__none(&rec->opts.target))
record__synthesize_workload(rec); record__synthesize_workload(rec, false);
} }
return fd; return fd;
} }
...@@ -689,8 +708,12 @@ perf_event__synth_time_conv(const struct perf_event_mmap_page *pc __maybe_unused ...@@ -689,8 +708,12 @@ perf_event__synth_time_conv(const struct perf_event_mmap_page *pc __maybe_unused
static const struct perf_event_mmap_page * static const struct perf_event_mmap_page *
perf_evlist__pick_pc(struct perf_evlist *evlist) perf_evlist__pick_pc(struct perf_evlist *evlist)
{ {
if (evlist && evlist->mmap && evlist->mmap[0].base) if (evlist) {
return evlist->mmap[0].base; if (evlist->mmap && evlist->mmap[0].base)
return evlist->mmap[0].base;
if (evlist->backward_mmap && evlist->backward_mmap[0].base)
return evlist->backward_mmap[0].base;
}
return NULL; return NULL;
} }
...@@ -704,7 +727,7 @@ static const struct perf_event_mmap_page *record__pick_pc(struct record *rec) ...@@ -704,7 +727,7 @@ static const struct perf_event_mmap_page *record__pick_pc(struct record *rec)
return NULL; return NULL;
} }
static int record__synthesize(struct record *rec) static int record__synthesize(struct record *rec, bool tail)
{ {
struct perf_session *session = rec->session; struct perf_session *session = rec->session;
struct machine *machine = &session->machines.host; struct machine *machine = &session->machines.host;
...@@ -714,6 +737,9 @@ static int record__synthesize(struct record *rec) ...@@ -714,6 +737,9 @@ static int record__synthesize(struct record *rec)
int fd = perf_data_file__fd(file); int fd = perf_data_file__fd(file);
int err = 0; int err = 0;
if (rec->opts.tail_synthesize != tail)
return 0;
if (file->is_pipe) { if (file->is_pipe) {
err = perf_event__synthesize_attrs(tool, session, err = perf_event__synthesize_attrs(tool, session,
process_synthesized_event); process_synthesized_event);
...@@ -877,7 +903,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) ...@@ -877,7 +903,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
machine = &session->machines.host; machine = &session->machines.host;
err = record__synthesize(rec); err = record__synthesize(rec, false);
if (err < 0) if (err < 0)
goto out_child; goto out_child;
...@@ -937,6 +963,17 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) ...@@ -937,6 +963,17 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
for (;;) { for (;;) {
unsigned long long hits = rec->samples; unsigned long long hits = rec->samples;
/*
* rec->evlist->bkw_mmap_state is possible to be
* BKW_MMAP_EMPTY here: when done == true and
* hits != rec->samples in previous round.
*
* perf_evlist__toggle_bkw_mmap ensure we never
* convert BKW_MMAP_EMPTY to BKW_MMAP_DATA_PENDING.
*/
if (trigger_is_hit(&switch_output_trigger) || done || draining)
perf_evlist__toggle_bkw_mmap(rec->evlist, BKW_MMAP_DATA_PENDING);
if (record__mmap_read_all(rec) < 0) { if (record__mmap_read_all(rec) < 0) {
trigger_error(&auxtrace_snapshot_trigger); trigger_error(&auxtrace_snapshot_trigger);
trigger_error(&switch_output_trigger); trigger_error(&switch_output_trigger);
...@@ -956,8 +993,26 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) ...@@ -956,8 +993,26 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
} }
if (trigger_is_hit(&switch_output_trigger)) { if (trigger_is_hit(&switch_output_trigger)) {
/*
* If switch_output_trigger is hit, the data in
* overwritable ring buffer should have been collected,
* so bkw_mmap_state should be set to BKW_MMAP_EMPTY.
*
* If SIGUSR2 raise after or during record__mmap_read_all(),
* record__mmap_read_all() didn't collect data from
* overwritable ring buffer. Read again.
*/
if (rec->evlist->bkw_mmap_state == BKW_MMAP_RUNNING)
continue;
trigger_ready(&switch_output_trigger); trigger_ready(&switch_output_trigger);
/*
* Reenable events in overwrite ring buffer after
* record__mmap_read_all(): we should have collected
* data from it.
*/
perf_evlist__toggle_bkw_mmap(rec->evlist, BKW_MMAP_RUNNING);
if (!quiet) if (!quiet)
fprintf(stderr, "[ perf record: dump data: Woken up %ld times ]\n", fprintf(stderr, "[ perf record: dump data: Woken up %ld times ]\n",
waking); waking);
...@@ -1012,6 +1067,9 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) ...@@ -1012,6 +1067,9 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
if (!quiet) if (!quiet)
fprintf(stderr, "[ perf record: Woken up %ld times to write data ]\n", waking); fprintf(stderr, "[ perf record: Woken up %ld times to write data ]\n", waking);
if (target__none(&rec->opts.target))
record__synthesize_workload(rec, true);
out_child: out_child:
if (forks) { if (forks) {
int exit_status; int exit_status;
...@@ -1030,6 +1088,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) ...@@ -1030,6 +1088,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
} else } else
status = err; status = err;
record__synthesize(rec, true);
/* this will be recalculated during process_buildids() */ /* this will be recalculated during process_buildids() */
rec->samples = 0; rec->samples = 0;
...@@ -1354,6 +1413,9 @@ struct option __record_options[] = { ...@@ -1354,6 +1413,9 @@ struct option __record_options[] = {
OPT_BOOLEAN_SET('i', "no-inherit", &record.opts.no_inherit, OPT_BOOLEAN_SET('i', "no-inherit", &record.opts.no_inherit,
&record.opts.no_inherit_set, &record.opts.no_inherit_set,
"child tasks do not inherit counters"), "child tasks do not inherit counters"),
OPT_BOOLEAN(0, "tail-synthesize", &record.opts.tail_synthesize,
"synthesize non-sample events at the end of output"),
OPT_BOOLEAN(0, "overwrite", &record.opts.overwrite, "use overwrite mode"),
OPT_UINTEGER('F', "freq", &record.opts.user_freq, "profile at this frequency"), OPT_UINTEGER('F', "freq", &record.opts.user_freq, "profile at this frequency"),
OPT_CALLBACK('m', "mmap-pages", &record.opts, "pages[,pages]", OPT_CALLBACK('m', "mmap-pages", &record.opts, "pages[,pages]",
"number of mmap data pages and AUX area tracing mmap pages", "number of mmap data pages and AUX area tracing mmap pages",
...@@ -1564,6 +1626,9 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -1564,6 +1626,9 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
} }
} }
if (record.opts.overwrite)
record.opts.tail_synthesize = true;
if (rec->evlist->nr_entries == 0 && if (rec->evlist->nr_entries == 0 &&
perf_evlist__add_default(rec->evlist) < 0) { perf_evlist__add_default(rec->evlist) < 0) {
pr_err("Not enough memory for event selector list\n"); pr_err("Not enough memory for event selector list\n");
......
...@@ -503,7 +503,7 @@ void pthread__unblock_sigwinch(void) ...@@ -503,7 +503,7 @@ void pthread__unblock_sigwinch(void)
static void cache_line_size(int *cacheline_sizep) static void cache_line_size(int *cacheline_sizep)
{ {
if (sysfs__read_int("devices/system/cpu/cpu0/cache/index0/coherency_line_size", cacheline_sizep)) if (sysfs__read_int("devices/system/cpu/cpu0/cache/index0/coherency_line_size", cacheline_sizep))
perror("cannot determine cache line size"); pr_debug("cannot determine cache line size");
} }
#endif #endif
......
...@@ -59,6 +59,8 @@ struct record_opts { ...@@ -59,6 +59,8 @@ struct record_opts {
bool record_switch_events; bool record_switch_events;
bool all_kernel; bool all_kernel;
bool all_user; bool all_user;
bool tail_synthesize;
bool overwrite;
unsigned int freq; unsigned int freq;
unsigned int mmap_pages; unsigned int mmap_pages;
unsigned int auxtrace_mmap_pages; unsigned int auxtrace_mmap_pages;
......
...@@ -31,8 +31,8 @@ static int count_samples(struct perf_evlist *evlist, int *sample_count, ...@@ -31,8 +31,8 @@ static int count_samples(struct perf_evlist *evlist, int *sample_count,
for (i = 0; i < evlist->nr_mmaps; i++) { for (i = 0; i < evlist->nr_mmaps; i++) {
union perf_event *event; union perf_event *event;
perf_evlist__mmap_read_catchup(evlist, i); perf_mmap__read_catchup(&evlist->backward_mmap[i]);
while ((event = perf_evlist__mmap_read_backward(evlist, i)) != NULL) { while ((event = perf_mmap__read_backward(&evlist->backward_mmap[i])) != NULL) {
const u32 type = event->header.type; const u32 type = event->header.type;
switch (type) { switch (type) {
...@@ -108,7 +108,11 @@ int test__backward_ring_buffer(int subtest __maybe_unused) ...@@ -108,7 +108,11 @@ int test__backward_ring_buffer(int subtest __maybe_unused)
} }
bzero(&parse_error, sizeof(parse_error)); bzero(&parse_error, sizeof(parse_error));
err = parse_events(evlist, "syscalls:sys_enter_prctl", &parse_error); /*
* Set backward bit, ring buffer should be writing from end. Record
* it in aux evlist
*/
err = parse_events(evlist, "syscalls:sys_enter_prctl/overwrite/", &parse_error);
if (err) { if (err) {
pr_debug("Failed to parse tracepoint event, try use root\n"); pr_debug("Failed to parse tracepoint event, try use root\n");
ret = TEST_SKIP; ret = TEST_SKIP;
...@@ -117,10 +121,6 @@ int test__backward_ring_buffer(int subtest __maybe_unused) ...@@ -117,10 +121,6 @@ int test__backward_ring_buffer(int subtest __maybe_unused)
perf_evlist__config(evlist, &opts, NULL); perf_evlist__config(evlist, &opts, NULL);
/* Set backward bit, ring buffer should be writing from end */
evlist__for_each_entry(evlist, evsel)
evsel->attr.write_backward = 1;
err = perf_evlist__open(evlist); err = perf_evlist__open(evlist);
if (err < 0) { if (err < 0) {
pr_debug("perf_evlist__open: %s\n", pr_debug("perf_evlist__open: %s\n",
......
This diff is collapsed.
...@@ -35,6 +35,40 @@ struct perf_mmap { ...@@ -35,6 +35,40 @@ struct perf_mmap {
char event_copy[PERF_SAMPLE_MAX_SIZE] __attribute__((aligned(8))); char event_copy[PERF_SAMPLE_MAX_SIZE] __attribute__((aligned(8)));
}; };
static inline size_t
perf_mmap__mmap_len(struct perf_mmap *map)
{
return map->mask + 1 + page_size;
}
/*
* State machine of bkw_mmap_state:
*
* .________________(forbid)_____________.
* | V
* NOTREADY --(0)--> RUNNING --(1)--> DATA_PENDING --(2)--> EMPTY
* ^ ^ | ^ |
* | |__(forbid)____/ |___(forbid)___/|
* | |
* \_________________(3)_______________/
*
* NOTREADY : Backward ring buffers are not ready
* RUNNING : Backward ring buffers are recording
* DATA_PENDING : We are required to collect data from backward ring buffers
* EMPTY : We have collected data from backward ring buffers.
*
* (0): Setup backward ring buffer
* (1): Pause ring buffers for reading
* (2): Read from ring buffers
* (3): Resume ring buffers for recording
*/
enum bkw_mmap_state {
BKW_MMAP_NOTREADY,
BKW_MMAP_RUNNING,
BKW_MMAP_DATA_PENDING,
BKW_MMAP_EMPTY,
};
struct perf_evlist { struct perf_evlist {
struct list_head entries; struct list_head entries;
struct hlist_head heads[PERF_EVLIST__HLIST_SIZE]; struct hlist_head heads[PERF_EVLIST__HLIST_SIZE];
...@@ -44,17 +78,18 @@ struct perf_evlist { ...@@ -44,17 +78,18 @@ struct perf_evlist {
bool overwrite; bool overwrite;
bool enabled; bool enabled;
bool has_user_cpus; bool has_user_cpus;
bool backward;
size_t mmap_len; size_t mmap_len;
int id_pos; int id_pos;
int is_pos; int is_pos;
u64 combined_sample_type; u64 combined_sample_type;
enum bkw_mmap_state bkw_mmap_state;
struct { struct {
int cork_fd; int cork_fd;
pid_t pid; pid_t pid;
} workload; } workload;
struct fdarray pollfd; struct fdarray pollfd;
struct perf_mmap *mmap; struct perf_mmap *mmap;
struct perf_mmap *backward_mmap;
struct thread_map *threads; struct thread_map *threads;
struct cpu_map *cpus; struct cpu_map *cpus;
struct perf_evsel *selected; struct perf_evsel *selected;
...@@ -129,6 +164,14 @@ struct perf_evsel *perf_evlist__id2evsel_strict(struct perf_evlist *evlist, ...@@ -129,6 +164,14 @@ struct perf_evsel *perf_evlist__id2evsel_strict(struct perf_evlist *evlist,
struct perf_sample_id *perf_evlist__id2sid(struct perf_evlist *evlist, u64 id); struct perf_sample_id *perf_evlist__id2sid(struct perf_evlist *evlist, u64 id);
void perf_evlist__toggle_bkw_mmap(struct perf_evlist *evlist, enum bkw_mmap_state state);
union perf_event *perf_mmap__read_forward(struct perf_mmap *map, bool check_messup);
union perf_event *perf_mmap__read_backward(struct perf_mmap *map);
void perf_mmap__read_catchup(struct perf_mmap *md);
void perf_mmap__consume(struct perf_mmap *md, bool overwrite);
union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx); union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx);
union perf_event *perf_evlist__mmap_read_forward(struct perf_evlist *evlist, union perf_event *perf_evlist__mmap_read_forward(struct perf_evlist *evlist,
...@@ -139,8 +182,6 @@ void perf_evlist__mmap_read_catchup(struct perf_evlist *evlist, int idx); ...@@ -139,8 +182,6 @@ void perf_evlist__mmap_read_catchup(struct perf_evlist *evlist, int idx);
void perf_evlist__mmap_consume(struct perf_evlist *evlist, int idx); void perf_evlist__mmap_consume(struct perf_evlist *evlist, int idx);
int perf_evlist__pause(struct perf_evlist *evlist);
int perf_evlist__resume(struct perf_evlist *evlist);
int perf_evlist__open(struct perf_evlist *evlist); int perf_evlist__open(struct perf_evlist *evlist);
void perf_evlist__close(struct perf_evlist *evlist); void perf_evlist__close(struct perf_evlist *evlist);
......
...@@ -695,6 +695,9 @@ static void apply_config_terms(struct perf_evsel *evsel, ...@@ -695,6 +695,9 @@ static void apply_config_terms(struct perf_evsel *evsel,
*/ */
attr->inherit = term->val.inherit ? 1 : 0; attr->inherit = term->val.inherit ? 1 : 0;
break; break;
case PERF_EVSEL__CONFIG_TERM_OVERWRITE:
attr->write_backward = term->val.overwrite ? 1 : 0;
break;
default: default:
break; break;
} }
...@@ -776,6 +779,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts, ...@@ -776,6 +779,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts,
attr->sample_id_all = perf_missing_features.sample_id_all ? 0 : 1; attr->sample_id_all = perf_missing_features.sample_id_all ? 0 : 1;
attr->inherit = !opts->no_inherit; attr->inherit = !opts->no_inherit;
attr->write_backward = opts->overwrite ? 1 : 0;
perf_evsel__set_sample_bit(evsel, IP); perf_evsel__set_sample_bit(evsel, IP);
perf_evsel__set_sample_bit(evsel, TID); perf_evsel__set_sample_bit(evsel, TID);
...@@ -1377,6 +1381,9 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, ...@@ -1377,6 +1381,9 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
int pid = -1, err; int pid = -1, err;
enum { NO_CHANGE, SET_TO_MAX, INCREASED_MAX } set_rlimit = NO_CHANGE; enum { NO_CHANGE, SET_TO_MAX, INCREASED_MAX } set_rlimit = NO_CHANGE;
if (perf_missing_features.write_backward && evsel->attr.write_backward)
return -EINVAL;
if (evsel->system_wide) if (evsel->system_wide)
nthreads = 1; nthreads = 1;
else else
...@@ -1407,11 +1414,6 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, ...@@ -1407,11 +1414,6 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
if (perf_missing_features.lbr_flags) if (perf_missing_features.lbr_flags)
evsel->attr.branch_sample_type &= ~(PERF_SAMPLE_BRANCH_NO_FLAGS | evsel->attr.branch_sample_type &= ~(PERF_SAMPLE_BRANCH_NO_FLAGS |
PERF_SAMPLE_BRANCH_NO_CYCLES); PERF_SAMPLE_BRANCH_NO_CYCLES);
if (perf_missing_features.write_backward) {
if (evsel->overwrite)
return -EINVAL;
evsel->attr.write_backward = false;
}
retry_sample_id: retry_sample_id:
if (perf_missing_features.sample_id_all) if (perf_missing_features.sample_id_all)
evsel->attr.sample_id_all = 0; evsel->attr.sample_id_all = 0;
...@@ -1513,7 +1515,7 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, ...@@ -1513,7 +1515,7 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
*/ */
if (!perf_missing_features.write_backward && evsel->attr.write_backward) { if (!perf_missing_features.write_backward && evsel->attr.write_backward) {
perf_missing_features.write_backward = true; perf_missing_features.write_backward = true;
goto fallback_missing_features; goto out_close;
} else if (!perf_missing_features.clockid_wrong && evsel->attr.use_clockid) { } else if (!perf_missing_features.clockid_wrong && evsel->attr.use_clockid) {
perf_missing_features.clockid_wrong = true; perf_missing_features.clockid_wrong = true;
goto fallback_missing_features; goto fallback_missing_features;
...@@ -2422,7 +2424,7 @@ int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target, ...@@ -2422,7 +2424,7 @@ int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target,
"We found oprofile daemon running, please stop it and try again."); "We found oprofile daemon running, please stop it and try again.");
break; break;
case EINVAL: case EINVAL:
if (evsel->overwrite && perf_missing_features.write_backward) if (evsel->attr.write_backward && perf_missing_features.write_backward)
return scnprintf(msg, size, "Reading from overwrite event is not supported by this kernel."); return scnprintf(msg, size, "Reading from overwrite event is not supported by this kernel.");
if (perf_missing_features.clockid) if (perf_missing_features.clockid)
return scnprintf(msg, size, "clockid feature not supported."); return scnprintf(msg, size, "clockid feature not supported.");
......
...@@ -45,6 +45,7 @@ enum { ...@@ -45,6 +45,7 @@ enum {
PERF_EVSEL__CONFIG_TERM_STACK_USER, PERF_EVSEL__CONFIG_TERM_STACK_USER,
PERF_EVSEL__CONFIG_TERM_INHERIT, PERF_EVSEL__CONFIG_TERM_INHERIT,
PERF_EVSEL__CONFIG_TERM_MAX_STACK, PERF_EVSEL__CONFIG_TERM_MAX_STACK,
PERF_EVSEL__CONFIG_TERM_OVERWRITE,
PERF_EVSEL__CONFIG_TERM_MAX, PERF_EVSEL__CONFIG_TERM_MAX,
}; };
...@@ -59,6 +60,7 @@ struct perf_evsel_config_term { ...@@ -59,6 +60,7 @@ struct perf_evsel_config_term {
u64 stack_user; u64 stack_user;
int max_stack; int max_stack;
bool inherit; bool inherit;
bool overwrite;
} val; } val;
}; };
...@@ -114,7 +116,6 @@ struct perf_evsel { ...@@ -114,7 +116,6 @@ struct perf_evsel {
bool tracking; bool tracking;
bool per_pkg; bool per_pkg;
bool precise_max; bool precise_max;
bool overwrite;
/* parse modifier helper */ /* parse modifier helper */
int exclude_GH; int exclude_GH;
int nr_members; int nr_members;
......
...@@ -902,6 +902,8 @@ static const char *config_term_names[__PARSE_EVENTS__TERM_TYPE_NR] = { ...@@ -902,6 +902,8 @@ static const char *config_term_names[__PARSE_EVENTS__TERM_TYPE_NR] = {
[PARSE_EVENTS__TERM_TYPE_NOINHERIT] = "no-inherit", [PARSE_EVENTS__TERM_TYPE_NOINHERIT] = "no-inherit",
[PARSE_EVENTS__TERM_TYPE_INHERIT] = "inherit", [PARSE_EVENTS__TERM_TYPE_INHERIT] = "inherit",
[PARSE_EVENTS__TERM_TYPE_MAX_STACK] = "max-stack", [PARSE_EVENTS__TERM_TYPE_MAX_STACK] = "max-stack",
[PARSE_EVENTS__TERM_TYPE_OVERWRITE] = "overwrite",
[PARSE_EVENTS__TERM_TYPE_NOOVERWRITE] = "no-overwrite",
}; };
static bool config_term_shrinked; static bool config_term_shrinked;
...@@ -994,6 +996,12 @@ do { \ ...@@ -994,6 +996,12 @@ do { \
case PARSE_EVENTS__TERM_TYPE_NOINHERIT: case PARSE_EVENTS__TERM_TYPE_NOINHERIT:
CHECK_TYPE_VAL(NUM); CHECK_TYPE_VAL(NUM);
break; break;
case PARSE_EVENTS__TERM_TYPE_OVERWRITE:
CHECK_TYPE_VAL(NUM);
break;
case PARSE_EVENTS__TERM_TYPE_NOOVERWRITE:
CHECK_TYPE_VAL(NUM);
break;
case PARSE_EVENTS__TERM_TYPE_NAME: case PARSE_EVENTS__TERM_TYPE_NAME:
CHECK_TYPE_VAL(STR); CHECK_TYPE_VAL(STR);
break; break;
...@@ -1046,6 +1054,8 @@ static int config_term_tracepoint(struct perf_event_attr *attr, ...@@ -1046,6 +1054,8 @@ static int config_term_tracepoint(struct perf_event_attr *attr,
case PARSE_EVENTS__TERM_TYPE_INHERIT: case PARSE_EVENTS__TERM_TYPE_INHERIT:
case PARSE_EVENTS__TERM_TYPE_NOINHERIT: case PARSE_EVENTS__TERM_TYPE_NOINHERIT:
case PARSE_EVENTS__TERM_TYPE_MAX_STACK: case PARSE_EVENTS__TERM_TYPE_MAX_STACK:
case PARSE_EVENTS__TERM_TYPE_OVERWRITE:
case PARSE_EVENTS__TERM_TYPE_NOOVERWRITE:
return config_term_common(attr, term, err); return config_term_common(attr, term, err);
default: default:
if (err) { if (err) {
...@@ -1118,6 +1128,12 @@ do { \ ...@@ -1118,6 +1128,12 @@ do { \
case PARSE_EVENTS__TERM_TYPE_MAX_STACK: case PARSE_EVENTS__TERM_TYPE_MAX_STACK:
ADD_CONFIG_TERM(MAX_STACK, max_stack, term->val.num); ADD_CONFIG_TERM(MAX_STACK, max_stack, term->val.num);
break; break;
case PARSE_EVENTS__TERM_TYPE_OVERWRITE:
ADD_CONFIG_TERM(OVERWRITE, overwrite, term->val.num ? 1 : 0);
break;
case PARSE_EVENTS__TERM_TYPE_NOOVERWRITE:
ADD_CONFIG_TERM(OVERWRITE, overwrite, term->val.num ? 0 : 1);
break;
default: default:
break; break;
} }
...@@ -2412,9 +2428,9 @@ static void config_terms_list(char *buf, size_t buf_sz) ...@@ -2412,9 +2428,9 @@ static void config_terms_list(char *buf, size_t buf_sz)
char *parse_events_formats_error_string(char *additional_terms) char *parse_events_formats_error_string(char *additional_terms)
{ {
char *str; char *str;
/* "branch_type" is the longest name */ /* "no-overwrite" is the longest name */
char static_terms[__PARSE_EVENTS__TERM_TYPE_NR * char static_terms[__PARSE_EVENTS__TERM_TYPE_NR *
(sizeof("branch_type") - 1)]; (sizeof("no-overwrite") - 1)];
config_terms_list(static_terms, sizeof(static_terms)); config_terms_list(static_terms, sizeof(static_terms));
/* valid terms */ /* valid terms */
......
...@@ -69,6 +69,8 @@ enum { ...@@ -69,6 +69,8 @@ enum {
PARSE_EVENTS__TERM_TYPE_NOINHERIT, PARSE_EVENTS__TERM_TYPE_NOINHERIT,
PARSE_EVENTS__TERM_TYPE_INHERIT, PARSE_EVENTS__TERM_TYPE_INHERIT,
PARSE_EVENTS__TERM_TYPE_MAX_STACK, PARSE_EVENTS__TERM_TYPE_MAX_STACK,
PARSE_EVENTS__TERM_TYPE_NOOVERWRITE,
PARSE_EVENTS__TERM_TYPE_OVERWRITE,
__PARSE_EVENTS__TERM_TYPE_NR, __PARSE_EVENTS__TERM_TYPE_NR,
}; };
......
...@@ -202,6 +202,8 @@ stack-size { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_STACKSIZE); } ...@@ -202,6 +202,8 @@ stack-size { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_STACKSIZE); }
max-stack { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_MAX_STACK); } max-stack { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_MAX_STACK); }
inherit { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_INHERIT); } inherit { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_INHERIT); }
no-inherit { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NOINHERIT); } no-inherit { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NOINHERIT); }
overwrite { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_OVERWRITE); }
no-overwrite { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NOOVERWRITE); }
, { return ','; } , { return ','; }
"/" { BEGIN(INITIAL); return '/'; } "/" { BEGIN(INITIAL); return '/'; }
{name_minus} { return str(yyscanner, PE_NAME); } {name_minus} { return str(yyscanner, PE_NAME); }
......
...@@ -1499,10 +1499,27 @@ int perf_session__register_idle_thread(struct perf_session *session) ...@@ -1499,10 +1499,27 @@ int perf_session__register_idle_thread(struct perf_session *session)
return err; return err;
} }
static void
perf_session__warn_order(const struct perf_session *session)
{
const struct ordered_events *oe = &session->ordered_events;
struct perf_evsel *evsel;
bool should_warn = true;
evlist__for_each_entry(session->evlist, evsel) {
if (evsel->attr.write_backward)
should_warn = false;
}
if (!should_warn)
return;
if (oe->nr_unordered_events != 0)
ui__warning("%u out of order events recorded.\n", oe->nr_unordered_events);
}
static void perf_session__warn_about_errors(const struct perf_session *session) static void perf_session__warn_about_errors(const struct perf_session *session)
{ {
const struct events_stats *stats = &session->evlist->stats; const struct events_stats *stats = &session->evlist->stats;
const struct ordered_events *oe = &session->ordered_events;
if (session->tool->lost == perf_event__process_lost && if (session->tool->lost == perf_event__process_lost &&
stats->nr_events[PERF_RECORD_LOST] != 0) { stats->nr_events[PERF_RECORD_LOST] != 0) {
...@@ -1559,8 +1576,7 @@ static void perf_session__warn_about_errors(const struct perf_session *session) ...@@ -1559,8 +1576,7 @@ static void perf_session__warn_about_errors(const struct perf_session *session)
stats->nr_unprocessable_samples); stats->nr_unprocessable_samples);
} }
if (oe->nr_unordered_events != 0) perf_session__warn_order(session);
ui__warning("%u out of order events recorded.\n", oe->nr_unordered_events);
events_stats__auxtrace_error_warn(stats); events_stats__auxtrace_error_warn(stats);
......
...@@ -2381,6 +2381,9 @@ static int sort_dimension__add(struct perf_hpp_list *list, const char *tok, ...@@ -2381,6 +2381,9 @@ static int sort_dimension__add(struct perf_hpp_list *list, const char *tok,
if (sort__mode != SORT_MODE__MEMORY) if (sort__mode != SORT_MODE__MEMORY)
return -EINVAL; return -EINVAL;
if (sd->entry == &sort_mem_dcacheline && cacheline_size == 0)
return -EINVAL;
if (sd->entry == &sort_mem_daddr_sym) if (sd->entry == &sort_mem_daddr_sym)
list->sym = 1; list->sym = 1;
...@@ -2424,7 +2427,10 @@ static int setup_sort_list(struct perf_hpp_list *list, char *str, ...@@ -2424,7 +2427,10 @@ static int setup_sort_list(struct perf_hpp_list *list, char *str,
if (*tok) { if (*tok) {
ret = sort_dimension__add(list, tok, evlist, level); ret = sort_dimension__add(list, tok, evlist, level);
if (ret == -EINVAL) { if (ret == -EINVAL) {
error("Invalid --sort key: `%s'", tok); if (!cacheline_size && !strncasecmp(tok, "dcacheline", strlen(tok)))
error("The \"dcacheline\" --sort key needs to know the cacheline size and it couldn't be determined on this system");
else
error("Invalid --sort key: `%s'", tok);
break; break;
} else if (ret == -ESRCH) { } else if (ret == -ESRCH) {
error("Unknown --sort key: `%s'", tok); error("Unknown --sort key: `%s'", tok);
......
...@@ -360,7 +360,7 @@ void print_binary(unsigned char *data, size_t len, ...@@ -360,7 +360,7 @@ void print_binary(unsigned char *data, size_t len,
size_t bytes_per_line, print_binary_t printer, size_t bytes_per_line, print_binary_t printer,
void *extra); void *extra);
#ifndef __GLIBC__ #if !defined(__GLIBC__) && !defined(__ANDROID__)
extern int sched_getcpu(void); extern int sched_getcpu(void);
#endif #endif
......
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