diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-03-05 13:50:22 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-03-05 13:50:22 -0500 |
| commit | 660f6a360be399f4ebdd6572a3d24afe54e9bb1c (patch) | |
| tree | 9c16463c495a656e34577d59c97b58997b61d242 /tools/perf/util | |
| parent | 586fac13f8685bf9dfb32e1ee98bfb14f0dd0061 (diff) | |
| parent | e5a11016643d1ab7172193591506d33a844734cc (diff) | |
Merge branch 'perf-probes-for-linus-2' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip
* 'perf-probes-for-linus-2' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip:
x86: Issue at least one memory barrier in stop_machine_text_poke()
perf probe: Correct probe syntax on command line help
perf probe: Add lazy line matching support
perf probe: Show more lines after last line
perf probe: Check function address range strictly in line finder
perf probe: Use libdw callback routines
perf probe: Use elfutils-libdw for analyzing debuginfo
perf probe: Rename probe finder functions
perf probe: Fix bugs in line range finder
perf probe: Update perf probe document
perf probe: Do not show --line option without dwarf support
kprobes: Add documents of jump optimization
kprobes/x86: Support kprobes jump optimization on x86
x86: Add text_poke_smp for SMP cross modifying code
kprobes/x86: Cleanup save/restore registers
kprobes/x86: Boost probes when reentering
kprobes: Jump optimization sysctl interface
kprobes: Introduce kprobes jump optimization
kprobes: Introduce generic insn_slot framework
kprobes/x86: Cleanup RELATIVEJUMP_INSTRUCTION to RELATIVEJUMP_OPCODE
Diffstat (limited to 'tools/perf/util')
| -rw-r--r-- | tools/perf/util/probe-event.c | 55 | ||||
| -rw-r--r-- | tools/perf/util/probe-finder.c | 1002 | ||||
| -rw-r--r-- | tools/perf/util/probe-finder.h | 53 | ||||
| -rw-r--r-- | tools/perf/util/string.c | 55 | ||||
| -rw-r--r-- | tools/perf/util/string.h | 1 |
5 files changed, 566 insertions, 600 deletions
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 8f0568849691..c971e81e9cbf 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
| @@ -119,14 +119,14 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) | |||
| 119 | char c, nc = 0; | 119 | char c, nc = 0; |
| 120 | /* | 120 | /* |
| 121 | * <Syntax> | 121 | * <Syntax> |
| 122 | * perf probe [EVENT=]SRC:LN | 122 | * perf probe [EVENT=]SRC[:LN|;PTN] |
| 123 | * perf probe [EVENT=]FUNC[+OFFS|%return][@SRC] | 123 | * perf probe [EVENT=]FUNC[@SRC][+OFFS|%return|:LN|;PAT] |
| 124 | * | 124 | * |
| 125 | * TODO:Group name support | 125 | * TODO:Group name support |
| 126 | */ | 126 | */ |
| 127 | 127 | ||
| 128 | ptr = strchr(arg, '='); | 128 | ptr = strpbrk(arg, ";=@+%"); |
| 129 | if (ptr) { /* Event name */ | 129 | if (ptr && *ptr == '=') { /* Event name */ |
| 130 | *ptr = '\0'; | 130 | *ptr = '\0'; |
| 131 | tmp = ptr + 1; | 131 | tmp = ptr + 1; |
| 132 | ptr = strchr(arg, ':'); | 132 | ptr = strchr(arg, ':'); |
| @@ -139,7 +139,7 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) | |||
| 139 | arg = tmp; | 139 | arg = tmp; |
| 140 | } | 140 | } |
| 141 | 141 | ||
| 142 | ptr = strpbrk(arg, ":+@%"); | 142 | ptr = strpbrk(arg, ";:+@%"); |
| 143 | if (ptr) { | 143 | if (ptr) { |
| 144 | nc = *ptr; | 144 | nc = *ptr; |
| 145 | *ptr++ = '\0'; | 145 | *ptr++ = '\0'; |
| @@ -156,7 +156,11 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) | |||
| 156 | while (ptr) { | 156 | while (ptr) { |
| 157 | arg = ptr; | 157 | arg = ptr; |
| 158 | c = nc; | 158 | c = nc; |
| 159 | ptr = strpbrk(arg, ":+@%"); | 159 | if (c == ';') { /* Lazy pattern must be the last part */ |
| 160 | pp->lazy_line = strdup(arg); | ||
| 161 | break; | ||
| 162 | } | ||
| 163 | ptr = strpbrk(arg, ";:+@%"); | ||
| 160 | if (ptr) { | 164 | if (ptr) { |
| 161 | nc = *ptr; | 165 | nc = *ptr; |
| 162 | *ptr++ = '\0'; | 166 | *ptr++ = '\0'; |
| @@ -165,13 +169,13 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) | |||
| 165 | case ':': /* Line number */ | 169 | case ':': /* Line number */ |
| 166 | pp->line = strtoul(arg, &tmp, 0); | 170 | pp->line = strtoul(arg, &tmp, 0); |
| 167 | if (*tmp != '\0') | 171 | if (*tmp != '\0') |
| 168 | semantic_error("There is non-digit charactor" | 172 | semantic_error("There is non-digit char" |
| 169 | " in line number."); | 173 | " in line number."); |
| 170 | break; | 174 | break; |
| 171 | case '+': /* Byte offset from a symbol */ | 175 | case '+': /* Byte offset from a symbol */ |
| 172 | pp->offset = strtoul(arg, &tmp, 0); | 176 | pp->offset = strtoul(arg, &tmp, 0); |
| 173 | if (*tmp != '\0') | 177 | if (*tmp != '\0') |
| 174 | semantic_error("There is non-digit charactor" | 178 | semantic_error("There is non-digit character" |
| 175 | " in offset."); | 179 | " in offset."); |
| 176 | break; | 180 | break; |
| 177 | case '@': /* File name */ | 181 | case '@': /* File name */ |
| @@ -179,9 +183,6 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) | |||
| 179 | semantic_error("SRC@SRC is not allowed."); | 183 | semantic_error("SRC@SRC is not allowed."); |
| 180 | pp->file = strdup(arg); | 184 | pp->file = strdup(arg); |
| 181 | DIE_IF(pp->file == NULL); | 185 | DIE_IF(pp->file == NULL); |
| 182 | if (ptr) | ||
| 183 | semantic_error("@SRC must be the last " | ||
| 184 | "option."); | ||
| 185 | break; | 186 | break; |
| 186 | case '%': /* Probe places */ | 187 | case '%': /* Probe places */ |
| 187 | if (strcmp(arg, "return") == 0) { | 188 | if (strcmp(arg, "return") == 0) { |
| @@ -196,11 +197,18 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) | |||
| 196 | } | 197 | } |
| 197 | 198 | ||
| 198 | /* Exclusion check */ | 199 | /* Exclusion check */ |
| 200 | if (pp->lazy_line && pp->line) | ||
| 201 | semantic_error("Lazy pattern can't be used with line number."); | ||
| 202 | |||
| 203 | if (pp->lazy_line && pp->offset) | ||
| 204 | semantic_error("Lazy pattern can't be used with offset."); | ||
| 205 | |||
| 199 | if (pp->line && pp->offset) | 206 | if (pp->line && pp->offset) |
| 200 | semantic_error("Offset can't be used with line number."); | 207 | semantic_error("Offset can't be used with line number."); |
| 201 | 208 | ||
| 202 | if (!pp->line && pp->file && !pp->function) | 209 | if (!pp->line && !pp->lazy_line && pp->file && !pp->function) |
| 203 | semantic_error("File always requires line number."); | 210 | semantic_error("File always requires line number or " |
| 211 | "lazy pattern."); | ||
| 204 | 212 | ||
| 205 | if (pp->offset && !pp->function) | 213 | if (pp->offset && !pp->function) |
| 206 | semantic_error("Offset requires an entry function."); | 214 | semantic_error("Offset requires an entry function."); |
| @@ -208,11 +216,13 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) | |||
| 208 | if (pp->retprobe && !pp->function) | 216 | if (pp->retprobe && !pp->function) |
| 209 | semantic_error("Return probe requires an entry function."); | 217 | semantic_error("Return probe requires an entry function."); |
| 210 | 218 | ||
| 211 | if ((pp->offset || pp->line) && pp->retprobe) | 219 | if ((pp->offset || pp->line || pp->lazy_line) && pp->retprobe) |
| 212 | semantic_error("Offset/Line can't be used with return probe."); | 220 | semantic_error("Offset/Line/Lazy pattern can't be used with " |
| 221 | "return probe."); | ||
| 213 | 222 | ||
| 214 | pr_debug("symbol:%s file:%s line:%d offset:%d, return:%d\n", | 223 | pr_debug("symbol:%s file:%s line:%d offset:%d return:%d lazy:%s\n", |
| 215 | pp->function, pp->file, pp->line, pp->offset, pp->retprobe); | 224 | pp->function, pp->file, pp->line, pp->offset, pp->retprobe, |
| 225 | pp->lazy_line); | ||
| 216 | } | 226 | } |
| 217 | 227 | ||
| 218 | /* Parse perf-probe event definition */ | 228 | /* Parse perf-probe event definition */ |
| @@ -458,6 +468,8 @@ static void clear_probe_point(struct probe_point *pp) | |||
| 458 | free(pp->function); | 468 | free(pp->function); |
| 459 | if (pp->file) | 469 | if (pp->file) |
| 460 | free(pp->file); | 470 | free(pp->file); |
| 471 | if (pp->lazy_line) | ||
| 472 | free(pp->lazy_line); | ||
| 461 | for (i = 0; i < pp->nr_args; i++) | 473 | for (i = 0; i < pp->nr_args; i++) |
| 462 | free(pp->args[i]); | 474 | free(pp->args[i]); |
| 463 | if (pp->args) | 475 | if (pp->args) |
| @@ -719,6 +731,7 @@ void del_trace_kprobe_events(struct strlist *dellist) | |||
| 719 | } | 731 | } |
| 720 | 732 | ||
| 721 | #define LINEBUF_SIZE 256 | 733 | #define LINEBUF_SIZE 256 |
| 734 | #define NR_ADDITIONAL_LINES 2 | ||
| 722 | 735 | ||
| 723 | static void show_one_line(FILE *fp, unsigned int l, bool skip, bool show_num) | 736 | static void show_one_line(FILE *fp, unsigned int l, bool skip, bool show_num) |
| 724 | { | 737 | { |
| @@ -779,5 +792,11 @@ void show_line_range(struct line_range *lr) | |||
| 779 | show_one_line(fp, (l++) - lr->offset, false, false); | 792 | show_one_line(fp, (l++) - lr->offset, false, false); |
| 780 | show_one_line(fp, (l++) - lr->offset, false, true); | 793 | show_one_line(fp, (l++) - lr->offset, false, true); |
| 781 | } | 794 | } |
| 795 | |||
| 796 | if (lr->end == INT_MAX) | ||
| 797 | lr->end = l + NR_ADDITIONAL_LINES; | ||
| 798 | while (l < lr->end && !feof(fp)) | ||
| 799 | show_one_line(fp, (l++) - lr->offset, false, false); | ||
| 800 | |||
| 782 | fclose(fp); | 801 | fclose(fp); |
| 783 | } | 802 | } |
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 1b2124d12f68..e77dc886760e 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
| @@ -32,21 +32,13 @@ | |||
| 32 | #include <stdarg.h> | 32 | #include <stdarg.h> |
| 33 | #include <ctype.h> | 33 | #include <ctype.h> |
| 34 | 34 | ||
| 35 | #include "string.h" | ||
| 35 | #include "event.h" | 36 | #include "event.h" |
| 36 | #include "debug.h" | 37 | #include "debug.h" |
| 37 | #include "util.h" | 38 | #include "util.h" |
| 38 | #include "probe-finder.h" | 39 | #include "probe-finder.h" |
| 39 | 40 | ||
| 40 | 41 | ||
| 41 | /* Dwarf_Die Linkage to parent Die */ | ||
| 42 | struct die_link { | ||
| 43 | struct die_link *parent; /* Parent die */ | ||
| 44 | Dwarf_Die die; /* Current die */ | ||
| 45 | }; | ||
| 46 | |||
| 47 | static Dwarf_Debug __dw_debug; | ||
| 48 | static Dwarf_Error __dw_error; | ||
| 49 | |||
| 50 | /* | 42 | /* |
| 51 | * Generic dwarf analysis helpers | 43 | * Generic dwarf analysis helpers |
| 52 | */ | 44 | */ |
| @@ -113,281 +105,190 @@ static int strtailcmp(const char *s1, const char *s2) | |||
| 113 | return 0; | 105 | return 0; |
| 114 | } | 106 | } |
| 115 | 107 | ||
| 116 | /* Find the fileno of the target file. */ | 108 | /* Line number list operations */ |
| 117 | static Dwarf_Unsigned cu_find_fileno(Dwarf_Die cu_die, const char *fname) | ||
| 118 | { | ||
| 119 | Dwarf_Signed cnt, i; | ||
| 120 | Dwarf_Unsigned found = 0; | ||
| 121 | char **srcs; | ||
| 122 | int ret; | ||
| 123 | 109 | ||
| 124 | if (!fname) | 110 | /* Add a line to line number list */ |
| 125 | return 0; | 111 | static void line_list__add_line(struct list_head *head, unsigned int line) |
| 112 | { | ||
| 113 | struct line_node *ln; | ||
| 114 | struct list_head *p; | ||
| 126 | 115 | ||
| 127 | ret = dwarf_srcfiles(cu_die, &srcs, &cnt, &__dw_error); | 116 | /* Reverse search, because new line will be the last one */ |
| 128 | if (ret == DW_DLV_OK) { | 117 | list_for_each_entry_reverse(ln, head, list) { |
| 129 | for (i = 0; i < cnt && !found; i++) { | 118 | if (ln->line < line) { |
| 130 | if (strtailcmp(srcs[i], fname) == 0) | 119 | p = &ln->list; |
| 131 | found = i + 1; | 120 | goto found; |
| 132 | dwarf_dealloc(__dw_debug, srcs[i], DW_DLA_STRING); | 121 | } else if (ln->line == line) /* Already exist */ |
| 133 | } | 122 | return ; |
| 134 | for (; i < cnt; i++) | ||
| 135 | dwarf_dealloc(__dw_debug, srcs[i], DW_DLA_STRING); | ||
| 136 | dwarf_dealloc(__dw_debug, srcs, DW_DLA_LIST); | ||
| 137 | } | 123 | } |
| 138 | if (found) | 124 | /* List is empty, or the smallest entry */ |
| 139 | pr_debug("found fno: %d\n", (int)found); | 125 | p = head; |
| 140 | return found; | 126 | found: |
| 127 | pr_debug("line list: add a line %u\n", line); | ||
| 128 | ln = zalloc(sizeof(struct line_node)); | ||
| 129 | DIE_IF(ln == NULL); | ||
| 130 | ln->line = line; | ||
| 131 | INIT_LIST_HEAD(&ln->list); | ||
| 132 | list_add(&ln->list, p); | ||
| 141 | } | 133 | } |
| 142 | 134 | ||
| 143 | static int cu_get_filename(Dwarf_Die cu_die, Dwarf_Unsigned fno, char **buf) | 135 | /* Check if the line in line number list */ |
| 136 | static int line_list__has_line(struct list_head *head, unsigned int line) | ||
| 144 | { | 137 | { |
| 145 | Dwarf_Signed cnt, i; | 138 | struct line_node *ln; |
| 146 | char **srcs; | 139 | |
| 147 | int ret = 0; | 140 | /* Reverse search, because new line will be the last one */ |
| 148 | 141 | list_for_each_entry(ln, head, list) | |
| 149 | if (!buf || !fno) | 142 | if (ln->line == line) |
| 150 | return -EINVAL; | 143 | return 1; |
| 151 | 144 | ||
| 152 | ret = dwarf_srcfiles(cu_die, &srcs, &cnt, &__dw_error); | 145 | return 0; |
| 153 | if (ret == DW_DLV_OK) { | ||
| 154 | if ((Dwarf_Unsigned)cnt > fno - 1) { | ||
| 155 | *buf = strdup(srcs[fno - 1]); | ||
| 156 | ret = 0; | ||
| 157 | pr_debug("found filename: %s\n", *buf); | ||
| 158 | } else | ||
| 159 | ret = -ENOENT; | ||
| 160 | for (i = 0; i < cnt; i++) | ||
| 161 | dwarf_dealloc(__dw_debug, srcs[i], DW_DLA_STRING); | ||
| 162 | dwarf_dealloc(__dw_debug, srcs, DW_DLA_LIST); | ||
| 163 | } else | ||
| 164 | ret = -EINVAL; | ||
| 165 | return ret; | ||
| 166 | } | 146 | } |
| 167 | 147 | ||
| 168 | /* Compare diename and tname */ | 148 | /* Init line number list */ |
| 169 | static int die_compare_name(Dwarf_Die dw_die, const char *tname) | 149 | static void line_list__init(struct list_head *head) |
| 170 | { | 150 | { |
| 171 | char *name; | 151 | INIT_LIST_HEAD(head); |
| 172 | int ret; | ||
| 173 | ret = dwarf_diename(dw_die, &name, &__dw_error); | ||
| 174 | DIE_IF(ret == DW_DLV_ERROR); | ||
| 175 | if (ret == DW_DLV_OK) { | ||
| 176 | ret = strcmp(tname, name); | ||
| 177 | dwarf_dealloc(__dw_debug, name, DW_DLA_STRING); | ||
| 178 | } else | ||
| 179 | ret = -1; | ||
| 180 | return ret; | ||
| 181 | } | 152 | } |
| 182 | 153 | ||
| 183 | /* Check the address is in the subprogram(function). */ | 154 | /* Free line number list */ |
| 184 | static int die_within_subprogram(Dwarf_Die sp_die, Dwarf_Addr addr, | 155 | static void line_list__free(struct list_head *head) |
| 185 | Dwarf_Signed *offs) | ||
| 186 | { | 156 | { |
| 187 | Dwarf_Addr lopc, hipc; | 157 | struct line_node *ln; |
| 188 | int ret; | 158 | while (!list_empty(head)) { |
| 189 | 159 | ln = list_first_entry(head, struct line_node, list); | |
| 190 | /* TODO: check ranges */ | 160 | list_del(&ln->list); |
| 191 | ret = dwarf_lowpc(sp_die, &lopc, &__dw_error); | 161 | free(ln); |
| 192 | DIE_IF(ret == DW_DLV_ERROR); | 162 | } |
| 193 | if (ret == DW_DLV_NO_ENTRY) | ||
| 194 | return 0; | ||
| 195 | ret = dwarf_highpc(sp_die, &hipc, &__dw_error); | ||
| 196 | DIE_IF(ret != DW_DLV_OK); | ||
| 197 | if (lopc <= addr && addr < hipc) { | ||
| 198 | *offs = addr - lopc; | ||
| 199 | return 1; | ||
| 200 | } else | ||
| 201 | return 0; | ||
| 202 | } | 163 | } |
| 203 | 164 | ||
| 204 | /* Check the die is inlined function */ | 165 | /* Dwarf wrappers */ |
| 205 | static Dwarf_Bool die_inlined_subprogram(Dwarf_Die dw_die) | 166 | |
| 167 | /* Find the realpath of the target file. */ | ||
| 168 | static const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname) | ||
| 206 | { | 169 | { |
| 207 | /* TODO: check strictly */ | 170 | Dwarf_Files *files; |
| 208 | Dwarf_Bool inl; | 171 | size_t nfiles, i; |
| 172 | const char *src; | ||
| 209 | int ret; | 173 | int ret; |
| 210 | 174 | ||
| 211 | ret = dwarf_hasattr(dw_die, DW_AT_inline, &inl, &__dw_error); | 175 | if (!fname) |
| 212 | DIE_IF(ret == DW_DLV_ERROR); | 176 | return NULL; |
| 213 | return inl; | ||
| 214 | } | ||
| 215 | 177 | ||
| 216 | /* Get the offset of abstruct_origin */ | 178 | ret = dwarf_getsrcfiles(cu_die, &files, &nfiles); |
| 217 | static Dwarf_Off die_get_abstract_origin(Dwarf_Die dw_die) | 179 | if (ret != 0) |
| 218 | { | 180 | return NULL; |
| 219 | Dwarf_Attribute attr; | ||
| 220 | Dwarf_Off cu_offs; | ||
| 221 | int ret; | ||
| 222 | 181 | ||
| 223 | ret = dwarf_attr(dw_die, DW_AT_abstract_origin, &attr, &__dw_error); | 182 | for (i = 0; i < nfiles; i++) { |
| 224 | DIE_IF(ret != DW_DLV_OK); | 183 | src = dwarf_filesrc(files, i, NULL, NULL); |
| 225 | ret = dwarf_formref(attr, &cu_offs, &__dw_error); | 184 | if (strtailcmp(src, fname) == 0) |
| 226 | DIE_IF(ret != DW_DLV_OK); | 185 | break; |
| 227 | dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR); | 186 | } |
| 228 | return cu_offs; | 187 | return src; |
| 229 | } | 188 | } |
| 230 | 189 | ||
| 231 | /* Get entry pc(or low pc, 1st entry of ranges) of the die */ | 190 | struct __addr_die_search_param { |
| 232 | static Dwarf_Addr die_get_entrypc(Dwarf_Die dw_die) | 191 | Dwarf_Addr addr; |
| 192 | Dwarf_Die *die_mem; | ||
| 193 | }; | ||
| 194 | |||
| 195 | static int __die_search_func_cb(Dwarf_Die *fn_die, void *data) | ||
| 233 | { | 196 | { |
| 234 | Dwarf_Attribute attr; | 197 | struct __addr_die_search_param *ad = data; |
| 235 | Dwarf_Addr addr; | ||
| 236 | Dwarf_Off offs; | ||
| 237 | Dwarf_Ranges *ranges; | ||
| 238 | Dwarf_Signed cnt; | ||
| 239 | int ret; | ||
| 240 | 198 | ||
| 241 | /* Try to get entry pc */ | 199 | if (dwarf_tag(fn_die) == DW_TAG_subprogram && |
| 242 | ret = dwarf_attr(dw_die, DW_AT_entry_pc, &attr, &__dw_error); | 200 | dwarf_haspc(fn_die, ad->addr)) { |
| 243 | DIE_IF(ret == DW_DLV_ERROR); | 201 | memcpy(ad->die_mem, fn_die, sizeof(Dwarf_Die)); |
| 244 | if (ret == DW_DLV_OK) { | 202 | return DWARF_CB_ABORT; |
| 245 | ret = dwarf_formaddr(attr, &addr, &__dw_error); | ||
| 246 | DIE_IF(ret != DW_DLV_OK); | ||
| 247 | dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR); | ||
| 248 | return addr; | ||
| 249 | } | 203 | } |
| 204 | return DWARF_CB_OK; | ||
| 205 | } | ||
| 250 | 206 | ||
| 251 | /* Try to get low pc */ | 207 | /* Search a real subprogram including this line, */ |
| 252 | ret = dwarf_lowpc(dw_die, &addr, &__dw_error); | 208 | static Dwarf_Die *die_get_real_subprogram(Dwarf_Die *cu_die, Dwarf_Addr addr, |
| 253 | DIE_IF(ret == DW_DLV_ERROR); | 209 | Dwarf_Die *die_mem) |
| 254 | if (ret == DW_DLV_OK) | 210 | { |
| 255 | return addr; | 211 | struct __addr_die_search_param ad; |
| 256 | 212 | ad.addr = addr; | |
| 257 | /* Try to get ranges */ | 213 | ad.die_mem = die_mem; |
| 258 | ret = dwarf_attr(dw_die, DW_AT_ranges, &attr, &__dw_error); | 214 | /* dwarf_getscopes can't find subprogram. */ |
| 259 | DIE_IF(ret != DW_DLV_OK); | 215 | if (!dwarf_getfuncs(cu_die, __die_search_func_cb, &ad, 0)) |
| 260 | ret = dwarf_formref(attr, &offs, &__dw_error); | 216 | return NULL; |
| 261 | DIE_IF(ret != DW_DLV_OK); | 217 | else |
| 262 | ret = dwarf_get_ranges(__dw_debug, offs, &ranges, &cnt, NULL, | 218 | return die_mem; |
| 263 | &__dw_error); | ||
| 264 | DIE_IF(ret != DW_DLV_OK); | ||
| 265 | addr = ranges[0].dwr_addr1; | ||
| 266 | dwarf_ranges_dealloc(__dw_debug, ranges, cnt); | ||
| 267 | return addr; | ||
| 268 | } | 219 | } |
| 269 | 220 | ||
| 270 | /* | 221 | /* Similar to dwarf_getfuncs, but returns inlined_subroutine if exists. */ |
| 271 | * Search a Die from Die tree. | 222 | static Dwarf_Die *die_get_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, |
| 272 | * Note: cur_link->die should be deallocated in this function. | 223 | Dwarf_Die *die_mem) |
| 273 | */ | ||
| 274 | static int __search_die_tree(struct die_link *cur_link, | ||
| 275 | int (*die_cb)(struct die_link *, void *), | ||
| 276 | void *data) | ||
| 277 | { | 224 | { |
| 278 | Dwarf_Die new_die; | 225 | Dwarf_Die child_die; |
| 279 | struct die_link new_link; | ||
| 280 | int ret; | 226 | int ret; |
| 281 | 227 | ||
| 282 | if (!die_cb) | 228 | ret = dwarf_child(sp_die, die_mem); |
| 283 | return 0; | 229 | if (ret != 0) |
| 284 | 230 | return NULL; | |
| 285 | /* Check current die */ | ||
| 286 | while (!(ret = die_cb(cur_link, data))) { | ||
| 287 | /* Check child die */ | ||
| 288 | ret = dwarf_child(cur_link->die, &new_die, &__dw_error); | ||
| 289 | DIE_IF(ret == DW_DLV_ERROR); | ||
| 290 | if (ret == DW_DLV_OK) { | ||
| 291 | new_link.parent = cur_link; | ||
| 292 | new_link.die = new_die; | ||
| 293 | ret = __search_die_tree(&new_link, die_cb, data); | ||
| 294 | if (ret) | ||
| 295 | break; | ||
| 296 | } | ||
| 297 | 231 | ||
| 298 | /* Move to next sibling */ | 232 | do { |
| 299 | ret = dwarf_siblingof(__dw_debug, cur_link->die, &new_die, | 233 | if (dwarf_tag(die_mem) == DW_TAG_inlined_subroutine && |
| 300 | &__dw_error); | 234 | dwarf_haspc(die_mem, addr)) |
| 301 | DIE_IF(ret == DW_DLV_ERROR); | 235 | return die_mem; |
| 302 | dwarf_dealloc(__dw_debug, cur_link->die, DW_DLA_DIE); | ||
| 303 | cur_link->die = new_die; | ||
| 304 | if (ret == DW_DLV_NO_ENTRY) | ||
| 305 | return 0; | ||
| 306 | } | ||
| 307 | dwarf_dealloc(__dw_debug, cur_link->die, DW_DLA_DIE); | ||
| 308 | return ret; | ||
| 309 | } | ||
| 310 | 236 | ||
| 311 | /* Search a die in its children's die tree */ | 237 | if (die_get_inlinefunc(die_mem, addr, &child_die)) { |
| 312 | static int search_die_from_children(Dwarf_Die parent_die, | 238 | memcpy(die_mem, &child_die, sizeof(Dwarf_Die)); |
| 313 | int (*die_cb)(struct die_link *, void *), | 239 | return die_mem; |
| 314 | void *data) | 240 | } |
| 315 | { | 241 | } while (dwarf_siblingof(die_mem, die_mem) == 0); |
| 316 | struct die_link new_link; | ||
| 317 | int ret; | ||
| 318 | 242 | ||
| 319 | new_link.parent = NULL; | 243 | return NULL; |
| 320 | ret = dwarf_child(parent_die, &new_link.die, &__dw_error); | ||
| 321 | DIE_IF(ret == DW_DLV_ERROR); | ||
| 322 | if (ret == DW_DLV_OK) | ||
| 323 | return __search_die_tree(&new_link, die_cb, data); | ||
| 324 | else | ||
| 325 | return 0; | ||
| 326 | } | 244 | } |
| 327 | 245 | ||
| 328 | /* Find a locdesc corresponding to the address */ | 246 | /* Compare diename and tname */ |
| 329 | static int attr_get_locdesc(Dwarf_Attribute attr, Dwarf_Locdesc *desc, | 247 | static bool die_compare_name(Dwarf_Die *dw_die, const char *tname) |
| 330 | Dwarf_Addr addr) | ||
| 331 | { | 248 | { |
| 332 | Dwarf_Signed lcnt; | 249 | const char *name; |
| 333 | Dwarf_Locdesc **llbuf; | 250 | name = dwarf_diename(dw_die); |
| 334 | int ret, i; | 251 | DIE_IF(name == NULL); |
| 335 | 252 | return strcmp(tname, name); | |
| 336 | ret = dwarf_loclist_n(attr, &llbuf, &lcnt, &__dw_error); | ||
| 337 | DIE_IF(ret != DW_DLV_OK); | ||
| 338 | ret = DW_DLV_NO_ENTRY; | ||
| 339 | for (i = 0; i < lcnt; ++i) { | ||
| 340 | if (llbuf[i]->ld_lopc <= addr && | ||
| 341 | llbuf[i]->ld_hipc > addr) { | ||
| 342 | memcpy(desc, llbuf[i], sizeof(Dwarf_Locdesc)); | ||
| 343 | desc->ld_s = | ||
| 344 | malloc(sizeof(Dwarf_Loc) * llbuf[i]->ld_cents); | ||
| 345 | DIE_IF(desc->ld_s == NULL); | ||
| 346 | memcpy(desc->ld_s, llbuf[i]->ld_s, | ||
| 347 | sizeof(Dwarf_Loc) * llbuf[i]->ld_cents); | ||
| 348 | ret = DW_DLV_OK; | ||
| 349 | break; | ||
| 350 | } | ||
| 351 | dwarf_dealloc(__dw_debug, llbuf[i]->ld_s, DW_DLA_LOC_BLOCK); | ||
| 352 | dwarf_dealloc(__dw_debug, llbuf[i], DW_DLA_LOCDESC); | ||
| 353 | } | ||
| 354 | /* Releasing loop */ | ||
| 355 | for (; i < lcnt; ++i) { | ||
| 356 | dwarf_dealloc(__dw_debug, llbuf[i]->ld_s, DW_DLA_LOC_BLOCK); | ||
| 357 | dwarf_dealloc(__dw_debug, llbuf[i], DW_DLA_LOCDESC); | ||
| 358 | } | ||
| 359 | dwarf_dealloc(__dw_debug, llbuf, DW_DLA_LIST); | ||
| 360 | return ret; | ||
| 361 | } | 253 | } |
| 362 | 254 | ||
| 363 | /* Get decl_file attribute value (file number) */ | 255 | /* Get entry pc(or low pc, 1st entry of ranges) of the die */ |
| 364 | static Dwarf_Unsigned die_get_decl_file(Dwarf_Die sp_die) | 256 | static Dwarf_Addr die_get_entrypc(Dwarf_Die *dw_die) |
| 365 | { | 257 | { |
| 366 | Dwarf_Attribute attr; | 258 | Dwarf_Addr epc; |
| 367 | Dwarf_Unsigned fno; | ||
| 368 | int ret; | 259 | int ret; |
| 369 | 260 | ||
| 370 | ret = dwarf_attr(sp_die, DW_AT_decl_file, &attr, &__dw_error); | 261 | ret = dwarf_entrypc(dw_die, &epc); |
| 371 | DIE_IF(ret != DW_DLV_OK); | 262 | DIE_IF(ret == -1); |
| 372 | dwarf_formudata(attr, &fno, &__dw_error); | 263 | return epc; |
| 373 | DIE_IF(ret != DW_DLV_OK); | ||
| 374 | dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR); | ||
| 375 | return fno; | ||
| 376 | } | 264 | } |
| 377 | 265 | ||
| 378 | /* Get decl_line attribute value (line number) */ | 266 | /* Get a variable die */ |
| 379 | static Dwarf_Unsigned die_get_decl_line(Dwarf_Die sp_die) | 267 | static Dwarf_Die *die_find_variable(Dwarf_Die *sp_die, const char *name, |
| 268 | Dwarf_Die *die_mem) | ||
| 380 | { | 269 | { |
| 381 | Dwarf_Attribute attr; | 270 | Dwarf_Die child_die; |
| 382 | Dwarf_Unsigned lno; | 271 | int tag; |
| 383 | int ret; | 272 | int ret; |
| 384 | 273 | ||
| 385 | ret = dwarf_attr(sp_die, DW_AT_decl_line, &attr, &__dw_error); | 274 | ret = dwarf_child(sp_die, die_mem); |
| 386 | DIE_IF(ret != DW_DLV_OK); | 275 | if (ret != 0) |
| 387 | dwarf_formudata(attr, &lno, &__dw_error); | 276 | return NULL; |
| 388 | DIE_IF(ret != DW_DLV_OK); | 277 | |
| 389 | dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR); | 278 | do { |
| 390 | return lno; | 279 | tag = dwarf_tag(die_mem); |
| 280 | if ((tag == DW_TAG_formal_parameter || | ||
| 281 | tag == DW_TAG_variable) && | ||
| 282 | (die_compare_name(die_mem, name) == 0)) | ||
| 283 | return die_mem; | ||
| 284 | |||
| 285 | if (die_find_variable(die_mem, name, &child_die)) { | ||
| 286 | memcpy(die_mem, &child_die, sizeof(Dwarf_Die)); | ||
| 287 | return die_mem; | ||
| 288 | } | ||
| 289 | } while (dwarf_siblingof(die_mem, die_mem) == 0); | ||
| 290 | |||
| 291 | return NULL; | ||
| 391 | } | 292 | } |
| 392 | 293 | ||
| 393 | /* | 294 | /* |
| @@ -395,47 +296,45 @@ static Dwarf_Unsigned die_get_decl_line(Dwarf_Die sp_die) | |||
| 395 | */ | 296 | */ |
| 396 | 297 | ||
| 397 | /* Show a location */ | 298 | /* Show a location */ |
| 398 | static void show_location(Dwarf_Loc *loc, struct probe_finder *pf) | 299 | static void show_location(Dwarf_Op *op, struct probe_finder *pf) |
| 399 | { | 300 | { |
| 400 | Dwarf_Small op; | 301 | unsigned int regn; |
| 401 | Dwarf_Unsigned regn; | 302 | Dwarf_Word offs = 0; |
| 402 | Dwarf_Signed offs; | ||
| 403 | int deref = 0, ret; | 303 | int deref = 0, ret; |
| 404 | const char *regs; | 304 | const char *regs; |
| 405 | 305 | ||
| 406 | op = loc->lr_atom; | 306 | /* TODO: support CFA */ |
| 407 | |||
| 408 | /* If this is based on frame buffer, set the offset */ | 307 | /* If this is based on frame buffer, set the offset */ |
| 409 | if (op == DW_OP_fbreg) { | 308 | if (op->atom == DW_OP_fbreg) { |
| 309 | if (pf->fb_ops == NULL) | ||
| 310 | die("The attribute of frame base is not supported.\n"); | ||
| 410 | deref = 1; | 311 | deref = 1; |
| 411 | offs = (Dwarf_Signed)loc->lr_number; | 312 | offs = op->number; |
| 412 | op = pf->fbloc.ld_s[0].lr_atom; | 313 | op = &pf->fb_ops[0]; |
| 413 | loc = &pf->fbloc.ld_s[0]; | 314 | } |
| 414 | } else | ||
| 415 | offs = 0; | ||
| 416 | 315 | ||
| 417 | if (op >= DW_OP_breg0 && op <= DW_OP_breg31) { | 316 | if (op->atom >= DW_OP_breg0 && op->atom <= DW_OP_breg31) { |
| 418 | regn = op - DW_OP_breg0; | 317 | regn = op->atom - DW_OP_breg0; |
| 419 | offs += (Dwarf_Signed)loc->lr_number; | 318 | offs += op->number; |
| 420 | deref = 1; | 319 | deref = 1; |
| 421 | } else if (op >= DW_OP_reg0 && op <= DW_OP_reg31) { | 320 | } else if (op->atom >= DW_OP_reg0 && op->atom <= DW_OP_reg31) { |
| 422 | regn = op - DW_OP_reg0; | 321 | regn = op->atom - DW_OP_reg0; |
| 423 | } else if (op == DW_OP_bregx) { | 322 | } else if (op->atom == DW_OP_bregx) { |
| 424 | regn = loc->lr_number; | 323 | regn = op->number; |
| 425 | offs += (Dwarf_Signed)loc->lr_number2; | 324 | offs += op->number2; |
| 426 | deref = 1; | 325 | deref = 1; |
| 427 | } else if (op == DW_OP_regx) { | 326 | } else if (op->atom == DW_OP_regx) { |
| 428 | regn = loc->lr_number; | 327 | regn = op->number; |
| 429 | } else | 328 | } else |
| 430 | die("Dwarf_OP %d is not supported.", op); | 329 | die("DW_OP %d is not supported.", op->atom); |
| 431 | 330 | ||
| 432 | regs = get_arch_regstr(regn); | 331 | regs = get_arch_regstr(regn); |
| 433 | if (!regs) | 332 | if (!regs) |
| 434 | die("%lld exceeds max register number.", regn); | 333 | die("%u exceeds max register number.", regn); |
| 435 | 334 | ||
| 436 | if (deref) | 335 | if (deref) |
| 437 | ret = snprintf(pf->buf, pf->len, | 336 | ret = snprintf(pf->buf, pf->len, " %s=+%ju(%s)", |
| 438 | " %s=%+lld(%s)", pf->var, offs, regs); | 337 | pf->var, (uintmax_t)offs, regs); |
| 439 | else | 338 | else |
| 440 | ret = snprintf(pf->buf, pf->len, " %s=%s", pf->var, regs); | 339 | ret = snprintf(pf->buf, pf->len, " %s=%s", pf->var, regs); |
| 441 | DIE_IF(ret < 0); | 340 | DIE_IF(ret < 0); |
| @@ -443,52 +342,37 @@ static void show_location(Dwarf_Loc *loc, struct probe_finder *pf) | |||
| 443 | } | 342 | } |
| 444 | 343 | ||
| 445 | /* Show a variables in kprobe event format */ | 344 | /* Show a variables in kprobe event format */ |
| 446 | static void show_variable(Dwarf_Die vr_die, struct probe_finder *pf) | 345 | static void show_variable(Dwarf_Die *vr_die, struct probe_finder *pf) |
| 447 | { | 346 | { |
| 448 | Dwarf_Attribute attr; | 347 | Dwarf_Attribute attr; |
| 449 | Dwarf_Locdesc ld; | 348 | Dwarf_Op *expr; |
| 349 | size_t nexpr; | ||
| 450 | int ret; | 350 | int ret; |
| 451 | 351 | ||
| 452 | ret = dwarf_attr(vr_die, DW_AT_location, &attr, &__dw_error); | 352 | if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL) |
| 453 | if (ret != DW_DLV_OK) | ||
| 454 | goto error; | 353 | goto error; |
| 455 | ret = attr_get_locdesc(attr, &ld, (pf->addr - pf->cu_base)); | 354 | /* TODO: handle more than 1 exprs */ |
| 456 | if (ret != DW_DLV_OK) | 355 | ret = dwarf_getlocation_addr(&attr, (pf->addr - pf->cu_base), |
| 356 | &expr, &nexpr, 1); | ||
| 357 | if (ret <= 0 || nexpr == 0) | ||
| 457 | goto error; | 358 | goto error; |
| 458 | /* TODO? */ | 359 | |
| 459 | DIE_IF(ld.ld_cents != 1); | 360 | show_location(expr, pf); |
| 460 | show_location(&ld.ld_s[0], pf); | 361 | /* *expr will be cached in libdw. Don't free it. */ |
| 461 | free(ld.ld_s); | ||
| 462 | dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR); | ||
| 463 | return ; | 362 | return ; |
| 464 | error: | 363 | error: |
| 364 | /* TODO: Support const_value */ | ||
| 465 | die("Failed to find the location of %s at this address.\n" | 365 | die("Failed to find the location of %s at this address.\n" |
| 466 | " Perhaps, it has been optimized out.", pf->var); | 366 | " Perhaps, it has been optimized out.", pf->var); |
| 467 | } | 367 | } |
| 468 | 368 | ||
| 469 | static int variable_callback(struct die_link *dlink, void *data) | ||
| 470 | { | ||
| 471 | struct probe_finder *pf = (struct probe_finder *)data; | ||
| 472 | Dwarf_Half tag; | ||
| 473 | int ret; | ||
| 474 | |||
| 475 | ret = dwarf_tag(dlink->die, &tag, &__dw_error); | ||
| 476 | DIE_IF(ret == DW_DLV_ERROR); | ||
| 477 | if ((tag == DW_TAG_formal_parameter || | ||
| 478 | tag == DW_TAG_variable) && | ||
| 479 | (die_compare_name(dlink->die, pf->var) == 0)) { | ||
| 480 | show_variable(dlink->die, pf); | ||
| 481 | return 1; | ||
| 482 | } | ||
| 483 | /* TODO: Support struct members and arrays */ | ||
| 484 | return 0; | ||
| 485 | } | ||
| 486 | |||
| 487 | /* Find a variable in a subprogram die */ | 369 | /* Find a variable in a subprogram die */ |
| 488 | static void find_variable(Dwarf_Die sp_die, struct probe_finder *pf) | 370 | static void find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) |
| 489 | { | 371 | { |
| 490 | int ret; | 372 | int ret; |
| 373 | Dwarf_Die vr_die; | ||
| 491 | 374 | ||
| 375 | /* TODO: Support struct members and arrays */ | ||
| 492 | if (!is_c_varname(pf->var)) { | 376 | if (!is_c_varname(pf->var)) { |
| 493 | /* Output raw parameters */ | 377 | /* Output raw parameters */ |
| 494 | ret = snprintf(pf->buf, pf->len, " %s", pf->var); | 378 | ret = snprintf(pf->buf, pf->len, " %s", pf->var); |
| @@ -499,58 +383,51 @@ static void find_variable(Dwarf_Die sp_die, struct probe_finder *pf) | |||
| 499 | 383 | ||
| 500 | pr_debug("Searching '%s' variable in context.\n", pf->var); | 384 | pr_debug("Searching '%s' variable in context.\n", pf->var); |
| 501 | /* Search child die for local variables and parameters. */ | 385 | /* Search child die for local variables and parameters. */ |
| 502 | ret = search_die_from_children(sp_die, variable_callback, pf); | 386 | if (!die_find_variable(sp_die, pf->var, &vr_die)) |
| 503 | if (!ret) | ||
| 504 | die("Failed to find '%s' in this function.", pf->var); | 387 | die("Failed to find '%s' in this function.", pf->var); |
| 505 | } | ||
| 506 | |||
| 507 | /* Get a frame base on the address */ | ||
| 508 | static void get_current_frame_base(Dwarf_Die sp_die, struct probe_finder *pf) | ||
| 509 | { | ||
| 510 | Dwarf_Attribute attr; | ||
| 511 | int ret; | ||
| 512 | 388 | ||
| 513 | ret = dwarf_attr(sp_die, DW_AT_frame_base, &attr, &__dw_error); | 389 | show_variable(&vr_die, pf); |
| 514 | DIE_IF(ret != DW_DLV_OK); | ||
| 515 | ret = attr_get_locdesc(attr, &pf->fbloc, (pf->addr - pf->cu_base)); | ||
| 516 | DIE_IF(ret != DW_DLV_OK); | ||
| 517 | dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR); | ||
| 518 | } | ||
| 519 | |||
| 520 | static void free_current_frame_base(struct probe_finder *pf) | ||
| 521 | { | ||
| 522 | free(pf->fbloc.ld_s); | ||
| 523 | memset(&pf->fbloc, 0, sizeof(Dwarf_Locdesc)); | ||
| 524 | } | 390 | } |
| 525 | 391 | ||
| 526 | /* Show a probe point to output buffer */ | 392 | /* Show a probe point to output buffer */ |
| 527 | static void show_probepoint(Dwarf_Die sp_die, Dwarf_Signed offs, | 393 | static void show_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) |
| 528 | struct probe_finder *pf) | ||
| 529 | { | 394 | { |
| 530 | struct probe_point *pp = pf->pp; | 395 | struct probe_point *pp = pf->pp; |
| 531 | char *name; | 396 | Dwarf_Addr eaddr; |
| 397 | Dwarf_Die die_mem; | ||
| 398 | const char *name; | ||
| 532 | char tmp[MAX_PROBE_BUFFER]; | 399 | char tmp[MAX_PROBE_BUFFER]; |
| 533 | int ret, i, len; | 400 | int ret, i, len; |
| 401 | Dwarf_Attribute fb_attr; | ||
| 402 | size_t nops; | ||
| 403 | |||
| 404 | /* If no real subprogram, find a real one */ | ||
| 405 | if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) { | ||
| 406 | sp_die = die_get_real_subprogram(&pf->cu_die, | ||
| 407 | pf->addr, &die_mem); | ||
| 408 | if (!sp_die) | ||
| 409 | die("Probe point is not found in subprograms."); | ||
| 410 | } | ||
| 534 | 411 | ||
| 535 | /* Output name of probe point */ | 412 | /* Output name of probe point */ |
| 536 | ret = dwarf_diename(sp_die, &name, &__dw_error); | 413 | name = dwarf_diename(sp_die); |
| 537 | DIE_IF(ret == DW_DLV_ERROR); | 414 | if (name) { |
| 538 | if (ret == DW_DLV_OK) { | 415 | dwarf_entrypc(sp_die, &eaddr); |
| 539 | ret = snprintf(tmp, MAX_PROBE_BUFFER, "%s+%u", name, | 416 | ret = snprintf(tmp, MAX_PROBE_BUFFER, "%s+%lu", name, |
| 540 | (unsigned int)offs); | 417 | (unsigned long)(pf->addr - eaddr)); |
| 541 | /* Copy the function name if possible */ | 418 | /* Copy the function name if possible */ |
| 542 | if (!pp->function) { | 419 | if (!pp->function) { |
| 543 | pp->function = strdup(name); | 420 | pp->function = strdup(name); |
| 544 | pp->offset = offs; | 421 | pp->offset = (size_t)(pf->addr - eaddr); |
| 545 | } | 422 | } |
| 546 | dwarf_dealloc(__dw_debug, name, DW_DLA_STRING); | ||
| 547 | } else { | 423 | } else { |
| 548 | /* This function has no name. */ | 424 | /* This function has no name. */ |
| 549 | ret = snprintf(tmp, MAX_PROBE_BUFFER, "0x%llx", pf->addr); | 425 | ret = snprintf(tmp, MAX_PROBE_BUFFER, "0x%jx", |
| 426 | (uintmax_t)pf->addr); | ||
| 550 | if (!pp->function) { | 427 | if (!pp->function) { |
| 551 | /* TODO: Use _stext */ | 428 | /* TODO: Use _stext */ |
| 552 | pp->function = strdup(""); | 429 | pp->function = strdup(""); |
| 553 | pp->offset = (int)pf->addr; | 430 | pp->offset = (size_t)pf->addr; |
| 554 | } | 431 | } |
| 555 | } | 432 | } |
| 556 | DIE_IF(ret < 0); | 433 | DIE_IF(ret < 0); |
| @@ -558,8 +435,15 @@ static void show_probepoint(Dwarf_Die sp_die, Dwarf_Signed offs, | |||
| 558 | len = ret; | 435 | len = ret; |
| 559 | pr_debug("Probe point found: %s\n", tmp); | 436 | pr_debug("Probe point found: %s\n", tmp); |
| 560 | 437 | ||
| 438 | /* Get the frame base attribute/ops */ | ||
| 439 | dwarf_attr(sp_die, DW_AT_frame_base, &fb_attr); | ||
| 440 | ret = dwarf_getlocation_addr(&fb_attr, (pf->addr - pf->cu_base), | ||
| 441 | &pf->fb_ops, &nops, 1); | ||
| 442 | if (ret <= 0 || nops == 0) | ||
| 443 | pf->fb_ops = NULL; | ||
| 444 | |||
| 561 | /* Find each argument */ | 445 | /* Find each argument */ |
| 562 | get_current_frame_base(sp_die, pf); | 446 | /* TODO: use dwarf_cfi_addrframe */ |
| 563 | for (i = 0; i < pp->nr_args; i++) { | 447 | for (i = 0; i < pp->nr_args; i++) { |
| 564 | pf->var = pp->args[i]; | 448 | pf->var = pp->args[i]; |
| 565 | pf->buf = &tmp[len]; | 449 | pf->buf = &tmp[len]; |
| @@ -567,289 +451,327 @@ static void show_probepoint(Dwarf_Die sp_die, Dwarf_Signed offs, | |||
| 567 | find_variable(sp_die, pf); | 451 | find_variable(sp_die, pf); |
| 568 | len += strlen(pf->buf); | 452 | len += strlen(pf->buf); |
| 569 | } | 453 | } |
| 570 | free_current_frame_base(pf); | 454 | |
| 455 | /* *pf->fb_ops will be cached in libdw. Don't free it. */ | ||
| 456 | pf->fb_ops = NULL; | ||
| 571 | 457 | ||
| 572 | pp->probes[pp->found] = strdup(tmp); | 458 | pp->probes[pp->found] = strdup(tmp); |
| 573 | pp->found++; | 459 | pp->found++; |
| 574 | } | 460 | } |
| 575 | 461 | ||
| 576 | static int probeaddr_callback(struct die_link *dlink, void *data) | 462 | /* Find probe point from its line number */ |
| 463 | static void find_probe_point_by_line(struct probe_finder *pf) | ||
| 577 | { | 464 | { |
| 578 | struct probe_finder *pf = (struct probe_finder *)data; | 465 | Dwarf_Lines *lines; |
| 579 | Dwarf_Half tag; | 466 | Dwarf_Line *line; |
| 580 | Dwarf_Signed offs; | 467 | size_t nlines, i; |
| 468 | Dwarf_Addr addr; | ||
| 469 | int lineno; | ||
| 581 | int ret; | 470 | int ret; |
| 582 | 471 | ||
| 583 | ret = dwarf_tag(dlink->die, &tag, &__dw_error); | 472 | ret = dwarf_getsrclines(&pf->cu_die, &lines, &nlines); |
| 584 | DIE_IF(ret == DW_DLV_ERROR); | 473 | DIE_IF(ret != 0); |
| 585 | /* Check the address is in this subprogram */ | 474 | |
| 586 | if (tag == DW_TAG_subprogram && | 475 | for (i = 0; i < nlines; i++) { |
| 587 | die_within_subprogram(dlink->die, pf->addr, &offs)) { | 476 | line = dwarf_onesrcline(lines, i); |
| 588 | show_probepoint(dlink->die, offs, pf); | 477 | dwarf_lineno(line, &lineno); |
| 589 | return 1; | 478 | if (lineno != pf->lno) |
| 479 | continue; | ||
| 480 | |||
| 481 | /* TODO: Get fileno from line, but how? */ | ||
| 482 | if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0) | ||
| 483 | continue; | ||
| 484 | |||
| 485 | ret = dwarf_lineaddr(line, &addr); | ||
| 486 | DIE_IF(ret != 0); | ||
| 487 | pr_debug("Probe line found: line[%d]:%d addr:0x%jx\n", | ||
| 488 | (int)i, lineno, (uintmax_t)addr); | ||
| 489 | pf->addr = addr; | ||
| 490 | |||
| 491 | show_probe_point(NULL, pf); | ||
| 492 | /* Continuing, because target line might be inlined. */ | ||
| 590 | } | 493 | } |
| 591 | return 0; | ||
| 592 | } | 494 | } |
| 593 | 495 | ||
| 594 | /* Find probe point from its line number */ | 496 | /* Find lines which match lazy pattern */ |
| 595 | static void find_probe_point_by_line(struct probe_finder *pf) | 497 | static int find_lazy_match_lines(struct list_head *head, |
| 498 | const char *fname, const char *pat) | ||
| 596 | { | 499 | { |
| 597 | Dwarf_Signed cnt, i, clm; | 500 | char *fbuf, *p1, *p2; |
| 598 | Dwarf_Line *lines; | 501 | int fd, line, nlines = 0; |
| 599 | Dwarf_Unsigned lineno = 0; | 502 | struct stat st; |
| 503 | |||
| 504 | fd = open(fname, O_RDONLY); | ||
| 505 | if (fd < 0) | ||
| 506 | die("failed to open %s", fname); | ||
| 507 | DIE_IF(fstat(fd, &st) < 0); | ||
| 508 | fbuf = malloc(st.st_size + 2); | ||
| 509 | DIE_IF(fbuf == NULL); | ||
| 510 | DIE_IF(read(fd, fbuf, st.st_size) < 0); | ||
| 511 | close(fd); | ||
| 512 | fbuf[st.st_size] = '\n'; /* Dummy line */ | ||
| 513 | fbuf[st.st_size + 1] = '\0'; | ||
| 514 | p1 = fbuf; | ||
| 515 | line = 1; | ||
| 516 | while ((p2 = strchr(p1, '\n')) != NULL) { | ||
| 517 | *p2 = '\0'; | ||
| 518 | if (strlazymatch(p1, pat)) { | ||
| 519 | line_list__add_line(head, line); | ||
| 520 | nlines++; | ||
| 521 | } | ||
| 522 | line++; | ||
| 523 | p1 = p2 + 1; | ||
| 524 | } | ||
| 525 | free(fbuf); | ||
| 526 | return nlines; | ||
| 527 | } | ||
| 528 | |||
| 529 | /* Find probe points from lazy pattern */ | ||
| 530 | static void find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) | ||
| 531 | { | ||
| 532 | Dwarf_Lines *lines; | ||
| 533 | Dwarf_Line *line; | ||
| 534 | size_t nlines, i; | ||
| 600 | Dwarf_Addr addr; | 535 | Dwarf_Addr addr; |
| 601 | Dwarf_Unsigned fno; | 536 | Dwarf_Die die_mem; |
| 537 | int lineno; | ||
| 602 | int ret; | 538 | int ret; |
| 603 | 539 | ||
| 604 | ret = dwarf_srclines(pf->cu_die, &lines, &cnt, &__dw_error); | 540 | if (list_empty(&pf->lcache)) { |
| 605 | DIE_IF(ret != DW_DLV_OK); | 541 | /* Matching lazy line pattern */ |
| 542 | ret = find_lazy_match_lines(&pf->lcache, pf->fname, | ||
| 543 | pf->pp->lazy_line); | ||
| 544 | if (ret <= 0) | ||
| 545 | die("No matched lines found in %s.", pf->fname); | ||
| 546 | } | ||
| 547 | |||
| 548 | ret = dwarf_getsrclines(&pf->cu_die, &lines, &nlines); | ||
| 549 | DIE_IF(ret != 0); | ||
| 550 | for (i = 0; i < nlines; i++) { | ||
| 551 | line = dwarf_onesrcline(lines, i); | ||
| 606 | 552 | ||
| 607 | for (i = 0; i < cnt; i++) { | 553 | dwarf_lineno(line, &lineno); |
| 608 | ret = dwarf_line_srcfileno(lines[i], &fno, &__dw_error); | 554 | if (!line_list__has_line(&pf->lcache, lineno)) |
| 609 | DIE_IF(ret != DW_DLV_OK); | ||
| 610 | if (fno != pf->fno) | ||
| 611 | continue; | 555 | continue; |
| 612 | 556 | ||
| 613 | ret = dwarf_lineno(lines[i], &lineno, &__dw_error); | 557 | /* TODO: Get fileno from line, but how? */ |
| 614 | DIE_IF(ret != DW_DLV_OK); | 558 | if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0) |
| 615 | if (lineno != pf->lno) | ||
| 616 | continue; | 559 | continue; |
| 617 | 560 | ||
| 618 | ret = dwarf_lineoff(lines[i], &clm, &__dw_error); | 561 | ret = dwarf_lineaddr(line, &addr); |
| 619 | DIE_IF(ret != DW_DLV_OK); | 562 | DIE_IF(ret != 0); |
| 563 | if (sp_die) { | ||
| 564 | /* Address filtering 1: does sp_die include addr? */ | ||
| 565 | if (!dwarf_haspc(sp_die, addr)) | ||
| 566 | continue; | ||
| 567 | /* Address filtering 2: No child include addr? */ | ||
| 568 | if (die_get_inlinefunc(sp_die, addr, &die_mem)) | ||
| 569 | continue; | ||
| 570 | } | ||
| 620 | 571 | ||
| 621 | ret = dwarf_lineaddr(lines[i], &addr, &__dw_error); | 572 | pr_debug("Probe line found: line[%d]:%d addr:0x%llx\n", |
| 622 | DIE_IF(ret != DW_DLV_OK); | 573 | (int)i, lineno, (unsigned long long)addr); |
| 623 | pr_debug("Probe line found: line[%d]:%u,%d addr:0x%llx\n", | ||
| 624 | (int)i, (unsigned)lineno, (int)clm, addr); | ||
| 625 | pf->addr = addr; | 574 | pf->addr = addr; |
| 626 | /* Search a real subprogram including this line, */ | 575 | |
| 627 | ret = search_die_from_children(pf->cu_die, | 576 | show_probe_point(sp_die, pf); |
| 628 | probeaddr_callback, pf); | ||
| 629 | if (ret == 0) | ||
| 630 | die("Probe point is not found in subprograms."); | ||
| 631 | /* Continuing, because target line might be inlined. */ | 577 | /* Continuing, because target line might be inlined. */ |
| 632 | } | 578 | } |
| 633 | dwarf_srclines_dealloc(__dw_debug, lines, cnt); | 579 | /* TODO: deallocate lines, but how? */ |
| 580 | } | ||
| 581 | |||
| 582 | static int probe_point_inline_cb(Dwarf_Die *in_die, void *data) | ||
| 583 | { | ||
| 584 | struct probe_finder *pf = (struct probe_finder *)data; | ||
| 585 | struct probe_point *pp = pf->pp; | ||
| 586 | |||
| 587 | if (pp->lazy_line) | ||
| 588 | find_probe_point_lazy(in_die, pf); | ||
| 589 | else { | ||
| 590 | /* Get probe address */ | ||
| 591 | pf->addr = die_get_entrypc(in_die); | ||
| 592 | pf->addr += pp->offset; | ||
| 593 | pr_debug("found inline addr: 0x%jx\n", | ||
| 594 | (uintmax_t)pf->addr); | ||
| 595 | |||
| 596 | show_probe_point(in_die, pf); | ||
| 597 | } | ||
| 598 | |||
| 599 | return DWARF_CB_OK; | ||
| 634 | } | 600 | } |
| 635 | 601 | ||
| 636 | /* Search function from function name */ | 602 | /* Search function from function name */ |
| 637 | static int probefunc_callback(struct die_link *dlink, void *data) | 603 | static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) |
| 638 | { | 604 | { |
| 639 | struct probe_finder *pf = (struct probe_finder *)data; | 605 | struct probe_finder *pf = (struct probe_finder *)data; |
| 640 | struct probe_point *pp = pf->pp; | 606 | struct probe_point *pp = pf->pp; |
| 641 | struct die_link *lk; | ||
| 642 | Dwarf_Signed offs; | ||
| 643 | Dwarf_Half tag; | ||
| 644 | int ret; | ||
| 645 | 607 | ||
| 646 | ret = dwarf_tag(dlink->die, &tag, &__dw_error); | 608 | /* Check tag and diename */ |
| 647 | DIE_IF(ret == DW_DLV_ERROR); | 609 | if (dwarf_tag(sp_die) != DW_TAG_subprogram || |
| 648 | if (tag == DW_TAG_subprogram) { | 610 | die_compare_name(sp_die, pp->function) != 0) |
| 649 | if (die_compare_name(dlink->die, pp->function) == 0) { | 611 | return 0; |
| 650 | if (pp->line) { /* Function relative line */ | 612 | |
| 651 | pf->fno = die_get_decl_file(dlink->die); | 613 | pf->fname = dwarf_decl_file(sp_die); |
| 652 | pf->lno = die_get_decl_line(dlink->die) | 614 | if (pp->line) { /* Function relative line */ |
| 653 | + pp->line; | 615 | dwarf_decl_line(sp_die, &pf->lno); |
| 654 | find_probe_point_by_line(pf); | 616 | pf->lno += pp->line; |
| 655 | return 1; | 617 | find_probe_point_by_line(pf); |
| 656 | } | 618 | } else if (!dwarf_func_inline(sp_die)) { |
| 657 | if (die_inlined_subprogram(dlink->die)) { | 619 | /* Real function */ |
| 658 | /* Inlined function, save it. */ | 620 | if (pp->lazy_line) |
| 659 | ret = dwarf_die_CU_offset(dlink->die, | 621 | find_probe_point_lazy(sp_die, pf); |
| 660 | &pf->inl_offs, | 622 | else { |
| 661 | &__dw_error); | 623 | pf->addr = die_get_entrypc(sp_die); |
| 662 | DIE_IF(ret != DW_DLV_OK); | ||
| 663 | pr_debug("inline definition offset %lld\n", | ||
| 664 | pf->inl_offs); | ||
| 665 | return 0; /* Continue to search */ | ||
| 666 | } | ||
| 667 | /* Get probe address */ | ||
| 668 | pf->addr = die_get_entrypc(dlink->die); | ||
| 669 | pf->addr += pp->offset; | 624 | pf->addr += pp->offset; |
| 670 | /* TODO: Check the address in this function */ | 625 | /* TODO: Check the address in this function */ |
| 671 | show_probepoint(dlink->die, pp->offset, pf); | 626 | show_probe_point(sp_die, pf); |
| 672 | return 1; /* Exit; no same symbol in this CU. */ | ||
| 673 | } | ||
| 674 | } else if (tag == DW_TAG_inlined_subroutine && pf->inl_offs) { | ||
| 675 | if (die_get_abstract_origin(dlink->die) == pf->inl_offs) { | ||
| 676 | /* Get probe address */ | ||
| 677 | pf->addr = die_get_entrypc(dlink->die); | ||
| 678 | pf->addr += pp->offset; | ||
| 679 | pr_debug("found inline addr: 0x%llx\n", pf->addr); | ||
| 680 | /* Inlined function. Get a real subprogram */ | ||
| 681 | for (lk = dlink->parent; lk != NULL; lk = lk->parent) { | ||
| 682 | tag = 0; | ||
| 683 | dwarf_tag(lk->die, &tag, &__dw_error); | ||
| 684 | DIE_IF(ret == DW_DLV_ERROR); | ||
| 685 | if (tag == DW_TAG_subprogram && | ||
| 686 | !die_inlined_subprogram(lk->die)) | ||
| 687 | goto found; | ||
| 688 | } | ||
| 689 | die("Failed to find real subprogram."); | ||
| 690 | found: | ||
| 691 | /* Get offset from subprogram */ | ||
| 692 | ret = die_within_subprogram(lk->die, pf->addr, &offs); | ||
| 693 | DIE_IF(!ret); | ||
| 694 | show_probepoint(lk->die, offs, pf); | ||
| 695 | /* Continue to search */ | ||
| 696 | } | 627 | } |
| 697 | } | 628 | } else |
| 698 | return 0; | 629 | /* Inlined function: search instances */ |
| 630 | dwarf_func_inline_instances(sp_die, probe_point_inline_cb, pf); | ||
| 631 | |||
| 632 | return 1; /* Exit; no same symbol in this CU. */ | ||
| 699 | } | 633 | } |
| 700 | 634 | ||
| 701 | static void find_probe_point_by_func(struct probe_finder *pf) | 635 | static void find_probe_point_by_func(struct probe_finder *pf) |
| 702 | { | 636 | { |
| 703 | search_die_from_children(pf->cu_die, probefunc_callback, pf); | 637 | dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, pf, 0); |
| 704 | } | 638 | } |
| 705 | 639 | ||
| 706 | /* Find a probe point */ | 640 | /* Find a probe point */ |
| 707 | int find_probepoint(int fd, struct probe_point *pp) | 641 | int find_probe_point(int fd, struct probe_point *pp) |
| 708 | { | 642 | { |
| 709 | Dwarf_Half addr_size = 0; | ||
| 710 | Dwarf_Unsigned next_cuh = 0; | ||
| 711 | int cu_number = 0, ret; | ||
| 712 | struct probe_finder pf = {.pp = pp}; | 643 | struct probe_finder pf = {.pp = pp}; |
| 644 | int ret; | ||
| 645 | Dwarf_Off off, noff; | ||
| 646 | size_t cuhl; | ||
| 647 | Dwarf_Die *diep; | ||
| 648 | Dwarf *dbg; | ||
| 713 | 649 | ||
| 714 | ret = dwarf_init(fd, DW_DLC_READ, 0, 0, &__dw_debug, &__dw_error); | 650 | dbg = dwarf_begin(fd, DWARF_C_READ); |
| 715 | if (ret != DW_DLV_OK) | 651 | if (!dbg) |
| 716 | return -ENOENT; | 652 | return -ENOENT; |
| 717 | 653 | ||
| 718 | pp->found = 0; | 654 | pp->found = 0; |
| 719 | while (++cu_number) { | 655 | off = 0; |
| 720 | /* Search CU (Compilation Unit) */ | 656 | line_list__init(&pf.lcache); |
| 721 | ret = dwarf_next_cu_header(__dw_debug, NULL, NULL, NULL, | 657 | /* Loop on CUs (Compilation Unit) */ |
| 722 | &addr_size, &next_cuh, &__dw_error); | 658 | while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL)) { |
| 723 | DIE_IF(ret == DW_DLV_ERROR); | ||
| 724 | if (ret == DW_DLV_NO_ENTRY) | ||
| 725 | break; | ||
| 726 | |||
| 727 | /* Get the DIE(Debugging Information Entry) of this CU */ | 659 | /* Get the DIE(Debugging Information Entry) of this CU */ |
| 728 | ret = dwarf_siblingof(__dw_debug, 0, &pf.cu_die, &__dw_error); | 660 | diep = dwarf_offdie(dbg, off + cuhl, &pf.cu_die); |
| 729 | DIE_IF(ret != DW_DLV_OK); | 661 | if (!diep) |
| 662 | continue; | ||
| 730 | 663 | ||
| 731 | /* Check if target file is included. */ | 664 | /* Check if target file is included. */ |
| 732 | if (pp->file) | 665 | if (pp->file) |
| 733 | pf.fno = cu_find_fileno(pf.cu_die, pp->file); | 666 | pf.fname = cu_find_realpath(&pf.cu_die, pp->file); |
| 667 | else | ||
| 668 | pf.fname = NULL; | ||
| 734 | 669 | ||
| 735 | if (!pp->file || pf.fno) { | 670 | if (!pp->file || pf.fname) { |
| 736 | /* Save CU base address (for frame_base) */ | 671 | /* Save CU base address (for frame_base) */ |
| 737 | ret = dwarf_lowpc(pf.cu_die, &pf.cu_base, &__dw_error); | 672 | ret = dwarf_lowpc(&pf.cu_die, &pf.cu_base); |
| 738 | DIE_IF(ret == DW_DLV_ERROR); | 673 | if (ret != 0) |
| 739 | if (ret == DW_DLV_NO_ENTRY) | ||
| 740 | pf.cu_base = 0; | 674 | pf.cu_base = 0; |
| 741 | if (pp->function) | 675 | if (pp->function) |
| 742 | find_probe_point_by_func(&pf); | 676 | find_probe_point_by_func(&pf); |
| 677 | else if (pp->lazy_line) | ||
| 678 | find_probe_point_lazy(NULL, &pf); | ||
| 743 | else { | 679 | else { |
| 744 | pf.lno = pp->line; | 680 | pf.lno = pp->line; |
| 745 | find_probe_point_by_line(&pf); | 681 | find_probe_point_by_line(&pf); |
| 746 | } | 682 | } |
| 747 | } | 683 | } |
| 748 | dwarf_dealloc(__dw_debug, pf.cu_die, DW_DLA_DIE); | 684 | off = noff; |
| 749 | } | 685 | } |
| 750 | ret = dwarf_finish(__dw_debug, &__dw_error); | 686 | line_list__free(&pf.lcache); |
| 751 | DIE_IF(ret != DW_DLV_OK); | 687 | dwarf_end(dbg); |
| 752 | 688 | ||
| 753 | return pp->found; | 689 | return pp->found; |
| 754 | } | 690 | } |
| 755 | 691 | ||
| 756 | |||
| 757 | static void line_range_add_line(struct line_range *lr, unsigned int line) | ||
| 758 | { | ||
| 759 | struct line_node *ln; | ||
| 760 | struct list_head *p; | ||
| 761 | |||
| 762 | /* Reverse search, because new line will be the last one */ | ||
| 763 | list_for_each_entry_reverse(ln, &lr->line_list, list) { | ||
| 764 | if (ln->line < line) { | ||
| 765 | p = &ln->list; | ||
| 766 | goto found; | ||
| 767 | } else if (ln->line == line) /* Already exist */ | ||
| 768 | return ; | ||
| 769 | } | ||
| 770 | /* List is empty, or the smallest entry */ | ||
| 771 | p = &lr->line_list; | ||
| 772 | found: | ||
| 773 | pr_debug("Debug: add a line %u\n", line); | ||
| 774 | ln = zalloc(sizeof(struct line_node)); | ||
| 775 | DIE_IF(ln == NULL); | ||
| 776 | ln->line = line; | ||
| 777 | INIT_LIST_HEAD(&ln->list); | ||
| 778 | list_add(&ln->list, p); | ||
| 779 | } | ||
| 780 | |||
| 781 | /* Find line range from its line number */ | 692 | /* Find line range from its line number */ |
| 782 | static void find_line_range_by_line(struct line_finder *lf) | 693 | static void find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) |
| 783 | { | 694 | { |
| 784 | Dwarf_Signed cnt, i; | 695 | Dwarf_Lines *lines; |
| 785 | Dwarf_Line *lines; | 696 | Dwarf_Line *line; |
| 786 | Dwarf_Unsigned lineno = 0; | 697 | size_t nlines, i; |
| 787 | Dwarf_Unsigned fno; | ||
| 788 | Dwarf_Addr addr; | 698 | Dwarf_Addr addr; |
| 699 | int lineno; | ||
| 789 | int ret; | 700 | int ret; |
| 701 | const char *src; | ||
| 702 | Dwarf_Die die_mem; | ||
| 790 | 703 | ||
| 791 | ret = dwarf_srclines(lf->cu_die, &lines, &cnt, &__dw_error); | 704 | line_list__init(&lf->lr->line_list); |
| 792 | DIE_IF(ret != DW_DLV_OK); | 705 | ret = dwarf_getsrclines(&lf->cu_die, &lines, &nlines); |
| 706 | DIE_IF(ret != 0); | ||
| 793 | 707 | ||
| 794 | for (i = 0; i < cnt; i++) { | 708 | for (i = 0; i < nlines; i++) { |
| 795 | ret = dwarf_line_srcfileno(lines[i], &fno, &__dw_error); | 709 | line = dwarf_onesrcline(lines, i); |
| 796 | DIE_IF(ret != DW_DLV_OK); | 710 | ret = dwarf_lineno(line, &lineno); |
| 797 | if (fno != lf->fno) | 711 | DIE_IF(ret != 0); |
| 798 | continue; | ||
| 799 | |||
| 800 | ret = dwarf_lineno(lines[i], &lineno, &__dw_error); | ||
| 801 | DIE_IF(ret != DW_DLV_OK); | ||
| 802 | if (lf->lno_s > lineno || lf->lno_e < lineno) | 712 | if (lf->lno_s > lineno || lf->lno_e < lineno) |
| 803 | continue; | 713 | continue; |
| 804 | 714 | ||
| 805 | /* Filter line in the function address range */ | 715 | if (sp_die) { |
| 806 | if (lf->addr_s && lf->addr_e) { | 716 | /* Address filtering 1: does sp_die include addr? */ |
| 807 | ret = dwarf_lineaddr(lines[i], &addr, &__dw_error); | 717 | ret = dwarf_lineaddr(line, &addr); |
| 808 | DIE_IF(ret != DW_DLV_OK); | 718 | DIE_IF(ret != 0); |
| 809 | if (lf->addr_s > addr || lf->addr_e <= addr) | 719 | if (!dwarf_haspc(sp_die, addr)) |
| 720 | continue; | ||
| 721 | |||
| 722 | /* Address filtering 2: No child include addr? */ | ||
| 723 | if (die_get_inlinefunc(sp_die, addr, &die_mem)) | ||
| 810 | continue; | 724 | continue; |
| 811 | } | 725 | } |
| 812 | line_range_add_line(lf->lr, (unsigned int)lineno); | 726 | |
| 727 | /* TODO: Get fileno from line, but how? */ | ||
| 728 | src = dwarf_linesrc(line, NULL, NULL); | ||
| 729 | if (strtailcmp(src, lf->fname) != 0) | ||
| 730 | continue; | ||
| 731 | |||
| 732 | /* Copy real path */ | ||
| 733 | if (!lf->lr->path) | ||
| 734 | lf->lr->path = strdup(src); | ||
| 735 | line_list__add_line(&lf->lr->line_list, (unsigned int)lineno); | ||
| 813 | } | 736 | } |
| 814 | dwarf_srclines_dealloc(__dw_debug, lines, cnt); | 737 | /* Update status */ |
| 815 | if (!list_empty(&lf->lr->line_list)) | 738 | if (!list_empty(&lf->lr->line_list)) |
| 816 | lf->found = 1; | 739 | lf->found = 1; |
| 740 | else { | ||
| 741 | free(lf->lr->path); | ||
| 742 | lf->lr->path = NULL; | ||
| 743 | } | ||
| 744 | } | ||
| 745 | |||
| 746 | static int line_range_inline_cb(Dwarf_Die *in_die, void *data) | ||
| 747 | { | ||
| 748 | find_line_range_by_line(in_die, (struct line_finder *)data); | ||
| 749 | return DWARF_CB_ABORT; /* No need to find other instances */ | ||
| 817 | } | 750 | } |
| 818 | 751 | ||
| 819 | /* Search function from function name */ | 752 | /* Search function from function name */ |
| 820 | static int linefunc_callback(struct die_link *dlink, void *data) | 753 | static int line_range_search_cb(Dwarf_Die *sp_die, void *data) |
| 821 | { | 754 | { |
| 822 | struct line_finder *lf = (struct line_finder *)data; | 755 | struct line_finder *lf = (struct line_finder *)data; |
| 823 | struct line_range *lr = lf->lr; | 756 | struct line_range *lr = lf->lr; |
| 824 | Dwarf_Half tag; | ||
| 825 | int ret; | ||
| 826 | 757 | ||
| 827 | ret = dwarf_tag(dlink->die, &tag, &__dw_error); | 758 | if (dwarf_tag(sp_die) == DW_TAG_subprogram && |
| 828 | DIE_IF(ret == DW_DLV_ERROR); | 759 | die_compare_name(sp_die, lr->function) == 0) { |
| 829 | if (tag == DW_TAG_subprogram && | 760 | lf->fname = dwarf_decl_file(sp_die); |
| 830 | die_compare_name(dlink->die, lr->function) == 0) { | 761 | dwarf_decl_line(sp_die, &lr->offset); |
| 831 | /* Get the address range of this function */ | 762 | pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset); |
| 832 | ret = dwarf_highpc(dlink->die, &lf->addr_e, &__dw_error); | ||
| 833 | if (ret == DW_DLV_OK) | ||
| 834 | ret = dwarf_lowpc(dlink->die, &lf->addr_s, &__dw_error); | ||
| 835 | DIE_IF(ret == DW_DLV_ERROR); | ||
| 836 | if (ret == DW_DLV_NO_ENTRY) { | ||
| 837 | lf->addr_s = 0; | ||
| 838 | lf->addr_e = 0; | ||
| 839 | } | ||
| 840 | |||
| 841 | lf->fno = die_get_decl_file(dlink->die); | ||
| 842 | lr->offset = die_get_decl_line(dlink->die);; | ||
| 843 | lf->lno_s = lr->offset + lr->start; | 763 | lf->lno_s = lr->offset + lr->start; |
| 844 | if (!lr->end) | 764 | if (!lr->end) |
| 845 | lf->lno_e = (Dwarf_Unsigned)-1; | 765 | lf->lno_e = INT_MAX; |
| 846 | else | 766 | else |
| 847 | lf->lno_e = lr->offset + lr->end; | 767 | lf->lno_e = lr->offset + lr->end; |
| 848 | lr->start = lf->lno_s; | 768 | lr->start = lf->lno_s; |
| 849 | lr->end = lf->lno_e; | 769 | lr->end = lf->lno_e; |
| 850 | find_line_range_by_line(lf); | 770 | if (dwarf_func_inline(sp_die)) |
| 851 | /* If we find a target function, this should be end. */ | 771 | dwarf_func_inline_instances(sp_die, |
| 852 | lf->found = 1; | 772 | line_range_inline_cb, lf); |
| 773 | else | ||
| 774 | find_line_range_by_line(sp_die, lf); | ||
| 853 | return 1; | 775 | return 1; |
| 854 | } | 776 | } |
| 855 | return 0; | 777 | return 0; |
| @@ -857,55 +779,55 @@ static int linefunc_callback(struct die_link *dlink, void *data) | |||
| 857 | 779 | ||
| 858 | static void find_line_range_by_func(struct line_finder *lf) | 780 | static void find_line_range_by_func(struct line_finder *lf) |
| 859 | { | 781 | { |
| 860 | search_die_from_children(lf->cu_die, linefunc_callback, lf); | 782 | dwarf_getfuncs(&lf->cu_die, line_range_search_cb, lf, 0); |
| 861 | } | 783 | } |
| 862 | 784 | ||
| 863 | int find_line_range(int fd, struct line_range *lr) | 785 | int find_line_range(int fd, struct line_range *lr) |
| 864 | { | 786 | { |
| 865 | Dwarf_Half addr_size = 0; | 787 | struct line_finder lf = {.lr = lr, .found = 0}; |
| 866 | Dwarf_Unsigned next_cuh = 0; | ||
| 867 | int ret; | 788 | int ret; |
| 868 | struct line_finder lf = {.lr = lr}; | 789 | Dwarf_Off off = 0, noff; |
| 790 | size_t cuhl; | ||
| 791 | Dwarf_Die *diep; | ||
| 792 | Dwarf *dbg; | ||
| 869 | 793 | ||
| 870 | ret = dwarf_init(fd, DW_DLC_READ, 0, 0, &__dw_debug, &__dw_error); | 794 | dbg = dwarf_begin(fd, DWARF_C_READ); |
| 871 | if (ret != DW_DLV_OK) | 795 | if (!dbg) |
| 872 | return -ENOENT; | 796 | return -ENOENT; |
| 873 | 797 | ||
| 798 | /* Loop on CUs (Compilation Unit) */ | ||
| 874 | while (!lf.found) { | 799 | while (!lf.found) { |
| 875 | /* Search CU (Compilation Unit) */ | 800 | ret = dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL); |
| 876 | ret = dwarf_next_cu_header(__dw_debug, NULL, NULL, NULL, | 801 | if (ret != 0) |
| 877 | &addr_size, &next_cuh, &__dw_error); | ||
| 878 | DIE_IF(ret == DW_DLV_ERROR); | ||
| 879 | if (ret == DW_DLV_NO_ENTRY) | ||
| 880 | break; | 802 | break; |
| 881 | 803 | ||
| 882 | /* Get the DIE(Debugging Information Entry) of this CU */ | 804 | /* Get the DIE(Debugging Information Entry) of this CU */ |
| 883 | ret = dwarf_siblingof(__dw_debug, 0, &lf.cu_die, &__dw_error); | 805 | diep = dwarf_offdie(dbg, off + cuhl, &lf.cu_die); |
| 884 | DIE_IF(ret != DW_DLV_OK); | 806 | if (!diep) |
| 807 | continue; | ||
| 885 | 808 | ||
| 886 | /* Check if target file is included. */ | 809 | /* Check if target file is included. */ |
| 887 | if (lr->file) | 810 | if (lr->file) |
| 888 | lf.fno = cu_find_fileno(lf.cu_die, lr->file); | 811 | lf.fname = cu_find_realpath(&lf.cu_die, lr->file); |
| 812 | else | ||
| 813 | lf.fname = 0; | ||
| 889 | 814 | ||
| 890 | if (!lr->file || lf.fno) { | 815 | if (!lr->file || lf.fname) { |
| 891 | if (lr->function) | 816 | if (lr->function) |
| 892 | find_line_range_by_func(&lf); | 817 | find_line_range_by_func(&lf); |
| 893 | else { | 818 | else { |
| 894 | lf.lno_s = lr->start; | 819 | lf.lno_s = lr->start; |
| 895 | if (!lr->end) | 820 | if (!lr->end) |
| 896 | lf.lno_e = (Dwarf_Unsigned)-1; | 821 | lf.lno_e = INT_MAX; |
| 897 | else | 822 | else |
| 898 | lf.lno_e = lr->end; | 823 | lf.lno_e = lr->end; |
| 899 | find_line_range_by_line(&lf); | 824 | find_line_range_by_line(NULL, &lf); |
| 900 | } | 825 | } |
| 901 | /* Get the real file path */ | ||
| 902 | if (lf.found) | ||
| 903 | cu_get_filename(lf.cu_die, lf.fno, &lr->path); | ||
| 904 | } | 826 | } |
| 905 | dwarf_dealloc(__dw_debug, lf.cu_die, DW_DLA_DIE); | 827 | off = noff; |
| 906 | } | 828 | } |
| 907 | ret = dwarf_finish(__dw_debug, &__dw_error); | 829 | pr_debug("path: %lx\n", (unsigned long)lr->path); |
| 908 | DIE_IF(ret != DW_DLV_OK); | 830 | dwarf_end(dbg); |
| 909 | return lf.found; | 831 | return lf.found; |
| 910 | } | 832 | } |
| 911 | 833 | ||
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index 972b386116f1..d1a651793ba6 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h | |||
| @@ -1,6 +1,7 @@ | |||
| 1 | #ifndef _PROBE_FINDER_H | 1 | #ifndef _PROBE_FINDER_H |
| 2 | #define _PROBE_FINDER_H | 2 | #define _PROBE_FINDER_H |
| 3 | 3 | ||
| 4 | #include <stdbool.h> | ||
| 4 | #include "util.h" | 5 | #include "util.h" |
| 5 | 6 | ||
| 6 | #define MAX_PATH_LEN 256 | 7 | #define MAX_PATH_LEN 256 |
| @@ -20,6 +21,7 @@ struct probe_point { | |||
| 20 | /* Inputs */ | 21 | /* Inputs */ |
| 21 | char *file; /* File name */ | 22 | char *file; /* File name */ |
| 22 | int line; /* Line number */ | 23 | int line; /* Line number */ |
| 24 | char *lazy_line; /* Lazy line pattern */ | ||
| 23 | 25 | ||
| 24 | char *function; /* Function name */ | 26 | char *function; /* Function name */ |
| 25 | int offset; /* Offset bytes */ | 27 | int offset; /* Offset bytes */ |
| @@ -46,53 +48,46 @@ struct line_range { | |||
| 46 | char *function; /* Function name */ | 48 | char *function; /* Function name */ |
| 47 | unsigned int start; /* Start line number */ | 49 | unsigned int start; /* Start line number */ |
| 48 | unsigned int end; /* End line number */ | 50 | unsigned int end; /* End line number */ |
| 49 | unsigned int offset; /* Start line offset */ | 51 | int offset; /* Start line offset */ |
| 50 | char *path; /* Real path name */ | 52 | char *path; /* Real path name */ |
| 51 | struct list_head line_list; /* Visible lines */ | 53 | struct list_head line_list; /* Visible lines */ |
| 52 | }; | 54 | }; |
| 53 | 55 | ||
| 54 | #ifndef NO_LIBDWARF | 56 | #ifndef NO_DWARF_SUPPORT |
| 55 | extern int find_probepoint(int fd, struct probe_point *pp); | 57 | extern int find_probe_point(int fd, struct probe_point *pp); |
| 56 | extern int find_line_range(int fd, struct line_range *lr); | 58 | extern int find_line_range(int fd, struct line_range *lr); |
| 57 | 59 | ||
| 58 | /* Workaround for undefined _MIPS_SZLONG bug in libdwarf.h: */ | ||
| 59 | #ifndef _MIPS_SZLONG | ||
| 60 | # define _MIPS_SZLONG 0 | ||
| 61 | #endif | ||
| 62 | |||
| 63 | #include <dwarf.h> | 60 | #include <dwarf.h> |
| 64 | #include <libdwarf.h> | 61 | #include <libdw.h> |
| 65 | 62 | ||
| 66 | struct probe_finder { | 63 | struct probe_finder { |
| 67 | struct probe_point *pp; /* Target probe point */ | 64 | struct probe_point *pp; /* Target probe point */ |
| 68 | 65 | ||
| 69 | /* For function searching */ | 66 | /* For function searching */ |
| 70 | Dwarf_Addr addr; /* Address */ | 67 | Dwarf_Addr addr; /* Address */ |
| 71 | Dwarf_Unsigned fno; /* File number */ | 68 | const char *fname; /* File name */ |
| 72 | Dwarf_Unsigned lno; /* Line number */ | 69 | int lno; /* Line number */ |
| 73 | Dwarf_Off inl_offs; /* Inline offset */ | 70 | Dwarf_Die cu_die; /* Current CU */ |
| 74 | Dwarf_Die cu_die; /* Current CU */ | ||
| 75 | 71 | ||
| 76 | /* For variable searching */ | 72 | /* For variable searching */ |
| 77 | Dwarf_Addr cu_base; /* Current CU base address */ | 73 | Dwarf_Op *fb_ops; /* Frame base attribute */ |
| 78 | Dwarf_Locdesc fbloc; /* Location of Current Frame Base */ | 74 | Dwarf_Addr cu_base; /* Current CU base address */ |
| 79 | const char *var; /* Current variable name */ | 75 | const char *var; /* Current variable name */ |
| 80 | char *buf; /* Current output buffer */ | 76 | char *buf; /* Current output buffer */ |
| 81 | int len; /* Length of output buffer */ | 77 | int len; /* Length of output buffer */ |
| 78 | struct list_head lcache; /* Line cache for lazy match */ | ||
| 82 | }; | 79 | }; |
| 83 | 80 | ||
| 84 | struct line_finder { | 81 | struct line_finder { |
| 85 | struct line_range *lr; /* Target line range */ | 82 | struct line_range *lr; /* Target line range */ |
| 86 | 83 | ||
| 87 | Dwarf_Unsigned fno; /* File number */ | 84 | const char *fname; /* File name */ |
| 88 | Dwarf_Unsigned lno_s; /* Start line number */ | 85 | int lno_s; /* Start line number */ |
| 89 | Dwarf_Unsigned lno_e; /* End line number */ | 86 | int lno_e; /* End line number */ |
| 90 | Dwarf_Addr addr_s; /* Start address */ | 87 | Dwarf_Die cu_die; /* Current CU */ |
| 91 | Dwarf_Addr addr_e; /* End address */ | ||
| 92 | Dwarf_Die cu_die; /* Current CU */ | ||
| 93 | int found; | 88 | int found; |
| 94 | }; | 89 | }; |
| 95 | 90 | ||
| 96 | #endif /* NO_LIBDWARF */ | 91 | #endif /* NO_DWARF_SUPPORT */ |
| 97 | 92 | ||
| 98 | #endif /*_PROBE_FINDER_H */ | 93 | #endif /*_PROBE_FINDER_H */ |
diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c index c397d4f6f748..a175949ed216 100644 --- a/tools/perf/util/string.c +++ b/tools/perf/util/string.c | |||
| @@ -265,21 +265,21 @@ error: | |||
| 265 | return false; | 265 | return false; |
| 266 | } | 266 | } |
| 267 | 267 | ||
| 268 | /** | 268 | /* Glob/lazy pattern matching */ |
| 269 | * strglobmatch - glob expression pattern matching | 269 | static bool __match_glob(const char *str, const char *pat, bool ignore_space) |
| 270 | * @str: the target string to match | ||
| 271 | * @pat: the pattern string to match | ||
| 272 | * | ||
| 273 | * This returns true if the @str matches @pat. @pat can includes wildcards | ||
| 274 | * ('*','?') and character classes ([CHARS], complementation and ranges are | ||
| 275 | * also supported). Also, this supports escape character ('\') to use special | ||
| 276 | * characters as normal character. | ||
| 277 | * | ||
| 278 | * Note: if @pat syntax is broken, this always returns false. | ||
| 279 | */ | ||
| 280 | bool strglobmatch(const char *str, const char *pat) | ||
| 281 | { | 270 | { |
| 282 | while (*str && *pat && *pat != '*') { | 271 | while (*str && *pat && *pat != '*') { |
| 272 | if (ignore_space) { | ||
| 273 | /* Ignore spaces for lazy matching */ | ||
| 274 | if (isspace(*str)) { | ||
| 275 | str++; | ||
| 276 | continue; | ||
| 277 | } | ||
| 278 | if (isspace(*pat)) { | ||
| 279 | pat++; | ||
| 280 | continue; | ||
| 281 | } | ||
| 282 | } | ||
| 283 | if (*pat == '?') { /* Matches any single character */ | 283 | if (*pat == '?') { /* Matches any single character */ |
| 284 | str++; | 284 | str++; |
| 285 | pat++; | 285 | pat++; |
| @@ -308,3 +308,32 @@ bool strglobmatch(const char *str, const char *pat) | |||
| 308 | return !*str && !*pat; | 308 | return !*str && !*pat; |
| 309 | } | 309 | } |
| 310 | 310 | ||
| 311 | /** | ||
| 312 | * strglobmatch - glob expression pattern matching | ||
| 313 | * @str: the target string to match | ||
| 314 | * @pat: the pattern string to match | ||
| 315 | * | ||
| 316 | * This returns true if the @str matches @pat. @pat can includes wildcards | ||
| 317 | * ('*','?') and character classes ([CHARS], complementation and ranges are | ||
| 318 | * also supported). Also, this supports escape character ('\') to use special | ||
| 319 | * characters as normal character. | ||
| 320 | * | ||
| 321 | * Note: if @pat syntax is broken, this always returns false. | ||
| 322 | */ | ||
| 323 | bool strglobmatch(const char *str, const char *pat) | ||
| 324 | { | ||
| 325 | return __match_glob(str, pat, false); | ||
| 326 | } | ||
| 327 | |||
| 328 | /** | ||
| 329 | * strlazymatch - matching pattern strings lazily with glob pattern | ||
| 330 | * @str: the target string to match | ||
| 331 | * @pat: the pattern string to match | ||
| 332 | * | ||
| 333 | * This is similar to strglobmatch, except this ignores spaces in | ||
| 334 | * the target string. | ||
| 335 | */ | ||
| 336 | bool strlazymatch(const char *str, const char *pat) | ||
| 337 | { | ||
| 338 | return __match_glob(str, pat, true); | ||
| 339 | } | ||
diff --git a/tools/perf/util/string.h b/tools/perf/util/string.h index 02ede58c54b4..542e44de3719 100644 --- a/tools/perf/util/string.h +++ b/tools/perf/util/string.h | |||
| @@ -10,6 +10,7 @@ s64 perf_atoll(const char *str); | |||
| 10 | char **argv_split(const char *str, int *argcp); | 10 | char **argv_split(const char *str, int *argcp); |
| 11 | void argv_free(char **argv); | 11 | void argv_free(char **argv); |
| 12 | bool strglobmatch(const char *str, const char *pat); | 12 | bool strglobmatch(const char *str, const char *pat); |
| 13 | bool strlazymatch(const char *str, const char *pat); | ||
| 13 | 14 | ||
| 14 | #define _STR(x) #x | 15 | #define _STR(x) #x |
| 15 | #define STR(x) _STR(x) | 16 | #define STR(x) _STR(x) |
