Commit 29bf4dbc authored by Ingo Molnar's avatar Ingo Molnar

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

Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core

Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo:

"
   User visible changes:

   - Enable sampling loads and stores simultaneously in 'perf mem' (Stephane Eranian)

   - 'perf diff' output improvements (Namhyung Kim)

   - Fix error reporting for evsel pgfault constructor (Arnaldo Carvalho de Melo)

   Infrastructure changes:

   - Move debugfs sterrno like method to tools/lib/ so that it may be used by
     other tools, as 'perf probe' will be soon (Arnaldo Carvalho de Melo)

   - Introduce function fro deleting/removing hist_entry to avoid code duplication
     (Arnaldo Carvalho de Melo)

   - Support parsing parameterized events (Cody P Schafer)

   - Add support for IP address formats in libtraceevent (David Ahern)

   - Fix typo in sample-parsing.c 'perf test' entry (Rasmus Villemoes)

   - Remove some unused functions from color.c (Rickard Strandqvist)
"
Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents f10698ed 3d199b5b
...@@ -52,12 +52,18 @@ Description: Per-pmu performance monitoring events specific to the running syste ...@@ -52,12 +52,18 @@ Description: Per-pmu performance monitoring events specific to the running syste
event=0x2abc event=0x2abc
event=0x423,inv,cmask=0x3 event=0x423,inv,cmask=0x3
domain=0x1,offset=0x8,starting_index=0xffff domain=0x1,offset=0x8,starting_index=0xffff
domain=0x1,offset=0x8,core=?
Each of the assignments indicates a value to be assigned to a Each of the assignments indicates a value to be assigned to a
particular set of bits (as defined by the format file particular set of bits (as defined by the format file
corresponding to the <term>) in the perf_event structure passed corresponding to the <term>) in the perf_event structure passed
to the perf_open syscall. to the perf_open syscall.
In the case of the last example, a value replacing "?" would
need to be provided by the user selecting the particular event.
This is referred to as "event parameterization". Event
parameters have the format 'param=?'.
What: /sys/bus/event_source/devices/<pmu>/events/<event>.unit What: /sys/bus/event_source/devices/<pmu>/events/<event>.unit
Date: 2014/02/24 Date: 2014/02/24
Contact: Linux kernel mailing list <linux-kernel@vger.kernel.org> Contact: Linux kernel mailing list <linux-kernel@vger.kernel.org>
......
#define _GNU_SOURCE
#include <errno.h> #include <errno.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
...@@ -98,3 +99,45 @@ char *debugfs_mount(const char *mountpoint) ...@@ -98,3 +99,45 @@ char *debugfs_mount(const char *mountpoint)
out: out:
return debugfs_mountpoint; return debugfs_mountpoint;
} }
int debugfs__strerror_open(int err, char *buf, size_t size, const char *filename)
{
char sbuf[128];
switch (err) {
case ENOENT:
if (debugfs_found) {
snprintf(buf, size,
"Error:\tFile %s/%s not found.\n"
"Hint:\tPerhaps this kernel misses some CONFIG_ setting to enable this feature?.\n",
debugfs_mountpoint, filename);
break;
}
snprintf(buf, size, "%s",
"Error:\tUnable to find debugfs\n"
"Hint:\tWas your kernel compiled with debugfs support?\n"
"Hint:\tIs the debugfs filesystem mounted?\n"
"Hint:\tTry 'sudo mount -t debugfs nodev /sys/kernel/debug'");
break;
case EACCES:
snprintf(buf, size,
"Error:\tNo permissions to read %s/%s\n"
"Hint:\tTry 'sudo mount -o remount,mode=755 %s'\n",
debugfs_mountpoint, filename, debugfs_mountpoint);
break;
default:
snprintf(buf, size, "%s", strerror_r(err, sbuf, sizeof(sbuf)));
break;
}
return 0;
}
int debugfs__strerror_open_tp(int err, char *buf, size_t size, const char *sys, const char *name)
{
char path[PATH_MAX];
snprintf(path, PATH_MAX, "tracing/events/%s/%s", sys, name ?: "*");
return debugfs__strerror_open(err, buf, size, path);
}
...@@ -26,4 +26,7 @@ char *debugfs_mount(const char *mountpoint); ...@@ -26,4 +26,7 @@ char *debugfs_mount(const char *mountpoint);
extern char debugfs_mountpoint[]; extern char debugfs_mountpoint[];
int debugfs__strerror_open(int err, char *buf, size_t size, const char *filename);
int debugfs__strerror_open_tp(int err, char *buf, size_t size, const char *sys, const char *name);
#endif /* __API_DEBUGFS_H__ */ #endif /* __API_DEBUGFS_H__ */
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#include <stdint.h> #include <stdint.h>
#include <limits.h> #include <limits.h>
#include <netinet/ip6.h>
#include "event-parse.h" #include "event-parse.h"
#include "event-utils.h" #include "event-utils.h"
...@@ -4149,6 +4150,324 @@ static void print_mac_arg(struct trace_seq *s, int mac, void *data, int size, ...@@ -4149,6 +4150,324 @@ static void print_mac_arg(struct trace_seq *s, int mac, void *data, int size,
trace_seq_printf(s, fmt, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); trace_seq_printf(s, fmt, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
} }
static void print_ip4_addr(struct trace_seq *s, char i, unsigned char *buf)
{
const char *fmt;
if (i == 'i')
fmt = "%03d.%03d.%03d.%03d";
else
fmt = "%d.%d.%d.%d";
trace_seq_printf(s, fmt, buf[0], buf[1], buf[2], buf[3]);
}
static inline bool ipv6_addr_v4mapped(const struct in6_addr *a)
{
return ((unsigned long)(a->s6_addr32[0] | a->s6_addr32[1]) |
(unsigned long)(a->s6_addr32[2] ^ htonl(0x0000ffff))) == 0UL;
}
static inline bool ipv6_addr_is_isatap(const struct in6_addr *addr)
{
return (addr->s6_addr32[2] | htonl(0x02000000)) == htonl(0x02005EFE);
}
static void print_ip6c_addr(struct trace_seq *s, unsigned char *addr)
{
int i, j, range;
unsigned char zerolength[8];
int longest = 1;
int colonpos = -1;
uint16_t word;
uint8_t hi, lo;
bool needcolon = false;
bool useIPv4;
struct in6_addr in6;
memcpy(&in6, addr, sizeof(struct in6_addr));
useIPv4 = ipv6_addr_v4mapped(&in6) || ipv6_addr_is_isatap(&in6);
memset(zerolength, 0, sizeof(zerolength));
if (useIPv4)
range = 6;
else
range = 8;
/* find position of longest 0 run */
for (i = 0; i < range; i++) {
for (j = i; j < range; j++) {
if (in6.s6_addr16[j] != 0)
break;
zerolength[i]++;
}
}
for (i = 0; i < range; i++) {
if (zerolength[i] > longest) {
longest = zerolength[i];
colonpos = i;
}
}
if (longest == 1) /* don't compress a single 0 */
colonpos = -1;
/* emit address */
for (i = 0; i < range; i++) {
if (i == colonpos) {
if (needcolon || i == 0)
trace_seq_printf(s, ":");
trace_seq_printf(s, ":");
needcolon = false;
i += longest - 1;
continue;
}
if (needcolon) {
trace_seq_printf(s, ":");
needcolon = false;
}
/* hex u16 without leading 0s */
word = ntohs(in6.s6_addr16[i]);
hi = word >> 8;
lo = word & 0xff;
if (hi)
trace_seq_printf(s, "%x%02x", hi, lo);
else
trace_seq_printf(s, "%x", lo);
needcolon = true;
}
if (useIPv4) {
if (needcolon)
trace_seq_printf(s, ":");
print_ip4_addr(s, 'I', &in6.s6_addr[12]);
}
return;
}
static void print_ip6_addr(struct trace_seq *s, char i, unsigned char *buf)
{
int j;
for (j = 0; j < 16; j += 2) {
trace_seq_printf(s, "%02x%02x", buf[j], buf[j+1]);
if (i == 'I' && j < 14)
trace_seq_printf(s, ":");
}
}
/*
* %pi4 print an IPv4 address with leading zeros
* %pI4 print an IPv4 address without leading zeros
* %pi6 print an IPv6 address without colons
* %pI6 print an IPv6 address with colons
* %pI6c print an IPv6 address in compressed form with colons
* %pISpc print an IP address based on sockaddr; p adds port.
*/
static int print_ipv4_arg(struct trace_seq *s, const char *ptr, char i,
void *data, int size, struct event_format *event,
struct print_arg *arg)
{
unsigned char *buf;
if (arg->type == PRINT_FUNC) {
process_defined_func(s, data, size, event, arg);
return 0;
}
if (arg->type != PRINT_FIELD) {
trace_seq_printf(s, "ARG TYPE NOT FIELD BUT %d", arg->type);
return 0;
}
if (!arg->field.field) {
arg->field.field =
pevent_find_any_field(event, arg->field.name);
if (!arg->field.field) {
do_warning("%s: field %s not found",
__func__, arg->field.name);
return 0;
}
}
buf = data + arg->field.field->offset;
if (arg->field.field->size != 4) {
trace_seq_printf(s, "INVALIDIPv4");
return 0;
}
print_ip4_addr(s, i, buf);
return 0;
}
static int print_ipv6_arg(struct trace_seq *s, const char *ptr, char i,
void *data, int size, struct event_format *event,
struct print_arg *arg)
{
char have_c = 0;
unsigned char *buf;
int rc = 0;
/* pI6c */
if (i == 'I' && *ptr == 'c') {
have_c = 1;
ptr++;
rc++;
}
if (arg->type == PRINT_FUNC) {
process_defined_func(s, data, size, event, arg);
return rc;
}
if (arg->type != PRINT_FIELD) {
trace_seq_printf(s, "ARG TYPE NOT FIELD BUT %d", arg->type);
return rc;
}
if (!arg->field.field) {
arg->field.field =
pevent_find_any_field(event, arg->field.name);
if (!arg->field.field) {
do_warning("%s: field %s not found",
__func__, arg->field.name);
return rc;
}
}
buf = data + arg->field.field->offset;
if (arg->field.field->size != 16) {
trace_seq_printf(s, "INVALIDIPv6");
return rc;
}
if (have_c)
print_ip6c_addr(s, buf);
else
print_ip6_addr(s, i, buf);
return rc;
}
static int print_ipsa_arg(struct trace_seq *s, const char *ptr, char i,
void *data, int size, struct event_format *event,
struct print_arg *arg)
{
char have_c = 0, have_p = 0;
unsigned char *buf;
struct sockaddr_storage *sa;
int rc = 0;
/* pISpc */
if (i == 'I') {
if (*ptr == 'p') {
have_p = 1;
ptr++;
rc++;
}
if (*ptr == 'c') {
have_c = 1;
ptr++;
rc++;
}
}
if (arg->type == PRINT_FUNC) {
process_defined_func(s, data, size, event, arg);
return rc;
}
if (arg->type != PRINT_FIELD) {
trace_seq_printf(s, "ARG TYPE NOT FIELD BUT %d", arg->type);
return rc;
}
if (!arg->field.field) {
arg->field.field =
pevent_find_any_field(event, arg->field.name);
if (!arg->field.field) {
do_warning("%s: field %s not found",
__func__, arg->field.name);
return rc;
}
}
sa = (struct sockaddr_storage *) (data + arg->field.field->offset);
if (sa->ss_family == AF_INET) {
struct sockaddr_in *sa4 = (struct sockaddr_in *) sa;
if (arg->field.field->size < sizeof(struct sockaddr_in)) {
trace_seq_printf(s, "INVALIDIPv4");
return rc;
}
print_ip4_addr(s, i, (unsigned char *) &sa4->sin_addr);
if (have_p)
trace_seq_printf(s, ":%d", ntohs(sa4->sin_port));
} else if (sa->ss_family == AF_INET6) {
struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) sa;
if (arg->field.field->size < sizeof(struct sockaddr_in6)) {
trace_seq_printf(s, "INVALIDIPv6");
return rc;
}
if (have_p)
trace_seq_printf(s, "[");
buf = (unsigned char *) &sa6->sin6_addr;
if (have_c)
print_ip6c_addr(s, buf);
else
print_ip6_addr(s, i, buf);
if (have_p)
trace_seq_printf(s, "]:%d", ntohs(sa6->sin6_port));
}
return rc;
}
static int print_ip_arg(struct trace_seq *s, const char *ptr,
void *data, int size, struct event_format *event,
struct print_arg *arg)
{
char i = *ptr; /* 'i' or 'I' */
char ver;
int rc = 0;
ptr++;
rc++;
ver = *ptr;
ptr++;
rc++;
switch (ver) {
case '4':
rc += print_ipv4_arg(s, ptr, i, data, size, event, arg);
break;
case '6':
rc += print_ipv6_arg(s, ptr, i, data, size, event, arg);
break;
case 'S':
rc += print_ipsa_arg(s, ptr, i, data, size, event, arg);
break;
default:
return 0;
}
return rc;
}
static int is_printable_array(char *p, unsigned int len) static int is_printable_array(char *p, unsigned int len)
{ {
unsigned int i; unsigned int i;
...@@ -4337,6 +4656,15 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event ...@@ -4337,6 +4656,15 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event
ptr++; ptr++;
arg = arg->next; arg = arg->next;
break; break;
} else if (*(ptr+1) == 'I' || *(ptr+1) == 'i') {
int n;
n = print_ip_arg(s, ptr+1, data, size, event, arg);
if (n > 0) {
ptr += n;
arg = arg->next;
break;
}
} }
/* fall through */ /* fall through */
......
...@@ -89,6 +89,19 @@ raw encoding of 0x1A8 can be used: ...@@ -89,6 +89,19 @@ raw encoding of 0x1A8 can be used:
You should refer to the processor specific documentation for getting these You should refer to the processor specific documentation for getting these
details. Some of them are referenced in the SEE ALSO section below. details. Some of them are referenced in the SEE ALSO section below.
PARAMETERIZED EVENTS
--------------------
Some pmu events listed by 'perf-list' will be displayed with '?' in them. For
example:
hv_gpci/dtbp_ptitc,phys_processor_idx=?/
This means that when provided as an event, a value for '?' must
also be supplied. For example:
perf stat -C 0 -e 'hv_gpci/dtbp_ptitc,phys_processor_idx=0x2/' ...
OPTIONS OPTIONS
------- -------
......
...@@ -12,11 +12,12 @@ SYNOPSIS ...@@ -12,11 +12,12 @@ SYNOPSIS
DESCRIPTION DESCRIPTION
----------- -----------
"perf mem -t <TYPE> record" runs a command and gathers memory operation data "perf mem record" runs a command and gathers memory operation data
from it, into perf.data. Perf record options are accepted and are passed through. from it, into perf.data. Perf record options are accepted and are passed through.
"perf mem -t <TYPE> report" displays the result. It invokes perf report with the "perf mem report" displays the result. It invokes perf report with the
right set of options to display a memory access profile. right set of options to display a memory access profile. By default, loads
and stores are sampled. Use the -t option to limit to loads or stores.
Note that on Intel systems the memory latency reported is the use-latency, Note that on Intel systems the memory latency reported is the use-latency,
not the pure load (or store latency). Use latency includes any pipeline not the pure load (or store latency). Use latency includes any pipeline
...@@ -29,7 +30,7 @@ OPTIONS ...@@ -29,7 +30,7 @@ OPTIONS
-t:: -t::
--type=:: --type=::
Select the memory operation type: load or store (default: load) Select the memory operation type: load or store (default: load,store)
-D:: -D::
--dump-raw-samples=:: --dump-raw-samples=::
......
...@@ -33,6 +33,18 @@ OPTIONS ...@@ -33,6 +33,18 @@ OPTIONS
- a raw PMU event (eventsel+umask) in the form of rNNN where NNN is a - a raw PMU event (eventsel+umask) in the form of rNNN where NNN is a
hexadecimal event descriptor. hexadecimal event descriptor.
- a symbolically formed PMU event like 'pmu/param1=0x3,param2/' where
'param1', 'param2', etc are defined as formats for the PMU in
/sys/bus/event_sources/devices/<pmu>/format/*.
- a symbolically formed event like 'pmu/config=M,config1=N,config3=K/'
where M, N, K are numbers (in decimal, hex, octal format). Acceptable
values for each of 'config', 'config1' and 'config2' are defined by
corresponding entries in /sys/bus/event_sources/devices/<pmu>/format/*
param1 and param2 are defined as formats for the PMU in:
/sys/bus/event_sources/devices/<pmu>/format/*
- a hardware breakpoint event in the form of '\mem:addr[:access]' - a hardware breakpoint event in the form of '\mem:addr[:access]'
where addr is the address in memory you want to break in. where addr is the address in memory you want to break in.
Access is the memory access type (read, write, execute) it can Access is the memory access type (read, write, execute) it can
......
...@@ -25,11 +25,23 @@ OPTIONS ...@@ -25,11 +25,23 @@ OPTIONS
-e:: -e::
--event=:: --event=::
Select the PMU event. Selection can be a symbolic event name Select the PMU event. Selection can be:
(use 'perf list' to list all events) or a raw PMU
event (eventsel+umask) in the form of rNNN where NNN is a - a symbolic event name (use 'perf list' to list all events)
- a raw PMU event (eventsel+umask) in the form of rNNN where NNN is a
hexadecimal event descriptor. hexadecimal event descriptor.
- a symbolically formed event like 'pmu/param1=0x3,param2/' where
param1 and param2 are defined as formats for the PMU in
/sys/bus/event_sources/devices/<pmu>/format/*
- a symbolically formed event like 'pmu/config=M,config1=N,config2=K/'
where M, N, K are numbers (in decimal, hex, octal format).
Acceptable values for each of 'config', 'config1' and 'config2'
parameters are defined by corresponding entries in
/sys/bus/event_sources/devices/<pmu>/format/*
-i:: -i::
--no-inherit:: --no-inherit::
child tasks do not inherit counters child tasks do not inherit counters
......
...@@ -390,6 +390,15 @@ static void perf_evlist__collapse_resort(struct perf_evlist *evlist) ...@@ -390,6 +390,15 @@ static void perf_evlist__collapse_resort(struct perf_evlist *evlist)
} }
} }
static struct data__file *fmt_to_data_file(struct perf_hpp_fmt *fmt)
{
struct diff_hpp_fmt *dfmt = container_of(fmt, struct diff_hpp_fmt, fmt);
void *ptr = dfmt - dfmt->idx;
struct data__file *d = container_of(ptr, struct data__file, fmt);
return d;
}
static struct hist_entry* static struct hist_entry*
get_pair_data(struct hist_entry *he, struct data__file *d) get_pair_data(struct hist_entry *he, struct data__file *d)
{ {
...@@ -407,8 +416,7 @@ get_pair_data(struct hist_entry *he, struct data__file *d) ...@@ -407,8 +416,7 @@ get_pair_data(struct hist_entry *he, struct data__file *d)
static struct hist_entry* static struct hist_entry*
get_pair_fmt(struct hist_entry *he, struct diff_hpp_fmt *dfmt) get_pair_fmt(struct hist_entry *he, struct diff_hpp_fmt *dfmt)
{ {
void *ptr = dfmt - dfmt->idx; struct data__file *d = fmt_to_data_file(&dfmt->fmt);
struct data__file *d = container_of(ptr, struct data__file, fmt);
return get_pair_data(he, d); return get_pair_data(he, d);
} }
...@@ -430,7 +438,7 @@ static void hists__baseline_only(struct hists *hists) ...@@ -430,7 +438,7 @@ static void hists__baseline_only(struct hists *hists)
next = rb_next(&he->rb_node_in); next = rb_next(&he->rb_node_in);
if (!hist_entry__next_pair(he)) { if (!hist_entry__next_pair(he)) {
rb_erase(&he->rb_node_in, root); rb_erase(&he->rb_node_in, root);
hist_entry__free(he); hist_entry__delete(he);
} }
} }
} }
...@@ -448,11 +456,14 @@ static void hists__precompute(struct hists *hists) ...@@ -448,11 +456,14 @@ static void hists__precompute(struct hists *hists)
next = rb_first(root); next = rb_first(root);
while (next != NULL) { while (next != NULL) {
struct hist_entry *he, *pair; struct hist_entry *he, *pair;
struct data__file *d;
int i;
he = rb_entry(next, struct hist_entry, rb_node_in); he = rb_entry(next, struct hist_entry, rb_node_in);
next = rb_next(&he->rb_node_in); next = rb_next(&he->rb_node_in);
pair = get_pair_data(he, &data__files[sort_compute]); data__for_each_file_new(i, d) {
pair = get_pair_data(he, d);
if (!pair) if (!pair)
continue; continue;
...@@ -470,6 +481,7 @@ static void hists__precompute(struct hists *hists) ...@@ -470,6 +481,7 @@ static void hists__precompute(struct hists *hists)
BUG_ON(1); BUG_ON(1);
} }
} }
}
} }
static int64_t cmp_doubles(double l, double r) static int64_t cmp_doubles(double l, double r)
...@@ -517,7 +529,7 @@ __hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right, ...@@ -517,7 +529,7 @@ __hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right,
static int64_t static int64_t
hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right, hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right,
int c) int c, int sort_idx)
{ {
bool pairs_left = hist_entry__has_pairs(left); bool pairs_left = hist_entry__has_pairs(left);
bool pairs_right = hist_entry__has_pairs(right); bool pairs_right = hist_entry__has_pairs(right);
...@@ -529,8 +541,8 @@ hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right, ...@@ -529,8 +541,8 @@ hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right,
if (!pairs_left || !pairs_right) if (!pairs_left || !pairs_right)
return pairs_left ? -1 : 1; return pairs_left ? -1 : 1;
p_left = get_pair_data(left, &data__files[sort_compute]); p_left = get_pair_data(left, &data__files[sort_idx]);
p_right = get_pair_data(right, &data__files[sort_compute]); p_right = get_pair_data(right, &data__files[sort_idx]);
if (!p_left && !p_right) if (!p_left && !p_right)
return 0; return 0;
...@@ -546,90 +558,102 @@ hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right, ...@@ -546,90 +558,102 @@ hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right,
} }
static int64_t static int64_t
hist_entry__cmp_nop(struct hist_entry *left __maybe_unused, hist_entry__cmp_compute_idx(struct hist_entry *left, struct hist_entry *right,
struct hist_entry *right __maybe_unused) int c, int sort_idx)
{ {
struct hist_entry *p_right, *p_left;
p_left = get_pair_data(left, &data__files[sort_idx]);
p_right = get_pair_data(right, &data__files[sort_idx]);
if (!p_left && !p_right)
return 0;
if (!p_left || !p_right)
return p_left ? -1 : 1;
if (c != COMPUTE_DELTA) {
/*
* The delta can be computed without the baseline, but
* others are not. Put those entries which have no
* values below.
*/
if (left->dummy && right->dummy)
return 0; return 0;
if (left->dummy || right->dummy)
return left->dummy ? 1 : -1;
}
return __hist_entry__cmp_compute(p_left, p_right, c);
} }
static int64_t static int64_t
hist_entry__cmp_baseline(struct hist_entry *left, struct hist_entry *right) hist_entry__cmp_nop(struct perf_hpp_fmt *fmt __maybe_unused,
struct hist_entry *left __maybe_unused,
struct hist_entry *right __maybe_unused)
{ {
if (sort_compute)
return 0; return 0;
}
static int64_t
hist_entry__cmp_baseline(struct perf_hpp_fmt *fmt __maybe_unused,
struct hist_entry *left, struct hist_entry *right)
{
if (left->stat.period == right->stat.period) if (left->stat.period == right->stat.period)
return 0; return 0;
return left->stat.period > right->stat.period ? 1 : -1; return left->stat.period > right->stat.period ? 1 : -1;
} }
static int64_t static int64_t
hist_entry__cmp_delta(struct hist_entry *left, struct hist_entry *right) hist_entry__cmp_delta(struct perf_hpp_fmt *fmt,
struct hist_entry *left, struct hist_entry *right)
{ {
return hist_entry__cmp_compute(right, left, COMPUTE_DELTA); struct data__file *d = fmt_to_data_file(fmt);
return hist_entry__cmp_compute(right, left, COMPUTE_DELTA, d->idx);
} }
static int64_t static int64_t
hist_entry__cmp_ratio(struct hist_entry *left, struct hist_entry *right) hist_entry__cmp_ratio(struct perf_hpp_fmt *fmt,
struct hist_entry *left, struct hist_entry *right)
{ {
return hist_entry__cmp_compute(right, left, COMPUTE_RATIO); struct data__file *d = fmt_to_data_file(fmt);
return hist_entry__cmp_compute(right, left, COMPUTE_RATIO, d->idx);
} }
static int64_t static int64_t
hist_entry__cmp_wdiff(struct hist_entry *left, struct hist_entry *right) hist_entry__cmp_wdiff(struct perf_hpp_fmt *fmt,
struct hist_entry *left, struct hist_entry *right)
{ {
return hist_entry__cmp_compute(right, left, COMPUTE_WEIGHTED_DIFF); struct data__file *d = fmt_to_data_file(fmt);
return hist_entry__cmp_compute(right, left, COMPUTE_WEIGHTED_DIFF, d->idx);
} }
static void insert_hist_entry_by_compute(struct rb_root *root, static int64_t
struct hist_entry *he, hist_entry__cmp_delta_idx(struct perf_hpp_fmt *fmt __maybe_unused,
int c) struct hist_entry *left, struct hist_entry *right)
{ {
struct rb_node **p = &root->rb_node; return hist_entry__cmp_compute_idx(right, left, COMPUTE_DELTA,
struct rb_node *parent = NULL; sort_compute);
struct hist_entry *iter;
while (*p != NULL) {
parent = *p;
iter = rb_entry(parent, struct hist_entry, rb_node);
if (hist_entry__cmp_compute(he, iter, c) < 0)
p = &(*p)->rb_left;
else
p = &(*p)->rb_right;
}
rb_link_node(&he->rb_node, parent, p);
rb_insert_color(&he->rb_node, root);
} }
static void hists__compute_resort(struct hists *hists) static int64_t
hist_entry__cmp_ratio_idx(struct perf_hpp_fmt *fmt __maybe_unused,
struct hist_entry *left, struct hist_entry *right)
{ {
struct rb_root *root; return hist_entry__cmp_compute_idx(right, left, COMPUTE_RATIO,
struct rb_node *next; sort_compute);
}
if (sort__need_collapse)
root = &hists->entries_collapsed;
else
root = hists->entries_in;
hists->entries = RB_ROOT;
next = rb_first(root);
hists__reset_stats(hists);
hists__reset_col_len(hists);
while (next != NULL) {
struct hist_entry *he;
he = rb_entry(next, struct hist_entry, rb_node_in);
next = rb_next(&he->rb_node_in);
insert_hist_entry_by_compute(&hists->entries, he, compute);
hists__inc_stats(hists, he);
if (!he->filtered) static int64_t
hists__calc_col_len(hists, he); hist_entry__cmp_wdiff_idx(struct perf_hpp_fmt *fmt __maybe_unused,
} struct hist_entry *left, struct hist_entry *right)
{
return hist_entry__cmp_compute_idx(right, left, COMPUTE_WEIGHTED_DIFF,
sort_compute);
} }
static void hists__process(struct hists *hists) static void hists__process(struct hists *hists)
...@@ -637,12 +661,8 @@ static void hists__process(struct hists *hists) ...@@ -637,12 +661,8 @@ static void hists__process(struct hists *hists)
if (show_baseline_only) if (show_baseline_only)
hists__baseline_only(hists); hists__baseline_only(hists);
if (sort_compute) {
hists__precompute(hists); hists__precompute(hists);
hists__compute_resort(hists);
} else {
hists__output_resort(hists, NULL); hists__output_resort(hists, NULL);
}
hists__fprintf(hists, true, 0, 0, 0, stdout); hists__fprintf(hists, true, 0, 0, 0, stdout);
} }
...@@ -841,7 +861,7 @@ static int __hpp__color_compare(struct perf_hpp_fmt *fmt, ...@@ -841,7 +861,7 @@ static int __hpp__color_compare(struct perf_hpp_fmt *fmt,
char pfmt[20] = " "; char pfmt[20] = " ";
if (!pair) if (!pair)
goto dummy_print; goto no_print;
switch (comparison_method) { switch (comparison_method) {
case COMPUTE_DELTA: case COMPUTE_DELTA:
...@@ -850,8 +870,6 @@ static int __hpp__color_compare(struct perf_hpp_fmt *fmt, ...@@ -850,8 +870,6 @@ static int __hpp__color_compare(struct perf_hpp_fmt *fmt,
else else
diff = compute_delta(he, pair); diff = compute_delta(he, pair);
if (fabs(diff) < 0.01)
goto dummy_print;
scnprintf(pfmt, 20, "%%%+d.2f%%%%", dfmt->header_width - 1); scnprintf(pfmt, 20, "%%%+d.2f%%%%", dfmt->header_width - 1);
return percent_color_snprintf(hpp->buf, hpp->size, return percent_color_snprintf(hpp->buf, hpp->size,
pfmt, diff); pfmt, diff);
...@@ -882,6 +900,9 @@ static int __hpp__color_compare(struct perf_hpp_fmt *fmt, ...@@ -882,6 +900,9 @@ static int __hpp__color_compare(struct perf_hpp_fmt *fmt,
BUG_ON(1); BUG_ON(1);
} }
dummy_print: dummy_print:
return scnprintf(hpp->buf, hpp->size, "%*s",
dfmt->header_width, "N/A");
no_print:
return scnprintf(hpp->buf, hpp->size, "%*s", return scnprintf(hpp->buf, hpp->size, "%*s",
dfmt->header_width, pfmt); dfmt->header_width, pfmt);
} }
...@@ -932,14 +953,15 @@ hpp__entry_pair(struct hist_entry *he, struct hist_entry *pair, ...@@ -932,14 +953,15 @@ hpp__entry_pair(struct hist_entry *he, struct hist_entry *pair,
else else
diff = compute_delta(he, pair); diff = compute_delta(he, pair);
if (fabs(diff) >= 0.01)
scnprintf(buf, size, "%+4.2F%%", diff); scnprintf(buf, size, "%+4.2F%%", diff);
break; break;
case PERF_HPP_DIFF__RATIO: case PERF_HPP_DIFF__RATIO:
/* No point for ratio number if we are dummy.. */ /* No point for ratio number if we are dummy.. */
if (he->dummy) if (he->dummy) {
scnprintf(buf, size, "N/A");
break; break;
}
if (pair->diff.computed) if (pair->diff.computed)
ratio = pair->diff.period_ratio; ratio = pair->diff.period_ratio;
...@@ -952,8 +974,10 @@ hpp__entry_pair(struct hist_entry *he, struct hist_entry *pair, ...@@ -952,8 +974,10 @@ hpp__entry_pair(struct hist_entry *he, struct hist_entry *pair,
case PERF_HPP_DIFF__WEIGHTED_DIFF: case PERF_HPP_DIFF__WEIGHTED_DIFF:
/* No point for wdiff number if we are dummy.. */ /* No point for wdiff number if we are dummy.. */
if (he->dummy) if (he->dummy) {
scnprintf(buf, size, "N/A");
break; break;
}
if (pair->diff.computed) if (pair->diff.computed)
wdiff = pair->diff.wdiff; wdiff = pair->diff.wdiff;
...@@ -1105,9 +1129,10 @@ static void data__hpp_register(struct data__file *d, int idx) ...@@ -1105,9 +1129,10 @@ static void data__hpp_register(struct data__file *d, int idx)
perf_hpp__register_sort_field(fmt); perf_hpp__register_sort_field(fmt);
} }
static void ui_init(void) static int ui_init(void)
{ {
struct data__file *d; struct data__file *d;
struct perf_hpp_fmt *fmt;
int i; int i;
data__for_each_file(i, d) { data__for_each_file(i, d) {
...@@ -1137,6 +1162,46 @@ static void ui_init(void) ...@@ -1137,6 +1162,46 @@ static void ui_init(void)
data__hpp_register(d, i ? PERF_HPP_DIFF__PERIOD : data__hpp_register(d, i ? PERF_HPP_DIFF__PERIOD :
PERF_HPP_DIFF__PERIOD_BASELINE); PERF_HPP_DIFF__PERIOD_BASELINE);
} }
if (!sort_compute)
return 0;
/*
* Prepend an fmt to sort on columns at 'sort_compute' first.
* This fmt is added only to the sort list but not to the
* output fields list.
*
* Note that this column (data) can be compared twice - one
* for this 'sort_compute' fmt and another for the normal
* diff_hpp_fmt. But it shouldn't a problem as most entries
* will be sorted out by first try or baseline and comparing
* is not a costly operation.
*/
fmt = zalloc(sizeof(*fmt));
if (fmt == NULL) {
pr_err("Memory allocation failed\n");
return -1;
}
fmt->cmp = hist_entry__cmp_nop;
fmt->collapse = hist_entry__cmp_nop;
switch (compute) {
case COMPUTE_DELTA:
fmt->sort = hist_entry__cmp_delta_idx;
break;
case COMPUTE_RATIO:
fmt->sort = hist_entry__cmp_ratio_idx;
break;
case COMPUTE_WEIGHTED_DIFF:
fmt->sort = hist_entry__cmp_wdiff_idx;
break;
default:
BUG_ON(1);
}
list_add(&fmt->sort_list, &perf_hpp__sort_list);
return 0;
} }
static int data_init(int argc, const char **argv) static int data_init(int argc, const char **argv)
...@@ -1202,7 +1267,8 @@ int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -1202,7 +1267,8 @@ int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused)
if (data_init(argc, argv) < 0) if (data_init(argc, argv) < 0)
return -1; return -1;
ui_init(); if (ui_init() < 0)
return -1;
sort__mode = SORT_MODE__DIFF; sort__mode = SORT_MODE__DIFF;
......
...@@ -7,44 +7,47 @@ ...@@ -7,44 +7,47 @@
#include "util/session.h" #include "util/session.h"
#include "util/data.h" #include "util/data.h"
#define MEM_OPERATION_LOAD "load" #define MEM_OPERATION_LOAD 0x1
#define MEM_OPERATION_STORE "store" #define MEM_OPERATION_STORE 0x2
static const char *mem_operation = MEM_OPERATION_LOAD;
struct perf_mem { struct perf_mem {
struct perf_tool tool; struct perf_tool tool;
char const *input_name; char const *input_name;
bool hide_unresolved; bool hide_unresolved;
bool dump_raw; bool dump_raw;
int operation;
const char *cpu_list; const char *cpu_list;
DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
}; };
static int __cmd_record(int argc, const char **argv) static int __cmd_record(int argc, const char **argv, struct perf_mem *mem)
{ {
int rec_argc, i = 0, j; int rec_argc, i = 0, j;
const char **rec_argv; const char **rec_argv;
char event[64];
int ret; int ret;
rec_argc = argc + 4; rec_argc = argc + 7; /* max number of arguments */
rec_argv = calloc(rec_argc + 1, sizeof(char *)); rec_argv = calloc(rec_argc + 1, sizeof(char *));
if (!rec_argv) if (!rec_argv)
return -1; return -1;
rec_argv[i++] = strdup("record"); rec_argv[i++] = "record";
if (!strcmp(mem_operation, MEM_OPERATION_LOAD))
rec_argv[i++] = strdup("-W");
rec_argv[i++] = strdup("-d");
rec_argv[i++] = strdup("-e");
if (strcmp(mem_operation, MEM_OPERATION_LOAD)) if (mem->operation & MEM_OPERATION_LOAD)
sprintf(event, "cpu/mem-stores/pp"); rec_argv[i++] = "-W";
else
sprintf(event, "cpu/mem-loads/pp"); rec_argv[i++] = "-d";
if (mem->operation & MEM_OPERATION_LOAD) {
rec_argv[i++] = "-e";
rec_argv[i++] = "cpu/mem-loads/pp";
}
if (mem->operation & MEM_OPERATION_STORE) {
rec_argv[i++] = "-e";
rec_argv[i++] = "cpu/mem-stores/pp";
}
rec_argv[i++] = strdup(event);
for (j = 1; j < argc; j++, i++) for (j = 1; j < argc; j++, i++)
rec_argv[i] = argv[j]; rec_argv[i] = argv[j];
...@@ -162,17 +165,17 @@ static int report_events(int argc, const char **argv, struct perf_mem *mem) ...@@ -162,17 +165,17 @@ static int report_events(int argc, const char **argv, struct perf_mem *mem)
if (!rep_argv) if (!rep_argv)
return -1; return -1;
rep_argv[i++] = strdup("report"); rep_argv[i++] = "report";
rep_argv[i++] = strdup("--mem-mode"); rep_argv[i++] = "--mem-mode";
rep_argv[i++] = strdup("-n"); /* display number of samples */ rep_argv[i++] = "-n"; /* display number of samples */
/* /*
* there is no weight (cost) associated with stores, so don't print * there is no weight (cost) associated with stores, so don't print
* the column * the column
*/ */
if (strcmp(mem_operation, MEM_OPERATION_LOAD)) if (!(mem->operation & MEM_OPERATION_LOAD))
rep_argv[i++] = strdup("--sort=mem,sym,dso,symbol_daddr," rep_argv[i++] = "--sort=mem,sym,dso,symbol_daddr,"
"dso_daddr,tlb,locked"); "dso_daddr,tlb,locked";
for (j = 1; j < argc; j++, i++) for (j = 1; j < argc; j++, i++)
rep_argv[i] = argv[j]; rep_argv[i] = argv[j];
...@@ -182,6 +185,75 @@ static int report_events(int argc, const char **argv, struct perf_mem *mem) ...@@ -182,6 +185,75 @@ static int report_events(int argc, const char **argv, struct perf_mem *mem)
return ret; return ret;
} }
struct mem_mode {
const char *name;
int mode;
};
#define MEM_OPT(n, m) \
{ .name = n, .mode = (m) }
#define MEM_END { .name = NULL }
static const struct mem_mode mem_modes[]={
MEM_OPT("load", MEM_OPERATION_LOAD),
MEM_OPT("store", MEM_OPERATION_STORE),
MEM_END
};
static int
parse_mem_ops(const struct option *opt, const char *str, int unset)
{
int *mode = (int *)opt->value;
const struct mem_mode *m;
char *s, *os = NULL, *p;
int ret = -1;
if (unset)
return 0;
/* str may be NULL in case no arg is passed to -t */
if (str) {
/* because str is read-only */
s = os = strdup(str);
if (!s)
return -1;
/* reset mode */
*mode = 0;
for (;;) {
p = strchr(s, ',');
if (p)
*p = '\0';
for (m = mem_modes; m->name; m++) {
if (!strcasecmp(s, m->name))
break;
}
if (!m->name) {
fprintf(stderr, "unknown sampling op %s,"
" check man page\n", s);
goto error;
}
*mode |= m->mode;
if (!p)
break;
s = p + 1;
}
}
ret = 0;
if (*mode == 0)
*mode = MEM_OPERATION_LOAD;
error:
free(os);
return ret;
}
int cmd_mem(int argc, const char **argv, const char *prefix __maybe_unused) int cmd_mem(int argc, const char **argv, const char *prefix __maybe_unused)
{ {
struct stat st; struct stat st;
...@@ -197,10 +269,15 @@ int cmd_mem(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -197,10 +269,15 @@ int cmd_mem(int argc, const char **argv, const char *prefix __maybe_unused)
.ordered_events = true, .ordered_events = true,
}, },
.input_name = "perf.data", .input_name = "perf.data",
/*
* default to both load an store sampling
*/
.operation = MEM_OPERATION_LOAD | MEM_OPERATION_STORE,
}; };
const struct option mem_options[] = { const struct option mem_options[] = {
OPT_STRING('t', "type", &mem_operation, OPT_CALLBACK('t', "type", &mem.operation,
"type", "memory operations(load/store)"), "type", "memory operations(load,store) Default load,store",
parse_mem_ops),
OPT_BOOLEAN('D', "dump-raw-samples", &mem.dump_raw, OPT_BOOLEAN('D', "dump-raw-samples", &mem.dump_raw,
"dump raw samples in ASCII"), "dump raw samples in ASCII"),
OPT_BOOLEAN('U', "hide-unresolved", &mem.hide_unresolved, OPT_BOOLEAN('U', "hide-unresolved", &mem.hide_unresolved,
...@@ -225,7 +302,7 @@ int cmd_mem(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -225,7 +302,7 @@ int cmd_mem(int argc, const char **argv, const char *prefix __maybe_unused)
argc = parse_options_subcommand(argc, argv, mem_options, mem_subcommands, argc = parse_options_subcommand(argc, argv, mem_options, mem_subcommands,
mem_usage, PARSE_OPT_STOP_AT_NON_OPTION); mem_usage, PARSE_OPT_STOP_AT_NON_OPTION);
if (!argc || !(strncmp(argv[0], "rec", 3) || mem_operation)) if (!argc || !(strncmp(argv[0], "rec", 3) || mem.operation))
usage_with_options(mem_usage, mem_options); usage_with_options(mem_usage, mem_options);
if (!mem.input_name || !strlen(mem.input_name)) { if (!mem.input_name || !strlen(mem.input_name)) {
...@@ -236,7 +313,7 @@ int cmd_mem(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -236,7 +313,7 @@ int cmd_mem(int argc, const char **argv, const char *prefix __maybe_unused)
} }
if (!strncmp(argv[0], "rec", 3)) if (!strncmp(argv[0], "rec", 3))
return __cmd_record(argc, argv); return __cmd_record(argc, argv, &mem);
else if (!strncmp(argv[0], "rep", 3)) else if (!strncmp(argv[0], "rep", 3))
return report_events(argc, argv, &mem); return report_events(argc, argv, &mem);
else else
......
...@@ -86,17 +86,6 @@ static int report__config(const char *var, const char *value, void *cb) ...@@ -86,17 +86,6 @@ static int report__config(const char *var, const char *value, void *cb)
return perf_default_config(var, value, cb); return perf_default_config(var, value, cb);
} }
static void report__inc_stats(struct report *rep, struct hist_entry *he)
{
/*
* The @he is either of a newly created one or an existing one
* merging current sample. We only want to count a new one so
* checking ->nr_events being 1.
*/
if (he->stat.nr_events == 1)
rep->nr_entries++;
}
static int hist_iter__report_callback(struct hist_entry_iter *iter, static int hist_iter__report_callback(struct hist_entry_iter *iter,
struct addr_location *al, bool single, struct addr_location *al, bool single,
void *arg) void *arg)
...@@ -108,8 +97,6 @@ static int hist_iter__report_callback(struct hist_entry_iter *iter, ...@@ -108,8 +97,6 @@ static int hist_iter__report_callback(struct hist_entry_iter *iter,
struct mem_info *mi; struct mem_info *mi;
struct branch_info *bi; struct branch_info *bi;
report__inc_stats(rep, he);
if (!ui__has_annotation()) if (!ui__has_annotation())
return 0; return 0;
...@@ -499,6 +486,9 @@ static int __cmd_report(struct report *rep) ...@@ -499,6 +486,9 @@ static int __cmd_report(struct report *rep)
report__warn_kptr_restrict(rep); report__warn_kptr_restrict(rep);
evlist__for_each(session->evlist, pos)
rep->nr_entries += evsel__hists(pos)->nr_entries;
if (use_browser == 0) { if (use_browser == 0) {
if (verbose > 3) if (verbose > 3)
perf_session__fprintf(session, stdout); perf_session__fprintf(session, stdout);
......
...@@ -2056,23 +2056,24 @@ static int trace__run(struct trace *trace, int argc, const char **argv) ...@@ -2056,23 +2056,24 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
if (trace->trace_syscalls && if (trace->trace_syscalls &&
perf_evlist__add_syscall_newtp(evlist, trace__sys_enter, perf_evlist__add_syscall_newtp(evlist, trace__sys_enter,
trace__sys_exit)) trace__sys_exit))
goto out_error_tp; goto out_error_raw_syscalls;
if (trace->trace_syscalls) if (trace->trace_syscalls)
perf_evlist__add_vfs_getname(evlist); perf_evlist__add_vfs_getname(evlist);
if ((trace->trace_pgfaults & TRACE_PFMAJ) && if ((trace->trace_pgfaults & TRACE_PFMAJ) &&
perf_evlist__add_pgfault(evlist, PERF_COUNT_SW_PAGE_FAULTS_MAJ)) perf_evlist__add_pgfault(evlist, PERF_COUNT_SW_PAGE_FAULTS_MAJ)) {
goto out_error_tp; goto out_error_mem;
}
if ((trace->trace_pgfaults & TRACE_PFMIN) && if ((trace->trace_pgfaults & TRACE_PFMIN) &&
perf_evlist__add_pgfault(evlist, PERF_COUNT_SW_PAGE_FAULTS_MIN)) perf_evlist__add_pgfault(evlist, PERF_COUNT_SW_PAGE_FAULTS_MIN))
goto out_error_tp; goto out_error_mem;
if (trace->sched && if (trace->sched &&
perf_evlist__add_newtp(evlist, "sched", "sched_stat_runtime", perf_evlist__add_newtp(evlist, "sched", "sched_stat_runtime",
trace__sched_stat_runtime)) trace__sched_stat_runtime))
goto out_error_tp; goto out_error_sched_stat_runtime;
err = perf_evlist__create_maps(evlist, &trace->opts.target); err = perf_evlist__create_maps(evlist, &trace->opts.target);
if (err < 0) { if (err < 0) {
...@@ -2202,8 +2203,12 @@ static int trace__run(struct trace *trace, int argc, const char **argv) ...@@ -2202,8 +2203,12 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
{ {
char errbuf[BUFSIZ]; char errbuf[BUFSIZ];
out_error_tp: out_error_sched_stat_runtime:
perf_evlist__strerror_tp(evlist, errno, errbuf, sizeof(errbuf)); debugfs__strerror_open_tp(errno, errbuf, sizeof(errbuf), "sched", "sched_stat_runtime");
goto out_error;
out_error_raw_syscalls:
debugfs__strerror_open_tp(errno, errbuf, sizeof(errbuf), "raw_syscalls", "sys_(enter|exit)");
goto out_error; goto out_error;
out_error_mmap: out_error_mmap:
...@@ -2217,6 +2222,9 @@ static int trace__run(struct trace *trace, int argc, const char **argv) ...@@ -2217,6 +2222,9 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
fprintf(trace->output, "%s\n", errbuf); fprintf(trace->output, "%s\n", errbuf);
goto out_delete_evlist; goto out_delete_evlist;
} }
out_error_mem:
fprintf(trace->output, "Not enough memory to run!\n");
goto out_delete_evlist;
} }
static int trace__replay(struct trace *trace) static int trace__replay(struct trace *trace)
......
...@@ -105,7 +105,6 @@ class Event(dict): ...@@ -105,7 +105,6 @@ class Event(dict):
if not self.compare_data(self[t], other[t]): if not self.compare_data(self[t], other[t]):
log.warning("expected %s=%s, got %s" % (t, self[t], other[t])) log.warning("expected %s=%s, got %s" % (t, self[t], other[t]))
# Test file description needs to have following sections: # Test file description needs to have following sections:
# [config] # [config]
# - just single instance in file # - just single instance in file
......
...@@ -140,7 +140,7 @@ static void del_hist_entries(struct hists *hists) ...@@ -140,7 +140,7 @@ static void del_hist_entries(struct hists *hists)
he = rb_entry(node, struct hist_entry, rb_node); he = rb_entry(node, struct hist_entry, rb_node);
rb_erase(node, root_out); rb_erase(node, root_out);
rb_erase(&he->rb_node_in, root_in); rb_erase(&he->rb_node_in, root_in);
hist_entry__free(he); hist_entry__delete(he);
} }
} }
......
...@@ -106,7 +106,7 @@ static void del_hist_entries(struct hists *hists) ...@@ -106,7 +106,7 @@ static void del_hist_entries(struct hists *hists)
he = rb_entry(node, struct hist_entry, rb_node); he = rb_entry(node, struct hist_entry, rb_node);
rb_erase(node, root_out); rb_erase(node, root_out);
rb_erase(&he->rb_node_in, root_in); rb_erase(&he->rb_node_in, root_in);
hist_entry__free(he); hist_entry__delete(he);
} }
} }
......
...@@ -223,7 +223,6 @@ tarpkg: ...@@ -223,7 +223,6 @@ tarpkg:
echo "- $@: $$cmd" && echo $$cmd > $@ && \ echo "- $@: $$cmd" && echo $$cmd > $@ && \
( eval $$cmd ) >> $@ 2>&1 ( eval $$cmd ) >> $@ 2>&1
all: $(run) $(run_O) tarpkg all: $(run) $(run_O) tarpkg
@echo OK @echo OK
......
...@@ -110,7 +110,7 @@ static bool samples_same(const struct perf_sample *s1, ...@@ -110,7 +110,7 @@ static bool samples_same(const struct perf_sample *s1,
if (type & PERF_SAMPLE_STACK_USER) { if (type & PERF_SAMPLE_STACK_USER) {
COMP(user_stack.size); COMP(user_stack.size);
if (memcmp(s1->user_stack.data, s1->user_stack.data, if (memcmp(s1->user_stack.data, s2->user_stack.data,
s1->user_stack.size)) { s1->user_stack.size)) {
pr_debug("Samples differ at 'user_stack'\n"); pr_debug("Samples differ at 'user_stack'\n");
return false; return false;
......
...@@ -867,7 +867,6 @@ static void annotate_browser__mark_jump_targets(struct annotate_browser *browser ...@@ -867,7 +867,6 @@ static void annotate_browser__mark_jump_targets(struct annotate_browser *browser
++browser->nr_jumps; ++browser->nr_jumps;
} }
} }
static inline int width_jumps(int n) static inline int width_jumps(int n)
......
...@@ -285,7 +285,8 @@ static int hpp__entry_##_type(struct perf_hpp_fmt *fmt, \ ...@@ -285,7 +285,8 @@ static int hpp__entry_##_type(struct perf_hpp_fmt *fmt, \
} }
#define __HPP_SORT_FN(_type, _field) \ #define __HPP_SORT_FN(_type, _field) \
static int64_t hpp__sort_##_type(struct hist_entry *a, struct hist_entry *b) \ static int64_t hpp__sort_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \
struct hist_entry *a, struct hist_entry *b) \
{ \ { \
return __hpp__sort(a, b, he_get_##_field); \ return __hpp__sort(a, b, he_get_##_field); \
} }
...@@ -312,7 +313,8 @@ static int hpp__entry_##_type(struct perf_hpp_fmt *fmt, \ ...@@ -312,7 +313,8 @@ static int hpp__entry_##_type(struct perf_hpp_fmt *fmt, \
} }
#define __HPP_SORT_ACC_FN(_type, _field) \ #define __HPP_SORT_ACC_FN(_type, _field) \
static int64_t hpp__sort_##_type(struct hist_entry *a, struct hist_entry *b) \ static int64_t hpp__sort_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \
struct hist_entry *a, struct hist_entry *b) \
{ \ { \
return __hpp__sort_acc(a, b, he_get_acc_##_field); \ return __hpp__sort_acc(a, b, he_get_acc_##_field); \
} }
...@@ -331,7 +333,8 @@ static int hpp__entry_##_type(struct perf_hpp_fmt *fmt, \ ...@@ -331,7 +333,8 @@ static int hpp__entry_##_type(struct perf_hpp_fmt *fmt, \
} }
#define __HPP_SORT_RAW_FN(_type, _field) \ #define __HPP_SORT_RAW_FN(_type, _field) \
static int64_t hpp__sort_##_type(struct hist_entry *a, struct hist_entry *b) \ static int64_t hpp__sort_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \
struct hist_entry *a, struct hist_entry *b) \
{ \ { \
return __hpp__sort(a, b, he_get_raw_##_field); \ return __hpp__sort(a, b, he_get_raw_##_field); \
} }
...@@ -361,7 +364,8 @@ HPP_PERCENT_ACC_FNS(overhead_acc, period) ...@@ -361,7 +364,8 @@ HPP_PERCENT_ACC_FNS(overhead_acc, period)
HPP_RAW_FNS(samples, nr_events) HPP_RAW_FNS(samples, nr_events)
HPP_RAW_FNS(period, period) HPP_RAW_FNS(period, period)
static int64_t hpp__nop_cmp(struct hist_entry *a __maybe_unused, static int64_t hpp__nop_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
struct hist_entry *a __maybe_unused,
struct hist_entry *b __maybe_unused) struct hist_entry *b __maybe_unused)
{ {
return 0; return 0;
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "../libslang.h" #include "../libslang.h"
char ui_helpline__last_msg[1024]; char ui_helpline__last_msg[1024];
bool tui_helpline__set;
static void tui_helpline__pop(void) static void tui_helpline__pop(void)
{ {
...@@ -35,6 +36,8 @@ static int tui_helpline__show(const char *format, va_list ap) ...@@ -35,6 +36,8 @@ static int tui_helpline__show(const char *format, va_list ap)
sizeof(ui_helpline__last_msg) - backlog, format, ap); sizeof(ui_helpline__last_msg) - backlog, format, ap);
backlog += ret; backlog += ret;
tui_helpline__set = true;
if (ui_helpline__last_msg[backlog - 1] == '\n') { if (ui_helpline__last_msg[backlog - 1] == '\n') {
ui_helpline__puts(ui_helpline__last_msg); ui_helpline__puts(ui_helpline__last_msg);
SLsmg_refresh(); SLsmg_refresh();
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
static volatile int ui__need_resize; static volatile int ui__need_resize;
extern struct perf_error_ops perf_tui_eops; extern struct perf_error_ops perf_tui_eops;
extern bool tui_helpline__set;
extern void hist_browser__init_hpp(void); extern void hist_browser__init_hpp(void);
...@@ -159,7 +160,7 @@ int ui__init(void) ...@@ -159,7 +160,7 @@ int ui__init(void)
void ui__exit(bool wait_for_ok) void ui__exit(bool wait_for_ok)
{ {
if (wait_for_ok) if (wait_for_ok && tui_helpline__set)
ui__question_window("Fatal Error", ui__question_window("Fatal Error",
ui_helpline__last_msg, ui_helpline__last_msg,
"Press any key...", 0); "Press any key...", 0);
......
...@@ -5,132 +5,6 @@ ...@@ -5,132 +5,6 @@
int perf_use_color_default = -1; int perf_use_color_default = -1;
static int parse_color(const char *name, int len)
{
static const char * const color_names[] = {
"normal", "black", "red", "green", "yellow",
"blue", "magenta", "cyan", "white"
};
char *end;
int i;
for (i = 0; i < (int)ARRAY_SIZE(color_names); i++) {
const char *str = color_names[i];
if (!strncasecmp(name, str, len) && !str[len])
return i - 1;
}
i = strtol(name, &end, 10);
if (end - name == len && i >= -1 && i <= 255)
return i;
return -2;
}
static int parse_attr(const char *name, int len)
{
static const int attr_values[] = { 1, 2, 4, 5, 7 };
static const char * const attr_names[] = {
"bold", "dim", "ul", "blink", "reverse"
};
unsigned int i;
for (i = 0; i < ARRAY_SIZE(attr_names); i++) {
const char *str = attr_names[i];
if (!strncasecmp(name, str, len) && !str[len])
return attr_values[i];
}
return -1;
}
void color_parse(const char *value, const char *var, char *dst)
{
color_parse_mem(value, strlen(value), var, dst);
}
void color_parse_mem(const char *value, int value_len, const char *var,
char *dst)
{
const char *ptr = value;
int len = value_len;
int attr = -1;
int fg = -2;
int bg = -2;
if (!strncasecmp(value, "reset", len)) {
strcpy(dst, PERF_COLOR_RESET);
return;
}
/* [fg [bg]] [attr] */
while (len > 0) {
const char *word = ptr;
int val, wordlen = 0;
while (len > 0 && !isspace(word[wordlen])) {
wordlen++;
len--;
}
ptr = word + wordlen;
while (len > 0 && isspace(*ptr)) {
ptr++;
len--;
}
val = parse_color(word, wordlen);
if (val >= -1) {
if (fg == -2) {
fg = val;
continue;
}
if (bg == -2) {
bg = val;
continue;
}
goto bad;
}
val = parse_attr(word, wordlen);
if (val < 0 || attr != -1)
goto bad;
attr = val;
}
if (attr >= 0 || fg >= 0 || bg >= 0) {
int sep = 0;
*dst++ = '\033';
*dst++ = '[';
if (attr >= 0) {
*dst++ = '0' + attr;
sep++;
}
if (fg >= 0) {
if (sep++)
*dst++ = ';';
if (fg < 8) {
*dst++ = '3';
*dst++ = '0' + fg;
} else {
dst += sprintf(dst, "38;5;%d", fg);
}
}
if (bg >= 0) {
if (sep++)
*dst++ = ';';
if (bg < 8) {
*dst++ = '4';
*dst++ = '0' + bg;
} else {
dst += sprintf(dst, "48;5;%d", bg);
}
}
*dst++ = 'm';
}
*dst = 0;
return;
bad:
die("bad color value '%.*s' for variable '%s'", value_len, value, var);
}
int perf_config_colorbool(const char *var, const char *value, int stdout_is_tty) int perf_config_colorbool(const char *var, const char *value, int stdout_is_tty)
{ {
if (value) { if (value) {
......
...@@ -30,8 +30,6 @@ extern int perf_use_color_default; ...@@ -30,8 +30,6 @@ extern int perf_use_color_default;
int perf_color_default_config(const char *var, const char *value, void *cb); int perf_color_default_config(const char *var, const char *value, void *cb);
int perf_config_colorbool(const char *var, const char *value, int stdout_is_tty); int perf_config_colorbool(const char *var, const char *value, int stdout_is_tty);
void color_parse(const char *value, const char *var, char *dst);
void color_parse_mem(const char *value, int len, const char *var, char *dst);
int color_vsnprintf(char *bf, size_t size, const char *color, int color_vsnprintf(char *bf, size_t size, const char *color,
const char *fmt, va_list args); const char *fmt, va_list args);
int color_vfprintf(FILE *fp, const char *color, const char *fmt, va_list args); int color_vfprintf(FILE *fp, const char *color, const char *fmt, va_list args);
......
...@@ -1436,33 +1436,6 @@ size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp) ...@@ -1436,33 +1436,6 @@ size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp)
return printed + fprintf(fp, "\n"); return printed + fprintf(fp, "\n");
} }
int perf_evlist__strerror_tp(struct perf_evlist *evlist __maybe_unused,
int err, char *buf, size_t size)
{
char sbuf[128];
switch (err) {
case ENOENT:
scnprintf(buf, size, "%s",
"Error:\tUnable to find debugfs\n"
"Hint:\tWas your kernel compiled with debugfs support?\n"
"Hint:\tIs the debugfs filesystem mounted?\n"
"Hint:\tTry 'sudo mount -t debugfs nodev /sys/kernel/debug'");
break;
case EACCES:
scnprintf(buf, size,
"Error:\tNo permissions to read %s/tracing/events/raw_syscalls\n"
"Hint:\tTry 'sudo mount -o remount,mode=755 %s'\n",
debugfs_mountpoint, debugfs_mountpoint);
break;
default:
scnprintf(buf, size, "%s", strerror_r(err, sbuf, sizeof(sbuf)));
break;
}
return 0;
}
int perf_evlist__strerror_open(struct perf_evlist *evlist __maybe_unused, int perf_evlist__strerror_open(struct perf_evlist *evlist __maybe_unused,
int err, char *buf, size_t size) int err, char *buf, size_t size)
{ {
......
...@@ -183,7 +183,6 @@ static inline struct perf_evsel *perf_evlist__last(struct perf_evlist *evlist) ...@@ -183,7 +183,6 @@ static inline struct perf_evsel *perf_evlist__last(struct perf_evlist *evlist)
size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp); size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp);
int perf_evlist__strerror_tp(struct perf_evlist *evlist, int err, char *buf, size_t size);
int perf_evlist__strerror_open(struct perf_evlist *evlist, int err, char *buf, size_t size); int perf_evlist__strerror_open(struct perf_evlist *evlist, int err, char *buf, size_t size);
int perf_evlist__strerror_mmap(struct perf_evlist *evlist, int err, char *buf, size_t size); int perf_evlist__strerror_mmap(struct perf_evlist *evlist, int err, char *buf, size_t size);
......
...@@ -241,6 +241,20 @@ static bool hists__decay_entry(struct hists *hists, struct hist_entry *he) ...@@ -241,6 +241,20 @@ static bool hists__decay_entry(struct hists *hists, struct hist_entry *he)
return he->stat.period == 0; return he->stat.period == 0;
} }
static void hists__delete_entry(struct hists *hists, struct hist_entry *he)
{
rb_erase(&he->rb_node, &hists->entries);
if (sort__need_collapse)
rb_erase(&he->rb_node_in, &hists->entries_collapsed);
--hists->nr_entries;
if (!he->filtered)
--hists->nr_non_filtered_entries;
hist_entry__delete(he);
}
void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel) void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel)
{ {
struct rb_node *next = rb_first(&hists->entries); struct rb_node *next = rb_first(&hists->entries);
...@@ -258,16 +272,7 @@ void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel) ...@@ -258,16 +272,7 @@ void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel)
(zap_kernel && n->level != '.') || (zap_kernel && n->level != '.') ||
hists__decay_entry(hists, n)) && hists__decay_entry(hists, n)) &&
!n->used) { !n->used) {
rb_erase(&n->rb_node, &hists->entries); hists__delete_entry(hists, n);
if (sort__need_collapse)
rb_erase(&n->rb_node_in, &hists->entries_collapsed);
--hists->nr_entries;
if (!n->filtered)
--hists->nr_non_filtered_entries;
hist_entry__free(n);
} }
} }
} }
...@@ -281,16 +286,7 @@ void hists__delete_entries(struct hists *hists) ...@@ -281,16 +286,7 @@ void hists__delete_entries(struct hists *hists)
n = rb_entry(next, struct hist_entry, rb_node); n = rb_entry(next, struct hist_entry, rb_node);
next = rb_next(&n->rb_node); next = rb_next(&n->rb_node);
rb_erase(&n->rb_node, &hists->entries); hists__delete_entry(hists, n);
if (sort__need_collapse)
rb_erase(&n->rb_node_in, &hists->entries_collapsed);
--hists->nr_entries;
if (!n->filtered)
--hists->nr_non_filtered_entries;
hist_entry__free(n);
} }
} }
...@@ -433,6 +429,8 @@ static struct hist_entry *add_hist_entry(struct hists *hists, ...@@ -433,6 +429,8 @@ static struct hist_entry *add_hist_entry(struct hists *hists,
if (!he) if (!he)
return NULL; return NULL;
hists->nr_entries++;
rb_link_node(&he->rb_node_in, parent, p); rb_link_node(&he->rb_node_in, parent, p);
rb_insert_color(&he->rb_node_in, hists->entries_in); rb_insert_color(&he->rb_node_in, hists->entries_in);
out: out:
...@@ -915,7 +913,7 @@ hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) ...@@ -915,7 +913,7 @@ hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
if (perf_hpp__should_skip(fmt)) if (perf_hpp__should_skip(fmt))
continue; continue;
cmp = fmt->cmp(left, right); cmp = fmt->cmp(fmt, left, right);
if (cmp) if (cmp)
break; break;
} }
...@@ -933,7 +931,7 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) ...@@ -933,7 +931,7 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)
if (perf_hpp__should_skip(fmt)) if (perf_hpp__should_skip(fmt))
continue; continue;
cmp = fmt->collapse(left, right); cmp = fmt->collapse(fmt, left, right);
if (cmp) if (cmp)
break; break;
} }
...@@ -941,7 +939,7 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) ...@@ -941,7 +939,7 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)
return cmp; return cmp;
} }
void hist_entry__free(struct hist_entry *he) void hist_entry__delete(struct hist_entry *he)
{ {
zfree(&he->branch_info); zfree(&he->branch_info);
zfree(&he->mem_info); zfree(&he->mem_info);
...@@ -981,7 +979,7 @@ static bool hists__collapse_insert_entry(struct hists *hists __maybe_unused, ...@@ -981,7 +979,7 @@ static bool hists__collapse_insert_entry(struct hists *hists __maybe_unused,
iter->callchain, iter->callchain,
he->callchain); he->callchain);
} }
hist_entry__free(he); hist_entry__delete(he);
return false; return false;
} }
...@@ -1063,7 +1061,7 @@ static int hist_entry__sort(struct hist_entry *a, struct hist_entry *b) ...@@ -1063,7 +1061,7 @@ static int hist_entry__sort(struct hist_entry *a, struct hist_entry *b)
if (perf_hpp__should_skip(fmt)) if (perf_hpp__should_skip(fmt))
continue; continue;
cmp = fmt->sort(a, b); cmp = fmt->sort(fmt, a, b);
if (cmp) if (cmp)
break; break;
} }
......
...@@ -119,7 +119,7 @@ int64_t hist_entry__collapse(struct hist_entry *left, struct hist_entry *right); ...@@ -119,7 +119,7 @@ int64_t hist_entry__collapse(struct hist_entry *left, struct hist_entry *right);
int hist_entry__transaction_len(void); int hist_entry__transaction_len(void);
int hist_entry__sort_snprintf(struct hist_entry *he, char *bf, size_t size, int hist_entry__sort_snprintf(struct hist_entry *he, char *bf, size_t size,
struct hists *hists); struct hists *hists);
void hist_entry__free(struct hist_entry *); void hist_entry__delete(struct hist_entry *he);
void hists__output_resort(struct hists *hists, struct ui_progress *prog); void hists__output_resort(struct hists *hists, struct ui_progress *prog);
void hists__collapse_resort(struct hists *hists, struct ui_progress *prog); void hists__collapse_resort(struct hists *hists, struct ui_progress *prog);
...@@ -195,9 +195,12 @@ struct perf_hpp_fmt { ...@@ -195,9 +195,12 @@ struct perf_hpp_fmt {
struct hist_entry *he); struct hist_entry *he);
int (*entry)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, int (*entry)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
struct hist_entry *he); struct hist_entry *he);
int64_t (*cmp)(struct hist_entry *a, struct hist_entry *b); int64_t (*cmp)(struct perf_hpp_fmt *fmt,
int64_t (*collapse)(struct hist_entry *a, struct hist_entry *b); struct hist_entry *a, struct hist_entry *b);
int64_t (*sort)(struct hist_entry *a, struct hist_entry *b); int64_t (*collapse)(struct perf_hpp_fmt *fmt,
struct hist_entry *a, struct hist_entry *b);
int64_t (*sort)(struct perf_hpp_fmt *fmt,
struct hist_entry *a, struct hist_entry *b);
struct list_head list; struct list_head list;
struct list_head sort_list; struct list_head sort_list;
......
...@@ -71,6 +71,7 @@ struct parse_events_term { ...@@ -71,6 +71,7 @@ struct parse_events_term {
int type_val; int type_val;
int type_term; int type_term;
struct list_head list; struct list_head list;
bool used;
}; };
struct parse_events_evlist { struct parse_events_evlist {
......
...@@ -46,7 +46,7 @@ static int get_value(struct parse_opt_ctx_t *p, ...@@ -46,7 +46,7 @@ static int get_value(struct parse_opt_ctx_t *p,
return opterror(opt, "is not usable", flags); return opterror(opt, "is not usable", flags);
if (opt->flags & PARSE_OPT_EXCLUSIVE) { if (opt->flags & PARSE_OPT_EXCLUSIVE) {
if (p->excl_opt) { if (p->excl_opt && p->excl_opt != opt) {
char msg[128]; char msg[128];
if (((flags & OPT_SHORT) && p->excl_opt->short_name) || if (((flags & OPT_SHORT) && p->excl_opt->short_name) ||
......
...@@ -550,6 +550,35 @@ static void pmu_format_value(unsigned long *format, __u64 value, __u64 *v, ...@@ -550,6 +550,35 @@ static void pmu_format_value(unsigned long *format, __u64 value, __u64 *v,
} }
} }
/*
* Term is a string term, and might be a param-term. Try to look up it's value
* in the remaining terms.
* - We have a term like "base-or-format-term=param-term",
* - We need to find the value supplied for "param-term" (with param-term named
* in a config string) later on in the term list.
*/
static int pmu_resolve_param_term(struct parse_events_term *term,
struct list_head *head_terms,
__u64 *value)
{
struct parse_events_term *t;
list_for_each_entry(t, head_terms, list) {
if (t->type_val == PARSE_EVENTS__TERM_TYPE_NUM) {
if (!strcmp(t->config, term->config)) {
t->used = true;
*value = t->val.num;
return 0;
}
}
}
if (verbose)
printf("Required parameter '%s' not specified\n", term->config);
return -1;
}
/* /*
* Setup one of config[12] attr members based on the * Setup one of config[12] attr members based on the
* user input data - term parameter. * user input data - term parameter.
...@@ -557,25 +586,33 @@ static void pmu_format_value(unsigned long *format, __u64 value, __u64 *v, ...@@ -557,25 +586,33 @@ static void pmu_format_value(unsigned long *format, __u64 value, __u64 *v,
static int pmu_config_term(struct list_head *formats, static int pmu_config_term(struct list_head *formats,
struct perf_event_attr *attr, struct perf_event_attr *attr,
struct parse_events_term *term, struct parse_events_term *term,
struct list_head *head_terms,
bool zero) bool zero)
{ {
struct perf_pmu_format *format; struct perf_pmu_format *format;
__u64 *vp; __u64 *vp;
__u64 val;
/*
* If this is a parameter we've already used for parameterized-eval,
* skip it in normal eval.
*/
if (term->used)
return 0;
/* /*
* Support only for hardcoded and numnerial terms.
* Hardcoded terms should be already in, so nothing * Hardcoded terms should be already in, so nothing
* to be done for them. * to be done for them.
*/ */
if (parse_events__is_hardcoded_term(term)) if (parse_events__is_hardcoded_term(term))
return 0; return 0;
if (term->type_val != PARSE_EVENTS__TERM_TYPE_NUM)
return -EINVAL;
format = pmu_find_format(formats, term->config); format = pmu_find_format(formats, term->config);
if (!format) if (!format) {
if (verbose)
printf("Invalid event/parameter '%s'\n", term->config);
return -EINVAL; return -EINVAL;
}
switch (format->value) { switch (format->value) {
case PERF_PMU_FORMAT_VALUE_CONFIG: case PERF_PMU_FORMAT_VALUE_CONFIG:
...@@ -592,11 +629,25 @@ static int pmu_config_term(struct list_head *formats, ...@@ -592,11 +629,25 @@ static int pmu_config_term(struct list_head *formats,
} }
/* /*
* XXX If we ever decide to go with string values for * Either directly use a numeric term, or try to translate string terms
* non-hardcoded terms, here's the place to translate * using event parameters.
* them into value.
*/ */
pmu_format_value(format->bits, term->val.num, vp, zero); if (term->type_val == PARSE_EVENTS__TERM_TYPE_NUM)
val = term->val.num;
else if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR) {
if (strcmp(term->val.str, "?")) {
if (verbose)
pr_info("Invalid sysfs entry %s=%s\n",
term->config, term->val.str);
return -EINVAL;
}
if (pmu_resolve_param_term(term, head_terms, &val))
return -EINVAL;
} else
return -EINVAL;
pmu_format_value(format->bits, val, vp, zero);
return 0; return 0;
} }
...@@ -607,9 +658,10 @@ int perf_pmu__config_terms(struct list_head *formats, ...@@ -607,9 +658,10 @@ int perf_pmu__config_terms(struct list_head *formats,
{ {
struct parse_events_term *term; struct parse_events_term *term;
list_for_each_entry(term, head_terms, list) list_for_each_entry(term, head_terms, list) {
if (pmu_config_term(formats, attr, term, zero)) if (pmu_config_term(formats, attr, term, head_terms, zero))
return -EINVAL; return -EINVAL;
}
return 0; return 0;
} }
...@@ -767,10 +819,36 @@ void perf_pmu__set_format(unsigned long *bits, long from, long to) ...@@ -767,10 +819,36 @@ void perf_pmu__set_format(unsigned long *bits, long from, long to)
set_bit(b, bits); set_bit(b, bits);
} }
static int sub_non_neg(int a, int b)
{
if (b > a)
return 0;
return a - b;
}
static char *format_alias(char *buf, int len, struct perf_pmu *pmu, static char *format_alias(char *buf, int len, struct perf_pmu *pmu,
struct perf_pmu_alias *alias) struct perf_pmu_alias *alias)
{ {
snprintf(buf, len, "%s/%s/", pmu->name, alias->name); struct parse_events_term *term;
int used = snprintf(buf, len, "%s/%s", pmu->name, alias->name);
list_for_each_entry(term, &alias->terms, list) {
if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR)
used += snprintf(buf + used, sub_non_neg(len, used),
",%s=%s", term->config,
term->val.str);
}
if (sub_non_neg(len, used) > 0) {
buf[used] = '/';
used++;
}
if (sub_non_neg(len, used) > 0) {
buf[used] = '\0';
used++;
} else
buf[len - 1] = '\0';
return buf; return buf;
} }
......
...@@ -1304,6 +1304,37 @@ static int __sort__hpp_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, ...@@ -1304,6 +1304,37 @@ static int __sort__hpp_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
return hse->se->se_snprintf(he, hpp->buf, hpp->size, len); return hse->se->se_snprintf(he, hpp->buf, hpp->size, len);
} }
static int64_t __sort__hpp_cmp(struct perf_hpp_fmt *fmt,
struct hist_entry *a, struct hist_entry *b)
{
struct hpp_sort_entry *hse;
hse = container_of(fmt, struct hpp_sort_entry, hpp);
return hse->se->se_cmp(a, b);
}
static int64_t __sort__hpp_collapse(struct perf_hpp_fmt *fmt,
struct hist_entry *a, struct hist_entry *b)
{
struct hpp_sort_entry *hse;
int64_t (*collapse_fn)(struct hist_entry *, struct hist_entry *);
hse = container_of(fmt, struct hpp_sort_entry, hpp);
collapse_fn = hse->se->se_collapse ?: hse->se->se_cmp;
return collapse_fn(a, b);
}
static int64_t __sort__hpp_sort(struct perf_hpp_fmt *fmt,
struct hist_entry *a, struct hist_entry *b)
{
struct hpp_sort_entry *hse;
int64_t (*sort_fn)(struct hist_entry *, struct hist_entry *);
hse = container_of(fmt, struct hpp_sort_entry, hpp);
sort_fn = hse->se->se_sort ?: hse->se->se_cmp;
return sort_fn(a, b);
}
static struct hpp_sort_entry * static struct hpp_sort_entry *
__sort_dimension__alloc_hpp(struct sort_dimension *sd) __sort_dimension__alloc_hpp(struct sort_dimension *sd)
{ {
...@@ -1322,9 +1353,9 @@ __sort_dimension__alloc_hpp(struct sort_dimension *sd) ...@@ -1322,9 +1353,9 @@ __sort_dimension__alloc_hpp(struct sort_dimension *sd)
hse->hpp.entry = __sort__hpp_entry; hse->hpp.entry = __sort__hpp_entry;
hse->hpp.color = NULL; hse->hpp.color = NULL;
hse->hpp.cmp = sd->entry->se_cmp; hse->hpp.cmp = __sort__hpp_cmp;
hse->hpp.collapse = sd->entry->se_collapse ? : sd->entry->se_cmp; hse->hpp.collapse = __sort__hpp_collapse;
hse->hpp.sort = sd->entry->se_sort ? : hse->hpp.collapse; hse->hpp.sort = __sort__hpp_sort;
INIT_LIST_HEAD(&hse->hpp.list); INIT_LIST_HEAD(&hse->hpp.list);
INIT_LIST_HEAD(&hse->hpp.sort_list); INIT_LIST_HEAD(&hse->hpp.sort_list);
......
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