2 * Debugger stack handling
4 * Copyright 1995 Alexandre Julliard
5 * Copyright 1996 Eric Youngdale
6 * Copyright 1999 Ove Kåven
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 #include "stackframe.h"
33 * We keep this info for each frame, so that we can
34 * find local variable information correctly.
42 struct symbol_info frame
;
46 static struct bt_info
* frames
= NULL
;
64 /***********************************************************************
67 * Dump the top of the stack
69 void DEBUG_InfoStack(void)
75 value
.cookie
= DV_TARGET
;
76 value
.addr
.seg
= DEBUG_context
.SegSs
;
77 value
.addr
.off
= DEBUG_context
.Esp
;
79 DEBUG_Printf(DBG_CHN_MESG
,"Stack dump:\n");
80 switch (DEBUG_GetSelectorType(value
.addr
.seg
))
82 case MODE_32
: /* 32-bit mode */
83 DEBUG_ExamineMemory( &value
, 24, 'x' );
85 case MODE_16
: /* 16-bit mode */
87 value
.addr
.off
&= 0xffff;
88 DEBUG_ExamineMemory( &value
, 24, 'w' );
91 DEBUG_Printf(DBG_CHN_MESG
, "Bad segment (%ld)\n", value
.addr
.seg
);
93 DEBUG_Printf(DBG_CHN_MESG
,"\n");
98 static void DEBUG_ForceFrame(DBG_ADDR
*stack
, DBG_ADDR
*code
, int frameno
, enum dbg_mode mode
,
99 int noisy
, const char *caveat
)
101 int theframe
= nframe
++;
102 frames
= (struct bt_info
*)DBG_realloc(frames
,
103 nframe
*sizeof(struct bt_info
));
105 DEBUG_Printf(DBG_CHN_MESG
,"%s%d ", (theframe
== curr_frame
? "=>" : " "),
107 frames
[theframe
].cs
= code
->seg
;
108 frames
[theframe
].eip
= code
->off
;
110 frames
[theframe
].frame
= DEBUG_PrintAddressAndArgs( code
, mode
, stack
->off
, TRUE
);
112 DEBUG_FindNearestSymbol( code
, TRUE
,
113 &frames
[theframe
].frame
.sym
, stack
->off
,
114 &frames
[theframe
].frame
.list
);
115 frames
[theframe
].ss
= stack
->seg
;
116 frames
[theframe
].ebp
= stack
->off
;
118 DEBUG_Printf( DBG_CHN_MESG
, (mode
!= MODE_32
) ? " (bp=%04lx%s)\n" : " (ebp=%08lx%s)\n",
119 stack
->off
, caveat
?caveat
:"" );
123 static BOOL
DEBUG_Frame16(DBG_THREAD
* thread
, DBG_ADDR
*addr
, unsigned int *cs
, int frameno
, int noisy
)
125 unsigned int possible_cs
= 0;
127 void* p
= (void*)DEBUG_ToLinear(addr
);
130 if (!p
) return FALSE
;
132 if (!DEBUG_READ_MEM(p
, &frame
, sizeof(frame
))) {
133 if (noisy
) DEBUG_InvalAddr(addr
);
136 if (!frame
.bp
) return FALSE
;
138 if (frame
.bp
& 1) *cs
= frame
.cs
;
140 /* not explicitly marked as far call,
141 * but check whether it could be anyway */
142 if (((frame
.cs
&7)==7) && (frame
.cs
!= *cs
)) {
145 if (GetThreadSelectorEntry( thread
->handle
, frame
.cs
, &le
) &&
146 (le
.HighWord
.Bits
.Type
& 0x08)) { /* code segment */
147 /* it is very uncommon to push a code segment cs as
148 * a parameter, so this should work in most cases */
149 *cs
= possible_cs
= frame
.cs
;
155 addr
->off
= frame
.bp
& ~1;
156 DEBUG_ForceFrame(addr
, &code
, frameno
, MODE_16
, noisy
,
157 possible_cs
? ", far call assumed" : NULL
);
161 static BOOL
DEBUG_Frame32(DBG_ADDR
*addr
, unsigned int *cs
, int frameno
, int noisy
)
164 void* p
= (void*)DEBUG_ToLinear(addr
);
166 DWORD old_bp
= addr
->off
;
168 if (!p
) return FALSE
;
170 if (!DEBUG_READ_MEM(p
, &frame
, sizeof(frame
))) {
171 if (noisy
) DEBUG_InvalAddr(addr
);
174 if (!frame
.ip
) return FALSE
;
178 addr
->off
= frame
.bp
;
179 DEBUG_ForceFrame(addr
, &code
, frameno
, MODE_32
, noisy
, NULL
);
180 if (addr
->off
== old_bp
) return FALSE
;
186 /***********************************************************************
189 * Display a stack back-trace.
191 void DEBUG_BackTrace(DWORD tid
, BOOL noisy
)
194 DBG_ADDR addr
, sw_addr
, code
, tmp
;
196 int frameno
= 0, is16
, ok
;
197 DWORD next_switch
, cur_switch
, p
;
198 STACK16FRAME frame16
;
199 STACK32FRAME frame32
;
205 int copy_curr_frame
= 0;
206 struct bt_info
* copy_frames
= NULL
;
208 if (noisy
) DEBUG_Printf( DBG_CHN_MESG
, "Backtrace:\n" );
210 if (tid
== DEBUG_CurrTid
)
213 thread
= DEBUG_CurrThread
;
215 if (frames
) DBG_free( frames
);
216 /* frames = (struct bt_info *) DBG_alloc( sizeof(struct bt_info) ); */
220 thread
= DEBUG_GetThread(DEBUG_CurrProcess
, tid
);
224 DEBUG_Printf( DBG_CHN_MESG
, "Unknown thread id (0x%08lx) in current process\n", tid
);
227 memset(&ctx
, 0, sizeof(ctx
));
228 ctx
.ContextFlags
= CONTEXT_CONTROL
| CONTEXT_SEGMENTS
;
230 if ( SuspendThread( thread
->handle
) == -1 ||
231 !GetThreadContext( thread
->handle
, &ctx
))
233 DEBUG_Printf( DBG_CHN_MESG
, "Can't get context for thread id (0x%08lx) in current process\n", tid
);
236 /* need to avoid trashing stack frame for current thread */
237 copy_nframe
= nframe
;
238 copy_frames
= frames
;
239 copy_curr_frame
= curr_frame
;
249 if (DEBUG_IsSelectorSystem(ss
)) ss
= 0;
250 if (DEBUG_IsSelectorSystem(cs
)) cs
= 0;
252 /* first stack frame from registers */
253 switch (DEBUG_GetSelectorType(ss
))
260 DEBUG_ForceFrame( &addr
, &code
, frameno
, MODE_32
, noisy
, NULL
);
261 if (!(code
.seg
|| code
.off
)) {
262 /* trying to execute a null pointer... yuck...
263 * if it was a call to null, the return EIP should be
264 * available at SS:ESP, so let's try to retrieve it */
267 if (DEBUG_READ_MEM((void *)DEBUG_ToLinear(&tmp
), &code
.off
, sizeof(code
.off
))) {
268 DEBUG_ForceFrame( &addr
, &code
, ++frameno
, MODE_32
, noisy
, ", null call assumed" );
276 code
.off
= LOWORD(ctx
.Eip
);
278 addr
.off
= LOWORD(ctx
.Ebp
);
279 DEBUG_ForceFrame( &addr
, &code
, frameno
, MODE_16
, noisy
, NULL
);
283 if (noisy
) DEBUG_Printf( DBG_CHN_MESG
, "Bad segment '%x'\n", ss
);
287 /* cur_switch holds address of curr_stack's field in TEB in debuggee
290 cur_switch
= (DWORD
)thread
->teb
+ OFFSET_OF(TEB
, cur_stack
);
291 if (!DEBUG_READ_MEM((void*)cur_switch
, &next_switch
, sizeof(next_switch
))) {
292 if (noisy
) DEBUG_Printf( DBG_CHN_MESG
, "Can't read TEB:cur_stack\n");
297 if (!DEBUG_READ_MEM((void*)next_switch
, &frame32
, sizeof(STACK32FRAME
))) {
298 if (noisy
) DEBUG_Printf( DBG_CHN_MESG
, "Bad stack frame 0x%08lx\n",
299 (unsigned long)(STACK32FRAME
*)next_switch
);
302 cur_switch
= (DWORD
)frame32
.frame16
;
303 sw_addr
.seg
= SELECTOROF(cur_switch
);
304 sw_addr
.off
= OFFSETOF(cur_switch
);
306 tmp
.seg
= SELECTOROF(next_switch
);
307 tmp
.off
= OFFSETOF(next_switch
);
308 p
= DEBUG_ToLinear(&tmp
);
310 if (!DEBUG_READ_MEM((void*)p
, &frame16
, sizeof(STACK16FRAME
))) {
311 if (noisy
) DEBUG_Printf( DBG_CHN_MESG
, "Bad stack frame 0x%08lx\n",
312 (unsigned long)(STACK16FRAME
*)p
);
315 cur_switch
= (DWORD
)frame16
.frame32
;
317 sw_addr
.off
= cur_switch
;
319 if (!DEBUG_READ_MEM((void*)DEBUG_ToLinear(&sw_addr
), &ch
, sizeof(ch
))) {
320 sw_addr
.seg
= (DWORD
)-1;
321 sw_addr
.off
= (DWORD
)-1;
324 for (ok
= TRUE
; ok
;) {
325 if ((frames
[frameno
].ss
== sw_addr
.seg
) &&
326 sw_addr
.off
&& (frames
[frameno
].ebp
>= sw_addr
.off
))
329 * yes, I know this is confusing, it gave me a headache too */
332 if (!DEBUG_READ_MEM((void*)next_switch
, &frame32
, sizeof(STACK32FRAME
))) {
333 if (noisy
) DEBUG_Printf( DBG_CHN_MESG
, "Bad stack frame 0x%08lx\n",
334 (unsigned long)(STACK32FRAME
*)next_switch
);
339 code
.off
= frame32
.retaddr
;
343 addr
.off
= frame32
.ebp
;
344 DEBUG_ForceFrame( &addr
, &code
, ++frameno
, MODE_32
, noisy
, NULL
);
346 next_switch
= cur_switch
;
347 tmp
.seg
= SELECTOROF(next_switch
);
348 tmp
.off
= OFFSETOF(next_switch
);
349 p
= DEBUG_ToLinear(&tmp
);
351 if (!DEBUG_READ_MEM((void*)p
, &frame16
, sizeof(STACK16FRAME
))) {
352 if (noisy
) DEBUG_Printf( DBG_CHN_MESG
, "Bad stack frame 0x%08lx\n",
353 (unsigned long)(STACK16FRAME
*)p
);
356 cur_switch
= (DWORD
)frame16
.frame32
;
358 sw_addr
.off
= cur_switch
;
362 tmp
.seg
= SELECTOROF(next_switch
);
363 tmp
.off
= OFFSETOF(next_switch
);
364 p
= DEBUG_ToLinear(&tmp
);
366 if (!DEBUG_READ_MEM((void*)p
, &frame16
, sizeof(STACK16FRAME
))) {
367 if (noisy
) DEBUG_Printf( DBG_CHN_MESG
, "Bad stack frame 0x%08lx\n",
368 (unsigned long)(STACK16FRAME
*)p
);
372 code
.seg
= frame16
.cs
;
373 code
.off
= frame16
.ip
;
376 addr
.seg
= SELECTOROF(next_switch
);
377 addr
.off
= frame16
.bp
;
378 DEBUG_ForceFrame( &addr
, &code
, ++frameno
, MODE_16
, noisy
, NULL
);
380 next_switch
= cur_switch
;
381 if (!DEBUG_READ_MEM((void*)next_switch
, &frame32
, sizeof(STACK32FRAME
))) {
382 if (noisy
) DEBUG_Printf( DBG_CHN_MESG
, "Bad stack frame 0x%08lx\n",
383 (unsigned long)(STACK32FRAME
*)next_switch
);
386 cur_switch
= (DWORD
)frame32
.frame16
;
387 sw_addr
.seg
= SELECTOROF(cur_switch
);
388 sw_addr
.off
= OFFSETOF(cur_switch
);
392 if (!DEBUG_READ_MEM((void*)DEBUG_ToLinear(&sw_addr
), &ch
, sizeof(ch
))) {
393 sw_addr
.seg
= (DWORD
)-1;
394 sw_addr
.off
= (DWORD
)-1;
397 /* ordinary stack frame */
398 ok
= is16
? DEBUG_Frame16( thread
, &addr
, &cs
, ++frameno
, noisy
)
399 : DEBUG_Frame32( &addr
, &cs
, ++frameno
, noisy
);
402 if (noisy
) DEBUG_Printf( DBG_CHN_MESG
, "\n" );
404 if (tid
!= DEBUG_CurrTid
)
406 ResumeThread( thread
->handle
);
407 /* restore stack frame for current thread */
408 if (frames
) DBG_free( frames
);
409 frames
= copy_frames
;
410 nframe
= copy_nframe
;
411 curr_frame
= copy_curr_frame
;
417 DEBUG_SetFrame(int newframe
)
422 curr_frame
= newframe
;
424 if( curr_frame
>= nframe
)
426 curr_frame
= nframe
- 1;
434 if( frames
&& frames
[curr_frame
].frame
.list
.sourcefile
!= NULL
)
436 DEBUG_List(&frames
[curr_frame
].frame
.list
, NULL
, 0);
443 #endif /* __i386__ */
447 DEBUG_GetCurrentFrame(struct name_hash
** name
, unsigned int * eip
,
452 * If we don't have a valid backtrace, then just return.
460 * If we don't know what the current function is, then we also have
461 * nothing to report here.
463 if( frames
[curr_frame
].frame
.sym
== NULL
)
468 *name
= frames
[curr_frame
].frame
.sym
;
469 *eip
= frames
[curr_frame
].eip
;
470 *ebp
= frames
[curr_frame
].ebp
;
475 #endif /* __i386__ */