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 @@
#define __SANE_USERSPACE_TYPES__ /* For PPC64, to get LL64 types */
#include <asm/types.h>
#include <asm/posix_types.h>
struct page;
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/
libperf-y += arch/
libperf-y += ui/
libperf-y += scripts/
libperf-y += trace/beauty/
gtk-y += ui/gtk/
......@@ -73,9 +73,11 @@ tools/include/uapi/asm-generic/mman-common.h
tools/include/uapi/asm-generic/mman.h
tools/include/uapi/linux/bpf.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/mman.h
tools/include/uapi/linux/perf_event.h
tools/include/uapi/linux/stat.h
tools/include/linux/poison.h
tools/include/linux/rbtree.h
tools/include/linux/rbtree_augmented.h
......
......@@ -338,6 +338,7 @@
329 common pkey_mprotect sys_pkey_mprotect
330 common pkey_alloc sys_pkey_alloc
331 common pkey_free sys_pkey_free
332 common statx sys_statx
#
# x32-specific system call numbers start at 512 to avoid cache impact
......
#include <string.h>
#include <regex.h>
#include "../../perf.h"
#include "../../util/util.h"
#include "../../util/perf_regs.h"
#include "../../util/debug.h"
const struct sample_reg sample_reg_masks[] = {
SMPL_REG(AX, PERF_REG_X86_AX),
......@@ -37,15 +39,23 @@ struct sdt_name_reg {
#define SDT_NAME_REG(n, m) {.sdt_name = "%" #n, .uprobe_name = "%" #m}
#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(rax, ax),
SDT_NAME_REG(al, ax),
SDT_NAME_REG(ah, ax),
SDT_NAME_REG(ebx, bx),
SDT_NAME_REG(rbx, bx),
SDT_NAME_REG(bl, bx),
SDT_NAME_REG(bh, bx),
SDT_NAME_REG(ecx, cx),
SDT_NAME_REG(rcx, cx),
SDT_NAME_REG(cl, cx),
SDT_NAME_REG(ch, cx),
SDT_NAME_REG(edx, dx),
SDT_NAME_REG(rdx, dx),
SDT_NAME_REG(dl, dx),
SDT_NAME_REG(dh, dx),
SDT_NAME_REG(esi, si),
SDT_NAME_REG(rsi, si),
SDT_NAME_REG(sil, si),
......@@ -87,45 +97,158 @@ static const struct sdt_name_reg sdt_reg_renamings[] = {
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;
char *new_desc, *old_desc = *pdesc;
size_t prefix_len, sdt_len, uprobe_len, old_desc_len, offset;
int ret = -1;
while (ret != 0 && rnames->sdt_name != NULL) {
sdt_len = strlen(rnames->sdt_name);
ret = strncmp(old_name, rnames->sdt_name, sdt_len);
rnames += !!ret;
}
static int initialized;
int ret = 0;
if (rnames->sdt_name == NULL)
if (initialized)
return 0;
sdt_len = strlen(rnames->sdt_name);
uprobe_len = strlen(rnames->uprobe_name);
old_desc_len = strlen(old_desc) + 1;
ret = regcomp(&sdt_op_regex, SDT_OP_REGEX, REG_EXTENDED);
if (ret < 0) {
pr_debug4("Regex compilation error.\n");
return ret;
}
new_desc = zalloc(old_desc_len + uprobe_len - sdt_len);
if (new_desc == NULL)
return -1;
initialized = 1;
return 0;
}
/* Copy the chars before the register name (at least '%') */
prefix_len = old_name - old_desc;
memcpy(new_desc, old_desc, prefix_len);
/*
* 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
/* Copy the new register name */
memcpy(new_desc + prefix_len, rnames->uprobe_name, uprobe_len);
/*
* 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;
/* Copy the chars after the register name (if need be) */
offset = prefix_len + sdt_len;
if (offset < old_desc_len)
memcpy(new_desc + prefix_len + uprobe_len,
old_desc + offset, old_desc_len - offset);
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;
}
}
free(old_desc);
*pdesc = new_desc;
strncpy(uprobe_reg, sdt_reg, sdt_len);
}
return 0;
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};
ret = sdt_init_op_regex();
if (ret < 0)
return ret;
/*
* If unsupported OR does not match with regex OR
* 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;
}
/*
* Prepare prefix.
* 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);
}
/* Rename register */
sdt_rename_register(old_op + rm[4].rm_so, rm[4].rm_eo - rm[4].rm_so,
new_reg);
/* Prepare final OP which should be valid for uprobe_events */
new_len = strlen(prefix) +
(rm[2].rm_eo - rm[2].rm_so) +
(rm[3].rm_eo - rm[3].rm_so) +
strlen(new_reg) +
(rm[5].rm_eo - rm[5].rm_so) +
1; /* NULL */
*new_op = zalloc(new_len);
if (!*new_op)
return -ENOMEM;
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)
}
}
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)
{
char *s;
......@@ -446,7 +440,6 @@ int cmd_help(int argc, const char **argv)
"perf help [--all] [--man|--web|--info] [command]",
NULL
};
const char *alias;
int rc;
load_command_list("perf-", &main_cmds, &other_cmds);
......@@ -472,12 +465,6 @@ int cmd_help(int argc, const char **argv)
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) {
case HELP_FORMAT_MAN:
rc = show_man_page(argv[0]);
......
......@@ -31,6 +31,7 @@
#include "util/intlist.h"
#include "util/thread_map.h"
#include "util/stat.h"
#include "trace/beauty/beauty.h"
#include "trace-event.h"
#include "util/parse-events.h"
#include "util/bpf-loader.h"
......@@ -267,15 +268,6 @@ static struct perf_evsel *perf_evsel__syscall_newtp(const char *direction, void
({ struct syscall_tp *fields = evsel->priv; \
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 {
int offset;
int nr_entries;
......@@ -771,6 +763,10 @@ static struct syscall_fmt {
.arg_parm = { [0] = &strarray__socket_families, /* family */ }, },
{ .name = "stat", .errmsg = true, .alias = "newstat", },
{ .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,
.arg_scnprintf = { [0] = SCA_FILENAME, /* specialfile */ }, },
{ .name = "swapon", .errmsg = true,
......@@ -821,12 +817,21 @@ struct syscall {
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;
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);
else if (duration >= 0.01)
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)
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;
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 interrupted = false;
......@@ -1045,10 +1064,10 @@ static void sig_handler(int sig)
}
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);
printed += fprintf_duration(duration, fp);
printed += fprintf_duration(duration, duration_calculated, fp);
if (trace->multiple_threads) {
if (trace->show_comm)
......@@ -1450,7 +1469,7 @@ static int trace__printf_interrupted_entry(struct trace *trace, struct perf_samp
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);
ttrace->entry_pending = false;
......@@ -1497,7 +1516,7 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel,
if (sc->is_exit) {
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);
}
} else {
......@@ -1545,6 +1564,7 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel,
{
long ret;
u64 duration = 0;
bool duration_calculated = false;
struct thread *thread;
int id = perf_evsel__sc_tp_uint(evsel, id, sample), err = -1, callchain_ret = 0;
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,
duration = sample->time - ttrace->entry_time;
if (trace__filter_duration(trace, duration))
goto out;
duration_calculated = true;
} else if (trace->duration_filter)
goto out;
......@@ -1588,7 +1609,7 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel,
if (trace->summary_only)
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) {
fprintf(trace->output, "%-70s", ttrace->entry_str);
......@@ -1855,7 +1876,7 @@ static int trace__pgfault(struct trace *trace,
thread__find_addr_location(thread, sample->cpumode, MAP__FUNCTION,
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 [",
evsel->attr.config == PERF_COUNT_SW_PAGE_FAULTS_MAJ ?
......
#!/bin/sh
HEADERS='
include/uapi/linux/fcntl.h
include/uapi/linux/perf_event.h
include/uapi/linux/stat.h
include/linux/hash.h
include/uapi/linux/hw_breakpoint.h
arch/x86/include/asm/disabled-features.h
......
......@@ -267,71 +267,6 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
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 USE_PAGER (1<<1)
......@@ -455,25 +390,12 @@ static void execv_dashed_external(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 */
handle_internal_command(*argcp, *argv);
/* .. then try the external ones */
execv_dashed_external(*argv);
/* See if it's an internal command */
handle_internal_command(*argcp, *argv);
/* 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;
/* .. then try the external ones */
execv_dashed_external(*argv);
return 0;
}
static void pthread__block_sigwinch(void)
......@@ -606,17 +528,12 @@ int main(int argc, const char **argv)
while (1) {
static int done_help;
int was_alias = run_argv(&argc, &argv);
run_argv(&argc, &argv);
if (errno != ENOENT)
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) {
cmd = argv[0] = help_unknown_cmd(cmd);
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 += block-range.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 @@
#define PERF_TRACEFS_ENVIRONMENT "PERF_TRACEFS_DIR"
#define PERF_PAGER_ENVIRONMENT "PERF_PAGER"
char *alias_lookup(const char *alias);
int split_cmdline(char *cmdline, const char ***argv);
#define alloc_nr(x) (((x)+16)*3/2)
......
......@@ -1105,63 +1105,100 @@ int callchain_branch_counts(struct callchain_root *root,
cycles_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)
static int counts_str_build(char *bf, int bfsize,
u64 branch_count, u64 predicted_count,
u64 abort_count, u64 cycles_count,
u64 iter_count, u64 samples_count)
{
double predicted_percent = 0.0;
const char *null_str = "";
char iter_str[32];
char *str;
u64 cycles = 0;
if (branch_count == 0) {
if (fp)
return fprintf(fp, " (calltrace)");
char cycle_str[32];
char *istr, *cstr;
u64 cycles;
if (branch_count == 0)
return scnprintf(bf, bfsize, " (calltrace)");
}
cycles = cycles_count / branch_count;
if (iter_count && samples_count) {
scnprintf(iter_str, sizeof(iter_str),
", iterations:%" PRId64 "",
iter_count / samples_count);
str = iter_str;
if (cycles > 0)
scnprintf(iter_str, sizeof(iter_str),
" iterations:%" PRId64 "",
iter_count / samples_count);
else
scnprintf(iter_str, sizeof(iter_str),
"iterations:%" PRId64 "",
iter_count / samples_count);
istr = iter_str;
} else
istr = (char *)null_str;
if (cycles > 0) {
scnprintf(cycle_str, sizeof(cycle_str),
"cycles:%" PRId64 "", cycles);
cstr = cycle_str;
} else
str = (char *)null_str;
cstr = (char *)null_str;
predicted_percent = predicted_count * 100.0 / branch_count;
cycles = cycles_count / branch_count;
if ((predicted_percent >= 100.0) && (abort_count == 0)) {
if (fp)
return fprintf(fp, " (cycles:%" PRId64 "%s)",
cycles, str);
if ((predicted_count == branch_count) && (abort_count == 0)) {
if ((cycles > 0) || (istr != (char *)null_str))
return scnprintf(bf, bfsize, " (%s%s)", cstr, istr);
else
return scnprintf(bf, bfsize, "%s", (char *)null_str);
}
return scnprintf(bf, bfsize, " (cycles:%" PRId64 "%s)",
cycles, str);
if ((predicted_count < branch_count) && (abort_count == 0)) {
if ((cycles > 0) || (istr != (char *)null_str))
return scnprintf(bf, bfsize,
" (predicted:%.1f%% %s%s)",
predicted_percent, cstr, istr);
else {
return scnprintf(bf, bfsize,
" (predicted:%.1f%%)",
predicted_percent);
}
}
if ((predicted_percent < 100.0) && (abort_count == 0)) {
if (fp)
return fprintf(fp,
" (predicted:%.1f%%, cycles:%" PRId64 "%s)",
predicted_percent, cycles, str);
if ((predicted_count == branch_count) && (abort_count > 0)) {
if ((cycles > 0) || (istr != (char *)null_str))
return scnprintf(bf, bfsize,
" (abort:%" PRId64 " %s%s)",
abort_count, cstr, istr);
else
return scnprintf(bf, bfsize,
" (abort:%" PRId64 ")",
abort_count);
}
if ((cycles > 0) || (istr != (char *)null_str))
return scnprintf(bf, bfsize,
" (predicted:%.1f%%, cycles:%" PRId64 "%s)",
predicted_percent, cycles, str);
}
" (predicted:%.1f%% abort:%" PRId64 " %s%s)",
predicted_percent, abort_count, cstr, istr);
return scnprintf(bf, bfsize,
" (predicted:%.1f%% abort:%" PRId64 ")",
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,
" (predicted:%.1f%%, abort:%" PRId64 ", cycles:%" PRId64 "%s)",
predicted_percent, abort_count, cycles, str);
return fprintf(fp, "%s", str);
return scnprintf(bf, bfsize,
" (predicted:%.1f%%, abort:%" PRId64 ", cycles:%" PRId64 "%s)",
predicted_percent, abort_count, cycles, str);
return scnprintf(bf, bfsize, "%s", str);
}
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)
{
int ret = -1;
const char *home = NULL;
char *user_config;
struct stat st;
/* Setting $PERF_CONFIG makes perf read _only_ the given config file. */
if (config_exclusive_filename)
......@@ -637,35 +639,41 @@ static int perf_config_set__init(struct perf_config_set *set)
}
home = getenv("HOME");
if (perf_config_global() && home) {
char *user_config = strdup(mkpath("%s/.perfconfig", home));
struct stat st;
if (user_config == NULL) {
warning("Not enough memory to process %s/.perfconfig, "
"ignoring it.", home);
goto out;
}
/*
* 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;
if (stat(user_config, &st) < 0) {
if (errno == ENOENT)
ret = 0;
goto out_free;
}
user_config = strdup(mkpath("%s/.perfconfig", home));
if (user_config == NULL) {
warning("Not enough memory to process %s/.perfconfig, "
"ignoring it.", home);
goto out;
}
if (stat(user_config, &st) < 0) {
if (errno == ENOENT)
ret = 0;
goto out_free;
}
ret = 0;
ret = 0;
if (st.st_uid && (st.st_uid != geteuid())) {
warning("File %s not owned by current user or root, "
"ignoring it.", user_config);
goto out_free;
}
if (st.st_uid && (st.st_uid != geteuid())) {
warning("File %s not owned by current user or root, "
"ignoring it.", user_config);
goto out_free;
}
if (st.st_size)
ret = perf_config_from_file(collect_config, user_config, set);
if (st.st_size)
ret = perf_config_from_file(collect_config, user_config, set);
out_free:
free(user_config);
}
free(user_config);
out:
return ret;
}
......
......@@ -6,16 +6,12 @@
#include "levenshtein.h"
static int autocorrect;
static struct cmdnames aliases;
static int perf_unknown_cmd_config(const char *var, const char *value,
void *cb __maybe_unused)
{
if (!strcmp(var, "help.autocorrect"))
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;
}
......@@ -59,14 +55,12 @@ const char *help_unknown_cmd(const char *cmd)
memset(&main_cmds, 0, sizeof(main_cmds));
memset(&other_cmds, 0, sizeof(main_cmds));
memset(&aliases, 0, sizeof(aliases));
perf_config(perf_unknown_cmd_config, NULL);
load_command_list("perf-", &main_cmds, &other_cmds);
if (add_cmd_list(&main_cmds, &aliases) < 0 ||
add_cmd_list(&main_cmds, &other_cmds) < 0) {
if (add_cmd_list(&main_cmds, &other_cmds) < 0) {
fprintf(stderr, "ERROR: Failed to allocate command list for unknown command.\n");
goto end;
}
......
......@@ -2459,7 +2459,7 @@ int parse_filter_percentage(const struct option *opt __maybe_unused,
else if (!strcmp(arg, "absolute"))
symbol_conf.filter_relative = false;
else {
pr_debug("Invalud percentage: %s\n", arg);
pr_debug("Invalid percentage: %s\n", arg);
return -1;
}
......
......@@ -6,10 +6,10 @@ const struct sample_reg __weak sample_reg_masks[] = {
SMPL_REG_END
};
int __weak sdt_rename_register(char **pdesc __maybe_unused,
char *old_name __maybe_unused)
int __weak arch_sdt_arg_parse_op(char *old_op __maybe_unused,
char **new_op __maybe_unused)
{
return 0;
return SDT_ARG_SKIP;
}
#ifdef HAVE_PERF_REGS_SUPPORT
......
......@@ -15,11 +15,12 @@ struct sample_reg {
extern const struct sample_reg sample_reg_masks[];
/*
* The table sdt_reg_renamings is used for adjusting gcc/gas-generated
* registers before filling the uprobe tracer interface.
*/
int sdt_rename_register(char **pdesc, char *old_name);
enum {
SDT_ARG_VALID = 0,
SDT_ARG_SKIP,
};
int arch_sdt_arg_parse_op(char *old_op, char **new_op);
#ifdef HAVE_PERF_REGS_SUPPORT
#include <perf_regs.h>
......
......@@ -694,10 +694,29 @@ static const char * const type_to_suffix[] = {
"", ":u8", ":u16", "", ":u32", "", "", "", ":u64"
};
/*
* 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)
{
long type_idx;
type_idx = strtol(n_ptr, NULL, 10);
if (type_idx < -8 || type_idx > 8) {
pr_debug4("Failed to get a valid sdt type\n");
return -1;
}
*suffix = type_to_suffix[type_idx + 8];
return 0;
}
static int synthesize_sdt_probe_arg(struct strbuf *buf, int i, const char *arg)
{
char *tmp, *desc = strdup(arg);
const char *prefix = "", *suffix = "";
char *op, *desc = strdup(arg), *new_op = NULL;
const char *suffix = "";
int ret = -1;
if (desc == NULL) {
......@@ -705,112 +724,37 @@ static int synthesize_sdt_probe_arg(struct strbuf *buf, int i, const char *arg)
return ret;
}
tmp = strchr(desc, '@');
if (tmp) {
long type_idx;
/*
* Isolate the string number and convert it into a
* 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) {
pr_debug4("Failed to get a valid sdt type\n");
goto error;
}
suffix = type_to_suffix[type_idx + 8];
/* Get rid of the sdt prefix which is now useless */
tmp++;
memmove(desc, tmp, strlen(tmp) + 1);
}
/*
* The uprobe tracer format does not support all the
* 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.
* Argument is in N@OP format. N is size of the argument and OP is
* the actual assembly operand. N can be omitted; in that case
* argument is just OP(without @).
*/
if (strchr(desc, ',')) {
pr_debug4("Skipping unsupported SDT argument; %s\n", desc);
goto out;
}
op = strchr(desc, '@');
if (op) {
op[0] = '\0';
op++;
/*
* If the argument addressing mode is indirect, we must check
* a few things...
*/
tmp = strchr(desc, '(');
if (tmp) {
int j;
/*
* ...if the addressing mode is indirect with a
* positive offset (ex.: "1608(%ax)"), we need to add
* a '+' prefix so as to be compliant with uprobe
* format.
*/
if (desc[0] != '+' && desc[0] != '-')
prefix = "+";
/*
* ...or if the addressing mode is indirect with a symbol
* as offset, the argument will not be supported by
* 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;
}
}
if (sdt_arg_parse_size(desc, &suffix))
goto error;
} else {
op = desc;
}
/*
* The uprobe tracer format does not support constants; if we
* find one in the current argument, let's skip the argument.
*/
if (strchr(desc, '$')) {
pr_debug4("Skipping unsupported SDT argument; %s\n", desc);
goto out;
}
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;
if (ret < 0)
goto error;
ret = sdt_rename_register(&desc, desc + offset);
if (ret == SDT_ARG_VALID) {
ret = strbuf_addf(buf, " arg%d=%s%s", i + 1, new_op, suffix);
if (ret < 0)
goto error;
/*
* The desc pointer might have changed; so, let's not
* try to reuse tmp for next lookup
*/
tmp = strchr(desc + offset + 1, '%');
}
if (strbuf_addf(buf, " arg%d=%s%s%s", i + 1, prefix, desc, suffix) < 0)
goto error;
out:
ret = 0;
error:
free(desc);
free(new_op);
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