From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 27827 invoked by alias); 20 Sep 2013 11:30:55 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Received: (qmail 27374 invoked by uid 89); 20 Sep 2013 11:30:51 -0000 Received: from mga09.intel.com (HELO mga09.intel.com) (134.134.136.24) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Fri, 20 Sep 2013 11:30:51 +0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-3.3 required=5.0 tests=AWL,BAYES_00,KAM_STOCKGEN,KHOP_THREADED,RDNS_NONE autolearn=no version=3.3.2 X-HELO: mga09.intel.com Received: from orsmga001.jf.intel.com ([10.7.209.18]) by orsmga102.jf.intel.com with ESMTP; 20 Sep 2013 04:27:41 -0700 X-ExtLoop1: 1 Received: from irvmail001.ir.intel.com ([163.33.26.43]) by orsmga001.jf.intel.com with ESMTP; 20 Sep 2013 04:30:44 -0700 Received: from ulslx001.iul.intel.com (ulslx001.iul.intel.com [172.28.207.63]) by irvmail001.ir.intel.com (8.14.3/8.13.6/MailSET/Hub) with ESMTP id r8KBUh9f019092; Fri, 20 Sep 2013 12:30:44 +0100 Received: from ulslx001.iul.intel.com (localhost [127.0.0.1]) by ulslx001.iul.intel.com with ESMTP id r8KBUh4c032199; Fri, 20 Sep 2013 13:30:43 +0200 Received: (from mmetzger@localhost) by ulslx001.iul.intel.com with id r8KBUh9V032195; Fri, 20 Sep 2013 13:30:43 +0200 From: Markus Metzger To: jan.kratochvil@redhat.com Cc: gdb-patches@sourceware.org Subject: [patch v6 18/21] record-btrace: extend unwinder Date: Fri, 20 Sep 2013 11:31:00 -0000 Message-Id: <1379676639-31802-19-git-send-email-markus.t.metzger@intel.com> In-Reply-To: <1379676639-31802-1-git-send-email-markus.t.metzger@intel.com> References: <1379676639-31802-1-git-send-email-markus.t.metzger@intel.com> X-IsSubscribed: yes X-SW-Source: 2013-09/txt/msg00755.txt.bz2 Extend the always failing unwinder to provide the PC based on the call structure detected in the branch trace. There's an open point: An assertion in get_frame_id at frame.c:340 requires that a frame provides a stack address. The record-btrace unwinder can't provide this since the trace does not contain data. I incorrectly set stack_addr_p to 1 to avoid the assertion. Reviewed-by: Eli Zaretskii 2013-04-24 Markus Metzger * frame.h (enum frame_type) : New. : New. (frame_is_tailcall): New. (skip_artificial_frames, frame_pop, get_frame_address_in_block): Call frame_is_tailcall. * infcmd.c (construct_inferior_arguments): Call frame_is_tailcall. * stack.h (frame_info): Call frame_is_tailcall. * dwarf2-frame-tailcall.c (frame_is_tailcall): Rename to .. (frame_is_dwarf2_tailcall): ... this. (cache_find): Update. * record-btrace.c: Include hashtab.h. (btrace_get_bfun_name): New. (btrace_call_history): Call btrace_get_bfun_name. (struct btrace_frame_cache): New. (bfcache): New. (bfcache_hash, bfcache_eq, bfcache_new): New. (btrace_get_frame_function): New. (record_btrace_frame_unwind_stop_reason): Allow unwinding. (record_btrace_frame_this_id): Compute own id. (record_btrace_frame_prev_register): Provide PC, throw_error for all other registers. (record_btrace_frame_sniffer): Detect btrace frames. (record_btrace_tailcall_frame_sniffer): New. (record_btrace_frame_dealloc_cache): New. (record_btrace_frame_unwind): Add new functions. (_initialize_record_btrace): Allocate cache. * btrace.c (btrace_clear): Call reinit_frame_cache. * NEWS: Announce it. testsuite/ * gdb.btrace/record_goto.exp: Add backtrace test. * gdb.btrace/tailcall.exp: Add backtrace test. --- gdb/NEWS | 2 + gdb/btrace.c | 4 + gdb/dwarf2-frame-tailcall.c | 4 +- gdb/frame.c | 8 +- gdb/frame.h | 17 ++- gdb/infcmd.c | 2 +- gdb/record-btrace.c | 290 ++++++++++++++++++++++++++++- gdb/stack.c | 2 +- gdb/testsuite/gdb.btrace/record_goto.exp | 13 ++ gdb/testsuite/gdb.btrace/tailcall.exp | 17 ++ 10 files changed, 340 insertions(+), 19 deletions(-) diff --git a/gdb/NEWS b/gdb/NEWS index 5c41a09..4b41e87 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -26,6 +26,8 @@ Nios II GNU/Linux nios2*-*-linux Texas Instruments MSP430 msp430*-*-elf * The btrace record target supports the 'record goto' command. + For locations inside the execution trace, the back trace is computed + based on the information stored in the execution trace. * The command 'record function-call-history' supports a new modifier '/c' to indent the function names based on their call stack depth. diff --git a/gdb/btrace.c b/gdb/btrace.c index 8f401f9..7029034 100644 --- a/gdb/btrace.c +++ b/gdb/btrace.c @@ -754,6 +754,10 @@ btrace_clear (struct thread_info *tp) DEBUG ("clear thread %d (%s)", tp->num, target_pid_to_str (tp->ptid)); + /* Make sure btrace frames that may hold a pointer into the branch + trace data are destroyed. */ + reinit_frame_cache (); + btinfo = &tp->btrace; it = btinfo->begin; diff --git a/gdb/dwarf2-frame-tailcall.c b/gdb/dwarf2-frame-tailcall.c index b82a051..35de19d 100644 --- a/gdb/dwarf2-frame-tailcall.c +++ b/gdb/dwarf2-frame-tailcall.c @@ -140,7 +140,7 @@ cache_unref (struct tailcall_cache *cache) return 0. */ static int -frame_is_tailcall (struct frame_info *fi) +frame_is_dwarf2_tailcall (struct frame_info *fi) { return frame_unwinder_is (fi, &dwarf2_tailcall_frame_unwind); } @@ -154,7 +154,7 @@ cache_find (struct frame_info *fi) struct tailcall_cache *cache; void **slot; - while (frame_is_tailcall (fi)) + while (frame_is_dwarf2_tailcall (fi)) { fi = get_next_frame (fi); gdb_assert (fi != NULL); diff --git a/gdb/frame.c b/gdb/frame.c index 5c080eb..0fd98ff 100644 --- a/gdb/frame.c +++ b/gdb/frame.c @@ -380,7 +380,7 @@ static struct frame_info * skip_artificial_frames (struct frame_info *frame) { while (get_frame_type (frame) == INLINE_FRAME - || get_frame_type (frame) == TAILCALL_FRAME) + || frame_is_tailcall (frame)) frame = get_prev_frame (frame); return frame; @@ -886,7 +886,7 @@ frame_pop (struct frame_info *this_frame) /* Ignore TAILCALL_FRAME type frames, they were executed already before entering THISFRAME. */ - while (get_frame_type (prev_frame) == TAILCALL_FRAME) + while (frame_is_tailcall (prev_frame)) prev_frame = get_prev_frame (prev_frame); /* Make a copy of all the register values unwound from this frame. @@ -2126,9 +2126,9 @@ get_frame_address_in_block (struct frame_info *this_frame) next_frame = next_frame->next; if ((get_frame_type (next_frame) == NORMAL_FRAME - || get_frame_type (next_frame) == TAILCALL_FRAME) + || frame_is_tailcall (next_frame)) && (get_frame_type (this_frame) == NORMAL_FRAME - || get_frame_type (this_frame) == TAILCALL_FRAME + || frame_is_tailcall (this_frame) || get_frame_type (this_frame) == INLINE_FRAME)) return pc - 1; diff --git a/gdb/frame.h b/gdb/frame.h index a5e1629..f0da19e 100644 --- a/gdb/frame.h +++ b/gdb/frame.h @@ -216,7 +216,11 @@ enum frame_type ARCH_FRAME, /* Sentinel or registers frame. This frame obtains register values direct from the inferior's registers. */ - SENTINEL_FRAME + SENTINEL_FRAME, + /* A branch tracing frame. */ + BTRACE_FRAME, + /* A branch tracing tail call frame. */ + BTRACE_TAILCALL_FRAME }; /* For every stopped thread, GDB tracks two frames: current and @@ -773,4 +777,15 @@ extern struct frame_info *create_new_frame (CORE_ADDR base, CORE_ADDR pc); extern int frame_unwinder_is (struct frame_info *fi, const struct frame_unwind *unwinder); +/* Return non-zero if FRAME is a tailcall frame, return zero otherwise. */ + +static inline int +frame_is_tailcall (struct frame_info *frame) +{ + enum frame_type type; + + type = get_frame_type (frame); + return (type == TAILCALL_FRAME || type == BTRACE_TAILCALL_FRAME); +} + #endif /* !defined (FRAME_H) */ diff --git a/gdb/infcmd.c b/gdb/infcmd.c index e29dcde..37cc917 100644 --- a/gdb/infcmd.c +++ b/gdb/infcmd.c @@ -1774,7 +1774,7 @@ finish_command (char *arg, int from_tty) /* Ignore TAILCALL_FRAME type frames, they were executed already before entering THISFRAME. */ - while (get_frame_type (frame) == TAILCALL_FRAME) + while (frame_is_tailcall (frame)) frame = get_prev_frame (frame); /* Find the function we will return from. */ diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c index 14a3ff5..14a99aa 100644 --- a/gdb/record-btrace.c +++ b/gdb/record-btrace.c @@ -34,6 +34,7 @@ #include "filenames.h" #include "regcache.h" #include "frame-unwind.h" +#include "hashtab.h" /* The target_ops of record-btrace. */ static struct target_ops record_btrace_ops; @@ -522,6 +523,28 @@ btrace_call_history_src_line (struct ui_out *uiout, ui_out_field_int (uiout, "max line", end); } +/* Get the name of a branch trace function. */ + +static const char * +btrace_get_bfun_name (const struct btrace_function *bfun) +{ + struct minimal_symbol *msym; + struct symbol *sym; + + if (bfun == NULL) + return "??"; + + msym = bfun->msym; + sym = bfun->sym; + + if (sym != NULL) + return SYMBOL_PRINT_NAME (sym); + else if (msym != NULL) + return SYMBOL_PRINT_NAME (msym); + else + return "??"; +} + /* Disassemble a section of the recorded function trace. */ static void @@ -543,8 +566,8 @@ btrace_call_history (struct ui_out *uiout, struct symbol *sym; bfun = btrace_call_get (&it); - msym = bfun->msym; sym = bfun->sym; + msym = bfun->msym; /* Print the function index. */ ui_out_field_uint (uiout, "index", bfun->number); @@ -908,13 +931,100 @@ record_btrace_prepare_to_store (struct target_ops *ops, } } +/* The branch trace frame cache. */ + +struct btrace_frame_cache +{ + /* The thread. */ + struct thread_info *tp; + + /* The frame info. */ + struct frame_info *frame; + + /* The branch trace function segment. */ + const struct btrace_function *bfun; +}; + +/* A struct btrace_frame_cache hash table indexed by NEXT. */ + +static htab_t bfcache; + +/* hash_f for htab_create_alloc of bfcache. */ + +static hashval_t +bfcache_hash (const void *arg) +{ + const struct btrace_frame_cache *cache = arg; + + return htab_hash_pointer (cache->frame); +} + +/* eq_f for htab_create_alloc of bfcache. */ + +static int +bfcache_eq (const void *arg1, const void *arg2) +{ + const struct btrace_frame_cache *cache1 = arg1; + const struct btrace_frame_cache *cache2 = arg2; + + return cache1->frame == cache2->frame; +} + +/* Create a new btrace frame cache. */ + +static struct btrace_frame_cache * +bfcache_new (struct frame_info *frame) +{ + struct btrace_frame_cache *cache; + void **slot; + + cache = FRAME_OBSTACK_ZALLOC (struct btrace_frame_cache); + cache->frame = frame; + + slot = htab_find_slot (bfcache, cache, INSERT); + gdb_assert (*slot == NULL); + *slot = cache; + + return cache; +} + +/* Extract the branch trace function from a branch trace frame. */ + +static const struct btrace_function * +btrace_get_frame_function (struct frame_info *frame) +{ + const struct btrace_frame_cache *cache; + const struct btrace_function *bfun; + struct btrace_frame_cache pattern; + void **slot; + + pattern.frame = frame; + + slot = htab_find_slot (bfcache, &pattern, NO_INSERT); + if (slot == NULL) + return NULL; + + cache = *slot; + return cache->bfun; +} + /* Implement stop_reason method for record_btrace_frame_unwind. */ static enum unwind_stop_reason record_btrace_frame_unwind_stop_reason (struct frame_info *this_frame, void **this_cache) { - return UNWIND_UNAVAILABLE; + const struct btrace_frame_cache *cache; + const struct btrace_function *bfun; + + cache = *this_cache; + bfun = cache->bfun; + gdb_assert (bfun != NULL); + + if (bfun->up == NULL) + return UNWIND_UNAVAILABLE; + + return UNWIND_NO_REASON; } /* Implement this_id method for record_btrace_frame_unwind. */ @@ -923,7 +1033,28 @@ static void record_btrace_frame_this_id (struct frame_info *this_frame, void **this_cache, struct frame_id *this_id) { - /* Leave there the outer_frame_id value. */ + const struct btrace_frame_cache *cache; + const struct btrace_function *bfun; + CORE_ADDR stack, code, special; + + cache = *this_cache; + + bfun = cache->bfun; + gdb_assert (bfun != NULL); + + while (bfun->segment.prev != NULL) + bfun = bfun->segment.prev; + + stack = 0; + code = get_frame_func (this_frame); + special = (CORE_ADDR) bfun; + + *this_id = frame_id_build_special (stack, code, special); + + DEBUG ("[frame] %s id: (!stack, pc=%s, special=%s)", + btrace_get_bfun_name (cache->bfun), + core_addr_to_string_nz (this_id->code_addr), + core_addr_to_string_nz (this_id->special_addr)); } /* Implement prev_register method for record_btrace_frame_unwind. */ @@ -933,8 +1064,47 @@ record_btrace_frame_prev_register (struct frame_info *this_frame, void **this_cache, int regnum) { - throw_error (NOT_AVAILABLE_ERROR, - _("Registers are not available in btrace record history")); + const struct btrace_frame_cache *cache; + const struct btrace_function *bfun, *caller; + const struct btrace_insn *insn; + struct gdbarch *gdbarch; + CORE_ADDR pc; + int pcreg; + + gdbarch = get_frame_arch (this_frame); + pcreg = gdbarch_pc_regnum (gdbarch); + if (pcreg < 0 || regnum != pcreg) + throw_error (NOT_AVAILABLE_ERROR, + _("Registers are not available in btrace record history")); + + cache = *this_cache; + bfun = cache->bfun; + gdb_assert (bfun != NULL); + + caller = bfun->up; + if (caller == NULL) + throw_error (NOT_AVAILABLE_ERROR, + _("No caller in btrace record history")); + + if ((bfun->flags & BFUN_UP_LINKS_TO_RET) != 0) + { + insn = VEC_index (btrace_insn_s, caller->insn, 0); + pc = insn->pc; + } + else + { + insn = VEC_last (btrace_insn_s, caller->insn); + pc = insn->pc; + + if ((bfun->flags & BFUN_UP_LINKS_TO_TAILCALL) == 0) + pc += gdb_insn_length (gdbarch, pc); + } + + DEBUG ("[frame] unwound PC in %s on level %d: %s", + btrace_get_bfun_name (bfun), bfun->level, + core_addr_to_string_nz (pc)); + + return frame_unwind_got_address (this_frame, regnum, pc); } /* Implement sniffer method for record_btrace_frame_unwind. */ @@ -944,15 +1114,99 @@ record_btrace_frame_sniffer (const struct frame_unwind *self, struct frame_info *this_frame, void **this_cache) { + const struct btrace_function *bfun; + struct btrace_frame_cache *cache; struct thread_info *tp; - struct btrace_thread_info *btinfo; - struct btrace_insn_iterator *replay; + struct frame_info *next; /* THIS_FRAME does not contain a reference to its thread. */ tp = find_thread_ptid (inferior_ptid); gdb_assert (tp != NULL); - return btrace_is_replaying (tp); + bfun = NULL; + next = get_next_frame (this_frame); + if (next == NULL) + { + const struct btrace_insn_iterator *replay; + + replay = tp->btrace.replay; + if (replay != NULL) + bfun = replay->function; + } + else + { + const struct btrace_function *callee; + + callee = btrace_get_frame_function (next); + if (callee != NULL && (callee->flags & BFUN_UP_LINKS_TO_TAILCALL) == 0) + bfun = callee->up; + } + + if (bfun == NULL) + return 0; + + DEBUG ("[frame] sniffed frame for %s on level %d", + btrace_get_bfun_name (bfun), bfun->level); + + /* This is our frame. Initialize the frame cache. */ + cache = bfcache_new (this_frame); + cache->tp = tp; + cache->bfun = bfun; + + *this_cache = cache; + return 1; +} + +/* Implement sniffer method for record_btrace_tailcall_frame_unwind. */ + +static int +record_btrace_tailcall_frame_sniffer (const struct frame_unwind *self, + struct frame_info *this_frame, + void **this_cache) +{ + const struct btrace_function *bfun, *callee; + struct btrace_frame_cache *cache; + struct frame_info *next; + + next = get_next_frame (this_frame); + if (next == NULL) + return 0; + + callee = btrace_get_frame_function (next); + if (callee == NULL) + return 0; + + if ((callee->flags & BFUN_UP_LINKS_TO_TAILCALL) == 0) + return 0; + + bfun = callee->up; + if (bfun == NULL) + return 0; + + DEBUG ("[frame] sniffed tailcall frame for %s on level %d", + btrace_get_bfun_name (bfun), bfun->level); + + /* This is our frame. Initialize the frame cache. */ + cache = bfcache_new (this_frame); + cache->tp = find_thread_ptid (inferior_ptid); + cache->bfun = bfun; + + *this_cache = cache; + return 1; +} + +static void +record_btrace_frame_dealloc_cache (struct frame_info *self, void *this_cache) +{ + struct btrace_frame_cache *cache; + void **slot; + + cache = this_cache; + + slot = htab_find_slot (bfcache, cache, NO_INSERT); + gdb_assert (slot != NULL); + + htab_remove_elt (bfcache, cache); } /* btrace recording does not store previous memory content, neither the stack @@ -963,12 +1217,24 @@ record_btrace_frame_sniffer (const struct frame_unwind *self, static const struct frame_unwind record_btrace_frame_unwind = { - NORMAL_FRAME, + BTRACE_FRAME, record_btrace_frame_unwind_stop_reason, record_btrace_frame_this_id, record_btrace_frame_prev_register, NULL, - record_btrace_frame_sniffer + record_btrace_frame_sniffer, + record_btrace_frame_dealloc_cache +}; + +static const struct frame_unwind record_btrace_tailcall_frame_unwind = +{ + BTRACE_TAILCALL_FRAME, + record_btrace_frame_unwind_stop_reason, + record_btrace_frame_this_id, + record_btrace_frame_prev_register, + NULL, + record_btrace_tailcall_frame_sniffer, + record_btrace_frame_dealloc_cache }; /* The to_resume method of target record-btrace. */ @@ -1156,6 +1422,7 @@ init_record_btrace_ops (void) ops->to_store_registers = record_btrace_store_registers; ops->to_prepare_to_store = record_btrace_prepare_to_store; ops->to_get_unwinder = &record_btrace_frame_unwind; + ops->to_get_tailcall_unwinder = &record_btrace_tailcall_frame_unwind; ops->to_resume = record_btrace_resume; ops->to_wait = record_btrace_wait; ops->to_find_new_threads = record_btrace_find_new_threads; @@ -1191,4 +1458,7 @@ _initialize_record_btrace (void) init_record_btrace_ops (); add_target (&record_btrace_ops); + + bfcache = htab_create_alloc (50, bfcache_hash, bfcache_eq, NULL, + xcalloc, xfree); } diff --git a/gdb/stack.c b/gdb/stack.c index cd4ac7a..66c6bad 100644 --- a/gdb/stack.c +++ b/gdb/stack.c @@ -1509,7 +1509,7 @@ frame_info (char *addr_exp, int from_tty) printf_filtered (_(" Outermost frame: %s\n"), frame_stop_reason_string (reason)); } - else if (get_frame_type (fi) == TAILCALL_FRAME) + else if (frame_is_tailcall (fi)) puts_filtered (" tail call frame"); else if (get_frame_type (fi) == INLINE_FRAME) printf_filtered (" inlined into frame %d", diff --git a/gdb/testsuite/gdb.btrace/record_goto.exp b/gdb/testsuite/gdb.btrace/record_goto.exp index ce27392..27c579b 100644 --- a/gdb/testsuite/gdb.btrace/record_goto.exp +++ b/gdb/testsuite/gdb.btrace/record_goto.exp @@ -86,6 +86,19 @@ gdb_test "record instruction-history" " gdb_test "record goto 26" " .*fun3 \\(\\) at record_goto.c:35.*" "record_goto - goto 26" +# check the back trace at that location +gdb_test "backtrace" " +#0.*fun3.*at record_goto.c:35.*\r +#1.*fun4.*at record_goto.c:44.*\r +#2.*main.*at record_goto.c:50.*\r +Backtrace stopped: not enough registers or memory available to unwind further" "backtrace at 25" + +# walk the backtrace +gdb_test "up" " +.*fun4.*at record_goto.c:44.*" "up to fun4" +gdb_test "up" " +.*main.*at record_goto.c:50.*" "up to main" + # the function call history should start at the new location gdb_test "record function-call-history /ci -" " 8\t fun3\tinst 19,21\r diff --git a/gdb/testsuite/gdb.btrace/tailcall.exp b/gdb/testsuite/gdb.btrace/tailcall.exp index 74c1ab7..900d731 100644 --- a/gdb/testsuite/gdb.btrace/tailcall.exp +++ b/gdb/testsuite/gdb.btrace/tailcall.exp @@ -58,3 +58,20 @@ gdb_test "record function-call-history /c 1" " 1\t foo\r 2\t bar\r 3\tmain" "tailcall - calls indented" + +# go into bar +gdb_test "record goto 3" " +.*bar \\(\\) at .*x86-tailcall.c:24\r\n.*" "go to bar" + +# check the backtrace +gdb_test "backtrace" " +#0.*bar \\(\\) at x86-tailcall.c:24\r +#1.*foo \\(\\) at x86-tailcall.c:29\r +#2.*main \\(\\) at x86-tailcall.c:37\r +Backtrace stopped: not enough registers or memory available to unwind further" "backtrace in bar" + +# walk the backtrace +gdb_test "up" " +#1\[^\r\n\]*foo \\(\\) at x86-tailcall.c:29\r\n.*" "up to foo" +gdb_test "up" " +#2\[^\r\n\]*main \\(\\) at x86-tailcall.c:37\r\n.*" "up to main" -- 1.7.1