linux/tools/perf/util/unwind-libdw.c
<<
>>
Prefs
   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 "debug.h"
   7#include "unwind.h"
   8#include "unwind-libdw.h"
   9#include "machine.h"
  10#include "thread.h"
  11#include <linux/types.h>
  12#include "event.h"
  13#include "perf_regs.h"
  14#include "callchain.h"
  15#include "util.h"
  16
  17static char *debuginfo_path;
  18
  19static const Dwfl_Callbacks offline_callbacks = {
  20        .find_debuginfo         = dwfl_standard_find_debuginfo,
  21        .debuginfo_path         = &debuginfo_path,
  22        .section_address        = dwfl_offline_section_address,
  23};
  24
  25static int __report_module(struct addr_location *al, u64 ip,
  26                            struct unwind_info *ui)
  27{
  28        Dwfl_Module *mod;
  29        struct dso *dso = NULL;
  30
  31        thread__find_addr_location(ui->thread,
  32                                   PERF_RECORD_MISC_USER,
  33                                   MAP__FUNCTION, ip, al);
  34
  35        if (al->map)
  36                dso = al->map->dso;
  37
  38        if (!dso)
  39                return 0;
  40
  41        mod = dwfl_addrmodule(ui->dwfl, ip);
  42        if (mod) {
  43                Dwarf_Addr s;
  44
  45                dwfl_module_info(mod, NULL, &s, NULL, NULL, NULL, NULL, NULL);
  46                if (s != al->map->start)
  47                        mod = 0;
  48        }
  49
  50        if (!mod)
  51                mod = dwfl_report_elf(ui->dwfl, dso->short_name,
  52                                      dso->long_name, -1, al->map->start,
  53                                      false);
  54
  55        return mod && dwfl_addrmodule(ui->dwfl, ip) == mod ? 0 : -1;
  56}
  57
  58static int report_module(u64 ip, struct unwind_info *ui)
  59{
  60        struct addr_location al;
  61
  62        return __report_module(&al, ip, ui);
  63}
  64
  65/*
  66 * Store all entries within entries array,
  67 * we will process it after we finish unwind.
  68 */
  69static int entry(u64 ip, struct unwind_info *ui)
  70
  71{
  72        struct unwind_entry *e = &ui->entries[ui->idx++];
  73        struct addr_location al;
  74
  75        if (__report_module(&al, ip, ui))
  76                return -1;
  77
  78        e->ip  = al.addr;
  79        e->map = al.map;
  80        e->sym = al.sym;
  81
  82        pr_debug("unwind: %s:ip = 0x%" PRIx64 " (0x%" PRIx64 ")\n",
  83                 al.sym ? al.sym->name : "''",
  84                 ip,
  85                 al.map ? al.map->map_ip(al.map, ip) : (u64) 0);
  86        return 0;
  87}
  88
  89static pid_t next_thread(Dwfl *dwfl, void *arg, void **thread_argp)
  90{
  91        /* We want only single thread to be processed. */
  92        if (*thread_argp != NULL)
  93                return 0;
  94
  95        *thread_argp = arg;
  96        return dwfl_pid(dwfl);
  97}
  98
  99static int access_dso_mem(struct unwind_info *ui, Dwarf_Addr addr,
 100                          Dwarf_Word *data)
 101{
 102        struct addr_location al;
 103        ssize_t size;
 104
 105        thread__find_addr_map(ui->thread, PERF_RECORD_MISC_USER,
 106                              MAP__FUNCTION, addr, &al);
 107        if (!al.map) {
 108                /*
 109                 * We've seen cases (softice) where DWARF unwinder went
 110                 * through non executable mmaps, which we need to lookup
 111                 * in MAP__VARIABLE tree.
 112                 */
 113                thread__find_addr_map(ui->thread, PERF_RECORD_MISC_USER,
 114                                      MAP__VARIABLE, addr, &al);
 115        }
 116
 117        if (!al.map) {
 118                pr_debug("unwind: no map for %lx\n", (unsigned long)addr);
 119                return -1;
 120        }
 121
 122        if (!al.map->dso)
 123                return -1;
 124
 125        size = dso__data_read_addr(al.map->dso, al.map, ui->machine,
 126                                   addr, (u8 *) data, sizeof(*data));
 127
 128        return !(size == sizeof(*data));
 129}
 130
 131static bool memory_read(Dwfl *dwfl __maybe_unused, Dwarf_Addr addr, Dwarf_Word *result,
 132                        void *arg)
 133{
 134        struct unwind_info *ui = arg;
 135        struct stack_dump *stack = &ui->sample->user_stack;
 136        u64 start, end;
 137        int offset;
 138        int ret;
 139
 140        ret = perf_reg_value(&start, &ui->sample->user_regs, PERF_REG_SP);
 141        if (ret)
 142                return false;
 143
 144        end = start + stack->size;
 145
 146        /* Check overflow. */
 147        if (addr + sizeof(Dwarf_Word) < addr)
 148                return false;
 149
 150        if (addr < start || addr + sizeof(Dwarf_Word) > end) {
 151                ret = access_dso_mem(ui, addr, result);
 152                if (ret) {
 153                        pr_debug("unwind: access_mem 0x%" PRIx64 " not inside range"
 154                                 " 0x%" PRIx64 "-0x%" PRIx64 "\n",
 155                                addr, start, end);
 156                        return false;
 157                }
 158                return true;
 159        }
 160
 161        offset  = addr - start;
 162        *result = *(Dwarf_Word *)&stack->data[offset];
 163        pr_debug("unwind: access_mem addr 0x%" PRIx64 ", val %lx, offset %d\n",
 164                 addr, (unsigned long)*result, offset);
 165        return true;
 166}
 167
 168static const Dwfl_Thread_Callbacks callbacks = {
 169        .next_thread            = next_thread,
 170        .memory_read            = memory_read,
 171        .set_initial_registers  = libdw__arch_set_initial_registers,
 172};
 173
 174static int
 175frame_callback(Dwfl_Frame *state, void *arg)
 176{
 177        struct unwind_info *ui = arg;
 178        Dwarf_Addr pc;
 179        bool isactivation;
 180
 181        if (!dwfl_frame_pc(state, &pc, NULL)) {
 182                pr_err("%s", dwfl_errmsg(-1));
 183                return DWARF_CB_ABORT;
 184        }
 185
 186        // report the module before we query for isactivation
 187        report_module(pc, ui);
 188
 189        if (!dwfl_frame_pc(state, &pc, &isactivation)) {
 190                pr_err("%s", dwfl_errmsg(-1));
 191                return DWARF_CB_ABORT;
 192        }
 193
 194        if (!isactivation)
 195                --pc;
 196
 197        return entry(pc, ui) || !(--ui->max_stack) ?
 198               DWARF_CB_ABORT : DWARF_CB_OK;
 199}
 200
 201int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
 202                        struct thread *thread,
 203                        struct perf_sample *data,
 204                        int max_stack)
 205{
 206        struct unwind_info *ui, ui_buf = {
 207                .sample         = data,
 208                .thread         = thread,
 209                .machine        = thread->mg->machine,
 210                .cb             = cb,
 211                .arg            = arg,
 212                .max_stack      = max_stack,
 213        };
 214        Dwarf_Word ip;
 215        int err = -EINVAL, i;
 216
 217        if (!data->user_regs.regs)
 218                return -EINVAL;
 219
 220        ui = zalloc(sizeof(ui_buf) + sizeof(ui_buf.entries[0]) * max_stack);
 221        if (!ui)
 222                return -ENOMEM;
 223
 224        *ui = ui_buf;
 225
 226        ui->dwfl = dwfl_begin(&offline_callbacks);
 227        if (!ui->dwfl)
 228                goto out;
 229
 230        err = perf_reg_value(&ip, &data->user_regs, PERF_REG_IP);
 231        if (err)
 232                goto out;
 233
 234        err = report_module(ip, ui);
 235        if (err)
 236                goto out;
 237
 238        if (!dwfl_attach_state(ui->dwfl, EM_NONE, thread->tid, &callbacks, ui))
 239                goto out;
 240
 241        err = dwfl_getthread_frames(ui->dwfl, thread->tid, frame_callback, ui);
 242
 243        if (err && ui->max_stack != max_stack)
 244                err = 0;
 245
 246        /*
 247         * Display what we got based on the order setup.
 248         */
 249        for (i = 0; i < ui->idx && !err; i++) {
 250                int j = i;
 251
 252                if (callchain_param.order == ORDER_CALLER)
 253                        j = ui->idx - i - 1;
 254
 255                err = ui->entries[j].ip ? ui->cb(&ui->entries[j], ui->arg) : 0;
 256        }
 257
 258 out:
 259        if (err)
 260                pr_debug("unwind: failed with '%s'\n", dwfl_errmsg(-1));
 261
 262        dwfl_end(ui->dwfl);
 263        free(ui);
 264        return 0;
 265}
 266