stack.c 15.3 KB
Newer Older
Alexandre Julliard's avatar
Alexandre Julliard committed
1 2 3 4
/*
 * Debugger stack handling
 *
 * Copyright 1995 Alexandre Julliard
Alexandre Julliard's avatar
Alexandre Julliard committed
5
 * Copyright 1996 Eric Youngdale
6
 * Copyright 1999 Ove Kåven
7 8 9 10 11 12 13 14 15 16 17 18 19
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
Alexandre Julliard's avatar
Alexandre Julliard committed
21 22
 */

23
#include "config.h"
24

Alexandre Julliard's avatar
Alexandre Julliard committed
25
#include <stdlib.h>
26
#include <stdio.h>
27

Alexandre Julliard's avatar
Alexandre Julliard committed
28
#include "debugger.h"
29
#include "winbase.h"
30
#include "wine/winbase16.h"
31
#include "tlhelp32.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
32 33

/***********************************************************************
34
 *           stack_info
Alexandre Julliard's avatar
Alexandre Julliard committed
35
 *
36
 * Dump the top of the stack. If len <= 0, a default length is used.
Alexandre Julliard's avatar
Alexandre Julliard committed
37
 */
38
void stack_info(int len)
Alexandre Julliard's avatar
Alexandre Julliard committed
39
{
40 41
    struct dbg_lvalue lvalue;

42 43 44
    if(len <= 0)
        len = 24;

45
    lvalue.cookie = 0;
46
    lvalue.type.id = dbg_itype_segptr;
47
    lvalue.type.module = 0;
48

49
    /* FIXME: we assume stack grows the same way as on i386 */
50 51
    if (!memory_get_current_stack(&lvalue.addr))
        dbg_printf("Bad segment (%d)\n", lvalue.addr.Segment);
Alexandre Julliard's avatar
Alexandre Julliard committed
52

53
    dbg_printf("Stack dump:\n");
54
    switch (lvalue.addr.Mode)
55
    {
56
    case AddrModeFlat: /* 32-bit or 64-bit mode */
57
        memory_examine(&lvalue, len, 'a');
58
        break;
59
    case AddrMode1632: /* 32-bit mode */
60
        memory_examine(&lvalue, len, 'x');
61 62 63
        break;
    case AddrModeReal:  /* 16-bit mode */
    case AddrMode1616:
64
        memory_examine(&lvalue, len, 'w');
65
	break;
Alexandre Julliard's avatar
Alexandre Julliard committed
66 67 68
    }
}

69
static BOOL stack_set_frame_internal(int newframe)
Alexandre Julliard's avatar
Alexandre Julliard committed
70
{
71 72 73 74
    if (newframe >= dbg_curr_thread->num_frames)
        newframe = dbg_curr_thread->num_frames - 1;
    if (newframe < 0)
        newframe = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
75

76 77 78
    if (dbg_curr_thread->curr_frame != newframe)
    {
        IMAGEHLP_STACK_FRAME    ihsf;
79

80 81 82 83 84 85 86
        dbg_curr_thread->curr_frame = newframe;
        stack_get_current_frame(&ihsf);
        SymSetContext(dbg_curr_process->handle, &ihsf, NULL);
    }
    return TRUE;
}

87
static BOOL stack_get_frame(int nf, IMAGEHLP_STACK_FRAME* ihsf)
88
{
89
    memset(ihsf, 0, sizeof(*ihsf));
90
    ihsf->InstructionOffset = dbg_curr_thread->frames[nf].linear_pc;
91 92 93 94 95 96 97 98 99
    /* if we're not the first frame, InstructionOffset is the return address
     * after the call instruction (at least on most processors I know of).
     * However, there are cases where this address is outside of the current function.
     * This happens when the called function is marked <NO RETURN>, in which
     * case the compiler can omit the epilog (gcc 4 does it)
     * Therefore, we decrement InstructionOffset in order to ensure that
     * the considered address is really inside the current function.
     */
    if (nf) ihsf->InstructionOffset--;
100
    ihsf->FrameOffset = dbg_curr_thread->frames[nf].linear_frame;
101
    ihsf->StackOffset = dbg_curr_thread->frames[nf].linear_stack;
102 103 104
    return TRUE;
}

