You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
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: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
@@ -52,12 +52,18 @@ Description: Per-pmu performance monitoring events specific to the running syste
|
||||
event=0x2abc
|
||||
event=0x423,inv,cmask=0x3
|
||||
domain=0x1,offset=0x8,starting_index=0xffff
|
||||
domain=0x1,offset=0x8,core=?
|
||||
|
||||
Each of the assignments indicates a value to be assigned to a
|
||||
particular set of bits (as defined by the format file
|
||||
corresponding to the <term>) in the perf_event structure passed
|
||||
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
|
||||
Date: 2014/02/24
|
||||
Contact: Linux kernel mailing list <linux-kernel@vger.kernel.org>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#define _GNU_SOURCE
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -98,3 +99,45 @@ char *debugfs_mount(const char *mountpoint)
|
||||
out:
|
||||
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);
|
||||
|
||||
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__ */
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <stdint.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <netinet/ip6.h>
|
||||
#include "event-parse.h"
|
||||
#include "event-utils.h"
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
unsigned int i;
|
||||
@@ -4337,6 +4656,15 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event
|
||||
ptr++;
|
||||
arg = arg->next;
|
||||
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 */
|
||||
|
||||
@@ -38,7 +38,7 @@ OPTIONS
|
||||
--remove=::
|
||||
Remove specified file from the cache.
|
||||
-M::
|
||||
--missing=::
|
||||
--missing=::
|
||||
List missing build ids in the cache for the specified file.
|
||||
-u::
|
||||
--update::
|
||||
|
||||
@@ -89,6 +89,19 @@ raw encoding of 0x1A8 can be used:
|
||||
You should refer to the processor specific documentation for getting these
|
||||
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
|
||||
-------
|
||||
|
||||
|
||||
@@ -12,11 +12,12 @@ SYNOPSIS
|
||||
|
||||
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.
|
||||
|
||||
"perf mem -t <TYPE> report" displays the result. It invokes perf report with the
|
||||
right set of options to display a memory access profile.
|
||||
"perf mem report" displays the result. It invokes perf report with the
|
||||
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,
|
||||
not the pure load (or store latency). Use latency includes any pipeline
|
||||
@@ -29,7 +30,7 @@ OPTIONS
|
||||
|
||||
-t::
|
||||
--type=::
|
||||
Select the memory operation type: load or store (default: load)
|
||||
Select the memory operation type: load or store (default: load,store)
|
||||
|
||||
-D::
|
||||
--dump-raw-samples=::
|
||||
|
||||
@@ -33,6 +33,18 @@ OPTIONS
|
||||
- a raw PMU event (eventsel+umask) in the form of rNNN where NNN is a
|
||||
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]'
|
||||
where addr is the address in memory you want to break in.
|
||||
Access is the memory access type (read, write, execute) it can
|
||||
|
||||
@@ -125,46 +125,46 @@ OPTIONS
|
||||
is equivalent to:
|
||||
|
||||
perf script -f trace:<fields> -f sw:<fields> -f hw:<fields>
|
||||
|
||||
|
||||
i.e., the specified fields apply to all event types if the type string
|
||||
is not given.
|
||||
|
||||
|
||||
The arguments are processed in the order received. A later usage can
|
||||
reset a prior request. e.g.:
|
||||
|
||||
|
||||
-f trace: -f comm,tid,time,ip,sym
|
||||
|
||||
|
||||
The first -f suppresses trace events (field list is ""), but then the
|
||||
second invocation sets the fields to comm,tid,time,ip,sym. In this case a
|
||||
warning is given to the user:
|
||||
|
||||
|
||||
"Overriding previous field request for all events."
|
||||
|
||||
|
||||
Alternatively, consider the order:
|
||||
|
||||
|
||||
-f comm,tid,time,ip,sym -f trace:
|
||||
|
||||
|
||||
The first -f sets the fields for all events and the second -f
|
||||
suppresses trace events. The user is given a warning message about
|
||||
the override, and the result of the above is that only S/W and H/W
|
||||
events are displayed with the given fields.
|
||||
|
||||
|
||||
For the 'wildcard' option if a user selected field is invalid for an
|
||||
event type, a message is displayed to the user that the option is
|
||||
ignored for that type. For example:
|
||||
|
||||
|
||||
$ perf script -f comm,tid,trace
|
||||
'trace' not valid for hardware events. Ignoring.
|
||||
'trace' not valid for software events. Ignoring.
|
||||
|
||||
|
||||
Alternatively, if the type is given an invalid field is specified it
|
||||
is an error. For example:
|
||||
|
||||
|
||||
perf script -v -f sw:comm,tid,trace
|
||||
'trace' not valid for software events.
|
||||
|
||||
|
||||
At this point usage is displayed, and perf-script exits.
|
||||
|
||||
|
||||
Finally, a user may not set fields to none for all event types.
|
||||
i.e., -f "" is not allowed.
|
||||
|
||||
|
||||
@@ -25,10 +25,22 @@ OPTIONS
|
||||
|
||||
-e::
|
||||
--event=::
|
||||
Select the PMU event. Selection can be a symbolic event name
|
||||
(use 'perf list' to list all events) or a raw PMU
|
||||
event (eventsel+umask) in the form of rNNN where NNN is a
|
||||
hexadecimal event descriptor.
|
||||
Select the PMU event. Selection can be:
|
||||
|
||||
- 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.
|
||||
|
||||
- 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::
|
||||
--no-inherit::
|
||||
|
||||
@@ -236,10 +236,10 @@ static bool dso__missing_buildid_cache(struct dso *dso, int parm __maybe_unused)
|
||||
if (errno == ENOENT)
|
||||
return false;
|
||||
|
||||
pr_warning("Problems with %s file, consider removing it from the cache\n",
|
||||
pr_warning("Problems with %s file, consider removing it from the cache\n",
|
||||
filename);
|
||||
} else if (memcmp(dso->build_id, build_id, sizeof(dso->build_id))) {
|
||||
pr_warning("Problems with %s file, consider removing it from the cache\n",
|
||||
pr_warning("Problems with %s file, consider removing it from the cache\n",
|
||||
filename);
|
||||
}
|
||||
|
||||
|
||||
+157
-91
@@ -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*
|
||||
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*
|
||||
get_pair_fmt(struct hist_entry *he, struct diff_hpp_fmt *dfmt)
|
||||
{
|
||||
void *ptr = dfmt - dfmt->idx;
|
||||
struct data__file *d = container_of(ptr, struct data__file, fmt);
|
||||
struct data__file *d = fmt_to_data_file(&dfmt->fmt);
|
||||
|
||||
return get_pair_data(he, d);
|
||||
}
|
||||
@@ -430,7 +438,7 @@ static void hists__baseline_only(struct hists *hists)
|
||||
next = rb_next(&he->rb_node_in);
|
||||
if (!hist_entry__next_pair(he)) {
|
||||
rb_erase(&he->rb_node_in, root);
|
||||
hist_entry__free(he);
|
||||
hist_entry__delete(he);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -448,26 +456,30 @@ static void hists__precompute(struct hists *hists)
|
||||
next = rb_first(root);
|
||||
while (next != NULL) {
|
||||
struct hist_entry *he, *pair;
|
||||
struct data__file *d;
|
||||
int i;
|
||||
|
||||
he = rb_entry(next, struct hist_entry, rb_node_in);
|
||||
next = rb_next(&he->rb_node_in);
|
||||
|
||||
pair = get_pair_data(he, &data__files[sort_compute]);
|
||||
if (!pair)
|
||||
continue;
|
||||
data__for_each_file_new(i, d) {
|
||||
pair = get_pair_data(he, d);
|
||||
if (!pair)
|
||||
continue;
|
||||
|
||||
switch (compute) {
|
||||
case COMPUTE_DELTA:
|
||||
compute_delta(he, pair);
|
||||
break;
|
||||
case COMPUTE_RATIO:
|
||||
compute_ratio(he, pair);
|
||||
break;
|
||||
case COMPUTE_WEIGHTED_DIFF:
|
||||
compute_wdiff(he, pair);
|
||||
break;
|
||||
default:
|
||||
BUG_ON(1);
|
||||
switch (compute) {
|
||||
case COMPUTE_DELTA:
|
||||
compute_delta(he, pair);
|
||||
break;
|
||||
case COMPUTE_RATIO:
|
||||
compute_ratio(he, pair);
|
||||
break;
|
||||
case COMPUTE_WEIGHTED_DIFF:
|
||||
compute_wdiff(he, pair);
|
||||
break;
|
||||
default:
|
||||
BUG_ON(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -517,7 +529,7 @@ __hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right,
|
||||
|
||||
static int64_t
|
||||
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_right = hist_entry__has_pairs(right);
|
||||
@@ -529,8 +541,8 @@ hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right,
|
||||
if (!pairs_left || !pairs_right)
|
||||
return pairs_left ? -1 : 1;
|
||||
|
||||
p_left = get_pair_data(left, &data__files[sort_compute]);
|
||||
p_right = get_pair_data(right, &data__files[sort_compute]);
|
||||
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;
|
||||
@@ -546,90 +558,102 @@ hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right,
|
||||
}
|
||||
|
||||
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,
|
||||
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;
|
||||
|
||||
if (left->dummy || right->dummy)
|
||||
return left->dummy ? 1 : -1;
|
||||
}
|
||||
|
||||
return __hist_entry__cmp_compute(p_left, p_right, c);
|
||||
}
|
||||
|
||||
static int64_t
|
||||
hist_entry__cmp_nop(struct perf_hpp_fmt *fmt __maybe_unused,
|
||||
struct hist_entry *left __maybe_unused,
|
||||
struct hist_entry *right __maybe_unused)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int64_t
|
||||
hist_entry__cmp_baseline(struct hist_entry *left, struct hist_entry *right)
|
||||
hist_entry__cmp_baseline(struct perf_hpp_fmt *fmt __maybe_unused,
|
||||
struct hist_entry *left, struct hist_entry *right)
|
||||
{
|
||||
if (sort_compute)
|
||||
return 0;
|
||||
|
||||
if (left->stat.period == right->stat.period)
|
||||
return 0;
|
||||
return left->stat.period > right->stat.period ? 1 : -1;
|
||||
}
|
||||
|
||||
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
|
||||
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
|
||||
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,
|
||||
struct hist_entry *he,
|
||||
int c)
|
||||
static int64_t
|
||||
hist_entry__cmp_delta_idx(struct perf_hpp_fmt *fmt __maybe_unused,
|
||||
struct hist_entry *left, struct hist_entry *right)
|
||||
{
|
||||
struct rb_node **p = &root->rb_node;
|
||||
struct rb_node *parent = NULL;
|
||||
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);
|
||||
return hist_entry__cmp_compute_idx(right, left, COMPUTE_DELTA,
|
||||
sort_compute);
|
||||
}
|
||||
|
||||
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;
|
||||
struct rb_node *next;
|
||||
return hist_entry__cmp_compute_idx(right, left, COMPUTE_RATIO,
|
||||
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)
|
||||
hists__calc_col_len(hists, he);
|
||||
}
|
||||
static int64_t
|
||||
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)
|
||||
@@ -637,12 +661,8 @@ static void hists__process(struct hists *hists)
|
||||
if (show_baseline_only)
|
||||
hists__baseline_only(hists);
|
||||
|
||||
if (sort_compute) {
|
||||
hists__precompute(hists);
|
||||
hists__compute_resort(hists);
|
||||
} else {
|
||||
hists__output_resort(hists, NULL);
|
||||
}
|
||||
hists__precompute(hists);
|
||||
hists__output_resort(hists, NULL);
|
||||
|
||||
hists__fprintf(hists, true, 0, 0, 0, stdout);
|
||||
}
|
||||
@@ -841,7 +861,7 @@ static int __hpp__color_compare(struct perf_hpp_fmt *fmt,
|
||||
char pfmt[20] = " ";
|
||||
|
||||
if (!pair)
|
||||
goto dummy_print;
|
||||
goto no_print;
|
||||
|
||||
switch (comparison_method) {
|
||||
case COMPUTE_DELTA:
|
||||
@@ -850,8 +870,6 @@ static int __hpp__color_compare(struct perf_hpp_fmt *fmt,
|
||||
else
|
||||
diff = compute_delta(he, pair);
|
||||
|
||||
if (fabs(diff) < 0.01)
|
||||
goto dummy_print;
|
||||
scnprintf(pfmt, 20, "%%%+d.2f%%%%", dfmt->header_width - 1);
|
||||
return percent_color_snprintf(hpp->buf, hpp->size,
|
||||
pfmt, diff);
|
||||
@@ -882,6 +900,9 @@ static int __hpp__color_compare(struct perf_hpp_fmt *fmt,
|
||||
BUG_ON(1);
|
||||
}
|
||||
dummy_print:
|
||||
return scnprintf(hpp->buf, hpp->size, "%*s",
|
||||
dfmt->header_width, "N/A");
|
||||
no_print:
|
||||
return scnprintf(hpp->buf, hpp->size, "%*s",
|
||||
dfmt->header_width, pfmt);
|
||||
}
|
||||
@@ -932,14 +953,15 @@ hpp__entry_pair(struct hist_entry *he, struct hist_entry *pair,
|
||||
else
|
||||
diff = compute_delta(he, pair);
|
||||
|
||||
if (fabs(diff) >= 0.01)
|
||||
scnprintf(buf, size, "%+4.2F%%", diff);
|
||||
scnprintf(buf, size, "%+4.2F%%", diff);
|
||||
break;
|
||||
|
||||
case PERF_HPP_DIFF__RATIO:
|
||||
/* No point for ratio number if we are dummy.. */
|
||||
if (he->dummy)
|
||||
if (he->dummy) {
|
||||
scnprintf(buf, size, "N/A");
|
||||
break;
|
||||
}
|
||||
|
||||
if (pair->diff.computed)
|
||||
ratio = pair->diff.period_ratio;
|
||||
@@ -952,8 +974,10 @@ hpp__entry_pair(struct hist_entry *he, struct hist_entry *pair,
|
||||
|
||||
case PERF_HPP_DIFF__WEIGHTED_DIFF:
|
||||
/* No point for wdiff number if we are dummy.. */
|
||||
if (he->dummy)
|
||||
if (he->dummy) {
|
||||
scnprintf(buf, size, "N/A");
|
||||
break;
|
||||
}
|
||||
|
||||
if (pair->diff.computed)
|
||||
wdiff = pair->diff.wdiff;
|
||||
@@ -1105,9 +1129,10 @@ static void data__hpp_register(struct data__file *d, int idx)
|
||||
perf_hpp__register_sort_field(fmt);
|
||||
}
|
||||
|
||||
static void ui_init(void)
|
||||
static int ui_init(void)
|
||||
{
|
||||
struct data__file *d;
|
||||
struct perf_hpp_fmt *fmt;
|
||||
int i;
|
||||
|
||||
data__for_each_file(i, d) {
|
||||
@@ -1137,6 +1162,46 @@ static void ui_init(void)
|
||||
data__hpp_register(d, i ? PERF_HPP_DIFF__PERIOD :
|
||||
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)
|
||||
@@ -1202,7 +1267,8 @@ int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
if (data_init(argc, argv) < 0)
|
||||
return -1;
|
||||
|
||||
ui_init();
|
||||
if (ui_init() < 0)
|
||||
return -1;
|
||||
|
||||
sort__mode = SORT_MODE__DIFF;
|
||||
|
||||
|
||||
+104
-27
@@ -7,44 +7,47 @@
|
||||
#include "util/session.h"
|
||||
#include "util/data.h"
|
||||
|
||||
#define MEM_OPERATION_LOAD "load"
|
||||
#define MEM_OPERATION_STORE "store"
|
||||
|
||||
static const char *mem_operation = MEM_OPERATION_LOAD;
|
||||
#define MEM_OPERATION_LOAD 0x1
|
||||
#define MEM_OPERATION_STORE 0x2
|
||||
|
||||
struct perf_mem {
|
||||
struct perf_tool tool;
|
||||
char const *input_name;
|
||||
bool hide_unresolved;
|
||||
bool dump_raw;
|
||||
int operation;
|
||||
const char *cpu_list;
|
||||
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;
|
||||
const char **rec_argv;
|
||||
char event[64];
|
||||
int ret;
|
||||
|
||||
rec_argc = argc + 4;
|
||||
rec_argc = argc + 7; /* max number of arguments */
|
||||
rec_argv = calloc(rec_argc + 1, sizeof(char *));
|
||||
if (!rec_argv)
|
||||
return -1;
|
||||
|
||||
rec_argv[i++] = strdup("record");
|
||||
if (!strcmp(mem_operation, MEM_OPERATION_LOAD))
|
||||
rec_argv[i++] = strdup("-W");
|
||||
rec_argv[i++] = strdup("-d");
|
||||
rec_argv[i++] = strdup("-e");
|
||||
rec_argv[i++] = "record";
|
||||
|
||||
if (strcmp(mem_operation, MEM_OPERATION_LOAD))
|
||||
sprintf(event, "cpu/mem-stores/pp");
|
||||
else
|
||||
sprintf(event, "cpu/mem-loads/pp");
|
||||
if (mem->operation & MEM_OPERATION_LOAD)
|
||||
rec_argv[i++] = "-W";
|
||||
|
||||
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++)
|
||||
rec_argv[i] = argv[j];
|
||||
|
||||
@@ -162,17 +165,17 @@ static int report_events(int argc, const char **argv, struct perf_mem *mem)
|
||||
if (!rep_argv)
|
||||
return -1;
|
||||
|
||||
rep_argv[i++] = strdup("report");
|
||||
rep_argv[i++] = strdup("--mem-mode");
|
||||
rep_argv[i++] = strdup("-n"); /* display number of samples */
|
||||
rep_argv[i++] = "report";
|
||||
rep_argv[i++] = "--mem-mode";
|
||||
rep_argv[i++] = "-n"; /* display number of samples */
|
||||
|
||||
/*
|
||||
* there is no weight (cost) associated with stores, so don't print
|
||||
* the column
|
||||
*/
|
||||
if (strcmp(mem_operation, MEM_OPERATION_LOAD))
|
||||
rep_argv[i++] = strdup("--sort=mem,sym,dso,symbol_daddr,"
|
||||
"dso_daddr,tlb,locked");
|
||||
if (!(mem->operation & MEM_OPERATION_LOAD))
|
||||
rep_argv[i++] = "--sort=mem,sym,dso,symbol_daddr,"
|
||||
"dso_daddr,tlb,locked";
|
||||
|
||||
for (j = 1; j < argc; j++, i++)
|
||||
rep_argv[i] = argv[j];
|
||||
@@ -182,6 +185,75 @@ static int report_events(int argc, const char **argv, struct perf_mem *mem)
|
||||
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)
|
||||
{
|
||||
struct stat st;
|
||||
@@ -197,10 +269,15 @@ int cmd_mem(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
.ordered_events = true,
|
||||
},
|
||||
.input_name = "perf.data",
|
||||
/*
|
||||
* default to both load an store sampling
|
||||
*/
|
||||
.operation = MEM_OPERATION_LOAD | MEM_OPERATION_STORE,
|
||||
};
|
||||
const struct option mem_options[] = {
|
||||
OPT_STRING('t', "type", &mem_operation,
|
||||
"type", "memory operations(load/store)"),
|
||||
OPT_CALLBACK('t', "type", &mem.operation,
|
||||
"type", "memory operations(load,store) Default load,store",
|
||||
parse_mem_ops),
|
||||
OPT_BOOLEAN('D', "dump-raw-samples", &mem.dump_raw,
|
||||
"dump raw samples in ASCII"),
|
||||
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)
|
||||
argc = parse_options_subcommand(argc, argv, mem_options, mem_subcommands,
|
||||
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);
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
if (!strncmp(argv[0], "rec", 3))
|
||||
return __cmd_record(argc, argv);
|
||||
return __cmd_record(argc, argv, &mem);
|
||||
else if (!strncmp(argv[0], "rep", 3))
|
||||
return report_events(argc, argv, &mem);
|
||||
else
|
||||
|
||||
@@ -86,17 +86,6 @@ static int report__config(const char *var, const char *value, void *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,
|
||||
struct addr_location *al, bool single,
|
||||
void *arg)
|
||||
@@ -108,8 +97,6 @@ static int hist_iter__report_callback(struct hist_entry_iter *iter,
|
||||
struct mem_info *mi;
|
||||
struct branch_info *bi;
|
||||
|
||||
report__inc_stats(rep, he);
|
||||
|
||||
if (!ui__has_annotation())
|
||||
return 0;
|
||||
|
||||
@@ -499,6 +486,9 @@ static int __cmd_report(struct report *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 (verbose > 3)
|
||||
perf_session__fprintf(session, stdout);
|
||||
|
||||
@@ -1730,7 +1730,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
"detailed run - start a lot of events"),
|
||||
OPT_BOOLEAN('S', "sync", &sync_run,
|
||||
"call sync() before starting a run"),
|
||||
OPT_CALLBACK_NOOPT('B', "big-num", NULL, NULL,
|
||||
OPT_CALLBACK_NOOPT('B', "big-num", NULL, NULL,
|
||||
"print large numbers with thousands\' separators",
|
||||
stat__set_big_num),
|
||||
OPT_STRING('C', "cpu", &target.cpu_list, "cpu",
|
||||
|
||||
@@ -165,7 +165,7 @@ static void ui__warn_map_erange(struct map *map, struct symbol *sym, u64 ip)
|
||||
err ? "[unknown]" : uts.release, perf_version_string);
|
||||
if (use_browser <= 0)
|
||||
sleep(5);
|
||||
|
||||
|
||||
map->erange_warned = true;
|
||||
}
|
||||
|
||||
|
||||
+57
-49
@@ -929,66 +929,66 @@ static struct syscall_fmt {
|
||||
.arg_scnprintf = { [0] = SCA_HEX, /* brk */ }, },
|
||||
{ .name = "clock_gettime", .errmsg = true, STRARRAY(0, clk_id, clockid), },
|
||||
{ .name = "close", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_CLOSE_FD, /* fd */ }, },
|
||||
.arg_scnprintf = { [0] = SCA_CLOSE_FD, /* fd */ }, },
|
||||
{ .name = "connect", .errmsg = true, },
|
||||
{ .name = "dup", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
{ .name = "dup2", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
{ .name = "dup3", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
{ .name = "epoll_ctl", .errmsg = true, STRARRAY(1, op, epoll_ctl_ops), },
|
||||
{ .name = "eventfd2", .errmsg = true,
|
||||
.arg_scnprintf = { [1] = SCA_EFD_FLAGS, /* flags */ }, },
|
||||
{ .name = "faccessat", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_FDAT, /* dfd */ }, },
|
||||
{ .name = "fadvise64", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
{ .name = "fallocate", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
{ .name = "fchdir", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
{ .name = "fchmod", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
{ .name = "fchmodat", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_FDAT, /* fd */ }, },
|
||||
.arg_scnprintf = { [0] = SCA_FDAT, /* fd */ }, },
|
||||
{ .name = "fchown", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
{ .name = "fchownat", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_FDAT, /* fd */ }, },
|
||||
.arg_scnprintf = { [0] = SCA_FDAT, /* fd */ }, },
|
||||
{ .name = "fcntl", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */
|
||||
[1] = SCA_STRARRAY, /* cmd */ },
|
||||
.arg_parm = { [1] = &strarray__fcntl_cmds, /* cmd */ }, },
|
||||
{ .name = "fdatasync", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
{ .name = "flock", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */
|
||||
[1] = SCA_FLOCK, /* cmd */ }, },
|
||||
{ .name = "fsetxattr", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
{ .name = "fstat", .errmsg = true, .alias = "newfstat",
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
{ .name = "fstatat", .errmsg = true, .alias = "newfstatat",
|
||||
.arg_scnprintf = { [0] = SCA_FDAT, /* dfd */ }, },
|
||||
.arg_scnprintf = { [0] = SCA_FDAT, /* dfd */ }, },
|
||||
{ .name = "fstatfs", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
{ .name = "fsync", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
{ .name = "ftruncate", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
{ .name = "futex", .errmsg = true,
|
||||
.arg_scnprintf = { [1] = SCA_FUTEX_OP, /* op */ }, },
|
||||
{ .name = "futimesat", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_FDAT, /* fd */ }, },
|
||||
.arg_scnprintf = { [0] = SCA_FDAT, /* fd */ }, },
|
||||
{ .name = "getdents", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
{ .name = "getdents64", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
{ .name = "getitimer", .errmsg = true, STRARRAY(0, which, itimers), },
|
||||
{ .name = "getrlimit", .errmsg = true, STRARRAY(0, resource, rlimit_resources), },
|
||||
{ .name = "ioctl", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
/*
|
||||
* FIXME: Make this available to all arches.
|
||||
@@ -1002,7 +1002,7 @@ static struct syscall_fmt {
|
||||
{ .name = "kill", .errmsg = true,
|
||||
.arg_scnprintf = { [1] = SCA_SIGNUM, /* sig */ }, },
|
||||
{ .name = "linkat", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_FDAT, /* fd */ }, },
|
||||
.arg_scnprintf = { [0] = SCA_FDAT, /* fd */ }, },
|
||||
{ .name = "lseek", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */
|
||||
[2] = SCA_STRARRAY, /* whence */ },
|
||||
@@ -1012,9 +1012,9 @@ static struct syscall_fmt {
|
||||
.arg_scnprintf = { [0] = SCA_HEX, /* start */
|
||||
[2] = SCA_MADV_BHV, /* behavior */ }, },
|
||||
{ .name = "mkdirat", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_FDAT, /* fd */ }, },
|
||||
.arg_scnprintf = { [0] = SCA_FDAT, /* fd */ }, },
|
||||
{ .name = "mknodat", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_FDAT, /* fd */ }, },
|
||||
.arg_scnprintf = { [0] = SCA_FDAT, /* fd */ }, },
|
||||
{ .name = "mlock", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_HEX, /* addr */ }, },
|
||||
{ .name = "mlockall", .errmsg = true,
|
||||
@@ -1036,9 +1036,9 @@ static struct syscall_fmt {
|
||||
{ .name = "munmap", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_HEX, /* addr */ }, },
|
||||
{ .name = "name_to_handle_at", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_FDAT, /* dfd */ }, },
|
||||
.arg_scnprintf = { [0] = SCA_FDAT, /* dfd */ }, },
|
||||
{ .name = "newfstatat", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_FDAT, /* dfd */ }, },
|
||||
.arg_scnprintf = { [0] = SCA_FDAT, /* dfd */ }, },
|
||||
{ .name = "open", .errmsg = true,
|
||||
.arg_scnprintf = { [1] = SCA_OPEN_FLAGS, /* flags */ }, },
|
||||
{ .name = "open_by_handle_at", .errmsg = true,
|
||||
@@ -1052,20 +1052,20 @@ static struct syscall_fmt {
|
||||
{ .name = "poll", .errmsg = true, .timeout = true, },
|
||||
{ .name = "ppoll", .errmsg = true, .timeout = true, },
|
||||
{ .name = "pread", .errmsg = true, .alias = "pread64",
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
{ .name = "preadv", .errmsg = true, .alias = "pread",
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
{ .name = "prlimit64", .errmsg = true, STRARRAY(1, resource, rlimit_resources), },
|
||||
{ .name = "pwrite", .errmsg = true, .alias = "pwrite64",
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
{ .name = "pwritev", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
{ .name = "read", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
{ .name = "readlinkat", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_FDAT, /* dfd */ }, },
|
||||
.arg_scnprintf = { [0] = SCA_FDAT, /* dfd */ }, },
|
||||
{ .name = "readv", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
{ .name = "recvfrom", .errmsg = true,
|
||||
.arg_scnprintf = { [3] = SCA_MSG_FLAGS, /* flags */ }, },
|
||||
{ .name = "recvmmsg", .errmsg = true,
|
||||
@@ -1073,7 +1073,7 @@ static struct syscall_fmt {
|
||||
{ .name = "recvmsg", .errmsg = true,
|
||||
.arg_scnprintf = { [2] = SCA_MSG_FLAGS, /* flags */ }, },
|
||||
{ .name = "renameat", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_FDAT, /* dfd */ }, },
|
||||
.arg_scnprintf = { [0] = SCA_FDAT, /* dfd */ }, },
|
||||
{ .name = "rt_sigaction", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_SIGNUM, /* sig */ }, },
|
||||
{ .name = "rt_sigprocmask", .errmsg = true, STRARRAY(0, how, sighow), },
|
||||
@@ -1091,7 +1091,7 @@ static struct syscall_fmt {
|
||||
{ .name = "setitimer", .errmsg = true, STRARRAY(0, which, itimers), },
|
||||
{ .name = "setrlimit", .errmsg = true, STRARRAY(0, resource, rlimit_resources), },
|
||||
{ .name = "shutdown", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
{ .name = "socket", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_STRARRAY, /* family */
|
||||
[1] = SCA_SK_TYPE, /* type */ },
|
||||
@@ -1102,7 +1102,7 @@ static struct syscall_fmt {
|
||||
.arg_parm = { [0] = &strarray__socket_families, /* family */ }, },
|
||||
{ .name = "stat", .errmsg = true, .alias = "newstat", },
|
||||
{ .name = "symlinkat", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_FDAT, /* dfd */ }, },
|
||||
.arg_scnprintf = { [0] = SCA_FDAT, /* dfd */ }, },
|
||||
{ .name = "tgkill", .errmsg = true,
|
||||
.arg_scnprintf = { [2] = SCA_SIGNUM, /* sig */ }, },
|
||||
{ .name = "tkill", .errmsg = true,
|
||||
@@ -1113,9 +1113,9 @@ static struct syscall_fmt {
|
||||
{ .name = "utimensat", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_FDAT, /* dirfd */ }, },
|
||||
{ .name = "write", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
{ .name = "writev", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
};
|
||||
|
||||
static int syscall_fmt__cmp(const void *name, const void *fmtp)
|
||||
@@ -1191,7 +1191,7 @@ static struct thread_trace *thread__trace(struct thread *thread, FILE *fp)
|
||||
|
||||
if (thread__priv(thread) == NULL)
|
||||
thread__set_priv(thread, thread_trace__new());
|
||||
|
||||
|
||||
if (thread__priv(thread) == NULL)
|
||||
goto fail;
|
||||
|
||||
@@ -2056,23 +2056,24 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
|
||||
if (trace->trace_syscalls &&
|
||||
perf_evlist__add_syscall_newtp(evlist, trace__sys_enter,
|
||||
trace__sys_exit))
|
||||
goto out_error_tp;
|
||||
goto out_error_raw_syscalls;
|
||||
|
||||
if (trace->trace_syscalls)
|
||||
perf_evlist__add_vfs_getname(evlist);
|
||||
|
||||
if ((trace->trace_pgfaults & TRACE_PFMAJ) &&
|
||||
perf_evlist__add_pgfault(evlist, PERF_COUNT_SW_PAGE_FAULTS_MAJ))
|
||||
goto out_error_tp;
|
||||
perf_evlist__add_pgfault(evlist, PERF_COUNT_SW_PAGE_FAULTS_MAJ)) {
|
||||
goto out_error_mem;
|
||||
}
|
||||
|
||||
if ((trace->trace_pgfaults & TRACE_PFMIN) &&
|
||||
perf_evlist__add_pgfault(evlist, PERF_COUNT_SW_PAGE_FAULTS_MIN))
|
||||
goto out_error_tp;
|
||||
goto out_error_mem;
|
||||
|
||||
if (trace->sched &&
|
||||
perf_evlist__add_newtp(evlist, "sched", "sched_stat_runtime",
|
||||
trace__sched_stat_runtime))
|
||||
goto out_error_tp;
|
||||
perf_evlist__add_newtp(evlist, "sched", "sched_stat_runtime",
|
||||
trace__sched_stat_runtime))
|
||||
goto out_error_sched_stat_runtime;
|
||||
|
||||
err = perf_evlist__create_maps(evlist, &trace->opts.target);
|
||||
if (err < 0) {
|
||||
@@ -2202,8 +2203,12 @@ out:
|
||||
{
|
||||
char errbuf[BUFSIZ];
|
||||
|
||||
out_error_tp:
|
||||
perf_evlist__strerror_tp(evlist, errno, errbuf, sizeof(errbuf));
|
||||
out_error_sched_stat_runtime:
|
||||
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;
|
||||
|
||||
out_error_mmap:
|
||||
@@ -2217,6 +2222,9 @@ out_error:
|
||||
fprintf(trace->output, "%s\n", errbuf);
|
||||
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)
|
||||
|
||||
@@ -104,7 +104,6 @@ class Event(dict):
|
||||
continue
|
||||
if not self.compare_data(self[t], other[t]):
|
||||
log.warning("expected %s=%s, got %s" % (t, self[t], other[t]))
|
||||
|
||||
|
||||
# Test file description needs to have following sections:
|
||||
# [config]
|
||||
|
||||
@@ -140,7 +140,7 @@ static void del_hist_entries(struct hists *hists)
|
||||
he = rb_entry(node, struct hist_entry, rb_node);
|
||||
rb_erase(node, root_out);
|
||||
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)
|
||||
he = rb_entry(node, struct hist_entry, rb_node);
|
||||
rb_erase(node, root_out);
|
||||
rb_erase(&he->rb_node_in, root_in);
|
||||
hist_entry__free(he);
|
||||
hist_entry__delete(he);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user