]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blame - gdb/btrace.c
Install the btrace target ops for i386-linux-nat and amd64-linux-nat.
[thirdparty/binutils-gdb.git] / gdb / btrace.c
CommitLineData
02d27625
MM
1/* Branch trace support for GDB, the GNU debugger.
2
3 Copyright (C) 2013 Free Software Foundation, Inc.
4
5 Contributed by Intel Corp. <markus.t.metzger@intel.com>
6
7 This file is part of GDB.
8
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>. */
21
22#include "btrace.h"
23#include "gdbthread.h"
24#include "exceptions.h"
25#include "inferior.h"
26#include "target.h"
27#include "record.h"
28#include "symtab.h"
29#include "disasm.h"
30#include "source.h"
31#include "filenames.h"
32
33/* Print a record debug message. Use do ... while (0) to avoid ambiguities
34 when used in if statements. */
35
36#define DEBUG(msg, args...) \
37 do \
38 { \
39 if (record_debug != 0) \
40 fprintf_unfiltered (gdb_stdlog, \
41 "[btrace] " msg "\n", ##args); \
42 } \
43 while (0)
44
45#define DEBUG_FTRACE(msg, args...) DEBUG ("[ftrace] " msg, ##args)
46
47/* Initialize the instruction iterator. */
48
49static void
50btrace_init_insn_iterator (struct btrace_thread_info *btinfo)
51{
52 DEBUG ("init insn iterator");
53
54 btinfo->insn_iterator.begin = 1;
55 btinfo->insn_iterator.end = 0;
56}
57
58/* Initialize the function iterator. */
59
60static void
61btrace_init_func_iterator (struct btrace_thread_info *btinfo)
62{
63 DEBUG ("init func iterator");
64
65 btinfo->func_iterator.begin = 1;
66 btinfo->func_iterator.end = 0;
67}
68
69/* Compute the instruction trace from the block trace. */
70
71static VEC (btrace_inst_s) *
72compute_itrace (VEC (btrace_block_s) *btrace)
73{
74 VEC (btrace_inst_s) *itrace;
75 struct gdbarch *gdbarch;
76 unsigned int b;
77
78 DEBUG ("compute itrace");
79
80 itrace = NULL;
81 gdbarch = target_gdbarch ();
82 b = VEC_length (btrace_block_s, btrace);
83
84 while (b-- != 0)
85 {
86 btrace_block_s *block;
87 CORE_ADDR pc;
88
89 block = VEC_index (btrace_block_s, btrace, b);
90 pc = block->begin;
91
92 /* Add instructions for this block. */
93 for (;;)
94 {
95 btrace_inst_s *inst;
96 int size;
97
98 /* We should hit the end of the block. Warn if we went too far. */
99 if (block->end < pc)
100 {
101 warning (_("Recorded trace may be corrupted."));
102 break;
103 }
104
105 inst = VEC_safe_push (btrace_inst_s, itrace, NULL);
106 inst->pc = pc;
107
108 /* We're done once we pushed the instruction at the end. */
109 if (block->end == pc)
110 break;
111
112 size = gdb_insn_length (gdbarch, pc);
113
114 /* Make sure we terminate if we fail to compute the size. */
115 if (size <= 0)
116 {
117 warning (_("Recorded trace may be incomplete."));
118 break;
119 }
120
121 pc += size;
122 }
123 }
124
125 return itrace;
126}
127
128/* Return the function name of a recorded function segment for printing.
129 This function never returns NULL. */
130
131static const char *
132ftrace_print_function_name (struct btrace_func *bfun)
133{
134 struct minimal_symbol *msym;
135 struct symbol *sym;
136
137 msym = bfun->msym;
138 sym = bfun->sym;
139
140 if (sym != NULL)
141 return SYMBOL_PRINT_NAME (sym);
142
143 if (msym != NULL)
144 return SYMBOL_PRINT_NAME (msym);
145
146 return "<unknown>";
147}
148
149/* Return the file name of a recorded function segment for printing.
150 This function never returns NULL. */
151
152static const char *
153ftrace_print_filename (struct btrace_func *bfun)
154{
155 struct symbol *sym;
156 const char *filename;
157
158 sym = bfun->sym;
159
160 if (sym != NULL)
161 filename = symtab_to_filename_for_display (sym->symtab);
162 else
163 filename = "<unknown>";
164
165 return filename;
166}
167
168/* Print an ftrace debug status message. */
169
170static void
171ftrace_debug (struct btrace_func *bfun, const char *prefix)
172{
173 DEBUG_FTRACE ("%s: fun = %s, file = %s, lines = [%d; %d], insn = [%u; %u]",
174 prefix, ftrace_print_function_name (bfun),
175 ftrace_print_filename (bfun), bfun->lbegin, bfun->lend,
176 bfun->ibegin, bfun->iend);
177}
178
179/* Initialize a recorded function segment. */
180
181static void
182ftrace_init_func (struct btrace_func *bfun, struct minimal_symbol *mfun,
183 struct symbol *fun, unsigned int idx)
184{
185 bfun->msym = mfun;
186 bfun->sym = fun;
187 bfun->lbegin = INT_MAX;
188 bfun->lend = 0;
189 bfun->ibegin = idx;
190 bfun->iend = idx;
191}
192
193/* Check whether the function has changed. */
194
195static int
196ftrace_function_switched (struct btrace_func *bfun,
197 struct minimal_symbol *mfun, struct symbol *fun)
198{
199 struct minimal_symbol *msym;
200 struct symbol *sym;
201
202 /* The function changed if we did not have one before. */
203 if (bfun == NULL)
204 return 1;
205
206 msym = bfun->msym;
207 sym = bfun->sym;
208
209 /* If the minimal symbol changed, we certainly switched functions. */
210 if (mfun != NULL && msym != NULL
211 && strcmp (SYMBOL_LINKAGE_NAME (mfun), SYMBOL_LINKAGE_NAME (msym)) != 0)
212 return 1;
213
214 /* If the symbol changed, we certainly switched functions. */
215 if (fun != NULL && sym != NULL)
216 {
217 const char *bfname, *fname;
218
219 /* Check the function name. */
220 if (strcmp (SYMBOL_LINKAGE_NAME (fun), SYMBOL_LINKAGE_NAME (sym)) != 0)
221 return 1;
222
223 /* Check the location of those functions, as well. */
224 bfname = symtab_to_fullname (sym->symtab);
225 fname = symtab_to_fullname (fun->symtab);
226 if (filename_cmp (fname, bfname) != 0)
227 return 1;
228 }
229
230 return 0;
231}
232
233/* Check if we should skip this file when generating the function call
234 history. We would want to do that if, say, a macro that is defined
235 in another file is expanded in this function. */
236
237static int
238ftrace_skip_file (struct btrace_func *bfun, const char *filename)
239{
240 struct symbol *sym;
241 const char *bfile;
242
243 sym = bfun->sym;
244
245 if (sym != NULL)
246 bfile = symtab_to_fullname (sym->symtab);
247 else
248 bfile = "";
249
250 if (filename == NULL)
251 filename = "";
252
253 return (filename_cmp (bfile, filename) != 0);
254}
255
256/* Compute the function trace from the instruction trace. */
257
258static VEC (btrace_func_s) *
259compute_ftrace (VEC (btrace_inst_s) *itrace)
260{
261 VEC (btrace_func_s) *ftrace;
262 struct btrace_inst *binst;
263 struct btrace_func *bfun;
264 unsigned int idx;
265
266 DEBUG ("compute ftrace");
267
268 ftrace = NULL;
269 bfun = NULL;
270
271 for (idx = 0; VEC_iterate (btrace_inst_s, itrace, idx, binst); ++idx)
272 {
273 struct symtab_and_line sal;
274 struct minimal_symbol *mfun;
275 struct symbol *fun;
276 const char *filename;
277 CORE_ADDR pc;
278
279 pc = binst->pc;
280
281 /* Try to determine the function we're in. We use both types of symbols
282 to avoid surprises when we sometimes get a full symbol and sometimes
283 only a minimal symbol. */
284 fun = find_pc_function (pc);
285 mfun = lookup_minimal_symbol_by_pc (pc);
286
287 if (fun == NULL && mfun == NULL)
288 {
289 DEBUG_FTRACE ("no symbol at %u, pc=%s", idx,
290 core_addr_to_string_nz (pc));
291 continue;
292 }
293
294 /* If we're switching functions, we start over. */
295 if (ftrace_function_switched (bfun, mfun, fun))
296 {
297 bfun = VEC_safe_push (btrace_func_s, ftrace, NULL);
298
299 ftrace_init_func (bfun, mfun, fun, idx);
300 ftrace_debug (bfun, "init");
301 }
302
303 /* Update the instruction range. */
304 bfun->iend = idx;
305 ftrace_debug (bfun, "update insns");
306
307 /* Let's see if we have source correlation, as well. */
308 sal = find_pc_line (pc, 0);
309 if (sal.symtab == NULL || sal.line == 0)
310 {
311 DEBUG_FTRACE ("no lines at %u, pc=%s", idx,
312 core_addr_to_string_nz (pc));
313 continue;
314 }
315
316 /* Check if we switched files. This could happen if, say, a macro that
317 is defined in another file is expanded here. */
318 filename = symtab_to_fullname (sal.symtab);
319 if (ftrace_skip_file (bfun, filename))
320 {
321 DEBUG_FTRACE ("ignoring file at %u, pc=%s, file=%s", idx,
322 core_addr_to_string_nz (pc), filename);
323 continue;
324 }
325
326 /* Update the line range. */
327 bfun->lbegin = min (bfun->lbegin, sal.line);
328 bfun->lend = max (bfun->lend, sal.line);
329 ftrace_debug (bfun, "update lines");
330 }
331
332 return ftrace;
333}
334
335/* See btrace.h. */
336
337void
338btrace_enable (struct thread_info *tp)
339{
340 if (tp->btrace.target != NULL)
341 return;
342
343 if (!target_supports_btrace ())
344 error (_("Target does not support branch tracing."));
345
346 DEBUG ("enable thread %d (%s)", tp->num, target_pid_to_str (tp->ptid));
347
348 tp->btrace.target = target_enable_btrace (tp->ptid);
349}
350
351/* See btrace.h. */
352
353void
354btrace_disable (struct thread_info *tp)
355{
356 struct btrace_thread_info *btp = &tp->btrace;
357 int errcode = 0;
358
359 if (btp->target == NULL)
360 return;
361
362 DEBUG ("disable thread %d (%s)", tp->num, target_pid_to_str (tp->ptid));
363
364 target_disable_btrace (btp->target);
365 btp->target = NULL;
366
367 btrace_clear (tp);
368}
369
370/* See btrace.h. */
371
372void
373btrace_teardown (struct thread_info *tp)
374{
375 struct btrace_thread_info *btp = &tp->btrace;
376 int errcode = 0;
377
378 if (btp->target == NULL)
379 return;
380
381 DEBUG ("teardown thread %d (%s)", tp->num, target_pid_to_str (tp->ptid));
382
383 target_teardown_btrace (btp->target);
384 btp->target = NULL;
385
386 btrace_clear (tp);
387}
388
389/* See btrace.h. */
390
391void
392btrace_fetch (struct thread_info *tp)
393{
394 struct btrace_thread_info *btinfo;
395 VEC (btrace_block_s) *btrace;
396
397 DEBUG ("fetch thread %d (%s)", tp->num, target_pid_to_str (tp->ptid));
398
399 btinfo = &tp->btrace;
400 if (btinfo->target == NULL)
401 return;
402
403 btrace = target_read_btrace (btinfo->target, btrace_read_new);
404 if (VEC_empty (btrace_block_s, btrace))
405 return;
406
407 btrace_clear (tp);
408
409 btinfo->btrace = btrace;
410 btinfo->itrace = compute_itrace (btinfo->btrace);
411 btinfo->ftrace = compute_ftrace (btinfo->itrace);
412
413 /* Initialize branch trace iterators. */
414 btrace_init_insn_iterator (btinfo);
415 btrace_init_func_iterator (btinfo);
416}
417
418/* See btrace.h. */
419
420void
421btrace_clear (struct thread_info *tp)
422{
423 struct btrace_thread_info *btinfo;
424
425 DEBUG ("clear thread %d (%s)", tp->num, target_pid_to_str (tp->ptid));
426
427 btinfo = &tp->btrace;
428
429 VEC_free (btrace_block_s, btinfo->btrace);
430 VEC_free (btrace_inst_s, btinfo->itrace);
431 VEC_free (btrace_func_s, btinfo->ftrace);
432
433 btinfo->btrace = NULL;
434 btinfo->itrace = NULL;
435 btinfo->ftrace = NULL;
436}
437
438/* See btrace.h. */
439
440void
441btrace_free_objfile (struct objfile *objfile)
442{
443 struct thread_info *tp;
444
445 DEBUG ("free objfile");
446
447 ALL_THREADS (tp)
448 btrace_clear (tp);
449}