Commit 1ccb8fed authored by Ingo Molnar's avatar Ingo Molnar

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

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

- The 'perf test bpf' entry hooked a eBPF proggie to the
  SyS_epoll_wait() kernel function and expected it to be hit when calling
  the epoll_wait() libc wrapper, which changed recently, in systems such
  as Fedora 27, with the glibc wrapper calling instead the epoll_pwait()
  syscall, so switch to epoll_pwait() for both the kernel and libc
  function, getting it to work both in old and new systems (Arnaldo Carvalho de Melo)

- Beautify 'gettid' syscall result in 'perf trace', and in doing so
  noticed that we need to handle namespaces in 'perf trace', will be
  dealt with in follow up patches where we'll try to figure out if
  the recent support for namespace in tools/perf/ can be used for this
  purpose as well. (Arnaldo Carvalho de Melo)

- Introduce 'perf report --mmaps' and 'perf report --tasks' to show
  info present in 'perf.data' (Jiri Olsa, Arnaldo Carvalho de Melo)

- Synchronize kernel <-> tooling headers wrt meltdown/spectre changes
  (Arnaldo Carvalho de Melo)

- Fix a wrong offset issue when using /proc/kcore (Jin Yao)

- Fix bug that prevented annotating symbols in perf.data files
  generated with 'perf record --branch-any'  (Jin Yao)

- Add infrastructure to record first and last sample time to the
  perf.data file header, so that when processing all samples in
  a 'perf record' session, such as when doing build-id processing,
  or when specifically requesting that that info be recorded, use
  that in 'perf report --time', that also got support for percent
  slices in addition to absolute ones.

  I.e. now it is possible to ask for the samples in the 10%-20%
  time slice of a perf.data file (Jin Yao)

- Enable building with libbabeltrace by default (Jiri Olsa)

- Display perf_event_attr::namespaces when duping the attributes
  in verbose mode (Jiri Olsa)

- Allocate context task_ctx_data for child event (Jiri Olsa)

- Update comments for PERF_RECORD_ITRACE_START and PERF_RECORD_MISC_* (Jiri Olsa)

- Add support for showing PERF_RECORD_LOST events in 'perf script' (Jiri Olsa)

- Add 'perf report --stats' option to display quick statistics about
  metadata events (PERF_RECORD_*) i.e. what we get at the end of 'perf
  report -D' (Jiri Olsa)