105 106 107 108 109 110
BOOL stack_get_current_frame(IMAGEHLP_STACK_FRAME* ihsf)
{
    /*
     * If we don't have a valid backtrace, then just return.
     */
    if (dbg_curr_thread->frames == NULL) return FALSE;
111
    return stack_get_frame(dbg_curr_thread->curr_frame, ihsf);
112 113
}

114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
BOOL stack_get_register_frame(const struct dbg_internal_var* div, DWORD_PTR** pval)
{
    if (dbg_curr_thread->frames == NULL) return FALSE;
    if (dbg_curr_thread->frames[dbg_curr_thread->curr_frame].is_ctx_valid)
        *pval = (DWORD_PTR*)((char*)&dbg_curr_thread->frames[dbg_curr_thread->curr_frame].context +
                             (DWORD_PTR)div->pval);
    else
    {
        enum be_cpu_addr        kind;

        if (!be_cpu->get_register_info(div->val, &kind)) return FALSE;

        /* reuse some known registers directly out of stackwalk details */
        switch (kind)
        {
        case be_cpu_addr_pc:
            *pval = &dbg_curr_thread->frames[dbg_curr_thread->curr_frame].linear_pc;
            break;
        case be_cpu_addr_stack:
            *pval = &dbg_curr_thread->frames[dbg_curr_thread->curr_frame].linear_stack;
            break;
        case be_cpu_addr_frame:
            *pval = &dbg_curr_thread->frames[dbg_curr_thread->curr_frame].linear_frame;
            break;
        }
    }
    return TRUE;
}

143 144
BOOL stack_set_frame(int newframe)
{
145
    ADDRESS64   addr;
146
    if (!stack_set_frame_internal(newframe)) return FALSE;
147
    addr.Mode = AddrModeFlat;
148
    addr.Offset = (DWORD_PTR)memory_to_linear_addr(&dbg_curr_thread->frames[dbg_curr_thread->curr_frame].addr_pc);
149
    source_list_from_addr(&addr, 0);
150
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
151
}
Alexandre Julliard's avatar
Alexandre Julliard committed
152

153 154 155 156 157 158
/******************************************************************
 *		stack_get_current_symbol
 *
 * Retrieves the symbol information for the current frame element
 */
BOOL stack_get_current_symbol(SYMBOL_INFO* symbol)
Alexandre Julliard's avatar
Alexandre Julliard committed
159
{
160 161
    IMAGEHLP_STACK_FRAME        ihsf;
    DWORD64                     disp;
162

163 164 165
    if (!stack_get_current_frame(&ihsf)) return FALSE;
    return SymFromAddr(dbg_curr_process->handle, ihsf.InstructionOffset,
                       &disp, symbol);
166
}
167

168
static BOOL CALLBACK stack_read_mem(HANDLE hProc, DWORD64 addr, 
169 170
                                    PVOID buffer, DWORD size, PDWORD written)
{
171 172 173
    SIZE_T sz;
    BOOL ret;

174 175
    struct dbg_process* pcs = dbg_get_process_h(hProc);
    if (!pcs) return FALSE;
176 177
    ret = pcs->process_io->read(hProc, (const void*)(DWORD_PTR)addr, buffer,
                                size, &sz);
178 179
    if (written != NULL) *written = sz;
    return ret;
180 181
}

182
/******************************************************************
183
 *		stack_fetch_frames
184
 *
185
 * Do a backtrace on the current thread
186
 */
