OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-old/] [gdb-7.1/] [gdb/] [inline-frame.c] - Blame information for rev 855

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 227 jeremybenn
/* Inline frame unwinder for GDB.
2
 
3
   Copyright (C) 2008, 2009, 2010 Free Software Foundation, Inc.
4
 
5
   This file is part of GDB.
6
 
7
   This program is free software; you can redistribute it and/or modify
8
   it under the terms of the GNU General Public License as published by
9
   the Free Software Foundation; either version 3 of the License, or
10
   (at your option) any later version.
11
 
12
   This program is distributed in the hope that it will be useful,
13
   but WITHOUT ANY WARRANTY; without even the implied warranty of
14
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
   GNU General Public License for more details.
16
 
17
   You should have received a copy of the GNU General Public License
18
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
19
 
20
#include "defs.h"
21
#include "addrmap.h"
22
#include "block.h"
23
#include "frame-unwind.h"
24
#include "inferior.h"
25
#include "regcache.h"
26
#include "symtab.h"
27
#include "vec.h"
28
 
29
#include "gdb_assert.h"
30
 
31
/* We need to save a few variables for every thread stopped at the
32
   virtual call site of an inlined function.  If there was always a
33
   "struct thread_info", we could hang it off that; in the mean time,
34
   keep our own list.  */
35
struct inline_state
36
{
37
  /* The thread this data relates to.  It should be a currently
38
     stopped thread; we assume thread IDs never change while the
39
     thread is stopped.  */
40
  ptid_t ptid;
41
 
42
  /* The number of inlined functions we are skipping.  Each of these
43
     functions can be stepped in to.  */
44
  int skipped_frames;
45
 
46
  /* Only valid if SKIPPED_FRAMES is non-zero.  This is the PC used
47
     when calculating SKIPPED_FRAMES; used to check whether we have
48
     moved to a new location by user request.  If so, we invalidate
49
     any skipped frames.  */
50
  CORE_ADDR saved_pc;
51
 
52
  /* Only valid if SKIPPED_FRAMES is non-zero.  This is the symbol
53
     of the outermost skipped inline function.  It's used to find the
54
     call site of the current frame.  */
55
  struct symbol *skipped_symbol;
56
};
57
 
58
typedef struct inline_state inline_state_s;
59
DEF_VEC_O(inline_state_s);
60
 
61
static VEC(inline_state_s) *inline_states;
62
 
63
/* Locate saved inlined frame state for PTID, if it exists
64
   and is valid.  */
65
 
66
static struct inline_state *
67
find_inline_frame_state (ptid_t ptid)
68
{
69
  struct inline_state *state;
70
  int ix;
71
 
72
  for (ix = 0; VEC_iterate (inline_state_s, inline_states, ix, state); ix++)
73
    {
74
      if (ptid_equal (state->ptid, ptid))
75
        {
76
          struct regcache *regcache = get_thread_regcache (ptid);
77
          CORE_ADDR current_pc = regcache_read_pc (regcache);
78
          if (current_pc != state->saved_pc)
79
            {
80
              /* PC has changed - this context is invalid.  Use the
81
                 default behavior.  */
82
              VEC_unordered_remove (inline_state_s, inline_states, ix);
83
              return NULL;
84
            }
85
          else
86
            return state;
87
        }
88
    }
89
 
90
  return NULL;
91
}
92
 
93
/* Allocate saved inlined frame state for PTID.  */
94
 
95
static struct inline_state *
96
allocate_inline_frame_state (ptid_t ptid)
97
{
98
  struct inline_state *state;
99
 
100
  state = VEC_safe_push (inline_state_s, inline_states, NULL);
101
  memset (state, 0, sizeof (*state));
102
  state->ptid = ptid;
103
 
104
  return state;
105
}
106
 
107
/* Forget about any hidden inlined functions in PTID, which is new or
108
   about to be resumed.  PTID may be minus_one_ptid (all processes)
109
   or a PID (all threads in this process).  */
110
 
111
void
112
clear_inline_frame_state (ptid_t ptid)
113
{
114
  struct inline_state *state;
115
  int ix;
116
 
117
  if (ptid_equal (ptid, minus_one_ptid))
118
    {
119
      VEC_free (inline_state_s, inline_states);
120
      return;
121
    }
122
 
123
  if (ptid_is_pid (ptid))
124
    {
125
      VEC (inline_state_s) *new_states = NULL;
126
      int pid = ptid_get_pid (ptid);
127
      for (ix = 0; VEC_iterate (inline_state_s, inline_states, ix, state); ix++)
128
        if (pid != ptid_get_pid (state->ptid))
129
          VEC_safe_push (inline_state_s, new_states, state);
130
      VEC_free (inline_state_s, inline_states);
131
      inline_states = new_states;
132
      return;
133
    }
134
 
135
  for (ix = 0; VEC_iterate (inline_state_s, inline_states, ix, state); ix++)
136
    if (ptid_equal (state->ptid, ptid))
137
      {
138
        VEC_unordered_remove (inline_state_s, inline_states, ix);
139
        return;
140
      }
141
}
142
 
