Basic JDB backtrace support for ARM

This commit is contained in:
Christian Prochaska
2013-02-23 01:40:01 +01:00
committed by Sebastian Sumpf
parent c24f16b8ab
commit 3854f1b941
3 changed files with 478 additions and 3 deletions

View File

@@ -212,7 +212,7 @@ INTERFACES_JDB := jdb_handler_queue jdb_module jdb_pic \
jdb_trace_set jdb_entry_frame \
jdb_kobject jdb_kobject_names \
jdb_util jdb_space jdb_utcb \
jdb_trap_state jdb_ipi jdb_rcupdate \
jdb_trap_state jdb_ipi jdb_rcupdate jdb_bt \
jdb_ipc_gate jdb_obj_space jdb_log jdb_factory \
jdb_thread jdb_scheduler jdb_sender_list\
jdb_perf jdb_vm jdb_regex jdb_disasm jdb_bp \
@@ -231,6 +231,7 @@ jdb_tcb_IMPL := jdb_tcb jdb_tcb-arm
jdb_ptab_IMPL := jdb_ptab jdb_ptab-ia32-ux-arm jdb_ptab-arm
jdb_entry_frame_IMPL := jdb_entry_frame-arm
jdb_trace_set_IMPL := jdb_trace_set jdb_trace_set-arm
jdb_bt_IMPL := jdb_bt-arm
jdb_bp := jdb_bp
thread_IMPL += thread-debug

View File

