Rename atomic singlestep to software singlestep
- The old name came from PPC implementation, where the feature was used for
skipping atomic instructions. But this is more useful, was reused for
fully software singlestepping on MIPS, and will be similarly used for ARM.
So rename the interface, and make the prototype more explicit.
- Also document the method in backend.h.
- And convert several void*'s to arch_addr_t's while there.
diff --git a/backend.h b/backend.h
index 5cd6c72..5656a2d 100644
--- a/backend.h
+++ b/backend.h
@@ -1,6 +1,6 @@
/*
* This file is part of ltrace.
- * Copyright (C) 2012 Petr Machata, Red Hat Inc.
+ * Copyright (C) 2012,2013 Petr Machata, Red Hat Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -340,4 +340,34 @@
* implemented: arch_fetch_param_pack_start,
* arch_fetch_param_pack_end. See fetch.h for details. */
+enum sw_singlestep_status {
+ SWS_FAIL,
+ SWS_OK,
+ SWS_HW,
+};
+struct sw_singlestep_data;
+
+/* The following callback has to be implemented in backend if arch.h
+ * defines ARCH_HAVE_SW_SINGLESTEP.
+ *
+ * This is called before the OS backend requests hardware singlestep.
+ * arch_sw_singlestep should consider whether a singlestep needs to be
+ * done in software. If not, it returns SWS_HW. Otherwise it needs
+ * to add one or several breakpoints by calling ADD_CB. When it is
+ * done, it continues the process as appropriate, and answers either
+ * SWS_OK, or SWS_FAIL, depending on how it went.
+ *
+ * PROC is the process that should perform the singlestep, BP the
+ * breakpoint that we are singlestepping over. ADD_CB is a callback
+ * to request adding breakpoints that should trap the process after
+ * it's continued. The arguments to ADD_CB are the address where the
+ * breakpoint should be added, and DATA. ADD_CB returns 0 on success
+ * or a negative value on failure. It is expected that
+ * arch_sw_singlestep returns SWS_FAIL if ADD_CB returns error. */
+enum sw_singlestep_status arch_sw_singlestep(struct process *proc,
+ struct breakpoint *bp,
+ int (*add_cb)(arch_addr_t addr,
+ struct sw_singlestep_data *),
+ struct sw_singlestep_data *data);
+
#endif /* BACKEND_H */
diff --git a/sysdeps/linux-gnu/mipsel/arch.h b/sysdeps/linux-gnu/mipsel/arch.h
index 684b546..ba1220d 100644
--- a/sysdeps/linux-gnu/mipsel/arch.h
+++ b/sysdeps/linux-gnu/mipsel/arch.h
@@ -1,5 +1,6 @@
/*
* This file is part of ltrace.
+ * Copyright (C) 2013 Petr Machata
* Copyright (C) 2006 Eric Vaitl
*
* This program is free software; you can redistribute it and/or
@@ -43,7 +44,7 @@
#define ARCH_HAVE_GET_SYMINFO
#define ARCH_HAVE_DYNLINK_DONE
#define ARCH_HAVE_ADD_PLT_ENTRY
-#define ARCH_HAVE_ATOMIC_SINGLESTEP
+#define ARCH_HAVE_SW_SINGLESTEP
#define ARCH_HAVE_SYMBOL_RET
#define ARCH_HAVE_LIBRARY_SYMBOL_DATA
diff --git a/sysdeps/linux-gnu/mipsel/trace.c b/sysdeps/linux-gnu/mipsel/trace.c
index 7fa3ed4..f553166 100644
--- a/sysdeps/linux-gnu/mipsel/trace.c
+++ b/sysdeps/linux-gnu/mipsel/trace.c
@@ -1,5 +1,6 @@
/*
* This file is part of ltrace.
+ * Copyright (C) 2013 Petr Machata, Red Hat Inc.
* Copyright (C) 2012 Edgar E. Iglesias, Axis Communications
* Copyright (C) 2010 Arnaud Patard, Mandriva SA
* Copyright (C) 2008,2009 Juan Cespedes
@@ -263,10 +264,10 @@
return 0;
}
-int
-arch_atomic_singlestep(struct process *proc, struct breakpoint *sbp,
- int (*add_cb)(void *addr, void *data),
- void *add_cb_data)
+enum sw_singlestep_status
+arch_sw_singlestep(struct process *proc, struct breakpoint *bp,
+ int (*add_cb)(arch_addr_t, struct sw_singlestep_data *),
+ struct sw_singlestep_data *add_cb_data)
{
uint32_t pc = (uint32_t) get_instruction_pointer(proc);
uint32_t newpcs[2];
@@ -283,11 +284,11 @@
}
if (add_cb(baddr, add_cb_data) < 0)
- return -1;
+ return SWS_FAIL;
}
ptrace(PTRACE_SYSCALL, proc->pid, 0, 0);
- return 0;
+ return SWS_OK;
}
/**
diff --git a/sysdeps/linux-gnu/ppc/arch.h b/sysdeps/linux-gnu/ppc/arch.h
index fb8768a..3b903ee 100644
--- a/sysdeps/linux-gnu/ppc/arch.h
+++ b/sysdeps/linux-gnu/ppc/arch.h
@@ -1,6 +1,6 @@
/*
* This file is part of ltrace.
- * Copyright (C) 2012 Petr Machata
+ * Copyright (C) 2012,2013 Petr Machata
* Copyright (C) 2006 Paul Gilliam
* Copyright (C) 2002,2004 Juan Cespedes
*
@@ -37,7 +37,7 @@
#define ARCH_SUPPORTS_OPD
#endif
-#define ARCH_HAVE_ATOMIC_SINGLESTEP
+#define ARCH_HAVE_SW_SINGLESTEP
#define ARCH_HAVE_ADD_PLT_ENTRY
#define ARCH_HAVE_TRANSLATE_ADDRESS
#define ARCH_HAVE_DYNLINK_DONE
diff --git a/sysdeps/linux-gnu/ppc/trace.c b/sysdeps/linux-gnu/ppc/trace.c
index d1c28e8..396ba3d 100644
--- a/sysdeps/linux-gnu/ppc/trace.c
+++ b/sysdeps/linux-gnu/ppc/trace.c
@@ -1,6 +1,6 @@
/*
* This file is part of ltrace.
- * Copyright (C) 2010,2012 Petr Machata, Red Hat Inc.
+ * Copyright (C) 2010,2012,2013 Petr Machata, Red Hat Inc.
* Copyright (C) 2011 Andreas Schwab
* Copyright (C) 2002,2004,2008,2009 Juan Cespedes
* Copyright (C) 2008 Luis Machado, IBM Corporation
@@ -89,15 +89,15 @@
/* In plt.h. XXX make this official interface. */
int read_target_4(struct process *proc, arch_addr_t addr, uint32_t *lp);
-int
-arch_atomic_singlestep(struct process *proc, struct breakpoint *sbp,
- int (*add_cb)(void *addr, void *data),
- void *add_cb_data)
+enum sw_singlestep_status
+arch_sw_singlestep(struct process *proc, struct breakpoint *sbp,
+ int (*add_cb)(arch_addr_t, struct sw_singlestep_data *),
+ struct sw_singlestep_data *add_cb_data)
{
arch_addr_t ip = get_instruction_pointer(proc);
struct breakpoint *other = address2bpstruct(proc->leader, ip);
- debug(1, "arch_atomic_singlestep pid=%d addr=%p %s(%p)",
+ debug(1, "arch_sw_singlestep pid=%d addr=%p %s(%p)",
proc->pid, ip, breakpoint_name(sbp), sbp->addr);
/* If the original instruction was lwarx/ldarx, we can't
@@ -112,12 +112,12 @@
} else if (read_target_4(proc, ip, &u.insn) < 0) {
fprintf(stderr, "couldn't read instruction at IP %p\n", ip);
/* Do the normal singlestep. */
- return 1;
+ return SWS_HW;
}
if ((u.insn & LWARX_MASK) != LWARX_INSTRUCTION
&& (u.insn & LWARX_MASK) != LDARX_INSTRUCTION)
- return 1;
+ return SWS_HW;
debug(1, "singlestep over atomic block at %p", ip);
@@ -127,7 +127,7 @@
addr += 4;
unsigned long l = ptrace(PTRACE_PEEKTEXT, proc->pid, addr, 0);
if (l == (unsigned long)-1 && errno)
- return -1;
+ return SWS_FAIL;
uint32_t insn;
#ifdef __powerpc64__
insn = l >> 32;
@@ -142,7 +142,7 @@
debug(1, "pid=%d, branch in atomic block from %p to %p",
proc->pid, addr, branch_addr);
if (add_cb(branch_addr, add_cb_data) < 0)
- return -1;
+ return SWS_FAIL;
}
/* Assume that the atomic sequence ends with a
@@ -159,18 +159,18 @@
if (insn_count > 16) {
fprintf(stderr, "[%d] couldn't find end of atomic block"
" at %p\n", proc->pid, ip);
- return -1;
+ return SWS_FAIL;
}
}
/* Put the breakpoint to the next instruction. */
addr += 4;
if (add_cb(addr, add_cb_data) < 0)
- return -1;
+ return SWS_FAIL;
debug(1, "PTRACE_CONT");
ptrace(PTRACE_CONT, proc->pid, 0, 0);
- return 0;
+ return SWS_OK;
}
size_t
diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c
index cf72bd6..fe1f960 100644
--- a/sysdeps/linux-gnu/trace.c
+++ b/sysdeps/linux-gnu/trace.c
@@ -1,6 +1,6 @@
/*
* This file is part of ltrace.
- * Copyright (C) 2007,2011,2012 Petr Machata, Red Hat Inc.
+ * Copyright (C) 2007,2011,2012,2013 Petr Machata, Red Hat Inc.
* Copyright (C) 2010 Joe Damato
* Copyright (C) 1998,2002,2003,2004,2008,2009 Juan Cespedes
* Copyright (C) 2006 Ian Wienand
@@ -540,19 +540,13 @@
return 1;
}
-/* The protocol is: 0 for success, negative for failure, positive if
- * default singlestep is to be used. */
-int arch_atomic_singlestep(struct process *proc, struct breakpoint *sbp,
- int (*add_cb)(void *addr, void *data),
- void *add_cb_data);
-
-#ifndef ARCH_HAVE_ATOMIC_SINGLESTEP
-int
-arch_atomic_singlestep(struct process *proc, struct breakpoint *sbp,
- int (*add_cb)(void *addr, void *data),
- void *add_cb_data)
+#ifndef ARCH_HAVE_SW_SINGLESTEP
+enum sw_singlestep_status
+arch_sw_singlestep(struct process *proc, struct breakpoint *bp,
+ int (*add_cb)(arch_addr_t, struct sw_singlestep_data *),
+ struct sw_singlestep_data *data)
{
- return 1;
+ return SWS_HW;
}
#endif
@@ -560,43 +554,46 @@
Event *event);
static void
-remove_atomic_breakpoints(struct process *proc)
+remove_sw_breakpoints(struct process *proc)
{
struct process_stopping_handler *self
= (void *)proc->leader->event_handler;
assert(self != NULL);
assert(self->super.on_event == process_stopping_on_event);
- int ct = sizeof(self->atomic_skip_bp_addrs)
- / sizeof(*self->atomic_skip_bp_addrs);
+ int ct = sizeof(self->sws_bp_addrs) / sizeof(*self->sws_bp_addrs);
int i;
for (i = 0; i < ct; ++i)
- if (self->atomic_skip_bp_addrs[i] != 0) {
- delete_breakpoint(proc, self->atomic_skip_bp_addrs[i]);
- self->atomic_skip_bp_addrs[i] = 0;
+ if (self->sws_bp_addrs[i] != 0) {
+ delete_breakpoint(proc, self->sws_bp_addrs[i]);
+ self->sws_bp_addrs[i] = 0;
}
}
static void
-atomic_singlestep_bp_on_hit(struct breakpoint *bp, struct process *proc)
+sw_singlestep_bp_on_hit(struct breakpoint *bp, struct process *proc)
{
- remove_atomic_breakpoints(proc);
+ remove_sw_breakpoints(proc);
}
+struct sw_singlestep_data {
+ struct process_stopping_handler *self;
+};
+
static int
-atomic_singlestep_add_bp(void *addr, void *data)
+sw_singlestep_add_bp(arch_addr_t addr, struct sw_singlestep_data *data)
{
- struct process_stopping_handler *self = data;
+ struct process_stopping_handler *self = data->self;
struct process *proc = self->task_enabling_breakpoint;
- int ct = sizeof(self->atomic_skip_bp_addrs)
- / sizeof(*self->atomic_skip_bp_addrs);
+ int ct = sizeof(self->sws_bp_addrs)
+ / sizeof(*self->sws_bp_addrs);
int i;
for (i = 0; i < ct; ++i)
- if (self->atomic_skip_bp_addrs[i] == 0) {
- self->atomic_skip_bp_addrs[i] = addr;
+ if (self->sws_bp_addrs[i] == 0) {
+ self->sws_bp_addrs[i] = addr;
static struct bp_callbacks cbs = {
- .on_hit = atomic_singlestep_bp_on_hit,
+ .on_hit = sw_singlestep_bp_on_hit,
};
struct breakpoint *bp
= insert_breakpoint(proc, addr, NULL);
@@ -604,7 +601,7 @@
return 0;
}
- assert(!"Too many atomic singlestep breakpoints!");
+ assert(!"Too many sw singlestep breakpoints!");
abort();
}
@@ -613,9 +610,10 @@
{
struct process *proc = self->task_enabling_breakpoint;
- int status = arch_atomic_singlestep(self->task_enabling_breakpoint,
- self->breakpoint_being_enabled,
- &atomic_singlestep_add_bp, self);
+ struct sw_singlestep_data data = { self };
+ int status = arch_sw_singlestep(self->task_enabling_breakpoint,
+ self->breakpoint_being_enabled,
+ &sw_singlestep_add_bp, &data);
/* Propagate failure and success. */
if (status <= 0)
@@ -641,7 +639,7 @@
struct process *proc = self->task_enabling_breakpoint;
- remove_atomic_breakpoints(proc);
+ remove_sw_breakpoints(proc);
self->breakpoint_being_enabled = NULL;
}
diff --git a/sysdeps/linux-gnu/trace.h b/sysdeps/linux-gnu/trace.h
index 7aed2e7..e988f70 100644
--- a/sysdeps/linux-gnu/trace.h
+++ b/sysdeps/linux-gnu/trace.h
@@ -1,6 +1,6 @@
/*
* This file is part of ltrace.
- * Copyright (C) 2011,2012 Petr Machata, Red Hat Inc.
+ * Copyright (C) 2011,2012,2013 Petr Machata, Red Hat Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -65,7 +65,7 @@
struct breakpoint *breakpoint_being_enabled;
/* Artificial atomic skip breakpoint, if any needed. */
- void *atomic_skip_bp_addrs[2];
+ arch_addr_t sws_bp_addrs[2];
/* When all tasks are stopped, this callback gets called. */
void (*on_all_stopped)(struct process_stopping_handler *);