Event queue
diff --git a/ChangeLog b/ChangeLog
index 83679ff..26999c9 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2011-06-01 Petr Machata <[email protected]>
+
+ * sysdeps/linux-gnu/events.c (delayed_events, end_delayed_events):
+ New variables for event queue.
+ (enque_event): To add new event into the queue.
+ (next_qd_event, each_qd_event, enum ecb_status): Iteration interfaces.
+ (next_event): Instead of waitpid, pop event que if there are
+ events waiting.
+ * ltrace.h (Event.next): New field to support event chaining.
+
2011-05-30 Petr Machata <[email protected]>
* common.h (enable_breakpoint, disable_breakpoint)
diff --git a/common.h b/common.h
index a53bc83..ab2465e 100644
--- a/common.h
+++ b/common.h
@@ -242,7 +242,6 @@
pcb_cont, /* The iteration should continue. */
};
-extern Event * next_event(void);
extern Process * pid2proc(pid_t pid);
extern void add_process(Process * proc);
extern void remove_process(Process * proc);
@@ -253,6 +252,18 @@
enum pcb_status (* cb)(Process * proc, void * data),
void * data);
+/* Events */
+enum ecb_status {
+ ecb_cont, /* The iteration should continue. */
+ ecb_yield, /* The iteration should stop, yielding this
+ * event. */
+ ecb_deque, /* Like ecb_stop, but the event should be removed
+ * from the queue. */
+};
+extern Event * next_event(void);
+extern Event * each_qd_event(enum ecb_status (* cb)(Event * event, void * data),
+ void * data);
+extern void enque_event(Event * event);
extern void handle_event(Event * event);
extern pid_t execute_program(const char * command, char ** argv);
extern int display_arg(enum tof type, Process * proc, int arg_num, arg_type_info * info);
diff --git a/ltrace.h b/ltrace.h
index 5e43ba5..0ff4572 100644
--- a/ltrace.h
+++ b/ltrace.h
@@ -20,6 +20,7 @@
typedef struct Process Process;
typedef struct Event Event;
struct Event {
+ struct Event * next;
Process * proc;
Event_type type;
union {
diff --git a/proc.c b/proc.c
index 0423eeb..fae6595 100644
--- a/proc.c
+++ b/proc.c
@@ -169,6 +169,23 @@
return pcb_cont;
}
+static enum ecb_status
+event_for_proc(Event * event, void * data)
+{
+ if (event->proc == data)
+ return ecb_deque;
+ else
+ return ecb_cont;
+}
+
+static void
+delete_events_for(Process * proc)
+{
+ Event * event;
+ while ((event = each_qd_event(&event_for_proc, proc)) != NULL)
+ free(event);
+}
+
void
remove_process(Process *proc)
{
@@ -182,6 +199,7 @@
if (list_of_processes == proc) {
tmp = list_of_processes;
list_of_processes = list_of_processes->next;
+ delete_events_for(tmp);
free(tmp);
return;
}
@@ -190,6 +208,7 @@
if (tmp->next == proc) {
tmp2 = tmp->next;
tmp->next = tmp->next->next;
+ delete_events_for(tmp2);
free(tmp2);
return;
}
diff --git a/sysdeps/linux-gnu/events.c b/sysdeps/linux-gnu/events.c
index b944cd6..6068fea 100644
--- a/sysdeps/linux-gnu/events.c
+++ b/sysdeps/linux-gnu/events.c
@@ -8,17 +8,96 @@
#include <signal.h>
#include <string.h>
#include <sys/ptrace.h>
+#include <assert.h>
#include "common.h"
static Event event;
+/* A queue of events that we missed while enabling the
+ * breakpoint in one of tasks. */
+static Event * delayed_events = NULL;
+static Event * end_delayed_events = NULL;
+
static enum pcb_status
first (Process * proc, void * data)
{
return pcb_stop;
}
+void
+enque_event(Event * event)
+{
+ debug(DEBUG_FUNCTION, "%d: queuing event %d for later",
+ event->proc->pid, event->type);
+ Event * ne = malloc(sizeof(*ne));
+ if (ne == NULL) {
+ perror("event will be missed: malloc");
+ return;
+ }
+
+ *ne = *event;
+ ne->next = NULL;
+ if (end_delayed_events == NULL) {
+ assert(delayed_events == NULL);
+ end_delayed_events = delayed_events = ne;
+ }
+ else {
+ assert(delayed_events != NULL);
+ end_delayed_events = end_delayed_events->next = ne;
+ }
+}
+
+Event *
+each_qd_event(enum ecb_status (*pred)(Event *, void *), void * data)
+{
+ Event * prev = delayed_events;
+ Event * event;
+ for (event = prev; event != NULL; ) {
+ switch ((*pred)(event, data)) {
+ case ecb_cont:
+ prev = event;
+ event = event->next;
+ continue;
+
+ case ecb_deque:
+ debug(DEBUG_FUNCTION, "dequeuing event %d for %d",
+ event->type,
+ event->proc != NULL ? event->proc->pid : -1);
+ /*
+ printf("dequeuing event %d for %d\n", event->type,
+ event->proc != NULL ? event->proc->pid : -1) ;
+ */
+ if (end_delayed_events == event)
+ end_delayed_events = prev;
+ if (delayed_events == event)
+ delayed_events = event->next;
+ else
+ prev->next = event->next;
+ if (delayed_events == NULL)
+ end_delayed_events = NULL;
+ /* fall-through */
+
+ case ecb_yield:
+ return event;
+ }
+ }
+
+ return NULL;
+}
+
+static enum ecb_status
+event_process_not_reenabling(Event * event, void * data)
+{
+ return ecb_deque;
+}
+
+static Event *
+next_qd_event(void)
+{
+ return each_qd_event(&event_process_not_reenabling, NULL);
+}
+
Event *
next_event(void)
{
@@ -28,6 +107,13 @@
int stop_signal;
debug(DEBUG_FUNCTION, "next_event()");
+ Event * ev;
+ if ((ev = next_qd_event()) != NULL) {
+ event = *ev;
+ free(ev);
+ return &event;
+ }
+
if (!each_process(NULL, &first, NULL)) {
debug(DEBUG_EVENT, "event: No more traced programs: exiting");
exit(0);