143
static void
144
inline_frame_this_id (struct frame_info *this_frame,
145
                      void **this_cache,
146
                      struct frame_id *this_id)
147
{
148
  struct symbol *func;
149
 
150
  /* In order to have a stable frame ID for a given inline function,
151
     we must get the stack / special addresses from the underlying
152
     real frame's this_id method.  So we must call get_prev_frame.
153
     Because we are inlined into some function, there must be previous
154
     frames, so this is safe - as long as we're careful not to
155
     create any cycles.  */
156
  *this_id = get_frame_id (get_prev_frame (this_frame));
157
 
158
  /* We need a valid frame ID, so we need to be based on a valid
159
     frame.  FSF submission NOTE: this would be a good assertion to
160
     apply to all frames, all the time.  That would fix the ambiguity
161
     of null_frame_id (between "no/any frame" and "the outermost
162
     frame").  This will take work.  */
163
  gdb_assert (frame_id_p (*this_id));
164
 
165
  /* For now, require we don't match outer_frame_id either (see
166
     comment above).  */
167
  gdb_assert (!frame_id_eq (*this_id, outer_frame_id));
168
 
169
  /* Future work NOTE: Alexandre Oliva applied a patch to GCC 4.3
170
     which generates DW_AT_entry_pc for inlined functions when
171
     possible.  If this attribute is available, we should use it
172
     in the frame ID (and eventually, to set breakpoints).  */
173
  func = get_frame_function (this_frame);
174
  gdb_assert (func != NULL);
175
  (*this_id).code_addr = BLOCK_START (SYMBOL_BLOCK_VALUE (func));
176
  (*this_id).inline_depth++;
177
}
178
 
179
static struct value *
180
inline_frame_prev_register (struct frame_info *this_frame, void **this_cache,
181
                            int regnum)
182
{
183
  /* Use get_frame_register_value instead of
184
     frame_unwind_got_register, to avoid requiring this frame's ID.
185
     This frame's ID depends on the previous frame's ID (unusual), and
186
     the previous frame's ID depends on this frame's unwound
187
     registers.  If unwinding registers from this frame called
188
     get_frame_id, there would be a loop.
189
 
190
     Do not copy this code into any other unwinder!  Inlined functions
191
     are special; other unwinders must not have a dependency on the
192
     previous frame's ID, and therefore can and should use
193
     frame_unwind_got_register instead.  */
194
  return get_frame_register_value (this_frame, regnum);
195
}
196
 
197
/* Check whether we are at an inlining site that does not already
198
   have an associated frame.  */
199
 
200
static int
201
inline_frame_sniffer (const struct frame_unwind *self,
202
                      struct frame_info *this_frame,
203
                      void **this_cache)
204
{
205
  CORE_ADDR this_pc;
206
  struct block *frame_block, *cur_block;
207
  int depth;
208
  struct frame_info *next_frame;
209
  struct inline_state *state = find_inline_frame_state (inferior_ptid);
210
 
211
  this_pc = get_frame_address_in_block (this_frame);
212
  frame_block = block_for_pc (this_pc);
213
  if (frame_block == NULL)
214
    return 0;
215
 
216
  /* Calculate DEPTH, the number of inlined functions at this
217
     location.  */
218
  depth = 0;
219
  cur_block = frame_block;
220
  while (BLOCK_SUPERBLOCK (cur_block))
221
    {
222
      if (block_inlined_p (cur_block))
223
        depth++;
224
 
225
      cur_block = BLOCK_SUPERBLOCK (cur_block);
226
    }
227
 
228
  /* Check how many inlined functions already have frames.  */
229
  for (next_frame = get_next_frame (this_frame);
230
       next_frame && get_frame_type (next_frame) == INLINE_FRAME;
231
       next_frame = get_next_frame (next_frame))
232
    {
233
      gdb_assert (depth > 0);
234
      depth--;
235
    }
236
 
237
  /* If this is the topmost frame, or all frames above us are inlined,
238
     then check whether we were requested to skip some frames (so they
239
     can be stepped into later).  */
240
  if (state != NULL && state->skipped_frames > 0 && next_frame == NULL)
241
    {
242
      gdb_assert (depth >= state->skipped_frames);
243
      depth -= state->skipped_frames;
244
    }
245
 
246
  /* If all the inlined functions here already have frames, then pass
247
     to the normal unwinder for this PC.  */
248
  if (depth == 0)
249
    return 0;
250
 
251
  /* If the next frame is an inlined function, but not the outermost, then
252
     we are the next outer.  If it is not an inlined function, then we
253
     are the innermost inlined function of a different real frame.  */
254
  return 1;
255
}
256
 
257
const struct frame_unwind inline_frame_unwinder = {
258
  INLINE_FRAME,
259
  inline_frame_this_id,
260
  inline_frame_prev_register,
261
  NULL,
262
  inline_frame_sniffer
263
};
264
 
