Re: JIT debugging (Attach and speed)

Pedro Alves
2016-03-24T01:17:59Z

On 03/23/2016 08:51 PM, Yichao Yu wrote:

> Also, it looks like gdb is trying to get info about the current frame?
> Can this be disabled for this breakpoint since it's not a "real" one?

Excellent suggestion. Seems like we can avoid computing a
frame unless we're stepping, or presenting a stop.

The prototype patch below (also in users/palves/jit-speedup, along
with the attach fixes), simply delays computing a frame until
it's necessary, and it cuts down the time for:

gdb ---args ./testsuite/outputs/gdb.base/jit/jit-main ./testsuite/outputs/gdb.base/jit/jit-solib.so 2000

from "takes too long so I canceled it" minutes, to around 2 seconds, for me.

There's one hack though -- I commented out the skip_inline_frames
call... We'll need to handle that some other way, or in some
other place. As is, this regresses some inline tests, naturally...

Having a pending breakpoint that is never resolved slows things down
significantly again though... The perf profile shows that breakpoint_re_set
ends up calling parse_linespec which wants the selected block, which wants
the selected frame, and then we're back at the same... Maybe we
could reconsider how pending breakpoints are re-parsed, and which
context they're parsed in. Not sure.

Still, very promising.

-----------
From a1e599495aafd10ae0505558343136f90b291038 Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Wed, 23 Mar 2016 18:11:45 +0000
Subject: [PATCH] Avoid creating a frame in internal stops

This speeds up JIT library loading by ${a lot}.
---
gdb/breakpoint.c | 3 +-
gdb/infrun.c | 98 ++++++++++++++++++++++++++++++--------------------------
2 files changed, 54 insertions(+), 47 deletions(-)

diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index f99a7ab..ed708a9 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -5792,8 +5792,7 @@ handle_jit_event (void)
breakpoint_re_set. */
target_terminal_ours_for_output ();
- frame = get_current_frame ();
- gdbarch = get_frame_arch (frame);
+ gdbarch = get_regcache_arch (get_current_regcache ());
jit_event_handler (gdbarch);
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 696105d..6067cb0 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -5608,8 +5608,6 @@ finish_step_over (struct execution_control_state *ecs)
static void
handle_signal_stop (struct execution_control_state *ecs)
{
- struct frame_info *frame;
- struct gdbarch *gdbarch;
int stopped_by_watchpoint;
enum stop_kind stop_soon;
int random_signal;
@@ -5717,10 +5715,6 @@ handle_signal_stop (struct execution_control_state *ecs)
deprecated_context_hook (ptid_to_global_thread_id (ecs->ptid));
}
- /* At this point, get hold of the now-current thread's frame. */
- frame = get_current_frame ();
- gdbarch = get_frame_arch (frame);
-
/* Pull the single step breakpoints out of the target. */
if (ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP)
{
@@ -5774,7 +5768,7 @@ handle_signal_stop (struct execution_control_state *ecs)
it in a moment. */
if (stopped_by_watchpoint
&& (target_have_steppable_watchpoint
- || gdbarch_have_nonsteppable_watchpoint (gdbarch)))
+ || gdbarch_have_nonsteppable_watchpoint (get_regcache_arch (get_current_regcache ()))))
{
/* At this point, we are stopped at an instruction which has
attempted to write to a piece of memory under control of
@@ -5838,27 +5832,25 @@ handle_signal_stop (struct execution_control_state *ecs)
user had set a breakpoint on that inlined code, the missing
skip_inline_frames call would break things. Fortunately
that's an extremely unlikely scenario. */
+#if 0
if (!pc_at_non_inline_function (aspace, stop_pc, &ecs->ws)
&& !(ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP
&& ecs->event_thread->control.trap_expected
&& pc_at_non_inline_function (aspace,
ecs->event_thread->prev_pc,
&ecs->ws)))
- {
- skip_inline_frames (ecs->ptid);
-
- /* Re-fetch current thread's frame in case that invalidated
- the frame cache. */
- frame = get_current_frame ();
- gdbarch = get_frame_arch (frame);
- }
+ skip_inline_frames (ecs->ptid);
+#endif
}
if (ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP
&& ecs->event_thread->control.trap_expected
- && gdbarch_single_step_through_delay_p (gdbarch)
+ && gdbarch_single_step_through_delay_p (get_regcache_arch (get_current_regcache ()))
&& currently_stepping (ecs->event_thread))
{
+ struct gdbarch *gdbarch = get_regcache_arch (get_current_regcache ());
+ struct frame_info *frame = get_current_frame ();
+
/* We're trying to step off a breakpoint. Turns out that we're
also on an instruction that needs to be stepped multiple
times before it's been fully executing. E.g., architectures
@@ -5946,6 +5938,9 @@ handle_signal_stop (struct execution_control_state *ecs)
been removed. */
if (random_signal && target_stopped_by_sw_breakpoint ())
{
+ struct frame_info *frame = get_current_frame ();
+ struct gdbarch *gdbarch = get_frame_arch (frame);
+
if (program_breakpoint_here_p (gdbarch, stop_pc))
{
struct regcache *regcache;
@@ -6051,6 +6046,7 @@ handle_signal_stop (struct execution_control_state *ecs)
&& ecs->event_thread->control.trap_expected
&& ecs->event_thread->control.step_resume_breakpoint == NULL)
{
+ struct frame_info *frame = get_current_frame ();
int was_in_line;
/* We were just starting a new sequence, attempting to
@@ -6105,11 +6101,14 @@ handle_signal_stop (struct execution_control_state *ecs)
if (ecs->event_thread->suspend.stop_signal != GDB_SIGNAL_0
&& (pc_in_thread_step_range (stop_pc, ecs->event_thread)
- || ecs->event_thread->control.step_range_end == 1)
- && frame_id_eq (get_stack_frame_id (frame),
- ecs->event_thread->control.step_stack_frame_id)
- && ecs->event_thread->control.step_resume_breakpoint == NULL)
+ || ecs->event_thread->control.step_range_end == 1))
{
+ struct frame_info *frame = get_current_frame ();
+
+ if (frame_id_eq (get_stack_frame_id (frame),
+ ecs->event_thread->control.step_stack_frame_id)
+ && ecs->event_thread->control.step_resume_breakpoint == NULL)
+ {
/* The inferior is about to take a signal that will take it
out of the single step range. Set a breakpoint at the
current PC (which is presumably where the signal handler
@@ -6131,6 +6130,7 @@ handle_signal_stop (struct execution_control_state *ecs)
ecs->event_thread->control.trap_expected = 0;
keep_going (ecs);
return;
+ }
}
/* Note: step_resume_breakpoint may be non-NULL. This occures
@@ -6164,16 +6164,11 @@ static void
process_event_stop_test (struct execution_control_state *ecs)
{
struct symtab_and_line stop_pc_sal;
- struct frame_info *frame;
- struct gdbarch *gdbarch;
CORE_ADDR jmp_buf_pc;
struct bpstat_what what;
/* Handle cases caused by hitting a breakpoint. */
- frame = get_current_frame ();
- gdbarch = get_frame_arch (frame);
-
what = bpstat_what (ecs->event_thread->control.stop_bpstat);
if (what.call_dummy)
@@ -6185,12 +6180,6 @@ process_event_stop_test (struct execution_control_state *ecs)
bp_jit_event). Run them now. */
bpstat_run_callbacks (ecs->event_thread->control.stop_bpstat);
- /* If we hit an internal event that triggers symbol changes, the
- current frame will be invalidated within bpstat_what (e.g., if we
- hit an internal solib event). Re-fetch it. */
- frame = get_current_frame ();
- gdbarch = get_frame_arch (frame);
-
switch (what.main_action)
{
case BPSTAT_WHAT_SET_LONGJMP_RESUME:
@@ -6207,6 +6196,8 @@ process_event_stop_test (struct execution_control_state *ecs)
if (what.is_longjmp)
{
struct value *arg_value;
+ struct frame_info *frame = get_current_frame ();
+ struct gdbarch *gdbarch = get_frame_arch (frame);
/* If we set the longjmp breakpoint via a SystemTap probe,
then use it to extract the arguments. The destination PC
@@ -6233,7 +6224,11 @@ process_event_stop_test (struct execution_control_state *ecs)
insert_longjmp_resume_breakpoint (gdbarch, jmp_buf_pc);
}
else
- check_exception_resume (ecs, frame);
+ {
+ struct frame_info *frame = get_current_frame ();
+
+ check_exception_resume (ecs, frame);
+ }
keep_going (ecs);
return;
@@ -6333,7 +6328,7 @@ process_event_stop_test (struct execution_control_state *ecs)
keep_going (ecs);
return;
}
- fill_in_stop_func (gdbarch, ecs);
+ fill_in_stop_func (get_regcache_arch (get_current_regcache ()), ecs);
if (stop_pc == ecs->stop_func_start
&& execution_direction == EXEC_REVERSE)
{
@@ -6450,12 +6445,7 @@ process_event_stop_test (struct execution_control_state *ecs)
return;
}
- /* Re-fetch current thread's frame in case the code above caused
- the frame cache to be re-initialized, making our FRAME variable
- a dangling pointer. */
- frame = get_current_frame ();
- gdbarch = get_frame_arch (frame);
- fill_in_stop_func (gdbarch, ecs);
+ fill_in_stop_func (get_regcache_arch (get_current_regcache ()), ecs);
/* If stepping through a line, keep going if still within it.
@@ -6469,9 +6459,11 @@ process_event_stop_test (struct execution_control_state *ecs)
if (pc_in_thread_step_range (stop_pc, ecs->event_thread)
&& (execution_direction != EXEC_REVERSE
- || frame_id_eq (get_frame_id (frame),
+ || frame_id_eq (get_frame_id (get_current_frame ()),
ecs->event_thread->control.step_frame_id)))
{
+ struct gdbarch *gdbarch = get_frame_arch (get_current_frame ());
+
if (debug_infrun)
fprintf_unfiltered
(gdb_stdlog, "infrun: stepping inside range [%s-%s]\n",
@@ -6514,6 +6506,7 @@ process_event_stop_test (struct execution_control_state *ecs)
&& ecs->event_thread->control.step_over_calls == STEP_OVER_UNDEBUGGABLE
&& in_solib_dynsym_resolve_code (stop_pc))
{
+ struct gdbarch *gdbarch = get_regcache_arch (get_current_regcache ());
CORE_ADDR pc_after_resolver =
gdbarch_skip_solib_resolver (gdbarch, stop_pc);
@@ -6529,7 +6522,7 @@ process_event_stop_test (struct execution_control_state *ecs)
init_sal (&sr_sal);
sr_sal.pc = pc_after_resolver;
- sr_sal.pspace = get_frame_program_space (frame);
+ sr_sal.pspace = current_program_space;
insert_step_resume_breakpoint_at_sal (gdbarch,
sr_sal, null_frame_id);
@@ -6542,7 +6535,7 @@ process_event_stop_test (struct execution_control_state *ecs)
if (ecs->event_thread->control.step_range_end != 1
&& (ecs->event_thread->control.step_over_calls == STEP_OVER_UNDEBUGGABLE
|| ecs->event_thread->control.step_over_calls == STEP_OVER_ALL)
- && get_frame_type (frame) == SIGTRAMP_FRAME)
+ && get_frame_type (get_current_frame ()) == SIGTRAMP_FRAME)
{
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog,
@@ -6561,12 +6554,14 @@ process_event_stop_test (struct execution_control_state *ecs)
/* macro/2012-04-25: This needs to come before the subroutine
call check below as on some targets return trampolines look
like subroutine calls (MIPS16 return thunks). */
- if (gdbarch_in_solib_return_trampoline (gdbarch,
+ if (gdbarch_in_solib_return_trampoline (get_regcache_arch (get_current_regcache ()),
stop_pc, ecs->stop_func_name)
&& ecs->event_thread->control.step_over_calls != STEP_OVER_NONE)
{
/* Determine where this trampoline returns. */
CORE_ADDR real_stop_pc;
+ struct frame_info *frame = get_current_frame ();
+ struct gdbarch *gdbarch = get_frame_arch (frame);
real_stop_pc = gdbarch_skip_trampoline_code (gdbarch, frame, stop_pc);
@@ -6615,7 +6610,7 @@ process_event_stop_test (struct execution_control_state *ecs)
initial outermost frame, before sp was valid, would
have code_addr == &_start. See the comment in frame_id_eq
for more. */
- if (!frame_id_eq (get_stack_frame_id (frame),
+ if (!frame_id_eq (get_stack_frame_id (get_current_frame ()),
ecs->event_thread->control.step_stack_frame_id)
&& (frame_id_eq (frame_unwind_caller_id (get_current_frame ()),
ecs->event_thread->control.step_stack_frame_id)
@@ -6625,6 +6620,8 @@ process_event_stop_test (struct execution_control_state *ecs)
!= find_pc_function (stop_pc)))))
{
CORE_ADDR real_stop_pc;
+ struct frame_info *frame = get_current_frame ();
+ struct gdbarch *gdbarch = get_frame_arch (frame);
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: stepped into subroutine\n");
@@ -6787,6 +6784,9 @@ process_event_stop_test (struct execution_control_state *ecs)
if (execution_direction == EXEC_REVERSE
&& ecs->event_thread->control.step_over_calls != STEP_OVER_NONE)
{
+ struct frame_info *frame = get_current_frame ();
+ struct gdbarch *gdbarch = get_frame_arch (frame);
+
if (gdbarch_skip_trampoline_code (gdbarch, frame, stop_pc)
|| (ecs->stop_func_start == 0
&& in_solib_dynsym_resolve_code (stop_pc)))
@@ -6825,6 +6825,9 @@ process_event_stop_test (struct execution_control_state *ecs)
&& ecs->stop_func_name == NULL
&& stop_pc_sal.line == 0)
{
+ struct frame_info *frame = get_current_frame ();
+ struct gdbarch *gdbarch = get_frame_arch (frame);
+
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog,
"infrun: stepped into undebuggable function\n");
@@ -6969,7 +6972,12 @@ process_event_stop_test (struct execution_control_state *ecs)
ecs->event_thread->control.step_range_start = stop_pc_sal.pc;
ecs->event_thread->control.step_range_end = stop_pc_sal.end;
ecs->event_thread->control.may_range_step = 1;
- set_step_info (frame, stop_pc_sal);
+
+ {
+ struct frame_info *frame = get_current_frame ();
+ struct gdbarch *gdbarch = get_frame_arch (frame);
+ set_step_info (frame, stop_pc_sal);
+ }
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: keep going\n");
--
2.5.5