1 #include <linux/compiler.h> 2 #include <elfutils/libdw.h> 3 #include <elfutils/libdwfl.h> 4 #include <inttypes.h> 5 #include <errno.h> 6 #include "unwind.h" 7 #include "unwind-libdw.h" 8 #include "machine.h" 9 #include "thread.h" 10 #include "types.h" 11 #include "event.h" 12 #include "perf_regs.h" 13 14 static char *debuginfo_path; 15 16 static const Dwfl_Callbacks offline_callbacks = { 17 .find_debuginfo = dwfl_standard_find_debuginfo, 18 .debuginfo_path = &debuginfo_path, 19 .section_address = dwfl_offline_section_address, 20 }; 21 22 static int __report_module(struct addr_location *al, u64 ip, 23 struct unwind_info *ui) 24 { 25 Dwfl_Module *mod; 26 struct dso *dso = NULL; 27 28 thread__find_addr_location(ui->thread, ui->machine, 29 PERF_RECORD_MISC_USER, 30 MAP__FUNCTION, ip, al); 31 32 if (al->map) 33 dso = al->map->dso; 34 35 if (!dso) 36 return 0; 37 38 mod = dwfl_addrmodule(ui->dwfl, ip); 39 if (!mod) 40 mod = dwfl_report_elf(ui->dwfl, dso->short_name, 41 dso->long_name, -1, al->map->start, 42 false); 43 44 return mod && dwfl_addrmodule(ui->dwfl, ip) == mod ? 0 : -1; 45 } 46 47 static int report_module(u64 ip, struct unwind_info *ui) 48 { 49 struct addr_location al; 50 51 return __report_module(&al, ip, ui); 52 } 53 54 static int entry(u64 ip, struct unwind_info *ui) 55 56 { 57 struct unwind_entry e; 58 struct addr_location al; 59 60 if (__report_module(&al, ip, ui)) 61 return -1; 62 63 e.ip = ip; 64 e.map = al.map; 65 e.sym = al.sym; 66 67 pr_debug("unwind: %s:ip = 0x%" PRIx64 " (0x%" PRIx64 ")\n", 68 al.sym ? al.sym->name : "''", 69 ip, 70 al.map ? al.map->map_ip(al.map, ip) : (u64) 0); 71 72 return ui->cb(&e, ui->arg); 73 } 74 75 static pid_t next_thread(Dwfl *dwfl, void *arg, void **thread_argp) 76 { 77 /* We want only single thread to be processed. */ 78 if (*thread_argp != NULL) 79 return 0; 80 81 *thread_argp = arg; 82 return dwfl_pid(dwfl); 83 } 84 85 static int access_dso_mem(struct unwind_info *ui, Dwarf_Addr addr, 86 Dwarf_Word *data) 87 { 88 struct addr_location al; 89 ssize_t size; 90 91 thread__find_addr_map(ui->thread, ui->machine, PERF_RECORD_MISC_USER, 92 MAP__FUNCTION, addr, &al); 93 if (!al.map) { 94 pr_debug("unwind: no map for %lx\n", (unsigned long)addr); 95 return -1; 96 } 97 98 if (!al.map->dso) 99 return -1; 100 101 size = dso__data_read_addr(al.map->dso, al.map, ui->machine, 102 addr, (u8 *) data, sizeof(*data)); 103 104 return !(size == sizeof(*data)); 105 } 106 107 static bool memory_read(Dwfl *dwfl __maybe_unused, Dwarf_Addr addr, Dwarf_Word *result, 108 void *arg) 109 { 110 struct unwind_info *ui = arg; 111 struct stack_dump *stack = &ui->sample->user_stack; 112 u64 start, end; 113 int offset; 114 int ret; 115 116 ret = perf_reg_value(&start, &ui->sample->user_regs, PERF_REG_SP); 117 if (ret) 118 return false; 119 120 end = start + stack->size; 121 122 /* Check overflow. */ 123 if (addr + sizeof(Dwarf_Word) < addr) 124 return false; 125 126 if (addr < start || addr + sizeof(Dwarf_Word) > end) { 127 ret = access_dso_mem(ui, addr, result); 128 if (ret) { 129 pr_debug("unwind: access_mem 0x%" PRIx64 " not inside range" 130 " 0x%" PRIx64 "-0x%" PRIx64 "\n", 131 addr, start, end); 132 return false; 133 } 134 return true; 135 } 136 137 offset = addr - start; 138 *result = *(Dwarf_Word *)&stack->data[offset]; 139 pr_debug("unwind: access_mem addr 0x%" PRIx64 ", val %lx, offset %d\n", 140 addr, (unsigned long)*result, offset); 141 return true; 142 } 143 144 static const Dwfl_Thread_Callbacks callbacks = { 145 .next_thread = next_thread, 146 .memory_read = memory_read, 147 .set_initial_registers = libdw__arch_set_initial_registers, 148 }; 149 150 static int 151 frame_callback(Dwfl_Frame *state, void *arg) 152 { 153 struct unwind_info *ui = arg; 154 Dwarf_Addr pc; 155 156 if (!dwfl_frame_pc(state, &pc, NULL)) { 157 pr_err("%s", dwfl_errmsg(-1)); 158 return DWARF_CB_ABORT; 159 } 160 161 return entry(pc, ui) || !(--ui->max_stack) ? 162 DWARF_CB_ABORT : DWARF_CB_OK; 163 } 164 165 int unwind__get_entries(unwind_entry_cb_t cb, void *arg, 166 struct machine *machine, struct thread *thread, 167 struct perf_sample *data, 168 int max_stack) 169 { 170 struct unwind_info ui = { 171 .sample = data, 172 .thread = thread, 173 .machine = machine, 174 .cb = cb, 175 .arg = arg, 176 .max_stack = max_stack, 177 }; 178 Dwarf_Word ip; 179 int err = -EINVAL; 180 181 if (!data->user_regs.regs) 182 return -EINVAL; 183 184 ui.dwfl = dwfl_begin(&offline_callbacks); 185 if (!ui.dwfl) 186 goto out; 187 188 err = perf_reg_value(&ip, &data->user_regs, PERF_REG_IP); 189 if (err) 190 goto out; 191 192 err = report_module(ip, &ui); 193 if (err) 194 goto out; 195 196 if (!dwfl_attach_state(ui.dwfl, EM_NONE, thread->tid, &callbacks, &ui)) 197 goto out; 198 199 err = dwfl_getthread_frames(ui.dwfl, thread->tid, frame_callback, &ui); 200 201 if (err && !ui.max_stack) 202 err = 0; 203 204 out: 205 if (err) 206 pr_debug("unwind: failed with '%s'\n", dwfl_errmsg(-1)); 207 208 dwfl_end(ui.dwfl); 209 return 0; 210 } 211