265
const struct frame_unwind *const inline_frame_unwind = &inline_frame_unwinder;
266
 
267
/* Return non-zero if BLOCK, an inlined function block containing PC,
268
   has a group of contiguous instructions starting at PC (but not
269
   before it).  */
270
 
271
static int
272
block_starting_point_at (CORE_ADDR pc, struct block *block)
273
{
274
  struct blockvector *bv;
275
  struct block *new_block;
276
 
277
  bv = blockvector_for_pc (pc, NULL);
278
  if (BLOCKVECTOR_MAP (bv) == NULL)
279
    return 0;
280
 
281
  new_block = addrmap_find (BLOCKVECTOR_MAP (bv), pc - 1);
282
  if (new_block == NULL)
283
    return 1;
284
 
285
  if (new_block == block || contained_in (new_block, block))
286
    return 0;
287
 
288
  /* The immediately preceeding address belongs to a different block,
289
     which is not a child of this one.  Treat this as an entrance into
290
     BLOCK.  */
291
  return 1;
292
}
293
 
294
/* Skip all inlined functions whose call sites are at the current PC.
295
   Frames for the hidden functions will not appear in the backtrace until the
296
   user steps into them.  */
297
 
298
void
299
skip_inline_frames (ptid_t ptid)
300
{
301
  CORE_ADDR this_pc;
302
  struct block *frame_block, *cur_block;
303
  struct symbol *last_sym = NULL;
304
  int skip_count = 0;
305
  struct inline_state *state;
306
 
307
  /* This function is called right after reinitializing the frame
308
     cache.  We try not to do more unwinding than absolutely
309
     necessary, for performance.  */
310
  this_pc = get_frame_pc (get_current_frame ());
311
  frame_block = block_for_pc (this_pc);
312
 
313
  if (frame_block != NULL)
314
    {
315
      cur_block = frame_block;
316
      while (BLOCK_SUPERBLOCK (cur_block))
317
        {
318
          if (block_inlined_p (cur_block))
319
            {
320
              /* See comments in inline_frame_this_id about this use
321
                 of BLOCK_START.  */
322
              if (BLOCK_START (cur_block) == this_pc
323
                  || block_starting_point_at (this_pc, cur_block))
324
                {
325
                  skip_count++;
326
                  last_sym = BLOCK_FUNCTION (cur_block);
327
                }
328
              else
329
                break;
330
            }
331
          cur_block = BLOCK_SUPERBLOCK (cur_block);
332
        }
333
    }
334
 
335
  gdb_assert (find_inline_frame_state (ptid) == NULL);
336
  state = allocate_inline_frame_state (ptid);
337
  state->skipped_frames = skip_count;
338
  state->saved_pc = this_pc;
339
  state->skipped_symbol = last_sym;
340
 
341
  if (skip_count != 0)
342
    reinit_frame_cache ();
343
}
344
 
345
/* Step into an inlined function by unhiding it.  */
346
 
347
void
348
step_into_inline_frame (ptid_t ptid)
349
{
350
  struct inline_state *state = find_inline_frame_state (ptid);
351
 
352
  gdb_assert (state != NULL && state->skipped_frames > 0);
353
  state->skipped_frames--;
354
  reinit_frame_cache ();
355
}
356
 
357
/* Return the number of hidden functions inlined into the current
358
   frame.  */
359
 
360
int
361
inline_skipped_frames (ptid_t ptid)
362
{
363
  struct inline_state *state = find_inline_frame_state (ptid);
364
 
365
  if (state == NULL)
366
    return 0;
367
  else
368
    return state->skipped_frames;
369
}
370
 
371
/* If one or more inlined functions are hidden, return the symbol for
372
   the function inlined into the current frame.  */
373
 
374
struct symbol *
375
inline_skipped_symbol (ptid_t ptid)
376
{
377
  struct inline_state *state = find_inline_frame_state (ptid);
378
 
379
  gdb_assert (state != NULL);
380
  return state->skipped_symbol;
381
}
382
 
383
/* Return the number of functions inlined into THIS_FRAME.  Some of
384
   the callees may not have associated frames (see
385
   skip_inline_frames).  */
386
 
387
int
388
frame_inlined_callees (struct frame_info *this_frame)
389
{
390
  struct frame_info *next_frame;
391
  int inline_count = 0;
392
 
393
  /* First count how many inlined functions at this PC have frames
394
     above FRAME (are inlined into FRAME).  */
395
  for (next_frame = get_next_frame (this_frame);
396
       next_frame && get_frame_type (next_frame) == INLINE_FRAME;
397
       next_frame = get_next_frame (next_frame))
398
    inline_count++;
399
 
400
  /* Simulate some most-inner inlined frames which were suppressed, so
401
     they can be stepped into later.  If we are unwinding already
402
     outer frames from some non-inlined frame this does not apply.  */
403
  if (next_frame == NULL)
404
    inline_count += inline_skipped_frames (inferior_ptid);
405
 
406
  return inline_count;
407
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.