aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexis Berlemont <alexis.berlemont@gmail.com>2016-12-13 19:07:32 -0500
committerArnaldo Carvalho de Melo <acme@redhat.com>2017-03-21 09:59:01 -0400
commit3b1f8311f6963cd11a7d1efbcd2fd900d472ba5c (patch)
treeb50763efb47b2dff1488ee22188c389a0515de14
parentbe88184b1c7054719296387c6063748fb48fa645 (diff)
perf probe: Add sdt probes arguments into the uprobe cmd string
An sdt probe can be associated with arguments but they were not passed to the user probe tracing interface (uprobe_events); this patch adapts the sdt argument descriptors according to the uprobe input format. As the uprobe parser does not support scaled address mode, perf will skip arguments which cannot be adapted to the uprobe format. Here are the results: $ perf buildid-cache -v --add test_sdt $ perf probe -x test_sdt sdt_libfoo:table_frob $ perf probe -x test_sdt sdt_libfoo:table_diddle $ perf record -e sdt_libfoo:table_frob -e sdt_libfoo:table_diddle test_sdt $ perf script test_sdt ... 666.255678: sdt_libfoo:table_frob: (4004d7) arg0=0 arg1=0 test_sdt ... 666.255683: sdt_libfoo:table_diddle: (40051a) arg0=0 arg1=0 test_sdt ... 666.255686: sdt_libfoo:table_frob: (4004d7) arg0=1 arg1=2 test_sdt ... 666.255689: sdt_libfoo:table_diddle: (40051a) arg0=3 arg1=4 test_sdt ... 666.255692: sdt_libfoo:table_frob: (4004d7) arg0=2 arg1=4 test_sdt ... 666.255694: sdt_libfoo:table_diddle: (40051a) arg0=6 arg1=8 Signed-off-by: Alexis Berlemont <alexis.berlemont@gmail.com> Acked-by: Masami Hiramatsu <mhiramat@kernel.org> Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com> Cc: Hemant Kumar <hemant@linux.vnet.ibm.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Ravi Bangoria <ravi.bangoria@linux.vnet.ibm.com> Link: http://lkml.kernel.org/r/20161214000732.1710-3-alexis.berlemont@gmail.com Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
-rw-r--r--tools/perf/arch/x86/util/perf_regs.c83
-rw-r--r--tools/perf/util/perf_regs.c6
-rw-r--r--tools/perf/util/perf_regs.h6
-rw-r--r--tools/perf/util/probe-file.c170
4 files changed, 261 insertions, 4 deletions
diff --git a/tools/perf/arch/x86/util/perf_regs.c b/tools/perf/arch/x86/util/perf_regs.c
index c5db14f36cc7..09a7f556dc7c 100644
--- a/tools/perf/arch/x86/util/perf_regs.c
+++ b/tools/perf/arch/x86/util/perf_regs.c
@@ -1,4 +1,7 @@
1#include <string.h>
2
1#include "../../perf.h" 3#include "../../perf.h"
4#include "../../util/util.h"
2#include "../../util/perf_regs.h" 5#include "../../util/perf_regs.h"
3 6
4const struct sample_reg sample_reg_masks[] = { 7const struct sample_reg sample_reg_masks[] = {
@@ -26,3 +29,83 @@ const struct sample_reg sample_reg_masks[] = {
26#endif 29#endif
27 SMPL_REG_END 30 SMPL_REG_END
28}; 31};
32
33struct sdt_name_reg {
34 const char *sdt_name;
35 const char *uprobe_name;
36};
37#define SDT_NAME_REG(n, m) {.sdt_name = "%" #n, .uprobe_name = "%" #m}
38#define SDT_NAME_REG_END {.sdt_name = NULL, .uprobe_name = NULL}
39
40static const struct sdt_name_reg sdt_reg_renamings[] = {
41 SDT_NAME_REG(eax, ax),
42 SDT_NAME_REG(rax, ax),
43 SDT_NAME_REG(ebx, bx),
44 SDT_NAME_REG(rbx, bx),
45 SDT_NAME_REG(ecx, cx),
46 SDT_NAME_REG(rcx, cx),
47 SDT_NAME_REG(edx, dx),
48 SDT_NAME_REG(rdx, dx),
49 SDT_NAME_REG(esi, si),
50 SDT_NAME_REG(rsi, si),
51 SDT_NAME_REG(edi, di),
52 SDT_NAME_REG(rdi, di),
53 SDT_NAME_REG(ebp, bp),
54 SDT_NAME_REG(rbp, bp),
55 SDT_NAME_REG_END,
56};
57
58int sdt_rename_register(char **pdesc, char *old_name)
59{
60 const struct sdt_name_reg *rnames = sdt_reg_renamings;
61 char *new_desc, *old_desc = *pdesc;
62 size_t prefix_len, sdt_len, uprobe_len, old_desc_len, offset;
63 int ret = -1;
64
65 while (ret != 0 && rnames->sdt_name != NULL) {
66 sdt_len = strlen(rnames->sdt_name);
67 ret = strncmp(old_name, rnames->sdt_name, sdt_len);
68 rnames += !!ret;
69 }
70
71 if (rnames->sdt_name == NULL)
72 return 0;
73
74 sdt_len = strlen(rnames->sdt_name);
75 uprobe_len = strlen(rnames->uprobe_name);
76 old_desc_len = strlen(old_desc) + 1;
77
78 new_desc = zalloc(old_desc_len + uprobe_len - sdt_len);
79 if (new_desc == NULL)
80 return -1;
81
82 /* Copy the chars before the register name (at least '%') */
83 prefix_len = old_name - old_desc;
84 memcpy(new_desc, old_desc, prefix_len);
85
86 /* Copy the new register name */
87 memcpy(new_desc + prefix_len, rnames->uprobe_name, uprobe_len);
88
89 /* Copy the chars after the register name (if need be) */
90 offset = prefix_len + sdt_len;
91 if (offset < old_desc_len) {
92 /*
93 * The orginal register name can be suffixed by 'b',
94 * 'w' or 'd' to indicate its size; so, we need to
95 * skip this char if we met one.
96 */
97 char sfx = old_desc[offset];
98
99 if (sfx == 'b' || sfx == 'w' || sfx == 'd')
100 offset++;
101 }
102
103 if (offset < old_desc_len)
104 memcpy(new_desc + prefix_len + uprobe_len,
105 old_desc + offset, old_desc_len - offset);
106
107 free(old_desc);
108 *pdesc = new_desc;
109
110 return 0;
111}
diff --git a/tools/perf/util/perf_regs.c b/tools/perf/util/perf_regs.c
index c4023f22f287..a37e5934aa2a 100644
--- a/tools/perf/util/perf_regs.c
+++ b/tools/perf/util/perf_regs.c
@@ -6,6 +6,12 @@ const struct sample_reg __weak sample_reg_masks[] = {
6 SMPL_REG_END 6 SMPL_REG_END
7}; 7};
8 8
9int __weak sdt_rename_register(char **pdesc __maybe_unused,
10 char *old_name __maybe_unused)
11{
12 return 0;
13}
14
9#ifdef HAVE_PERF_REGS_SUPPORT 15#ifdef HAVE_PERF_REGS_SUPPORT
10int perf_reg_value(u64 *valp, struct regs_dump *regs, int id) 16int perf_reg_value(u64 *valp, struct regs_dump *regs, int id)
11{ 17{
diff --git a/tools/perf/util/perf_regs.h b/tools/perf/util/perf_regs.h
index 679d6e493962..7544a157e159 100644
--- a/tools/perf/util/perf_regs.h
+++ b/tools/perf/util/perf_regs.h
@@ -15,6 +15,12 @@ struct sample_reg {
15 15
16extern const struct sample_reg sample_reg_masks[]; 16extern const struct sample_reg sample_reg_masks[];
17 17
18/*
19 * The table sdt_reg_renamings is used for adjusting gcc/gas-generated
20 * registers before filling the uprobe tracer interface.
21 */
22int sdt_rename_register(char **pdesc, char *old_name);
23
18#ifdef HAVE_PERF_REGS_SUPPORT 24#ifdef HAVE_PERF_REGS_SUPPORT
19#include <perf_regs.h> 25#include <perf_regs.h>
20 26
diff --git a/tools/perf/util/probe-file.c b/tools/perf/util/probe-file.c
index c3c287125be5..d741634cbfc0 100644
--- a/tools/perf/util/probe-file.c
+++ b/tools/perf/util/probe-file.c
@@ -27,6 +27,7 @@
27#include "probe-event.h" 27#include "probe-event.h"
28#include "probe-file.h" 28#include "probe-file.h"
29#include "session.h" 29#include "session.h"
30#include "perf_regs.h"
30 31
31/* 4096 - 2 ('\n' + '\0') */ 32/* 4096 - 2 ('\n' + '\0') */
32#define MAX_CMDLEN 4094 33#define MAX_CMDLEN 4094
@@ -688,6 +689,166 @@ static unsigned long long sdt_note__get_addr(struct sdt_note *note)
688 : (unsigned long long)note->addr.a64[0]; 689 : (unsigned long long)note->addr.a64[0];
689} 690}
690 691
692static const char * const type_to_suffix[] = {
693 ":s64", "", "", "", ":s32", "", ":s16", ":s8",
694 "", ":u8", ":u16", "", ":u32", "", "", "", ":u64"
695};
696
697static int synthesize_sdt_probe_arg(struct strbuf *buf, int i, const char *arg)
698{
699 char *tmp, *desc = strdup(arg);
700 const char *prefix = "", *suffix = "";
701 int ret = -1;
702
703 if (desc == NULL) {
704 pr_debug4("Allocation error\n");
705 return ret;
706 }
707
708 tmp = strchr(desc, '@');
709 if (tmp) {
710 long type_idx;
711 /*
712 * Isolate the string number and convert it into a
713 * binary value; this will be an index to get suffix
714 * of the uprobe name (defining the type)
715 */
716 tmp[0] = '\0';
717 type_idx = strtol(desc, NULL, 10);
718 /* Check that the conversion went OK */
719 if (type_idx == LONG_MIN || type_idx == LONG_MAX) {
720 pr_debug4("Failed to parse sdt type\n");
721 goto error;
722 }
723 /* Check that the converted value is OK */
724 if (type_idx < -8 || type_idx > 8) {
725 pr_debug4("Failed to get a valid sdt type\n");
726 goto error;
727 }
728 suffix = type_to_suffix[type_idx + 8];
729 /* Get rid of the sdt prefix which is now useless */
730 tmp++;
731 memmove(desc, tmp, strlen(tmp) + 1);
732 }
733
734 /*
735 * The uprobe tracer format does not support all the
736 * addressing modes (notably: in x86 the scaled mode); so, we
737 * detect ',' characters, if there is just one, there is no
738 * use converting the sdt arg into a uprobe one.
739 */
740 if (strchr(desc, ',')) {
741 pr_debug4("Skipping unsupported SDT argument; %s\n", desc);
742 goto out;
743 }
744
745 /*
746 * If the argument addressing mode is indirect, we must check
747 * a few things...
748 */
749 tmp = strchr(desc, '(');
750 if (tmp) {
751 int j;
752
753 /*
754 * ...if the addressing mode is indirect with a
755 * positive offset (ex.: "1608(%ax)"), we need to add
756 * a '+' prefix so as to be compliant with uprobe
757 * format.
758 */
759 if (desc[0] != '+' && desc[0] != '-')
760 prefix = "+";
761
762 /*
763 * ...or if the addressing mode is indirect with a symbol
764 * as offset, the argument will not be supported by
765 * the uprobe tracer format; so, let's skip this one.
766 */
767 for (j = 0; j < tmp - desc; j++) {
768 if (desc[j] != '+' && desc[j] != '-' &&
769 !isdigit(desc[j])) {
770 pr_debug4("Skipping unsupported SDT argument; "
771 "%s\n", desc);
772 goto out;
773 }
774 }
775 }
776
777 /*
778 * The uprobe tracer format does not support constants; if we
779 * find one in the current argument, let's skip the argument.
780 */
781 if (strchr(desc, '$')) {
782 pr_debug4("Skipping unsupported SDT argument; %s\n", desc);
783 goto out;
784 }
785
786 /*
787 * The uprobe parser does not support all gas register names;
788 * so, we have to replace them (ex. for x86_64: %rax -> %ax);
789 * the loop below looks for the register names (starting with
790 * a '%' and tries to perform the needed renamings.
791 */
792 tmp = strchr(desc, '%');
793 while (tmp) {
794 size_t offset = tmp - desc;
795
796 ret = sdt_rename_register(&desc, desc + offset);
797 if (ret < 0)
798 goto error;
799
800 /*
801 * The desc pointer might have changed; so, let's not
802 * try to reuse tmp for next lookup
803 */
804 tmp = strchr(desc + offset + 1, '%');
805 }
806
807 if (strbuf_addf(buf, " arg%d=%s%s%s", i + 1, prefix, desc, suffix) < 0)
808 goto error;
809
810out:
811 ret = 0;
812error:
813 free(desc);
814 return ret;
815}
816
817static char *synthesize_sdt_probe_command(struct sdt_note *note,
818 const char *pathname,
819 const char *sdtgrp)
820{
821 struct strbuf buf;
822 char *ret = NULL, **args;
823 int i, args_count;
824
825 if (strbuf_init(&buf, 32) < 0)
826 return NULL;
827
828 if (strbuf_addf(&buf, "p:%s/%s %s:0x%llx",
829 sdtgrp, note->name, pathname,
830 sdt_note__get_addr(note)) < 0)
831 goto error;
832
833 if (!note->args)
834 goto out;
835
836 if (note->args) {
837 args = argv_split(note->args, &args_count);
838
839 for (i = 0; i < args_count; ++i) {
840 if (synthesize_sdt_probe_arg(&buf, i, args[i]) < 0)
841 goto error;
842 }
843 }
844
845out:
846 ret = strbuf_detach(&buf, NULL);
847error:
848 strbuf_release(&buf);
849 return ret;
850}
851
691int probe_cache__scan_sdt(struct probe_cache *pcache, const char *pathname) 852int probe_cache__scan_sdt(struct probe_cache *pcache, const char *pathname)
692{ 853{
693 struct probe_cache_entry *entry = NULL; 854 struct probe_cache_entry *entry = NULL;
@@ -724,11 +885,12 @@ int probe_cache__scan_sdt(struct probe_cache *pcache, const char *pathname)
724 entry->pev.group = strdup(sdtgrp); 885 entry->pev.group = strdup(sdtgrp);
725 list_add_tail(&entry->node, &pcache->entries); 886 list_add_tail(&entry->node, &pcache->entries);
726 } 887 }
727 ret = asprintf(&buf, "p:%s/%s %s:0x%llx", 888 buf = synthesize_sdt_probe_command(note, pathname, sdtgrp);
728 sdtgrp, note->name, pathname, 889 if (!buf) {
729 sdt_note__get_addr(note)); 890 ret = -ENOMEM;
730 if (ret < 0)
731 break; 891 break;
892 }
893
732 strlist__add(entry->tevlist, buf); 894 strlist__add(entry->tevlist, buf);
733 free(buf); 895 free(buf);
734 entry = NULL; 896 entry = NULL;