Commit fcc309e6 authored by Ingo Molnar's avatar Ingo Molnar

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

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

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

New features:

- Beautify the statx syscall arguments in 'perf trace' (Arnaldo Carvalho de Melo)

    e.g.:

  System wide strace like session:

  # trace -e statx
   16612.967 ( 0.028 ms): statx/4562 statx(dfd: CWD, filename: /tmp/statx, flags: SYMLINK_NOFOLLOW, mask: TYPE|MODE|NLINK|UID|GID|ATIME|MTIME|CTIME|INO|SIZE|BLOCKS|BTIME, buffer: 0x7ffef195d660) = 0
   36050.891 ( 0.007 ms): statx/4576 statx(dfd: CWD, filename: /etc/passwd, flags: SYMLINK_NOFOLLOW|STATX_DONT_SYNC, mask: BTIME, buffer: 0x7ffda9bf50f0) = 0
  ^C#

User visible changes:

- Handle unpaired raw_syscalls:sys_exit events in 'perf trace', i.e. we
  shouldn't try to calculate duration or print the timestamp for a missing
  matching raw_syscalls:sys_enter (Arnaldo Carvalho de Melo)

- Do not print "cycles: 0" in perf report LBR lines in platforms not
  supporting 'cycles', such as Intel's Broadwell (Jin Yao)

- Handle missing $HOME env var (Jiri Olsa)

