Skip to content

Commit

Permalink
Use the skip list library to manage overwritten instructions in the d…
Browse files Browse the repository at this point in the history
…ebugger

This avoids the quadratic behaviors of the previous array-based
implementation.

Closes: ocaml#9606
  • Loading branch information
xavierleroy committed Jun 4, 2020
1 parent 8473928 commit 7cb9e23
Showing 1 changed file with 24 additions and 50 deletions.
74 changes: 24 additions & 50 deletions runtime/debugger.c
Expand Up @@ -28,6 +28,7 @@
#include "caml/debugger.h"
#include "caml/misc.h"
#include "caml/osdeps.h"
#include "caml/skiplist.h"

int caml_debugger_in_use = 0;
uintnat caml_event_count;
Expand Down Expand Up @@ -95,7 +96,7 @@ static struct channel * dbg_out;/* Output channel on the socket */

static char *dbg_addr = NULL;

static struct ext_table breakpoints_table;
static struct skiplist event_points_table = SKIPLIST_STATIC_INITIALIZER;

static void open_connection(void)
{
Expand Down Expand Up @@ -188,8 +189,6 @@ void caml_debugger_init(void)
if (dbg_addr != NULL) caml_stat_free(dbg_addr);
dbg_addr = address;

caml_ext_table_init(&breakpoints_table, 16);

#ifdef _WIN32
winsock_startup();
(void)atexit(winsock_cleanup);
Expand Down Expand Up @@ -271,38 +270,14 @@ static void safe_output_value(struct channel *chan, value val)
Caml_state->external_raise = saved_external_raise;
}

struct breakpoint {
code_t pc;
opcode_t saved;
};

static struct breakpoint *find_breakpoint(code_t pc)
{
struct breakpoint *bpti;
int i;

for (i = 0; i < breakpoints_table.size; i++) {
bpti = (struct breakpoint *) breakpoints_table.contents[i];
if (bpti->pc == pc)
return bpti;
}

return NULL;
}

static void save_instruction(code_t pc)
{
struct breakpoint *bpt;

if (find_breakpoint(pc) != NULL) {
uintnat saved;
if (caml_skiplist_find(&event_points_table, (uintnat) pc, &saved)) {
/* Already saved. Nothing to do. */
return;
}

bpt = caml_stat_alloc(sizeof(struct breakpoint));
bpt->pc = pc;
bpt->saved = *pc;
caml_ext_table_add(&breakpoints_table, bpt);
caml_skiplist_insert(&event_points_table, (uintnat) pc, *pc);
}

static void set_instruction(code_t pc, opcode_t opcode)
Expand All @@ -313,11 +288,12 @@ static void set_instruction(code_t pc, opcode_t opcode)

static void restore_instruction(code_t pc)
{
struct breakpoint *bpt = find_breakpoint(pc);
CAMLassert (bpt != NULL);

*pc = bpt->saved;
caml_ext_table_remove(&breakpoints_table, bpt);
CAMLunused_start int found; CAMLunused_end
uintnat saved;
found = caml_skiplist_find(&event_points_table, (uintnat) pc, &saved);
CAMLassert(found);
*pc = saved;
caml_skiplist_remove(&event_points_table, (uintnat) pc);
}

static code_t pc_from_pos(int frag, intnat pos)
Expand All @@ -334,17 +310,18 @@ static code_t pc_from_pos(int frag, intnat pos)

opcode_t caml_debugger_saved_instruction(code_t pc)
{
struct breakpoint *bpt = find_breakpoint(pc);
CAMLassert (bpt != NULL);

return bpt->saved;
CAMLunused_start int found; CAMLunused_end
uintnat saved;
found = caml_skiplist_find(&event_points_table, (uintnat) pc, &saved);
CAMLassert(found);
return saved;
}

void caml_debugger_code_unloaded(int index)
{
struct code_fragment *cf;
struct breakpoint *bpti;
int i;
struct skipcell * e, * f;
char * pc;

if (!caml_debugger_in_use) return;

Expand All @@ -353,14 +330,11 @@ void caml_debugger_code_unloaded(int index)

cf = (struct code_fragment *) caml_code_fragments_table.contents[index];

for (i = 0; i < breakpoints_table.size; i++) {
bpti = (struct breakpoint *) breakpoints_table.contents[i];
if ((char*) bpti->pc >= cf->code_start && (char*) bpti->pc < cf->code_end) {
caml_ext_table_remove(&breakpoints_table, bpti);
/* caml_ext_table_remove has shifted the next element in place
of the one we just removed. Decrement i for the next
iteration. */
i--;
for (e = event_points_table.forward[0]; e != NULL; e = f) {
f = e->forward[0];
pc = (char *) e->key;
if (pc >= cf->code_start && pc < cf->code_end) {
caml_skiplist_remove(&event_points_table, (uintnat) pc);
}
}
}
Expand Down Expand Up @@ -580,7 +554,7 @@ void caml_debugger(enum event_kind event, value param)

void caml_debugger_cleanup_fork(void)
{
/* We could remove all of the breakpoints, but closing the connection
/* We could remove all of the event points, but closing the connection
* means that they'll just be skipped anyway. */
close_connection();
caml_debugger_in_use = 0;
Expand Down

0 comments on commit 7cb9e23

Please sign in to comment.