xref: /dflybsd-src/contrib/gdb-7/gdb/record-btrace.c (revision de8e141f24382815c10a4012d209bbbf7abf1112)
1*ef5ccd6cSJohn Marino /* Branch trace support for GDB, the GNU debugger.
2*ef5ccd6cSJohn Marino 
3*ef5ccd6cSJohn Marino    Copyright (C) 2013 Free Software Foundation, Inc.
4*ef5ccd6cSJohn Marino 
5*ef5ccd6cSJohn Marino    Contributed by Intel Corp. <markus.t.metzger@intel.com>
6*ef5ccd6cSJohn Marino 
7*ef5ccd6cSJohn Marino    This file is part of GDB.
8*ef5ccd6cSJohn Marino 
9*ef5ccd6cSJohn Marino    This program is free software; you can redistribute it and/or modify
10*ef5ccd6cSJohn Marino    it under the terms of the GNU General Public License as published by
11*ef5ccd6cSJohn Marino    the Free Software Foundation; either version 3 of the License, or
12*ef5ccd6cSJohn Marino    (at your option) any later version.
13*ef5ccd6cSJohn Marino 
14*ef5ccd6cSJohn Marino    This program is distributed in the hope that it will be useful,
15*ef5ccd6cSJohn Marino    but WITHOUT ANY WARRANTY; without even the implied warranty of
16*ef5ccd6cSJohn Marino    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17*ef5ccd6cSJohn Marino    GNU General Public License for more details.
18*ef5ccd6cSJohn Marino 
19*ef5ccd6cSJohn Marino    You should have received a copy of the GNU General Public License
20*ef5ccd6cSJohn Marino    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
21*ef5ccd6cSJohn Marino 
22*ef5ccd6cSJohn Marino #include "defs.h"
23*ef5ccd6cSJohn Marino #include "record.h"
24*ef5ccd6cSJohn Marino #include "gdbthread.h"
25*ef5ccd6cSJohn Marino #include "target.h"
26*ef5ccd6cSJohn Marino #include "gdbcmd.h"
27*ef5ccd6cSJohn Marino #include "disasm.h"
28*ef5ccd6cSJohn Marino #include "observer.h"
29*ef5ccd6cSJohn Marino #include "exceptions.h"
30*ef5ccd6cSJohn Marino #include "cli/cli-utils.h"
31*ef5ccd6cSJohn Marino #include "source.h"
32*ef5ccd6cSJohn Marino #include "ui-out.h"
33*ef5ccd6cSJohn Marino #include "symtab.h"
34*ef5ccd6cSJohn Marino #include "filenames.h"
35*ef5ccd6cSJohn Marino 
36*ef5ccd6cSJohn Marino /* The target_ops of record-btrace.  */
37*ef5ccd6cSJohn Marino static struct target_ops record_btrace_ops;
38*ef5ccd6cSJohn Marino 
39*ef5ccd6cSJohn Marino /* A new thread observer enabling branch tracing for the new thread.  */
40*ef5ccd6cSJohn Marino static struct observer *record_btrace_thread_observer;
41*ef5ccd6cSJohn Marino 
42*ef5ccd6cSJohn Marino /* Print a record-btrace debug message.  Use do ... while (0) to avoid
43*ef5ccd6cSJohn Marino    ambiguities when used in if statements.  */
44*ef5ccd6cSJohn Marino 
45*ef5ccd6cSJohn Marino #define DEBUG(msg, args...)						\
46*ef5ccd6cSJohn Marino   do									\
47*ef5ccd6cSJohn Marino     {									\
48*ef5ccd6cSJohn Marino       if (record_debug != 0)						\
49*ef5ccd6cSJohn Marino         fprintf_unfiltered (gdb_stdlog,					\
50*ef5ccd6cSJohn Marino 			    "[record-btrace] " msg "\n", ##args);	\
51*ef5ccd6cSJohn Marino     }									\
52*ef5ccd6cSJohn Marino   while (0)
53*ef5ccd6cSJohn Marino 
54*ef5ccd6cSJohn Marino 
55*ef5ccd6cSJohn Marino /* Update the branch trace for the current thread and return a pointer to its
56*ef5ccd6cSJohn Marino    branch trace information struct.
57*ef5ccd6cSJohn Marino 
58*ef5ccd6cSJohn Marino    Throws an error if there is no thread or no trace.  This function never
59*ef5ccd6cSJohn Marino    returns NULL.  */
60*ef5ccd6cSJohn Marino 
61*ef5ccd6cSJohn Marino static struct btrace_thread_info *
require_btrace(void)62*ef5ccd6cSJohn Marino require_btrace (void)
63*ef5ccd6cSJohn Marino {
64*ef5ccd6cSJohn Marino   struct thread_info *tp;
65*ef5ccd6cSJohn Marino   struct btrace_thread_info *btinfo;
66*ef5ccd6cSJohn Marino 
67*ef5ccd6cSJohn Marino   DEBUG ("require");
68*ef5ccd6cSJohn Marino 
69*ef5ccd6cSJohn Marino   tp = find_thread_ptid (inferior_ptid);
70*ef5ccd6cSJohn Marino   if (tp == NULL)
71*ef5ccd6cSJohn Marino     error (_("No thread."));
72*ef5ccd6cSJohn Marino 
73*ef5ccd6cSJohn Marino   btrace_fetch (tp);
74*ef5ccd6cSJohn Marino 
75*ef5ccd6cSJohn Marino   btinfo = &tp->btrace;
76*ef5ccd6cSJohn Marino 
77*ef5ccd6cSJohn Marino   if (VEC_empty (btrace_inst_s, btinfo->itrace))
78*ef5ccd6cSJohn Marino     error (_("No trace."));
79*ef5ccd6cSJohn Marino 
80*ef5ccd6cSJohn Marino   return btinfo;
81*ef5ccd6cSJohn Marino }
82*ef5ccd6cSJohn Marino 
83*ef5ccd6cSJohn Marino /* Enable branch tracing for one thread.  Warn on errors.  */
84*ef5ccd6cSJohn Marino 
85*ef5ccd6cSJohn Marino static void
record_btrace_enable_warn(struct thread_info * tp)86*ef5ccd6cSJohn Marino record_btrace_enable_warn (struct thread_info *tp)
87*ef5ccd6cSJohn Marino {
88*ef5ccd6cSJohn Marino   volatile struct gdb_exception error;
89*ef5ccd6cSJohn Marino 
90*ef5ccd6cSJohn Marino   TRY_CATCH (error, RETURN_MASK_ERROR)
91*ef5ccd6cSJohn Marino     btrace_enable (tp);
92*ef5ccd6cSJohn Marino 
93*ef5ccd6cSJohn Marino   if (error.message != NULL)
94*ef5ccd6cSJohn Marino     warning ("%s", error.message);
95*ef5ccd6cSJohn Marino }
96*ef5ccd6cSJohn Marino 
97*ef5ccd6cSJohn Marino /* Callback function to disable branch tracing for one thread.  */
98*ef5ccd6cSJohn Marino 
99*ef5ccd6cSJohn Marino static void
record_btrace_disable_callback(void * arg)100*ef5ccd6cSJohn Marino record_btrace_disable_callback (void *arg)
101*ef5ccd6cSJohn Marino {
102*ef5ccd6cSJohn Marino   struct thread_info *tp;
103*ef5ccd6cSJohn Marino 
104*ef5ccd6cSJohn Marino   tp = arg;
105*ef5ccd6cSJohn Marino 
106*ef5ccd6cSJohn Marino   btrace_disable (tp);
107*ef5ccd6cSJohn Marino }
108*ef5ccd6cSJohn Marino 
109*ef5ccd6cSJohn Marino /* Enable automatic tracing of new threads.  */
110*ef5ccd6cSJohn Marino 
111*ef5ccd6cSJohn Marino static void
record_btrace_auto_enable(void)112*ef5ccd6cSJohn Marino record_btrace_auto_enable (void)
113*ef5ccd6cSJohn Marino {
114*ef5ccd6cSJohn Marino   DEBUG ("attach thread observer");
115*ef5ccd6cSJohn Marino 
116*ef5ccd6cSJohn Marino   record_btrace_thread_observer
117*ef5ccd6cSJohn Marino     = observer_attach_new_thread (record_btrace_enable_warn);
118*ef5ccd6cSJohn Marino }
119*ef5ccd6cSJohn Marino 
120*ef5ccd6cSJohn Marino /* Disable automatic tracing of new threads.  */
121*ef5ccd6cSJohn Marino 
122*ef5ccd6cSJohn Marino static void
record_btrace_auto_disable(void)123*ef5ccd6cSJohn Marino record_btrace_auto_disable (void)
124*ef5ccd6cSJohn Marino {
125*ef5ccd6cSJohn Marino   /* The observer may have been detached, already.  */
126*ef5ccd6cSJohn Marino   if (record_btrace_thread_observer == NULL)
127*ef5ccd6cSJohn Marino     return;
128*ef5ccd6cSJohn Marino 
129*ef5ccd6cSJohn Marino   DEBUG ("detach thread observer");
130*ef5ccd6cSJohn Marino 
131*ef5ccd6cSJohn Marino   observer_detach_new_thread (record_btrace_thread_observer);
132*ef5ccd6cSJohn Marino   record_btrace_thread_observer = NULL;
133*ef5ccd6cSJohn Marino }
134*ef5ccd6cSJohn Marino 
135*ef5ccd6cSJohn Marino /* The to_open method of target record-btrace.  */
136*ef5ccd6cSJohn Marino 
137*ef5ccd6cSJohn Marino static void
record_btrace_open(char * args,int from_tty)138*ef5ccd6cSJohn Marino record_btrace_open (char *args, int from_tty)
139*ef5ccd6cSJohn Marino {
140*ef5ccd6cSJohn Marino   struct cleanup *disable_chain;
141*ef5ccd6cSJohn Marino   struct thread_info *tp;
142*ef5ccd6cSJohn Marino 
143*ef5ccd6cSJohn Marino   DEBUG ("open");
144*ef5ccd6cSJohn Marino 
145*ef5ccd6cSJohn Marino   if (RECORD_IS_USED)
146*ef5ccd6cSJohn Marino     error (_("The process is already being recorded."));
147*ef5ccd6cSJohn Marino 
148*ef5ccd6cSJohn Marino   if (!target_has_execution)
149*ef5ccd6cSJohn Marino     error (_("The program is not being run."));
150*ef5ccd6cSJohn Marino 
151*ef5ccd6cSJohn Marino   if (!target_supports_btrace ())
152*ef5ccd6cSJohn Marino     error (_("Target does not support branch tracing."));
153*ef5ccd6cSJohn Marino 
154*ef5ccd6cSJohn Marino   gdb_assert (record_btrace_thread_observer == NULL);
155*ef5ccd6cSJohn Marino 
156*ef5ccd6cSJohn Marino   disable_chain = make_cleanup (null_cleanup, NULL);
157*ef5ccd6cSJohn Marino   ALL_THREADS (tp)
158*ef5ccd6cSJohn Marino     if (args == NULL || *args == 0 || number_is_in_list (args, tp->num))
159*ef5ccd6cSJohn Marino       {
160*ef5ccd6cSJohn Marino 	btrace_enable (tp);
161*ef5ccd6cSJohn Marino 
162*ef5ccd6cSJohn Marino 	make_cleanup (record_btrace_disable_callback, tp);
163*ef5ccd6cSJohn Marino       }
164*ef5ccd6cSJohn Marino 
165*ef5ccd6cSJohn Marino   record_btrace_auto_enable ();
166*ef5ccd6cSJohn Marino 
167*ef5ccd6cSJohn Marino   push_target (&record_btrace_ops);
168*ef5ccd6cSJohn Marino 
169*ef5ccd6cSJohn Marino   observer_notify_record_changed (current_inferior (),  1);
170*ef5ccd6cSJohn Marino 
171*ef5ccd6cSJohn Marino   discard_cleanups (disable_chain);
172*ef5ccd6cSJohn Marino }
173*ef5ccd6cSJohn Marino 
174*ef5ccd6cSJohn Marino /* The to_stop_recording method of target record-btrace.  */
175*ef5ccd6cSJohn Marino 
176*ef5ccd6cSJohn Marino static void
record_btrace_stop_recording(void)177*ef5ccd6cSJohn Marino record_btrace_stop_recording (void)
178*ef5ccd6cSJohn Marino {
179*ef5ccd6cSJohn Marino   struct thread_info *tp;
180*ef5ccd6cSJohn Marino 
181*ef5ccd6cSJohn Marino   DEBUG ("stop recording");
182*ef5ccd6cSJohn Marino 
183*ef5ccd6cSJohn Marino   record_btrace_auto_disable ();
184*ef5ccd6cSJohn Marino 
185*ef5ccd6cSJohn Marino   ALL_THREADS (tp)
186*ef5ccd6cSJohn Marino     if (tp->btrace.target != NULL)
187*ef5ccd6cSJohn Marino       btrace_disable (tp);
188*ef5ccd6cSJohn Marino }
189*ef5ccd6cSJohn Marino 
190*ef5ccd6cSJohn Marino /* The to_close method of target record-btrace.  */
191*ef5ccd6cSJohn Marino 
192*ef5ccd6cSJohn Marino static void
record_btrace_close(int quitting)193*ef5ccd6cSJohn Marino record_btrace_close (int quitting)
194*ef5ccd6cSJohn Marino {
195*ef5ccd6cSJohn Marino   /* Make sure automatic recording gets disabled even if we did not stop
196*ef5ccd6cSJohn Marino      recording before closing the record-btrace target.  */
197*ef5ccd6cSJohn Marino   record_btrace_auto_disable ();
198*ef5ccd6cSJohn Marino 
199*ef5ccd6cSJohn Marino   /* We already stopped recording.  */
200*ef5ccd6cSJohn Marino }
201*ef5ccd6cSJohn Marino 
202*ef5ccd6cSJohn Marino /* The to_info_record method of target record-btrace.  */
203*ef5ccd6cSJohn Marino 
204*ef5ccd6cSJohn Marino static void
record_btrace_info(void)205*ef5ccd6cSJohn Marino record_btrace_info (void)
206*ef5ccd6cSJohn Marino {
207*ef5ccd6cSJohn Marino   struct btrace_thread_info *btinfo;
208*ef5ccd6cSJohn Marino   struct thread_info *tp;
209*ef5ccd6cSJohn Marino   unsigned int insts, funcs;
210*ef5ccd6cSJohn Marino 
211*ef5ccd6cSJohn Marino   DEBUG ("info");
212*ef5ccd6cSJohn Marino 
213*ef5ccd6cSJohn Marino   tp = find_thread_ptid (inferior_ptid);
214*ef5ccd6cSJohn Marino   if (tp == NULL)
215*ef5ccd6cSJohn Marino     error (_("No thread."));
216*ef5ccd6cSJohn Marino 
217*ef5ccd6cSJohn Marino   btrace_fetch (tp);
218*ef5ccd6cSJohn Marino 
219*ef5ccd6cSJohn Marino   btinfo = &tp->btrace;
220*ef5ccd6cSJohn Marino   insts = VEC_length (btrace_inst_s, btinfo->itrace);
221*ef5ccd6cSJohn Marino   funcs = VEC_length (btrace_func_s, btinfo->ftrace);
222*ef5ccd6cSJohn Marino 
223*ef5ccd6cSJohn Marino   printf_unfiltered (_("Recorded %u instructions in %u functions for thread "
224*ef5ccd6cSJohn Marino 		       "%d (%s).\n"), insts, funcs, tp->num,
225*ef5ccd6cSJohn Marino 		     target_pid_to_str (tp->ptid));
226*ef5ccd6cSJohn Marino }
227*ef5ccd6cSJohn Marino 
228*ef5ccd6cSJohn Marino /* Print an unsigned int.  */
229*ef5ccd6cSJohn Marino 
230*ef5ccd6cSJohn Marino static void
ui_out_field_uint(struct ui_out * uiout,const char * fld,unsigned int val)231*ef5ccd6cSJohn Marino ui_out_field_uint (struct ui_out *uiout, const char *fld, unsigned int val)
232*ef5ccd6cSJohn Marino {
233*ef5ccd6cSJohn Marino   ui_out_field_fmt (uiout, fld, "%u", val);
234*ef5ccd6cSJohn Marino }
235*ef5ccd6cSJohn Marino 
236*ef5ccd6cSJohn Marino /* Disassemble a section of the recorded instruction trace.  */
237*ef5ccd6cSJohn Marino 
238*ef5ccd6cSJohn Marino static void
btrace_insn_history(struct btrace_thread_info * btinfo,struct ui_out * uiout,unsigned int begin,unsigned int end,int flags)239*ef5ccd6cSJohn Marino btrace_insn_history (struct btrace_thread_info *btinfo, struct ui_out *uiout,
240*ef5ccd6cSJohn Marino 		     unsigned int begin, unsigned int end, int flags)
241*ef5ccd6cSJohn Marino {
242*ef5ccd6cSJohn Marino   struct gdbarch *gdbarch;
243*ef5ccd6cSJohn Marino   struct btrace_inst *inst;
244*ef5ccd6cSJohn Marino   unsigned int idx;
245*ef5ccd6cSJohn Marino 
246*ef5ccd6cSJohn Marino   DEBUG ("itrace (0x%x): [%u; %u[", flags, begin, end);
247*ef5ccd6cSJohn Marino 
248*ef5ccd6cSJohn Marino   gdbarch = target_gdbarch ();
249*ef5ccd6cSJohn Marino 
250*ef5ccd6cSJohn Marino   for (idx = begin; VEC_iterate (btrace_inst_s, btinfo->itrace, idx, inst)
251*ef5ccd6cSJohn Marino 	 && idx < end; ++idx)
252*ef5ccd6cSJohn Marino     {
253*ef5ccd6cSJohn Marino       /* Print the instruction index.  */
254*ef5ccd6cSJohn Marino       ui_out_field_uint (uiout, "index", idx);
255*ef5ccd6cSJohn Marino       ui_out_text (uiout, "\t");
256*ef5ccd6cSJohn Marino 
257*ef5ccd6cSJohn Marino       /* Disassembly with '/m' flag may not produce the expected result.
258*ef5ccd6cSJohn Marino 	 See PR gdb/11833.  */
259*ef5ccd6cSJohn Marino       gdb_disassembly (gdbarch, uiout, NULL, flags, 1, inst->pc, inst->pc + 1);
260*ef5ccd6cSJohn Marino     }
261*ef5ccd6cSJohn Marino }
262*ef5ccd6cSJohn Marino 
263*ef5ccd6cSJohn Marino /* The to_insn_history method of target record-btrace.  */
264*ef5ccd6cSJohn Marino 
265*ef5ccd6cSJohn Marino static void
record_btrace_insn_history(int size,int flags)266*ef5ccd6cSJohn Marino record_btrace_insn_history (int size, int flags)
267*ef5ccd6cSJohn Marino {
268*ef5ccd6cSJohn Marino   struct btrace_thread_info *btinfo;
269*ef5ccd6cSJohn Marino   struct cleanup *uiout_cleanup;
270*ef5ccd6cSJohn Marino   struct ui_out *uiout;
271*ef5ccd6cSJohn Marino   unsigned int context, last, begin, end;
272*ef5ccd6cSJohn Marino 
273*ef5ccd6cSJohn Marino   uiout = current_uiout;
274*ef5ccd6cSJohn Marino   uiout_cleanup = make_cleanup_ui_out_tuple_begin_end (uiout,
275*ef5ccd6cSJohn Marino 						       "insn history");
276*ef5ccd6cSJohn Marino   btinfo = require_btrace ();
277*ef5ccd6cSJohn Marino   last = VEC_length (btrace_inst_s, btinfo->itrace);
278*ef5ccd6cSJohn Marino 
279*ef5ccd6cSJohn Marino   context = abs (size);
280*ef5ccd6cSJohn Marino   begin = btinfo->insn_iterator.begin;
281*ef5ccd6cSJohn Marino   end = btinfo->insn_iterator.end;
282*ef5ccd6cSJohn Marino 
283*ef5ccd6cSJohn Marino   DEBUG ("insn-history (0x%x): %d, prev: [%u; %u[", flags, size, begin, end);
284*ef5ccd6cSJohn Marino 
285*ef5ccd6cSJohn Marino   if (context == 0)
286*ef5ccd6cSJohn Marino     error (_("Bad record instruction-history-size."));
287*ef5ccd6cSJohn Marino 
288*ef5ccd6cSJohn Marino   /* We start at the end.  */
289*ef5ccd6cSJohn Marino   if (end < begin)
290*ef5ccd6cSJohn Marino     {
291*ef5ccd6cSJohn Marino       /* Truncate the context, if necessary.  */
292*ef5ccd6cSJohn Marino       context = min (context, last);
293*ef5ccd6cSJohn Marino 
294*ef5ccd6cSJohn Marino       end = last;
295*ef5ccd6cSJohn Marino       begin = end - context;
296*ef5ccd6cSJohn Marino     }
297*ef5ccd6cSJohn Marino   else if (size < 0)
298*ef5ccd6cSJohn Marino     {
299*ef5ccd6cSJohn Marino       if (begin == 0)
300*ef5ccd6cSJohn Marino 	{
301*ef5ccd6cSJohn Marino 	  printf_unfiltered (_("At the start of the branch trace record.\n"));
302*ef5ccd6cSJohn Marino 
303*ef5ccd6cSJohn Marino 	  btinfo->insn_iterator.end = 0;
304*ef5ccd6cSJohn Marino 	  return;
305*ef5ccd6cSJohn Marino 	}
306*ef5ccd6cSJohn Marino 
307*ef5ccd6cSJohn Marino       /* Truncate the context, if necessary.  */
308*ef5ccd6cSJohn Marino       context = min (context, begin);
309*ef5ccd6cSJohn Marino 
310*ef5ccd6cSJohn Marino       end = begin;
311*ef5ccd6cSJohn Marino       begin -= context;
312*ef5ccd6cSJohn Marino     }
313*ef5ccd6cSJohn Marino   else
314*ef5ccd6cSJohn Marino     {
315*ef5ccd6cSJohn Marino       if (end == last)
316*ef5ccd6cSJohn Marino 	{
317*ef5ccd6cSJohn Marino 	  printf_unfiltered (_("At the end of the branch trace record.\n"));
318*ef5ccd6cSJohn Marino 
319*ef5ccd6cSJohn Marino 	  btinfo->insn_iterator.begin = last;
320*ef5ccd6cSJohn Marino 	  return;
321*ef5ccd6cSJohn Marino 	}
322*ef5ccd6cSJohn Marino 
323*ef5ccd6cSJohn Marino       /* Truncate the context, if necessary.  */
324*ef5ccd6cSJohn Marino       context = min (context, last - end);
325*ef5ccd6cSJohn Marino 
326*ef5ccd6cSJohn Marino       begin = end;
327*ef5ccd6cSJohn Marino       end += context;
328*ef5ccd6cSJohn Marino     }
329*ef5ccd6cSJohn Marino 
330*ef5ccd6cSJohn Marino   btrace_insn_history (btinfo, uiout, begin, end, flags);
331*ef5ccd6cSJohn Marino 
332*ef5ccd6cSJohn Marino   btinfo->insn_iterator.begin = begin;
333*ef5ccd6cSJohn Marino   btinfo->insn_iterator.end = end;
334*ef5ccd6cSJohn Marino 
335*ef5ccd6cSJohn Marino   do_cleanups (uiout_cleanup);
336*ef5ccd6cSJohn Marino }
337*ef5ccd6cSJohn Marino 
338*ef5ccd6cSJohn Marino /* The to_insn_history_range method of target record-btrace.  */
339*ef5ccd6cSJohn Marino 
340*ef5ccd6cSJohn Marino static void
record_btrace_insn_history_range(ULONGEST from,ULONGEST to,int flags)341*ef5ccd6cSJohn Marino record_btrace_insn_history_range (ULONGEST from, ULONGEST to, int flags)
342*ef5ccd6cSJohn Marino {
343*ef5ccd6cSJohn Marino   struct btrace_thread_info *btinfo;
344*ef5ccd6cSJohn Marino   struct cleanup *uiout_cleanup;
345*ef5ccd6cSJohn Marino   struct ui_out *uiout;
346*ef5ccd6cSJohn Marino   unsigned int last, begin, end;
347*ef5ccd6cSJohn Marino 
348*ef5ccd6cSJohn Marino   uiout = current_uiout;
349*ef5ccd6cSJohn Marino   uiout_cleanup = make_cleanup_ui_out_tuple_begin_end (uiout,
350*ef5ccd6cSJohn Marino 						       "insn history");
351*ef5ccd6cSJohn Marino   btinfo = require_btrace ();
352*ef5ccd6cSJohn Marino   last = VEC_length (btrace_inst_s, btinfo->itrace);
353*ef5ccd6cSJohn Marino 
354*ef5ccd6cSJohn Marino   begin = (unsigned int) from;
355*ef5ccd6cSJohn Marino   end = (unsigned int) to;
356*ef5ccd6cSJohn Marino 
357*ef5ccd6cSJohn Marino   DEBUG ("insn-history (0x%x): [%u; %u[", flags, begin, end);
358*ef5ccd6cSJohn Marino 
359*ef5ccd6cSJohn Marino   /* Check for wrap-arounds.  */
360*ef5ccd6cSJohn Marino   if (begin != from || end != to)
361*ef5ccd6cSJohn Marino     error (_("Bad range."));
362*ef5ccd6cSJohn Marino 
363*ef5ccd6cSJohn Marino   if (end <= begin)
364*ef5ccd6cSJohn Marino     error (_("Bad range."));
365*ef5ccd6cSJohn Marino 
366*ef5ccd6cSJohn Marino   if (last <= begin)
367*ef5ccd6cSJohn Marino     error (_("Range out of bounds."));
368*ef5ccd6cSJohn Marino 
369*ef5ccd6cSJohn Marino   /* Truncate the range, if necessary.  */
370*ef5ccd6cSJohn Marino   if (last < end)
371*ef5ccd6cSJohn Marino     end = last;
372*ef5ccd6cSJohn Marino 
373*ef5ccd6cSJohn Marino   btrace_insn_history (btinfo, uiout, begin, end, flags);
374*ef5ccd6cSJohn Marino 
375*ef5ccd6cSJohn Marino   btinfo->insn_iterator.begin = begin;
376*ef5ccd6cSJohn Marino   btinfo->insn_iterator.end = end;
377*ef5ccd6cSJohn Marino 
378*ef5ccd6cSJohn Marino   do_cleanups (uiout_cleanup);
379*ef5ccd6cSJohn Marino }
380*ef5ccd6cSJohn Marino 
381*ef5ccd6cSJohn Marino /* The to_insn_history_from method of target record-btrace.  */
382*ef5ccd6cSJohn Marino 
383*ef5ccd6cSJohn Marino static void
record_btrace_insn_history_from(ULONGEST from,int size,int flags)384*ef5ccd6cSJohn Marino record_btrace_insn_history_from (ULONGEST from, int size, int flags)
385*ef5ccd6cSJohn Marino {
386*ef5ccd6cSJohn Marino   ULONGEST begin, end, context;
387*ef5ccd6cSJohn Marino 
388*ef5ccd6cSJohn Marino   context = abs (size);
389*ef5ccd6cSJohn Marino 
390*ef5ccd6cSJohn Marino   if (size < 0)
391*ef5ccd6cSJohn Marino     {
392*ef5ccd6cSJohn Marino       end = from;
393*ef5ccd6cSJohn Marino 
394*ef5ccd6cSJohn Marino       if (from < context)
395*ef5ccd6cSJohn Marino 	begin = 0;
396*ef5ccd6cSJohn Marino       else
397*ef5ccd6cSJohn Marino 	begin = from - context;
398*ef5ccd6cSJohn Marino     }
399*ef5ccd6cSJohn Marino   else
400*ef5ccd6cSJohn Marino     {
401*ef5ccd6cSJohn Marino       begin = from;
402*ef5ccd6cSJohn Marino       end = from + context;
403*ef5ccd6cSJohn Marino 
404*ef5ccd6cSJohn Marino       /* Check for wrap-around.  */
405*ef5ccd6cSJohn Marino       if (end < begin)
406*ef5ccd6cSJohn Marino 	end = ULONGEST_MAX;
407*ef5ccd6cSJohn Marino     }
408*ef5ccd6cSJohn Marino 
409*ef5ccd6cSJohn Marino   record_btrace_insn_history_range (begin, end, flags);
410*ef5ccd6cSJohn Marino }
411*ef5ccd6cSJohn Marino 
412*ef5ccd6cSJohn Marino /* Print the instruction number range for a function call history line.  */
413*ef5ccd6cSJohn Marino 
414*ef5ccd6cSJohn Marino static void
btrace_func_history_insn_range(struct ui_out * uiout,struct btrace_func * bfun)415*ef5ccd6cSJohn Marino btrace_func_history_insn_range (struct ui_out *uiout, struct btrace_func *bfun)
416*ef5ccd6cSJohn Marino {
417*ef5ccd6cSJohn Marino   ui_out_field_uint (uiout, "insn begin", bfun->ibegin);
418*ef5ccd6cSJohn Marino 
419*ef5ccd6cSJohn Marino   if (bfun->ibegin == bfun->iend)
420*ef5ccd6cSJohn Marino     return;
421*ef5ccd6cSJohn Marino 
422*ef5ccd6cSJohn Marino   ui_out_text (uiout, "-");
423*ef5ccd6cSJohn Marino   ui_out_field_uint (uiout, "insn end", bfun->iend);
424*ef5ccd6cSJohn Marino }
425*ef5ccd6cSJohn Marino 
426*ef5ccd6cSJohn Marino /* Print the source line information for a function call history line.  */
427*ef5ccd6cSJohn Marino 
428*ef5ccd6cSJohn Marino static void
btrace_func_history_src_line(struct ui_out * uiout,struct btrace_func * bfun)429*ef5ccd6cSJohn Marino btrace_func_history_src_line (struct ui_out *uiout, struct btrace_func *bfun)
430*ef5ccd6cSJohn Marino {
431*ef5ccd6cSJohn Marino   struct symbol *sym;
432*ef5ccd6cSJohn Marino 
433*ef5ccd6cSJohn Marino   sym = bfun->sym;
434*ef5ccd6cSJohn Marino   if (sym == NULL)
435*ef5ccd6cSJohn Marino     return;
436*ef5ccd6cSJohn Marino 
437*ef5ccd6cSJohn Marino   ui_out_field_string (uiout, "file",
438*ef5ccd6cSJohn Marino 		       symtab_to_filename_for_display (sym->symtab));
439*ef5ccd6cSJohn Marino 
440*ef5ccd6cSJohn Marino   if (bfun->lend == 0)
441*ef5ccd6cSJohn Marino     return;
442*ef5ccd6cSJohn Marino 
443*ef5ccd6cSJohn Marino   ui_out_text (uiout, ":");
444*ef5ccd6cSJohn Marino   ui_out_field_int (uiout, "min line", bfun->lbegin);
445*ef5ccd6cSJohn Marino 
446*ef5ccd6cSJohn Marino   if (bfun->lend == bfun->lbegin)
447*ef5ccd6cSJohn Marino     return;
448*ef5ccd6cSJohn Marino 
449*ef5ccd6cSJohn Marino   ui_out_text (uiout, "-");
450*ef5ccd6cSJohn Marino   ui_out_field_int (uiout, "max line", bfun->lend);
451*ef5ccd6cSJohn Marino }
452*ef5ccd6cSJohn Marino 
453*ef5ccd6cSJohn Marino /* Disassemble a section of the recorded function trace.  */
454*ef5ccd6cSJohn Marino 
455*ef5ccd6cSJohn Marino static void
btrace_func_history(struct btrace_thread_info * btinfo,struct ui_out * uiout,unsigned int begin,unsigned int end,enum record_print_flag flags)456*ef5ccd6cSJohn Marino btrace_func_history (struct btrace_thread_info *btinfo, struct ui_out *uiout,
457*ef5ccd6cSJohn Marino 		     unsigned int begin, unsigned int end,
458*ef5ccd6cSJohn Marino 		     enum record_print_flag flags)
459*ef5ccd6cSJohn Marino {
460*ef5ccd6cSJohn Marino   struct btrace_func *bfun;
461*ef5ccd6cSJohn Marino   unsigned int idx;
462*ef5ccd6cSJohn Marino 
463*ef5ccd6cSJohn Marino   DEBUG ("ftrace (0x%x): [%u; %u[", flags, begin, end);
464*ef5ccd6cSJohn Marino 
465*ef5ccd6cSJohn Marino   for (idx = begin; VEC_iterate (btrace_func_s, btinfo->ftrace, idx, bfun)
466*ef5ccd6cSJohn Marino 	 && idx < end; ++idx)
467*ef5ccd6cSJohn Marino     {
468*ef5ccd6cSJohn Marino       /* Print the function index.  */
469*ef5ccd6cSJohn Marino       ui_out_field_uint (uiout, "index", idx);
470*ef5ccd6cSJohn Marino       ui_out_text (uiout, "\t");
471*ef5ccd6cSJohn Marino 
472*ef5ccd6cSJohn Marino       if ((flags & record_print_insn_range) != 0)
473*ef5ccd6cSJohn Marino 	{
474*ef5ccd6cSJohn Marino 	  btrace_func_history_insn_range (uiout, bfun);
475*ef5ccd6cSJohn Marino 	  ui_out_text (uiout, "\t");
476*ef5ccd6cSJohn Marino 	}
477*ef5ccd6cSJohn Marino 
478*ef5ccd6cSJohn Marino       if ((flags & record_print_src_line) != 0)
479*ef5ccd6cSJohn Marino 	{
480*ef5ccd6cSJohn Marino 	  btrace_func_history_src_line (uiout, bfun);
481*ef5ccd6cSJohn Marino 	  ui_out_text (uiout, "\t");
482*ef5ccd6cSJohn Marino 	}
483*ef5ccd6cSJohn Marino 
484*ef5ccd6cSJohn Marino       if (bfun->sym != NULL)
485*ef5ccd6cSJohn Marino 	ui_out_field_string (uiout, "function", SYMBOL_PRINT_NAME (bfun->sym));
486*ef5ccd6cSJohn Marino       else if (bfun->msym != NULL)
487*ef5ccd6cSJohn Marino 	ui_out_field_string (uiout, "function", SYMBOL_PRINT_NAME (bfun->msym));
488*ef5ccd6cSJohn Marino       ui_out_text (uiout, "\n");
489*ef5ccd6cSJohn Marino     }
490*ef5ccd6cSJohn Marino }
491*ef5ccd6cSJohn Marino 
492*ef5ccd6cSJohn Marino /* The to_call_history method of target record-btrace.  */
493*ef5ccd6cSJohn Marino 
494*ef5ccd6cSJohn Marino static void
record_btrace_call_history(int size,int flags)495*ef5ccd6cSJohn Marino record_btrace_call_history (int size, int flags)
496*ef5ccd6cSJohn Marino {
497*ef5ccd6cSJohn Marino   struct btrace_thread_info *btinfo;
498*ef5ccd6cSJohn Marino   struct cleanup *uiout_cleanup;
499*ef5ccd6cSJohn Marino   struct ui_out *uiout;
500*ef5ccd6cSJohn Marino   unsigned int context, last, begin, end;
501*ef5ccd6cSJohn Marino 
502*ef5ccd6cSJohn Marino   uiout = current_uiout;
503*ef5ccd6cSJohn Marino   uiout_cleanup = make_cleanup_ui_out_tuple_begin_end (uiout,
504*ef5ccd6cSJohn Marino 						       "insn history");
505*ef5ccd6cSJohn Marino   btinfo = require_btrace ();
506*ef5ccd6cSJohn Marino   last = VEC_length (btrace_func_s, btinfo->ftrace);
507*ef5ccd6cSJohn Marino 
508*ef5ccd6cSJohn Marino   context = abs (size);
509*ef5ccd6cSJohn Marino   begin = btinfo->func_iterator.begin;
510*ef5ccd6cSJohn Marino   end = btinfo->func_iterator.end;
511*ef5ccd6cSJohn Marino 
512*ef5ccd6cSJohn Marino   DEBUG ("func-history (0x%x): %d, prev: [%u; %u[", flags, size, begin, end);
513*ef5ccd6cSJohn Marino 
514*ef5ccd6cSJohn Marino   if (context == 0)
515*ef5ccd6cSJohn Marino     error (_("Bad record function-call-history-size."));
516*ef5ccd6cSJohn Marino 
517*ef5ccd6cSJohn Marino   /* We start at the end.  */
518*ef5ccd6cSJohn Marino   if (end < begin)
519*ef5ccd6cSJohn Marino     {
520*ef5ccd6cSJohn Marino       /* Truncate the context, if necessary.  */
521*ef5ccd6cSJohn Marino       context = min (context, last);
522*ef5ccd6cSJohn Marino 
523*ef5ccd6cSJohn Marino       end = last;
524*ef5ccd6cSJohn Marino       begin = end - context;
525*ef5ccd6cSJohn Marino     }
526*ef5ccd6cSJohn Marino   else if (size < 0)
527*ef5ccd6cSJohn Marino     {
528*ef5ccd6cSJohn Marino       if (begin == 0)
529*ef5ccd6cSJohn Marino 	{
530*ef5ccd6cSJohn Marino 	  printf_unfiltered (_("At the start of the branch trace record.\n"));
531*ef5ccd6cSJohn Marino 
532*ef5ccd6cSJohn Marino 	  btinfo->func_iterator.end = 0;
533*ef5ccd6cSJohn Marino 	  return;
534*ef5ccd6cSJohn Marino 	}
535*ef5ccd6cSJohn Marino 
536*ef5ccd6cSJohn Marino       /* Truncate the context, if necessary.  */
537*ef5ccd6cSJohn Marino       context = min (context, begin);
538*ef5ccd6cSJohn Marino 
539*ef5ccd6cSJohn Marino       end = begin;
540*ef5ccd6cSJohn Marino       begin -= context;
541*ef5ccd6cSJohn Marino     }
542*ef5ccd6cSJohn Marino   else
543*ef5ccd6cSJohn Marino     {
544*ef5ccd6cSJohn Marino       if (end == last)
545*ef5ccd6cSJohn Marino 	{
546*ef5ccd6cSJohn Marino 	  printf_unfiltered (_("At the end of the branch trace record.\n"));
547*ef5ccd6cSJohn Marino 
548*ef5ccd6cSJohn Marino 	  btinfo->func_iterator.begin = last;
549*ef5ccd6cSJohn Marino 	  return;
550*ef5ccd6cSJohn Marino 	}
551*ef5ccd6cSJohn Marino 
552*ef5ccd6cSJohn Marino       /* Truncate the context, if necessary.  */
553*ef5ccd6cSJohn Marino       context = min (context, last - end);
554*ef5ccd6cSJohn Marino 
555*ef5ccd6cSJohn Marino       begin = end;
556*ef5ccd6cSJohn Marino       end += context;
557*ef5ccd6cSJohn Marino     }
558*ef5ccd6cSJohn Marino 
559*ef5ccd6cSJohn Marino   btrace_func_history (btinfo, uiout, begin, end, flags);
560*ef5ccd6cSJohn Marino 
561*ef5ccd6cSJohn Marino   btinfo->func_iterator.begin = begin;
562*ef5ccd6cSJohn Marino   btinfo->func_iterator.end = end;
563*ef5ccd6cSJohn Marino 
564*ef5ccd6cSJohn Marino   do_cleanups (uiout_cleanup);
565*ef5ccd6cSJohn Marino }
566*ef5ccd6cSJohn Marino 
567*ef5ccd6cSJohn Marino /* The to_call_history_range method of target record-btrace.  */
568*ef5ccd6cSJohn Marino 
569*ef5ccd6cSJohn Marino static void
record_btrace_call_history_range(ULONGEST from,ULONGEST to,int flags)570*ef5ccd6cSJohn Marino record_btrace_call_history_range (ULONGEST from, ULONGEST to, int flags)
571*ef5ccd6cSJohn Marino {
572*ef5ccd6cSJohn Marino   struct btrace_thread_info *btinfo;
573*ef5ccd6cSJohn Marino   struct cleanup *uiout_cleanup;
574*ef5ccd6cSJohn Marino   struct ui_out *uiout;
575*ef5ccd6cSJohn Marino   unsigned int last, begin, end;
576*ef5ccd6cSJohn Marino 
577*ef5ccd6cSJohn Marino   uiout = current_uiout;
578*ef5ccd6cSJohn Marino   uiout_cleanup = make_cleanup_ui_out_tuple_begin_end (uiout,
579*ef5ccd6cSJohn Marino 						       "func history");
580*ef5ccd6cSJohn Marino   btinfo = require_btrace ();
581*ef5ccd6cSJohn Marino   last = VEC_length (btrace_func_s, btinfo->ftrace);
582*ef5ccd6cSJohn Marino 
583*ef5ccd6cSJohn Marino   begin = (unsigned int) from;
584*ef5ccd6cSJohn Marino   end = (unsigned int) to;
585*ef5ccd6cSJohn Marino 
586*ef5ccd6cSJohn Marino   DEBUG ("func-history (0x%x): [%u; %u[", flags, begin, end);
587*ef5ccd6cSJohn Marino 
588*ef5ccd6cSJohn Marino   /* Check for wrap-arounds.  */
589*ef5ccd6cSJohn Marino   if (begin != from || end != to)
590*ef5ccd6cSJohn Marino     error (_("Bad range."));
591*ef5ccd6cSJohn Marino 
592*ef5ccd6cSJohn Marino   if (end <= begin)
593*ef5ccd6cSJohn Marino     error (_("Bad range."));
594*ef5ccd6cSJohn Marino 
595*ef5ccd6cSJohn Marino   if (last <= begin)
596*ef5ccd6cSJohn Marino     error (_("Range out of bounds."));
597*ef5ccd6cSJohn Marino 
598*ef5ccd6cSJohn Marino   /* Truncate the range, if necessary.  */
599*ef5ccd6cSJohn Marino   if (last < end)
600*ef5ccd6cSJohn Marino     end = last;
601*ef5ccd6cSJohn Marino 
602*ef5ccd6cSJohn Marino   btrace_func_history (btinfo, uiout, begin, end, flags);
603*ef5ccd6cSJohn Marino 
604*ef5ccd6cSJohn Marino   btinfo->func_iterator.begin = begin;
605*ef5ccd6cSJohn Marino   btinfo->func_iterator.end = end;
606*ef5ccd6cSJohn Marino 
607*ef5ccd6cSJohn Marino   do_cleanups (uiout_cleanup);
608*ef5ccd6cSJohn Marino }
609*ef5ccd6cSJohn Marino 
610*ef5ccd6cSJohn Marino /* The to_call_history_from method of target record-btrace.  */
611*ef5ccd6cSJohn Marino 
612*ef5ccd6cSJohn Marino static void
record_btrace_call_history_from(ULONGEST from,int size,int flags)613*ef5ccd6cSJohn Marino record_btrace_call_history_from (ULONGEST from, int size, int flags)
614*ef5ccd6cSJohn Marino {
615*ef5ccd6cSJohn Marino   ULONGEST begin, end, context;
616*ef5ccd6cSJohn Marino 
617*ef5ccd6cSJohn Marino   context = abs (size);
618*ef5ccd6cSJohn Marino 
619*ef5ccd6cSJohn Marino   if (size < 0)
620*ef5ccd6cSJohn Marino     {
621*ef5ccd6cSJohn Marino       end = from;
622*ef5ccd6cSJohn Marino 
623*ef5ccd6cSJohn Marino       if (from < context)
624*ef5ccd6cSJohn Marino 	begin = 0;
625*ef5ccd6cSJohn Marino       else
626*ef5ccd6cSJohn Marino 	begin = from - context;
627*ef5ccd6cSJohn Marino     }
628*ef5ccd6cSJohn Marino   else
629*ef5ccd6cSJohn Marino     {
630*ef5ccd6cSJohn Marino       begin = from;
631*ef5ccd6cSJohn Marino       end = from + context;
632*ef5ccd6cSJohn Marino 
633*ef5ccd6cSJohn Marino       /* Check for wrap-around.  */
634*ef5ccd6cSJohn Marino       if (end < begin)
635*ef5ccd6cSJohn Marino 	end = ULONGEST_MAX;
636*ef5ccd6cSJohn Marino     }
637*ef5ccd6cSJohn Marino 
638*ef5ccd6cSJohn Marino   record_btrace_call_history_range (begin, end, flags);
639*ef5ccd6cSJohn Marino }
640*ef5ccd6cSJohn Marino 
641*ef5ccd6cSJohn Marino /* Initialize the record-btrace target ops.  */
642*ef5ccd6cSJohn Marino 
643*ef5ccd6cSJohn Marino static void
init_record_btrace_ops(void)644*ef5ccd6cSJohn Marino init_record_btrace_ops (void)
645*ef5ccd6cSJohn Marino {
646*ef5ccd6cSJohn Marino   struct target_ops *ops;
647*ef5ccd6cSJohn Marino 
648*ef5ccd6cSJohn Marino   ops = &record_btrace_ops;
649*ef5ccd6cSJohn Marino   ops->to_shortname = "record-btrace";
650*ef5ccd6cSJohn Marino   ops->to_longname = "Branch tracing target";
651*ef5ccd6cSJohn Marino   ops->to_doc = "Collect control-flow trace and provide the execution history.";
652*ef5ccd6cSJohn Marino   ops->to_open = record_btrace_open;
653*ef5ccd6cSJohn Marino   ops->to_close = record_btrace_close;
654*ef5ccd6cSJohn Marino   ops->to_detach = record_detach;
655*ef5ccd6cSJohn Marino   ops->to_disconnect = record_disconnect;
656*ef5ccd6cSJohn Marino   ops->to_mourn_inferior = record_mourn_inferior;
657*ef5ccd6cSJohn Marino   ops->to_kill = record_kill;
658*ef5ccd6cSJohn Marino   ops->to_create_inferior = find_default_create_inferior;
659*ef5ccd6cSJohn Marino   ops->to_stop_recording = record_btrace_stop_recording;
660*ef5ccd6cSJohn Marino   ops->to_info_record = record_btrace_info;
661*ef5ccd6cSJohn Marino   ops->to_insn_history = record_btrace_insn_history;
662*ef5ccd6cSJohn Marino   ops->to_insn_history_from = record_btrace_insn_history_from;
663*ef5ccd6cSJohn Marino   ops->to_insn_history_range = record_btrace_insn_history_range;
664*ef5ccd6cSJohn Marino   ops->to_call_history = record_btrace_call_history;
665*ef5ccd6cSJohn Marino   ops->to_call_history_from = record_btrace_call_history_from;
666*ef5ccd6cSJohn Marino   ops->to_call_history_range = record_btrace_call_history_range;
667*ef5ccd6cSJohn Marino   ops->to_stratum = record_stratum;
668*ef5ccd6cSJohn Marino   ops->to_magic = OPS_MAGIC;
669*ef5ccd6cSJohn Marino }
670*ef5ccd6cSJohn Marino 
671*ef5ccd6cSJohn Marino /* Alias for "target record".  */
672*ef5ccd6cSJohn Marino 
673*ef5ccd6cSJohn Marino static void
cmd_record_btrace_start(char * args,int from_tty)674*ef5ccd6cSJohn Marino cmd_record_btrace_start (char *args, int from_tty)
675*ef5ccd6cSJohn Marino {
676*ef5ccd6cSJohn Marino   if (args != NULL && *args != 0)
677*ef5ccd6cSJohn Marino     error (_("Invalid argument."));
678*ef5ccd6cSJohn Marino 
679*ef5ccd6cSJohn Marino   execute_command ("target record-btrace", from_tty);
680*ef5ccd6cSJohn Marino }
681*ef5ccd6cSJohn Marino 
682*ef5ccd6cSJohn Marino void _initialize_record_btrace (void);
683*ef5ccd6cSJohn Marino 
684*ef5ccd6cSJohn Marino /* Initialize btrace commands.  */
685*ef5ccd6cSJohn Marino 
686*ef5ccd6cSJohn Marino void
_initialize_record_btrace(void)687*ef5ccd6cSJohn Marino _initialize_record_btrace (void)
688*ef5ccd6cSJohn Marino {
689*ef5ccd6cSJohn Marino   add_cmd ("btrace", class_obscure, cmd_record_btrace_start,
690*ef5ccd6cSJohn Marino 	   _("Start branch trace recording."),
691*ef5ccd6cSJohn Marino 	   &record_cmdlist);
692*ef5ccd6cSJohn Marino   add_alias_cmd ("b", "btrace", class_obscure, 1, &record_cmdlist);
693*ef5ccd6cSJohn Marino 
694*ef5ccd6cSJohn Marino   init_record_btrace_ops ();
695*ef5ccd6cSJohn Marino   add_target (&record_btrace_ops);
696*ef5ccd6cSJohn Marino }
697