187
unsigned stack_fetch_frames(const CONTEXT* _ctx)
188
{
189 190
    STACKFRAME64 sf;
    unsigned     nf = 0;
191 192 193
    /* as native stackwalk can modify the context passed to it, simply copy
     * it to avoid any damage
     */
194
    CONTEXT      ctx = *_ctx;
195

196 197 198
    HeapFree(GetProcessHeap(), 0, dbg_curr_thread->frames);
    dbg_curr_thread->frames = NULL;

199
    memset(&sf, 0, sizeof(sf));
200 201 202
    be_cpu->get_addr(dbg_curr_thread->handle, &ctx, be_cpu_addr_frame, &sf.AddrFrame);
    be_cpu->get_addr(dbg_curr_thread->handle, &ctx, be_cpu_addr_pc, &sf.AddrPC);
    be_cpu->get_addr(dbg_curr_thread->handle, &ctx, be_cpu_addr_stack, &sf.AddrStack);
203

204 205 206
    /* don't confuse StackWalk by passing in inconsistent addresses */
    if ((sf.AddrPC.Mode == AddrModeFlat) && (sf.AddrFrame.Mode != AddrModeFlat))
    {
207
        sf.AddrFrame.Offset = (ULONG_PTR)memory_to_linear_addr(&sf.AddrFrame);
208 209 210
        sf.AddrFrame.Mode = AddrModeFlat;
    }

211
    while (StackWalk64(be_cpu->machine, dbg_curr_process->handle,
212
                       dbg_curr_thread->handle, &sf, &ctx, stack_read_mem,
213
                       SymFunctionTableAccess64, SymGetModuleBase64, NULL))
Alexandre Julliard's avatar
Alexandre Julliard committed
214
    {
215 216
        dbg_curr_thread->frames = dbg_heap_realloc(dbg_curr_thread->frames, 
                                                   (nf + 1) * sizeof(dbg_curr_thread->frames[0]));
Alexandre Julliard's avatar
Alexandre Julliard committed
217

218
        dbg_curr_thread->frames[nf].addr_pc      = sf.AddrPC;
219
        dbg_curr_thread->frames[nf].linear_pc    = (DWORD_PTR)memory_to_linear_addr(&sf.AddrPC);
220
        dbg_curr_thread->frames[nf].addr_frame   = sf.AddrFrame;
221
        dbg_curr_thread->frames[nf].linear_frame = (DWORD_PTR)memory_to_linear_addr(&sf.AddrFrame);
222
        dbg_curr_thread->frames[nf].addr_stack   = sf.AddrStack;
223
        dbg_curr_thread->frames[nf].linear_stack = (DWORD_PTR)memory_to_linear_addr(&sf.AddrStack);
224
        dbg_curr_thread->frames[nf].context      = ctx;
225 226 227 228 229 230 231
        /* FIXME: can this heuristic be improved: we declare first context always valid, and next ones
         * if it has been modified by the call to StackWalk...
         */
        dbg_curr_thread->frames[nf].is_ctx_valid =
            (nf == 0 ||
             (dbg_curr_thread->frames[nf - 1].is_ctx_valid &&
              memcmp(&dbg_curr_thread->frames[nf - 1].context, &ctx, sizeof(ctx))));
232
        nf++;
233
        /* we've probably gotten ourselves into an infinite loop so bail */
234 235
        if (nf > 200) break;
    }
236 237 238 239
    dbg_curr_thread->curr_frame = -1;
    dbg_curr_thread->num_frames = nf;
    stack_set_frame_internal(0);
    return nf;
240 241
}

242 243
struct sym_enum
{
244
    DWORD_PTR   frame;
245
    BOOL        first;
246 247
};

248
static BOOL WINAPI sym_enum_cb(PSYMBOL_INFO sym_info, ULONG size, PVOID user)
249
{
250
    struct sym_enum*    se = user;
251

252
    if (sym_info->Flags & SYMFLAG_PARAMETER)
253
    {
254 255
        if (!se->first) dbg_printf(", "); else se->first = FALSE;
        symbol_print_local(sym_info, se->frame, FALSE);
256 257 258 259 260 261 262 263 264
    }
    return TRUE;
}

