Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 #include <linux/compiler.h>
0003 #include <elfutils/libdw.h>
0004 #include <elfutils/libdwfl.h>
0005 #include <inttypes.h>
0006 #include <errno.h>
0007 #include "debug.h"
0008 #include "dso.h"
0009 #include "unwind.h"
0010 #include "unwind-libdw.h"
0011 #include "machine.h"
0012 #include "map.h"
0013 #include "symbol.h"
0014 #include "thread.h"
0015 #include <linux/types.h>
0016 #include <linux/zalloc.h>
0017 #include "event.h"
0018 #include "perf_regs.h"
0019 #include "callchain.h"
0020 
0021 static char *debuginfo_path;
0022 
0023 static int __find_debuginfo(Dwfl_Module *mod __maybe_unused, void **userdata,
0024                 const char *modname __maybe_unused, Dwarf_Addr base __maybe_unused,
0025                 const char *file_name, const char *debuglink_file __maybe_unused,
0026                 GElf_Word debuglink_crc __maybe_unused, char **debuginfo_file_name)
0027 {
0028     const struct dso *dso = *userdata;
0029 
0030     assert(dso);
0031     if (dso->symsrc_filename && strcmp (file_name, dso->symsrc_filename))
0032         *debuginfo_file_name = strdup(dso->symsrc_filename);
0033     return -1;
0034 }
0035 
0036 static const Dwfl_Callbacks offline_callbacks = {
0037     .find_debuginfo     = __find_debuginfo,
0038     .debuginfo_path     = &debuginfo_path,
0039     .section_address    = dwfl_offline_section_address,
0040     // .find_elf is not set as we use dwfl_report_elf() instead.
0041 };
0042 
0043 static int __report_module(struct addr_location *al, u64 ip,
0044                 struct unwind_info *ui)
0045 {
0046     Dwfl_Module *mod;
0047     struct dso *dso = NULL;
0048     /*
0049      * Some callers will use al->sym, so we can't just use the
0050      * cheaper thread__find_map() here.
0051      */
0052     thread__find_symbol(ui->thread, PERF_RECORD_MISC_USER, ip, al);
0053 
0054     if (al->map)
0055         dso = al->map->dso;
0056 
0057     if (!dso)
0058         return 0;
0059 
0060     mod = dwfl_addrmodule(ui->dwfl, ip);
0061     if (mod) {
0062         Dwarf_Addr s;
0063 
0064         dwfl_module_info(mod, NULL, &s, NULL, NULL, NULL, NULL, NULL);
0065         if (s != al->map->start - al->map->pgoff)
0066             mod = 0;
0067     }
0068 
0069     if (!mod)
0070         mod = dwfl_report_elf(ui->dwfl, dso->short_name, dso->long_name, -1,
0071                       al->map->start - al->map->pgoff, false);
0072     if (!mod) {
0073         char filename[PATH_MAX];
0074 
0075         if (dso__build_id_filename(dso, filename, sizeof(filename), false))
0076             mod = dwfl_report_elf(ui->dwfl, dso->short_name, filename, -1,
0077                           al->map->start - al->map->pgoff, false);
0078     }
0079 
0080     if (mod) {
0081         void **userdatap;
0082 
0083         dwfl_module_info(mod, &userdatap, NULL, NULL, NULL, NULL, NULL, NULL);
0084         *userdatap = dso;
0085     }
0086 
0087     return mod && dwfl_addrmodule(ui->dwfl, ip) == mod ? 0 : -1;
0088 }
0089 
0090 static int report_module(u64 ip, struct unwind_info *ui)
0091 {
0092     struct addr_location al;
0093 
0094     return __report_module(&al, ip, ui);
0095 }
0096 
0097 /*
0098  * Store all entries within entries array,
0099  * we will process it after we finish unwind.
0100  */
0101 static int entry(u64 ip, struct unwind_info *ui)
0102 
0103 {
0104     struct unwind_entry *e = &ui->entries[ui->idx++];
0105     struct addr_location al;
0106 
0107     if (__report_module(&al, ip, ui))
0108         return -1;
0109 
0110     e->ip     = ip;
0111     e->ms.maps = al.maps;
0112     e->ms.map = al.map;
0113     e->ms.sym = al.sym;
0114 
0115     pr_debug("unwind: %s:ip = 0x%" PRIx64 " (0x%" PRIx64 ")\n",
0116          al.sym ? al.sym->name : "''",
0117          ip,
0118          al.map ? al.map->map_ip(al.map, ip) : (u64) 0);
0119     return 0;
0120 }
0121 
0122 static pid_t next_thread(Dwfl *dwfl, void *arg, void **thread_argp)
0123 {
0124     /* We want only single thread to be processed. */
0125     if (*thread_argp != NULL)
0126         return 0;
0127 
0128     *thread_argp = arg;
0129     return dwfl_pid(dwfl);
0130 }
0131 
0132 static int access_dso_mem(struct unwind_info *ui, Dwarf_Addr addr,
0133               Dwarf_Word *data)
0134 {
0135     struct addr_location al;
0136     ssize_t size;
0137 
0138     if (!thread__find_map(ui->thread, PERF_RECORD_MISC_USER, addr, &al)) {
0139         pr_debug("unwind: no map for %lx\n", (unsigned long)addr);
0140         return -1;
0141     }
0142 
0143     if (!al.map->dso)
0144         return -1;
0145 
0146     size = dso__data_read_addr(al.map->dso, al.map, ui->machine,
0147                    addr, (u8 *) data, sizeof(*data));
0148 
0149     return !(size == sizeof(*data));
0150 }
0151 
0152 static bool memory_read(Dwfl *dwfl __maybe_unused, Dwarf_Addr addr, Dwarf_Word *result,
0153             void *arg)
0154 {
0155     struct unwind_info *ui = arg;
0156     struct stack_dump *stack = &ui->sample->user_stack;
0157     u64 start, end;
0158     int offset;
0159     int ret;
0160 
0161     ret = perf_reg_value(&start, &ui->sample->user_regs, PERF_REG_SP);
0162     if (ret)
0163         return false;
0164 
0165     end = start + stack->size;
0166 
0167     /* Check overflow. */
0168     if (addr + sizeof(Dwarf_Word) < addr)
0169         return false;
0170 
0171     if (addr < start || addr + sizeof(Dwarf_Word) > end) {
0172         ret = access_dso_mem(ui, addr, result);
0173         if (ret) {
0174             pr_debug("unwind: access_mem 0x%" PRIx64 " not inside range"
0175                  " 0x%" PRIx64 "-0x%" PRIx64 "\n",
0176                 addr, start, end);
0177             return false;
0178         }
0179         return true;
0180     }
0181 
0182     offset  = addr - start;
0183     *result = *(Dwarf_Word *)&stack->data[offset];
0184     pr_debug("unwind: access_mem addr 0x%" PRIx64 ", val %lx, offset %d\n",
0185          addr, (unsigned long)*result, offset);
0186     return true;
0187 }
0188 
0189 static const Dwfl_Thread_Callbacks callbacks = {
0190     .next_thread        = next_thread,
0191     .memory_read        = memory_read,
0192     .set_initial_registers  = libdw__arch_set_initial_registers,
0193 };
0194 
0195 static int
0196 frame_callback(Dwfl_Frame *state, void *arg)
0197 {
0198     struct unwind_info *ui = arg;
0199     Dwarf_Addr pc;
0200     bool isactivation;
0201 
0202     if (!dwfl_frame_pc(state, &pc, NULL)) {
0203         if (!ui->best_effort)
0204             pr_err("%s", dwfl_errmsg(-1));
0205         return DWARF_CB_ABORT;
0206     }
0207 
0208     // report the module before we query for isactivation
0209     report_module(pc, ui);
0210 
0211     if (!dwfl_frame_pc(state, &pc, &isactivation)) {
0212         if (!ui->best_effort)
0213             pr_err("%s", dwfl_errmsg(-1));
0214         return DWARF_CB_ABORT;
0215     }
0216 
0217     if (!isactivation)
0218         --pc;
0219 
0220     return entry(pc, ui) || !(--ui->max_stack) ?
0221            DWARF_CB_ABORT : DWARF_CB_OK;
0222 }
0223 
0224 int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
0225             struct thread *thread,
0226             struct perf_sample *data,
0227             int max_stack,
0228             bool best_effort)
0229 {
0230     struct unwind_info *ui, ui_buf = {
0231         .sample     = data,
0232         .thread     = thread,
0233         .machine    = thread->maps->machine,
0234         .cb     = cb,
0235         .arg        = arg,
0236         .max_stack  = max_stack,
0237         .best_effort    = best_effort
0238     };
0239     Dwarf_Word ip;
0240     int err = -EINVAL, i;
0241 
0242     if (!data->user_regs.regs)
0243         return -EINVAL;
0244 
0245     ui = zalloc(sizeof(ui_buf) + sizeof(ui_buf.entries[0]) * max_stack);
0246     if (!ui)
0247         return -ENOMEM;
0248 
0249     *ui = ui_buf;
0250 
0251     ui->dwfl = dwfl_begin(&offline_callbacks);
0252     if (!ui->dwfl)
0253         goto out;
0254 
0255     err = perf_reg_value(&ip, &data->user_regs, PERF_REG_IP);
0256     if (err)
0257         goto out;
0258 
0259     err = report_module(ip, ui);
0260     if (err)
0261         goto out;
0262 
0263     err = !dwfl_attach_state(ui->dwfl, EM_NONE, thread->tid, &callbacks, ui);
0264     if (err)
0265         goto out;
0266 
0267     err = dwfl_getthread_frames(ui->dwfl, thread->tid, frame_callback, ui);
0268 
0269     if (err && ui->max_stack != max_stack)
0270         err = 0;
0271 
0272     /*
0273      * Display what we got based on the order setup.
0274      */
0275     for (i = 0; i < ui->idx && !err; i++) {
0276         int j = i;
0277 
0278         if (callchain_param.order == ORDER_CALLER)
0279             j = ui->idx - i - 1;
0280 
0281         err = ui->entries[j].ip ? ui->cb(&ui->entries[j], ui->arg) : 0;
0282     }
0283 
0284  out:
0285     if (err)
0286         pr_debug("unwind: failed with '%s'\n", dwfl_errmsg(-1));
0287 
0288     dwfl_end(ui->dwfl);
0289     free(ui);
0290     return 0;
0291 }