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