- Fix compile error with libunwind x86 (Wang Nan)
Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents 9128d3ed 5d64db29
......@@ -612,9 +612,12 @@ struct perf_event_mmap_page {
*/
#define PERF_RECORD_MISC_PROC_MAP_PARSE_TIMEOUT (1 << 12)
/*
* PERF_RECORD_MISC_MMAP_DATA and PERF_RECORD_MISC_COMM_EXEC are used on
* different events so can reuse the same bit position.
* Ditto PERF_RECORD_MISC_SWITCH_OUT.
* Following PERF_RECORD_MISC_* are used on different
* events, so can reuse the same bit position:
*
* PERF_RECORD_MISC_MMAP_DATA - PERF_RECORD_MMAP* events
* PERF_RECORD_MISC_COMM_EXEC - PERF_RECORD_COMM event
* PERF_RECORD_MISC_SWITCH_OUT - PERF_RECORD_SWITCH* events
*/
#define PERF_RECORD_MISC_MMAP_DATA (1 << 13)
#define PERF_RECORD_MISC_COMM_EXEC (1 << 13)
......@@ -864,6 +867,7 @@ enum perf_event_type {
* struct perf_event_header header;
* u32 pid;
* u32 tid;
* struct sample_id sample_id;
* };
*/
PERF_RECORD_ITRACE_START = 12,
......
......@@ -178,21 +178,6 @@ put_callchain_entry(int rctx)
put_recursion_context(this_cpu_ptr(callchain_recursion), rctx);
}
struct perf_callchain_entry *
perf_callchain(struct perf_event *event, struct pt_regs *regs)
{
bool kernel = !event->attr.exclude_callchain_kernel;
bool user = !event->attr.exclude_callchain_user;
/* Disallow cross-task user callchains. */
bool crosstask = event->ctx->task && event->ctx->task != current;
const u32 max_stack = event->attr.sample_max_stack;
if (!kernel && !user)
return NULL;
return get_perf_callchain(regs, 0, kernel, user, max_stack, crosstask, true);
}
struct perf_callchain_entry *
get_perf_callchain(struct pt_regs *regs, u32 init_nr, bool kernel, bool user,
u32 max_stack, bool crosstask, bool add_mark)
......
......@@ -5815,19 +5815,11 @@ void perf_output_sample(struct perf_output_handle *handle,
perf_output_read(handle, event);
if (sample_type & PERF_SAMPLE_CALLCHAIN) {
if (data->callchain) {
int size = 1;
if (data->callchain)
size += data->callchain->nr;
size *= sizeof(u64);
int size = 1;
__output_copy(handle, data->callchain, size);
} else {
u64 nr = 0;
perf_output_put(handle, nr);
}
size += data->callchain->nr;
size *= sizeof(u64);
__output_copy(handle, data->callchain, size);
}
if (sample_type & PERF_SAMPLE_RAW) {
......@@ -5980,6 +5972,26 @@ static u64 perf_virt_to_phys(u64 virt)
return phys_addr;
}
static struct perf_callchain_entry __empty_callchain = { .nr = 0, };
static struct perf_callchain_entry *
perf_callchain(struct perf_event *event, struct pt_regs *regs)
{
bool kernel = !event->attr.exclude_callchain_kernel;
bool user = !event->attr.exclude_callchain_user;
/* Disallow cross-task user callchains. */
bool crosstask = event->ctx->task && event->ctx->task != current;
const u32 max_stack = event->attr.sample_max_stack;
struct perf_callchain_entry *callchain;
if (!kernel && !user)
return &__empty_callchain;
callchain = get_perf_callchain(regs, 0, kernel, user,
max_stack, crosstask, true);
return callchain ?: &__empty_callchain;
}
void perf_prepare_sample(struct perf_event_header *header,
struct perf_sample_data *data,
struct perf_event *event,
......@@ -6002,9 +6014,7 @@ void perf_prepare_sample(struct perf_event_header *header,
int size = 1;
data->callchain = perf_callchain(event, regs);
if (data->callchain)
size += data->callchain->nr;
size += data->callchain->nr;
header->size += size * sizeof(u64);
}
......@@ -10703,6 +10713,19 @@ inherit_event(struct perf_event *parent_event,
if (IS_ERR(child_event))
return child_event;
if ((child_event->attach_state & PERF_ATTACH_TASK_DATA) &&
!child_ctx->task_ctx_data) {
struct pmu *pmu = child_event->pmu;
child_ctx->task_ctx_data = kzalloc(pmu->task_ctx_size,
GFP_KERNEL);
if (!child_ctx->task_ctx_data) {
free_event(child_event);
return NULL;
}
}
/*
* is_orphaned_event() and list_add_tail(&parent_event->child_list)
* must be under the same lock in order to serialize against
......@@ -10713,6 +10736,7 @@ inherit_event(struct perf_event *parent_event,
if (is_orphaned_event(parent_event) ||
!atomic_long_inc_not_zero(&parent_event->refcount)) {
mutex_unlock(&parent_event->child_mutex);
/* task_ctx_data is freed with child_ctx */
free_event(child_event);
return NULL;
}
......
......@@ -201,10 +201,6 @@ arch_perf_out_copy_user(void *dst, const void *src, unsigned long n)
DEFINE_OUTPUT_COPY(__output_copy_user, arch_perf_out_copy_user)
/* Callchain handling */
extern struct perf_callchain_entry *
perf_callchain(struct perf_event *event, struct pt_regs *regs);
static inline int get_recursion_context(int *recursion)
{
int rctx;
......
......@@ -197,11 +197,12 @@
#define X86_FEATURE_CAT_L3 ( 7*32+ 4) /* Cache Allocation Technology L3 */
#define X86_FEATURE_CAT_L2 ( 7*32+ 5) /* Cache Allocation Technology L2 */
#define X86_FEATURE_CDP_L3 ( 7*32+ 6) /* Code and Data Prioritization L3 */
#define X86_FEATURE_INVPCID_SINGLE ( 7*32+ 7) /* Effectively INVPCID && CR4.PCIDE=1 */
#define X86_FEATURE_HW_PSTATE ( 7*32+ 8) /* AMD HW-PState */
#define X86_FEATURE_PROC_FEEDBACK ( 7*32+ 9) /* AMD ProcFeedbackInterface */
#define X86_FEATURE_SME ( 7*32+10) /* AMD Secure Memory Encryption */
#define X86_FEATURE_PTI ( 7*32+11) /* Kernel Page Table Isolation enabled */
#define X86_FEATURE_INTEL_PPIN ( 7*32+14) /* Intel Processor Inventory Number */
#define X86_FEATURE_INTEL_PT ( 7*32+15) /* Intel Processor Trace */
#define X86_FEATURE_AVX512_4VNNIW ( 7*32+16) /* AVX-512 Neural Network Instructions */
......@@ -340,5 +341,6 @@
#define X86_BUG_SWAPGS_FENCE X86_BUG(11) /* SWAPGS without input dep on GS */
#define X86_BUG_MONITOR X86_BUG(12) /* IPI required to wake up remote CPU */
#define X86_BUG_AMD_E400 X86_BUG(13) /* CPU is among the affected by Erratum 400 */
#define X86_BUG_CPU_MELTDOWN X86_BUG(14) /* CPU is affected by meltdown attack and needs kernel page table isolation */
#endif /* _ASM_X86_CPUFEATURES_H */
......@@ -50,6 +50,12 @@
# define DISABLE_LA57 (1<<(X86_FEATURE_LA57 & 31))
#endif
#ifdef CONFIG_PAGE_TABLE_ISOLATION
# define DISABLE_PTI 0
#else
# define DISABLE_PTI (1 << (X86_FEATURE_PTI & 31))
#endif
/*
* Make sure to add features to the correct mask
*/
......@@ -60,7 +66,7 @@
#define DISABLED_MASK4 (DISABLE_PCID)
#define DISABLED_MASK5 0
#define DISABLED_MASK6 0
#define DISABLED_MASK7 0
#define DISABLED_MASK7 (DISABLE_PTI)
#define DISABLED_MASK8 0
#define DISABLED_MASK9 (DISABLE_MPX)
#define DISABLED_MASK10 0
......
......@@ -612,9 +612,12 @@ struct perf_event_mmap_page {
*/
#define PERF_RECORD_MISC_PROC_MAP_PARSE_TIMEOUT (1 << 12)
/*
* PERF_RECORD_MISC_MMAP_DATA and PERF_RECORD_MISC_COMM_EXEC are used on
* different events so can reuse the same bit position.
* Ditto PERF_RECORD_MISC_SWITCH_OUT.
* Following PERF_RECORD_MISC_* are used on different
* events, so can reuse the same bit position:
*
* PERF_RECORD_MISC_MMAP_DATA - PERF_RECORD_MMAP* events
* PERF_RECORD_MISC_COMM_EXEC - PERF_RECORD_COMM event
* PERF_RECORD_MISC_SWITCH_OUT - PERF_RECORD_SWITCH* events
*/
#define PERF_RECORD_MISC_MMAP_DATA (1 << 13)
#define PERF_RECORD_MISC_COMM_EXEC (1 << 13)
......@@ -864,6 +867,7 @@ enum perf_event_type {
* struct perf_event_header header;
* u32 pid;
* u32 tid;
* struct sample_id sample_id;
* };
*/
PERF_RECORD_ITRACE_START = 12,
......
......@@ -430,6 +430,9 @@ Configure all used events to run in user space.
--timestamp-filename
Append timestamp to output file name.
--timestamp-boundary::
Record timestamp boundary (time of first/last samples).
--switch-output[=mode]::
Generate multiple perf.data files, timestamp prefixed, switching to a new one
based on 'mode' value:
......
......@@ -402,6 +402,26 @@ OPTIONS
stop time is not given (i.e, time string is 'x.y,') then analysis goes
to end of file.
Also support time percent with multiple time range. Time string is
'a%/n,b%/m,...' or 'a%-b%,c%-%d,...'. The maximum number of slices is 10.
For example:
Select the second 10% time slice:
perf report --time 10%/2
Select from 0% to 10% time slice:
perf report --time 0%-10%
Select the first and second 10% time slices:
perf report --time 10%/1,10%/2
Select from 0% to 10% and 30% to 40% slices:
perf report --time 0%-10%,30%-40%
--itrace::
Options for decoding instruction tracing data. The options are:
......@@ -437,8 +457,23 @@ include::itrace.txt[]
will be printed. Each entry is function name or file/line. Enabled by
default, disable with --no-inline.
--mmaps::
Show --tasks output plus mmap information in a format similar to
/proc/<PID>/maps.
Please note that not all mmaps are stored, options affecting which ones
are include 'perf record --data', for instance.
--stats::
Display overall events statistics without any further processing.
(like the one at the end of the perf report -D command)
--tasks::
Display monitored tasks stored in perf data. Displaying pid/tid/ppid
plus the command string aligned to distinguish parent and child tasks.
include::callchain-overhead-calculation.txt[]
SEE ALSO
--------
linkperf:perf-stat[1], linkperf:perf-annotate[1]
linkperf:perf-stat[1], linkperf:perf-annotate[1], linkperf:perf-record[1]
......@@ -117,7 +117,7 @@ OPTIONS
Comma separated list of fields to print. Options are:
comm, tid, pid, time, cpu, event, trace, ip, sym, dso, addr, symoff,
srcline, period, iregs, uregs, brstack, brstacksym, flags, bpf-output, brstackinsn,
brstackoff, callindent, insn, insnlen, synth, phys_addr, metric.
brstackoff, callindent, insn, insnlen, synth, phys_addr, metric, misc.
Field list can be prepended with the type, trace, sw or hw,
to indicate to which event type the field list applies.
e.g., -F sw:comm,tid,time,ip,sym and -F trace:time,cpu,trace
......@@ -225,6 +225,24 @@ OPTIONS
that the metric computed is averaged over the whole sampling
period, not just for the sample point.
For sample events it's possible to display misc field with -F +misc option,
following letters are displayed for each bit:
PERF_RECORD_MISC_KERNEL K
PERF_RECORD_MISC_USER U
PERF_RECORD_MISC_HYPERVISOR H
PERF_RECORD_MISC_GUEST_KERNEL G
PERF_RECORD_MISC_GUEST_USER g
PERF_RECORD_MISC_MMAP_DATA* M
PERF_RECORD_MISC_COMM_EXEC E
PERF_RECORD_MISC_SWITCH_OUT S
$ perf script -F +misc ...
sched-messaging 1414 K 28690.636582: 4590 cycles ...
sched-messaging 1407 U 28690.636600: 325620 cycles ...
sched-messaging 1414 K 28690.636608: 19473 cycles ...
misc field ___________/
-k::
--vmlinux=<file>::
vmlinux pathname
......@@ -282,6 +300,9 @@ OPTIONS
Display context switch events i.e. events of type PERF_RECORD_SWITCH or
PERF_RECORD_SWITCH_CPU_WIDE.
--show-lost-events
Display lost events i.e. events of type PERF_RECORD_LOST.
--demangle::
Demangle symbol names to human readable form. It's enabled by default,
disable with --no-demangle.
......@@ -329,6 +350,22 @@ include::itrace.txt[]
stop time is not given (i.e, time string is 'x.y,') then analysis goes
to end of file.
Also support time percent with multipe time range. Time string is
'a%/n,b%/m,...' or 'a%-b%,c%-%d,...'. The maximum number of slices is 10.
For example:
Select the second 10% time slice
perf script --time 10%/2
Select from 0% to 10% time slice
perf script --time 0%-10%
Select the first and second 10% time slices
perf script --time 10%/1,10%/2
Select from 0% to 10% and 30% to 40% slices
perf script --time 0%-10%,30%-40%
--max-blocks::
Set the maximum number of program blocks to print with brstackasm for
each sample.
......
......@@ -261,6 +261,10 @@ struct {
struct perf_header_string map;
}[number_of_cache_levels];
HEADER_SAMPLE_TIME = 21,
Two uint64_t for the time of first sample and the time of last sample.
other bits are reserved and should ignored for now
HEADER_FEAT_BITS = 256,
......
......@@ -780,7 +780,7 @@ else
NO_PERF_READ_VDSOX32 := 1
endif
ifdef LIBBABELTRACE
ifndef NO_LIBBABELTRACE
$(call feature_check,libbabeltrace)
ifeq ($(feature-libbabeltrace), 1)
CFLAGS += -DHAVE_LIBBABELTRACE_SUPPORT $(LIBBABELTRACE_CFLAGS)
......
......@@ -77,7 +77,7 @@ include ../scripts/utilities.mak
#
# Define NO_ZLIB if you do not want to support compressed kernel modules
#
# Define LIBBABELTRACE if you DO want libbabeltrace support
# Define NO_LIBBABELTRACE if you do not want libbabeltrace support
# for CTF data format.
#
# Define NO_LZMA if you do not want to support compressed (xz) kernel modules
......
// SPDX-License-Identifier: GPL-2.0
#ifndef REMOTE_UNWIND_LIBUNWIND
#include <errno.h>
#ifndef REMOTE_UNWIND_LIBUNWIND
#include <libunwind.h>
#include "perf_regs.h"
#include "../../util/unwind.h"
......
......@@ -78,6 +78,7 @@ struct record {
bool no_buildid_cache_set;
bool buildid_all;
bool timestamp_filename;
bool timestamp_boundary;
struct switch_output switch_output;
unsigned long long samples;
};
......@@ -409,8 +410,15 @@ static int process_sample_event(struct perf_tool *tool,
{
struct record *rec = container_of(tool, struct record, tool);
rec->samples++;
if (rec->evlist->first_sample_time == 0)
rec->evlist->first_sample_time = sample->time;
rec->evlist->last_sample_time = sample->time;
if (rec->buildid_all)
return 0;
rec->samples++;
return build_id__mark_dso_hit(tool, event, sample, evsel, machine);
}
......@@ -435,9 +443,11 @@ static int process_buildids(struct record *rec)
/*
* If --buildid-all is given, it marks all DSO regardless of hits,
* so no need to process samples.
* so no need to process samples. But if timestamp_boundary is enabled,
* it still needs to walk on all samples to get the timestamps of
* first/last samples.
*/
if (rec->buildid_all)
if (rec->buildid_all && !rec->timestamp_boundary)
rec->tool.sample = NULL;
return perf_session__process_events(session);
......@@ -1621,6 +1631,8 @@ static struct option __record_options[] = {
"Record build-id of all DSOs regardless of hits"),
OPT_BOOLEAN(0, "timestamp-filename", &record.timestamp_filename,
"append timestamp to output filename"),
OPT_BOOLEAN(0, "timestamp-boundary", &record.timestamp_boundary,
"Record timestamp boundary (time of first/last samples)"),
OPT_STRING_OPTARG_SET(0, "switch-output", &record.switch_output.str,
&record.switch_output.set, "signal,size,time",
"Switch output when receive SIGUSR2 or cross size,time threshold",
......
......@@ -15,6 +15,7 @@
#include "util/color.h"
#include <linux/list.h>
#include <linux/rbtree.h>
#include <linux/err.h>
#include "util/symbol.h"
#include "util/callchain.h"
#include "util/values.h"
......@@ -51,6 +52,9 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/mman.h>
#define PTIME_RANGE_MAX 10
struct report {
struct perf_tool tool;
......@@ -60,6 +64,9 @@ struct report {
bool show_threads;
bool inverted_callchain;
bool mem_mode;
bool stats_mode;
bool tasks_mode;
bool mmaps_mode;
bool header;
bool header_only;
bool nonany_branch_mode;
......@@ -69,7 +76,8 @@ struct report {
const char *cpu_list;
const char *symbol_filter_str;
const char *time_str;
struct perf_time_interval ptime;
struct perf_time_interval ptime_range[PTIME_RANGE_MAX];
int range_num;
float min_percent;
u64 nr_entries;
u64 queue_size;
......@@ -162,12 +170,28 @@ static int hist_iter__branch_callback(struct hist_entry_iter *iter,
struct hist_entry *he = iter->he;
struct report *rep = arg;
struct branch_info *bi;
struct perf_sample *sample = iter->sample;
struct perf_evsel *evsel = iter->evsel;
int err;
if (!ui__has_annotation())
return 0;
hist__account_cycles(sample->branch_stack, al, sample,
rep->nonany_branch_mode);
bi = he->branch_info;
err = addr_map_symbol__inc_samples(&bi->from, sample, evsel->idx);
if (err)
goto out;
err = addr_map_symbol__inc_samples(&bi->to, sample, evsel->idx);
branch_type_count(&rep->brtype_stat, &bi->flags,
bi->from.addr, bi->to.addr);
return 0;
out:
return err;
}
static int process_sample_event(struct perf_tool *tool,
......@@ -186,8 +210,10 @@ static int process_sample_event(struct perf_tool *tool,
};
int ret = 0;
if (perf_time__skip_sample(&rep->ptime, sample->time))
if (perf_time__ranges_skip_sample(rep->ptime_range, rep->range_num,
sample->time)) {
return 0;
}
if (machine__resolve(machine, &al, sample) < 0) {
pr_debug("problem processing %d event, skipping it.\n",
......@@ -567,6 +593,174 @@ static void report__output_resort(struct report *rep)
ui_progress__finish();
}
static void stats_setup(struct report *rep)
{
memset(&rep->tool, 0, sizeof(rep->tool));
rep->tool.no_warn = true;
}
static int stats_print(struct report *rep)
{
struct perf_session *session = rep->session;
perf_session__fprintf_nr_events(session, stdout);
return 0;
}
static void tasks_setup(struct report *rep)
{
memset(&rep->tool, 0, sizeof(rep->tool));
if (rep->mmaps_mode) {
rep->tool.mmap = perf_event__process_mmap;
rep->tool.mmap2 = perf_event__process_mmap2;
}
rep->tool.comm = perf_event__process_comm;
rep->tool.exit = perf_event__process_exit;
rep->tool.fork = perf_event__process_fork;
rep->tool.no_warn = true;
}
struct task {
struct thread *thread;
struct list_head list;
struct list_head children;
};
static struct task *tasks_list(struct task *task, struct machine *machine)
{
struct thread *parent_thread, *thread = task->thread;
struct task *parent_task;
/* Already listed. */
if (!list_empty(&task->list))
return NULL;
/* Last one in the chain. */
if (thread->ppid == -1)
return task;
parent_thread = machine__find_thread(machine, -1, thread->ppid);
if (!parent_thread)
return ERR_PTR(-ENOENT);
parent_task = thread__priv(parent_thread);
list_add_tail(&task->list, &parent_task->children);
return tasks_list(parent_task, machine);
}
static size_t maps__fprintf_task(struct maps *maps, int indent, FILE *fp)
{
size_t printed = 0;
struct rb_node *nd;
for (nd = rb_first(&maps->entries); nd; nd = rb_next(nd)) {
struct map *map = rb_entry(nd, struct map, rb_node);
printed += fprintf(fp, "%*s %" PRIx64 "-%" PRIx64 " %c%c%c%c %08" PRIx64 " %" PRIu64 " %s\n",
indent, "", map->start, map->end,
map->prot & PROT_READ ? 'r' : '-',
map->prot & PROT_WRITE ? 'w' : '-',
map->prot & PROT_EXEC ? 'x' : '-',
map->flags & MAP_SHARED ? 's' : 'p',
map->pgoff,
map->ino, map->dso->name);
}
return printed;
}
static int map_groups__fprintf_task(struct map_groups *mg, int indent, FILE *fp)
{
int printed = 0, i;
for (i = 0; i < MAP__NR_TYPES; ++i)
printed += maps__fprintf_task(&mg->maps[i], indent, fp);
return printed;
}
static void task__print_level(struct task *task, FILE *fp, int level)
{
struct thread *thread = task->thread;
struct task *child;
int comm_indent = fprintf(fp, " %8d %8d %8d |%*s",
thread->pid_, thread->tid, thread->ppid,
level, "");
fprintf(fp, "%s\n", thread__comm_str(thread));
map_groups__fprintf_task(thread->mg, comm_indent, fp);
if (!list_empty(&task->children)) {
list_for_each_entry(child, &task->children, list)
task__print_level(child, fp, level + 1);
}
}
static int tasks_print(struct report *rep, FILE *fp)
{
struct perf_session *session = rep->session;
struct machine *machine = &session->machines.host;
struct task *tasks, *task;
unsigned int nr = 0, itask = 0, i;
struct rb_node *nd;
LIST_HEAD(list);
/*
* No locking needed while accessing machine->threads,
* because --tasks is single threaded command.
*/
/* Count all the threads. */
for (i = 0; i < THREADS__TABLE_SIZE; i++)
nr += machine->threads[i].nr;
tasks = malloc(sizeof(*tasks) * nr);
if (!tasks)
return -ENOMEM;
for (i = 0; i < THREADS__TABLE_SIZE; i++) {
struct threads *threads = &machine->threads[i];
for (nd = rb_first(&threads->entries); nd; nd = rb_next(nd)) {
task = tasks + itask++;
task->thread = rb_entry(nd, struct thread, rb_node);
INIT_LIST_HEAD(&task->children);
INIT_LIST_HEAD(&task->list);
thread__set_priv(task->thread, task);
}
}
/*
* Iterate every task down to the unprocessed parent
* and link all in task children list. Task with no
* parent is added into 'list'.
*/
for (itask = 0; itask < nr; itask++) {
task = tasks + itask;
if (!list_empty(&task->list))
continue;
task = tasks_list(task, machine);
if (IS_ERR(task)) {
pr_err("Error: failed to process tasks\n");
free(tasks);
return PTR_ERR(task);
}
if (task)
list_add_tail(&task->list, &list);
}
fprintf(fp, "# %8s %8s %8s %s\n", "pid", "tid", "ppid", "comm");
list_for_each_entry(task, &list, list)
task__print_level(task, fp, 0);
free(tasks);
return 0;
}
static int __cmd_report(struct report *rep)
{
int ret;
......@@ -598,12 +792,24 @@ static int __cmd_report(struct report *rep)
return ret;
}
if (rep->stats_mode)
stats_setup(rep);
if (rep->tasks_mode)
tasks_setup(rep);
ret = perf_session__process_events(session);
if (ret) {
ui__error("failed to process sample\n");
return ret;
}
if (rep->stats_mode)
return stats_print(rep);
if (rep->tasks_mode)
return tasks_print(rep, stdout);
report__warn_kptr_restrict(rep);
evlist__for_each_entry(session->evlist, pos)
......@@ -760,6 +966,9 @@ int cmd_report(int argc, const char **argv)
OPT_BOOLEAN('q', "quiet", &quiet, "Do not show any message"),
OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
"dump raw trace in ASCII"),
OPT_BOOLEAN(0, "stats", &report.stats_mode, "Display event stats"),
OPT_BOOLEAN(0, "tasks", &report.tasks_mode, "Display recorded tasks"),
OPT_BOOLEAN(0, "mmaps", &report.mmaps_mode, "Display recorded tasks memory maps"),
OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
"file", "vmlinux pathname"),
OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name,
......@@ -907,6 +1116,9 @@ int cmd_report(int argc, const char **argv)
report.symbol_filter_str = argv[0];
}
if (report.mmaps_mode)
report.tasks_mode = true;
if (quiet)
perf_quiet_option();
......@@ -1021,6 +1233,12 @@ int cmd_report(int argc, const char **argv)
report.tool.show_feat_hdr = SHOW_FEAT_HEADER;
if (report.show_full_info)
report.tool.show_feat_hdr = SHOW_FEAT_HEADER_FULL_INFO;
if (report.stats_mode || report.tasks_mode)
use_browser = 0;
if (report.stats_mode && report.tasks_mode) {
pr_err("Error: --tasks and --mmaps can't be used together with --stats\n");
goto error;
}
if (strcmp(input_name, "-") != 0)
setup_browser(true);
......@@ -1043,7 +1261,8 @@ int cmd_report(int argc, const char **argv)
ret = 0;
goto error;
}
} else if (use_browser == 0 && !quiet) {
} else if (use_browser == 0 && !quiet &&
!report.stats_mode && !report.tasks_mode) {
fputs("# To display the perf.data header info, please use --header/--header-only options.\n#\n",
stdout);
}
......@@ -1077,9 +1296,25 @@ int cmd_report(int argc, const char **argv)
if (symbol__init(&session->header.env) < 0)
goto error;
if (perf_time__parse_str(&report.ptime, report.time_str) != 0) {
pr_err("Invalid time string\n");
return -EINVAL;
if (perf_time__parse_str(report.ptime_range, report.time_str) != 0) {
if (session->evlist->first_sample_time == 0 &&
session->evlist->last_sample_time == 0) {
pr_err("No first/last sample time in perf data\n");
return -EINVAL;
}
report.range_num = perf_time__percent_parse_str(
report.ptime_range, PTIME_RANGE_MAX,
report.time_str,
session->evlist->first_sample_time,
session->evlist->last_sample_time);
if (report.range_num < 0) {
pr_err("Invalid time string\n");
return -EINVAL;
}
} else {
report.range_num = 1;
}
sort__setup_elide(stdout);
......
......@@ -93,6 +93,7 @@ enum perf_output_field {
PERF_OUTPUT_PHYS_ADDR = 1U << 26,
PERF_OUTPUT_UREGS = 1U << 27,
PERF_OUTPUT_METRIC = 1U << 28,
PERF_OUTPUT_MISC = 1U << 29,
};
struct output_option {
......@@ -128,6 +129,7 @@ struct output_option {
{.str = "synth", .field = PERF_OUTPUT_SYNTH},
{.str = "phys_addr", .field = PERF_OUTPUT_PHYS_ADDR},
{.str = "metric", .field = PERF_OUTPUT_METRIC},
{.str = "misc", .field = PERF_OUTPUT_MISC},
};
enum {
......@@ -594,7 +596,8 @@ static int perf_sample__fprintf_uregs(struct perf_sample *sample,
static int perf_sample__fprintf_start(struct perf_sample *sample,
struct thread *thread,
struct perf_evsel *evsel, FILE *fp)
struct perf_evsel *evsel,
u32 type, FILE *fp)
{
struct perf_event_attr *attr = &evsel->attr;
unsigned long secs;
......@@ -624,6 +627,47 @@ static int perf_sample__fprintf_start(struct perf_sample *sample,
printed += fprintf(fp, "[%03d] ", sample->cpu);
}
if (PRINT_FIELD(MISC)) {
int ret = 0;
#define has(m) \
(sample->misc & PERF_RECORD_MISC_##m) == PERF_RECORD_MISC_##m
if (has(KERNEL))
ret += fprintf(fp, "K");
if (has(USER))
ret += fprintf(fp, "U");
if (has(HYPERVISOR))
ret += fprintf(fp, "H");
if (has(GUEST_KERNEL))
ret += fprintf(fp, "G");
if (has(GUEST_USER))
ret += fprintf(fp, "g");
switch (type) {
case PERF_RECORD_MMAP:
case PERF_RECORD_MMAP2:
if (has(MMAP_DATA))
ret += fprintf(fp, "M");
break;
case PERF_RECORD_COMM:
if (has(COMM_EXEC))
ret += fprintf(fp, "E");
break;
case PERF_RECORD_SWITCH:
case PERF_RECORD_SWITCH_CPU_WIDE:
if (has(SWITCH_OUT))
ret += fprintf(fp, "S");
default:
break;
}
#undef has
ret += fprintf(fp, "%*s", 6 - ret, " ");
printed += ret;
}
if (PRINT_FIELD(TIME)) {
nsecs = sample->time;
secs = nsecs / NSEC_PER_SEC;
......@@ -1436,6 +1480,8 @@ static int perf_sample__fprintf_synth(struct perf_sample *sample,
return 0;
}
#define PTIME_RANGE_MAX 10
struct perf_script {
struct perf_tool tool;
struct perf_session *session;
......@@ -1443,13 +1489,15 @@ struct perf_script {
bool show_mmap_events;
bool show_switch_events;
bool show_namespace_events;
bool show_lost_events;
bool allocated;
bool per_event_dump;
struct cpu_map *cpus;
struct thread_map *threads;
int name_width;
const char *time_str;
struct perf_time_interval ptime;
struct perf_time_interval ptime_range[PTIME_RANGE_MAX];
int range_num;
};
static int perf_evlist__max_name_len(struct perf_evlist *evlist)
......@@ -1499,7 +1547,7 @@ static void script_print_metric(void *ctx, const char *color,
if (!fmt)
return;
perf_sample__fprintf_start(mctx->sample, mctx->thread, mctx->evsel,
mctx->fp);
PERF_RECORD_SAMPLE, mctx->fp);
fputs("\tmetric: ", mctx->fp);
if (color)
color_fprintf(mctx->fp, color, fmt, val);
......@@ -1513,7 +1561,7 @@ static void script_new_line(void *ctx)
struct metric_ctx *mctx = ctx;
perf_sample__fprintf_start(mctx->sample, mctx->thread, mctx->evsel,
mctx->fp);
PERF_RECORD_SAMPLE, mctx->fp);
fputs("\tmetric: ", mctx->fp);
}
......@@ -1581,7 +1629,8 @@ static void process_event(struct perf_script *script,
++es->samples;
perf_sample__fprintf_start(sample, thread, evsel, fp);
perf_sample__fprintf_start(sample, thread, evsel,
PERF_RECORD_SAMPLE, fp);
if (PRINT_FIELD(PERIOD))
fprintf(fp, "%10" PRIu64 " ", sample->period);
......@@ -1734,8 +1783,10 @@ static int process_sample_event(struct perf_tool *tool,
struct perf_script *scr = container_of(tool, struct perf_script, tool);
struct addr_location al;
if (perf_time__skip_sample(&scr->ptime, sample->time))
if (perf_time__ranges_skip_sample(scr->ptime_range, scr->range_num,
sample->time)) {
return 0;
}
if (debug_mode) {
if (sample->time < last_timestamp) {
......@@ -1828,7 +1879,8 @@ static int process_comm_event(struct perf_tool *tool,
sample->tid = event->comm.tid;
sample->pid = event->comm.pid;
}
perf_sample__fprintf_start(sample, thread, evsel, stdout);
perf_sample__fprintf_start(sample, thread, evsel,
PERF_RECORD_COMM, stdout);
perf_event__fprintf(event, stdout);
ret = 0;
out:
......@@ -1863,7 +1915,8 @@ static int process_namespaces_event(struct perf_tool *tool,
sample->tid = event->namespaces.tid;
sample->pid = event->namespaces.pid;
}
perf_sample__fprintf_start(sample, thread, evsel, stdout);
perf_sample__fprintf_start(sample, thread, evsel,
PERF_RECORD_NAMESPACES, stdout);
perf_event__fprintf(event, stdout);
ret = 0;
out:
......@@ -1896,7 +1949,8 @@ static int process_fork_event(struct perf_tool *tool,
sample->tid = event->fork.tid;
sample->pid = event->fork.pid;
}
perf_sample__fprintf_start(sample, thread, evsel, stdout);
perf_sample__fprintf_start(sample, thread, evsel,
PERF_RECORD_FORK, stdout);
perf_event__fprintf(event, stdout);
thread__put(thread);
......@@ -1925,7 +1979,8 @@ static int process_exit_event(struct perf_tool *tool,
sample->tid = event->fork.tid;
sample->pid = event->fork.pid;
}
perf_sample__fprintf_start(sample, thread, evsel, stdout);
perf_sample__fprintf_start(sample, thread, evsel,
PERF_RECORD_EXIT, stdout);
perf_event__fprintf(event, stdout);
if (perf_event__process_exit(tool, event, sample, machine) < 0)
......@@ -1960,7 +2015,8 @@ static int process_mmap_event(struct perf_tool *tool,
sample->tid = event->mmap.tid;
sample->pid = event->mmap.pid;
}
perf_sample__fprintf_start(sample, thread, evsel, stdout);
perf_sample__fprintf_start(sample, thread, evsel,
PERF_RECORD_MMAP, stdout);
perf_event__fprintf(event, stdout);
thread__put(thread);
return 0;
......@@ -1991,7 +2047,8 @@ static int process_mmap2_event(struct perf_tool *tool,
sample->tid = event->mmap2.tid;
sample->pid = event->mmap2.pid;
}
perf_sample__fprintf_start(sample, thread, evsel, stdout);
perf_sample__fprintf_start(sample, thread, evsel,
PERF_RECORD_MMAP2, stdout);
perf_event__fprintf(event, stdout);
thread__put(thread);
return 0;
......@@ -2017,7 +2074,31 @@ static int process_switch_event(struct perf_tool *tool,
return -1;
}
perf_sample__fprintf_start(sample, thread, evsel, stdout);
perf_sample__fprintf_start(sample, thread, evsel,
PERF_RECORD_SWITCH, stdout);
perf_event__fprintf(event, stdout);
thread__put(thread);
return 0;
}
static int
process_lost_event(struct perf_tool *tool,
union perf_event *event,
struct perf_sample *sample,
struct machine *machine)
{
struct perf_script *script = container_of(tool, struct perf_script, tool);
struct perf_session *session = script->session;
struct perf_evsel *evsel = perf_evlist__id2evsel(session->evlist, sample->id);
struct thread *thread;
thread = machine__findnew_thread(machine, sample->pid,
sample->tid);
if (thread == NULL)
return -1;
perf_sample__fprintf_start(sample, thread, evsel,
PERF_RECORD_LOST, stdout);
perf_event__fprintf(event, stdout);
thread__put(thread);
return 0;
......@@ -2117,6 +2198,8 @@ static int __cmd_script(struct perf_script *script)
script->tool.context_switch = process_switch_event;
if (script->show_namespace_events)
script->tool.namespaces = process_namespaces_event;
if (script->show_lost_events)
script->tool.lost = process_lost_event;
if (perf_script__setup_per_event_dump(script)) {
pr_err("Couldn't create the per event dump files\n");
......@@ -3053,6 +3136,8 @@ int cmd_script(int argc, const char **argv)
"Show context switch events (if recorded)"),
OPT_BOOLEAN('\0', "show-namespace-events", &script.show_namespace_events,
"Show namespace events (if recorded)"),
OPT_BOOLEAN('\0', "show-lost-events", &script.show_lost_events,
"Show lost events (if recorded)"),
OPT_BOOLEAN('\0', "per-event-dump", &script.per_event_dump,
"Dump trace output to files named by the monitored events"),
OPT_BOOLEAN('f', "force", &symbol_conf.force, "don't complain, do it"),
......@@ -3360,10 +3445,27 @@ int cmd_script(int argc, const char **argv)
goto out_delete;
/* needs to be parsed after looking up reference time */
if (perf_time__parse_str(&script.ptime, script.time_str) != 0) {
pr_err("Invalid time string\n");
err = -EINVAL;
goto out_delete;
if (perf_time__parse_str(script.ptime_range, script.time_str) != 0) {
if (session->evlist->first_sample_time == 0 &&
session->evlist->last_sample_time == 0) {
pr_err("No first/last sample time in perf data\n");
err = -EINVAL;
goto out_delete;
}
script.range_num = perf_time__percent_parse_str(
script.ptime_range, PTIME_RANGE_MAX,
script.time_str,
session->evlist->first_sample_time,
session->evlist->last_sample_time);
if (script.range_num < 0) {
pr_err("Invalid time string\n");
err = -EINVAL;
goto out_delete;
}
} else {
script.range_num = 1;
}
err = __cmd_script(&script);
......
......@@ -622,6 +622,7 @@ static struct syscall_fmt {
.arg = { [2] = { .scnprintf = SCA_GETRANDOM_FLAGS, /* flags */ }, }, },
{ .name = "getrlimit",
.arg = { [0] = STRARRAY(resource, rlimit_resources), }, },
{ .name = "gettid", .errpid = true, },
{ .name = "ioctl",
.arg = {
#if defined(__i386__) || defined(__x86_64__)
......
......@@ -31,8 +31,8 @@ struct bpf_map_def SEC("maps") flip_table = {
.max_entries = 1,
};
SEC("func=SyS_epoll_wait")
int bpf_func__SyS_epoll_wait(void *ctx)
SEC("func=SyS_epoll_pwait")
int bpf_func__SyS_epoll_pwait(void *ctx)
{
int ind =0;
int *flag = bpf_map_lookup_elem(&flip_table, &ind);
......
......@@ -19,13 +19,13 @@
#ifdef HAVE_LIBBPF_SUPPORT
static int epoll_wait_loop(void)
static int epoll_pwait_loop(void)
{
int i;
/* Should fail NR_ITERS times */
for (i = 0; i < NR_ITERS; i++)
epoll_wait(-(i + 1), NULL, 0, 0);
epoll_pwait(-(i + 1), NULL, 0, 0, NULL);
return 0;
}
......@@ -63,46 +63,41 @@ static struct {
bool pin;
} bpf_testcase_table[] = {
{
LLVM_TESTCASE_BASE,
"Basic BPF filtering",
"[basic_bpf_test]",
"fix 'perf test LLVM' first",
"load bpf object failed",
&epoll_wait_loop,
(NR_ITERS + 1) / 2,
false,
.prog_id = LLVM_TESTCASE_BASE,
.desc = "Basic BPF filtering",
.name = "[basic_bpf_test]",
.msg_compile_fail = "fix 'perf test LLVM' first",
.msg_load_fail = "load bpf object failed",
.target_func = &epoll_pwait_loop,
.expect_result = (NR_ITERS + 1) / 2,
},
{
LLVM_TESTCASE_BASE,
"BPF pinning",
"[bpf_pinning]",
"fix kbuild first",
"check your vmlinux setting?",
&epoll_wait_loop,
(NR_ITERS + 1) / 2,
true,
.prog_id = LLVM_TESTCASE_BASE,
.desc = "BPF pinning",
.name = "[bpf_pinning]",
.msg_compile_fail = "fix kbuild first",
.msg_load_fail = "check your vmlinux setting?",
.target_func = &epoll_pwait_loop,
.expect_result = (NR_ITERS + 1) / 2,
.pin = true,
},
#ifdef HAVE_BPF_PROLOGUE
{
LLVM_TESTCASE_BPF_PROLOGUE,
"BPF prologue generation",
"[bpf_prologue_test]",
"fix kbuild first",
"check your vmlinux setting?",
&llseek_loop,
(NR_ITERS + 1) / 4,
false,
.prog_id = LLVM_TESTCASE_BPF_PROLOGUE,
.desc = "BPF prologue generation",
.name = "[bpf_prologue_test]",
.msg_compile_fail = "fix kbuild first",
.msg_load_fail = "check your vmlinux setting?",
.target_func = &llseek_loop,
.expect_result = (NR_ITERS + 1) / 4,
},
#endif
{
LLVM_TESTCASE_BPF_RELOCATION,
"BPF relocation checker",
"[bpf_relocation_test]",
"fix 'perf test LLVM' first",
"libbpf error when dealing with relocation",
NULL,
0,
false,
.prog_id = LLVM_TESTCASE_BPF_RELOCATION,
.desc = "BPF relocation checker",
.name = "[bpf_relocation_test]",
.msg_compile_fail = "fix 'perf test LLVM' first",
.msg_load_fail = "libbpf error when dealing with relocation",
},
};
......@@ -190,7 +185,7 @@ static int do_test(struct bpf_object *obj, int (*func)(void),
}
if (count != expect) {
pr_debug("BPF filter result incorrect\n");
pr_debug("BPF filter result incorrect, expected %d, got %d samples\n", expect, count);
goto out_delete_evlist;
}
......
......@@ -1960,7 +1960,8 @@ static void annotation__calc_lines(struct annotation *notes, struct map *map,
if (percent_max <= 0.5)
continue;
al->path = get_srcline(map->dso, start + al->offset, NULL, false, true);
al->path = get_srcline(map->dso, start + al->offset, NULL,
false, true, start + al->offset);
insert_source_line(&tmp_root, al);
}
......
......@@ -1435,6 +1435,11 @@ size_t perf_event__fprintf_switch(union perf_event *event, FILE *fp)
event->context_switch.next_prev_tid);
}
static size_t perf_event__fprintf_lost(union perf_event *event, FILE *fp)
{
return fprintf(fp, " lost %" PRIu64 "\n", event->lost.lost);
}
size_t perf_event__fprintf(union perf_event *event, FILE *fp)
{
size_t ret = fprintf(fp, "PERF_RECORD_%s",
......@@ -1467,6 +1472,9 @@ size_t perf_event__fprintf(union perf_event *event, FILE *fp)
case PERF_RECORD_SWITCH_CPU_WIDE:
ret += perf_event__fprintf_switch(event, fp);
break;
case PERF_RECORD_LOST:
ret += perf_event__fprintf_lost(event, fp);
break;
default:
ret += fprintf(fp, "\n");
}
......
......@@ -205,6 +205,7 @@ struct perf_sample {
u32 flags;
u16 insn_len;
u8 cpumode;
u16 misc;
char insn[MAX_INSN];
void *raw_data;
struct ip_callchain *callchain;
......
......@@ -50,6 +50,8 @@ struct perf_evlist {
struct perf_evsel *selected;
struct events_stats stats;
struct perf_env *env;
u64 first_sample_time;
u64 last_sample_time;
};
struct perf_evsel_str_handler {
......
......@@ -1577,6 +1577,7 @@ int perf_event_attr__fprintf(FILE *fp, struct perf_event_attr *attr,
PRINT_ATTRf(use_clockid, p_unsigned);
PRINT_ATTRf(context_switch, p_unsigned);
PRINT_ATTRf(write_backward, p_unsigned);
PRINT_ATTRf(namespaces, p_unsigned);
PRINT_ATTRn("{ wakeup_events, wakeup_watermark }", wakeup_events, p_unsigned);
PRINT_ATTRf(bp_type, p_unsigned);
......@@ -2041,6 +2042,7 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,
data->stream_id = data->id = data->time = -1ULL;
data->period = evsel->attr.sample_period;
data->cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
data->misc = event->header.misc;
data->id = -1ULL;
data->data_src = PERF_MEM_DATA_SRC_NONE;
......
......@@ -16,6 +16,7 @@
#include <linux/stringify.h>
#include <sys/stat.h>
#include <sys/utsname.h>
#include <linux/time64.h>
#include "evlist.h"
#include "evsel.h"
......@@ -35,6 +36,7 @@
#include <api/fs/fs.h>
#include "asm/bug.h"
#include "tool.h"
#include "time-utils.h"
#include "sane_ctype.h"
......@@ -1180,6 +1182,20 @@ static int write_stat(struct feat_fd *ff __maybe_unused,
return 0;
}
static int write_sample_time(struct feat_fd *ff,
struct perf_evlist *evlist)
{
int ret;
ret = do_write(ff, &evlist->first_sample_time,
sizeof(evlist->first_sample_time));
if (ret < 0)
return ret;
return do_write(ff, &evlist->last_sample_time,
sizeof(evlist->last_sample_time));
}
static void print_hostname(struct feat_fd *ff, FILE *fp)
{
fprintf(fp, "# hostname : %s\n", ff->ph->env.hostname);
......@@ -1505,6 +1521,28 @@ static void print_group_desc(struct feat_fd *ff, FILE *fp)
}
}
static void print_sample_time(struct feat_fd *ff, FILE *fp)
{
struct perf_session *session;
char time_buf[32];
double d;
session = container_of(ff->ph, struct perf_session, header);
timestamp__scnprintf_usec(session->evlist->first_sample_time,
time_buf, sizeof(time_buf));
fprintf(fp, "# time of first sample : %s\n", time_buf);
timestamp__scnprintf_usec(session->evlist->last_sample_time,
time_buf, sizeof(time_buf));
fprintf(fp, "# time of last sample : %s\n", time_buf);
d = (double)(session->evlist->last_sample_time -
session->evlist->first_sample_time) / NSEC_PER_MSEC;
fprintf(fp, "# sample duration : %10.3f ms\n", d);
}
static int __event_process_build_id(struct build_id_event *bev,
char *filename,
struct perf_session *session)
......@@ -2146,6 +2184,27 @@ static int process_cache(struct feat_fd *ff, void *data __maybe_unused)
return -1;
}
static int process_sample_time(struct feat_fd *ff, void *data __maybe_unused)
{
struct perf_session *session;
u64 first_sample_time, last_sample_time;
int ret;
session = container_of(ff->ph, struct perf_session, header);
ret = do_read_u64(ff, &first_sample_time);
if (ret)
return -1;
ret = do_read_u64(ff, &last_sample_time);
if (ret)
return -1;
session->evlist->first_sample_time = first_sample_time;
session->evlist->last_sample_time = last_sample_time;
return 0;
}
struct feature_ops {
int (*write)(struct feat_fd *ff, struct perf_evlist *evlist);
void (*print)(struct feat_fd *ff, FILE *fp);
......@@ -2203,6 +2262,7 @@ static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = {
FEAT_OPN(AUXTRACE, auxtrace, false),
FEAT_OPN(STAT, stat, false),
FEAT_OPN(CACHE, cache, true),
FEAT_OPR(SAMPLE_TIME, sample_time, false),
};
struct header_print_data {
......
......@@ -35,6 +35,7 @@ enum {
HEADER_AUXTRACE,
HEADER_STAT,
HEADER_CACHE,
HEADER_SAMPLE_TIME,
HEADER_LAST_FEATURE,
HEADER_FEAT_BITS = 256,
};
......
......@@ -1726,7 +1726,7 @@ static char *callchain_srcline(struct map *map, struct symbol *sym, u64 ip)
bool show_addr = callchain_param.key == CCKEY_ADDRESS;
srcline = get_srcline(map->dso, map__rip_2objdump(map, ip),
sym, show_sym, show_addr);
sym, show_sym, show_addr, ip);
srcline__tree_insert(&map->dso->srclines, ip, srcline);
}
......
......@@ -419,7 +419,7 @@ int map__fprintf_srcline(struct map *map, u64 addr, const char *prefix,
if (map && map->dso) {
srcline = get_srcline(map->dso,
map__rip_2objdump(map, addr), NULL,
true, true);
true, true, addr);
if (srcline != SRCLINE_UNKNOWN)
ret = fprintf(fp, "%s%s", prefix, srcline);
free_srcline(srcline);
......
......@@ -1773,7 +1773,8 @@ static int __perf_session__process_pipe_events(struct perf_session *session)
err = perf_session__flush_thread_stacks(session);
out_err:
free(buf);
perf_session__warn_about_errors(session);
if (!tool->no_warn)
perf_session__warn_about_errors(session);
ordered_events__free(&session->ordered_events);
auxtrace__free_events(session);
return err;
......@@ -1929,7 +1930,8 @@ static int __perf_session__process_events(struct perf_session *session,
err = perf_session__flush_thread_stacks(session);
out_err:
ui_progress__finish();
perf_session__warn_about_errors(session);
if (!tool->no_warn)
perf_session__warn_about_errors(session);
/*
* We may switching perf.data output, make ordered_events
* reusable.
......
......@@ -336,7 +336,7 @@ char *hist_entry__get_srcline(struct hist_entry *he)
return SRCLINE_UNKNOWN;
return get_srcline(map->dso, map__rip_2objdump(map, he->ip),
he->ms.sym, true, true);
he->ms.sym, true, true, he->ip);
}
static int64_t
......@@ -380,7 +380,8 @@ sort__srcline_from_cmp(struct hist_entry *left, struct hist_entry *right)
map__rip_2objdump(map,
left->branch_info->from.al_addr),
left->branch_info->from.sym,
true, true);
true, true,
left->branch_info->from.al_addr);
}
if (!right->branch_info->srcline_from) {
struct map *map = right->branch_info->from.map;
......@@ -391,7 +392,8 @@ sort__srcline_from_cmp(struct hist_entry *left, struct hist_entry *right)
map__rip_2objdump(map,
right->branch_info->from.al_addr),
right->branch_info->from.sym,
true, true);
true, true,
right->branch_info->from.al_addr);
}
return strcmp(right->branch_info->srcline_from, left->branch_info->srcline_from);
}
......@@ -423,7 +425,8 @@ sort__srcline_to_cmp(struct hist_entry *left, struct hist_entry *right)
map__rip_2objdump(map,
left->branch_info->to.al_addr),
left->branch_info->from.sym,
true, true);
true, true,
left->branch_info->to.al_addr);
}
if (!right->branch_info->srcline_to) {
struct map *map = right->branch_info->to.map;
......@@ -434,7 +437,8 @@ sort__srcline_to_cmp(struct hist_entry *left, struct hist_entry *right)
map__rip_2objdump(map,
right->branch_info->to.al_addr),
right->branch_info->to.sym,
true, true);
true, true,
right->branch_info->to.al_addr);
}
return strcmp(right->branch_info->srcline_to, left->branch_info->srcline_to);
}
......@@ -465,7 +469,7 @@ static char *hist_entry__get_srcfile(struct hist_entry *e)
return no_srcfile;
sf = __get_srcline(map->dso, map__rip_2objdump(map, e->ip),
e->ms.sym, false, true, true);
e->ms.sym, false, true, true, e->ip);
if (!strcmp(sf, SRCLINE_UNKNOWN))
return no_srcfile;
p = strchr(sf, ':');
......
......@@ -496,7 +496,8 @@ static struct inline_node *addr2inlines(const char *dso_name, u64 addr,
#define A2L_FAIL_LIMIT 123
char *__get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
bool show_sym, bool show_addr, bool unwind_inlines)
bool show_sym, bool show_addr, bool unwind_inlines,
u64 ip)
{
char *file = NULL;
unsigned line = 0;
......@@ -536,7 +537,7 @@ char *__get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
if (sym) {
if (asprintf(&srcline, "%s+%" PRIu64, show_sym ? sym->name : "",
addr - sym->start) < 0)
ip - sym->start) < 0)
return SRCLINE_UNKNOWN;
} else if (asprintf(&srcline, "%s[%" PRIx64 "]", dso->short_name, addr) < 0)
return SRCLINE_UNKNOWN;
......@@ -550,9 +551,9 @@ void free_srcline(char *srcline)
}
char *get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
bool show_sym, bool show_addr)
bool show_sym, bool show_addr, u64 ip)
{
return __get_srcline(dso, addr, sym, show_sym, show_addr, false);
return __get_srcline(dso, addr, sym, show_sym, show_addr, false, ip);
}
struct srcline_node {
......
......@@ -11,9 +11,10 @@ struct symbol;
extern bool srcline_full_filename;
char *get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
bool show_sym, bool show_addr);
bool show_sym, bool show_addr, u64 ip);
char *__get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
bool show_sym, bool show_addr, bool unwind_inlines);
bool show_sym, bool show_addr, bool unwind_inlines,
u64 ip);
void free_srcline(char *srcline);
/* insert the srcline into the DSO, which will take ownership */
......
......@@ -6,6 +6,7 @@
#include <time.h>
#include <errno.h>
#include <inttypes.h>
#include <math.h>
#include "perf.h"
#include "debug.h"
......@@ -60,11 +61,10 @@ static int parse_timestr_sec_nsec(struct perf_time_interval *ptime,
return 0;
}
int perf_time__parse_str(struct perf_time_interval *ptime, const char *ostr)
static int split_start_end(char **start, char **end, const char *ostr, char ch)
{
char *start_str, *end_str;
char *d, *str;
int rc = 0;
if (ostr == NULL || *ostr == '\0')
return 0;
......@@ -74,25 +74,35 @@ int perf_time__parse_str(struct perf_time_interval *ptime, const char *ostr)
if (str == NULL)
return -ENOMEM;
ptime->start = 0;
ptime->end = 0;
/* str has the format: <start>,<stop>
* variations: <start>,
* ,<stop>
* ,
*/
start_str = str;
d = strchr(start_str, ',');
d = strchr(start_str, ch);
if (d) {
*d = '\0';
++d;
}
end_str = d;
*start = start_str;
*end = end_str;
return 0;
}
int perf_time__parse_str(struct perf_time_interval *ptime, const char *ostr)
{
char *start_str = NULL, *end_str;
int rc;
rc = split_start_end(&start_str, &end_str, ostr, ',');
if (rc || !start_str)
return rc;
ptime->start = 0;
ptime->end = 0;
rc = parse_timestr_sec_nsec(ptime, start_str, end_str);
free(str);
free(start_str);
/* make sure end time is after start time if it was given */
if (rc == 0 && ptime->end && ptime->end < ptime->start)
......@@ -104,6 +114,177 @@ int perf_time__parse_str(struct perf_time_interval *ptime, const char *ostr)
return rc;
}
static int parse_percent(double *pcnt, char *str)
{
char *c;
c = strchr(str, '%');
if (c)
*c = '\0';
else
return -1;
*pcnt = atof(str) / 100.0;
return 0;
}
static int percent_slash_split(char *str, struct perf_time_interval *ptime,
u64 start, u64 end)
{
char *p, *end_str;
double pcnt, start_pcnt, end_pcnt;
u64 total = end - start;
int i;
/*
* Example:
* 10%/2: select the second 10% slice and the third 10% slice
*/
/* We can modify this string since the original one is copied */
p = strchr(str, '/');
if (!p)
return -1;
*p = '\0';
if (parse_percent(&pcnt, str) < 0)
return -1;
p++;
i = (int)strtol(p, &end_str, 10);
if (*end_str)
return -1;
if (pcnt <= 0.0)
return -1;
start_pcnt = pcnt * (i - 1);
end_pcnt = pcnt * i;
if (start_pcnt < 0.0 || start_pcnt > 1.0 ||
end_pcnt < 0.0 || end_pcnt > 1.0) {
return -1;
}
ptime->start = start + round(start_pcnt * total);
ptime->end = start + round(end_pcnt * total);
return 0;
}
static int percent_dash_split(char *str, struct perf_time_interval *ptime,
u64 start, u64 end)
{
char *start_str = NULL, *end_str;
double start_pcnt, end_pcnt;
u64 total = end - start;
int ret;
/*
* Example: 0%-10%
*/
ret = split_start_end(&start_str, &end_str, str, '-');
if (ret || !start_str)
return ret;
if ((parse_percent(&start_pcnt, start_str) != 0) ||
(parse_percent(&end_pcnt, end_str) != 0)) {
free(start_str);
return -1;
}
free(start_str);
if (start_pcnt < 0.0 || start_pcnt > 1.0 ||
end_pcnt < 0.0 || end_pcnt > 1.0 ||
start_pcnt > end_pcnt) {
return -1;
}
ptime->start = start + round(start_pcnt * total);
ptime->end = start + round(end_pcnt * total);
return 0;
}
typedef int (*time_pecent_split)(char *, struct perf_time_interval *,
u64 start, u64 end);
static int percent_comma_split(struct perf_time_interval *ptime_buf, int num,
const char *ostr, u64 start, u64 end,
time_pecent_split func)
{
char *str, *p1, *p2;
int len, ret, i = 0;
str = strdup(ostr);
if (str == NULL)
return -ENOMEM;
len = strlen(str);
p1 = str;
while (p1 < str + len) {
if (i >= num) {
free(str);
return -1;
}
p2 = strchr(p1, ',');
if (p2)
*p2 = '\0';
ret = (func)(p1, &ptime_buf[i], start, end);
if (ret < 0) {
free(str);
return -1;
}
pr_debug("start time %d: %" PRIu64 ", ", i, ptime_buf[i].start);
pr_debug("end time %d: %" PRIu64 "\n", i, ptime_buf[i].end);
i++;
if (p2)
p1 = p2 + 1;
else
break;
}
free(str);
return i;
}
int perf_time__percent_parse_str(struct perf_time_interval *ptime_buf, int num,
const char *ostr, u64 start, u64 end)
{
char *c;
/*
* ostr example:
* 10%/2,10%/3: select the second 10% slice and the third 10% slice
* 0%-10%,30%-40%: multiple time range
*/
memset(ptime_buf, 0, sizeof(*ptime_buf) * num);
c = strchr(ostr, '/');
if (c) {
return percent_comma_split(ptime_buf, num, ostr, start,
end, percent_slash_split);
}
c = strchr(ostr, '-');
if (c) {
return percent_comma_split(ptime_buf, num, ostr, start,
end, percent_dash_split);
}
return -1;
}
bool perf_time__skip_sample(struct perf_time_interval *ptime, u64 timestamp)
{
/* if time is not set don't drop sample */
......@@ -119,6 +300,34 @@ bool perf_time__skip_sample(struct perf_time_interval *ptime, u64 timestamp)
return false;
}
bool perf_time__ranges_skip_sample(struct perf_time_interval *ptime_buf,
int num, u64 timestamp)
{
struct perf_time_interval *ptime;
int i;
if ((timestamp == 0) || (num == 0))
return false;
if (num == 1)
return perf_time__skip_sample(&ptime_buf[0], timestamp);
/*
* start/end of multiple time ranges must be valid.
*/
for (i = 0; i < num; i++) {
ptime = &ptime_buf[i];
if (timestamp >= ptime->start &&
((timestamp < ptime->end && i < num - 1) ||
(timestamp <= ptime->end && i == num - 1))) {
break;
}
}
return (i == num) ? true : false;
}
int timestamp__scnprintf_usec(u64 timestamp, char *buf, size_t sz)
{
u64 sec = timestamp / NSEC_PER_SEC;
......
......@@ -13,8 +13,14 @@ int parse_nsec_time(const char *str, u64 *ptime);
int perf_time__parse_str(struct perf_time_interval *ptime, const char *ostr);
int perf_time__percent_parse_str(struct perf_time_interval *ptime_buf, int num,
const char *ostr, u64 start, u64 end);
bool perf_time__skip_sample(struct perf_time_interval *ptime, u64 timestamp);
bool perf_time__ranges_skip_sample(struct perf_time_interval *ptime_buf,
int num, u64 timestamp);
int timestamp__scnprintf_usec(u64 timestamp, char *buf, size_t sz);
int fetch_current_timestamp(char *buf, size_t sz);
......
......@@ -76,6 +76,7 @@ struct perf_tool {
bool ordered_events;
bool ordering_requires_timestamps;
bool namespace_events;
bool no_warn;
enum show_feature_header show_feat_hdr;
};
......
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