Commit | Line | Data |
---|---|---|
bd5635a1 RP |
1 | /* Get info from stack frames; |
2 | convert between frames, blocks, functions and pc values. | |
3 | Copyright (C) 1986, 1987, 1988, 1989 Free Software Foundation, Inc. | |
4 | ||
5 | This file is part of GDB. | |
6 | ||
7 | GDB 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 1, or (at your option) | |
10 | any later version. | |
11 | ||
12 | GDB 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 GDB; see the file COPYING. If not, write to | |
19 | the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ | |
20 | ||
21 | #include "defs.h" | |
22 | #include "param.h" | |
23 | #include "symtab.h" | |
24 | #include "frame.h" | |
25 | #include "gdbcore.h" | |
26 | #include "value.h" /* for read_register */ | |
27 | #include "target.h" /* for target_has_stack */ | |
28 | ||
29 | /* Required by INIT_EXTRA_FRAME_INFO on 88k. */ | |
30 | #include <setjmp.h> | |
31 | #include <obstack.h> | |
32 | ||
33 | CORE_ADDR read_pc (); /* In infcmd.c */ | |
34 | ||
35 | /* Start and end of object file containing the entry point. | |
36 | STARTUP_FILE_END is the first address of the next file. | |
37 | This file is assumed to be a startup file | |
38 | and frames with pc's inside it | |
39 | are treated as nonexistent. | |
40 | ||
41 | Setting these variables is necessary so that backtraces do not fly off | |
42 | the bottom of the stack. */ | |
43 | CORE_ADDR startup_file_start; | |
44 | CORE_ADDR startup_file_end; | |
45 | ||
46 | /* Is ADDR outside the startup file? Note that if your machine | |
47 | has a way to detect the bottom of the stack, there is no need | |
48 | to call this function from FRAME_CHAIN_VALID; the reason for | |
49 | doing so is that some machines have no way of detecting bottom | |
50 | of stack. */ | |
51 | int | |
52 | outside_startup_file (addr) | |
53 | CORE_ADDR addr; | |
54 | { | |
55 | return !(addr >= startup_file_start && addr < startup_file_end); | |
56 | } | |
57 | ||
58 | /* Address of innermost stack frame (contents of FP register) */ | |
59 | ||
60 | static FRAME current_frame; | |
61 | ||
62 | /* | |
63 | * Cache for frame addresses already read by gdb. Valid only while | |
64 | * inferior is stopped. Control variables for the frame cache should | |
65 | * be local to this module. | |
66 | */ | |
67 | struct obstack frame_cache_obstack; | |
68 | ||
69 | /* Return the innermost (currently executing) stack frame. */ | |
70 | ||
71 | FRAME | |
72 | get_current_frame () | |
73 | { | |
74 | /* We assume its address is kept in a general register; | |
75 | param.h says which register. */ | |
76 | ||
77 | return current_frame; | |
78 | } | |
79 | ||
80 | void | |
81 | set_current_frame (frame) | |
82 | FRAME frame; | |
83 | { | |
84 | current_frame = frame; | |
85 | } | |
86 | ||
87 | FRAME | |
88 | create_new_frame (addr, pc) | |
89 | FRAME_ADDR addr; | |
90 | CORE_ADDR pc; | |
91 | { | |
92 | struct frame_info *fci; /* Same type as FRAME */ | |
93 | ||
94 | fci = (struct frame_info *) | |
95 | obstack_alloc (&frame_cache_obstack, | |
96 | sizeof (struct frame_info)); | |
97 | ||
98 | /* Arbitrary frame */ | |
99 | fci->next = (struct frame_info *) 0; | |
100 | fci->prev = (struct frame_info *) 0; | |
101 | fci->frame = addr; | |
102 | fci->next_frame = 0; /* Since arbitrary */ | |
103 | fci->pc = pc; | |
104 | ||
105 | #ifdef INIT_EXTRA_FRAME_INFO | |
106 | INIT_EXTRA_FRAME_INFO (fci); | |
107 | #endif | |
108 | ||
109 | return fci; | |
110 | } | |
111 | ||
112 | /* Return the frame that called FRAME. | |
113 | If FRAME is the original frame (it has no caller), return 0. */ | |
114 | ||
115 | FRAME | |
116 | get_prev_frame (frame) | |
117 | FRAME frame; | |
118 | { | |
119 | /* We're allowed to know that FRAME and "struct frame_info *" are | |
120 | the same */ | |
121 | return get_prev_frame_info (frame); | |
122 | } | |
123 | ||
124 | /* Return the frame that FRAME calls (0 if FRAME is the innermost | |
125 | frame). */ | |
126 | ||
127 | FRAME | |
128 | get_next_frame (frame) | |
129 | FRAME frame; | |
130 | { | |
131 | /* We're allowed to know that FRAME and "struct frame_info *" are | |
132 | the same */ | |
133 | return frame->next; | |
134 | } | |
135 | ||
136 | /* | |
137 | * Flush the entire frame cache. | |
138 | */ | |
139 | void | |
140 | flush_cached_frames () | |
141 | { | |
142 | /* Since we can't really be sure what the first object allocated was */ | |
143 | obstack_free (&frame_cache_obstack, 0); | |
144 | obstack_init (&frame_cache_obstack); | |
145 | ||
146 | current_frame = (struct frame_info *) 0; /* Invalidate cache */ | |
147 | } | |
148 | ||
2403f49b JK |
149 | /* Flush the frame cache, and start a new one if necessary. */ |
150 | void | |
151 | reinit_frame_cache () | |
152 | { | |
153 | FRAME fr = current_frame; | |
154 | flush_cached_frames (); | |
155 | if (fr) | |
156 | set_current_frame ( create_new_frame (read_register (FP_REGNUM), | |
157 | read_pc ())); | |
158 | } | |
159 | ||
bd5635a1 RP |
160 | /* Return a structure containing various interesting information |
161 | about a specified stack frame. */ | |
162 | /* How do I justify including this function? Well, the FRAME | |
163 | identifier format has gone through several changes recently, and | |
164 | it's not completely inconceivable that it could happen again. If | |
165 | it does, have this routine around will help */ | |
166 | ||
167 | struct frame_info * | |
168 | get_frame_info (frame) | |
169 | FRAME frame; | |
170 | { | |
171 | return frame; | |
172 | } | |
173 | ||
174 | /* If a machine allows frameless functions, it should define a macro | |
175 | FRAMELESS_FUNCTION_INVOCATION(FI, FRAMELESS) in param.h. FI is the struct | |
176 | frame_info for the frame, and FRAMELESS should be set to nonzero | |
177 | if it represents a frameless function invocation. */ | |
178 | ||
179 | /* Return nonzero if the function for this frame has a prologue. Many | |
180 | machines can define FRAMELESS_FUNCTION_INVOCATION to just call this | |
181 | function. */ | |
182 | ||
183 | int | |
184 | frameless_look_for_prologue (frame) | |
185 | FRAME frame; | |
186 | { | |
187 | CORE_ADDR func_start, after_prologue; | |
188 | func_start = (get_pc_function_start (frame->pc) + | |
189 | FUNCTION_START_OFFSET); | |
190 | if (func_start) | |
191 | { | |
192 | after_prologue = func_start; | |
193 | SKIP_PROLOGUE (after_prologue); | |
194 | return after_prologue == func_start; | |
195 | } | |
196 | else | |
197 | /* If we can't find the start of the function, we don't really | |
198 | know whether the function is frameless, but we should be able | |
199 | to get a reasonable (i.e. best we can do under the | |
200 | circumstances) backtrace by saying that it isn't. */ | |
201 | return 0; | |
202 | } | |
203 | ||
204 | #if !defined (INIT_FRAME_PC) | |
205 | #define INIT_FRAME_PC(fromleaf, prev) \ | |
206 | prev->pc = (fromleaf ? SAVED_PC_AFTER_CALL (prev->next) : \ | |
207 | prev->next ? FRAME_SAVED_PC (prev->next) : read_pc ()); | |
208 | #endif | |
209 | ||
210 | /* Return a structure containing various interesting information | |
211 | about the frame that called NEXT_FRAME. Returns NULL | |
212 | if there is no such frame. */ | |
213 | ||
214 | struct frame_info * | |
215 | get_prev_frame_info (next_frame) | |
216 | FRAME next_frame; | |
217 | { | |
218 | FRAME_ADDR address; | |
219 | struct frame_info *prev; | |
220 | int fromleaf = 0; | |
221 | ||
222 | /* If the requested entry is in the cache, return it. | |
223 | Otherwise, figure out what the address should be for the entry | |
224 | we're about to add to the cache. */ | |
225 | ||
226 | if (!next_frame) | |
227 | { | |
228 | if (!current_frame) | |
229 | { | |
230 | error ("You haven't set up a process's stack to examine."); | |
231 | } | |
232 | ||
233 | return current_frame; | |
234 | } | |
235 | ||
236 | /* If we have the prev one, return it */ | |
237 | if (next_frame->prev) | |
238 | return next_frame->prev; | |
239 | ||
240 | /* On some machines it is possible to call a function without | |
241 | setting up a stack frame for it. On these machines, we | |
242 | define this macro to take two args; a frameinfo pointer | |
243 | identifying a frame and a variable to set or clear if it is | |
244 | or isn't leafless. */ | |
245 | #ifdef FRAMELESS_FUNCTION_INVOCATION | |
246 | /* Still don't want to worry about this except on the innermost | |
247 | frame. This macro will set FROMLEAF if NEXT_FRAME is a | |
248 | frameless function invocation. */ | |
249 | if (!(next_frame->next)) | |
250 | { | |
251 | FRAMELESS_FUNCTION_INVOCATION (next_frame, fromleaf); | |
252 | if (fromleaf) | |
253 | address = next_frame->frame; | |
254 | } | |
255 | #endif | |
256 | ||
257 | if (!fromleaf) | |
258 | { | |
259 | /* Two macros defined in tm.h specify the machine-dependent | |
260 | actions to be performed here. | |
261 | First, get the frame's chain-pointer. | |
262 | If that is zero, the frame is the outermost frame or a leaf | |
263 | called by the outermost frame. This means that if start | |
264 | calls main without a frame, we'll return 0 (which is fine | |
265 | anyway). | |
266 | ||
267 | Nope; there's a problem. This also returns when the current | |
268 | routine is a leaf of main. This is unacceptable. We move | |
269 | this to after the ffi test; I'd rather have backtraces from | |
270 | start go curfluy than have an abort called from main not show | |
271 | main. */ | |
272 | address = FRAME_CHAIN (next_frame); | |
273 | if (!FRAME_CHAIN_VALID (address, next_frame)) | |
274 | return 0; | |
275 | address = FRAME_CHAIN_COMBINE (address, next_frame); | |
276 | } | |
277 | ||
278 | prev = (struct frame_info *) | |
279 | obstack_alloc (&frame_cache_obstack, | |
280 | sizeof (struct frame_info)); | |
281 | ||
282 | if (next_frame) | |
283 | next_frame->prev = prev; | |
284 | prev->next = next_frame; | |
285 | prev->prev = (struct frame_info *) 0; | |
286 | prev->frame = address; | |
287 | prev->next_frame = prev->next ? prev->next->frame : 0; | |
288 | ||
289 | #ifdef INIT_EXTRA_FRAME_INFO | |
290 | INIT_EXTRA_FRAME_INFO(prev); | |
291 | #endif | |
292 | ||
293 | /* This entry is in the frame queue now, which is good since | |
294 | FRAME_SAVED_PC may use that queue to figure out it's value | |
295 | (see m-sparc.h). We want the pc saved in the inferior frame. */ | |
296 | INIT_FRAME_PC(fromleaf, prev); | |
297 | ||
298 | return prev; | |
299 | } | |
300 | ||
301 | CORE_ADDR | |
302 | get_frame_pc (frame) | |
303 | FRAME frame; | |
304 | { | |
305 | struct frame_info *fi; | |
306 | fi = get_frame_info (frame); | |
307 | return fi->pc; | |
308 | } | |
309 | ||
310 | #if defined (FRAME_FIND_SAVED_REGS) | |
311 | /* Find the addresses in which registers are saved in FRAME. */ | |
312 | ||
313 | void | |
314 | get_frame_saved_regs (frame_info_addr, saved_regs_addr) | |
315 | struct frame_info *frame_info_addr; | |
316 | struct frame_saved_regs *saved_regs_addr; | |
317 | { | |
318 | FRAME_FIND_SAVED_REGS (frame_info_addr, *saved_regs_addr); | |
319 | } | |
320 | #endif | |
321 | ||
322 | /* Return the innermost lexical block in execution | |
323 | in a specified stack frame. The frame address is assumed valid. */ | |
324 | ||
325 | struct block * | |
326 | get_frame_block (frame) | |
327 | FRAME frame; | |
328 | { | |
329 | struct frame_info *fi; | |
330 | CORE_ADDR pc; | |
331 | ||
332 | fi = get_frame_info (frame); | |
333 | ||
334 | pc = fi->pc; | |
335 | if (fi->next_frame != 0) | |
336 | /* We are not in the innermost frame. We need to subtract one to | |
337 | get the correct block, in case the call instruction was the | |
338 | last instruction of the block. If there are any machines on | |
339 | which the saved pc does not point to after the call insn, we | |
340 | probably want to make fi->pc point after the call insn anyway. */ | |
341 | --pc; | |
342 | return block_for_pc (pc); | |
343 | } | |
344 | ||
345 | struct block * | |
346 | get_current_block () | |
347 | { | |
348 | return block_for_pc (read_pc ()); | |
349 | } | |
350 | ||
351 | CORE_ADDR | |
352 | get_pc_function_start (pc) | |
353 | CORE_ADDR pc; | |
354 | { | |
355 | register struct block *bl = block_for_pc (pc); | |
356 | register struct symbol *symbol; | |
357 | if (bl == 0 || (symbol = block_function (bl)) == 0) | |
358 | { | |
359 | register int misc_index = find_pc_misc_function (pc); | |
360 | if (misc_index >= 0) | |
361 | return misc_function_vector[misc_index].address; | |
362 | return 0; | |
363 | } | |
364 | bl = SYMBOL_BLOCK_VALUE (symbol); | |
365 | return BLOCK_START (bl); | |
366 | } | |
367 | ||
368 | /* Return the symbol for the function executing in frame FRAME. */ | |
369 | ||
370 | struct symbol * | |
371 | get_frame_function (frame) | |
372 | FRAME frame; | |
373 | { | |
374 | register struct block *bl = get_frame_block (frame); | |
375 | if (bl == 0) | |
376 | return 0; | |
377 | return block_function (bl); | |
378 | } | |
379 | \f | |
380 | /* Return the blockvector immediately containing the innermost lexical block | |
381 | containing the specified pc value, or 0 if there is none. | |
382 | PINDEX is a pointer to the index value of the block. If PINDEX | |
383 | is NULL, we don't pass this information back to the caller. */ | |
384 | ||
385 | struct blockvector * | |
386 | blockvector_for_pc (pc, pindex) | |
387 | register CORE_ADDR pc; | |
388 | int *pindex; | |
389 | { | |
390 | register struct block *b; | |
391 | register int bot, top, half; | |
392 | register struct symtab *s; | |
393 | struct blockvector *bl; | |
394 | ||
395 | /* First search all symtabs for one whose file contains our pc */ | |
396 | s = find_pc_symtab (pc); | |
397 | if (s == 0) | |
398 | return 0; | |
399 | ||
400 | bl = BLOCKVECTOR (s); | |
401 | b = BLOCKVECTOR_BLOCK (bl, 0); | |
402 | ||
403 | /* Then search that symtab for the smallest block that wins. */ | |
404 | /* Use binary search to find the last block that starts before PC. */ | |
405 | ||
406 | bot = 0; | |
407 | top = BLOCKVECTOR_NBLOCKS (bl); | |
408 | ||
409 | while (top - bot > 1) | |
410 | { | |
411 | half = (top - bot + 1) >> 1; | |
412 | b = BLOCKVECTOR_BLOCK (bl, bot + half); | |
413 | if (BLOCK_START (b) <= pc) | |
414 | bot += half; | |
415 | else | |
416 | top = bot + half; | |
417 | } | |
418 | ||
419 | /* Now search backward for a block that ends after PC. */ | |
420 | ||
421 | while (bot >= 0) | |
422 | { | |
423 | b = BLOCKVECTOR_BLOCK (bl, bot); | |
424 | if (BLOCK_END (b) > pc) | |
425 | { | |
426 | if (pindex) | |
427 | *pindex = bot; | |
428 | return bl; | |
429 | } | |
430 | bot--; | |
431 | } | |
432 | ||
433 | return 0; | |
434 | } | |
435 | ||
436 | /* Return the innermost lexical block containing the specified pc value, | |
437 | or 0 if there is none. */ | |
438 | ||
439 | struct block * | |
440 | block_for_pc (pc) | |
441 | register CORE_ADDR pc; | |
442 | { | |
443 | register struct blockvector *bl; | |
444 | int index; | |
445 | ||
446 | bl = blockvector_for_pc (pc, &index); | |
447 | if (bl) | |
448 | return BLOCKVECTOR_BLOCK (bl, index); | |
449 | return 0; | |
450 | } | |
451 | ||
452 | /* Return the function containing pc value PC. | |
453 | Returns 0 if function is not known. */ | |
454 | ||
455 | struct symbol * | |
456 | find_pc_function (pc) | |
457 | CORE_ADDR pc; | |
458 | { | |
459 | register struct block *b = block_for_pc (pc); | |
460 | if (b == 0) | |
461 | return 0; | |
462 | return block_function (b); | |
463 | } | |
464 | ||
465 | /* These variables are used to cache the most recent result | |
466 | * of find_pc_partial_function. */ | |
467 | ||
468 | static CORE_ADDR cache_pc_function_low = 0; | |
469 | static CORE_ADDR cache_pc_function_high = 0; | |
470 | static char *cache_pc_function_name = 0; | |
471 | ||
472 | /* Clear cache, e.g. when symbol table is discarded. */ | |
473 | ||
474 | void | |
475 | clear_pc_function_cache() | |
476 | { | |
477 | cache_pc_function_low = 0; | |
478 | cache_pc_function_high = 0; | |
479 | cache_pc_function_name = (char *)0; | |
480 | } | |
481 | ||
482 | /* Finds the "function" (text symbol) that is smaller than PC | |
483 | but greatest of all of the potential text symbols. Sets | |
484 | *NAME and/or *ADDRESS conditionally if that pointer is non-zero. | |
485 | Returns 0 if it couldn't find anything, 1 if it did. On a zero | |
486 | return, *NAME and *ADDRESS are always set to zero. On a 1 return, | |
487 | *NAME and *ADDRESS contain real information. */ | |
488 | ||
489 | int | |
490 | find_pc_partial_function (pc, name, address) | |
491 | CORE_ADDR pc; | |
492 | char **name; | |
493 | CORE_ADDR *address; | |
494 | { | |
495 | struct partial_symtab *pst; | |
496 | struct symbol *f; | |
497 | int miscfunc; | |
498 | struct partial_symbol *psb; | |
499 | ||
500 | if (pc >= cache_pc_function_low && pc < cache_pc_function_high) | |
501 | { | |
502 | if (address) | |
503 | *address = cache_pc_function_low; | |
504 | if (name) | |
505 | *name = cache_pc_function_name; | |
506 | return 1; | |
507 | } | |
508 | ||
509 | pst = find_pc_psymtab (pc); | |
510 | if (pst) | |
511 | { | |
512 | if (pst->readin) | |
513 | { | |
514 | /* The information we want has already been read in. | |
515 | We can go to the already readin symbols and we'll get | |
516 | the best possible answer. */ | |
517 | f = find_pc_function (pc); | |
518 | if (!f) | |
519 | { | |
520 | return_error: | |
521 | /* No available symbol. */ | |
522 | if (name != 0) | |
523 | *name = 0; | |
524 | if (address != 0) | |
525 | *address = 0; | |
526 | return 0; | |
527 | } | |
528 | ||
529 | cache_pc_function_low = BLOCK_START (SYMBOL_BLOCK_VALUE (f)); | |
530 | cache_pc_function_high = BLOCK_END (SYMBOL_BLOCK_VALUE (f)); | |
531 | cache_pc_function_name = SYMBOL_NAME (f); | |
532 | if (name) | |
533 | *name = cache_pc_function_name; | |
534 | if (address) | |
535 | *address = cache_pc_function_low; | |
536 | return 1; | |
537 | } | |
538 | ||
539 | /* Get the information from a combination of the pst | |
540 | (static symbols), and the misc function vector (extern | |
541 | symbols). */ | |
542 | miscfunc = find_pc_misc_function (pc); | |
543 | psb = find_pc_psymbol (pst, pc); | |
544 | ||
545 | if (!psb && miscfunc == -1) | |
546 | { | |
547 | goto return_error; | |
548 | } | |
549 | if (psb | |
550 | && (miscfunc == -1 | |
551 | || (SYMBOL_VALUE_ADDRESS (psb) | |
552 | >= misc_function_vector[miscfunc].address))) | |
553 | { | |
554 | /* This case isn't being cached currently. */ | |
555 | if (address) | |
556 | *address = SYMBOL_VALUE_ADDRESS (psb); | |
557 | if (name) | |
558 | *name = SYMBOL_NAME (psb); | |
559 | return 1; | |
560 | } | |
561 | } | |
562 | else | |
563 | /* Must be in the misc function stuff. */ | |
564 | { | |
565 | miscfunc = find_pc_misc_function (pc); | |
566 | if (miscfunc == -1) | |
567 | goto return_error; | |
568 | } | |
569 | ||
570 | { | |
571 | if (misc_function_vector[miscfunc].type == mf_text) | |
572 | cache_pc_function_low = misc_function_vector[miscfunc].address; | |
573 | else | |
574 | /* It is a transfer table for Sun shared libraries. */ | |
575 | cache_pc_function_low = pc - FUNCTION_START_OFFSET; | |
576 | } | |
577 | cache_pc_function_name = misc_function_vector[miscfunc].name; | |
578 | if (miscfunc < misc_function_count && 1 /* FIXME mf_text again? */ ) | |
579 | cache_pc_function_high = misc_function_vector[miscfunc+1].address; | |
580 | else | |
581 | cache_pc_function_high = cache_pc_function_low + 1; | |
582 | if (address) | |
583 | *address = cache_pc_function_low; | |
584 | if (name) | |
585 | *name = cache_pc_function_name; | |
586 | return 1; | |
587 | } | |
588 | ||
589 | /* Find the misc function whose address is the largest | |
590 | while being less than PC. Return its index in misc_function_vector. | |
591 | Returns -1 if PC is not in suitable range. */ | |
592 | ||
593 | int | |
594 | find_pc_misc_function (pc) | |
595 | register CORE_ADDR pc; | |
596 | { | |
597 | register int lo = 0; | |
598 | register int hi = misc_function_count-1; | |
599 | register int new; | |
600 | ||
601 | /* Note that the last thing in the vector is always _etext. */ | |
602 | /* Actually, "end", now that non-functions | |
603 | go on the misc_function_vector. */ | |
604 | ||
605 | /* Above statement is not *always* true - fix for case where there are */ | |
606 | /* no misc functions at all (ie no symbol table has been read). */ | |
607 | if (hi < 0) return -1; /* no misc functions recorded */ | |
608 | ||
609 | /* trivial reject range test */ | |
610 | if (pc < misc_function_vector[0].address || | |
611 | pc > misc_function_vector[hi].address) | |
612 | return -1; | |
613 | ||
614 | /* Note that the following search will not return hi if | |
615 | pc == misc_function_vector[hi].address. If "end" points to the | |
616 | first unused location, this is correct and the above test | |
617 | simply needs to be changed to | |
618 | "pc >= misc_function_vector[hi].address". */ | |
619 | do { | |
620 | new = (lo + hi) >> 1; | |
621 | if (misc_function_vector[new].address == pc) | |
622 | return new; /* an exact match */ | |
623 | else if (misc_function_vector[new].address > pc) | |
624 | hi = new; | |
625 | else | |
626 | lo = new; | |
627 | } while (hi-lo != 1); | |
628 | ||
629 | /* if here, we had no exact match, so return the lower choice */ | |
630 | return lo; | |
631 | } | |
632 | ||
633 | /* Return the innermost stack frame executing inside of the specified block, | |
634 | or zero if there is no such frame. */ | |
635 | ||
636 | FRAME | |
637 | block_innermost_frame (block) | |
638 | struct block *block; | |
639 | { | |
640 | struct frame_info *fi; | |
641 | register FRAME frame; | |
642 | register CORE_ADDR start = BLOCK_START (block); | |
643 | register CORE_ADDR end = BLOCK_END (block); | |
644 | ||
645 | frame = 0; | |
646 | while (1) | |
647 | { | |
648 | frame = get_prev_frame (frame); | |
649 | if (frame == 0) | |
650 | return 0; | |
651 | fi = get_frame_info (frame); | |
652 | if (fi->pc >= start && fi->pc < end) | |
653 | return frame; | |
654 | } | |
655 | } | |
656 | ||
657 | void | |
658 | _initialize_blockframe () | |
659 | { | |
660 | obstack_init (&frame_cache_obstack); | |
661 | } |