winedbg: Hardware breakpoints

- implemented hardware assisted breakpoints (new 'hbreak' command
  which behaves just as 'break' command)
- small improvements to break handling (saving hit xpoint across
  exception handling)
- fixed 'cont N' command for watchpoints
This commit is contained in:
Eric Pouech 2006-01-27 16:17:22 +01:00 committed by Alexandre Julliard
parent 8b0feb253b
commit 6ab9b23526
5 changed files with 88 additions and 50 deletions

View File

@ -26,6 +26,16 @@
WINE_DEFAULT_DEBUG_CHANNEL(winedbg);
static int is_xpoint_break(int bpnum)
{
int type = dbg_curr_process->bp[bpnum].xpoint_type;
if (type == be_xpoint_break || type == be_xpoint_watch_exec) return TRUE;
if (type == be_xpoint_watch_read || type == be_xpoint_watch_write) return FALSE;
RaiseException(DEBUG_STATUS_INTERNAL_ERROR, 0, 0, NULL);
return 0; /* never reached */
}
/***********************************************************************
* break_set_xpoints
*
@ -46,7 +56,7 @@ void break_set_xpoints(BOOL set)
{
if (!bp[i].refcount || !bp[i].enabled) continue;
if (bp[i].xpoint_type == be_xpoint_break)
if (is_xpoint_break(i))
size = 0;
else
size = bp[i].w.len + 1;
@ -150,13 +160,14 @@ static BOOL get_watched_value(int num, LPDWORD val)
*
* Add a breakpoint.
*/
BOOL break_add_break(const ADDRESS* addr, BOOL verbose)
BOOL break_add_break(const ADDRESS* addr, BOOL verbose, BOOL swbp)
{
int num;
BYTE ch;
struct dbg_breakpoint* bp = dbg_curr_process->bp;
int type = swbp ? be_xpoint_break : be_xpoint_watch_exec;
if ((num = find_xpoint(addr, be_xpoint_break)) >= 1)
if ((num = find_xpoint(addr, type)) >= 1)
{
bp[num].refcount++;
dbg_printf("Breakpoint %d at ", num);
@ -176,7 +187,7 @@ BOOL break_add_break(const ADDRESS* addr, BOOL verbose)
return FALSE;
}
if ((num = init_xpoint(be_xpoint_break, addr)) == -1)
if ((num = init_xpoint(type, addr)) == -1)
return FALSE;
dbg_printf("Breakpoint %d at ", num);
@ -191,13 +202,13 @@ BOOL break_add_break(const ADDRESS* addr, BOOL verbose)
*
* Add a breakpoint.
*/
BOOL break_add_break_from_lvalue(const struct dbg_lvalue* lvalue)
BOOL break_add_break_from_lvalue(const struct dbg_lvalue* lvalue, BOOL swbp)
{
ADDRESS addr;
types_extract_as_address(lvalue, &addr);
if (!break_add_break(&addr, TRUE))
if (!break_add_break(&addr, TRUE, swbp))
{
if (!DBG_IVAR(CanDeferOnBPByAddr))
{
@ -210,8 +221,9 @@ BOOL break_add_break_from_lvalue(const struct dbg_lvalue* lvalue)
dbg_heap_realloc(dbg_curr_process->delayed_bp,
sizeof(struct dbg_delayed_bp) * ++dbg_curr_process->num_delayed_bp);
dbg_curr_process->delayed_bp[dbg_curr_process->num_delayed_bp - 1].is_symbol = FALSE;
dbg_curr_process->delayed_bp[dbg_curr_process->num_delayed_bp - 1].u.addr = addr;
dbg_curr_process->delayed_bp[dbg_curr_process->num_delayed_bp - 1].is_symbol = FALSE;
dbg_curr_process->delayed_bp[dbg_curr_process->num_delayed_bp - 1].software_bp = swbp;
dbg_curr_process->delayed_bp[dbg_curr_process->num_delayed_bp - 1].u.addr = addr;
return TRUE;
}
return FALSE;
@ -222,7 +234,7 @@ BOOL break_add_break_from_lvalue(const struct dbg_lvalue* lvalue)
*
* Add a breakpoint from a function name (and eventually a line #)
*/
void break_add_break_from_id(const char *name, int lineno)
void break_add_break_from_id(const char *name, int lineno, BOOL swbp)
{
struct dbg_lvalue lvalue;
int i;
@ -230,7 +242,7 @@ void break_add_break_from_id(const char *name, int lineno)
switch (symbol_get_lvalue(name, lineno, &lvalue, TRUE))
{
case sglv_found:
break_add_break(&lvalue.addr, TRUE);
break_add_break(&lvalue.addr, TRUE, swbp);
return;
case sglv_unknown:
break;
@ -249,8 +261,9 @@ void break_add_break_from_id(const char *name, int lineno)
dbg_curr_process->delayed_bp = dbg_heap_realloc(dbg_curr_process->delayed_bp,
sizeof(struct dbg_delayed_bp) * ++dbg_curr_process->num_delayed_bp);
dbg_curr_process->delayed_bp[dbg_curr_process->num_delayed_bp - 1].is_symbol = TRUE;
dbg_curr_process->delayed_bp[dbg_curr_process->num_delayed_bp - 1].u.symbol.name = strcpy(HeapAlloc(GetProcessHeap(), 0, strlen(name) + 1), name);
dbg_curr_process->delayed_bp[dbg_curr_process->num_delayed_bp - 1].is_symbol = TRUE;
dbg_curr_process->delayed_bp[dbg_curr_process->num_delayed_bp - 1].software_bp = swbp;
dbg_curr_process->delayed_bp[dbg_curr_process->num_delayed_bp - 1].u.symbol.name = strcpy(HeapAlloc(GetProcessHeap(), 0, strlen(name) + 1), name);
dbg_curr_process->delayed_bp[dbg_curr_process->num_delayed_bp - 1].u.symbol.lineno = lineno;
}
@ -278,7 +291,7 @@ static BOOL CALLBACK line_cb(SRCCODEINFO* sci, void* user)
*
* Add a breakpoint from a line number in current file
*/
void break_add_break_from_lineno(int lineno)
void break_add_break_from_lineno(int lineno, BOOL swbp)
{
struct cb_break_lineno bkln;
@ -310,7 +323,7 @@ void break_add_break_from_lineno(int lineno)
}
}
break_add_break(&bkln.addr, TRUE);
break_add_break(&bkln.addr, TRUE, swbp);
}
/***********************************************************************
@ -343,7 +356,7 @@ void break_check_delayed_bp(void)
WINE_TRACE("\t'%s' @ %d\n",
dbp[i].u.symbol.name, dbp[i].u.symbol.lineno);
if (break_add_break(&lvalue.addr, FALSE))
if (break_add_break(&lvalue.addr, FALSE, dbp[i].software_bp))
memmove(&dbp[i], &dbp[i+1], (--dbg_curr_process->num_delayed_bp - i) * sizeof(*dbp));
}
}
@ -543,7 +556,7 @@ static int find_triggered_watch(LPDWORD oldval)
{
DWORD val = 0;
if (bp[i].refcount && bp[i].enabled && bp[i].xpoint_type != be_xpoint_break &&
if (bp[i].refcount && bp[i].enabled && !is_xpoint_break(i) &&
(be_cpu->is_watchpoint_set(&dbg_context, bp[i].info)))
{
be_cpu->clear_watchpoint(&dbg_context, bp[i].info);
@ -567,7 +580,7 @@ static int find_triggered_watch(LPDWORD oldval)
{
DWORD val = 0;
if (bp[i].refcount && bp[i].enabled && bp[i].xpoint_type != be_xpoint_break &&
if (bp[i].refcount && bp[i].enabled && !is_xpoint_break(i) &&
get_watched_value(i, &val))
{
*oldval = bp[i].w.oldval;
@ -602,7 +615,7 @@ void break_info(void)
{
if (bp[i].refcount)
{
if (bp[i].xpoint_type == be_xpoint_break) nbp++; else nwp++;
if (is_xpoint_break(i)) nbp++; else nwp++;
}
}
@ -611,11 +624,12 @@ void break_info(void)
dbg_printf("Breakpoints:\n");
for (i = 1; i < dbg_curr_process->next_bp; i++)
{
if (!bp[i].refcount || bp[i].xpoint_type != be_xpoint_break)
if (!bp[i].refcount || !is_xpoint_break(i))
continue;
dbg_printf("%d: %c ", i, bp[i].enabled ? 'y' : 'n');
print_address(&bp[i].addr, TRUE);
dbg_printf(" (%u)\n", bp[i].refcount);
dbg_printf(" (%u)%s\n", bp[i].refcount,
bp[i].xpoint_type == be_xpoint_watch_exec ? " (hardware assisted)" : "");
if (bp[i].condition != NULL)
{
dbg_printf("\t\tstop when ");
@ -630,7 +644,7 @@ void break_info(void)
dbg_printf("Watchpoints:\n");
for (i = 1; i < dbg_curr_process->next_bp; i++)
{
if (!bp[i].refcount || bp[i].xpoint_type == be_xpoint_break)
if (!bp[i].refcount || is_xpoint_break(i))
continue;
dbg_printf("%d: %c ", i, bp[i].enabled ? 'y' : 'n');
print_address(&bp[i].addr, TRUE);
@ -709,9 +723,7 @@ static BOOL should_stop(int bpnum)
*/
BOOL break_should_continue(ADDRESS* addr, DWORD code, int* count, BOOL* is_break)
{
int bpnum;
DWORD oldval = 0;
int wpnum;
enum dbg_exec_mode mode = dbg_curr_thread->exec_mode;
*is_break = FALSE;
@ -719,32 +731,47 @@ BOOL break_should_continue(ADDRESS* addr, DWORD code, int* count, BOOL* is_break
if (code == EXCEPTION_BREAKPOINT)
addr->Offset += be_cpu->adjust_pc_for_break(&dbg_context, TRUE);
bpnum = find_xpoint(addr, be_xpoint_break);
dbg_curr_process->bp[0].enabled = FALSE; /* disable the step-over breakpoint */
if (bpnum > 0)
dbg_curr_thread->stopped_xpoint = find_xpoint(addr, be_xpoint_break);
if (dbg_curr_thread->stopped_xpoint > 0)
{
if (!should_stop(bpnum)) return TRUE;
if (!should_stop(dbg_curr_thread->stopped_xpoint)) return TRUE;
dbg_printf("Stopped on breakpoint %d at ", bpnum);
print_address(&dbg_curr_process->bp[bpnum].addr, TRUE);
dbg_printf("Stopped on breakpoint %d at ", dbg_curr_thread->stopped_xpoint);
print_address(&dbg_curr_process->bp[dbg_curr_thread->stopped_xpoint].addr, TRUE);
dbg_printf("\n");
return FALSE;
}
wpnum = find_triggered_watch(&oldval);
if (wpnum > 0)
dbg_curr_thread->stopped_xpoint = find_xpoint(addr, be_xpoint_watch_exec);
if (dbg_curr_thread->stopped_xpoint > 0)
{
/* If not single-stepping, do not back up over the break instruction */
if (code == EXCEPTION_BREAKPOINT)
addr->Offset += be_cpu->adjust_pc_for_break(&dbg_context, FALSE);
if (!should_stop(wpnum)) return TRUE;
if (!should_stop(dbg_curr_thread->stopped_xpoint)) return TRUE;
dbg_printf("Stopped on watchpoint %d at ", wpnum);
dbg_printf("Stopped on breakpoint %d at ", dbg_curr_thread->stopped_xpoint);
print_address(&dbg_curr_process->bp[dbg_curr_thread->stopped_xpoint].addr, TRUE);
dbg_printf("\n");
return FALSE;
}
dbg_curr_thread->stopped_xpoint = find_triggered_watch(&oldval);
if (dbg_curr_thread->stopped_xpoint > 0)
{
/* If not single-stepping, do not back up over the break instruction */
if (code == EXCEPTION_BREAKPOINT)
addr->Offset += be_cpu->adjust_pc_for_break(&dbg_context, FALSE);
if (!should_stop(dbg_curr_thread->stopped_xpoint)) return TRUE;
dbg_printf("Stopped on watchpoint %d at ", dbg_curr_thread->stopped_xpoint);
print_address(addr, TRUE);
dbg_printf(" values: old=%lu new=%lu\n",
oldval, dbg_curr_process->bp[wpnum].w.oldval);
oldval, dbg_curr_process->bp[dbg_curr_thread->stopped_xpoint].w.oldval);
return FALSE;
}
@ -777,7 +804,7 @@ BOOL break_should_continue(ADDRESS* addr, DWORD code, int* count, BOOL* is_break
* either we must have encountered a break insn in the Windows program
* or someone is trying to stop us
*/
if (bpnum == -1 && code == EXCEPTION_BREAKPOINT)
if (dbg_curr_thread->stopped_xpoint == -1 && code == EXCEPTION_BREAKPOINT)
{
*is_break = TRUE;
addr->Offset += be_cpu->adjust_pc_for_break(&dbg_context, FALSE);
@ -808,7 +835,6 @@ void break_suspend_execution(void)
void break_restart_execution(int count)
{
ADDRESS addr;
int bp;
enum dbg_line_status status;
enum dbg_exec_mode mode, ret_mode;
ADDRESS callee;
@ -823,17 +849,19 @@ void break_restart_execution(int count)
*/
ret_mode = mode = dbg_curr_thread->exec_mode;
bp = find_xpoint(&addr, be_xpoint_break);
if (bp != -1 && bp != 0)
/* we've stopped on a xpoint (other than step over) */
if (dbg_curr_thread->stopped_xpoint > 0)
{
/*
* If we have set a new value, then save it in the BP number.
*/
if (count != 0 && mode == dbg_exec_cont)
{
dbg_curr_process->bp[bp].skipcount = count;
dbg_curr_process->bp[dbg_curr_thread->stopped_xpoint].skipcount = count;
}
mode = dbg_exec_step_into_insn; /* If there's a breakpoint, skip it */
/* If we've stopped on a breakpoint, single step over it (, then run) */
if (is_xpoint_break(dbg_curr_thread->stopped_xpoint))
mode = dbg_exec_step_into_insn;
}
else if (mode == dbg_exec_cont && count > 1)
{

View File

@ -50,7 +50,8 @@ int yyerror(const char*);
}
%token tCONT tPASS tSTEP tLIST tNEXT tQUIT tHELP tBACKTRACE tALL tINFO tUP tDOWN
%token tENABLE tDISABLE tBREAK tWATCH tDELETE tSET tMODE tPRINT tEXAM tABORT tVM86
%token tENABLE tDISABLE tBREAK tHBREAK tWATCH tDELETE tSET tMODE tPRINT tEXAM
%token tABORT tVM86
%token tCLASS tMAPS tSTACK tSEGMENTS tSYMBOL tREGS tWND tQUEUE tLOCAL tEXCEPTION
%token tPROCESS tTHREAD tMODREF tEOL tEOF
%token tFRAME tSHARE tCOND tDISPLAY tUNDISPLAY tDISASSEMBLE
@ -215,11 +216,16 @@ print_command:
;
break_command:
tBREAK '*' expr_lvalue { break_add_break_from_lvalue(&$3); }
| tBREAK identifier { break_add_break_from_id($2, -1); }
| tBREAK identifier ':' tNUM { break_add_break_from_id($2, $4); }
| tBREAK tNUM { break_add_break_from_lineno($2); }
| tBREAK { break_add_break_from_lineno(-1); }
tBREAK '*' expr_lvalue { break_add_break_from_lvalue(&$3, TRUE); }
| tBREAK identifier { break_add_break_from_id($2, -1, TRUE); }
| tBREAK identifier ':' tNUM { break_add_break_from_id($2, $4, TRUE); }
| tBREAK tNUM { break_add_break_from_lineno($2, TRUE); }
| tBREAK { break_add_break_from_lineno(-1, TRUE); }
| tHBREAK '*' expr_lvalue { break_add_break_from_lvalue(&$3, FALSE); }
| tHBREAK identifier { break_add_break_from_id($2, -1, FALSE); }
| tHBREAK identifier ':' tNUM { break_add_break_from_id($2, $4, FALSE); }
| tHBREAK tNUM { break_add_break_from_lineno($2, FALSE); }
| tHBREAK { break_add_break_from_lineno(-1, FALSE); }
| tENABLE tNUM { break_enable_xpoint($2, TRUE); }
| tENABLE tBREAK tNUM { break_enable_xpoint($3, TRUE); }
| tDISABLE tNUM { break_enable_xpoint($2, FALSE); }

View File

@ -173,6 +173,7 @@ STRING \"[^\n"]+\"
<INITIAL>symbolfile|symbols|symbol|sf { BEGIN(PATH_EXPECTED); return tSYMBOLFILE; }
<INITIAL,INFO_CMD,BD_CMD>break|brea|bre|br|b { BEGIN(NOCMD); return tBREAK; }
<INITIAL,INFO_CMD,BD_CMD>hbreak|hbrea|hbre|hbr|hb { BEGIN(NOCMD); return tHBREAK; }
<INITIAL>watch|watc|wat { BEGIN(NOCMD); return tWATCH; }
<INITIAL>whatis|whati|what { BEGIN(NOCMD); return tWHATIS; }
<INITIAL,NOPROCESS>run|ru|r { BEGIN(ASTRING_EXPECTED); return tRUN;}

View File

@ -165,6 +165,7 @@ struct dbg_thread
enum dbg_exec_mode exec_mode; /* mode the thread is run (step/run...) */
int exec_count; /* count of mode operations */
ADDRESS_MODE addr_mode; /* mode */
int stopped_xpoint; /* xpoint on which the thread has stopped (-1 if none) */
struct dbg_breakpoint step_over_bp;
char name[9];
struct dbg_thread* next;
@ -183,6 +184,7 @@ struct dbg_thread
struct dbg_delayed_bp
{
BOOL is_symbol;
BOOL software_bp;
union
{
struct
@ -257,10 +259,10 @@ struct type_expr_t
/* break.c */
extern void break_set_xpoints(BOOL set);
extern BOOL break_add_break(const ADDRESS* addr, BOOL verbose);
extern BOOL break_add_break_from_lvalue(const struct dbg_lvalue* value);
extern void break_add_break_from_id(const char* name, int lineno);
extern void break_add_break_from_lineno(int lineno);
extern BOOL break_add_break(const ADDRESS* addr, BOOL verbose, BOOL swbp);
extern BOOL break_add_break_from_lvalue(const struct dbg_lvalue* value, BOOL swbp);
extern void break_add_break_from_id(const char* name, int lineno, BOOL swbp);
extern void break_add_break_from_lineno(int lineno, BOOL swbp);
extern void break_add_watch_from_lvalue(const struct dbg_lvalue* lvalue);
extern void break_add_watch_from_id(const char* name);
extern void break_check_delayed_bp(void);

View File

@ -392,6 +392,7 @@ struct dbg_thread* dbg_add_thread(struct dbg_process* p, DWORD tid,
t->exec_count = 0;
t->step_over_bp.enabled = FALSE;
t->step_over_bp.refcount = 0;
t->stopped_xpoint = -1;
t->in_exception = FALSE;
t->frames = NULL;
t->num_frames = 0;
@ -420,7 +421,7 @@ static void dbg_init_current_thread(void* start)
break_set_xpoints(FALSE);
addr.Mode = AddrModeFlat;
addr.Offset = (DWORD)start;
break_add_break(&addr, TRUE);
break_add_break(&addr, TRUE, TRUE);
break_set_xpoints(TRUE);
}
}