@@ -0,0 +1,474 @@
IMPLEMENTATION[arm]:
/* jdb_bt-ia32-ux.cpp adapted for ARM */
#include <cstdio>
#include <cctype>
#include "config.h"
#include "jdb.h"
#include "jdb_input.h"
#include "jdb_kobject.h"
#include "jdb_lines.h"
#include "jdb_module.h"
#include "jdb_symbol.h"
#include "mem_layout.h"
#include "keycodes.h"
#include "thread_object.h"
#include "task.h"
class Jdb_bt : public Jdb_module, public Jdb_input_task_addr
{
public:
Jdb_bt() FIASCO_INIT;
private:
static char dummy;
static char first_char;
static char first_char_addr;
static Address addr;
static Thread * tid;
static Kobject *ko_tid;
static Space * task;
};
char Jdb_bt::dummy;
char Jdb_bt::first_char;
char Jdb_bt::first_char_addr;
Address Jdb_bt::addr;
Thread * Jdb_bt::tid;
Space *Jdb_bt::task;
Kobject *Jdb_bt::ko_tid;
// determine the user level ebp and eip considering the current thread state
static void
Jdb_bt::get_user_eip_ebp(Address &eip, Address &ebp)
{
if (!task)
{
// kernel thread doesn't execute user code -- at least we hope so :-)
ebp = eip = 0;
return;
}
Thread *t = tid;
Address tcb_next = (Address)context_of(t->get_kernel_sp()) + Context::Size;
Mword *ktop = (Mword *)(Cpu::stack_align(tcb_next));
#if 0
Jdb::Guessed_thread_state state = Jdb::guess_thread_state(t);
eip = ktop[-5];
#endif
eip = t->user_ip();
ebp = ktop[-7]; /* r11 */
#if 0
if (state == Jdb::s_ipc)
{
// If thread is in IPC, EBP lays on the stack (see C-bindings). EBP
// location is by now dependent from ipc-type, maybe different for
// other syscalls, maybe unstable during calls but only for user EBP.
Mword entry_esp = ktop[-2];
Mword entry_ss = ktop[-1];
if (entry_ss & 3)
{
// kernel entered from user level
if (eip >= Mem_layout::Syscalls)
{
if ((entry_esp & (sizeof(Mword)-1))
|| !Jdb::peek((Address*)entry_esp, task, eip))
{
printf("\n esp page invalid");
ebp = eip = 0;
return;
}
entry_esp += sizeof(Mword);
}
if ((entry_esp & (sizeof(Mword)-1))
||!Jdb::peek((Mword*)entry_esp, task, ebp))
{
printf("\n esp page invalid");
ebp = eip = 0;
return;
}
}
}
else if (state == Jdb::s_pagefault)
{
// see pagefault handler gate stack layout
ebp = ktop[-6];
}
else if (state == Jdb::s_slowtrap)
{
// see slowtrap handler gate stack layout
ebp = ktop[-13];
}
else
{
// Thread is doing (probaly) no IPC currently so we guess the
// user ebp by following the kernel ebp upwards. Some kernel
// entry pathes (e.g. timer interrupt) push the ebp register
// like gcc.
ebp = get_user_ebp_following_kernel_stack();
}
#endif
}
#if 0
static Mword
Jdb_bt::get_user_ebp_following_kernel_stack()
{
if (!Config::Have_frame_ptr)
return 0;
Mword ebp, dummy;
get_kernel_eip_ebp(dummy, dummy, ebp);
for (int i=0; i<30 /* sanity check */; i++)
{
Mword m1, m2;
if ( (ebp == 0) || (ebp & (sizeof(Mword)-1))
|| !Jdb::peek((Address*)ebp, 0 /*kernel*/, m1)
|| !Jdb::peek((Address*)ebp+1, 0 /*kernel*/, m2))
// invalid ebp -- leaving
return 0;
ebp = m1;
if (!Mem_layout::in_kernel_code(m2))
{
#if 0
if (m2 < Kmem::mem_user_max)
// valid user ebp found
return m1;
else
// invalid ebp
return 0;
#endif
}
}
return 0;
}
#endif
struct Is_current
{
Thread *tid;
mutable Thread *c;
mutable Cpu_number cpu;
void operator () (Cpu_number _cpu) const
{
Thread *t = Jdb::get_thread(_cpu);
if (t == tid)
{ c = t; cpu = _cpu; }
}
};
static void
Jdb_bt::get_kernel_eip_ebp(Mword &eip1, Mword &eip2, Mword &ebp)
{
if (tid == Jdb::get_current_active())
{
ebp = (Mword)__builtin_frame_address(3);
eip1 = eip2 = 0;
}
else
{
Is_current is_current;
is_current.tid = tid;
is_current.c = 0;
is_current.cpu = Cpu_number(0);
Jdb::foreach_cpu(is_current);
Mword *ksp;
Mword tcb;
if (is_current.c)
{
ksp = (Mword*)Jdb::entry_frame.cpu(is_current.cpu)->sp();
tcb = (Mword)is_current.c;
printf("\n current on cpu %u\n", cxx::int_value<Cpu_number>(is_current.cpu));
}
else
{
ksp = (Mword*) tid->get_kernel_sp();
tcb = Mword(tid); //Mem_layout::Tcbs + tid.gthread()*Context::size;
}
Mword tcb_next = tcb + Context::Size;
// search for valid ebp/eip
for (int i=0; (Address)(ksp+i+1)<tcb_next-20; i++)
{
if (Mem_layout::in_kernel_code(ksp[i+1]) &&
ksp[i] >= tcb+0x180 &&
ksp[i] < tcb_next-20 &&
ksp[i] > (Address)(ksp+i))
{
// valid frame pointer found
ebp = ksp[i ];
eip1 = ksp[i+1];
eip2 = ksp[0];
return;
}
}
ebp = eip1 = eip2 = 0;
}
}
/** Show one backtrace item we found. Add symbol name and line info */
static void
Jdb_bt::show_item(int nr, Address ksp, Address addr, Address_type user)
{
char buffer[74];
printf(" %s#%d " L4_PTR_FMT " " L4_PTR_FMT "", nr<10 ? " ": "", nr, ksp, addr);
Address sym_addr = addr;
if (Jdb_symbol::match_addr_to_symbol_fuzzy(&sym_addr,
user == ADDR_KERNEL ? 0 : task,
buffer,
56 < sizeof(buffer)
? 56 : sizeof(buffer))
// if the previous symbol is to far away assume that there is no
// symbol for that entry
&& (addr-sym_addr < 1024))
{
printf(" : %s", buffer);
if (addr-sym_addr)
printf(" %s+ 0x%lx\033[m", Jdb::esc_line, addr-sym_addr);
}
#if 0
// search appropriate line backwards starting from addr-1 because we
// don't want to see the line info for the next statement after the
// call but the line info for the call itself
Address line_addr = addr-1;
if (Jdb_lines::match_addr_to_line_fuzzy(&line_addr,
user == ADDR_KERNEL ? 0 : task,
buffer, sizeof(buffer)-1, 0)
// if the previous line is to far away assume that there is no
// line for that entry
&& (addr-line_addr < 128))
printf("\n%6s%s%s\033[m", "", Jdb::esc_line, buffer);
#endif
putchar('\n');
}
static void
Jdb_bt::show_without_ebp()
{
Mword *ksp = (Mword*) tid->get_kernel_sp();
Mword tcb_next = Mword(tid) + Context::Size;
// search for valid eip
for (int i=0, j=1; (Address)(ksp+i)<tcb_next-20; i++)
{
if (Mem_layout::in_kernel_code(ksp[i]))
show_item(j++, (Address)(ksp+i), ksp[i], ADDR_KERNEL);
}
}
static void
Jdb_bt::show(Mword ebp, Mword eip1, Mword eip2, Address_type user)
{
for (int i=0; i<40 /*sanity check*/; i++)
{
Mword m1, m2;
if (i > 1)
{
/* stack layout:
*
* lr1 <-
* fp1 |
* ... |
* lr2 |
* fp2 --
*/
if ( (ebp == 0) || (ebp & (sizeof(Mword)-1))
|| !Jdb::peek((Address*)ebp-1, task, m1)
|| !Jdb::peek((Address*)ebp, task, m2))
// invalid ebp -- leaving
return;
ebp = m1;
#if 0
if ( (user==ADDR_KERNEL && !Mem_layout::in_kernel_code(m2))
||(user==ADDR_USER && (m2==0 || m2>Kmem::mem_user_max)))
// no valid eip found -- leaving
return;
#endif
}
else if (i == 1)
{
if (eip1 == 0)
continue;
m2 = eip1;
}
else
{
if (eip2 == 0)
continue;
m2 = eip2;
}
show_item(i, ebp, m2, user);
}
}
PUBLIC
Jdb_module::Action_code
Jdb_bt::action(int cmd, void *&args, char const *&fmt, int &next_char)
{
if (cmd == 0)
{
Address eip, ebp;
if (args == &dummy)
{
// default value for thread
tid = Jdb::get_current_active();
fmt = "%C";
args = &first_char;
return EXTRA_INPUT;
}
else if (args == &first_char)
{
if (first_char == 't')
{
putstr("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\033[K");
fmt = " thread=%q";
args = &ko_tid;
return EXTRA_INPUT;
}
else if (first_char == 'a')
{
putstr("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\033[K");
fmt = " addr=%C";
args = &Jdb_input_task_addr::first_char;
return EXTRA_INPUT;
}
else if (first_char != KEY_RETURN && first_char != ' ')
{
// ignore wrong input
fmt = "%C";
return EXTRA_INPUT;
}
else
// backtrace from current thread
goto start_backtrace;
}
else if (args == &ko_tid)
{
{
tid = 0;
Kobject* o = ko_tid;
if (o)
tid = Kobject::dcast<Thread_object*>(o);
if (!tid)
{
puts(" Invalid thread id");
return NOTHING;
}
}
start_backtrace:
task = tid->space();
get_user_eip_ebp(eip, ebp);
start_backtrace_known_ebp:
printf("\n\nbacktrace (thread %lx, fp=" L4_PTR_FMT
", pc=" L4_PTR_FMT "):\n",
tid->dbg_info()->dbg_id(), ebp, eip);
if (task != 0)
show(ebp, eip, 0, ADDR_USER);
#if 0
if (!Config::Have_frame_ptr)
{
puts("\n --kernel-bt-follows-- "
"(don't trust w/o frame pointer!!)");
task = 0;
show_without_ebp();
}
else
{
Mword eip2;
puts("\n --kernel-bt-follows--");
get_kernel_eip_ebp(eip, eip2, ebp);
task = 0;
show(ebp, eip, eip2, ADDR_KERNEL);
}
#endif
putchar('\n');
}
else
{
Jdb_module::Action_code code;
switch ((code = Jdb_input_task_addr::action(args, fmt, next_char)))
{
case ERROR:
return ERROR;
case NOTHING:
task = Jdb_input_task_addr::space();
eip = 0;
ebp = Jdb_input_task_addr::addr();
goto start_backtrace_known_ebp;
default:
return code;
}
}
} else if (cmd == 1) {
if ((args == &dummy) && (dummy == 't'))
Jdb::execute_command("bt");
}
return NOTHING;
}
PUBLIC
Jdb_module::Cmd const *
Jdb_bt::cmds() const
{
static Cmd cs[] =
{
{ 0, "bt", "backtrace", " [a]ddr/[t]hread",
"bt[t<threadid>][<addr>]\tshow backtrace of current/given "
"thread/addr",
&dummy },
/*
* The "bt" command only works with the help of the "b" command,
* which is currently not registered by another module on ARM
*/
{ 1, "b", "b_dummy", "%c",
"b\tdummy command to make the 'bt' command work in the\n"
"\tabsence of a real 'b' command",
&dummy },
};
return cs;
}
PUBLIC
int
Jdb_bt::num_cmds() const
{
return 2;
}
IMPLEMENT
Jdb_bt::Jdb_bt()
: Jdb_module("INFO")
{}
static Jdb_bt jdb_bt INIT_PRIORITY(JDB_MODULE_INIT_PRIO);

View File

@@ -24,9 +24,9 @@ if (!defined $ARGV[0])
exit 1;
}
my $nm = 'nm';
my $nm = 'genode-x86-nm';
$nm = "$ENV{'SYSTEM_TARGET'}$nm" if defined $ENV{"SYSTEM_TARGET"};
$nm = 'arm-linux-nm' if !(system("file -L $ARGV[0] | grep -qw ARM") >> 8);
$nm = 'genode-arm-nm' if !(system("file -L $ARGV[0] | grep -qw ARM") >> 8);
while (@ARGV)
{