From 3854f1b9418fddc7518b1222b67148acee758177 Mon Sep 17 00:00:00 2001 From: Christian Prochaska Date: Sat, 23 Feb 2013 01:40:01 +0100 Subject: [PATCH] Basic JDB backtrace support for ARM --- kernel/fiasco/src/Modules.arm | 3 +- kernel/fiasco/src/jdb/arm/jdb_bt-arm.cpp | 474 +++++++++++++++++++++++ kernel/fiasco/tool/backtrace | 4 +- 3 files changed, 478 insertions(+), 3 deletions(-) create mode 100644 kernel/fiasco/src/jdb/arm/jdb_bt-arm.cpp diff --git a/kernel/fiasco/src/Modules.arm b/kernel/fiasco/src/Modules.arm index 2e75b027..9f70e799 100644 --- a/kernel/fiasco/src/Modules.arm +++ b/kernel/fiasco/src/Modules.arm @@ -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 diff --git a/kernel/fiasco/src/jdb/arm/jdb_bt-arm.cpp b/kernel/fiasco/src/jdb/arm/jdb_bt-arm.cpp new file mode 100644 index 00000000..016cdc85 --- /dev/null +++ b/kernel/fiasco/src/jdb/arm/jdb_bt-arm.cpp @@ -0,0 +1,474 @@ +IMPLEMENTATION[arm]: + +/* jdb_bt-ia32-ux.cpp adapted for ARM */ + +#include +#include + +#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(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+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) 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(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][]\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); + diff --git a/kernel/fiasco/tool/backtrace b/kernel/fiasco/tool/backtrace index 71c035a5..164787dc 100755 --- a/kernel/fiasco/tool/backtrace +++ b/kernel/fiasco/tool/backtrace @@ -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) {