static void stack_print_addr_and_args(int nf)
{
    char                        buffer[sizeof(SYMBOL_INFO) + 256];
    SYMBOL_INFO*                si = (SYMBOL_INFO*)buffer;
    IMAGEHLP_STACK_FRAME        ihsf;
265
    IMAGEHLP_LINE64             il;
266 267 268 269 270
    IMAGEHLP_MODULE             im;
    DWORD64                     disp64;

    print_bare_address(&dbg_curr_thread->frames[nf].addr_pc);

271
    stack_get_frame(nf, &ihsf);
272 273 274 275 276 277 278 279 280 281 282 283 284 285

    /* grab module where symbol is. If we don't have a module, we cannot print more */
    im.SizeOfStruct = sizeof(im);
    if (!SymGetModuleInfo(dbg_curr_process->handle, ihsf.InstructionOffset, &im))
        return;

    si->SizeOfStruct = sizeof(*si);
    si->MaxNameLen   = 256;
    if (SymFromAddr(dbg_curr_process->handle, ihsf.InstructionOffset, &disp64, si))
    {
        struct sym_enum se;
        DWORD           disp;

        dbg_printf(" %s", si->Name);
286
        if (disp64) dbg_printf("+0x%lx", (DWORD_PTR)disp64);
287 288

        SymSetContext(dbg_curr_process->handle, &ihsf, NULL);
289
        se.first = TRUE;
290
        se.frame = ihsf.FrameOffset;
291
        dbg_printf("(");
292
        SymEnumSymbols(dbg_curr_process->handle, 0, NULL, sym_enum_cb, &se);
293
        dbg_printf(")");
294 295

        il.SizeOfStruct = sizeof(il);
296 297
        if (SymGetLineFromAddr64(dbg_curr_process->handle,
				 ihsf.InstructionOffset, &disp, &il))
298
            dbg_printf(" [%s:%u]", il.FileName, il.LineNumber);
299 300 301 302 303 304
        dbg_printf(" in %s", im.ModuleName);
    }
    else dbg_printf(" in %s (+0x%lx)", 
                    im.ModuleName, (DWORD_PTR)(ihsf.InstructionOffset - im.BaseOfImage));
}

305 306 307
/******************************************************************
 *		backtrace
 *
308
 * Do a backtrace on the current thread
309
 */
310
static void backtrace(void)
311
{
312
    unsigned                    cf = dbg_curr_thread->curr_frame;
313
    IMAGEHLP_STACK_FRAME        ihsf;
314 315

    dbg_printf("Backtrace:\n");
316 317 318
    for (dbg_curr_thread->curr_frame = 0;
         dbg_curr_thread->curr_frame < dbg_curr_thread->num_frames;
         dbg_curr_thread->curr_frame++)
319 320
    {
        dbg_printf("%s%d ", 
321
                   (cf == dbg_curr_thread->curr_frame ? "=>" : "  "),
322
                   dbg_curr_thread->curr_frame);
323
        stack_print_addr_and_args(dbg_curr_thread->curr_frame);
324
        dbg_printf(" (");
325
        print_bare_address(&dbg_curr_thread->frames[dbg_curr_thread->curr_frame].addr_frame);
326
        dbg_printf(")\n");
Alexandre Julliard's avatar
Alexandre Julliard committed
327
    }
328
    /* reset context to current stack frame */
329
    dbg_curr_thread->curr_frame = cf;
330
    if (!dbg_curr_thread->frames) return;
331
    stack_get_frame(dbg_curr_thread->curr_frame, &ihsf);
332
    SymSetContext(dbg_curr_process->handle, &ihsf, NULL);
333 334 335 336 337 338 339 340
}

/******************************************************************
 *		backtrace_tid
 *
 * Do a backtrace on a thread from its process and its identifier
 * (preserves current thread and context information)
 */
341
static void backtrace_tid(struct dbg_process* pcs, DWORD tid)
342 343
{
    struct dbg_thread*  thread = dbg_curr_thread;
Alexandre Julliard's avatar
Alexandre Julliard committed
344

345
    if (!(dbg_curr_thread = dbg_get_thread(pcs, tid)))
346
        dbg_printf("Unknown thread id (%04x) in process (%04x)\n", tid, pcs->pid);
347 348
    else
    {
349
        CONTEXT context;
350 351

        dbg_curr_tid = dbg_curr_thread->tid;
352 353
        memset(&context, 0, sizeof(context));
        context.ContextFlags = CONTEXT_FULL;
354 355
        if (SuspendThread(dbg_curr_thread->handle) != -1)
        {
356
            if (!GetThreadContext(dbg_curr_thread->handle, &context))
357
            {
358
                dbg_printf("Can't get context for thread %04x in current process\n",
359 360
                           tid);
            }
361 362
            else
            {
363
                stack_fetch_frames(&context);
364 365
                backtrace();
            }
366 367
            ResumeThread(dbg_curr_thread->handle);
        }
368
        else dbg_printf("Can't suspend thread %04x in current process\n", tid);
369 370 371 372 373 374 375 376 377 378 379 380 381
    }
    dbg_curr_thread = thread;
    dbg_curr_tid = thread ? thread->tid : 0;
}

