summaryrefslogtreecommitdiff
path: root/source/ucli/unwind.cc
blob: f09d7a59312647cc46fc06634265a9acdc3321f7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#include "unwind.h"

#include <errno.h>
#include <stdio.h>

#include <string>

static std::string unknow_symbol("UNKNOWN");

// static int unwind_frame_callback(struct unwind_entry *entry, void *arg) {
//   symbol sym;
//   std::string symbol;  // Use std::string instead of string
//   elf_file file;

//   sym.reset(entry->ip);

//   if (g_symbol_parser.find_symbol_in_cache(entry->pid, entry->ip, symbol)) {
//     printf("#~        0x%lx %s ([symbol])\n", entry->ip, symbol.c_str());
//     return 0;
//   }

//   if (g_symbol_parser.get_symbol_info(entry->pid, sym, file)) {
//     if (g_symbol_parser.find_elf_symbol(sym, file, entry->pid,
//     entry->pid_ns)) {
//       printf("#~        0x%lx %s ([symbol])\n", entry->ip, sym.name.c_str());
//       g_symbol_parser.putin_symbol_cache(entry->pid, entry->ip, sym.name);
//     } else {
//       printf("#~        0x%lx %s ([symbol])\n", entry->ip,
//       "(unknown)[symbol]"); g_symbol_parser.putin_symbol_cache(entry->pid,
//       entry->ip, unknow_symbol);
//     }
//   } else {
//     printf("#~        0x%lx %s ([symbol])\n", entry->ip,
//     "(unknown)[vma,elf]"); g_symbol_parser.putin_symbol_cache(entry->pid,
//     entry->ip, unknow_symbol);
//   }

//   return 0;
// }
/*
 * Optimization point.
 */
static int reg_value(unw_word_t *valp, struct regs_dump *regs, int id) {
  /* we only support 3 registers. RIP, RSP and RBP */
  if (id < 0 || id > 2)
    return -EINVAL;

  *valp = regs->regs[id];
  return 0;
}

static int entry(u64 ip, int pid, int pid_ns, unwind_entry_cb_t cb, void *arg) {
  struct unwind_entry e;

  e.ip = ip;
  e.pid = pid;
  e.pid_ns = pid_ns;

  return cb(&e, arg);
}

static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,
                       void *arg) {
  unw_addr_space_t addr_space;
  unw_cursor_t c;
  entry_cb_arg_t *cb_arg = (entry_cb_arg_t *)arg;
  int ret;
  int loops = 0;

  addr_space = unw_create_addr_space(&accessors, 0);
  if (!addr_space) {
    // pr_err("unwind: Can't create unwind address space.\n");
    return -ENOMEM;
  }

  unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL);

  ret = unw_init_remote(&c, addr_space, ui); /* @ui is args */

  while (!ret && (unw_step(&c) > 0)) {
    unw_word_t ip;

    unw_get_reg(&c, UNW_REG_IP, &ip); // get IP from current step;
    cb_arg->arg = &c;
    ret = entry(ip, ui->pid, ui->pid_ns, cb, cb_arg);

    loops++;
    if (loops >= 50)
      break;
  }

  unw_destroy_addr_space(addr_space);
  return ret;
}

int unwind__get_entries(unwind_entry_cb_t cb, void *arg, symbol_parser *sp,
                        int pid, int pid_ns, struct perf_sample *data) {
  unw_word_t ip;
  struct unwind_info ui = {
      .sample = data,
      .pid = pid,
      .pid_ns = pid_ns,
      .sp = sp,
  };
  int ret;

  if (!data->user_regs.regs)
    return -EINVAL;

  ret = reg_value(&ip, &data->user_regs, PERF_REG_IP);
  if (ret)
    return ret;

  ret = entry(ip, pid, pid_ns, cb, arg);
  if (ret)
    return -ENOMEM;

  return get_entries(&ui, cb, arg);
}