- Map 8-bit registers (al, bl, etc), not supported in uprobes_events, to
  the next best thing (ax, bx, etc) supported (Ravi Bangoria)
Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents 25df39f2 fd5cead2
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#define __SANE_USERSPACE_TYPES__ /* For PPC64, to get LL64 types */ #define __SANE_USERSPACE_TYPES__ /* For PPC64, to get LL64 types */
#include <asm/types.h> #include <asm/types.h>
#include <asm/posix_types.h>
struct page; struct page;
struct kmem_cache; struct kmem_cache;
......
#ifndef _UAPI_LINUX_FCNTL_H
#define _UAPI_LINUX_FCNTL_H
#include <asm/fcntl.h>
#define F_SETLEASE (F_LINUX_SPECIFIC_BASE + 0)
#define F_GETLEASE (F_LINUX_SPECIFIC_BASE + 1)
/*
* Cancel a blocking posix lock; internal use only until we expose an
* asynchronous lock api to userspace:
*/
#define F_CANCELLK (F_LINUX_SPECIFIC_BASE + 5)
/* Create a file descriptor with FD_CLOEXEC set. */
#define F_DUPFD_CLOEXEC (F_LINUX_SPECIFIC_BASE + 6)
/*
* Request nofications on a directory.
* See below for events that may be notified.
*/
#define F_NOTIFY (F_LINUX_SPECIFIC_BASE+2)
/*
* Set and get of pipe page size array
*/
#define F_SETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 7)
#define F_GETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 8)
/*
* Set/Get seals
*/
#define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9)
#define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10)
/*
* Types of seals
*/
#define F_SEAL_SEAL 0x0001 /* prevent further seals from being set */
#define F_SEAL_SHRINK 0x0002 /* prevent file from shrinking */
#define F_SEAL_GROW 0x0004 /* prevent file from growing */
#define F_SEAL_WRITE 0x0008 /* prevent writes */
/* (1U << 31) is reserved for signed error codes */
/*
* Types of directory notifications that may be requested.
*/
#define DN_ACCESS 0x00000001 /* File accessed */
#define DN_MODIFY 0x00000002 /* File modified */
#define DN_CREATE 0x00000004 /* File created */
#define DN_DELETE 0x00000008 /* File removed */
#define DN_RENAME 0x00000010 /* File renamed */
#define DN_ATTRIB 0x00000020 /* File changed attibutes */
#define DN_MULTISHOT 0x80000000 /* Don't remove notifier */
#define AT_FDCWD -100 /* Special value used to indicate
openat should use the current
working directory. */
#define AT_SYMLINK_NOFOLLOW 0x100 /* Do not follow symbolic links. */
#define AT_REMOVEDIR 0x200 /* Remove directory instead of
unlinking file. */
#define AT_SYMLINK_FOLLOW 0x400 /* Follow symbolic links. */
#define AT_NO_AUTOMOUNT 0x800 /* Suppress terminal automount traversal */
#define AT_EMPTY_PATH 0x1000 /* Allow empty relative pathname */
#define AT_STATX_SYNC_TYPE 0x6000 /* Type of synchronisation required from statx() */
#define AT_STATX_SYNC_AS_STAT 0x0000 /* - Do whatever stat() does */
#define AT_STATX_FORCE_SYNC 0x2000 /* - Force the attributes to be sync'd with the server */
#define AT_STATX_DONT_SYNC 0x4000 /* - Don't sync attributes with the server */
#endif /* _UAPI_LINUX_FCNTL_H */
#ifndef _UAPI_LINUX_STAT_H
#define _UAPI_LINUX_STAT_H
#include <linux/types.h>
#if defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2)
#define S_IFMT 00170000
#define S_IFSOCK 0140000
#define S_IFLNK 0120000
#define S_IFREG 0100000
#define S_IFBLK 0060000
#define S_IFDIR 0040000
#define S_IFCHR 0020000
#define S_IFIFO 0010000
#define S_ISUID 0004000
#define S_ISGID 0002000
#define S_ISVTX 0001000
#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
#define S_IRWXU 00700
#define S_IRUSR 00400
#define S_IWUSR 00200
#define S_IXUSR 00100
#define S_IRWXG 00070
#define S_IRGRP 00040
#define S_IWGRP 00020
#define S_IXGRP 00010
#define S_IRWXO 00007
#define S_IROTH 00004
#define S_IWOTH 00002
#define S_IXOTH 00001
#endif
/*
* Timestamp structure for the timestamps in struct statx.
*
* tv_sec holds the number of seconds before (negative) or after (positive)
* 00:00:00 1st January 1970 UTC.
*
* tv_nsec holds a number of nanoseconds before (0..-999,999,999 if tv_sec is
* negative) or after (0..999,999,999 if tv_sec is positive) the tv_sec time.
*
* Note that if both tv_sec and tv_nsec are non-zero, then the two values must
* either be both positive or both negative.
*
* __reserved is held in case we need a yet finer resolution.
*/
struct statx_timestamp {
__s64 tv_sec;
__s32 tv_nsec;
__s32 __reserved;
};
/*
* Structures for the extended file attribute retrieval system call
* (statx()).
*
* The caller passes a mask of what they're specifically interested in as a
* parameter to statx(). What statx() actually got will be indicated in
* st_mask upon return.
*
* For each bit in the mask argument:
*
* - if the datum is not supported:
*
* - the bit will be cleared, and
*
* - the datum will be set to an appropriate fabricated value if one is
* available (eg. CIFS can take a default uid and gid), otherwise
*
* - the field will be cleared;
*
* - otherwise, if explicitly requested:
*
* - the datum will be synchronised to the server if AT_STATX_FORCE_SYNC is
* set or if the datum is considered out of date, and
*
* - the field will be filled in and the bit will be set;
*
* - otherwise, if not requested, but available in approximate form without any
* effort, it will be filled in anyway, and the bit will be set upon return
* (it might not be up to date, however, and no attempt will be made to
* synchronise the internal state first);
*
* - otherwise the field and the bit will be cleared before returning.
*
* Items in STATX_BASIC_STATS may be marked unavailable on return, but they
* will have values installed for compatibility purposes so that stat() and
* co. can be emulated in userspace.
*/
struct statx {
/* 0x00 */
__u32 stx_mask; /* What results were written [uncond] */
__u32 stx_blksize; /* Preferred general I/O size [uncond] */
__u64 stx_attributes; /* Flags conveying information about the file [uncond] */
/* 0x10 */
__u32 stx_nlink; /* Number of hard links */
__u32 stx_uid; /* User ID of owner */
__u32 stx_gid; /* Group ID of owner */
__u16 stx_mode; /* File mode */
__u16 __spare0[1];
/* 0x20 */
__u64 stx_ino; /* Inode number */
__u64 stx_size; /* File size */
__u64 stx_blocks; /* Number of 512-byte blocks allocated */
__u64 __spare1[1];
/* 0x40 */
struct statx_timestamp stx_atime; /* Last access time */
struct statx_timestamp stx_btime; /* File creation time */
struct statx_timestamp stx_ctime; /* Last attribute change time */
struct statx_timestamp stx_mtime; /* Last data modification time */
/* 0x80 */
__u32 stx_rdev_major; /* Device ID of special file [if bdev/cdev] */
__u32 stx_rdev_minor;
__u32 stx_dev_major; /* ID of device containing file [uncond] */
__u32 stx_dev_minor;
/* 0x90 */
__u64 __spare2[14]; /* Spare space for future expansion */
/* 0x100 */
};
/*
* Flags to be stx_mask
*
* Query request/result mask for statx() and struct statx::stx_mask.
*
* These bits should be set in the mask argument of statx() to request
* particular items when calling statx().
*/
#define STATX_TYPE 0x00000001U /* Want/got stx_mode & S_IFMT */
#define STATX_MODE 0x00000002U /* Want/got stx_mode & ~S_IFMT */
#define STATX_NLINK 0x00000004U /* Want/got stx_nlink */
#define STATX_UID 0x00000008U /* Want/got stx_uid */
#define STATX_GID 0x00000010U /* Want/got stx_gid */
#define STATX_ATIME 0x00000020U /* Want/got stx_atime */
#define STATX_MTIME 0x00000040U /* Want/got stx_mtime */
#define STATX_CTIME 0x00000080U /* Want/got stx_ctime */
#define STATX_INO 0x00000100U /* Want/got stx_ino */
#define STATX_SIZE 0x00000200U /* Want/got stx_size */
#define STATX_BLOCKS 0x00000400U /* Want/got stx_blocks */
#define STATX_BASIC_STATS 0x000007ffU /* The stuff in the normal stat struct */
#define STATX_BTIME 0x00000800U /* Want/got stx_btime */
#define STATX_ALL 0x00000fffU /* All currently supported flags */
/*
* Attributes to be found in stx_attributes
*
* These give information about the features or the state of a file that might
* be of use to ordinary userspace programs such as GUIs or ls rather than
* specialised tools.
*
* Note that the flags marked [I] correspond to generic FS_IOC_FLAGS
* semantically. Where possible, the numerical value is picked to correspond
* also.
*/
#define STATX_ATTR_COMPRESSED 0x00000004 /* [I] File is compressed by the fs */
#define STATX_ATTR_IMMUTABLE 0x00000010 /* [I] File is marked immutable */
#define STATX_ATTR_APPEND 0x00000020 /* [I] File is append-only */
#define STATX_ATTR_NODUMP 0x00000040 /* [I] File is not to be dumped */
#define STATX_ATTR_ENCRYPTED 0x00000800 /* [I] File requires key to decrypt in fs */
#define STATX_ATTR_AUTOMOUNT 0x00001000 /* Dir: Automount trigger */
#endif /* _UAPI_LINUX_STAT_H */
...@@ -50,5 +50,6 @@ libperf-y += util/ ...@@ -50,5 +50,6 @@ libperf-y += util/
libperf-y += arch/ libperf-y += arch/
libperf-y += ui/ libperf-y += ui/
libperf-y += scripts/ libperf-y += scripts/
libperf-y += trace/beauty/
gtk-y += ui/gtk/ gtk-y += ui/gtk/
...@@ -73,9 +73,11 @@ tools/include/uapi/asm-generic/mman-common.h ...@@ -73,9 +73,11 @@ tools/include/uapi/asm-generic/mman-common.h
tools/include/uapi/asm-generic/mman.h tools/include/uapi/asm-generic/mman.h
tools/include/uapi/linux/bpf.h tools/include/uapi/linux/bpf.h
tools/include/uapi/linux/bpf_common.h tools/include/uapi/linux/bpf_common.h
tools/include/uapi/linux/fcntl.h
tools/include/uapi/linux/hw_breakpoint.h tools/include/uapi/linux/hw_breakpoint.h
tools/include/uapi/linux/mman.h tools/include/uapi/linux/mman.h
tools/include/uapi/linux/perf_event.h tools/include/uapi/linux/perf_event.h
tools/include/uapi/linux/stat.h
tools/include/linux/poison.h tools/include/linux/poison.h
tools/include/linux/rbtree.h tools/include/linux/rbtree.h
tools/include/linux/rbtree_augmented.h tools/include/linux/rbtree_augmented.h
......
...@@ -338,6 +338,7 @@ ...@@ -338,6 +338,7 @@
329 common pkey_mprotect sys_pkey_mprotect 329 common pkey_mprotect sys_pkey_mprotect
330 common pkey_alloc sys_pkey_alloc 330 common pkey_alloc sys_pkey_alloc
331 common pkey_free sys_pkey_free 331 common pkey_free sys_pkey_free
332 common statx sys_statx
# #
# x32-specific system call numbers start at 512 to avoid cache impact # x32-specific system call numbers start at 512 to avoid cache impact
......
#include <string.h> #include <string.h>
#include <regex.h>
#include "../../perf.h" #include "../../perf.h"
#include "../../util/util.h" #include "../../util/util.h"
#include "../../util/perf_regs.h" #include "../../util/perf_regs.h"
#include "../../util/debug.h"
const struct sample_reg sample_reg_masks[] = { const struct sample_reg sample_reg_masks[] = {
SMPL_REG(AX, PERF_REG_X86_AX), SMPL_REG(AX, PERF_REG_X86_AX),
...@@ -37,15 +39,23 @@ struct sdt_name_reg { ...@@ -37,15 +39,23 @@ struct sdt_name_reg {
#define SDT_NAME_REG(n, m) {.sdt_name = "%" #n, .uprobe_name = "%" #m} #define SDT_NAME_REG(n, m) {.sdt_name = "%" #n, .uprobe_name = "%" #m}
#define SDT_NAME_REG_END {.sdt_name = NULL, .uprobe_name = NULL} #define SDT_NAME_REG_END {.sdt_name = NULL, .uprobe_name = NULL}
static const struct sdt_name_reg sdt_reg_renamings[] = { static const struct sdt_name_reg sdt_reg_tbl[] = {
SDT_NAME_REG(eax, ax), SDT_NAME_REG(eax, ax),
SDT_NAME_REG(rax, ax), SDT_NAME_REG(rax, ax),
SDT_NAME_REG(al, ax),
SDT_NAME_REG(ah, ax),
SDT_NAME_REG(ebx, bx), SDT_NAME_REG(ebx, bx),
SDT_NAME_REG(rbx, bx), SDT_NAME_REG(rbx, bx),
SDT_NAME_REG(bl, bx),
SDT_NAME_REG(bh, bx),
SDT_NAME_REG(ecx, cx), SDT_NAME_REG(ecx, cx),
SDT_NAME_REG(rcx, cx), SDT_NAME_REG(rcx, cx),
SDT_NAME_REG(cl, cx),
SDT_NAME_REG(ch, cx),
SDT_NAME_REG(edx, dx), SDT_NAME_REG(edx, dx),
SDT_NAME_REG(rdx, dx), SDT_NAME_REG(rdx, dx),
SDT_NAME_REG(dl, dx),
SDT_NAME_REG(dh, dx),
SDT_NAME_REG(esi, si), SDT_NAME_REG(esi, si),
SDT_NAME_REG(rsi, si), SDT_NAME_REG(rsi, si),
SDT_NAME_REG(sil, si), SDT_NAME_REG(sil, si),
...@@ -87,45 +97,158 @@ static const struct sdt_name_reg sdt_reg_renamings[] = { ...@@ -87,45 +97,158 @@ static const struct sdt_name_reg sdt_reg_renamings[] = {
SDT_NAME_REG_END, SDT_NAME_REG_END,
}; };
int sdt_rename_register(char **pdesc, char *old_name) /*
* Perf only supports OP which is in +/-NUM(REG) form.
* Here plus-minus sign, NUM and parenthesis are optional,
* only REG is mandatory.
*
* SDT events also supports indirect addressing mode with a
* symbol as offset, scaled mode and constants in OP. But
* perf does not support them yet. Below are few examples.
*
* OP with scaled mode:
* (%rax,%rsi,8)
* 10(%ras,%rsi,8)
*
* OP with indirect addressing mode:
* check_action(%rip)
* mp_+52(%rip)
* 44+mp_(%rip)
*
* OP with constant values:
* $0
* $123
* $-1
*/
#define SDT_OP_REGEX "^([+\\-]?)([0-9]*)(\\(?)(%[a-z][a-z0-9]+)(\\)?)$"
static regex_t sdt_op_regex;
static int sdt_init_op_regex(void)
{ {
const struct sdt_name_reg *rnames = sdt_reg_renamings; static int initialized;
char *new_desc, *old_desc = *pdesc; int ret = 0;
size_t prefix_len, sdt_len, uprobe_len, old_desc_len, offset;
int ret = -1; if (initialized)
return 0;
while (ret != 0 && rnames->sdt_name != NULL) {
sdt_len = strlen(rnames->sdt_name); ret = regcomp(&sdt_op_regex, SDT_OP_REGEX, REG_EXTENDED);
ret = strncmp(old_name, rnames->sdt_name, sdt_len); if (ret < 0) {
rnames += !!ret; pr_debug4("Regex compilation error.\n");
return ret;
} }
if (rnames->sdt_name == NULL) initialized = 1;
return 0; return 0;
}
/*
* Max x86 register name length is 5(ex: %r15d). So, 6th char
* should always contain NULL. This helps to find register name
* length using strlen, insted of maintaing one more variable.
*/
#define SDT_REG_NAME_SIZE 6
/*
* The uprobe parser does not support all gas register names;
* so, we have to replace them (ex. for x86_64: %rax -> %ax).
* Note: If register does not require renaming, just copy
* paste as it is, but don't leave it empty.
*/
static void sdt_rename_register(char *sdt_reg, int sdt_len, char *uprobe_reg)
{
int i = 0;
for (i = 0; sdt_reg_tbl[i].sdt_name != NULL; i++) {
if (!strncmp(sdt_reg_tbl[i].sdt_name, sdt_reg, sdt_len)) {
strcpy(uprobe_reg, sdt_reg_tbl[i].uprobe_name);
return;
}
}
strncpy(uprobe_reg, sdt_reg, sdt_len);
}
int arch_sdt_arg_parse_op(char *old_op, char **new_op)
{
char new_reg[SDT_REG_NAME_SIZE] = {0};
int new_len = 0, ret;
/*
* rm[0]: +/-NUM(REG)
* rm[1]: +/-
* rm[2]: NUM
* rm[3]: (
* rm[4]: REG
* rm[5]: )
*/
regmatch_t rm[6];
/*
* Max prefix length is 2 as it may contains sign(+/-)
* and displacement 0 (Both sign and displacement 0 are
* optional so it may be empty). Use one more character
* to hold last NULL so that strlen can be used to find
* prefix length, instead of maintaing one more variable.
*/
char prefix[3] = {0};
sdt_len = strlen(rnames->sdt_name); ret = sdt_init_op_regex();
uprobe_len = strlen(rnames->uprobe_name); if (ret < 0)
old_desc_len = strlen(old_desc) + 1; return ret;
new_desc = zalloc(old_desc_len + uprobe_len - sdt_len); /*
if (new_desc == NULL) * If unsupported OR does not match with regex OR
return -1; * register name too long, skip it.
*/
if (strchr(old_op, ',') || strchr(old_op, '$') ||
regexec(&sdt_op_regex, old_op, 6, rm, 0) ||
rm[4].rm_eo - rm[4].rm_so > SDT_REG_NAME_SIZE) {
pr_debug4("Skipping unsupported SDT argument: %s\n", old_op);
return SDT_ARG_SKIP;
}
/* Copy the chars before the register name (at least '%') */ /*
prefix_len = old_name - old_desc; * Prepare prefix.
memcpy(new_desc, old_desc, prefix_len); * If SDT OP has parenthesis but does not provide
* displacement, add 0 for displacement.
* SDT Uprobe Prefix
* -----------------------------
* +24(%rdi) +24(%di) +
* 24(%rdi) +24(%di) +
* %rdi %di
* (%rdi) +0(%di) +0
* -80(%rbx) -80(%bx) -
*/
if (rm[3].rm_so != rm[3].rm_eo) {
if (rm[1].rm_so != rm[1].rm_eo)
prefix[0] = *(old_op + rm[1].rm_so);
else if (rm[2].rm_so != rm[2].rm_eo)
prefix[0] = '+';
else
strncpy(prefix, "+0", 2);
}
/* Copy the new register name */ /* Rename register */
memcpy(new_desc + prefix_len, rnames->uprobe_name, uprobe_len); sdt_rename_register(old_op + rm[4].rm_so, rm[4].rm_eo - rm[4].rm_so,
new_reg);
/* Copy the chars after the register name (if need be) */ /* Prepare final OP which should be valid for uprobe_events */
offset = prefix_len + sdt_len; new_len = strlen(prefix) +
if (offset < old_desc_len) (rm[2].rm_eo - rm[2].rm_so) +
memcpy(new_desc + prefix_len + uprobe_len, (rm[3].rm_eo - rm[3].rm_so) +
old_desc + offset, old_desc_len - offset); strlen(new_reg) +
(rm[5].rm_eo - rm[5].rm_so) +
1; /* NULL */
free(old_desc); *new_op = zalloc(new_len);
*pdesc = new_desc; if (!*new_op)
return -ENOMEM;
return 0; scnprintf(*new_op, new_len, "%.*s%.*s%.*s%.*s%.*s",
strlen(prefix), prefix,
(int)(rm[2].rm_eo - rm[2].rm_so), old_op + rm[2].rm_so,
(int)(rm[3].rm_eo - rm[3].rm_so), old_op + rm[3].rm_so,
strlen(new_reg), new_reg,
(int)(rm[5].rm_eo - rm[5].rm_so), old_op + rm[5].rm_so);
return SDT_ARG_VALID;
} }
...@@ -301,12 +301,6 @@ void list_common_cmds_help(void) ...@@ -301,12 +301,6 @@ void list_common_cmds_help(void)
} }
} }
static int is_perf_command(const char *s)
{
return is_in_cmdlist(&main_cmds, s) ||
is_in_cmdlist(&other_cmds, s);
}
static const char *cmd_to_page(const char *perf_cmd) static const char *cmd_to_page(const char *perf_cmd)
{ {
char *s; char *s;
...@@ -446,7 +440,6 @@ int cmd_help(int argc, const char **argv) ...@@ -446,7 +440,6 @@ int cmd_help(int argc, const char **argv)
"perf help [--all] [--man|--web|--info] [command]", "perf help [--all] [--man|--web|--info] [command]",
NULL NULL
}; };
const char *alias;
int rc; int rc;
load_command_list("perf-", &main_cmds, &other_cmds); load_command_list("perf-", &main_cmds, &other_cmds);
...@@ -472,12 +465,6 @@ int cmd_help(int argc, const char **argv) ...@@ -472,12 +465,6 @@ int cmd_help(int argc, const char **argv)
return 0; return 0;
} }
alias = alias_lookup(argv[0]);
if (alias && !is_perf_command(argv[0])) {
printf("`perf %s' is aliased to `%s'\n", argv[0], alias);
return 0;
}
switch (help_format) { switch (help_format) {
case HELP_FORMAT_MAN: case HELP_FORMAT_MAN:
rc = show_man_page(argv[0]); rc = show_man_page(argv[0]);
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include "util/intlist.h" #include "util/intlist.h"
#include "util/thread_map.h" #include "util/thread_map.h"
#include "util/stat.h" #include "util/stat.h"
#include "trace/beauty/beauty.h"
#include "trace-event.h" #include "trace-event.h"
#include "util/parse-events.h" #include "util/parse-events.h"
#include "util/bpf-loader.h" #include "util/bpf-loader.h"
...@@ -267,15 +268,6 @@ static struct perf_evsel *perf_evsel__syscall_newtp(const char *direction, void ...@@ -267,15 +268,6 @@ static struct perf_evsel *perf_evsel__syscall_newtp(const char *direction, void
({ struct syscall_tp *fields = evsel->priv; \ ({ struct syscall_tp *fields = evsel->priv; \
fields->name.pointer(&fields->name, sample); }) fields->name.pointer(&fields->name, sample); })
struct syscall_arg {
unsigned long val;
struct thread *thread;
struct trace *trace;
void *parm;
u8 idx;
u8 mask;
};
struct strarray { struct strarray {
int offset; int offset;
int nr_entries; int nr_entries;
...@@ -771,6 +763,10 @@ static struct syscall_fmt { ...@@ -771,6 +763,10 @@ static struct syscall_fmt {
.arg_parm = { [0] = &strarray__socket_families, /* family */ }, }, .arg_parm = { [0] = &strarray__socket_families, /* family */ }, },
{ .name = "stat", .errmsg = true, .alias = "newstat", }, { .name = "stat", .errmsg = true, .alias = "newstat", },
{ .name = "statfs", .errmsg = true, }, { .name = "statfs", .errmsg = true, },
{ .name = "statx", .errmsg = true,
.arg_scnprintf = { [0] = SCA_FDAT, /* flags */
[2] = SCA_STATX_FLAGS, /* flags */
[3] = SCA_STATX_MASK, /* mask */ }, },
{ .name = "swapoff", .errmsg = true, { .name = "swapoff", .errmsg = true,
.arg_scnprintf = { [0] = SCA_FILENAME, /* specialfile */ }, }, .arg_scnprintf = { [0] = SCA_FILENAME, /* specialfile */ }, },
{ .name = "swapon", .errmsg = true, { .name = "swapon", .errmsg = true,
...@@ -821,12 +817,21 @@ struct syscall { ...@@ -821,12 +817,21 @@ struct syscall {
void **arg_parm; void **arg_parm;
}; };
static size_t fprintf_duration(unsigned long t, FILE *fp) /*
* We need to have this 'calculated' boolean because in some cases we really
* don't know what is the duration of a syscall, for instance, when we start
* a session and some threads are waiting for a syscall to finish, say 'poll',
* in which case all we can do is to print "( ? ) for duration and for the
* start timestamp.
*/
static size_t fprintf_duration(unsigned long t, bool calculated, FILE *fp)
{ {
double duration = (double)t / NSEC_PER_MSEC; double duration = (double)t / NSEC_PER_MSEC;
size_t printed = fprintf(fp, "("); size_t printed = fprintf(fp, "(");
if (duration >= 1.0) if (!calculated)
printed += fprintf(fp, " ? ");
else if (duration >= 1.0)
printed += color_fprintf(fp, PERF_COLOR_RED, "%6.3f ms", duration); printed += color_fprintf(fp, PERF_COLOR_RED, "%6.3f ms", duration);
else if (duration >= 0.01) else if (duration >= 0.01)
printed += color_fprintf(fp, PERF_COLOR_YELLOW, "%6.3f ms", duration); printed += color_fprintf(fp, PERF_COLOR_YELLOW, "%6.3f ms", duration);
...@@ -1028,13 +1033,27 @@ static bool trace__filter_duration(struct trace *trace, double t) ...@@ -1028,13 +1033,27 @@ static bool trace__filter_duration(struct trace *trace, double t)
return t < (trace->duration_filter * NSEC_PER_MSEC); return t < (trace->duration_filter * NSEC_PER_MSEC);
} }
static size_t trace__fprintf_tstamp(struct trace *trace, u64 tstamp, FILE *fp) static size_t __trace__fprintf_tstamp(struct trace *trace, u64 tstamp, FILE *fp)
{ {
double ts = (double)(tstamp - trace->base_time) / NSEC_PER_MSEC; double ts = (double)(tstamp - trace->base_time) / NSEC_PER_MSEC;
return fprintf(fp, "%10.3f ", ts); return fprintf(fp, "%10.3f ", ts);
} }
/*
* We're handling tstamp=0 as an undefined tstamp, i.e. like when we are
* using ttrace->entry_time for a thread that receives a sys_exit without
* first having received a sys_enter ("poll" issued before tracing session
* starts, lost sys_enter exit due to ring buffer overflow).
*/
static size_t trace__fprintf_tstamp(struct trace *trace, u64 tstamp, FILE *fp)
{
if (tstamp > 0)
return __trace__fprintf_tstamp(trace, tstamp, fp);
return fprintf(fp, " ? ");
}
static bool done = false; static bool done = false;
static bool interrupted = false; static bool interrupted = false;
...@@ -1045,10 +1064,10 @@ static void sig_handler(int sig) ...@@ -1045,10 +1064,10 @@ static void sig_handler(int sig)
} }
static size_t trace__fprintf_entry_head(struct trace *trace, struct thread *thread, static size_t trace__fprintf_entry_head(struct trace *trace, struct thread *thread,
u64 duration, u64 tstamp, FILE *fp) u64 duration, bool duration_calculated, u64 tstamp, FILE *fp)
{ {
size_t printed = trace__fprintf_tstamp(trace, tstamp, fp); size_t printed = trace__fprintf_tstamp(trace, tstamp, fp);
printed += fprintf_duration(duration, fp); printed += fprintf_duration(duration, duration_calculated, fp);
if (trace->multiple_threads) { if (trace->multiple_threads) {
if (trace->show_comm) if (trace->show_comm)
...@@ -1450,7 +1469,7 @@ static int trace__printf_interrupted_entry(struct trace *trace, struct perf_samp ...@@ -1450,7 +1469,7 @@ static int trace__printf_interrupted_entry(struct trace *trace, struct perf_samp
duration = sample->time - ttrace->entry_time; duration = sample->time - ttrace->entry_time;
printed = trace__fprintf_entry_head(trace, trace->current, duration, ttrace->entry_time, trace->output); printed = trace__fprintf_entry_head(trace, trace->current, duration, true, ttrace->entry_time, trace->output);
printed += fprintf(trace->output, "%-70s) ...\n", ttrace->entry_str); printed += fprintf(trace->output, "%-70s) ...\n", ttrace->entry_str);
ttrace->entry_pending = false; ttrace->entry_pending = false;
...@@ -1497,7 +1516,7 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, ...@@ -1497,7 +1516,7 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel,
if (sc->is_exit) { if (sc->is_exit) {
if (!(trace->duration_filter || trace->summary_only || trace->min_stack)) { if (!(trace->duration_filter || trace->summary_only || trace->min_stack)) {
trace__fprintf_entry_head(trace, thread, 1, ttrace->entry_time, trace->output); trace__fprintf_entry_head(trace, thread, 0, false, ttrace->entry_time, trace->output);
fprintf(trace->output, "%-70s)\n", ttrace->entry_str); fprintf(trace->output, "%-70s)\n", ttrace->entry_str);
} }
} else { } else {
...@@ -1545,6 +1564,7 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, ...@@ -1545,6 +1564,7 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel,
{ {
long ret; long ret;
u64 duration = 0; u64 duration = 0;
bool duration_calculated = false;
struct thread *thread; struct thread *thread;
int id = perf_evsel__sc_tp_uint(evsel, id, sample), err = -1, callchain_ret = 0; int id = perf_evsel__sc_tp_uint(evsel, id, sample), err = -1, callchain_ret = 0;
struct syscall *sc = trace__syscall_info(trace, evsel, id); struct syscall *sc = trace__syscall_info(trace, evsel, id);
...@@ -1573,6 +1593,7 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, ...@@ -1573,6 +1593,7 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel,
duration = sample->time - ttrace->entry_time; duration = sample->time - ttrace->entry_time;
if (trace__filter_duration(trace, duration)) if (trace__filter_duration(trace, duration))
goto out; goto out;
duration_calculated = true;
} else if (trace->duration_filter) } else if (trace->duration_filter)
goto out; goto out;
...@@ -1588,7 +1609,7 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, ...@@ -1588,7 +1609,7 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel,
if (trace->summary_only) if (trace->summary_only)
goto out; goto out;
trace__fprintf_entry_head(trace, thread, duration, ttrace->entry_time, trace->output); trace__fprintf_entry_head(trace, thread, duration, duration_calculated, ttrace->entry_time, trace->output);
if (ttrace->entry_pending) { if (ttrace->entry_pending) {
fprintf(trace->output, "%-70s", ttrace->entry_str); fprintf(trace->output, "%-70s", ttrace->entry_str);
...@@ -1855,7 +1876,7 @@ static int trace__pgfault(struct trace *trace, ...@@ -1855,7 +1876,7 @@ static int trace__pgfault(struct trace *trace,
thread__find_addr_location(thread, sample->cpumode, MAP__FUNCTION, thread__find_addr_location(thread, sample->cpumode, MAP__FUNCTION,
sample->ip, &al); sample->ip, &al);
trace__fprintf_entry_head(trace, thread, 0, sample->time, trace->output); trace__fprintf_entry_head(trace, thread, 0, true, sample->time, trace->output);
fprintf(trace->output, "%sfault [", fprintf(trace->output, "%sfault [",
evsel->attr.config == PERF_COUNT_SW_PAGE_FAULTS_MAJ ? evsel->attr.config == PERF_COUNT_SW_PAGE_FAULTS_MAJ ?
......
#!/bin/sh #!/bin/sh
HEADERS=' HEADERS='
include/uapi/linux/fcntl.h
include/uapi/linux/perf_event.h include/uapi/linux/perf_event.h
include/uapi/linux/stat.h
include/linux/hash.h include/linux/hash.h
include/uapi/linux/hw_breakpoint.h include/uapi/linux/hw_breakpoint.h
arch/x86/include/asm/disabled-features.h arch/x86/include/asm/disabled-features.h
......
...@@ -267,71 +267,6 @@ static int handle_options(const char ***argv, int *argc, int *envchanged) ...@@ -267,71 +267,6 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
return handled; return handled;
} }
static int handle_alias(int *argcp, const char ***argv)
{
int envchanged = 0, ret = 0, saved_errno = errno;
int count, option_count;
const char **new_argv;
const char *alias_command;
char *alias_string;
alias_command = (*argv)[0];
alias_string = alias_lookup(alias_command);
if (alias_string) {
if (alias_string[0] == '!') {
if (*argcp > 1) {
struct strbuf buf;
if (strbuf_init(&buf, PATH_MAX) < 0 ||
strbuf_addstr(&buf, alias_string) < 0 ||
sq_quote_argv(&buf, (*argv) + 1,
PATH_MAX) < 0)
die("Failed to allocate memory.");
free(alias_string);
alias_string = buf.buf;
}
ret = system(alias_string + 1);
if (ret >= 0 && WIFEXITED(ret) &&
WEXITSTATUS(ret) != 127)
exit(WEXITSTATUS(ret));
die("Failed to run '%s' when expanding alias '%s'",
alias_string + 1, alias_command);
}
count = split_cmdline(alias_string, &new_argv);
if (count < 0)
die("Bad alias.%s string", alias_command);
option_count = handle_options(&new_argv, &count, &envchanged);
if (envchanged)
die("alias '%s' changes environment variables\n"
"You can use '!perf' in the alias to do this.",
alias_command);
memmove(new_argv - option_count, new_argv,
count * sizeof(char *));
new_argv -= option_count;
if (count < 1)
die("empty alias for %s", alias_command);
if (!strcmp(alias_command, new_argv[0]))
die("recursive alias: %s", alias_command);
new_argv = realloc(new_argv, sizeof(char *) *
(count + *argcp + 1));
/* insert after command name */
memcpy(new_argv + count, *argv + 1, sizeof(char *) * *argcp);
new_argv[count + *argcp] = NULL;
*argv = new_argv;
*argcp += count - 1;
ret = 1;
}
errno = saved_errno;
return ret;
}
#define RUN_SETUP (1<<0) #define RUN_SETUP (1<<0)
#define USE_PAGER (1<<1) #define USE_PAGER (1<<1)
...@@ -455,25 +390,12 @@ static void execv_dashed_external(const char **argv) ...@@ -455,25 +390,12 @@ static void execv_dashed_external(const char **argv)
static int run_argv(int *argcp, const char ***argv) static int run_argv(int *argcp, const char ***argv)
{ {
int done_alias = 0;
while (1) {
/* See if it's an internal command */ /* See if it's an internal command */
handle_internal_command(*argcp, *argv); handle_internal_command(*argcp, *argv);
/* .. then try the external ones */ /* .. then try the external ones */
execv_dashed_external(*argv); execv_dashed_external(*argv);
return 0;
/* It could be an alias -- this works around the insanity
* of overriding "perf log" with "perf show" by having
* alias.log = show
*/
if (done_alias || !handle_alias(argcp, argv))
break;
done_alias = 1;
}
return done_alias;
} }
static void pthread__block_sigwinch(void) static void pthread__block_sigwinch(void)
...@@ -606,17 +528,12 @@ int main(int argc, const char **argv) ...@@ -606,17 +528,12 @@ int main(int argc, const char **argv)
while (1) { while (1) {
static int done_help; static int done_help;
int was_alias = run_argv(&argc, &argv);
run_argv(&argc, &argv);
if (errno != ENOENT) if (errno != ENOENT)
break; break;
if (was_alias) {
fprintf(stderr, "Expansion of alias '%s' failed; "
"'%s' is not a perf-command\n",
cmd, argv[0]);
goto out;
}
if (!done_help) { if (!done_help) {
cmd = argv[0] = help_unknown_cmd(cmd); cmd = argv[0] = help_unknown_cmd(cmd);
done_help = 1; done_help = 1;
......
#ifndef _PERF_TRACE_BEAUTY_H
#define _PERF_TRACE_BEAUTY_H
#include <linux/types.h>
struct trace;
struct thread;
struct syscall_arg {
unsigned long val;
struct thread *thread;
struct trace *trace;
void *parm;
u8 idx;
u8 mask;
};
size_t syscall_arg__scnprintf_statx_flags(char *bf, size_t size, struct syscall_arg *arg);
#define SCA_STATX_FLAGS syscall_arg__scnprintf_statx_flags
size_t syscall_arg__scnprintf_statx_mask(char *bf, size_t size, struct syscall_arg *arg);
#define SCA_STATX_MASK syscall_arg__scnprintf_statx_mask
#endif /* _PERF_TRACE_BEAUTY_H */
/*
* trace/beauty/statx.c
*
* Copyright (C) 2017, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com>
*
* Released under the GPL v2. (and only v2, not any later version)
*/
#include "trace/beauty/beauty.h"
#include <linux/kernel.h>
#include <sys/types.h>
#include <uapi/linux/fcntl.h>
#include <uapi/linux/stat.h>
size_t syscall_arg__scnprintf_statx_flags(char *bf, size_t size, struct syscall_arg *arg)
{
int printed = 0, flags = arg->val;
if (flags == 0)
return scnprintf(bf, size, "SYNC_AS_STAT");
#define P_FLAG(n) \
if (flags & AT_##n) { \
printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", #n); \
flags &= ~AT_##n; \
}
P_FLAG(SYMLINK_NOFOLLOW);
P_FLAG(REMOVEDIR);
P_FLAG(SYMLINK_FOLLOW);
P_FLAG(NO_AUTOMOUNT);
P_FLAG(EMPTY_PATH);
P_FLAG(STATX_FORCE_SYNC);
P_FLAG(STATX_DONT_SYNC);
#undef P_FLAG
if (flags)
printed += scnprintf(bf + printed, size - printed, "%s%#x", printed ? "|" : "", flags);
return printed;
}
size_t syscall_arg__scnprintf_statx_mask(char *bf, size_t size, struct syscall_arg *arg)
{
int printed = 0, flags = arg->val;
#define P_FLAG(n) \
if (flags & STATX_##n) { \
printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", #n); \
flags &= ~STATX_##n; \
}
P_FLAG(TYPE);
P_FLAG(MODE);
P_FLAG(NLINK);
P_FLAG(UID);
P_FLAG(GID);
P_FLAG(ATIME);
P_FLAG(MTIME);
P_FLAG(CTIME);
P_FLAG(INO);
P_FLAG(SIZE);
P_FLAG(BLOCKS);
P_FLAG(BTIME);
#undef P_FLAG
if (flags)
printed += scnprintf(bf + printed, size - printed, "%s%#x", printed ? "|" : "", flags);
return printed;
}
libperf-y += alias.o
libperf-y += annotate.o libperf-y += annotate.o
libperf-y += block-range.o libperf-y += block-range.o
libperf-y += build-id.o libperf-y += build-id.o
......
#include "cache.h"
#include "util.h"
#include "config.h"
static const char *alias_key;
static char *alias_val;
static int alias_lookup_cb(const char *k, const char *v,
void *cb __maybe_unused)
{
if (!prefixcmp(k, "alias.") && !strcmp(k+6, alias_key)) {
if (!v)
return config_error_nonbool(k);
alias_val = strdup(v);
return 0;
}
return 0;
}
char *alias_lookup(const char *alias)
{
alias_key = alias;
alias_val = NULL;
perf_config(alias_lookup_cb, NULL);
return alias_val;
}
int split_cmdline(char *cmdline, const char ***argv)
{
int src, dst, count = 0, size = 16;
char quoted = 0;
*argv = malloc(sizeof(char*) * size);
/* split alias_string */
(*argv)[count++] = cmdline;
for (src = dst = 0; cmdline[src];) {
char c = cmdline[src];
if (!quoted && isspace(c)) {
cmdline[dst++] = 0;
while (cmdline[++src]
&& isspace(cmdline[src]))
; /* skip */
if (count >= size) {
size += 16;
*argv = realloc(*argv, sizeof(char*) * size);
}
(*argv)[count++] = cmdline + dst;
} else if (!quoted && (c == '\'' || c == '"')) {
quoted = c;
src++;
} else if (c == quoted) {
quoted = 0;
src++;
} else {
if (c == '\\' && quoted != '\'') {
src++;
c = cmdline[src];
if (!c) {
zfree(argv);
return error("cmdline ends with \\");
}
}
cmdline[dst++] = c;
src++;
}
}
cmdline[dst] = 0;
if (quoted) {
zfree(argv);
return error("unclosed quote");
}
return count;
}
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
#define PERF_TRACEFS_ENVIRONMENT "PERF_TRACEFS_DIR" #define PERF_TRACEFS_ENVIRONMENT "PERF_TRACEFS_DIR"
#define PERF_PAGER_ENVIRONMENT "PERF_PAGER" #define PERF_PAGER_ENVIRONMENT "PERF_PAGER"
char *alias_lookup(const char *alias);
int split_cmdline(char *cmdline, const char ***argv); int split_cmdline(char *cmdline, const char ***argv);
#define alloc_nr(x) (((x)+16)*3/2) #define alloc_nr(x) (((x)+16)*3/2)
......
...@@ -1105,7 +1105,7 @@ int callchain_branch_counts(struct callchain_root *root, ...@@ -1105,7 +1105,7 @@ int callchain_branch_counts(struct callchain_root *root,
cycles_count); cycles_count);
} }
static int callchain_counts_printf(FILE *fp, char *bf, int bfsize, static int counts_str_build(char *bf, int bfsize,
u64 branch_count, u64 predicted_count, u64 branch_count, u64 predicted_count,
u64 abort_count, u64 cycles_count, u64 abort_count, u64 cycles_count,
u64 iter_count, u64 samples_count) u64 iter_count, u64 samples_count)
...@@ -1113,55 +1113,92 @@ static int callchain_counts_printf(FILE *fp, char *bf, int bfsize, ...@@ -1113,55 +1113,92 @@ static int callchain_counts_printf(FILE *fp, char *bf, int bfsize,
double predicted_percent = 0.0; double predicted_percent = 0.0;
const char *null_str = ""; const char *null_str = "";
char iter_str[32]; char iter_str[32];
char *str; char cycle_str[32];
u64 cycles = 0; char *istr, *cstr;
u64 cycles;
if (branch_count == 0) {
if (fp)
return fprintf(fp, " (calltrace)");
if (branch_count == 0)
return scnprintf(bf, bfsize, " (calltrace)"); return scnprintf(bf, bfsize, " (calltrace)");
}
cycles = cycles_count / branch_count;
if (iter_count && samples_count) { if (iter_count && samples_count) {
if (cycles > 0)
scnprintf(iter_str, sizeof(iter_str),
" iterations:%" PRId64 "",
iter_count / samples_count);
else
scnprintf(iter_str, sizeof(iter_str), scnprintf(iter_str, sizeof(iter_str),
", iterations:%" PRId64 "", "iterations:%" PRId64 "",
iter_count / samples_count); iter_count / samples_count);
str = iter_str; istr = iter_str;
} else } else
str = (char *)null_str; istr = (char *)null_str;
predicted_percent = predicted_count * 100.0 / branch_count; if (cycles > 0) {
cycles = cycles_count / branch_count; scnprintf(cycle_str, sizeof(cycle_str),
"cycles:%" PRId64 "", cycles);
cstr = cycle_str;
} else
cstr = (char *)null_str;
if ((predicted_percent >= 100.0) && (abort_count == 0)) { predicted_percent = predicted_count * 100.0 / branch_count;
if (fp)
return fprintf(fp, " (cycles:%" PRId64 "%s)",
cycles, str);
return scnprintf(bf, bfsize, " (cycles:%" PRId64 "%s)", if ((predicted_count == branch_count) && (abort_count == 0)) {
cycles, str); if ((cycles > 0) || (istr != (char *)null_str))
return scnprintf(bf, bfsize, " (%s%s)", cstr, istr);
else
return scnprintf(bf, bfsize, "%s", (char *)null_str);
} }
if ((predicted_percent < 100.0) && (abort_count == 0)) { if ((predicted_count < branch_count) && (abort_count == 0)) {
if (fp) if ((cycles > 0) || (istr != (char *)null_str))
return fprintf(fp, return scnprintf(bf, bfsize,
" (predicted:%.1f%%, cycles:%" PRId64 "%s)", " (predicted:%.1f%% %s%s)",
predicted_percent, cycles, str); predicted_percent, cstr, istr);
else {
return scnprintf(bf, bfsize,
" (predicted:%.1f%%)",
predicted_percent);
}
}
if ((predicted_count == branch_count) && (abort_count > 0)) {
if ((cycles > 0) || (istr != (char *)null_str))
return scnprintf(bf, bfsize, return scnprintf(bf, bfsize,
" (predicted:%.1f%%, cycles:%" PRId64 "%s)", " (abort:%" PRId64 " %s%s)",
predicted_percent, cycles, str); abort_count, cstr, istr);
else
return scnprintf(bf, bfsize,
" (abort:%" PRId64 ")",
abort_count);
} }
if (fp) if ((cycles > 0) || (istr != (char *)null_str))
return fprintf(fp, return scnprintf(bf, bfsize,
" (predicted:%.1f%%, abort:%" PRId64 ", cycles:%" PRId64 "%s)", " (predicted:%.1f%% abort:%" PRId64 " %s%s)",
predicted_percent, abort_count, cycles, str); predicted_percent, abort_count, cstr, istr);
return scnprintf(bf, bfsize, return scnprintf(bf, bfsize,
" (predicted:%.1f%%, abort:%" PRId64 ", cycles:%" PRId64 "%s)", " (predicted:%.1f%% abort:%" PRId64 ")",
predicted_percent, abort_count, cycles, str); predicted_percent, abort_count);
}
static int callchain_counts_printf(FILE *fp, char *bf, int bfsize,
u64 branch_count, u64 predicted_count,
u64 abort_count, u64 cycles_count,
u64 iter_count, u64 samples_count)
{
char str[128];
counts_str_build(str, sizeof(str), branch_count,
predicted_count, abort_count, cycles_count,
iter_count, samples_count);
if (fp)
return fprintf(fp, "%s", str);
return scnprintf(bf, bfsize, "%s", str);
} }
int callchain_list_counts__printf_value(struct callchain_node *node, int callchain_list_counts__printf_value(struct callchain_node *node,
......
...@@ -627,6 +627,8 @@ static int perf_config_set__init(struct perf_config_set *set) ...@@ -627,6 +627,8 @@ static int perf_config_set__init(struct perf_config_set *set)
{ {
int ret = -1; int ret = -1;
const char *home = NULL; const char *home = NULL;
char *user_config;
struct stat st;
/* Setting $PERF_CONFIG makes perf read _only_ the given config file. */ /* Setting $PERF_CONFIG makes perf read _only_ the given config file. */
if (config_exclusive_filename) if (config_exclusive_filename)
...@@ -637,10 +639,16 @@ static int perf_config_set__init(struct perf_config_set *set) ...@@ -637,10 +639,16 @@ static int perf_config_set__init(struct perf_config_set *set)
} }
home = getenv("HOME"); home = getenv("HOME");
if (perf_config_global() && home) {
char *user_config = strdup(mkpath("%s/.perfconfig", home));
struct stat st;
/*
* Skip reading user config if:
* - there is no place to read it from (HOME)
* - we are asked not to (PERF_CONFIG_NOGLOBAL=1)
*/
if (!home || !*home || !perf_config_global())
return 0;
user_config = strdup(mkpath("%s/.perfconfig", home));
if (user_config == NULL) { if (user_config == NULL) {
warning("Not enough memory to process %s/.perfconfig, " warning("Not enough memory to process %s/.perfconfig, "
"ignoring it.", home); "ignoring it.", home);
...@@ -663,9 +671,9 @@ static int perf_config_set__init(struct perf_config_set *set) ...@@ -663,9 +671,9 @@ static int perf_config_set__init(struct perf_config_set *set)
if (st.st_size) if (st.st_size)
ret = perf_config_from_file(collect_config, user_config, set); ret = perf_config_from_file(collect_config, user_config, set);
out_free: out_free:
free(user_config); free(user_config);
}
out: out:
return ret; return ret;
} }
......
...@@ -6,16 +6,12 @@ ...@@ -6,16 +6,12 @@
#include "levenshtein.h" #include "levenshtein.h"
static int autocorrect; static int autocorrect;
static struct cmdnames aliases;
static int perf_unknown_cmd_config(const char *var, const char *value, static int perf_unknown_cmd_config(const char *var, const char *value,
void *cb __maybe_unused) void *cb __maybe_unused)
{ {
if (!strcmp(var, "help.autocorrect")) if (!strcmp(var, "help.autocorrect"))
autocorrect = perf_config_int(var,value); autocorrect = perf_config_int(var,value);
/* Also use aliases for command lookup */
if (!prefixcmp(var, "alias."))
add_cmdname(&aliases, var + 6, strlen(var + 6));
return 0; return 0;
} }
...@@ -59,14 +55,12 @@ const char *help_unknown_cmd(const char *cmd) ...@@ -59,14 +55,12 @@ const char *help_unknown_cmd(const char *cmd)
memset(&main_cmds, 0, sizeof(main_cmds)); memset(&main_cmds, 0, sizeof(main_cmds));
memset(&other_cmds, 0, sizeof(main_cmds)); memset(&other_cmds, 0, sizeof(main_cmds));
memset(&aliases, 0, sizeof(aliases));
perf_config(perf_unknown_cmd_config, NULL); perf_config(perf_unknown_cmd_config, NULL);
load_command_list("perf-", &main_cmds, &other_cmds); load_command_list("perf-", &main_cmds, &other_cmds);
if (add_cmd_list(&main_cmds, &aliases) < 0 || if (add_cmd_list(&main_cmds, &other_cmds) < 0) {
add_cmd_list(&main_cmds, &other_cmds) < 0) {
fprintf(stderr, "ERROR: Failed to allocate command list for unknown command.\n"); fprintf(stderr, "ERROR: Failed to allocate command list for unknown command.\n");
goto end; goto end;
} }
......
...@@ -2459,7 +2459,7 @@ int parse_filter_percentage(const struct option *opt __maybe_unused, ...@@ -2459,7 +2459,7 @@ int parse_filter_percentage(const struct option *opt __maybe_unused,
else if (!strcmp(arg, "absolute")) else if (!strcmp(arg, "absolute"))
symbol_conf.filter_relative = false; symbol_conf.filter_relative = false;
else { else {
pr_debug("Invalud percentage: %s\n", arg); pr_debug("Invalid percentage: %s\n", arg);
return -1; return -1;
} }
......
...@@ -6,10 +6,10 @@ const struct sample_reg __weak sample_reg_masks[] = { ...@@ -6,10 +6,10 @@ const struct sample_reg __weak sample_reg_masks[] = {
SMPL_REG_END SMPL_REG_END
}; };
int __weak sdt_rename_register(char **pdesc __maybe_unused, int __weak arch_sdt_arg_parse_op(char *old_op __maybe_unused,
char *old_name __maybe_unused) char **new_op __maybe_unused)
{ {
return 0; return SDT_ARG_SKIP;
} }
#ifdef HAVE_PERF_REGS_SUPPORT #ifdef HAVE_PERF_REGS_SUPPORT
......
...@@ -15,11 +15,12 @@ struct sample_reg { ...@@ -15,11 +15,12 @@ struct sample_reg {
extern const struct sample_reg sample_reg_masks[]; extern const struct sample_reg sample_reg_masks[];
/* enum {
* The table sdt_reg_renamings is used for adjusting gcc/gas-generated SDT_ARG_VALID = 0,
* registers before filling the uprobe tracer interface. SDT_ARG_SKIP,
*/ };
int sdt_rename_register(char **pdesc, char *old_name);
int arch_sdt_arg_parse_op(char *old_op, char **new_op);
#ifdef HAVE_PERF_REGS_SUPPORT #ifdef HAVE_PERF_REGS_SUPPORT
#include <perf_regs.h> #include <perf_regs.h>
......
...@@ -694,123 +694,67 @@ static const char * const type_to_suffix[] = { ...@@ -694,123 +694,67 @@ static const char * const type_to_suffix[] = {
"", ":u8", ":u16", "", ":u32", "", "", "", ":u64" "", ":u8", ":u16", "", ":u32", "", "", "", ":u64"
}; };
static int synthesize_sdt_probe_arg(struct strbuf *buf, int i, const char *arg) /*
* Isolate the string number and convert it into a decimal value;
* this will be an index to get suffix of the uprobe name (defining
* the type)
*/
static int sdt_arg_parse_size(char *n_ptr, const char **suffix)
{ {
char *tmp, *desc = strdup(arg);
const char *prefix = "", *suffix = "";
int ret = -1;
if (desc == NULL) {
pr_debug4("Allocation error\n");
return ret;
}
tmp = strchr(desc, '@');
if (tmp) {
long type_idx; long type_idx;
/*
* Isolate the string number and convert it into a type_idx = strtol(n_ptr, NULL, 10);
* binary value; this will be an index to get suffix
* of the uprobe name (defining the type)
*/
tmp[0] = '\0';
type_idx = strtol(desc, NULL, 10);
/* Check that the conversion went OK */
if (type_idx == LONG_MIN || type_idx == LONG_MAX) {
pr_debug4("Failed to parse sdt type\n");
goto error;
}
/* Check that the converted value is OK */
if (type_idx < -8 || type_idx > 8) { if (type_idx < -8 || type_idx > 8) {
pr_debug4("Failed to get a valid sdt type\n"); pr_debug4("Failed to get a valid sdt type\n");
goto error; return -1;
}
suffix = type_to_suffix[type_idx + 8];
/* Get rid of the sdt prefix which is now useless */
tmp++;
memmove(desc, tmp, strlen(tmp) + 1);
} }
/* *suffix = type_to_suffix[type_idx + 8];
* The uprobe tracer format does not support all the return 0;
* addressing modes (notably: in x86 the scaled mode); so, we }
* detect ',' characters, if there is just one, there is no
* use converting the sdt arg into a uprobe one.
*/
if (strchr(desc, ',')) {
pr_debug4("Skipping unsupported SDT argument; %s\n", desc);
goto out;
}
/*
* If the argument addressing mode is indirect, we must check
* a few things...
*/
tmp = strchr(desc, '(');
if (tmp) {
int j;
/* static int synthesize_sdt_probe_arg(struct strbuf *buf, int i, const char *arg)
* ...if the addressing mode is indirect with a {
* positive offset (ex.: "1608(%ax)"), we need to add char *op, *desc = strdup(arg), *new_op = NULL;
* a '+' prefix so as to be compliant with uprobe const char *suffix = "";
* format. int ret = -1;
*/
if (desc[0] != '+' && desc[0] != '-')
prefix = "+";
/* if (desc == NULL) {
* ...or if the addressing mode is indirect with a symbol pr_debug4("Allocation error\n");
* as offset, the argument will not be supported by return ret;
* the uprobe tracer format; so, let's skip this one.
*/
for (j = 0; j < tmp - desc; j++) {
if (desc[j] != '+' && desc[j] != '-' &&
!isdigit(desc[j])) {
pr_debug4("Skipping unsupported SDT argument; "
"%s\n", desc);
goto out;
}
}
} }
/* /*
* The uprobe tracer format does not support constants; if we * Argument is in N@OP format. N is size of the argument and OP is
* find one in the current argument, let's skip the argument. * the actual assembly operand. N can be omitted; in that case
* argument is just OP(without @).
*/ */
if (strchr(desc, '$')) { op = strchr(desc, '@');
pr_debug4("Skipping unsupported SDT argument; %s\n", desc); if (op) {
goto out; op[0] = '\0';
op++;
if (sdt_arg_parse_size(desc, &suffix))
goto error;
} else {
op = desc;
} }
/* ret = arch_sdt_arg_parse_op(op, &new_op);
* The uprobe parser does not support all gas register names;
* so, we have to replace them (ex. for x86_64: %rax -> %ax);
* the loop below looks for the register names (starting with
* a '%' and tries to perform the needed renamings.
*/
tmp = strchr(desc, '%');
while (tmp) {
size_t offset = tmp - desc;
ret = sdt_rename_register(&desc, desc + offset);
if (ret < 0) if (ret < 0)
goto error; goto error;
/* if (ret == SDT_ARG_VALID) {
* The desc pointer might have changed; so, let's not ret = strbuf_addf(buf, " arg%d=%s%s", i + 1, new_op, suffix);
* try to reuse tmp for next lookup if (ret < 0)
*/
tmp = strchr(desc + offset + 1, '%');
}
if (strbuf_addf(buf, " arg%d=%s%s%s", i + 1, prefix, desc, suffix) < 0)
goto error; goto error;
}
out:
ret = 0; ret = 0;
error: error:
free(desc); free(desc);
free(new_op);
return ret; return ret;
} }
......
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