14ea42b18SMasami Hiramatsu /* 24ea42b18SMasami Hiramatsu * probe-finder.c : C expression to kprobe event converter 34ea42b18SMasami Hiramatsu * 44ea42b18SMasami Hiramatsu * Written by Masami Hiramatsu <mhiramat@redhat.com> 54ea42b18SMasami Hiramatsu * 64ea42b18SMasami Hiramatsu * This program is free software; you can redistribute it and/or modify 74ea42b18SMasami Hiramatsu * it under the terms of the GNU General Public License as published by 84ea42b18SMasami Hiramatsu * the Free Software Foundation; either version 2 of the License, or 94ea42b18SMasami Hiramatsu * (at your option) any later version. 104ea42b18SMasami Hiramatsu * 114ea42b18SMasami Hiramatsu * This program is distributed in the hope that it will be useful, 124ea42b18SMasami Hiramatsu * but WITHOUT ANY WARRANTY; without even the implied warranty of 134ea42b18SMasami Hiramatsu * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 144ea42b18SMasami Hiramatsu * GNU General Public License for more details. 154ea42b18SMasami Hiramatsu * 164ea42b18SMasami Hiramatsu * You should have received a copy of the GNU General Public License 174ea42b18SMasami Hiramatsu * along with this program; if not, write to the Free Software 184ea42b18SMasami Hiramatsu * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 194ea42b18SMasami Hiramatsu * 204ea42b18SMasami Hiramatsu */ 214ea42b18SMasami Hiramatsu 224ea42b18SMasami Hiramatsu #include <sys/utsname.h> 234ea42b18SMasami Hiramatsu #include <sys/types.h> 244ea42b18SMasami Hiramatsu #include <sys/stat.h> 254ea42b18SMasami Hiramatsu #include <fcntl.h> 264ea42b18SMasami Hiramatsu #include <errno.h> 274ea42b18SMasami Hiramatsu #include <stdio.h> 284ea42b18SMasami Hiramatsu #include <unistd.h> 294ea42b18SMasami Hiramatsu #include <getopt.h> 304ea42b18SMasami Hiramatsu #include <stdlib.h> 314ea42b18SMasami Hiramatsu #include <string.h> 324ea42b18SMasami Hiramatsu #include <stdarg.h> 334ea42b18SMasami Hiramatsu #include <ctype.h> 34cd932c59SIan Munsie #include <dwarf-regs.h> 35074fc0e4SMasami Hiramatsu 362a9c8c36SMasami Hiramatsu #include "string.h" 3789c69c0eSMasami Hiramatsu #include "event.h" 3889c69c0eSMasami Hiramatsu #include "debug.h" 39074fc0e4SMasami Hiramatsu #include "util.h" 409ed7e1b8SChase Douglas #include "symbol.h" 414ea42b18SMasami Hiramatsu #include "probe-finder.h" 424ea42b18SMasami Hiramatsu 434984912eSMasami Hiramatsu /* Kprobe tracer basic type is up to u64 */ 444984912eSMasami Hiramatsu #define MAX_BASIC_TYPE_BITS 64 454984912eSMasami Hiramatsu 464ea42b18SMasami Hiramatsu /* 474ea42b18SMasami Hiramatsu * Compare the tail of two strings. 484ea42b18SMasami Hiramatsu * Return 0 if whole of either string is same as another's tail part. 494ea42b18SMasami Hiramatsu */ 504ea42b18SMasami Hiramatsu static int strtailcmp(const char *s1, const char *s2) 514ea42b18SMasami Hiramatsu { 524ea42b18SMasami Hiramatsu int i1 = strlen(s1); 534ea42b18SMasami Hiramatsu int i2 = strlen(s2); 54d56728b8SJuha Leppanen while (--i1 >= 0 && --i2 >= 0) { 554ea42b18SMasami Hiramatsu if (s1[i1] != s2[i2]) 564ea42b18SMasami Hiramatsu return s1[i1] - s2[i2]; 574ea42b18SMasami Hiramatsu } 584ea42b18SMasami Hiramatsu return 0; 594ea42b18SMasami Hiramatsu } 604ea42b18SMasami Hiramatsu 619ed7e1b8SChase Douglas /* 629ed7e1b8SChase Douglas * Find a src file from a DWARF tag path. Prepend optional source path prefix 639ed7e1b8SChase Douglas * and chop off leading directories that do not exist. Result is passed back as 649ed7e1b8SChase Douglas * a newly allocated path on success. 659ed7e1b8SChase Douglas * Return 0 if file was found and readable, -errno otherwise. 669ed7e1b8SChase Douglas */ 679ed7e1b8SChase Douglas static int get_real_path(const char *raw_path, char **new_path) 689ed7e1b8SChase Douglas { 699ed7e1b8SChase Douglas if (!symbol_conf.source_prefix) { 709ed7e1b8SChase Douglas if (access(raw_path, R_OK) == 0) { 719ed7e1b8SChase Douglas *new_path = strdup(raw_path); 729ed7e1b8SChase Douglas return 0; 739ed7e1b8SChase Douglas } else 749ed7e1b8SChase Douglas return -errno; 759ed7e1b8SChase Douglas } 769ed7e1b8SChase Douglas 779ed7e1b8SChase Douglas *new_path = malloc((strlen(symbol_conf.source_prefix) + 789ed7e1b8SChase Douglas strlen(raw_path) + 2)); 799ed7e1b8SChase Douglas if (!*new_path) 809ed7e1b8SChase Douglas return -ENOMEM; 819ed7e1b8SChase Douglas 829ed7e1b8SChase Douglas for (;;) { 839ed7e1b8SChase Douglas sprintf(*new_path, "%s/%s", symbol_conf.source_prefix, 849ed7e1b8SChase Douglas raw_path); 859ed7e1b8SChase Douglas 869ed7e1b8SChase Douglas if (access(*new_path, R_OK) == 0) 879ed7e1b8SChase Douglas return 0; 889ed7e1b8SChase Douglas 899ed7e1b8SChase Douglas switch (errno) { 909ed7e1b8SChase Douglas case ENAMETOOLONG: 919ed7e1b8SChase Douglas case ENOENT: 929ed7e1b8SChase Douglas case EROFS: 939ed7e1b8SChase Douglas case EFAULT: 949ed7e1b8SChase Douglas raw_path = strchr(++raw_path, '/'); 959ed7e1b8SChase Douglas if (!raw_path) { 969ed7e1b8SChase Douglas free(*new_path); 979ed7e1b8SChase Douglas *new_path = NULL; 989ed7e1b8SChase Douglas return -ENOENT; 999ed7e1b8SChase Douglas } 1009ed7e1b8SChase Douglas continue; 1019ed7e1b8SChase Douglas 1029ed7e1b8SChase Douglas default: 1039ed7e1b8SChase Douglas free(*new_path); 1049ed7e1b8SChase Douglas *new_path = NULL; 1059ed7e1b8SChase Douglas return -errno; 1069ed7e1b8SChase Douglas } 1079ed7e1b8SChase Douglas } 1089ed7e1b8SChase Douglas } 1099ed7e1b8SChase Douglas 1102a9c8c36SMasami Hiramatsu /* Line number list operations */ 1112a9c8c36SMasami Hiramatsu 1122a9c8c36SMasami Hiramatsu /* Add a line to line number list */ 113d3b63d7aSMasami Hiramatsu static int line_list__add_line(struct list_head *head, int line) 1142a9c8c36SMasami Hiramatsu { 1152a9c8c36SMasami Hiramatsu struct line_node *ln; 1162a9c8c36SMasami Hiramatsu struct list_head *p; 1172a9c8c36SMasami Hiramatsu 1182a9c8c36SMasami Hiramatsu /* Reverse search, because new line will be the last one */ 1192a9c8c36SMasami Hiramatsu list_for_each_entry_reverse(ln, head, list) { 1202a9c8c36SMasami Hiramatsu if (ln->line < line) { 1212a9c8c36SMasami Hiramatsu p = &ln->list; 1222a9c8c36SMasami Hiramatsu goto found; 1232a9c8c36SMasami Hiramatsu } else if (ln->line == line) /* Already exist */ 124e334016fSMasami Hiramatsu return 1; 1252a9c8c36SMasami Hiramatsu } 1262a9c8c36SMasami Hiramatsu /* List is empty, or the smallest entry */ 1272a9c8c36SMasami Hiramatsu p = head; 1282a9c8c36SMasami Hiramatsu found: 1292a9c8c36SMasami Hiramatsu pr_debug("line list: add a line %u\n", line); 130e334016fSMasami Hiramatsu ln = zalloc(sizeof(struct line_node)); 131e334016fSMasami Hiramatsu if (ln == NULL) 132e334016fSMasami Hiramatsu return -ENOMEM; 1332a9c8c36SMasami Hiramatsu ln->line = line; 1342a9c8c36SMasami Hiramatsu INIT_LIST_HEAD(&ln->list); 1352a9c8c36SMasami Hiramatsu list_add(&ln->list, p); 136e334016fSMasami Hiramatsu return 0; 1372a9c8c36SMasami Hiramatsu } 1382a9c8c36SMasami Hiramatsu 1392a9c8c36SMasami Hiramatsu /* Check if the line in line number list */ 140d3b63d7aSMasami Hiramatsu static int line_list__has_line(struct list_head *head, int line) 1412a9c8c36SMasami Hiramatsu { 1422a9c8c36SMasami Hiramatsu struct line_node *ln; 1432a9c8c36SMasami Hiramatsu 1442a9c8c36SMasami Hiramatsu /* Reverse search, because new line will be the last one */ 1452a9c8c36SMasami Hiramatsu list_for_each_entry(ln, head, list) 1462a9c8c36SMasami Hiramatsu if (ln->line == line) 1472a9c8c36SMasami Hiramatsu return 1; 1482a9c8c36SMasami Hiramatsu 1492a9c8c36SMasami Hiramatsu return 0; 1502a9c8c36SMasami Hiramatsu } 1512a9c8c36SMasami Hiramatsu 1522a9c8c36SMasami Hiramatsu /* Init line number list */ 1532a9c8c36SMasami Hiramatsu static void line_list__init(struct list_head *head) 1542a9c8c36SMasami Hiramatsu { 1552a9c8c36SMasami Hiramatsu INIT_LIST_HEAD(head); 1562a9c8c36SMasami Hiramatsu } 1572a9c8c36SMasami Hiramatsu 1582a9c8c36SMasami Hiramatsu /* Free line number list */ 1592a9c8c36SMasami Hiramatsu static void line_list__free(struct list_head *head) 1602a9c8c36SMasami Hiramatsu { 1612a9c8c36SMasami Hiramatsu struct line_node *ln; 1622a9c8c36SMasami Hiramatsu while (!list_empty(head)) { 1632a9c8c36SMasami Hiramatsu ln = list_first_entry(head, struct line_node, list); 1642a9c8c36SMasami Hiramatsu list_del(&ln->list); 1652a9c8c36SMasami Hiramatsu free(ln); 1662a9c8c36SMasami Hiramatsu } 1672a9c8c36SMasami Hiramatsu } 1682a9c8c36SMasami Hiramatsu 1692a9c8c36SMasami Hiramatsu /* Dwarf wrappers */ 1702a9c8c36SMasami Hiramatsu 1712a9c8c36SMasami Hiramatsu /* Find the realpath of the target file. */ 1722a9c8c36SMasami Hiramatsu static const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname) 1734ea42b18SMasami Hiramatsu { 174804b3606SMasami Hiramatsu Dwarf_Files *files; 175804b3606SMasami Hiramatsu size_t nfiles, i; 176accd3cc4SArnaldo Carvalho de Melo const char *src = NULL; 1774ea42b18SMasami Hiramatsu int ret; 1784ea42b18SMasami Hiramatsu 1794ea42b18SMasami Hiramatsu if (!fname) 1802a9c8c36SMasami Hiramatsu return NULL; 181631c9defSMasami Hiramatsu 182804b3606SMasami Hiramatsu ret = dwarf_getsrcfiles(cu_die, &files, &nfiles); 1832a9c8c36SMasami Hiramatsu if (ret != 0) 1842a9c8c36SMasami Hiramatsu return NULL; 1852a9c8c36SMasami Hiramatsu 186804b3606SMasami Hiramatsu for (i = 0; i < nfiles; i++) { 187804b3606SMasami Hiramatsu src = dwarf_filesrc(files, i, NULL, NULL); 1882a9c8c36SMasami Hiramatsu if (strtailcmp(src, fname) == 0) 189804b3606SMasami Hiramatsu break; 190804b3606SMasami Hiramatsu } 191c9e38582SMasami Hiramatsu if (i == nfiles) 192c9e38582SMasami Hiramatsu return NULL; 1932a9c8c36SMasami Hiramatsu return src; 194631c9defSMasami Hiramatsu } 195631c9defSMasami Hiramatsu 196016f262eSMasami Hiramatsu /* Compare diename and tname */ 197016f262eSMasami Hiramatsu static bool die_compare_name(Dwarf_Die *dw_die, const char *tname) 198016f262eSMasami Hiramatsu { 199016f262eSMasami Hiramatsu const char *name; 200016f262eSMasami Hiramatsu name = dwarf_diename(dw_die); 201b55a87adSMasami Hiramatsu return name ? strcmp(tname, name) : -1; 202016f262eSMasami Hiramatsu } 203016f262eSMasami Hiramatsu 2047df2f329SMasami Hiramatsu /* Get type die, but skip qualifiers and typedef */ 2057df2f329SMasami Hiramatsu static Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) 2067df2f329SMasami Hiramatsu { 2077df2f329SMasami Hiramatsu Dwarf_Attribute attr; 2087df2f329SMasami Hiramatsu int tag; 2097df2f329SMasami Hiramatsu 2107df2f329SMasami Hiramatsu do { 2117df2f329SMasami Hiramatsu if (dwarf_attr(vr_die, DW_AT_type, &attr) == NULL || 2127df2f329SMasami Hiramatsu dwarf_formref_die(&attr, die_mem) == NULL) 2137df2f329SMasami Hiramatsu return NULL; 2147df2f329SMasami Hiramatsu 2157df2f329SMasami Hiramatsu tag = dwarf_tag(die_mem); 2167df2f329SMasami Hiramatsu vr_die = die_mem; 2177df2f329SMasami Hiramatsu } while (tag == DW_TAG_const_type || 2187df2f329SMasami Hiramatsu tag == DW_TAG_restrict_type || 2197df2f329SMasami Hiramatsu tag == DW_TAG_volatile_type || 2207df2f329SMasami Hiramatsu tag == DW_TAG_shared_type || 2217df2f329SMasami Hiramatsu tag == DW_TAG_typedef); 2227df2f329SMasami Hiramatsu 2237df2f329SMasami Hiramatsu return die_mem; 2247df2f329SMasami Hiramatsu } 2257df2f329SMasami Hiramatsu 2264984912eSMasami Hiramatsu static bool die_is_signed_type(Dwarf_Die *tp_die) 2274984912eSMasami Hiramatsu { 2284984912eSMasami Hiramatsu Dwarf_Attribute attr; 2294984912eSMasami Hiramatsu Dwarf_Word ret; 2304984912eSMasami Hiramatsu 2314984912eSMasami Hiramatsu if (dwarf_attr(tp_die, DW_AT_encoding, &attr) == NULL || 2324984912eSMasami Hiramatsu dwarf_formudata(&attr, &ret) != 0) 2334984912eSMasami Hiramatsu return false; 2344984912eSMasami Hiramatsu 2354984912eSMasami Hiramatsu return (ret == DW_ATE_signed_char || ret == DW_ATE_signed || 2364984912eSMasami Hiramatsu ret == DW_ATE_signed_fixed); 2374984912eSMasami Hiramatsu } 2384984912eSMasami Hiramatsu 2394984912eSMasami Hiramatsu static int die_get_byte_size(Dwarf_Die *tp_die) 2404984912eSMasami Hiramatsu { 2414984912eSMasami Hiramatsu Dwarf_Attribute attr; 2424984912eSMasami Hiramatsu Dwarf_Word ret; 2434984912eSMasami Hiramatsu 2444984912eSMasami Hiramatsu if (dwarf_attr(tp_die, DW_AT_byte_size, &attr) == NULL || 2454984912eSMasami Hiramatsu dwarf_formudata(&attr, &ret) != 0) 2464984912eSMasami Hiramatsu return 0; 2474984912eSMasami Hiramatsu 2484984912eSMasami Hiramatsu return (int)ret; 2494984912eSMasami Hiramatsu } 2504984912eSMasami Hiramatsu 251de1439d8SMasami Hiramatsu /* Get data_member_location offset */ 252de1439d8SMasami Hiramatsu static int die_get_data_member_location(Dwarf_Die *mb_die, Dwarf_Word *offs) 253de1439d8SMasami Hiramatsu { 254de1439d8SMasami Hiramatsu Dwarf_Attribute attr; 255de1439d8SMasami Hiramatsu Dwarf_Op *expr; 256de1439d8SMasami Hiramatsu size_t nexpr; 257de1439d8SMasami Hiramatsu int ret; 258de1439d8SMasami Hiramatsu 259de1439d8SMasami Hiramatsu if (dwarf_attr(mb_die, DW_AT_data_member_location, &attr) == NULL) 260de1439d8SMasami Hiramatsu return -ENOENT; 261de1439d8SMasami Hiramatsu 262de1439d8SMasami Hiramatsu if (dwarf_formudata(&attr, offs) != 0) { 263de1439d8SMasami Hiramatsu /* DW_AT_data_member_location should be DW_OP_plus_uconst */ 264de1439d8SMasami Hiramatsu ret = dwarf_getlocation(&attr, &expr, &nexpr); 265de1439d8SMasami Hiramatsu if (ret < 0 || nexpr == 0) 266de1439d8SMasami Hiramatsu return -ENOENT; 267de1439d8SMasami Hiramatsu 268de1439d8SMasami Hiramatsu if (expr[0].atom != DW_OP_plus_uconst || nexpr != 1) { 269de1439d8SMasami Hiramatsu pr_debug("Unable to get offset:Unexpected OP %x (%zd)\n", 270de1439d8SMasami Hiramatsu expr[0].atom, nexpr); 271de1439d8SMasami Hiramatsu return -ENOTSUP; 272de1439d8SMasami Hiramatsu } 273de1439d8SMasami Hiramatsu *offs = (Dwarf_Word)expr[0].number; 274de1439d8SMasami Hiramatsu } 275de1439d8SMasami Hiramatsu return 0; 276de1439d8SMasami Hiramatsu } 277de1439d8SMasami Hiramatsu 278016f262eSMasami Hiramatsu /* Return values for die_find callbacks */ 279016f262eSMasami Hiramatsu enum { 280016f262eSMasami Hiramatsu DIE_FIND_CB_FOUND = 0, /* End of Search */ 281016f262eSMasami Hiramatsu DIE_FIND_CB_CHILD = 1, /* Search only children */ 282016f262eSMasami Hiramatsu DIE_FIND_CB_SIBLING = 2, /* Search only siblings */ 283016f262eSMasami Hiramatsu DIE_FIND_CB_CONTINUE = 3, /* Search children and siblings */ 284016f262eSMasami Hiramatsu }; 285016f262eSMasami Hiramatsu 286016f262eSMasami Hiramatsu /* Search a child die */ 287016f262eSMasami Hiramatsu static Dwarf_Die *die_find_child(Dwarf_Die *rt_die, 288016f262eSMasami Hiramatsu int (*callback)(Dwarf_Die *, void *), 289016f262eSMasami Hiramatsu void *data, Dwarf_Die *die_mem) 290016f262eSMasami Hiramatsu { 291016f262eSMasami Hiramatsu Dwarf_Die child_die; 292016f262eSMasami Hiramatsu int ret; 293016f262eSMasami Hiramatsu 294016f262eSMasami Hiramatsu ret = dwarf_child(rt_die, die_mem); 295016f262eSMasami Hiramatsu if (ret != 0) 296016f262eSMasami Hiramatsu return NULL; 297016f262eSMasami Hiramatsu 298016f262eSMasami Hiramatsu do { 299016f262eSMasami Hiramatsu ret = callback(die_mem, data); 300016f262eSMasami Hiramatsu if (ret == DIE_FIND_CB_FOUND) 301016f262eSMasami Hiramatsu return die_mem; 302016f262eSMasami Hiramatsu 303016f262eSMasami Hiramatsu if ((ret & DIE_FIND_CB_CHILD) && 304016f262eSMasami Hiramatsu die_find_child(die_mem, callback, data, &child_die)) { 305016f262eSMasami Hiramatsu memcpy(die_mem, &child_die, sizeof(Dwarf_Die)); 306016f262eSMasami Hiramatsu return die_mem; 307016f262eSMasami Hiramatsu } 308016f262eSMasami Hiramatsu } while ((ret & DIE_FIND_CB_SIBLING) && 309016f262eSMasami Hiramatsu dwarf_siblingof(die_mem, die_mem) == 0); 310016f262eSMasami Hiramatsu 311016f262eSMasami Hiramatsu return NULL; 312016f262eSMasami Hiramatsu } 313016f262eSMasami Hiramatsu 314804b3606SMasami Hiramatsu struct __addr_die_search_param { 315804b3606SMasami Hiramatsu Dwarf_Addr addr; 316804b3606SMasami Hiramatsu Dwarf_Die *die_mem; 317804b3606SMasami Hiramatsu }; 318804b3606SMasami Hiramatsu 319804b3606SMasami Hiramatsu static int __die_search_func_cb(Dwarf_Die *fn_die, void *data) 320804b3606SMasami Hiramatsu { 321804b3606SMasami Hiramatsu struct __addr_die_search_param *ad = data; 322804b3606SMasami Hiramatsu 323804b3606SMasami Hiramatsu if (dwarf_tag(fn_die) == DW_TAG_subprogram && 324804b3606SMasami Hiramatsu dwarf_haspc(fn_die, ad->addr)) { 325804b3606SMasami Hiramatsu memcpy(ad->die_mem, fn_die, sizeof(Dwarf_Die)); 326804b3606SMasami Hiramatsu return DWARF_CB_ABORT; 327804b3606SMasami Hiramatsu } 328804b3606SMasami Hiramatsu return DWARF_CB_OK; 329804b3606SMasami Hiramatsu } 330804b3606SMasami Hiramatsu 331804b3606SMasami Hiramatsu /* Search a real subprogram including this line, */ 33295a3e4c4SMasami Hiramatsu static Dwarf_Die *die_find_real_subprogram(Dwarf_Die *cu_die, Dwarf_Addr addr, 333804b3606SMasami Hiramatsu Dwarf_Die *die_mem) 334804b3606SMasami Hiramatsu { 335804b3606SMasami Hiramatsu struct __addr_die_search_param ad; 336804b3606SMasami Hiramatsu ad.addr = addr; 337804b3606SMasami Hiramatsu ad.die_mem = die_mem; 338804b3606SMasami Hiramatsu /* dwarf_getscopes can't find subprogram. */ 339804b3606SMasami Hiramatsu if (!dwarf_getfuncs(cu_die, __die_search_func_cb, &ad, 0)) 340804b3606SMasami Hiramatsu return NULL; 341804b3606SMasami Hiramatsu else 342804b3606SMasami Hiramatsu return die_mem; 343804b3606SMasami Hiramatsu } 344804b3606SMasami Hiramatsu 345016f262eSMasami Hiramatsu /* die_find callback for inline function search */ 346016f262eSMasami Hiramatsu static int __die_find_inline_cb(Dwarf_Die *die_mem, void *data) 347016f262eSMasami Hiramatsu { 348016f262eSMasami Hiramatsu Dwarf_Addr *addr = data; 349016f262eSMasami Hiramatsu 350016f262eSMasami Hiramatsu if (dwarf_tag(die_mem) == DW_TAG_inlined_subroutine && 351016f262eSMasami Hiramatsu dwarf_haspc(die_mem, *addr)) 352016f262eSMasami Hiramatsu return DIE_FIND_CB_FOUND; 353016f262eSMasami Hiramatsu 354016f262eSMasami Hiramatsu return DIE_FIND_CB_CONTINUE; 355016f262eSMasami Hiramatsu } 356016f262eSMasami Hiramatsu 357161a26b0SMasami Hiramatsu /* Similar to dwarf_getfuncs, but returns inlined_subroutine if exists. */ 35895a3e4c4SMasami Hiramatsu static Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, 359161a26b0SMasami Hiramatsu Dwarf_Die *die_mem) 360161a26b0SMasami Hiramatsu { 361016f262eSMasami Hiramatsu return die_find_child(sp_die, __die_find_inline_cb, &addr, die_mem); 362161a26b0SMasami Hiramatsu } 363161a26b0SMasami Hiramatsu 364016f262eSMasami Hiramatsu static int __die_find_variable_cb(Dwarf_Die *die_mem, void *data) 3654ea42b18SMasami Hiramatsu { 366016f262eSMasami Hiramatsu const char *name = data; 367e92b85e1SMasami Hiramatsu int tag; 3684ea42b18SMasami Hiramatsu 369e92b85e1SMasami Hiramatsu tag = dwarf_tag(die_mem); 370e92b85e1SMasami Hiramatsu if ((tag == DW_TAG_formal_parameter || 371e92b85e1SMasami Hiramatsu tag == DW_TAG_variable) && 372e92b85e1SMasami Hiramatsu (die_compare_name(die_mem, name) == 0)) 373016f262eSMasami Hiramatsu return DIE_FIND_CB_FOUND; 3744ea42b18SMasami Hiramatsu 375016f262eSMasami Hiramatsu return DIE_FIND_CB_CONTINUE; 3764ea42b18SMasami Hiramatsu } 3774ea42b18SMasami Hiramatsu 378016f262eSMasami Hiramatsu /* Find a variable called 'name' */ 379016f262eSMasami Hiramatsu static Dwarf_Die *die_find_variable(Dwarf_Die *sp_die, const char *name, 380016f262eSMasami Hiramatsu Dwarf_Die *die_mem) 381016f262eSMasami Hiramatsu { 382016f262eSMasami Hiramatsu return die_find_child(sp_die, __die_find_variable_cb, (void *)name, 383016f262eSMasami Hiramatsu die_mem); 384e92b85e1SMasami Hiramatsu } 385b0ef0732SMasami Hiramatsu 3867df2f329SMasami Hiramatsu static int __die_find_member_cb(Dwarf_Die *die_mem, void *data) 3877df2f329SMasami Hiramatsu { 3887df2f329SMasami Hiramatsu const char *name = data; 3897df2f329SMasami Hiramatsu 3907df2f329SMasami Hiramatsu if ((dwarf_tag(die_mem) == DW_TAG_member) && 3917df2f329SMasami Hiramatsu (die_compare_name(die_mem, name) == 0)) 3927df2f329SMasami Hiramatsu return DIE_FIND_CB_FOUND; 3937df2f329SMasami Hiramatsu 3947df2f329SMasami Hiramatsu return DIE_FIND_CB_SIBLING; 3957df2f329SMasami Hiramatsu } 3967df2f329SMasami Hiramatsu 3977df2f329SMasami Hiramatsu /* Find a member called 'name' */ 3987df2f329SMasami Hiramatsu static Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name, 3997df2f329SMasami Hiramatsu Dwarf_Die *die_mem) 4007df2f329SMasami Hiramatsu { 4017df2f329SMasami Hiramatsu return die_find_child(st_die, __die_find_member_cb, (void *)name, 4027df2f329SMasami Hiramatsu die_mem); 4037df2f329SMasami Hiramatsu } 4047df2f329SMasami Hiramatsu 4054ea42b18SMasami Hiramatsu /* 4064ea42b18SMasami Hiramatsu * Probe finder related functions 4074ea42b18SMasami Hiramatsu */ 4084ea42b18SMasami Hiramatsu 409*b7dcb857SMasami Hiramatsu static struct kprobe_trace_arg_ref *alloc_trace_arg_ref(long offs) 4104ea42b18SMasami Hiramatsu { 411*b7dcb857SMasami Hiramatsu struct kprobe_trace_arg_ref *ref; 412*b7dcb857SMasami Hiramatsu ref = zalloc(sizeof(struct kprobe_trace_arg_ref)); 413*b7dcb857SMasami Hiramatsu if (ref != NULL) 414*b7dcb857SMasami Hiramatsu ref->offset = offs; 415*b7dcb857SMasami Hiramatsu return ref; 416*b7dcb857SMasami Hiramatsu } 417*b7dcb857SMasami Hiramatsu 418*b7dcb857SMasami Hiramatsu /* Show a location */ 419*b7dcb857SMasami Hiramatsu static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf) 420*b7dcb857SMasami Hiramatsu { 421*b7dcb857SMasami Hiramatsu Dwarf_Attribute attr; 422*b7dcb857SMasami Hiramatsu Dwarf_Op *op; 423*b7dcb857SMasami Hiramatsu size_t nops; 424804b3606SMasami Hiramatsu unsigned int regn; 425804b3606SMasami Hiramatsu Dwarf_Word offs = 0; 4264235b045SMasami Hiramatsu bool ref = false; 4274ea42b18SMasami Hiramatsu const char *regs; 4284235b045SMasami Hiramatsu struct kprobe_trace_arg *tvar = pf->tvar; 429*b7dcb857SMasami Hiramatsu int ret; 430*b7dcb857SMasami Hiramatsu 431*b7dcb857SMasami Hiramatsu /* TODO: handle more than 1 exprs */ 432*b7dcb857SMasami Hiramatsu if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL || 433*b7dcb857SMasami Hiramatsu dwarf_getlocation_addr(&attr, pf->addr, &op, &nops, 1) <= 0 || 434*b7dcb857SMasami Hiramatsu nops == 0) { 435*b7dcb857SMasami Hiramatsu /* TODO: Support const_value */ 436*b7dcb857SMasami Hiramatsu pr_err("Failed to find the location of %s at this address.\n" 437*b7dcb857SMasami Hiramatsu " Perhaps, it has been optimized out.\n", pf->pvar->var); 438*b7dcb857SMasami Hiramatsu return -ENOENT; 439*b7dcb857SMasami Hiramatsu } 440*b7dcb857SMasami Hiramatsu 441*b7dcb857SMasami Hiramatsu if (op->atom == DW_OP_addr) { 442*b7dcb857SMasami Hiramatsu /* Static variables on memory (not stack), make @varname */ 443*b7dcb857SMasami Hiramatsu ret = strlen(dwarf_diename(vr_die)); 444*b7dcb857SMasami Hiramatsu tvar->value = zalloc(ret + 2); 445*b7dcb857SMasami Hiramatsu if (tvar->value == NULL) 446*b7dcb857SMasami Hiramatsu return -ENOMEM; 447*b7dcb857SMasami Hiramatsu snprintf(tvar->value, ret + 2, "@%s", dwarf_diename(vr_die)); 448*b7dcb857SMasami Hiramatsu tvar->ref = alloc_trace_arg_ref((long)offs); 449*b7dcb857SMasami Hiramatsu if (tvar->ref == NULL) 450*b7dcb857SMasami Hiramatsu return -ENOMEM; 451*b7dcb857SMasami Hiramatsu return 0; 452*b7dcb857SMasami Hiramatsu } 4534ea42b18SMasami Hiramatsu 4544ea42b18SMasami Hiramatsu /* If this is based on frame buffer, set the offset */ 455804b3606SMasami Hiramatsu if (op->atom == DW_OP_fbreg) { 456b55a87adSMasami Hiramatsu if (pf->fb_ops == NULL) { 457b55a87adSMasami Hiramatsu pr_warning("The attribute of frame base is not " 458b55a87adSMasami Hiramatsu "supported.\n"); 459b55a87adSMasami Hiramatsu return -ENOTSUP; 460b55a87adSMasami Hiramatsu } 4614235b045SMasami Hiramatsu ref = true; 462804b3606SMasami Hiramatsu offs = op->number; 463804b3606SMasami Hiramatsu op = &pf->fb_ops[0]; 464804b3606SMasami Hiramatsu } 4654ea42b18SMasami Hiramatsu 466804b3606SMasami Hiramatsu if (op->atom >= DW_OP_breg0 && op->atom <= DW_OP_breg31) { 467804b3606SMasami Hiramatsu regn = op->atom - DW_OP_breg0; 468804b3606SMasami Hiramatsu offs += op->number; 4694235b045SMasami Hiramatsu ref = true; 470804b3606SMasami Hiramatsu } else if (op->atom >= DW_OP_reg0 && op->atom <= DW_OP_reg31) { 471804b3606SMasami Hiramatsu regn = op->atom - DW_OP_reg0; 472804b3606SMasami Hiramatsu } else if (op->atom == DW_OP_bregx) { 473804b3606SMasami Hiramatsu regn = op->number; 474804b3606SMasami Hiramatsu offs += op->number2; 4754235b045SMasami Hiramatsu ref = true; 476804b3606SMasami Hiramatsu } else if (op->atom == DW_OP_regx) { 477804b3606SMasami Hiramatsu regn = op->number; 478b55a87adSMasami Hiramatsu } else { 479b55a87adSMasami Hiramatsu pr_warning("DW_OP %x is not supported.\n", op->atom); 480b55a87adSMasami Hiramatsu return -ENOTSUP; 481b55a87adSMasami Hiramatsu } 4824ea42b18SMasami Hiramatsu 4834ea42b18SMasami Hiramatsu regs = get_arch_regstr(regn); 484b55a87adSMasami Hiramatsu if (!regs) { 485cd932c59SIan Munsie pr_warning("Mapping for DWARF register number %u missing on this architecture.", regn); 486b55a87adSMasami Hiramatsu return -ERANGE; 487b55a87adSMasami Hiramatsu } 4884ea42b18SMasami Hiramatsu 48902b95dadSMasami Hiramatsu tvar->value = strdup(regs); 49002b95dadSMasami Hiramatsu if (tvar->value == NULL) 49102b95dadSMasami Hiramatsu return -ENOMEM; 49202b95dadSMasami Hiramatsu 4934235b045SMasami Hiramatsu if (ref) { 494*b7dcb857SMasami Hiramatsu tvar->ref = alloc_trace_arg_ref((long)offs); 495e334016fSMasami Hiramatsu if (tvar->ref == NULL) 496e334016fSMasami Hiramatsu return -ENOMEM; 4974235b045SMasami Hiramatsu } 498b55a87adSMasami Hiramatsu return 0; 4994ea42b18SMasami Hiramatsu } 5004ea42b18SMasami Hiramatsu 501b55a87adSMasami Hiramatsu static int convert_variable_type(Dwarf_Die *vr_die, 50273317b95SMasami Hiramatsu struct kprobe_trace_arg *tvar, 50373317b95SMasami Hiramatsu const char *cast) 5044984912eSMasami Hiramatsu { 50573317b95SMasami Hiramatsu struct kprobe_trace_arg_ref **ref_ptr = &tvar->ref; 5064984912eSMasami Hiramatsu Dwarf_Die type; 5074984912eSMasami Hiramatsu char buf[16]; 5084984912eSMasami Hiramatsu int ret; 5094984912eSMasami Hiramatsu 51073317b95SMasami Hiramatsu /* TODO: check all types */ 51173317b95SMasami Hiramatsu if (cast && strcmp(cast, "string") != 0) { 51273317b95SMasami Hiramatsu /* Non string type is OK */ 51373317b95SMasami Hiramatsu tvar->type = strdup(cast); 51473317b95SMasami Hiramatsu return (tvar->type == NULL) ? -ENOMEM : 0; 51573317b95SMasami Hiramatsu } 51673317b95SMasami Hiramatsu 517b55a87adSMasami Hiramatsu if (die_get_real_type(vr_die, &type) == NULL) { 518b55a87adSMasami Hiramatsu pr_warning("Failed to get a type information of %s.\n", 5194984912eSMasami Hiramatsu dwarf_diename(vr_die)); 520b55a87adSMasami Hiramatsu return -ENOENT; 521b55a87adSMasami Hiramatsu } 5224984912eSMasami Hiramatsu 523b2a3c12bSMasami Hiramatsu pr_debug("%s type is %s.\n", 524b2a3c12bSMasami Hiramatsu dwarf_diename(vr_die), dwarf_diename(&type)); 525b2a3c12bSMasami Hiramatsu 52673317b95SMasami Hiramatsu if (cast && strcmp(cast, "string") == 0) { /* String type */ 52773317b95SMasami Hiramatsu ret = dwarf_tag(&type); 52873317b95SMasami Hiramatsu if (ret != DW_TAG_pointer_type && 52973317b95SMasami Hiramatsu ret != DW_TAG_array_type) { 53073317b95SMasami Hiramatsu pr_warning("Failed to cast into string: " 53173317b95SMasami Hiramatsu "%s(%s) is not a pointer nor array.", 53273317b95SMasami Hiramatsu dwarf_diename(vr_die), dwarf_diename(&type)); 53373317b95SMasami Hiramatsu return -EINVAL; 53473317b95SMasami Hiramatsu } 53573317b95SMasami Hiramatsu if (ret == DW_TAG_pointer_type) { 53673317b95SMasami Hiramatsu if (die_get_real_type(&type, &type) == NULL) { 53773317b95SMasami Hiramatsu pr_warning("Failed to get a type information."); 53873317b95SMasami Hiramatsu return -ENOENT; 53973317b95SMasami Hiramatsu } 54073317b95SMasami Hiramatsu while (*ref_ptr) 54173317b95SMasami Hiramatsu ref_ptr = &(*ref_ptr)->next; 54273317b95SMasami Hiramatsu /* Add new reference with offset +0 */ 54373317b95SMasami Hiramatsu *ref_ptr = zalloc(sizeof(struct kprobe_trace_arg_ref)); 54473317b95SMasami Hiramatsu if (*ref_ptr == NULL) { 54573317b95SMasami Hiramatsu pr_warning("Out of memory error\n"); 54673317b95SMasami Hiramatsu return -ENOMEM; 54773317b95SMasami Hiramatsu } 54873317b95SMasami Hiramatsu } 54973317b95SMasami Hiramatsu if (die_compare_name(&type, "char") != 0 && 55073317b95SMasami Hiramatsu die_compare_name(&type, "unsigned char") != 0) { 55173317b95SMasami Hiramatsu pr_warning("Failed to cast into string: " 55273317b95SMasami Hiramatsu "%s is not (unsigned) char *.", 55373317b95SMasami Hiramatsu dwarf_diename(vr_die)); 55473317b95SMasami Hiramatsu return -EINVAL; 55573317b95SMasami Hiramatsu } 55673317b95SMasami Hiramatsu tvar->type = strdup(cast); 55773317b95SMasami Hiramatsu return (tvar->type == NULL) ? -ENOMEM : 0; 55873317b95SMasami Hiramatsu } 55973317b95SMasami Hiramatsu 5604984912eSMasami Hiramatsu ret = die_get_byte_size(&type) * 8; 5614984912eSMasami Hiramatsu if (ret) { 5624984912eSMasami Hiramatsu /* Check the bitwidth */ 5634984912eSMasami Hiramatsu if (ret > MAX_BASIC_TYPE_BITS) { 564b55a87adSMasami Hiramatsu pr_info("%s exceeds max-bitwidth." 5654984912eSMasami Hiramatsu " Cut down to %d bits.\n", 5664984912eSMasami Hiramatsu dwarf_diename(&type), MAX_BASIC_TYPE_BITS); 5674984912eSMasami Hiramatsu ret = MAX_BASIC_TYPE_BITS; 5684984912eSMasami Hiramatsu } 5694984912eSMasami Hiramatsu 5704984912eSMasami Hiramatsu ret = snprintf(buf, 16, "%c%d", 5714984912eSMasami Hiramatsu die_is_signed_type(&type) ? 's' : 'u', ret); 572b55a87adSMasami Hiramatsu if (ret < 0 || ret >= 16) { 573b55a87adSMasami Hiramatsu if (ret >= 16) 574b55a87adSMasami Hiramatsu ret = -E2BIG; 575b55a87adSMasami Hiramatsu pr_warning("Failed to convert variable type: %s\n", 576b55a87adSMasami Hiramatsu strerror(-ret)); 577b55a87adSMasami Hiramatsu return ret; 578b55a87adSMasami Hiramatsu } 57973317b95SMasami Hiramatsu tvar->type = strdup(buf); 58073317b95SMasami Hiramatsu if (tvar->type == NULL) 58102b95dadSMasami Hiramatsu return -ENOMEM; 5824984912eSMasami Hiramatsu } 583b55a87adSMasami Hiramatsu return 0; 5844984912eSMasami Hiramatsu } 5854984912eSMasami Hiramatsu 586b55a87adSMasami Hiramatsu static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, 5877df2f329SMasami Hiramatsu struct perf_probe_arg_field *field, 5884984912eSMasami Hiramatsu struct kprobe_trace_arg_ref **ref_ptr, 5894984912eSMasami Hiramatsu Dwarf_Die *die_mem) 5907df2f329SMasami Hiramatsu { 5917df2f329SMasami Hiramatsu struct kprobe_trace_arg_ref *ref = *ref_ptr; 5927df2f329SMasami Hiramatsu Dwarf_Die type; 5937df2f329SMasami Hiramatsu Dwarf_Word offs; 594b2a3c12bSMasami Hiramatsu int ret, tag; 5957df2f329SMasami Hiramatsu 5967df2f329SMasami Hiramatsu pr_debug("converting %s in %s\n", field->name, varname); 597b55a87adSMasami Hiramatsu if (die_get_real_type(vr_die, &type) == NULL) { 598b55a87adSMasami Hiramatsu pr_warning("Failed to get the type of %s.\n", varname); 599b55a87adSMasami Hiramatsu return -ENOENT; 600b55a87adSMasami Hiramatsu } 601b2a3c12bSMasami Hiramatsu pr_debug2("Var real type: (%x)\n", (unsigned)dwarf_dieoffset(&type)); 602b2a3c12bSMasami Hiramatsu tag = dwarf_tag(&type); 6037df2f329SMasami Hiramatsu 604b2a3c12bSMasami Hiramatsu if (field->name[0] == '[' && 605b2a3c12bSMasami Hiramatsu (tag == DW_TAG_array_type || tag == DW_TAG_pointer_type)) { 606b2a3c12bSMasami Hiramatsu if (field->next) 607b2a3c12bSMasami Hiramatsu /* Save original type for next field */ 608b2a3c12bSMasami Hiramatsu memcpy(die_mem, &type, sizeof(*die_mem)); 609b2a3c12bSMasami Hiramatsu /* Get the type of this array */ 610b2a3c12bSMasami Hiramatsu if (die_get_real_type(&type, &type) == NULL) { 611b2a3c12bSMasami Hiramatsu pr_warning("Failed to get the type of %s.\n", varname); 612b2a3c12bSMasami Hiramatsu return -ENOENT; 613b2a3c12bSMasami Hiramatsu } 614b2a3c12bSMasami Hiramatsu pr_debug2("Array real type: (%x)\n", 615b2a3c12bSMasami Hiramatsu (unsigned)dwarf_dieoffset(&type)); 616b2a3c12bSMasami Hiramatsu if (tag == DW_TAG_pointer_type) { 617b2a3c12bSMasami Hiramatsu ref = zalloc(sizeof(struct kprobe_trace_arg_ref)); 618b2a3c12bSMasami Hiramatsu if (ref == NULL) 619b2a3c12bSMasami Hiramatsu return -ENOMEM; 620b2a3c12bSMasami Hiramatsu if (*ref_ptr) 621b2a3c12bSMasami Hiramatsu (*ref_ptr)->next = ref; 622b2a3c12bSMasami Hiramatsu else 623b2a3c12bSMasami Hiramatsu *ref_ptr = ref; 624b2a3c12bSMasami Hiramatsu } 625b2a3c12bSMasami Hiramatsu ref->offset += die_get_byte_size(&type) * field->index; 626b2a3c12bSMasami Hiramatsu if (!field->next) 627b2a3c12bSMasami Hiramatsu /* Save vr_die for converting types */ 628b2a3c12bSMasami Hiramatsu memcpy(die_mem, vr_die, sizeof(*die_mem)); 629b2a3c12bSMasami Hiramatsu goto next; 630b2a3c12bSMasami Hiramatsu } else if (tag == DW_TAG_pointer_type) { 6317df2f329SMasami Hiramatsu /* Check the pointer and dereference */ 632b55a87adSMasami Hiramatsu if (!field->ref) { 633b55a87adSMasami Hiramatsu pr_err("Semantic error: %s must be referred by '->'\n", 6347df2f329SMasami Hiramatsu field->name); 635b55a87adSMasami Hiramatsu return -EINVAL; 636b55a87adSMasami Hiramatsu } 6377df2f329SMasami Hiramatsu /* Get the type pointed by this pointer */ 638b55a87adSMasami Hiramatsu if (die_get_real_type(&type, &type) == NULL) { 639b55a87adSMasami Hiramatsu pr_warning("Failed to get the type of %s.\n", varname); 640b55a87adSMasami Hiramatsu return -ENOENT; 641b55a87adSMasami Hiramatsu } 64212e5a7aeSMasami Hiramatsu /* Verify it is a data structure */ 643b55a87adSMasami Hiramatsu if (dwarf_tag(&type) != DW_TAG_structure_type) { 644b55a87adSMasami Hiramatsu pr_warning("%s is not a data structure.\n", varname); 645b55a87adSMasami Hiramatsu return -EINVAL; 646b55a87adSMasami Hiramatsu } 64712e5a7aeSMasami Hiramatsu 648e334016fSMasami Hiramatsu ref = zalloc(sizeof(struct kprobe_trace_arg_ref)); 649e334016fSMasami Hiramatsu if (ref == NULL) 650e334016fSMasami Hiramatsu return -ENOMEM; 6517df2f329SMasami Hiramatsu if (*ref_ptr) 6527df2f329SMasami Hiramatsu (*ref_ptr)->next = ref; 6537df2f329SMasami Hiramatsu else 6547df2f329SMasami Hiramatsu *ref_ptr = ref; 6557df2f329SMasami Hiramatsu } else { 65612e5a7aeSMasami Hiramatsu /* Verify it is a data structure */ 657b2a3c12bSMasami Hiramatsu if (tag != DW_TAG_structure_type) { 658b55a87adSMasami Hiramatsu pr_warning("%s is not a data structure.\n", varname); 659b55a87adSMasami Hiramatsu return -EINVAL; 660b55a87adSMasami Hiramatsu } 661b2a3c12bSMasami Hiramatsu if (field->name[0] == '[') { 662b2a3c12bSMasami Hiramatsu pr_err("Semantic error: %s is not a pointor nor array.", 663b2a3c12bSMasami Hiramatsu varname); 664b2a3c12bSMasami Hiramatsu return -EINVAL; 665b2a3c12bSMasami Hiramatsu } 666b55a87adSMasami Hiramatsu if (field->ref) { 667b55a87adSMasami Hiramatsu pr_err("Semantic error: %s must be referred by '.'\n", 6687df2f329SMasami Hiramatsu field->name); 669b55a87adSMasami Hiramatsu return -EINVAL; 670b55a87adSMasami Hiramatsu } 671b55a87adSMasami Hiramatsu if (!ref) { 672b55a87adSMasami Hiramatsu pr_warning("Structure on a register is not " 673b55a87adSMasami Hiramatsu "supported yet.\n"); 674b55a87adSMasami Hiramatsu return -ENOTSUP; 675b55a87adSMasami Hiramatsu } 6767df2f329SMasami Hiramatsu } 6777df2f329SMasami Hiramatsu 678b55a87adSMasami Hiramatsu if (die_find_member(&type, field->name, die_mem) == NULL) { 679b55a87adSMasami Hiramatsu pr_warning("%s(tyep:%s) has no member %s.\n", varname, 6807df2f329SMasami Hiramatsu dwarf_diename(&type), field->name); 681b55a87adSMasami Hiramatsu return -EINVAL; 682b55a87adSMasami Hiramatsu } 6837df2f329SMasami Hiramatsu 6847df2f329SMasami Hiramatsu /* Get the offset of the field */ 685de1439d8SMasami Hiramatsu ret = die_get_data_member_location(die_mem, &offs); 686de1439d8SMasami Hiramatsu if (ret < 0) { 687b55a87adSMasami Hiramatsu pr_warning("Failed to get the offset of %s.\n", field->name); 688de1439d8SMasami Hiramatsu return ret; 689b55a87adSMasami Hiramatsu } 6907df2f329SMasami Hiramatsu ref->offset += (long)offs; 6917df2f329SMasami Hiramatsu 692b2a3c12bSMasami Hiramatsu next: 6937df2f329SMasami Hiramatsu /* Converting next field */ 6947df2f329SMasami Hiramatsu if (field->next) 695b55a87adSMasami Hiramatsu return convert_variable_fields(die_mem, field->name, 696b55a87adSMasami Hiramatsu field->next, &ref, die_mem); 697b55a87adSMasami Hiramatsu else 698b55a87adSMasami Hiramatsu return 0; 6997df2f329SMasami Hiramatsu } 7007df2f329SMasami Hiramatsu 7014ea42b18SMasami Hiramatsu /* Show a variables in kprobe event format */ 702b55a87adSMasami Hiramatsu static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) 7034ea42b18SMasami Hiramatsu { 7044984912eSMasami Hiramatsu Dwarf_Die die_mem; 7054ea42b18SMasami Hiramatsu int ret; 7064ea42b18SMasami Hiramatsu 707*b7dcb857SMasami Hiramatsu pr_debug("Converting variable %s into trace event.\n", 708*b7dcb857SMasami Hiramatsu dwarf_diename(vr_die)); 709804b3606SMasami Hiramatsu 710*b7dcb857SMasami Hiramatsu ret = convert_variable_location(vr_die, pf); 711b55a87adSMasami Hiramatsu if (ret == 0 && pf->pvar->field) { 712b55a87adSMasami Hiramatsu ret = convert_variable_fields(vr_die, pf->pvar->var, 7134984912eSMasami Hiramatsu pf->pvar->field, &pf->tvar->ref, 7144984912eSMasami Hiramatsu &die_mem); 7154984912eSMasami Hiramatsu vr_die = &die_mem; 7164984912eSMasami Hiramatsu } 71773317b95SMasami Hiramatsu if (ret == 0) 71873317b95SMasami Hiramatsu ret = convert_variable_type(vr_die, pf->tvar, pf->pvar->type); 719804b3606SMasami Hiramatsu /* *expr will be cached in libdw. Don't free it. */ 720b55a87adSMasami Hiramatsu return ret; 7214ea42b18SMasami Hiramatsu } 7224ea42b18SMasami Hiramatsu 7234ea42b18SMasami Hiramatsu /* Find a variable in a subprogram die */ 724b55a87adSMasami Hiramatsu static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) 7254ea42b18SMasami Hiramatsu { 726*b7dcb857SMasami Hiramatsu Dwarf_Die vr_die, *scopes; 72711a1ca35SMasami Hiramatsu char buf[32], *ptr; 728*b7dcb857SMasami Hiramatsu int ret, nscopes; 7294ea42b18SMasami Hiramatsu 73048481938SMasami Hiramatsu if (pf->pvar->name) 73102b95dadSMasami Hiramatsu pf->tvar->name = strdup(pf->pvar->name); 73248481938SMasami Hiramatsu else { 73302b95dadSMasami Hiramatsu ret = synthesize_perf_probe_arg(pf->pvar, buf, 32); 73402b95dadSMasami Hiramatsu if (ret < 0) 73502b95dadSMasami Hiramatsu return ret; 73611a1ca35SMasami Hiramatsu ptr = strchr(buf, ':'); /* Change type separator to _ */ 73711a1ca35SMasami Hiramatsu if (ptr) 73811a1ca35SMasami Hiramatsu *ptr = '_'; 73902b95dadSMasami Hiramatsu pf->tvar->name = strdup(buf); 74048481938SMasami Hiramatsu } 74102b95dadSMasami Hiramatsu if (pf->tvar->name == NULL) 74202b95dadSMasami Hiramatsu return -ENOMEM; 74348481938SMasami Hiramatsu 74448481938SMasami Hiramatsu if (!is_c_varname(pf->pvar->var)) { 74548481938SMasami Hiramatsu /* Copy raw parameters */ 74602b95dadSMasami Hiramatsu pf->tvar->value = strdup(pf->pvar->var); 74702b95dadSMasami Hiramatsu if (pf->tvar->value == NULL) 74802b95dadSMasami Hiramatsu return -ENOMEM; 74902b95dadSMasami Hiramatsu else 750b55a87adSMasami Hiramatsu return 0; 751b55a87adSMasami Hiramatsu } 752b55a87adSMasami Hiramatsu 7534235b045SMasami Hiramatsu pr_debug("Searching '%s' variable in context.\n", 75448481938SMasami Hiramatsu pf->pvar->var); 7554ea42b18SMasami Hiramatsu /* Search child die for local variables and parameters. */ 756*b7dcb857SMasami Hiramatsu if (die_find_variable(sp_die, pf->pvar->var, &vr_die)) 757*b7dcb857SMasami Hiramatsu ret = convert_variable(&vr_die, pf); 758*b7dcb857SMasami Hiramatsu else { 759*b7dcb857SMasami Hiramatsu /* Search upper class */ 760*b7dcb857SMasami Hiramatsu nscopes = dwarf_getscopes_die(sp_die, &scopes); 761*b7dcb857SMasami Hiramatsu if (nscopes > 0) { 762*b7dcb857SMasami Hiramatsu ret = dwarf_getscopevar(scopes, nscopes, pf->pvar->var, 763*b7dcb857SMasami Hiramatsu 0, NULL, 0, 0, &vr_die); 764*b7dcb857SMasami Hiramatsu if (ret >= 0) 765*b7dcb857SMasami Hiramatsu ret = convert_variable(&vr_die, pf); 766*b7dcb857SMasami Hiramatsu else 767*b7dcb857SMasami Hiramatsu ret = -ENOENT; 768*b7dcb857SMasami Hiramatsu free(scopes); 769*b7dcb857SMasami Hiramatsu } else 770*b7dcb857SMasami Hiramatsu ret = -ENOENT; 771*b7dcb857SMasami Hiramatsu } 772*b7dcb857SMasami Hiramatsu if (ret < 0) 773b55a87adSMasami Hiramatsu pr_warning("Failed to find '%s' in this function.\n", 77448481938SMasami Hiramatsu pf->pvar->var); 775*b7dcb857SMasami Hiramatsu return ret; 7764ea42b18SMasami Hiramatsu } 7774ea42b18SMasami Hiramatsu 7784ea42b18SMasami Hiramatsu /* Show a probe point to output buffer */ 779b55a87adSMasami Hiramatsu static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) 7804ea42b18SMasami Hiramatsu { 7814235b045SMasami Hiramatsu struct kprobe_trace_event *tev; 782e92b85e1SMasami Hiramatsu Dwarf_Addr eaddr; 783e92b85e1SMasami Hiramatsu Dwarf_Die die_mem; 784804b3606SMasami Hiramatsu const char *name; 7854235b045SMasami Hiramatsu int ret, i; 786804b3606SMasami Hiramatsu Dwarf_Attribute fb_attr; 787804b3606SMasami Hiramatsu size_t nops; 7884ea42b18SMasami Hiramatsu 789ef4a3565SMasami Hiramatsu if (pf->ntevs == pf->max_tevs) { 790ef4a3565SMasami Hiramatsu pr_warning("Too many( > %d) probe point found.\n", 791ef4a3565SMasami Hiramatsu pf->max_tevs); 792b55a87adSMasami Hiramatsu return -ERANGE; 793b55a87adSMasami Hiramatsu } 7944235b045SMasami Hiramatsu tev = &pf->tevs[pf->ntevs++]; 7954235b045SMasami Hiramatsu 796e92b85e1SMasami Hiramatsu /* If no real subprogram, find a real one */ 797e92b85e1SMasami Hiramatsu if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) { 79895a3e4c4SMasami Hiramatsu sp_die = die_find_real_subprogram(&pf->cu_die, 799e92b85e1SMasami Hiramatsu pf->addr, &die_mem); 800b55a87adSMasami Hiramatsu if (!sp_die) { 801b55a87adSMasami Hiramatsu pr_warning("Failed to find probe point in any " 802b55a87adSMasami Hiramatsu "functions.\n"); 803b55a87adSMasami Hiramatsu return -ENOENT; 804b55a87adSMasami Hiramatsu } 805e92b85e1SMasami Hiramatsu } 806e92b85e1SMasami Hiramatsu 8074235b045SMasami Hiramatsu /* Copy the name of probe point */ 808804b3606SMasami Hiramatsu name = dwarf_diename(sp_die); 809804b3606SMasami Hiramatsu if (name) { 810b55a87adSMasami Hiramatsu if (dwarf_entrypc(sp_die, &eaddr) != 0) { 811b55a87adSMasami Hiramatsu pr_warning("Failed to get entry pc of %s\n", 812b55a87adSMasami Hiramatsu dwarf_diename(sp_die)); 813b55a87adSMasami Hiramatsu return -ENOENT; 814b55a87adSMasami Hiramatsu } 81502b95dadSMasami Hiramatsu tev->point.symbol = strdup(name); 81602b95dadSMasami Hiramatsu if (tev->point.symbol == NULL) 81702b95dadSMasami Hiramatsu return -ENOMEM; 8184235b045SMasami Hiramatsu tev->point.offset = (unsigned long)(pf->addr - eaddr); 8194235b045SMasami Hiramatsu } else 8204ea42b18SMasami Hiramatsu /* This function has no name. */ 8214235b045SMasami Hiramatsu tev->point.offset = (unsigned long)pf->addr; 8224235b045SMasami Hiramatsu 8234235b045SMasami Hiramatsu pr_debug("Probe point found: %s+%lu\n", tev->point.symbol, 8244235b045SMasami Hiramatsu tev->point.offset); 8254ea42b18SMasami Hiramatsu 826804b3606SMasami Hiramatsu /* Get the frame base attribute/ops */ 827804b3606SMasami Hiramatsu dwarf_attr(sp_die, DW_AT_frame_base, &fb_attr); 828d0cb4260SMasami Hiramatsu ret = dwarf_getlocation_addr(&fb_attr, pf->addr, &pf->fb_ops, &nops, 1); 829a34a9854SMasami Hiramatsu if (ret <= 0 || nops == 0) { 830804b3606SMasami Hiramatsu pf->fb_ops = NULL; 8317752f1b0SMasami Hiramatsu #if _ELFUTILS_PREREQ(0, 142) 832a34a9854SMasami Hiramatsu } else if (nops == 1 && pf->fb_ops[0].atom == DW_OP_call_frame_cfa && 833a34a9854SMasami Hiramatsu pf->cfi != NULL) { 834a34a9854SMasami Hiramatsu Dwarf_Frame *frame; 835b55a87adSMasami Hiramatsu if (dwarf_cfi_addrframe(pf->cfi, pf->addr, &frame) != 0 || 836b55a87adSMasami Hiramatsu dwarf_frame_cfa(frame, &pf->fb_ops, &nops) != 0) { 837b55a87adSMasami Hiramatsu pr_warning("Failed to get CFA on 0x%jx\n", 838b55a87adSMasami Hiramatsu (uintmax_t)pf->addr); 839b55a87adSMasami Hiramatsu return -ENOENT; 840b55a87adSMasami Hiramatsu } 8417752f1b0SMasami Hiramatsu #endif 842a34a9854SMasami Hiramatsu } 843804b3606SMasami Hiramatsu 8444ea42b18SMasami Hiramatsu /* Find each argument */ 8454235b045SMasami Hiramatsu tev->nargs = pf->pev->nargs; 846e334016fSMasami Hiramatsu tev->args = zalloc(sizeof(struct kprobe_trace_arg) * tev->nargs); 847e334016fSMasami Hiramatsu if (tev->args == NULL) 848e334016fSMasami Hiramatsu return -ENOMEM; 8494235b045SMasami Hiramatsu for (i = 0; i < pf->pev->nargs; i++) { 8504235b045SMasami Hiramatsu pf->pvar = &pf->pev->args[i]; 8514235b045SMasami Hiramatsu pf->tvar = &tev->args[i]; 852b55a87adSMasami Hiramatsu ret = find_variable(sp_die, pf); 853b55a87adSMasami Hiramatsu if (ret != 0) 854b55a87adSMasami Hiramatsu return ret; 8554ea42b18SMasami Hiramatsu } 856804b3606SMasami Hiramatsu 857804b3606SMasami Hiramatsu /* *pf->fb_ops will be cached in libdw. Don't free it. */ 858804b3606SMasami Hiramatsu pf->fb_ops = NULL; 859b55a87adSMasami Hiramatsu return 0; 8604ea42b18SMasami Hiramatsu } 8614ea42b18SMasami Hiramatsu 8624ea42b18SMasami Hiramatsu /* Find probe point from its line number */ 863b55a87adSMasami Hiramatsu static int find_probe_point_by_line(struct probe_finder *pf) 8644ea42b18SMasami Hiramatsu { 865804b3606SMasami Hiramatsu Dwarf_Lines *lines; 866804b3606SMasami Hiramatsu Dwarf_Line *line; 867804b3606SMasami Hiramatsu size_t nlines, i; 868e92b85e1SMasami Hiramatsu Dwarf_Addr addr; 869804b3606SMasami Hiramatsu int lineno; 870b55a87adSMasami Hiramatsu int ret = 0; 8714ea42b18SMasami Hiramatsu 872b55a87adSMasami Hiramatsu if (dwarf_getsrclines(&pf->cu_die, &lines, &nlines) != 0) { 873b55a87adSMasami Hiramatsu pr_warning("No source lines found in this CU.\n"); 874b55a87adSMasami Hiramatsu return -ENOENT; 875b55a87adSMasami Hiramatsu } 8764ea42b18SMasami Hiramatsu 877b55a87adSMasami Hiramatsu for (i = 0; i < nlines && ret == 0; i++) { 878804b3606SMasami Hiramatsu line = dwarf_onesrcline(lines, i); 879b55a87adSMasami Hiramatsu if (dwarf_lineno(line, &lineno) != 0 || 880b55a87adSMasami Hiramatsu lineno != pf->lno) 8814ea42b18SMasami Hiramatsu continue; 8824ea42b18SMasami Hiramatsu 883804b3606SMasami Hiramatsu /* TODO: Get fileno from line, but how? */ 884804b3606SMasami Hiramatsu if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0) 885804b3606SMasami Hiramatsu continue; 886b0ef0732SMasami Hiramatsu 887b55a87adSMasami Hiramatsu if (dwarf_lineaddr(line, &addr) != 0) { 888b55a87adSMasami Hiramatsu pr_warning("Failed to get the address of the line.\n"); 889b55a87adSMasami Hiramatsu return -ENOENT; 890b55a87adSMasami Hiramatsu } 891804b3606SMasami Hiramatsu pr_debug("Probe line found: line[%d]:%d addr:0x%jx\n", 892804b3606SMasami Hiramatsu (int)i, lineno, (uintmax_t)addr); 8934ea42b18SMasami Hiramatsu pf->addr = addr; 894804b3606SMasami Hiramatsu 895b55a87adSMasami Hiramatsu ret = convert_probe_point(NULL, pf); 8964ea42b18SMasami Hiramatsu /* Continuing, because target line might be inlined. */ 8974ea42b18SMasami Hiramatsu } 898b55a87adSMasami Hiramatsu return ret; 8994ea42b18SMasami Hiramatsu } 9004ea42b18SMasami Hiramatsu 9012a9c8c36SMasami Hiramatsu /* Find lines which match lazy pattern */ 9022a9c8c36SMasami Hiramatsu static int find_lazy_match_lines(struct list_head *head, 9032a9c8c36SMasami Hiramatsu const char *fname, const char *pat) 9042a9c8c36SMasami Hiramatsu { 9052a9c8c36SMasami Hiramatsu char *fbuf, *p1, *p2; 906b448c4b6SArnaldo Carvalho de Melo int fd, line, nlines = -1; 9072a9c8c36SMasami Hiramatsu struct stat st; 9082a9c8c36SMasami Hiramatsu 9092a9c8c36SMasami Hiramatsu fd = open(fname, O_RDONLY); 910b55a87adSMasami Hiramatsu if (fd < 0) { 911b55a87adSMasami Hiramatsu pr_warning("Failed to open %s: %s\n", fname, strerror(-fd)); 912b448c4b6SArnaldo Carvalho de Melo return -errno; 913b55a87adSMasami Hiramatsu } 914b55a87adSMasami Hiramatsu 915b448c4b6SArnaldo Carvalho de Melo if (fstat(fd, &st) < 0) { 916b55a87adSMasami Hiramatsu pr_warning("Failed to get the size of %s: %s\n", 917b55a87adSMasami Hiramatsu fname, strerror(errno)); 918b448c4b6SArnaldo Carvalho de Melo nlines = -errno; 919b448c4b6SArnaldo Carvalho de Melo goto out_close; 920b55a87adSMasami Hiramatsu } 921b448c4b6SArnaldo Carvalho de Melo 922b448c4b6SArnaldo Carvalho de Melo nlines = -ENOMEM; 923b448c4b6SArnaldo Carvalho de Melo fbuf = malloc(st.st_size + 2); 924b448c4b6SArnaldo Carvalho de Melo if (fbuf == NULL) 925b448c4b6SArnaldo Carvalho de Melo goto out_close; 926b448c4b6SArnaldo Carvalho de Melo if (read(fd, fbuf, st.st_size) < 0) { 927b55a87adSMasami Hiramatsu pr_warning("Failed to read %s: %s\n", fname, strerror(errno)); 928b448c4b6SArnaldo Carvalho de Melo nlines = -errno; 929b448c4b6SArnaldo Carvalho de Melo goto out_free_fbuf; 930b55a87adSMasami Hiramatsu } 9312a9c8c36SMasami Hiramatsu fbuf[st.st_size] = '\n'; /* Dummy line */ 9322a9c8c36SMasami Hiramatsu fbuf[st.st_size + 1] = '\0'; 9332a9c8c36SMasami Hiramatsu p1 = fbuf; 9342a9c8c36SMasami Hiramatsu line = 1; 935b448c4b6SArnaldo Carvalho de Melo nlines = 0; 9362a9c8c36SMasami Hiramatsu while ((p2 = strchr(p1, '\n')) != NULL) { 9372a9c8c36SMasami Hiramatsu *p2 = '\0'; 9382a9c8c36SMasami Hiramatsu if (strlazymatch(p1, pat)) { 9392a9c8c36SMasami Hiramatsu line_list__add_line(head, line); 9402a9c8c36SMasami Hiramatsu nlines++; 9412a9c8c36SMasami Hiramatsu } 9422a9c8c36SMasami Hiramatsu line++; 9432a9c8c36SMasami Hiramatsu p1 = p2 + 1; 9442a9c8c36SMasami Hiramatsu } 945b448c4b6SArnaldo Carvalho de Melo out_free_fbuf: 9462a9c8c36SMasami Hiramatsu free(fbuf); 947b448c4b6SArnaldo Carvalho de Melo out_close: 948b448c4b6SArnaldo Carvalho de Melo close(fd); 9492a9c8c36SMasami Hiramatsu return nlines; 9502a9c8c36SMasami Hiramatsu } 9512a9c8c36SMasami Hiramatsu 9522a9c8c36SMasami Hiramatsu /* Find probe points from lazy pattern */ 953b55a87adSMasami Hiramatsu static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) 9542a9c8c36SMasami Hiramatsu { 9552a9c8c36SMasami Hiramatsu Dwarf_Lines *lines; 9562a9c8c36SMasami Hiramatsu Dwarf_Line *line; 9572a9c8c36SMasami Hiramatsu size_t nlines, i; 9582a9c8c36SMasami Hiramatsu Dwarf_Addr addr; 9592a9c8c36SMasami Hiramatsu Dwarf_Die die_mem; 9602a9c8c36SMasami Hiramatsu int lineno; 961b55a87adSMasami Hiramatsu int ret = 0; 9622a9c8c36SMasami Hiramatsu 9632a9c8c36SMasami Hiramatsu if (list_empty(&pf->lcache)) { 9642a9c8c36SMasami Hiramatsu /* Matching lazy line pattern */ 9652a9c8c36SMasami Hiramatsu ret = find_lazy_match_lines(&pf->lcache, pf->fname, 9664235b045SMasami Hiramatsu pf->pev->point.lazy_line); 967b55a87adSMasami Hiramatsu if (ret == 0) { 968b55a87adSMasami Hiramatsu pr_debug("No matched lines found in %s.\n", pf->fname); 969b55a87adSMasami Hiramatsu return 0; 970b55a87adSMasami Hiramatsu } else if (ret < 0) 971b55a87adSMasami Hiramatsu return ret; 9722a9c8c36SMasami Hiramatsu } 9732a9c8c36SMasami Hiramatsu 974b55a87adSMasami Hiramatsu if (dwarf_getsrclines(&pf->cu_die, &lines, &nlines) != 0) { 975b55a87adSMasami Hiramatsu pr_warning("No source lines found in this CU.\n"); 976b55a87adSMasami Hiramatsu return -ENOENT; 977b55a87adSMasami Hiramatsu } 978b55a87adSMasami Hiramatsu 979b55a87adSMasami Hiramatsu for (i = 0; i < nlines && ret >= 0; i++) { 9802a9c8c36SMasami Hiramatsu line = dwarf_onesrcline(lines, i); 9812a9c8c36SMasami Hiramatsu 982b55a87adSMasami Hiramatsu if (dwarf_lineno(line, &lineno) != 0 || 983b55a87adSMasami Hiramatsu !line_list__has_line(&pf->lcache, lineno)) 9842a9c8c36SMasami Hiramatsu continue; 9852a9c8c36SMasami Hiramatsu 9862a9c8c36SMasami Hiramatsu /* TODO: Get fileno from line, but how? */ 9872a9c8c36SMasami Hiramatsu if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0) 9882a9c8c36SMasami Hiramatsu continue; 9892a9c8c36SMasami Hiramatsu 990b55a87adSMasami Hiramatsu if (dwarf_lineaddr(line, &addr) != 0) { 991b55a87adSMasami Hiramatsu pr_debug("Failed to get the address of line %d.\n", 992b55a87adSMasami Hiramatsu lineno); 993b55a87adSMasami Hiramatsu continue; 994b55a87adSMasami Hiramatsu } 9952a9c8c36SMasami Hiramatsu if (sp_die) { 9962a9c8c36SMasami Hiramatsu /* Address filtering 1: does sp_die include addr? */ 9972a9c8c36SMasami Hiramatsu if (!dwarf_haspc(sp_die, addr)) 9982a9c8c36SMasami Hiramatsu continue; 9992a9c8c36SMasami Hiramatsu /* Address filtering 2: No child include addr? */ 100095a3e4c4SMasami Hiramatsu if (die_find_inlinefunc(sp_die, addr, &die_mem)) 10012a9c8c36SMasami Hiramatsu continue; 10022a9c8c36SMasami Hiramatsu } 10032a9c8c36SMasami Hiramatsu 10042a9c8c36SMasami Hiramatsu pr_debug("Probe line found: line[%d]:%d addr:0x%llx\n", 10052a9c8c36SMasami Hiramatsu (int)i, lineno, (unsigned long long)addr); 10062a9c8c36SMasami Hiramatsu pf->addr = addr; 10072a9c8c36SMasami Hiramatsu 1008b55a87adSMasami Hiramatsu ret = convert_probe_point(sp_die, pf); 10092a9c8c36SMasami Hiramatsu /* Continuing, because target line might be inlined. */ 10102a9c8c36SMasami Hiramatsu } 10112a9c8c36SMasami Hiramatsu /* TODO: deallocate lines, but how? */ 1012b55a87adSMasami Hiramatsu return ret; 10132a9c8c36SMasami Hiramatsu } 10142a9c8c36SMasami Hiramatsu 1015b55a87adSMasami Hiramatsu /* Callback parameter with return value */ 1016b55a87adSMasami Hiramatsu struct dwarf_callback_param { 1017b55a87adSMasami Hiramatsu void *data; 1018b55a87adSMasami Hiramatsu int retval; 1019b55a87adSMasami Hiramatsu }; 1020b55a87adSMasami Hiramatsu 1021e92b85e1SMasami Hiramatsu static int probe_point_inline_cb(Dwarf_Die *in_die, void *data) 10224ea42b18SMasami Hiramatsu { 1023b55a87adSMasami Hiramatsu struct dwarf_callback_param *param = data; 1024b55a87adSMasami Hiramatsu struct probe_finder *pf = param->data; 10254235b045SMasami Hiramatsu struct perf_probe_point *pp = &pf->pev->point; 1026b55a87adSMasami Hiramatsu Dwarf_Addr addr; 10274ea42b18SMasami Hiramatsu 10282a9c8c36SMasami Hiramatsu if (pp->lazy_line) 1029b55a87adSMasami Hiramatsu param->retval = find_probe_point_lazy(in_die, pf); 10302a9c8c36SMasami Hiramatsu else { 1031e92b85e1SMasami Hiramatsu /* Get probe address */ 1032b55a87adSMasami Hiramatsu if (dwarf_entrypc(in_die, &addr) != 0) { 1033b55a87adSMasami Hiramatsu pr_warning("Failed to get entry pc of %s.\n", 1034b55a87adSMasami Hiramatsu dwarf_diename(in_die)); 1035b55a87adSMasami Hiramatsu param->retval = -ENOENT; 1036b55a87adSMasami Hiramatsu return DWARF_CB_ABORT; 1037b55a87adSMasami Hiramatsu } 1038b55a87adSMasami Hiramatsu pf->addr = addr; 1039e92b85e1SMasami Hiramatsu pf->addr += pp->offset; 10402a9c8c36SMasami Hiramatsu pr_debug("found inline addr: 0x%jx\n", 10412a9c8c36SMasami Hiramatsu (uintmax_t)pf->addr); 1042e92b85e1SMasami Hiramatsu 1043b55a87adSMasami Hiramatsu param->retval = convert_probe_point(in_die, pf); 10445d1ee041SMasami Hiramatsu if (param->retval < 0) 10455d1ee041SMasami Hiramatsu return DWARF_CB_ABORT; 10462a9c8c36SMasami Hiramatsu } 10472a9c8c36SMasami Hiramatsu 1048e92b85e1SMasami Hiramatsu return DWARF_CB_OK; 1049e92b85e1SMasami Hiramatsu } 1050e92b85e1SMasami Hiramatsu 1051e92b85e1SMasami Hiramatsu /* Search function from function name */ 1052e92b85e1SMasami Hiramatsu static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) 1053e92b85e1SMasami Hiramatsu { 1054b55a87adSMasami Hiramatsu struct dwarf_callback_param *param = data; 1055b55a87adSMasami Hiramatsu struct probe_finder *pf = param->data; 10564235b045SMasami Hiramatsu struct perf_probe_point *pp = &pf->pev->point; 1057e92b85e1SMasami Hiramatsu 1058e92b85e1SMasami Hiramatsu /* Check tag and diename */ 1059e92b85e1SMasami Hiramatsu if (dwarf_tag(sp_die) != DW_TAG_subprogram || 1060e92b85e1SMasami Hiramatsu die_compare_name(sp_die, pp->function) != 0) 1061b55a87adSMasami Hiramatsu return DWARF_CB_OK; 1062e92b85e1SMasami Hiramatsu 1063e92b85e1SMasami Hiramatsu pf->fname = dwarf_decl_file(sp_die); 10642a9c8c36SMasami Hiramatsu if (pp->line) { /* Function relative line */ 1065e92b85e1SMasami Hiramatsu dwarf_decl_line(sp_die, &pf->lno); 1066804b3606SMasami Hiramatsu pf->lno += pp->line; 1067b55a87adSMasami Hiramatsu param->retval = find_probe_point_by_line(pf); 1068e92b85e1SMasami Hiramatsu } else if (!dwarf_func_inline(sp_die)) { 1069e92b85e1SMasami Hiramatsu /* Real function */ 10702a9c8c36SMasami Hiramatsu if (pp->lazy_line) 1071b55a87adSMasami Hiramatsu param->retval = find_probe_point_lazy(sp_die, pf); 10722a9c8c36SMasami Hiramatsu else { 1073b55a87adSMasami Hiramatsu if (dwarf_entrypc(sp_die, &pf->addr) != 0) { 1074b55a87adSMasami Hiramatsu pr_warning("Failed to get entry pc of %s.\n", 1075b55a87adSMasami Hiramatsu dwarf_diename(sp_die)); 1076b55a87adSMasami Hiramatsu param->retval = -ENOENT; 1077b55a87adSMasami Hiramatsu return DWARF_CB_ABORT; 1078b55a87adSMasami Hiramatsu } 10794ea42b18SMasami Hiramatsu pf->addr += pp->offset; 10804ea42b18SMasami Hiramatsu /* TODO: Check the address in this function */ 1081b55a87adSMasami Hiramatsu param->retval = convert_probe_point(sp_die, pf); 10822a9c8c36SMasami Hiramatsu } 1083b55a87adSMasami Hiramatsu } else { 1084b55a87adSMasami Hiramatsu struct dwarf_callback_param _param = {.data = (void *)pf, 1085b55a87adSMasami Hiramatsu .retval = 0}; 1086e92b85e1SMasami Hiramatsu /* Inlined function: search instances */ 1087b55a87adSMasami Hiramatsu dwarf_func_inline_instances(sp_die, probe_point_inline_cb, 1088b55a87adSMasami Hiramatsu &_param); 1089b55a87adSMasami Hiramatsu param->retval = _param.retval; 10904ea42b18SMasami Hiramatsu } 10914ea42b18SMasami Hiramatsu 1092b55a87adSMasami Hiramatsu return DWARF_CB_ABORT; /* Exit; no same symbol in this CU. */ 1093b55a87adSMasami Hiramatsu } 1094b55a87adSMasami Hiramatsu 1095b55a87adSMasami Hiramatsu static int find_probe_point_by_func(struct probe_finder *pf) 10964ea42b18SMasami Hiramatsu { 1097b55a87adSMasami Hiramatsu struct dwarf_callback_param _param = {.data = (void *)pf, 1098b55a87adSMasami Hiramatsu .retval = 0}; 1099b55a87adSMasami Hiramatsu dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, &_param, 0); 1100b55a87adSMasami Hiramatsu return _param.retval; 11014ea42b18SMasami Hiramatsu } 11024ea42b18SMasami Hiramatsu 11034235b045SMasami Hiramatsu /* Find kprobe_trace_events specified by perf_probe_event from debuginfo */ 11044235b045SMasami Hiramatsu int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, 1105ef4a3565SMasami Hiramatsu struct kprobe_trace_event **tevs, int max_tevs) 11064ea42b18SMasami Hiramatsu { 1107ef4a3565SMasami Hiramatsu struct probe_finder pf = {.pev = pev, .max_tevs = max_tevs}; 11084235b045SMasami Hiramatsu struct perf_probe_point *pp = &pev->point; 1109804b3606SMasami Hiramatsu Dwarf_Off off, noff; 1110804b3606SMasami Hiramatsu size_t cuhl; 1111804b3606SMasami Hiramatsu Dwarf_Die *diep; 1112804b3606SMasami Hiramatsu Dwarf *dbg; 1113b55a87adSMasami Hiramatsu int ret = 0; 11144ea42b18SMasami Hiramatsu 1115ef4a3565SMasami Hiramatsu pf.tevs = zalloc(sizeof(struct kprobe_trace_event) * max_tevs); 1116e334016fSMasami Hiramatsu if (pf.tevs == NULL) 1117e334016fSMasami Hiramatsu return -ENOMEM; 11184235b045SMasami Hiramatsu *tevs = pf.tevs; 11194235b045SMasami Hiramatsu pf.ntevs = 0; 11204235b045SMasami Hiramatsu 1121804b3606SMasami Hiramatsu dbg = dwarf_begin(fd, DWARF_C_READ); 1122b55a87adSMasami Hiramatsu if (!dbg) { 1123b55a87adSMasami Hiramatsu pr_warning("No dwarf info found in the vmlinux - " 1124b55a87adSMasami Hiramatsu "please rebuild with CONFIG_DEBUG_INFO=y.\n"); 1125b448c4b6SArnaldo Carvalho de Melo free(pf.tevs); 1126b448c4b6SArnaldo Carvalho de Melo *tevs = NULL; 1127b55a87adSMasami Hiramatsu return -EBADF; 1128b55a87adSMasami Hiramatsu } 11294ea42b18SMasami Hiramatsu 11307752f1b0SMasami Hiramatsu #if _ELFUTILS_PREREQ(0, 142) 1131a34a9854SMasami Hiramatsu /* Get the call frame information from this dwarf */ 1132a34a9854SMasami Hiramatsu pf.cfi = dwarf_getcfi(dbg); 11337752f1b0SMasami Hiramatsu #endif 1134a34a9854SMasami Hiramatsu 1135804b3606SMasami Hiramatsu off = 0; 11362a9c8c36SMasami Hiramatsu line_list__init(&pf.lcache); 1137804b3606SMasami Hiramatsu /* Loop on CUs (Compilation Unit) */ 1138b55a87adSMasami Hiramatsu while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL) && 1139b55a87adSMasami Hiramatsu ret >= 0) { 11404ea42b18SMasami Hiramatsu /* Get the DIE(Debugging Information Entry) of this CU */ 1141804b3606SMasami Hiramatsu diep = dwarf_offdie(dbg, off + cuhl, &pf.cu_die); 1142804b3606SMasami Hiramatsu if (!diep) 1143804b3606SMasami Hiramatsu continue; 11444ea42b18SMasami Hiramatsu 11454ea42b18SMasami Hiramatsu /* Check if target file is included. */ 11464ea42b18SMasami Hiramatsu if (pp->file) 11472a9c8c36SMasami Hiramatsu pf.fname = cu_find_realpath(&pf.cu_die, pp->file); 1148804b3606SMasami Hiramatsu else 11492a9c8c36SMasami Hiramatsu pf.fname = NULL; 11504ea42b18SMasami Hiramatsu 11512a9c8c36SMasami Hiramatsu if (!pp->file || pf.fname) { 11524ea42b18SMasami Hiramatsu if (pp->function) 1153b55a87adSMasami Hiramatsu ret = find_probe_point_by_func(&pf); 11542a9c8c36SMasami Hiramatsu else if (pp->lazy_line) 1155b55a87adSMasami Hiramatsu ret = find_probe_point_lazy(NULL, &pf); 1156b0ef0732SMasami Hiramatsu else { 1157b0ef0732SMasami Hiramatsu pf.lno = pp->line; 1158b55a87adSMasami Hiramatsu ret = find_probe_point_by_line(&pf); 11594ea42b18SMasami Hiramatsu } 1160b0ef0732SMasami Hiramatsu } 1161804b3606SMasami Hiramatsu off = noff; 11624ea42b18SMasami Hiramatsu } 11632a9c8c36SMasami Hiramatsu line_list__free(&pf.lcache); 1164804b3606SMasami Hiramatsu dwarf_end(dbg); 11654ea42b18SMasami Hiramatsu 1166b55a87adSMasami Hiramatsu return (ret < 0) ? ret : pf.ntevs; 11674ea42b18SMasami Hiramatsu } 11684ea42b18SMasami Hiramatsu 1169fb1587d8SMasami Hiramatsu /* Reverse search */ 1170fb1587d8SMasami Hiramatsu int find_perf_probe_point(int fd, unsigned long addr, 1171fb1587d8SMasami Hiramatsu struct perf_probe_point *ppt) 1172fb1587d8SMasami Hiramatsu { 1173fb1587d8SMasami Hiramatsu Dwarf_Die cudie, spdie, indie; 1174fb1587d8SMasami Hiramatsu Dwarf *dbg; 1175fb1587d8SMasami Hiramatsu Dwarf_Line *line; 1176fb1587d8SMasami Hiramatsu Dwarf_Addr laddr, eaddr; 1177fb1587d8SMasami Hiramatsu const char *tmp; 1178fb1587d8SMasami Hiramatsu int lineno, ret = 0; 1179b55a87adSMasami Hiramatsu bool found = false; 1180fb1587d8SMasami Hiramatsu 1181fb1587d8SMasami Hiramatsu dbg = dwarf_begin(fd, DWARF_C_READ); 1182fb1587d8SMasami Hiramatsu if (!dbg) 1183b55a87adSMasami Hiramatsu return -EBADF; 1184fb1587d8SMasami Hiramatsu 1185fb1587d8SMasami Hiramatsu /* Find cu die */ 118675ec5a24SMasami Hiramatsu if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr, &cudie)) { 118775ec5a24SMasami Hiramatsu ret = -EINVAL; 118875ec5a24SMasami Hiramatsu goto end; 118975ec5a24SMasami Hiramatsu } 1190fb1587d8SMasami Hiramatsu 1191fb1587d8SMasami Hiramatsu /* Find a corresponding line */ 1192fb1587d8SMasami Hiramatsu line = dwarf_getsrc_die(&cudie, (Dwarf_Addr)addr); 1193fb1587d8SMasami Hiramatsu if (line) { 1194b55a87adSMasami Hiramatsu if (dwarf_lineaddr(line, &laddr) == 0 && 1195b55a87adSMasami Hiramatsu (Dwarf_Addr)addr == laddr && 1196b55a87adSMasami Hiramatsu dwarf_lineno(line, &lineno) == 0) { 1197fb1587d8SMasami Hiramatsu tmp = dwarf_linesrc(line, NULL, NULL); 1198b55a87adSMasami Hiramatsu if (tmp) { 1199b55a87adSMasami Hiramatsu ppt->line = lineno; 120002b95dadSMasami Hiramatsu ppt->file = strdup(tmp); 120102b95dadSMasami Hiramatsu if (ppt->file == NULL) { 120202b95dadSMasami Hiramatsu ret = -ENOMEM; 120302b95dadSMasami Hiramatsu goto end; 120402b95dadSMasami Hiramatsu } 1205b55a87adSMasami Hiramatsu found = true; 1206b55a87adSMasami Hiramatsu } 1207fb1587d8SMasami Hiramatsu } 1208fb1587d8SMasami Hiramatsu } 1209fb1587d8SMasami Hiramatsu 1210fb1587d8SMasami Hiramatsu /* Find a corresponding function */ 1211fb1587d8SMasami Hiramatsu if (die_find_real_subprogram(&cudie, (Dwarf_Addr)addr, &spdie)) { 1212fb1587d8SMasami Hiramatsu tmp = dwarf_diename(&spdie); 1213b55a87adSMasami Hiramatsu if (!tmp || dwarf_entrypc(&spdie, &eaddr) != 0) 1214fb1587d8SMasami Hiramatsu goto end; 1215fb1587d8SMasami Hiramatsu 1216b55a87adSMasami Hiramatsu if (ppt->line) { 1217b55a87adSMasami Hiramatsu if (die_find_inlinefunc(&spdie, (Dwarf_Addr)addr, 1218b55a87adSMasami Hiramatsu &indie)) { 1219fb1587d8SMasami Hiramatsu /* addr in an inline function */ 1220fb1587d8SMasami Hiramatsu tmp = dwarf_diename(&indie); 1221fb1587d8SMasami Hiramatsu if (!tmp) 1222fb1587d8SMasami Hiramatsu goto end; 1223b55a87adSMasami Hiramatsu ret = dwarf_decl_line(&indie, &lineno); 1224fb1587d8SMasami Hiramatsu } else { 1225b55a87adSMasami Hiramatsu if (eaddr == addr) { /* Function entry */ 1226fb1587d8SMasami Hiramatsu lineno = ppt->line; 1227b55a87adSMasami Hiramatsu ret = 0; 1228b55a87adSMasami Hiramatsu } else 1229b55a87adSMasami Hiramatsu ret = dwarf_decl_line(&spdie, &lineno); 1230fb1587d8SMasami Hiramatsu } 1231b55a87adSMasami Hiramatsu if (ret == 0) { 1232b55a87adSMasami Hiramatsu /* Make a relative line number */ 1233b55a87adSMasami Hiramatsu ppt->line -= lineno; 1234b55a87adSMasami Hiramatsu goto found; 1235b55a87adSMasami Hiramatsu } 1236b55a87adSMasami Hiramatsu } 1237b55a87adSMasami Hiramatsu /* We don't have a line number, let's use offset */ 1238b55a87adSMasami Hiramatsu ppt->offset = addr - (unsigned long)eaddr; 1239b55a87adSMasami Hiramatsu found: 124002b95dadSMasami Hiramatsu ppt->function = strdup(tmp); 124102b95dadSMasami Hiramatsu if (ppt->function == NULL) { 124202b95dadSMasami Hiramatsu ret = -ENOMEM; 124302b95dadSMasami Hiramatsu goto end; 124402b95dadSMasami Hiramatsu } 1245b55a87adSMasami Hiramatsu found = true; 1246fb1587d8SMasami Hiramatsu } 1247fb1587d8SMasami Hiramatsu 1248fb1587d8SMasami Hiramatsu end: 1249fb1587d8SMasami Hiramatsu dwarf_end(dbg); 1250b55a87adSMasami Hiramatsu if (ret >= 0) 1251b55a87adSMasami Hiramatsu ret = found ? 1 : 0; 1252fb1587d8SMasami Hiramatsu return ret; 1253fb1587d8SMasami Hiramatsu } 1254fb1587d8SMasami Hiramatsu 1255f6c903f5SMasami Hiramatsu /* Add a line and store the src path */ 1256f6c903f5SMasami Hiramatsu static int line_range_add_line(const char *src, unsigned int lineno, 1257f6c903f5SMasami Hiramatsu struct line_range *lr) 1258f6c903f5SMasami Hiramatsu { 12599ed7e1b8SChase Douglas int ret; 12609ed7e1b8SChase Douglas 1261f6c903f5SMasami Hiramatsu /* Copy real path */ 1262f6c903f5SMasami Hiramatsu if (!lr->path) { 12639ed7e1b8SChase Douglas ret = get_real_path(src, &lr->path); 12649ed7e1b8SChase Douglas if (ret != 0) 12659ed7e1b8SChase Douglas return ret; 1266f6c903f5SMasami Hiramatsu } 1267f6c903f5SMasami Hiramatsu return line_list__add_line(&lr->line_list, lineno); 1268f6c903f5SMasami Hiramatsu } 1269f6c903f5SMasami Hiramatsu 1270f6c903f5SMasami Hiramatsu /* Search function declaration lines */ 1271f6c903f5SMasami Hiramatsu static int line_range_funcdecl_cb(Dwarf_Die *sp_die, void *data) 1272f6c903f5SMasami Hiramatsu { 1273f6c903f5SMasami Hiramatsu struct dwarf_callback_param *param = data; 1274f6c903f5SMasami Hiramatsu struct line_finder *lf = param->data; 1275f6c903f5SMasami Hiramatsu const char *src; 1276f6c903f5SMasami Hiramatsu int lineno; 1277f6c903f5SMasami Hiramatsu 1278f6c903f5SMasami Hiramatsu src = dwarf_decl_file(sp_die); 1279f6c903f5SMasami Hiramatsu if (src && strtailcmp(src, lf->fname) != 0) 1280f6c903f5SMasami Hiramatsu return DWARF_CB_OK; 1281f6c903f5SMasami Hiramatsu 1282f6c903f5SMasami Hiramatsu if (dwarf_decl_line(sp_die, &lineno) != 0 || 1283f6c903f5SMasami Hiramatsu (lf->lno_s > lineno || lf->lno_e < lineno)) 1284f6c903f5SMasami Hiramatsu return DWARF_CB_OK; 1285f6c903f5SMasami Hiramatsu 1286f6c903f5SMasami Hiramatsu param->retval = line_range_add_line(src, lineno, lf->lr); 12875d1ee041SMasami Hiramatsu if (param->retval < 0) 12885d1ee041SMasami Hiramatsu return DWARF_CB_ABORT; 1289f6c903f5SMasami Hiramatsu return DWARF_CB_OK; 1290f6c903f5SMasami Hiramatsu } 1291f6c903f5SMasami Hiramatsu 1292f6c903f5SMasami Hiramatsu static int find_line_range_func_decl_lines(struct line_finder *lf) 1293f6c903f5SMasami Hiramatsu { 1294f6c903f5SMasami Hiramatsu struct dwarf_callback_param param = {.data = (void *)lf, .retval = 0}; 1295f6c903f5SMasami Hiramatsu dwarf_getfuncs(&lf->cu_die, line_range_funcdecl_cb, ¶m, 0); 1296f6c903f5SMasami Hiramatsu return param.retval; 1297f6c903f5SMasami Hiramatsu } 1298fb1587d8SMasami Hiramatsu 1299631c9defSMasami Hiramatsu /* Find line range from its line number */ 1300b55a87adSMasami Hiramatsu static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) 1301631c9defSMasami Hiramatsu { 1302804b3606SMasami Hiramatsu Dwarf_Lines *lines; 1303804b3606SMasami Hiramatsu Dwarf_Line *line; 1304804b3606SMasami Hiramatsu size_t nlines, i; 1305631c9defSMasami Hiramatsu Dwarf_Addr addr; 1306f6c903f5SMasami Hiramatsu int lineno, ret = 0; 1307804b3606SMasami Hiramatsu const char *src; 1308161a26b0SMasami Hiramatsu Dwarf_Die die_mem; 1309631c9defSMasami Hiramatsu 13102a9c8c36SMasami Hiramatsu line_list__init(&lf->lr->line_list); 1311b55a87adSMasami Hiramatsu if (dwarf_getsrclines(&lf->cu_die, &lines, &nlines) != 0) { 1312b55a87adSMasami Hiramatsu pr_warning("No source lines found in this CU.\n"); 1313b55a87adSMasami Hiramatsu return -ENOENT; 1314b55a87adSMasami Hiramatsu } 1315631c9defSMasami Hiramatsu 1316f6c903f5SMasami Hiramatsu /* Search probable lines on lines list */ 1317804b3606SMasami Hiramatsu for (i = 0; i < nlines; i++) { 1318804b3606SMasami Hiramatsu line = dwarf_onesrcline(lines, i); 1319b55a87adSMasami Hiramatsu if (dwarf_lineno(line, &lineno) != 0 || 1320b55a87adSMasami Hiramatsu (lf->lno_s > lineno || lf->lno_e < lineno)) 1321631c9defSMasami Hiramatsu continue; 1322631c9defSMasami Hiramatsu 1323161a26b0SMasami Hiramatsu if (sp_die) { 1324161a26b0SMasami Hiramatsu /* Address filtering 1: does sp_die include addr? */ 1325b55a87adSMasami Hiramatsu if (dwarf_lineaddr(line, &addr) != 0 || 1326b55a87adSMasami Hiramatsu !dwarf_haspc(sp_die, addr)) 1327161a26b0SMasami Hiramatsu continue; 1328161a26b0SMasami Hiramatsu 1329161a26b0SMasami Hiramatsu /* Address filtering 2: No child include addr? */ 133095a3e4c4SMasami Hiramatsu if (die_find_inlinefunc(sp_die, addr, &die_mem)) 1331161a26b0SMasami Hiramatsu continue; 1332161a26b0SMasami Hiramatsu } 1333161a26b0SMasami Hiramatsu 1334804b3606SMasami Hiramatsu /* TODO: Get fileno from line, but how? */ 1335804b3606SMasami Hiramatsu src = dwarf_linesrc(line, NULL, NULL); 1336804b3606SMasami Hiramatsu if (strtailcmp(src, lf->fname) != 0) 1337631c9defSMasami Hiramatsu continue; 1338631c9defSMasami Hiramatsu 1339f6c903f5SMasami Hiramatsu ret = line_range_add_line(src, lineno, lf->lr); 1340f6c903f5SMasami Hiramatsu if (ret < 0) 1341f6c903f5SMasami Hiramatsu return ret; 134202b95dadSMasami Hiramatsu } 1343f6c903f5SMasami Hiramatsu 1344f6c903f5SMasami Hiramatsu /* 1345f6c903f5SMasami Hiramatsu * Dwarf lines doesn't include function declarations. We have to 1346f6c903f5SMasami Hiramatsu * check functions list or given function. 1347f6c903f5SMasami Hiramatsu */ 1348f6c903f5SMasami Hiramatsu if (sp_die) { 1349f6c903f5SMasami Hiramatsu src = dwarf_decl_file(sp_die); 1350f6c903f5SMasami Hiramatsu if (src && dwarf_decl_line(sp_die, &lineno) == 0 && 1351f6c903f5SMasami Hiramatsu (lf->lno_s <= lineno && lf->lno_e >= lineno)) 1352f6c903f5SMasami Hiramatsu ret = line_range_add_line(src, lineno, lf->lr); 1353f6c903f5SMasami Hiramatsu } else 1354f6c903f5SMasami Hiramatsu ret = find_line_range_func_decl_lines(lf); 1355f6c903f5SMasami Hiramatsu 1356804b3606SMasami Hiramatsu /* Update status */ 1357f6c903f5SMasami Hiramatsu if (ret >= 0) 1358631c9defSMasami Hiramatsu if (!list_empty(&lf->lr->line_list)) 1359f6c903f5SMasami Hiramatsu ret = lf->found = 1; 1360f6c903f5SMasami Hiramatsu else 1361f6c903f5SMasami Hiramatsu ret = 0; /* Lines are not found */ 1362804b3606SMasami Hiramatsu else { 1363804b3606SMasami Hiramatsu free(lf->lr->path); 1364804b3606SMasami Hiramatsu lf->lr->path = NULL; 1365804b3606SMasami Hiramatsu } 1366f6c903f5SMasami Hiramatsu return ret; 1367631c9defSMasami Hiramatsu } 1368631c9defSMasami Hiramatsu 1369161a26b0SMasami Hiramatsu static int line_range_inline_cb(Dwarf_Die *in_die, void *data) 1370161a26b0SMasami Hiramatsu { 1371b55a87adSMasami Hiramatsu struct dwarf_callback_param *param = data; 1372b55a87adSMasami Hiramatsu 1373b55a87adSMasami Hiramatsu param->retval = find_line_range_by_line(in_die, param->data); 1374161a26b0SMasami Hiramatsu return DWARF_CB_ABORT; /* No need to find other instances */ 1375161a26b0SMasami Hiramatsu } 1376161a26b0SMasami Hiramatsu 1377631c9defSMasami Hiramatsu /* Search function from function name */ 1378e92b85e1SMasami Hiramatsu static int line_range_search_cb(Dwarf_Die *sp_die, void *data) 1379631c9defSMasami Hiramatsu { 1380b55a87adSMasami Hiramatsu struct dwarf_callback_param *param = data; 1381b55a87adSMasami Hiramatsu struct line_finder *lf = param->data; 1382631c9defSMasami Hiramatsu struct line_range *lr = lf->lr; 1383631c9defSMasami Hiramatsu 1384e92b85e1SMasami Hiramatsu if (dwarf_tag(sp_die) == DW_TAG_subprogram && 1385e92b85e1SMasami Hiramatsu die_compare_name(sp_die, lr->function) == 0) { 1386e92b85e1SMasami Hiramatsu lf->fname = dwarf_decl_file(sp_die); 1387e92b85e1SMasami Hiramatsu dwarf_decl_line(sp_die, &lr->offset); 1388804b3606SMasami Hiramatsu pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset); 1389631c9defSMasami Hiramatsu lf->lno_s = lr->offset + lr->start; 1390d3b63d7aSMasami Hiramatsu if (lf->lno_s < 0) /* Overflow */ 1391d3b63d7aSMasami Hiramatsu lf->lno_s = INT_MAX; 1392631c9defSMasami Hiramatsu lf->lno_e = lr->offset + lr->end; 1393d3b63d7aSMasami Hiramatsu if (lf->lno_e < 0) /* Overflow */ 1394d3b63d7aSMasami Hiramatsu lf->lno_e = INT_MAX; 1395d3b63d7aSMasami Hiramatsu pr_debug("New line range: %d to %d\n", lf->lno_s, lf->lno_e); 1396631c9defSMasami Hiramatsu lr->start = lf->lno_s; 1397631c9defSMasami Hiramatsu lr->end = lf->lno_e; 1398b55a87adSMasami Hiramatsu if (dwarf_func_inline(sp_die)) { 1399b55a87adSMasami Hiramatsu struct dwarf_callback_param _param; 1400b55a87adSMasami Hiramatsu _param.data = (void *)lf; 1401b55a87adSMasami Hiramatsu _param.retval = 0; 1402161a26b0SMasami Hiramatsu dwarf_func_inline_instances(sp_die, 1403b55a87adSMasami Hiramatsu line_range_inline_cb, 1404b55a87adSMasami Hiramatsu &_param); 1405b55a87adSMasami Hiramatsu param->retval = _param.retval; 1406b55a87adSMasami Hiramatsu } else 1407b55a87adSMasami Hiramatsu param->retval = find_line_range_by_line(sp_die, lf); 1408b55a87adSMasami Hiramatsu return DWARF_CB_ABORT; 1409631c9defSMasami Hiramatsu } 1410b55a87adSMasami Hiramatsu return DWARF_CB_OK; 1411631c9defSMasami Hiramatsu } 1412631c9defSMasami Hiramatsu 1413b55a87adSMasami Hiramatsu static int find_line_range_by_func(struct line_finder *lf) 1414631c9defSMasami Hiramatsu { 1415b55a87adSMasami Hiramatsu struct dwarf_callback_param param = {.data = (void *)lf, .retval = 0}; 1416b55a87adSMasami Hiramatsu dwarf_getfuncs(&lf->cu_die, line_range_search_cb, ¶m, 0); 1417b55a87adSMasami Hiramatsu return param.retval; 1418631c9defSMasami Hiramatsu } 1419631c9defSMasami Hiramatsu 1420631c9defSMasami Hiramatsu int find_line_range(int fd, struct line_range *lr) 1421631c9defSMasami Hiramatsu { 1422804b3606SMasami Hiramatsu struct line_finder lf = {.lr = lr, .found = 0}; 1423b55a87adSMasami Hiramatsu int ret = 0; 1424804b3606SMasami Hiramatsu Dwarf_Off off = 0, noff; 1425804b3606SMasami Hiramatsu size_t cuhl; 1426804b3606SMasami Hiramatsu Dwarf_Die *diep; 1427804b3606SMasami Hiramatsu Dwarf *dbg; 1428631c9defSMasami Hiramatsu 1429804b3606SMasami Hiramatsu dbg = dwarf_begin(fd, DWARF_C_READ); 1430b55a87adSMasami Hiramatsu if (!dbg) { 1431b55a87adSMasami Hiramatsu pr_warning("No dwarf info found in the vmlinux - " 1432b55a87adSMasami Hiramatsu "please rebuild with CONFIG_DEBUG_INFO=y.\n"); 1433b55a87adSMasami Hiramatsu return -EBADF; 1434b55a87adSMasami Hiramatsu } 1435631c9defSMasami Hiramatsu 1436804b3606SMasami Hiramatsu /* Loop on CUs (Compilation Unit) */ 1437b55a87adSMasami Hiramatsu while (!lf.found && ret >= 0) { 1438b55a87adSMasami Hiramatsu if (dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL) != 0) 1439631c9defSMasami Hiramatsu break; 1440631c9defSMasami Hiramatsu 1441631c9defSMasami Hiramatsu /* Get the DIE(Debugging Information Entry) of this CU */ 1442804b3606SMasami Hiramatsu diep = dwarf_offdie(dbg, off + cuhl, &lf.cu_die); 1443804b3606SMasami Hiramatsu if (!diep) 1444804b3606SMasami Hiramatsu continue; 1445631c9defSMasami Hiramatsu 1446631c9defSMasami Hiramatsu /* Check if target file is included. */ 1447631c9defSMasami Hiramatsu if (lr->file) 14482a9c8c36SMasami Hiramatsu lf.fname = cu_find_realpath(&lf.cu_die, lr->file); 1449804b3606SMasami Hiramatsu else 14502a9c8c36SMasami Hiramatsu lf.fname = 0; 1451631c9defSMasami Hiramatsu 14522a9c8c36SMasami Hiramatsu if (!lr->file || lf.fname) { 1453631c9defSMasami Hiramatsu if (lr->function) 1454b55a87adSMasami Hiramatsu ret = find_line_range_by_func(&lf); 1455631c9defSMasami Hiramatsu else { 1456631c9defSMasami Hiramatsu lf.lno_s = lr->start; 1457631c9defSMasami Hiramatsu lf.lno_e = lr->end; 1458b55a87adSMasami Hiramatsu ret = find_line_range_by_line(NULL, &lf); 1459631c9defSMasami Hiramatsu } 1460631c9defSMasami Hiramatsu } 1461804b3606SMasami Hiramatsu off = noff; 1462631c9defSMasami Hiramatsu } 1463804b3606SMasami Hiramatsu pr_debug("path: %lx\n", (unsigned long)lr->path); 1464804b3606SMasami Hiramatsu dwarf_end(dbg); 1465b55a87adSMasami Hiramatsu 1466b55a87adSMasami Hiramatsu return (ret < 0) ? ret : lf.found; 1467631c9defSMasami Hiramatsu } 1468631c9defSMasami Hiramatsu 1469