perf/core improvements and fixes:
New features: - Support AArch64 in the 'annotate' code, native/local and cross-arch/remote (Kim Phillips) - Allow considering just events in a given time interval, via the '--time start.s.ms,end.s.ms' command line, added to 'perf kmem', 'perf report', 'perf sched timehist' and 'perf script' (David Ahern) - Add option to stop printing a callchain at one of a given group of symbol names (David Ahern) - Handle cpu migration events in 'perf sched timehist' (David Ahern) - Track memory freed in 'perf kmem stat' (David Ahern) Infrastructure: - Initial support (and perf test entry) for tooling hooks, starting with 'record_start' and 'record_end', that will have as its initial user the eBPF infrastructure, where perf_ prefixed functions will be JITed and run when such hooks are called (Wang Nan) - Remove redundant "test" and similar strings from 'perf test' descriptions (Arnaldo Carvalho de Melo) - libbpf assorted improvements (Wang Nan) Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAABCAAGBQJYQF7CAAoJENZQFvNTUqpA89oP/2s0Gtq1xztDTj78eCI9q4og a9GxNOttvPtD2L/sZq2MQlJ3/Mf7ExsD5+wOhQMRZ0dz3445VXOAammdzqb4Qo1V MuxNFfv7T7VhP4LaXP4jq0X0TBHNUKTGVrO0uzvMi1oacBsFYFtkat0pbHfNBmVG 4BAztinp5hpueN5d20TtVe8PCI9/epRWl+xTGCBR+bpSIwzpPAcYZ/T8Cj7g2XyI fwXitzQipvyXVX2ZujdNCHytMjll4loQLpBJ1rIED0fJxYUA4kfwUHeC+SE2Tbka GUh1jzZgnfpFTtyT9s5KFR7H0mNqsBUZs1RunfeHIM8hdgJYiBxyvTU9FdeLKF25 xttmwwrYhP14nb6RHFfy8J7fEkrz3/z308B6ko3L81cZKMK7MIqj15cYNVGWmvjQ 32z047bmeCxTft5xqlV1VEQ5JKy7yyRnvEqMEkSWsfSpL2QKD6Ekyr2qi/kxm0zy SqfPY4ea1BIDdpaqbk5mzXZ/VIEKei4ccej7slesF5+Kd63qJfd/tGNZV8dF0rCB 9feM798XdsmGTGKhq19BuB7msBEHyh9PRnqBcIGP1v7a1JjPR+VZG9zJMw0efAav V7TAXIi4LzTjtIqPhvsTUWmN/b/RkSJltdQHUXsm6d0WuU0swt8paDBQy4SZJNws O7bxsVav2ChKLPjfQ48C =hE+p -----END PGP SIGNATURE----- Merge tag 'perf-core-for-mingo-20161201' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo: New features: - Support AArch64 in the 'annotate' code, native/local and cross-arch/remote (Kim Phillips) - Allow considering just events in a given time interval, via the '--time start.s.ms,end.s.ms' command line, added to 'perf kmem', 'perf report', 'perf sched timehist' and 'perf script' (David Ahern) - Add option to stop printing a callchain at one of a given group of symbol names (David Ahern) - Handle CPU migration events in 'perf sched timehist' (David Ahern) - Track memory freed in 'perf kmem stat' (David Ahern) Infrastructure: - Add initial support (and perf test entry) for tooling hooks, starting with 'record_start' and 'record_end', that will have as its initial user the eBPF infrastructure, where perf_ prefixed functions will be JITed and run when such hooks are called (Wang Nan) - Remove redundant "test" and similar strings from 'perf test' descriptions (Arnaldo Carvalho de Melo) - Implement assorted libbpf improvements (Wang Nan) Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
commit
e7af7b1512
|
@ -110,3 +110,59 @@ int bpf_map_update_elem(int fd, void *key, void *value,
|
|||
|
||||
return sys_bpf(BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
int bpf_map_lookup_elem(int fd, void *key, void *value)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
|
||||
bzero(&attr, sizeof(attr));
|
||||
attr.map_fd = fd;
|
||||
attr.key = ptr_to_u64(key);
|
||||
attr.value = ptr_to_u64(value);
|
||||
|
||||
return sys_bpf(BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
int bpf_map_delete_elem(int fd, void *key)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
|
||||
bzero(&attr, sizeof(attr));
|
||||
attr.map_fd = fd;
|
||||
attr.key = ptr_to_u64(key);
|
||||
|
||||
return sys_bpf(BPF_MAP_DELETE_ELEM, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
int bpf_map_get_next_key(int fd, void *key, void *next_key)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
|
||||
bzero(&attr, sizeof(attr));
|
||||
attr.map_fd = fd;
|
||||
attr.key = ptr_to_u64(key);
|
||||
attr.next_key = ptr_to_u64(next_key);
|
||||
|
||||
return sys_bpf(BPF_MAP_GET_NEXT_KEY, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
int bpf_obj_pin(int fd, const char *pathname)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
|
||||
bzero(&attr, sizeof(attr));
|
||||
attr.pathname = ptr_to_u64((void *)pathname);
|
||||
attr.bpf_fd = fd;
|
||||
|
||||
return sys_bpf(BPF_OBJ_PIN, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
int bpf_obj_get(const char *pathname)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
|
||||
bzero(&attr, sizeof(attr));
|
||||
attr.pathname = ptr_to_u64((void *)pathname);
|
||||
|
||||
return sys_bpf(BPF_OBJ_GET, &attr, sizeof(attr));
|
||||
}
|
||||
|
|
|
@ -35,4 +35,11 @@ int bpf_load_program(enum bpf_prog_type type, struct bpf_insn *insns,
|
|||
|
||||
int bpf_map_update_elem(int fd, void *key, void *value,
|
||||
u64 flags);
|
||||
|
||||
int bpf_map_lookup_elem(int fd, void *key, void *value);
|
||||
int bpf_map_delete_elem(int fd, void *key);
|
||||
int bpf_map_get_next_key(int fd, void *key, void *next_key);
|
||||
int bpf_obj_pin(int fd, const char *pathname);
|
||||
int bpf_obj_get(const char *pathname);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -229,6 +229,10 @@ struct bpf_object {
|
|||
* all objects.
|
||||
*/
|
||||
struct list_head list;
|
||||
|
||||
void *priv;
|
||||
bpf_object_clear_priv_t clear_priv;
|
||||
|
||||
char path[];
|
||||
};
|
||||
#define obj_elf_valid(o) ((o)->efile.elf)
|
||||
|
@ -1229,6 +1233,9 @@ void bpf_object__close(struct bpf_object *obj)
|
|||
if (!obj)
|
||||
return;
|
||||
|
||||
if (obj->clear_priv)
|
||||
obj->clear_priv(obj, obj->priv);
|
||||
|
||||
bpf_object__elf_finish(obj);
|
||||
bpf_object__unload(obj);
|
||||
|
||||
|
@ -1282,6 +1289,22 @@ unsigned int bpf_object__kversion(struct bpf_object *obj)
|
|||
return obj ? obj->kern_version : 0;
|
||||
}
|
||||
|
||||
int bpf_object__set_priv(struct bpf_object *obj, void *priv,
|
||||
bpf_object_clear_priv_t clear_priv)
|
||||
{
|
||||
if (obj->priv && obj->clear_priv)
|
||||
obj->clear_priv(obj, obj->priv);
|
||||
|
||||
obj->priv = priv;
|
||||
obj->clear_priv = clear_priv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *bpf_object__priv(struct bpf_object *obj)
|
||||
{
|
||||
return obj ? obj->priv : ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
struct bpf_program *
|
||||
bpf_program__next(struct bpf_program *prev, struct bpf_object *obj)
|
||||
{
|
||||
|
@ -1501,3 +1524,15 @@ bpf_object__find_map_by_name(struct bpf_object *obj, const char *name)
|
|||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct bpf_map *
|
||||
bpf_object__find_map_by_offset(struct bpf_object *obj, size_t offset)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < obj->nr_maps; i++) {
|
||||
if (obj->maps[i].offset == offset)
|
||||
return &obj->maps[i];
|
||||
}
|
||||
return ERR_PTR(-ENOENT);
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <linux/err.h>
|
||||
#include <sys/types.h> // for size_t
|
||||
|
||||
enum libbpf_errno {
|
||||
__LIBBPF_ERRNO__START = 4000,
|
||||
|
@ -79,6 +80,11 @@ struct bpf_object *bpf_object__next(struct bpf_object *prev);
|
|||
(pos) != NULL; \
|
||||
(pos) = (tmp), (tmp) = bpf_object__next(tmp))
|
||||
|
||||
typedef void (*bpf_object_clear_priv_t)(struct bpf_object *, void *);
|
||||
int bpf_object__set_priv(struct bpf_object *obj, void *priv,
|
||||
bpf_object_clear_priv_t clear_priv);
|
||||
void *bpf_object__priv(struct bpf_object *prog);
|
||||
|
||||
/* Accessors of bpf_program. */
|
||||
struct bpf_program;
|
||||
struct bpf_program *bpf_program__next(struct bpf_program *prog,
|
||||
|
@ -195,6 +201,13 @@ struct bpf_map;
|
|||
struct bpf_map *
|
||||
bpf_object__find_map_by_name(struct bpf_object *obj, const char *name);
|
||||
|
||||
/*
|
||||
* Get bpf_map through the offset of corresponding struct bpf_map_def
|
||||
* in the bpf object file.
|
||||
*/
|
||||
struct bpf_map *
|
||||
bpf_object__find_map_by_offset(struct bpf_object *obj, size_t offset);
|
||||
|
||||
struct bpf_map *
|
||||
bpf_map__next(struct bpf_map *map, struct bpf_object *obj);
|
||||
#define bpf_map__for_each(pos, obj) \
|
||||
|
|
|
@ -61,6 +61,13 @@ OPTIONS
|
|||
default, but this option shows live (currently allocated) pages
|
||||
instead. (This option works with --page option only)
|
||||
|
||||
--time::
|
||||
Only analyze samples within given time window: <start>,<stop>. Times
|
||||
have the format seconds.microseconds. If start is not given (i.e., time
|
||||
string is ',x.y') then analysis starts at the beginning of the file. If
|
||||
stop time is not given (i.e, time string is 'x.y,') then analysis goes
|
||||
to end of file.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-record[1]
|
||||
|
|
|
@ -382,6 +382,13 @@ OPTIONS
|
|||
--header-only::
|
||||
Show only perf.data header (forces --stdio).
|
||||
|
||||
--time::
|
||||
Only analyze samples within given time window: <start>,<stop>. Times
|
||||
have the format seconds.microseconds. If start is not given (i.e., time
|
||||
string is ',x.y') then analysis starts at the beginning of the file. If
|
||||
stop time is not given (i.e, time string is 'x.y,') then analysis goes
|
||||
to end of file.
|
||||
|
||||
--itrace::
|
||||
Options for decoding instruction tracing data. The options are:
|
||||
|
||||
|
|
|
@ -128,6 +128,18 @@ OPTIONS for 'perf sched timehist'
|
|||
--wakeups::
|
||||
Show wakeup events.
|
||||
|
||||
-M::
|
||||
--migrations::
|
||||
Show migration events.
|
||||
|
||||
--time::
|
||||
Only analyze samples within given time window: <start>,<stop>. Times
|
||||
have the format seconds.microseconds. If start is not given (i.e., time
|
||||
string is ',x.y') then analysis starts at the beginning of the file. If
|
||||
stop time is not given (i.e, time string is 'x.y,') then analysis goes
|
||||
to end of file.
|
||||
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-record[1]
|
||||
|
|
|
@ -212,6 +212,9 @@ OPTIONS
|
|||
--hide-call-graph::
|
||||
When printing symbols do not display call chain.
|
||||
|
||||
--stop-bt::
|
||||
Stop display of callgraph at these symbols
|
||||
|
||||
-C::
|
||||
--cpu:: Only report samples for the list of CPUs provided. Multiple CPUs can
|
||||
be provided as a comma-separated list with no space: 0,1. Ranges of
|
||||
|
@ -289,6 +292,13 @@ include::itrace.txt[]
|
|||
--force::
|
||||
Don't do ownership validation.
|
||||
|
||||
--time::
|
||||
Only analyze samples within given time window: <start>,<stop>. Times
|
||||
have the format seconds.microseconds. If start is not given (i.e., time
|
||||
string is ',x.y') then analysis starts at the beginning of the file. If
|
||||
stop time is not given (i.e, time string is 'x.y,') then analysis goes
|
||||
to end of file.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-record[1], linkperf:perf-script-perl[1],
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
#include <sys/types.h>
|
||||
#include <regex.h>
|
||||
|
||||
struct arm64_annotate {
|
||||
regex_t call_insn,
|
||||
jump_insn;
|
||||
};
|
||||
|
||||
static struct ins_ops *arm64__associate_instruction_ops(struct arch *arch, const char *name)
|
||||
{
|
||||
struct arm64_annotate *arm = arch->priv;
|
||||
struct ins_ops *ops;
|
||||
regmatch_t match[2];
|
||||
|
||||
if (!regexec(&arm->jump_insn, name, 2, match, 0))
|
||||
ops = &jump_ops;
|
||||
else if (!regexec(&arm->call_insn, name, 2, match, 0))
|
||||
ops = &call_ops;
|
||||
else if (!strcmp(name, "ret"))
|
||||
ops = &ret_ops;
|
||||
else
|
||||
return NULL;
|
||||
|
||||
arch__associate_ins_ops(arch, name, ops);
|
||||
return ops;
|
||||
}
|
||||
|
||||
static int arm64__annotate_init(struct arch *arch)
|
||||
{
|
||||
struct arm64_annotate *arm;
|
||||
int err;
|
||||
|
||||
if (arch->initialized)
|
||||
return 0;
|
||||
|
||||
arm = zalloc(sizeof(*arm));
|
||||
if (!arm)
|
||||
return -1;
|
||||
|
||||
/* bl, blr */
|
||||
err = regcomp(&arm->call_insn, "^blr?$", REG_EXTENDED);
|
||||
if (err)
|
||||
goto out_free_arm;
|
||||
/* b, b.cond, br, cbz/cbnz, tbz/tbnz */
|
||||
err = regcomp(&arm->jump_insn, "^[ct]?br?\\.?(cc|cs|eq|ge|gt|hi|le|ls|lt|mi|ne|pl)?n?z?$",
|
||||
REG_EXTENDED);
|
||||
if (err)
|
||||
goto out_free_call;
|
||||
|
||||
arch->initialized = true;
|
||||
arch->priv = arm;
|
||||
arch->associate_instruction_ops = arm64__associate_instruction_ops;
|
||||
arch->objdump.comment_char = ';';
|
||||
arch->objdump.skip_functions_char = '+';
|
||||
return 0;
|
||||
|
||||
out_free_call:
|
||||
regfree(&arm->call_insn);
|
||||
out_free_arm:
|
||||
free(arm);
|
||||
return -1;
|
||||
}
|
|
@ -4,27 +4,27 @@
|
|||
|
||||
struct test arch_tests[] = {
|
||||
{
|
||||
.desc = "x86 rdpmc test",
|
||||
.desc = "x86 rdpmc",
|
||||
.func = test__rdpmc,
|
||||
},
|
||||
{
|
||||
.desc = "Test converting perf time to TSC",
|
||||
.desc = "Convert perf time to TSC",
|
||||
.func = test__perf_time_to_tsc,
|
||||
},
|
||||
#ifdef HAVE_DWARF_UNWIND_SUPPORT
|
||||
{
|
||||
.desc = "Test dwarf unwind",
|
||||
.desc = "DWARF unwind",
|
||||
.func = test__dwarf_unwind,
|
||||
},
|
||||
#endif
|
||||
#ifdef HAVE_AUXTRACE_SUPPORT
|
||||
{
|
||||
.desc = "Test x86 instruction decoder - new instructions",
|
||||
.desc = "x86 instruction decoder - new instructions",
|
||||
.func = test__insn_x86,
|
||||
},
|
||||
#endif
|
||||
{
|
||||
.desc = "Test intel cqm nmi context read",
|
||||
.desc = "Intel cqm nmi context read",
|
||||
.func = test__intel_cqm_count_nmi_context,
|
||||
},
|
||||
{
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "util/session.h"
|
||||
#include "util/tool.h"
|
||||
#include "util/callchain.h"
|
||||
#include "util/time-utils.h"
|
||||
|
||||
#include <subcmd/parse-options.h>
|
||||
#include "util/trace-event.h"
|
||||
|
@ -49,6 +50,7 @@ struct alloc_stat {
|
|||
u64 ptr;
|
||||
u64 bytes_req;
|
||||
u64 bytes_alloc;
|
||||
u64 last_alloc;
|
||||
u32 hit;
|
||||
u32 pingpong;
|
||||
|
||||
|
@ -62,9 +64,13 @@ static struct rb_root root_alloc_sorted;
|
|||
static struct rb_root root_caller_stat;
|
||||
static struct rb_root root_caller_sorted;
|
||||
|
||||
static unsigned long total_requested, total_allocated;
|
||||
static unsigned long total_requested, total_allocated, total_freed;
|
||||
static unsigned long nr_allocs, nr_cross_allocs;
|
||||
|
||||
/* filters for controlling start and stop of time of analysis */
|
||||
static struct perf_time_interval ptime;
|
||||
const char *time_str;
|
||||
|
||||
static int insert_alloc_stat(unsigned long call_site, unsigned long ptr,
|
||||
int bytes_req, int bytes_alloc, int cpu)
|
||||
{
|
||||
|
@ -105,6 +111,8 @@ static int insert_alloc_stat(unsigned long call_site, unsigned long ptr,
|
|||
}
|
||||
data->call_site = call_site;
|
||||
data->alloc_cpu = cpu;
|
||||
data->last_alloc = bytes_alloc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -223,6 +231,8 @@ static int perf_evsel__process_free_event(struct perf_evsel *evsel,
|
|||
if (!s_alloc)
|
||||
return 0;
|
||||
|
||||
total_freed += s_alloc->last_alloc;
|
||||
|
||||
if ((short)sample->cpu != s_alloc->alloc_cpu) {
|
||||
s_alloc->pingpong++;
|
||||
|
||||
|
@ -907,6 +917,15 @@ static int perf_evsel__process_page_free_event(struct perf_evsel *evsel,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static bool perf_kmem__skip_sample(struct perf_sample *sample)
|
||||
{
|
||||
/* skip sample based on time? */
|
||||
if (perf_time__skip_sample(&ptime, sample->time))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
typedef int (*tracepoint_handler)(struct perf_evsel *evsel,
|
||||
struct perf_sample *sample);
|
||||
|
||||
|
@ -926,6 +945,9 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (perf_kmem__skip_sample(sample))
|
||||
return 0;
|
||||
|
||||
dump_printf(" ... thread: %s:%d\n", thread__comm_str(thread), thread->tid);
|
||||
|
||||
if (evsel->handler != NULL) {
|
||||
|
@ -1128,6 +1150,11 @@ static void print_slab_summary(void)
|
|||
printf("\n========================\n");
|
||||
printf("Total bytes requested: %'lu\n", total_requested);
|
||||
printf("Total bytes allocated: %'lu\n", total_allocated);
|
||||
printf("Total bytes freed: %'lu\n", total_freed);
|
||||
if (total_allocated > total_freed) {
|
||||
printf("Net total bytes allocated: %'lu\n",
|
||||
total_allocated - total_freed);
|
||||
}
|
||||
printf("Total bytes wasted on internal fragmentation: %'lu\n",
|
||||
total_allocated - total_requested);
|
||||
printf("Internal fragmentation: %f%%\n",
|
||||
|
@ -1884,6 +1911,8 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
OPT_CALLBACK_NOOPT(0, "page", NULL, NULL, "Analyze page allocator",
|
||||
parse_page_opt),
|
||||
OPT_BOOLEAN(0, "live", &live_page, "Show live page stat"),
|
||||
OPT_STRING(0, "time", &time_str, "str",
|
||||
"Time span of interest (start,stop)"),
|
||||
OPT_END()
|
||||
};
|
||||
const char *const kmem_subcommands[] = { "record", "stat", NULL };
|
||||
|
@ -1944,6 +1973,11 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
|
||||
symbol__init(&session->header.env);
|
||||
|
||||
if (perf_time__parse_str(&ptime, time_str) != 0) {
|
||||
pr_err("Invalid time string\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!strcmp(argv[0], "stat")) {
|
||||
setlocale(LC_ALL, "");
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include "util/llvm-utils.h"
|
||||
#include "util/bpf-loader.h"
|
||||
#include "util/trigger.h"
|
||||
#include "util/perf-hooks.h"
|
||||
#include "asm/bug.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
@ -206,6 +207,12 @@ static void sig_handler(int sig)
|
|||
done = 1;
|
||||
}
|
||||
|
||||
static void sigsegv_handler(int sig)
|
||||
{
|
||||
perf_hooks__recover();
|
||||
sighandler_dump_stack(sig);
|
||||
}
|
||||
|
||||
static void record__sig_exit(void)
|
||||
{
|
||||
if (signr == -1)
|
||||
|
@ -833,6 +840,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
|
|||
signal(SIGCHLD, sig_handler);
|
||||
signal(SIGINT, sig_handler);
|
||||
signal(SIGTERM, sig_handler);
|
||||
signal(SIGSEGV, sigsegv_handler);
|
||||
|
||||
if (rec->opts.auxtrace_snapshot_mode || rec->switch_output) {
|
||||
signal(SIGUSR2, snapshot_sig_handler);
|
||||
|
@ -970,6 +978,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
|
|||
|
||||
trigger_ready(&auxtrace_snapshot_trigger);
|
||||
trigger_ready(&switch_output_trigger);
|
||||
perf_hooks__invoke_record_start();
|
||||
for (;;) {
|
||||
unsigned long long hits = rec->samples;
|
||||
|
||||
|
@ -1114,6 +1123,8 @@ out_child:
|
|||
}
|
||||
}
|
||||
|
||||
perf_hooks__invoke_record_end();
|
||||
|
||||
if (!err && !quiet) {
|
||||
char samples[128];
|
||||
const char *postfix = rec->timestamp_filename ?
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
#include "util/hist.h"
|
||||
#include "util/data.h"
|
||||
#include "arch/common.h"
|
||||
|
||||
#include "util/time-utils.h"
|
||||
#include "util/auxtrace.h"
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
@ -59,6 +59,8 @@ struct report {
|
|||
const char *pretty_printing_style;
|
||||
const char *cpu_list;
|
||||
const char *symbol_filter_str;
|
||||
const char *time_str;
|
||||
struct perf_time_interval ptime;
|
||||
float min_percent;
|
||||
u64 nr_entries;
|
||||
u64 queue_size;
|
||||
|
@ -158,6 +160,9 @@ static int process_sample_event(struct perf_tool *tool,
|
|||
};
|
||||
int ret = 0;
|
||||
|
||||
if (perf_time__skip_sample(&rep->ptime, sample->time))
|
||||
return 0;
|
||||
|
||||
if (machine__resolve(machine, &al, sample) < 0) {
|
||||
pr_debug("problem processing %d event, skipping it.\n",
|
||||
event->header.type);
|
||||
|
@ -830,6 +835,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
OPT_CALLBACK_DEFAULT(0, "stdio-color", NULL, "mode",
|
||||
"'always' (default), 'never' or 'auto' only applicable to --stdio mode",
|
||||
stdio__config_color, "always"),
|
||||
OPT_STRING(0, "time", &report.time_str, "str",
|
||||
"Time span of interest (start,stop)"),
|
||||
OPT_END()
|
||||
};
|
||||
struct perf_data_file file = {
|
||||
|
@ -1015,6 +1022,11 @@ repeat:
|
|||
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;
|
||||
}
|
||||
|
||||
sort__setup_elide(stdout);
|
||||
|
||||
ret = __cmd_report(&report);
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "util/color.h"
|
||||
#include "util/stat.h"
|
||||
#include "util/callchain.h"
|
||||
#include "util/time-utils.h"
|
||||
|
||||
#include <subcmd/parse-options.h>
|
||||
#include "util/trace-event.h"
|
||||
|
@ -203,7 +204,10 @@ struct perf_sched {
|
|||
unsigned int max_stack;
|
||||
bool show_cpu_visual;
|
||||
bool show_wakeups;
|
||||
bool show_migrations;
|
||||
u64 skipped_samples;
|
||||
const char *time_str;
|
||||
struct perf_time_interval ptime;
|
||||
};
|
||||
|
||||
/* per thread run time data */
|
||||
|
@ -216,6 +220,8 @@ struct thread_runtime {
|
|||
|
||||
struct stats run_stats;
|
||||
u64 total_run_time;
|
||||
|
||||
u64 migrations;
|
||||
};
|
||||
|
||||
/* per event run time data */
|
||||
|
@ -1834,13 +1840,14 @@ static void timehist_header(struct perf_sched *sched)
|
|||
static void timehist_print_sample(struct perf_sched *sched,
|
||||
struct perf_sample *sample,
|
||||
struct addr_location *al,
|
||||
struct thread *thread)
|
||||
struct thread *thread,
|
||||
u64 t)
|
||||
{
|
||||
struct thread_runtime *tr = thread__priv(thread);
|
||||
u32 max_cpus = sched->max_cpu + 1;
|
||||
char tstr[64];
|
||||
|
||||
timestamp__scnprintf_usec(sample->time, tstr, sizeof(tstr));
|
||||
timestamp__scnprintf_usec(t, tstr, sizeof(tstr));
|
||||
printf("%15s [%04d] ", tstr, sample->cpu);
|
||||
|
||||
if (sched->show_cpu_visual) {
|
||||
|
@ -2191,12 +2198,94 @@ static int timehist_sched_wakeup_event(struct perf_tool *tool,
|
|||
tr->ready_to_run = sample->time;
|
||||
|
||||
/* show wakeups if requested */
|
||||
if (sched->show_wakeups)
|
||||
if (sched->show_wakeups &&
|
||||
!perf_time__skip_sample(&sched->ptime, sample->time))
|
||||
timehist_print_wakeup_event(sched, sample, machine, thread);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void timehist_print_migration_event(struct perf_sched *sched,
|
||||
struct perf_evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine,
|
||||
struct thread *migrated)
|
||||
{
|
||||
struct thread *thread;
|
||||
char tstr[64];
|
||||
u32 max_cpus = sched->max_cpu + 1;
|
||||
u32 ocpu, dcpu;
|
||||
|
||||
if (sched->summary_only)
|
||||
return;
|
||||
|
||||
max_cpus = sched->max_cpu + 1;
|
||||
ocpu = perf_evsel__intval(evsel, sample, "orig_cpu");
|
||||
dcpu = perf_evsel__intval(evsel, sample, "dest_cpu");
|
||||
|
||||
thread = machine__findnew_thread(machine, sample->pid, sample->tid);
|
||||
if (thread == NULL)
|
||||
return;
|
||||
|
||||
if (timehist_skip_sample(sched, thread) &&
|
||||
timehist_skip_sample(sched, migrated)) {
|
||||
return;
|
||||
}
|
||||
|
||||
timestamp__scnprintf_usec(sample->time, tstr, sizeof(tstr));
|
||||
printf("%15s [%04d] ", tstr, sample->cpu);
|
||||
|
||||
if (sched->show_cpu_visual) {
|
||||
u32 i;
|
||||
char c;
|
||||
|
||||
printf(" ");
|
||||
for (i = 0; i < max_cpus; ++i) {
|
||||
c = (i == sample->cpu) ? 'm' : ' ';
|
||||
printf("%c", c);
|
||||
}
|
||||
printf(" ");
|
||||
}
|
||||
|
||||
printf(" %-*s ", comm_width, timehist_get_commstr(thread));
|
||||
|
||||
/* dt spacer */
|
||||
printf(" %9s %9s %9s ", "", "", "");
|
||||
|
||||
printf("migrated: %s", timehist_get_commstr(migrated));
|
||||
printf(" cpu %d => %d", ocpu, dcpu);
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static int timehist_migrate_task_event(struct perf_tool *tool,
|
||||
union perf_event *event __maybe_unused,
|
||||
struct perf_evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine)
|
||||
{
|
||||
struct perf_sched *sched = container_of(tool, struct perf_sched, tool);
|
||||
struct thread *thread;
|
||||
struct thread_runtime *tr = NULL;
|
||||
/* want pid of migrated task not pid in sample */
|
||||
const u32 pid = perf_evsel__intval(evsel, sample, "pid");
|
||||
|
||||
thread = machine__findnew_thread(machine, 0, pid);
|
||||
if (thread == NULL)
|
||||
return -1;
|
||||
|
||||
tr = thread__get_runtime(thread);
|
||||
if (tr == NULL)
|
||||
return -1;
|
||||
|
||||
tr->migrations++;
|
||||
|
||||
/* show migrations if requested */
|
||||
timehist_print_migration_event(sched, evsel, sample, machine, thread);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int timehist_sched_change_event(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_evsel *evsel,
|
||||
|
@ -2204,10 +2293,11 @@ static int timehist_sched_change_event(struct perf_tool *tool,
|
|||
struct machine *machine)
|
||||
{
|
||||
struct perf_sched *sched = container_of(tool, struct perf_sched, tool);
|
||||
struct perf_time_interval *ptime = &sched->ptime;
|
||||
struct addr_location al;
|
||||
struct thread *thread;
|
||||
struct thread_runtime *tr = NULL;
|
||||
u64 tprev;
|
||||
u64 tprev, t = sample->time;
|
||||
int rc = 0;
|
||||
|
||||
if (machine__resolve(machine, &al, sample) < 0) {
|
||||
|
@ -2234,9 +2324,35 @@ static int timehist_sched_change_event(struct perf_tool *tool,
|
|||
|
||||
tprev = perf_evsel__get_time(evsel, sample->cpu);
|
||||
|
||||
timehist_update_runtime_stats(tr, sample->time, tprev);
|
||||
/*
|
||||
* If start time given:
|
||||
* - sample time is under window user cares about - skip sample
|
||||
* - tprev is under window user cares about - reset to start of window
|
||||
*/
|
||||
if (ptime->start && ptime->start > t)
|
||||
goto out;
|
||||
|
||||
if (ptime->start > tprev)
|
||||
tprev = ptime->start;
|
||||
|
||||
/*
|
||||
* If end time given:
|
||||
* - previous sched event is out of window - we are done
|
||||
* - sample time is beyond window user cares about - reset it
|
||||
* to close out stats for time window interest
|
||||
*/
|
||||
if (ptime->end) {
|
||||
if (tprev > ptime->end)
|
||||
goto out;
|
||||
|
||||
if (t > ptime->end)
|
||||
t = ptime->end;
|
||||
}
|
||||
|
||||
timehist_update_runtime_stats(tr, t, tprev);
|
||||
|
||||
if (!sched->summary_only)
|
||||
timehist_print_sample(sched, sample, &al, thread);
|
||||
timehist_print_sample(sched, sample, &al, thread, t);
|
||||
|
||||
out:
|
||||
if (tr) {
|
||||
|
@ -2295,6 +2411,7 @@ static void print_thread_runtime(struct thread *t,
|
|||
print_sched_time(r->run_stats.max, 6);
|
||||
printf(" ");
|
||||
printf("%5.2f", stddev);
|
||||
printf(" %5" PRIu64, r->migrations);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
|
@ -2356,10 +2473,10 @@ static void timehist_print_summary(struct perf_sched *sched,
|
|||
|
||||
printf("\nRuntime summary\n");
|
||||
printf("%*s parent sched-in ", comm_width, "comm");
|
||||
printf(" run-time min-run avg-run max-run stddev\n");
|
||||
printf(" run-time min-run avg-run max-run stddev migrations\n");
|
||||
printf("%*s (count) ", comm_width, "");
|
||||
printf(" (msec) (msec) (msec) (msec) %%\n");
|
||||
printf("%.105s\n", graph_dotted_line);
|
||||
printf("%.117s\n", graph_dotted_line);
|
||||
|
||||
machine__for_each_thread(m, show_thread_runtime, &totals);
|
||||
task_count = totals.task_count;
|
||||
|
@ -2460,6 +2577,9 @@ static int perf_sched__timehist(struct perf_sched *sched)
|
|||
{ "sched:sched_wakeup", timehist_sched_wakeup_event, },
|
||||
{ "sched:sched_wakeup_new", timehist_sched_wakeup_event, },
|
||||
};
|
||||
const struct perf_evsel_str_handler migrate_handlers[] = {
|
||||
{ "sched:sched_migrate_task", timehist_migrate_task_event, },
|
||||
};
|
||||
struct perf_data_file file = {
|
||||
.path = input_name,
|
||||
.mode = PERF_DATA_MODE_READ,
|
||||
|
@ -2495,6 +2615,11 @@ static int perf_sched__timehist(struct perf_sched *sched)
|
|||
|
||||
symbol__init(&session->header.env);
|
||||
|
||||
if (perf_time__parse_str(&sched->ptime, sched->time_str) != 0) {
|
||||
pr_err("Invalid time string\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (timehist_check_attr(sched, evlist) != 0)
|
||||
goto out;
|
||||
|
||||
|
@ -2507,6 +2632,10 @@ static int perf_sched__timehist(struct perf_sched *sched)
|
|||
if (!perf_session__has_traces(session, "record -R"))
|
||||
goto out;
|
||||
|
||||
if (sched->show_migrations &&
|
||||
perf_session__set_tracepoints_handlers(session, migrate_handlers))
|
||||
goto out;
|
||||
|
||||
/* pre-allocate struct for per-CPU idle stats */
|
||||
sched->max_cpu = session->header.env.nr_cpus_online;
|
||||
if (sched->max_cpu == 0)
|
||||
|
@ -2903,7 +3032,10 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
OPT_BOOLEAN('S', "with-summary", &sched.summary,
|
||||
"Show all syscalls and summary with statistics"),
|
||||
OPT_BOOLEAN('w', "wakeups", &sched.show_wakeups, "Show wakeup events"),
|
||||
OPT_BOOLEAN('M', "migrations", &sched.show_migrations, "Show migration events"),
|
||||
OPT_BOOLEAN('V', "cpu-visual", &sched.show_cpu_visual, "Add CPU visual"),
|
||||
OPT_STRING(0, "time", &sched.time_str, "str",
|
||||
"Time span for analysis (start,stop)"),
|
||||
OPT_PARENT(sched_options)
|
||||
};
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "util/thread_map.h"
|
||||
#include "util/stat.h"
|
||||
#include "util/thread-stack.h"
|
||||
#include "util/time-utils.h"
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/stringify.h>
|
||||
#include <linux/time64.h>
|
||||
|
@ -833,6 +834,8 @@ struct perf_script {
|
|||
struct cpu_map *cpus;
|
||||
struct thread_map *threads;
|
||||
int name_width;
|
||||
const char *time_str;
|
||||
struct perf_time_interval ptime;
|
||||
};
|
||||
|
||||
static int perf_evlist__max_name_len(struct perf_evlist *evlist)
|
||||
|
@ -1014,6 +1017,9 @@ 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))
|
||||
return 0;
|
||||
|
||||
if (debug_mode) {
|
||||
if (sample->time < last_timestamp) {
|
||||
pr_err("Samples misordered, previous: %" PRIu64
|
||||
|
@ -2151,6 +2157,8 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
"system-wide collection from all CPUs"),
|
||||
OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]",
|
||||
"only consider these symbols"),
|
||||
OPT_STRING(0, "stop-bt", &symbol_conf.bt_stop_list_str, "symbol[,symbol...]",
|
||||
"Stop display of callgraph at these symbols"),
|
||||
OPT_STRING('C', "cpu", &cpu_list, "cpu", "list of cpus to profile"),
|
||||
OPT_STRING('c', "comms", &symbol_conf.comm_list_str, "comm[,comm...]",
|
||||
"only display events for these comms"),
|
||||
|
@ -2184,7 +2192,8 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
"Enable symbol demangling"),
|
||||
OPT_BOOLEAN(0, "demangle-kernel", &symbol_conf.demangle_kernel,
|
||||
"Enable kernel symbol demangling"),
|
||||
|
||||
OPT_STRING(0, "time", &script.time_str, "str",
|
||||
"Time span of interest (start,stop)"),
|
||||
OPT_END()
|
||||
};
|
||||
const char * const script_subcommands[] = { "record", "report", NULL };
|
||||
|
@ -2463,6 +2472,12 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
if (err < 0)
|
||||
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");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = __cmd_script(&script);
|
||||
|
||||
flush_scripting();
|
||||
|
|
|
@ -74,8 +74,6 @@ struct trace {
|
|||
size_t nr;
|
||||
int *entries;
|
||||
} ev_qualifier_ids;
|
||||
struct intlist *tid_list;
|
||||
struct intlist *pid_list;
|
||||
struct {
|
||||
size_t nr;
|
||||
pid_t *entries;
|
||||
|
@ -1890,18 +1888,6 @@ out_put:
|
|||
return err;
|
||||
}
|
||||
|
||||
static bool skip_sample(struct trace *trace, struct perf_sample *sample)
|
||||
{
|
||||
if ((trace->pid_list && intlist__find(trace->pid_list, sample->pid)) ||
|
||||
(trace->tid_list && intlist__find(trace->tid_list, sample->tid)))
|
||||
return false;
|
||||
|
||||
if (trace->pid_list || trace->tid_list)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void trace__set_base_time(struct trace *trace,
|
||||
struct perf_evsel *evsel,
|
||||
struct perf_sample *sample)
|
||||
|
@ -1926,11 +1912,13 @@ static int trace__process_sample(struct perf_tool *tool,
|
|||
struct machine *machine __maybe_unused)
|
||||
{
|
||||
struct trace *trace = container_of(tool, struct trace, tool);
|
||||
struct thread *thread;
|
||||
int err = 0;
|
||||
|
||||
tracepoint_handler handler = evsel->handler;
|
||||
|
||||
if (skip_sample(trace, sample))
|
||||
thread = machine__findnew_thread(trace->host, sample->pid, sample->tid);
|
||||
if (thread && thread__is_filtered(thread))
|
||||
return 0;
|
||||
|
||||
trace__set_base_time(trace, evsel, sample);
|
||||
|
@ -1943,27 +1931,6 @@ static int trace__process_sample(struct perf_tool *tool,
|
|||
return err;
|
||||
}
|
||||
|
||||
static int parse_target_str(struct trace *trace)
|
||||
{
|
||||
if (trace->opts.target.pid) {
|
||||
trace->pid_list = intlist__new(trace->opts.target.pid);
|
||||
if (trace->pid_list == NULL) {
|
||||
pr_err("Error parsing process id string\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (trace->opts.target.tid) {
|
||||
trace->tid_list = intlist__new(trace->opts.target.tid);
|
||||
if (trace->tid_list == NULL) {
|
||||
pr_err("Error parsing thread id string\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int trace__record(struct trace *trace, int argc, const char **argv)
|
||||
{
|
||||
unsigned int rec_argc, i, j;
|
||||
|
@ -2460,6 +2427,12 @@ static int trace__replay(struct trace *trace)
|
|||
if (session == NULL)
|
||||
return -1;
|
||||
|
||||
if (trace->opts.target.pid)
|
||||
symbol_conf.pid_list_str = strdup(trace->opts.target.pid);
|
||||
|
||||
if (trace->opts.target.tid)
|
||||
symbol_conf.tid_list_str = strdup(trace->opts.target.tid);
|
||||
|
||||
if (symbol__init(&session->header.env) < 0)
|
||||
goto out;
|
||||
|
||||
|
@ -2503,10 +2476,6 @@ static int trace__replay(struct trace *trace)
|
|||
evsel->handler = trace__pgfault;
|
||||
}
|
||||
|
||||
err = parse_target_str(trace);
|
||||
if (err != 0)
|
||||
goto out;
|
||||
|
||||
setup_pager();
|
||||
|
||||
err = perf_session__process_events(session);
|
||||
|
|
|
@ -42,6 +42,7 @@ perf-y += backward-ring-buffer.o
|
|||
perf-y += sdt.o
|
||||
perf-y += is_printable_array.o
|
||||
perf-y += bitmap.o
|
||||
perf-y += perf-hooks.o
|
||||
|
||||
$(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c tests/Build
|
||||
$(call rule_mkdir)
|
||||
|
|
|
@ -57,7 +57,7 @@ static struct {
|
|||
} bpf_testcase_table[] = {
|
||||
{
|
||||
LLVM_TESTCASE_BASE,
|
||||
"Test basic BPF filtering",
|
||||
"Basic BPF filtering",
|
||||
"[basic_bpf_test]",
|
||||
"fix 'perf test LLVM' first",
|
||||
"load bpf object failed",
|
||||
|
@ -67,7 +67,7 @@ static struct {
|
|||
#ifdef HAVE_BPF_PROLOGUE
|
||||
{
|
||||
LLVM_TESTCASE_BPF_PROLOGUE,
|
||||
"Test BPF prologue generation",
|
||||
"BPF prologue generation",
|
||||
"[bpf_prologue_test]",
|
||||
"fix kbuild first",
|
||||
"check your vmlinux setting?",
|
||||
|
@ -77,7 +77,7 @@ static struct {
|
|||
#endif
|
||||
{
|
||||
LLVM_TESTCASE_BPF_RELOCATION,
|
||||
"Test BPF relocation checker",
|
||||
"BPF relocation checker",
|
||||
"[bpf_relocation_test]",
|
||||
"fix 'perf test LLVM' first",
|
||||
"libbpf error when dealing with relocation",
|
||||
|
|
|
@ -28,119 +28,119 @@ static struct test generic_tests[] = {
|
|||
.func = test__vmlinux_matches_kallsyms,
|
||||
},
|
||||
{
|
||||
.desc = "detect openat syscall event",
|
||||
.desc = "Detect openat syscall event",
|
||||
.func = test__openat_syscall_event,
|
||||
},
|
||||
{
|
||||
.desc = "detect openat syscall event on all cpus",
|
||||
.desc = "Detect openat syscall event on all cpus",
|
||||
.func = test__openat_syscall_event_on_all_cpus,
|
||||
},
|
||||
{
|
||||
.desc = "read samples using the mmap interface",
|
||||
.desc = "Read samples using the mmap interface",
|
||||
.func = test__basic_mmap,
|
||||
},
|
||||
{
|
||||
.desc = "parse events tests",
|
||||
.desc = "Parse event definition strings",
|
||||
.func = test__parse_events,
|
||||
},
|
||||
{
|
||||
.desc = "Validate PERF_RECORD_* events & perf_sample fields",
|
||||
.desc = "PERF_RECORD_* events & perf_sample fields",
|
||||
.func = test__PERF_RECORD,
|
||||
},
|
||||
{
|
||||
.desc = "Test perf pmu format parsing",
|
||||
.desc = "Parse perf pmu format",
|
||||
.func = test__pmu,
|
||||
},
|
||||
{
|
||||
.desc = "Test dso data read",
|
||||
.desc = "DSO data read",
|
||||
.func = test__dso_data,
|
||||
},
|
||||
{
|
||||
.desc = "Test dso data cache",
|
||||
.desc = "DSO data cache",
|
||||
.func = test__dso_data_cache,
|
||||
},
|
||||
{
|
||||
.desc = "Test dso data reopen",
|
||||
.desc = "DSO data reopen",
|
||||
.func = test__dso_data_reopen,
|
||||
},
|
||||
{
|
||||
.desc = "roundtrip evsel->name check",
|
||||
.desc = "Roundtrip evsel->name",
|
||||
.func = test__perf_evsel__roundtrip_name_test,
|
||||
},
|
||||
{
|
||||
.desc = "Check parsing of sched tracepoints fields",
|
||||
.desc = "Parse sched tracepoints fields",
|
||||
.func = test__perf_evsel__tp_sched_test,
|
||||
},
|
||||
{
|
||||
.desc = "Generate and check syscalls:sys_enter_openat event fields",
|
||||
.desc = "syscalls:sys_enter_openat event fields",
|
||||
.func = test__syscall_openat_tp_fields,
|
||||
},
|
||||
{
|
||||
.desc = "struct perf_event_attr setup",
|
||||
.desc = "Setup struct perf_event_attr",
|
||||
.func = test__attr,
|
||||
},
|
||||
{
|
||||
.desc = "Test matching and linking multiple hists",
|
||||
.desc = "Match and link multiple hists",
|
||||
.func = test__hists_link,
|
||||
},
|
||||
{
|
||||
.desc = "Try 'import perf' in python, checking link problems",
|
||||
.desc = "'import perf' in python",
|
||||
.func = test__python_use,
|
||||
},
|
||||
{
|
||||
.desc = "Test breakpoint overflow signal handler",
|
||||
.desc = "Breakpoint overflow signal handler",
|
||||
.func = test__bp_signal,
|
||||
},
|
||||
{
|
||||
.desc = "Test breakpoint overflow sampling",
|
||||
.desc = "Breakpoint overflow sampling",
|
||||
.func = test__bp_signal_overflow,
|
||||
},
|
||||
{
|
||||
.desc = "Test number of exit event of a simple workload",
|
||||
.desc = "Number of exit events of a simple workload",
|
||||
.func = test__task_exit,
|
||||
},
|
||||
{
|
||||
.desc = "Test software clock events have valid period values",
|
||||
.desc = "Software clock events period values",
|
||||
.func = test__sw_clock_freq,
|
||||
},
|
||||
{
|
||||
.desc = "Test object code reading",
|
||||
.desc = "Object code reading",
|
||||
.func = test__code_reading,
|
||||
},
|
||||
{
|
||||
.desc = "Test sample parsing",
|
||||
.desc = "Sample parsing",
|
||||
.func = test__sample_parsing,
|
||||
},
|
||||
{
|
||||
.desc = "Test using a dummy software event to keep tracking",
|
||||
.desc = "Use a dummy software event to keep tracking",
|
||||
.func = test__keep_tracking,
|
||||
},
|
||||
{
|
||||
.desc = "Test parsing with no sample_id_all bit set",
|
||||
.desc = "Parse with no sample_id_all bit set",
|
||||
.func = test__parse_no_sample_id_all,
|
||||
},
|
||||
{
|
||||
.desc = "Test filtering hist entries",
|
||||
.desc = "Filter hist entries",
|
||||
.func = test__hists_filter,
|
||||
},
|
||||
{
|
||||
.desc = "Test mmap thread lookup",
|
||||
.desc = "Lookup mmap thread",
|
||||
.func = test__mmap_thread_lookup,
|
||||
},
|
||||
{
|
||||
.desc = "Test thread mg sharing",
|
||||
.desc = "Share thread mg",
|
||||
.func = test__thread_mg_share,
|
||||
},
|
||||
{
|
||||
.desc = "Test output sorting of hist entries",
|
||||
.desc = "Sort output of hist entries",
|
||||
.func = test__hists_output,
|
||||
},
|
||||
{
|
||||
.desc = "Test cumulation of child hist entries",
|
||||
.desc = "Cumulate child hist entries",
|
||||
.func = test__hists_cumulate,
|
||||
},
|
||||
{
|
||||
.desc = "Test tracking with sched_switch",
|
||||
.desc = "Track with sched_switch",
|
||||
.func = test__switch_tracking,
|
||||
},
|
||||
{
|
||||
|
@ -152,15 +152,15 @@ static struct test generic_tests[] = {
|
|||
.func = test__fdarray__add,
|
||||
},
|
||||
{
|
||||
.desc = "Test kmod_path__parse function",
|
||||
.desc = "kmod_path__parse",
|
||||
.func = test__kmod_path__parse,
|
||||
},
|
||||
{
|
||||
.desc = "Test thread map",
|
||||
.desc = "Thread map",
|
||||
.func = test__thread_map,
|
||||
},
|
||||
{
|
||||
.desc = "Test LLVM searching and compiling",
|
||||
.desc = "LLVM search and compile",
|
||||
.func = test__llvm,
|
||||
.subtest = {
|
||||
.skip_if_fail = true,
|
||||
|
@ -169,11 +169,11 @@ static struct test generic_tests[] = {
|
|||
},
|
||||
},
|
||||
{
|
||||
.desc = "Test topology in session",
|
||||
.desc = "Session topology",
|
||||
.func = test_session_topology,
|
||||
},
|
||||
{
|
||||
.desc = "Test BPF filter",
|
||||
.desc = "BPF filter",
|
||||
.func = test__bpf,
|
||||
.subtest = {
|
||||
.skip_if_fail = true,
|
||||
|
@ -182,53 +182,57 @@ static struct test generic_tests[] = {
|
|||
},
|
||||
},
|
||||
{
|
||||
.desc = "Test thread map synthesize",
|
||||
.desc = "Synthesize thread map",
|
||||
.func = test__thread_map_synthesize,
|
||||
},
|
||||
{
|
||||
.desc = "Test cpu map synthesize",
|
||||
.desc = "Synthesize cpu map",
|
||||
.func = test__cpu_map_synthesize,
|
||||
},
|
||||
{
|
||||
.desc = "Test stat config synthesize",
|
||||
.desc = "Synthesize stat config",
|
||||
.func = test__synthesize_stat_config,
|
||||
},
|
||||
{
|
||||
.desc = "Test stat synthesize",
|
||||
.desc = "Synthesize stat",
|
||||
.func = test__synthesize_stat,
|
||||
},
|
||||
{
|
||||
.desc = "Test stat round synthesize",
|
||||
.desc = "Synthesize stat round",
|
||||
.func = test__synthesize_stat_round,
|
||||
},
|
||||
{
|
||||
.desc = "Test attr update synthesize",
|
||||
.desc = "Synthesize attr update",
|
||||
.func = test__event_update,
|
||||
},
|
||||
{
|
||||
.desc = "Test events times",
|
||||
.desc = "Event times",
|
||||
.func = test__event_times,
|
||||
},
|
||||
{
|
||||
.desc = "Test backward reading from ring buffer",
|
||||
.desc = "Read backward ring buffer",
|
||||
.func = test__backward_ring_buffer,
|
||||
},
|
||||
{
|
||||
.desc = "Test cpu map print",
|
||||
.desc = "Print cpu map",
|
||||
.func = test__cpu_map_print,
|
||||
},
|
||||
{
|
||||
.desc = "Test SDT event probing",
|
||||
.desc = "Probe SDT events",
|
||||
.func = test__sdt_event,
|
||||
},
|
||||
{
|
||||
.desc = "Test is_printable_array function",
|
||||
.desc = "is_printable_array",
|
||||
.func = test__is_printable_array,
|
||||
},
|
||||
{
|
||||
.desc = "Test bitmap print",
|
||||
.desc = "Print bitmap",
|
||||
.func = test__bitmap_print,
|
||||
},
|
||||
{
|
||||
.desc = "perf hooks",
|
||||
.func = test__perf_hooks,
|
||||
},
|
||||
{
|
||||
.func = NULL,
|
||||
},
|
||||
|
|
|
@ -34,19 +34,19 @@ static struct {
|
|||
} bpf_source_table[__LLVM_TESTCASE_MAX] = {
|
||||
[LLVM_TESTCASE_BASE] = {
|
||||
.source = test_llvm__bpf_base_prog,
|
||||
.desc = "Basic BPF llvm compiling test",
|
||||
.desc = "Basic BPF llvm compile",
|
||||
},
|
||||
[LLVM_TESTCASE_KBUILD] = {
|
||||
.source = test_llvm__bpf_test_kbuild_prog,
|
||||
.desc = "Test kbuild searching",
|
||||
.desc = "kbuild searching",
|
||||
},
|
||||
[LLVM_TESTCASE_BPF_PROLOGUE] = {
|
||||
.source = test_llvm__bpf_test_prologue_prog,
|
||||
.desc = "Compile source for BPF prologue generation test",
|
||||
.desc = "Compile source for BPF prologue generation",
|
||||
},
|
||||
[LLVM_TESTCASE_BPF_RELOCATION] = {
|
||||
.source = test_llvm__bpf_test_relocation,
|
||||
.desc = "Compile source for BPF relocation test",
|
||||
.desc = "Compile source for BPF relocation",
|
||||
.should_load_fail = true,
|
||||
},
|
||||
};
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "tests.h"
|
||||
#include "debug.h"
|
||||
#include "util.h"
|
||||
#include "perf-hooks.h"
|
||||
|
||||
static void sigsegv_handler(int sig __maybe_unused)
|
||||
{
|
||||
pr_debug("SIGSEGV is observed as expected, try to recover.\n");
|
||||
perf_hooks__recover();
|
||||
signal(SIGSEGV, SIG_DFL);
|
||||
raise(SIGSEGV);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
static int hook_flags;
|
||||
|
||||
static void the_hook(void)
|
||||
{
|
||||
int *p = NULL;
|
||||
|
||||
hook_flags = 1234;
|
||||
|
||||
/* Generate a segfault, test perf_hooks__recover */
|
||||
*p = 0;
|
||||
}
|
||||
|
||||
int test__perf_hooks(int subtest __maybe_unused)
|
||||
{
|
||||
signal(SIGSEGV, sigsegv_handler);
|
||||
perf_hooks__set_hook("test", the_hook);
|
||||
perf_hooks__invoke_test();
|
||||
|
||||
/* hook is triggered? */
|
||||
if (hook_flags != 1234)
|
||||
return TEST_FAIL;
|
||||
|
||||
/* the buggy hook is removed? */
|
||||
if (perf_hooks__get_hook("test"))
|
||||
return TEST_FAIL;
|
||||
return TEST_OK;
|
||||
}
|
|
@ -91,6 +91,7 @@ int test__cpu_map_print(int subtest);
|
|||
int test__sdt_event(int subtest);
|
||||
int test__is_printable_array(int subtest);
|
||||
int test__bitmap_print(int subtest);
|
||||
int test__perf_hooks(int subtest);
|
||||
|
||||
#if defined(__arm__) || defined(__aarch64__)
|
||||
#ifdef HAVE_DWARF_UNWIND_SUPPORT
|
||||
|
|
|
@ -543,14 +543,16 @@ struct disasm_line *annotate_browser__find_offset(struct annotate_browser *brows
|
|||
static bool annotate_browser__jump(struct annotate_browser *browser)
|
||||
{
|
||||
struct disasm_line *dl = browser->selection;
|
||||
u64 offset;
|
||||
s64 idx;
|
||||
|
||||
if (!ins__is_jump(&dl->ins))
|
||||
return false;
|
||||
|
||||
dl = annotate_browser__find_offset(browser, dl->ops.target.offset, &idx);
|
||||
offset = dl->ops.target.offset;
|
||||
dl = annotate_browser__find_offset(browser, offset, &idx);
|
||||
if (dl == NULL) {
|
||||
ui_helpline__puts("Invalid jump offset");
|
||||
ui_helpline__printf("Invalid jump offset: %" PRIx64, offset);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -72,3 +72,13 @@ int ui_helpline__vshow(const char *fmt, va_list ap)
|
|||
{
|
||||
return helpline_fns->show(fmt, ap);
|
||||
}
|
||||
|
||||
void ui_helpline__printf(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
ui_helpline__pop();
|
||||
va_start(ap, fmt);
|
||||
ui_helpline__vpush(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ void ui_helpline__push(const char *msg);
|
|||
void ui_helpline__vpush(const char *fmt, va_list ap);
|
||||
void ui_helpline__fpush(const char *fmt, ...);
|
||||
void ui_helpline__puts(const char *msg);
|
||||
void ui_helpline__printf(const char *fmt, ...);
|
||||
int ui_helpline__vshow(const char *fmt, va_list ap);
|
||||
|
||||
extern char ui_helpline__current[512];
|
||||
|
|
|
@ -87,6 +87,7 @@ libperf-y += help-unknown-cmd.o
|
|||
libperf-y += mem-events.o
|
||||
libperf-y += vsprintf.o
|
||||
libperf-y += drv_configs.o
|
||||
libperf-y += time-utils.o
|
||||
|
||||
libperf-$(CONFIG_LIBBPF) += bpf-loader.o
|
||||
libperf-$(CONFIG_BPF_PROLOGUE) += bpf-prologue.o
|
||||
|
@ -123,6 +124,8 @@ libperf-$(CONFIG_LIBELF) += genelf.o
|
|||
libperf-$(CONFIG_DWARF) += genelf_debug.o
|
||||
endif
|
||||
|
||||
libperf-y += perf-hooks.o
|
||||
|
||||
CFLAGS_config.o += -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))"
|
||||
# avoid compiler warnings in 32-bit mode
|
||||
CFLAGS_genelf_debug.o += -Wno-packed
|
||||
|
|
|
@ -105,6 +105,7 @@ static int arch__associate_ins_ops(struct arch* arch, const char *name, struct i
|
|||
}
|
||||
|
||||
#include "arch/arm/annotate/instructions.c"
|
||||
#include "arch/arm64/annotate/instructions.c"
|
||||
#include "arch/x86/annotate/instructions.c"
|
||||
#include "arch/powerpc/annotate/instructions.c"
|
||||
|
||||
|
@ -113,6 +114,10 @@ static struct arch architectures[] = {
|
|||
.name = "arm",
|
||||
.init = arm__annotate_init,
|
||||
},
|
||||
{
|
||||
.name = "arm64",
|
||||
.init = arm64__annotate_init,
|
||||
},
|
||||
{
|
||||
.name = "x86",
|
||||
.instructions = x86__instructions,
|
||||
|
@ -408,7 +413,7 @@ static int dec__parse(struct arch *arch __maybe_unused, struct ins_operands *ops
|
|||
if (ops->target.raw == NULL)
|
||||
return -1;
|
||||
|
||||
comment = strchr(s, '#');
|
||||
comment = strchr(s, arch->objdump.comment_char);
|
||||
if (comment == NULL)
|
||||
return 0;
|
||||
|
||||
|
|
|
@ -166,6 +166,14 @@ int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment,
|
|||
if (!print_oneline)
|
||||
printed += fprintf(fp, "\n");
|
||||
|
||||
if (symbol_conf.bt_stop_list &&
|
||||
node->sym &&
|
||||
node->sym->name &&
|
||||
strlist__has_entry(symbol_conf.bt_stop_list,
|
||||
node->sym->name)) {
|
||||
break;
|
||||
}
|
||||
|
||||
first = false;
|
||||
next:
|
||||
callchain_cursor_advance(cursor);
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
PERF_HOOK(record_start)
|
||||
PERF_HOOK(record_end)
|
||||
PERF_HOOK(test)
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* perf_hooks.c
|
||||
*
|
||||
* Copyright (C) 2016 Wang Nan <wangnan0@huawei.com>
|
||||
* Copyright (C) 2016 Huawei Inc.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <setjmp.h>
|
||||
#include <linux/err.h>
|
||||
#include "util/util.h"
|
||||
#include "util/debug.h"
|
||||
#include "util/perf-hooks.h"
|
||||
|
||||
static sigjmp_buf jmpbuf;
|
||||
static const struct perf_hook_desc *current_perf_hook;
|
||||
|
||||
void perf_hooks__invoke(const struct perf_hook_desc *desc)
|
||||
{
|
||||
if (!(desc && desc->p_hook_func && *desc->p_hook_func))
|
||||
return;
|
||||
|
||||
if (sigsetjmp(jmpbuf, 1)) {
|
||||
pr_warning("Fatal error (SEGFAULT) in perf hook '%s'\n",
|
||||
desc->hook_name);
|
||||
*(current_perf_hook->p_hook_func) = NULL;
|
||||
} else {
|
||||
current_perf_hook = desc;
|
||||
(**desc->p_hook_func)();
|
||||
}
|
||||
current_perf_hook = NULL;
|
||||
}
|
||||
|
||||
void perf_hooks__recover(void)
|
||||
{
|
||||
if (current_perf_hook)
|
||||
siglongjmp(jmpbuf, 1);
|
||||
}
|
||||
|
||||
#define PERF_HOOK(name) \
|
||||
perf_hook_func_t __perf_hook_func_##name = NULL; \
|
||||
struct perf_hook_desc __perf_hook_desc_##name = \
|
||||
{.hook_name = #name, .p_hook_func = &__perf_hook_func_##name};
|
||||
#include "perf-hooks-list.h"
|
||||
#undef PERF_HOOK
|
||||
|
||||
#define PERF_HOOK(name) \
|
||||
&__perf_hook_desc_##name,
|
||||
|
||||
static struct perf_hook_desc *perf_hooks[] = {
|
||||
#include "perf-hooks-list.h"
|
||||
};
|
||||
#undef PERF_HOOK
|
||||
|
||||
int perf_hooks__set_hook(const char *hook_name,
|
||||
perf_hook_func_t hook_func)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(perf_hooks); i++) {
|
||||
if (strcmp(hook_name, perf_hooks[i]->hook_name) != 0)
|
||||
continue;
|
||||
|
||||
if (*(perf_hooks[i]->p_hook_func))
|
||||
pr_warning("Overwrite existing hook: %s\n", hook_name);
|
||||
*(perf_hooks[i]->p_hook_func) = hook_func;
|
||||
return 0;
|
||||
}
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
perf_hook_func_t perf_hooks__get_hook(const char *hook_name)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(perf_hooks); i++) {
|
||||
if (strcmp(hook_name, perf_hooks[i]->hook_name) != 0)
|
||||
continue;
|
||||
|
||||
return *(perf_hooks[i]->p_hook_func);
|
||||
}
|
||||
return ERR_PTR(-ENOENT);
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
#ifndef PERF_UTIL_PERF_HOOKS_H
|
||||
#define PERF_UTIL_PERF_HOOKS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef void (*perf_hook_func_t)(void);
|
||||
struct perf_hook_desc {
|
||||
const char * const hook_name;
|
||||
perf_hook_func_t * const p_hook_func;
|
||||
};
|
||||
|
||||
extern void perf_hooks__invoke(const struct perf_hook_desc *);
|
||||
extern void perf_hooks__recover(void);
|
||||
|
||||
#define PERF_HOOK(name) \
|
||||
extern struct perf_hook_desc __perf_hook_desc_##name; \
|
||||
static inline void perf_hooks__invoke_##name(void) \
|
||||
{ \
|
||||
perf_hooks__invoke(&__perf_hook_desc_##name); \
|
||||
}
|
||||
|
||||
#include "perf-hooks-list.h"
|
||||
#undef PERF_HOOK
|
||||
|
||||
extern int
|
||||
perf_hooks__set_hook(const char *hook_name,
|
||||
perf_hook_func_t hook_func);
|
||||
|
||||
extern perf_hook_func_t
|
||||
perf_hooks__get_hook(const char *hook_name);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
|
@ -2032,6 +2032,10 @@ int symbol__init(struct perf_env *env)
|
|||
symbol_conf.sym_list_str, "symbol") < 0)
|
||||
goto out_free_tid_list;
|
||||
|
||||
if (setup_list(&symbol_conf.bt_stop_list,
|
||||
symbol_conf.bt_stop_list_str, "symbol") < 0)
|
||||
goto out_free_sym_list;
|
||||
|
||||
/*
|
||||
* A path to symbols of "/" is identical to ""
|
||||
* reset here for simplicity.
|
||||
|
@ -2049,6 +2053,8 @@ int symbol__init(struct perf_env *env)
|
|||
symbol_conf.initialized = true;
|
||||
return 0;
|
||||
|
||||
out_free_sym_list:
|
||||
strlist__delete(symbol_conf.sym_list);
|
||||
out_free_tid_list:
|
||||
intlist__delete(symbol_conf.tid_list);
|
||||
out_free_pid_list:
|
||||
|
@ -2064,6 +2070,7 @@ void symbol__exit(void)
|
|||
{
|
||||
if (!symbol_conf.initialized)
|
||||
return;
|
||||
strlist__delete(symbol_conf.bt_stop_list);
|
||||
strlist__delete(symbol_conf.sym_list);
|
||||
strlist__delete(symbol_conf.dso_list);
|
||||
strlist__delete(symbol_conf.comm_list);
|
||||
|
@ -2071,6 +2078,7 @@ void symbol__exit(void)
|
|||
intlist__delete(symbol_conf.pid_list);
|
||||
vmlinux_path__exit();
|
||||
symbol_conf.sym_list = symbol_conf.dso_list = symbol_conf.comm_list = NULL;
|
||||
symbol_conf.bt_stop_list = NULL;
|
||||
symbol_conf.initialized = false;
|
||||
}
|
||||
|
||||
|
|
|
@ -132,14 +132,16 @@ struct symbol_conf {
|
|||
*pid_list_str,
|
||||
*tid_list_str,
|
||||
*sym_list_str,
|
||||
*col_width_list_str;
|
||||
*col_width_list_str,
|
||||
*bt_stop_list_str;
|
||||
struct strlist *dso_list,
|
||||
*comm_list,
|
||||
*sym_list,
|
||||
*dso_from_list,
|
||||
*dso_to_list,
|
||||
*sym_from_list,
|
||||
*sym_to_list;
|
||||
*sym_to_list,
|
||||
*bt_stop_list;
|
||||
struct intlist *pid_list,
|
||||
*tid_list;
|
||||
const char *symfs;
|
||||
|
|
|
@ -0,0 +1,119 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
#include <linux/time64.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "perf.h"
|
||||
#include "debug.h"
|
||||
#include "time-utils.h"
|
||||
|
||||
int parse_nsec_time(const char *str, u64 *ptime)
|
||||
{
|
||||
u64 time_sec, time_nsec;
|
||||
char *end;
|
||||
|
||||
time_sec = strtoul(str, &end, 10);
|
||||
if (*end != '.' && *end != '\0')
|
||||
return -1;
|
||||
|
||||
if (*end == '.') {
|
||||
int i;
|
||||
char nsec_buf[10];
|
||||
|
||||
if (strlen(++end) > 9)
|
||||
return -1;
|
||||
|
||||
strncpy(nsec_buf, end, 9);
|
||||
nsec_buf[9] = '\0';
|
||||
|
||||
/* make it nsec precision */
|
||||
for (i = strlen(nsec_buf); i < 9; i++)
|
||||
nsec_buf[i] = '0';
|
||||
|
||||
time_nsec = strtoul(nsec_buf, &end, 10);
|
||||
if (*end != '\0')
|
||||
return -1;
|
||||
} else
|
||||
time_nsec = 0;
|
||||
|
||||
*ptime = time_sec * NSEC_PER_SEC + time_nsec;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_timestr_sec_nsec(struct perf_time_interval *ptime,
|
||||
char *start_str, char *end_str)
|
||||
{
|
||||
if (start_str && (*start_str != '\0') &&
|
||||
(parse_nsec_time(start_str, &ptime->start) != 0)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (end_str && (*end_str != '\0') &&
|
||||
(parse_nsec_time(end_str, &ptime->end) != 0)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int perf_time__parse_str(struct perf_time_interval *ptime, const char *ostr)
|
||||
{
|
||||
char *start_str, *end_str;
|
||||
char *d, *str;
|
||||
int rc = 0;
|
||||
|
||||
if (ostr == NULL || *ostr == '\0')
|
||||
return 0;
|
||||
|
||||
/* copy original string because we need to modify it */
|
||||
str = strdup(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, ',');
|
||||
if (d) {
|
||||
*d = '\0';
|
||||
++d;
|
||||
}
|
||||
end_str = d;
|
||||
|
||||
rc = parse_timestr_sec_nsec(ptime, start_str, end_str);
|
||||
|
||||
free(str);
|
||||
|
||||
/* make sure end time is after start time if it was given */
|
||||
if (rc == 0 && ptime->end && ptime->end < ptime->start)
|
||||
return -EINVAL;
|
||||
|
||||
pr_debug("start time %" PRIu64 ", ", ptime->start);
|
||||
pr_debug("end time %" PRIu64 "\n", ptime->end);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool perf_time__skip_sample(struct perf_time_interval *ptime, u64 timestamp)
|
||||
{
|
||||
/* if time is not set don't drop sample */
|
||||
if (timestamp == 0)
|
||||
return false;
|
||||
|
||||
/* otherwise compare sample time to time window */
|
||||
if ((ptime->start && timestamp < ptime->start) ||
|
||||
(ptime->end && timestamp > ptime->end)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
#ifndef _TIME_UTILS_H_
|
||||
#define _TIME_UTILS_H_
|
||||
|
||||
struct perf_time_interval {
|
||||
u64 start, end;
|
||||
};
|
||||
|
||||
int parse_nsec_time(const char *str, u64 *ptime);
|
||||
|
||||
int perf_time__parse_str(struct perf_time_interval *ptime, const char *ostr);
|
||||
|
||||
bool perf_time__skip_sample(struct perf_time_interval *ptime, u64 timestamp);
|
||||
|
||||
#endif
|
|
@ -400,39 +400,6 @@ void sighandler_dump_stack(int sig)
|
|||
raise(sig);
|
||||
}
|
||||
|
||||
int parse_nsec_time(const char *str, u64 *ptime)
|
||||
{
|
||||
u64 time_sec, time_nsec;
|
||||
char *end;
|
||||
|
||||
time_sec = strtoul(str, &end, 10);
|
||||
if (*end != '.' && *end != '\0')
|
||||
return -1;
|
||||
|
||||
if (*end == '.') {
|
||||
int i;
|
||||
char nsec_buf[10];
|
||||
|
||||
if (strlen(++end) > 9)
|
||||
return -1;
|
||||
|
||||
strncpy(nsec_buf, end, 9);
|
||||
nsec_buf[9] = '\0';
|
||||
|
||||
/* make it nsec precision */
|
||||
for (i = strlen(nsec_buf); i < 9; i++)
|
||||
nsec_buf[i] = '0';
|
||||
|
||||
time_nsec = strtoul(nsec_buf, &end, 10);
|
||||
if (*end != '\0')
|
||||
return -1;
|
||||
} else
|
||||
time_nsec = 0;
|
||||
|
||||
*ptime = time_sec * NSEC_PER_SEC + time_nsec;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int timestamp__scnprintf_usec(u64 timestamp, char *buf, size_t sz)
|
||||
{
|
||||
u64 sec = timestamp / NSEC_PER_SEC;
|
||||
|
|
|
@ -179,8 +179,6 @@ static inline void *zalloc(size_t size)
|
|||
#undef tolower
|
||||
#undef toupper
|
||||
|
||||
int parse_nsec_time(const char *str, u64 *ptime);
|
||||
|
||||
extern unsigned char sane_ctype[256];
|
||||
#define GIT_SPACE 0x01
|
||||
#define GIT_DIGIT 0x02
|
||||
|
|
Loading…
Reference in New Issue