/******************************************************************
 *		backtrace_all
 *
 * Do a backtrace on every running thread in the system (except the debugger)
 * (preserves current process information)
 */
static void backtrace_all(void)
{
382
    struct dbg_process* process = dbg_curr_process;
383 384 385
    struct dbg_thread*  thread = dbg_curr_thread;
    CONTEXT             ctx = dbg_context;
    DWORD               cpid = dbg_curr_pid;
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400
    THREADENTRY32       entry;
    HANDLE              snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);

    if (snapshot == INVALID_HANDLE_VALUE)
    {
        dbg_printf("Unable to create toolhelp snapshot\n");
        return;
    }

    entry.dwSize = sizeof(entry);
    if (Thread32First(snapshot, &entry))
    {
        do
        {
            if (entry.th32OwnerProcessID == GetCurrentProcessId()) continue;
401 402
            if (dbg_curr_process && dbg_curr_pid != entry.th32OwnerProcessID &&
                cpid != dbg_curr_pid)
403
                dbg_curr_process->process_io->close_process(dbg_curr_process, FALSE);
404

405 406 407 408 409 410
            if (entry.th32OwnerProcessID == cpid)
            {
                dbg_curr_process = process;
                dbg_curr_pid = cpid;
            }
            else if (entry.th32OwnerProcessID != dbg_curr_pid)
411
            {
412
                if (!dbg_attach_debuggee(entry.th32OwnerProcessID, FALSE))
413
                {
414
                    dbg_printf("\nwarning: could not attach to %04x\n",
415 416 417 418
                               entry.th32OwnerProcessID);
                    continue;
                }
                dbg_curr_pid = dbg_curr_process->pid;
419
                dbg_active_wait_for_first_exception();
420 421
            }

422
            dbg_printf("\nBacktracing for thread %04x in process %04lx (%s):\n",
423 424
                       entry.th32ThreadID, dbg_curr_pid,
                       dbg_W2A(dbg_curr_process->imageName, -1));
425
            backtrace_tid(dbg_curr_process, entry.th32ThreadID);
426 427 428
        }
        while (Thread32Next(snapshot, &entry));

429
        if (dbg_curr_process && cpid != dbg_curr_pid)
430
            dbg_curr_process->process_io->close_process(dbg_curr_process, FALSE);
431 432
    }
    CloseHandle(snapshot);
433
    dbg_curr_process = process;
434 435 436 437
    dbg_curr_pid = cpid;
    dbg_curr_thread = thread;
    dbg_curr_tid = thread ? thread->tid : 0;
    dbg_context = ctx;
438 439
}

440
void stack_backtrace(DWORD tid)
441 442 443 444 445 446 447 448 449 450 451 452
{
    /* backtrace every thread in every process except the debugger itself,
     * invoking via "bt all"
     */
    if (tid == -1) return backtrace_all();

    if (!dbg_curr_process) 
    {
        dbg_printf("You must be attached to a process to run this command.\n");
        return;
    }
    
453
    if (tid == dbg_curr_tid)
Alexandre Julliard's avatar
Alexandre Julliard committed
454
    {
455
        backtrace();
Alexandre Julliard's avatar
Alexandre Julliard committed
456
    }
457
    else
Alexandre Julliard's avatar
Alexandre Julliard committed
458
    {
459
        backtrace_tid(dbg_curr_process, tid);
Alexandre Julliard's avatar
Alexandre Julliard committed
460 461
    }
}