void debug_panic (const char *file, int line, const char *function,
const char *message, ...) PRINTF_FORMAT (4, 5) NO_RETURN;
void debug_backtrace (void);
+void debug_backtrace_all (void);
#endif
#include <string.h>
#include "threads/init.h"
#include "threads/interrupt.h"
+#include "threads/thread.h"
+#include "threads/switch.h"
+#include "threads/vaddr.h"
#include "devices/serial.h"
/* Halts the OS, printing the source file name, line number, and
power_off ();
for (;;);
}
+
+/* Print call stack of a thread.
+ The thread may be running, ready, or blocked. */
+static void
+print_stacktrace(struct thread *t, void *aux UNUSED)
+{
+ void *retaddr = NULL, **frame = NULL;
+ const char *status = "UNKNOWN";
+
+ switch (t->status) {
+ case THREAD_RUNNING:
+ status = "RUNNING";
+ break;
+
+ case THREAD_READY:
+ status = "READY";
+ break;
+
+ case THREAD_BLOCKED:
+ status = "BLOCKED";
+ break;
+
+ default:
+ break;
+ }
+
+ printf ("Call stack of thread `%s' (status %s):", t->name, status);
+
+ if (t == thread_current())
+ {
+ frame = __builtin_frame_address (1);
+ retaddr = __builtin_return_address (0);
+ }
+ else
+ {
+ /* Retrieve the values of the base and instruction pointers
+ as they were saved when this thread called switch_threads. */
+ struct switch_threads_frame * saved_frame;
+
+ saved_frame = (struct switch_threads_frame *)t->stack;
+
+ /* Skip threads if they have been added to the all threads
+ list, but have never been scheduled.
+ We can identify because their `stack' member either points
+ at the top of their kernel stack page, or the
+ switch_threads_frame's 'eip' member points at switch_entry.
+ See also threads.c. */
+ if (t->stack == (uint8_t *)t + PGSIZE || saved_frame->eip == switch_entry)
+ {
+ printf (" thread was never scheduled.\n");
+ return;
+ }
+
+ frame = (void **) saved_frame->ebp;
+ retaddr = (void *) saved_frame->eip;
+ }
+
+ printf (" %p", retaddr);
+ for (; (uintptr_t) frame >= 0x1000 && frame[0] != NULL; frame = frame[0])
+ printf (" %p", frame[1]);
+ printf (".\n");
+}
+
+/* Prints call stack of all threads. */
+void
+debug_backtrace_all (void)
+{
+ enum intr_level oldlevel = intr_disable ();
+
+ thread_foreach (print_stacktrace, 0);
+ intr_set_level (oldlevel);
+}
that are ready to run but not actually running. */
static struct list ready_list;
+/* List of all processes. Processes are added to this list
+ when they are first scheduled and removed when they exit. */
+static struct list all_list;
+
/* Idle thread. */
static struct thread *idle_thread;
lock_init (&tid_lock);
list_init (&ready_list);
+ list_init (&all_list);
/* Set up a thread structure for the running thread. */
initial_thread = running_thread ();
struct switch_entry_frame *ef;
struct switch_threads_frame *sf;
tid_t tid;
+ enum intr_level old_level;
ASSERT (function != NULL);
init_thread (t, name, priority);
tid = t->tid = allocate_tid ();
+ /* Prepare thread for first run by initializing its stack.
+ Do this atomically so intermediate values for the 'stack'
+ member cannot be observed. */
+ old_level = intr_disable ();
+
/* Stack frame for kernel_thread(). */
kf = alloc_frame (t, sizeof *kf);
kf->eip = NULL;
/* Stack frame for switch_threads(). */
sf = alloc_frame (t, sizeof *sf);
sf->eip = switch_entry;
+ sf->ebp = 0;
+
+ intr_set_level (old_level);
/* Add to run queue. */
thread_unblock (t);
process_exit ();
#endif
- /* Just set our status to dying and schedule another process.
- We will be destroyed during the call to schedule_tail(). */
+ /* Remove thread from all threads list, set our status to dying,
+ and schedule another process. That process will destroy us
+ when it call schedule_tail(). */
intr_disable ();
+ list_remove (&thread_current()->allelem);
thread_current ()->status = THREAD_DYING;
schedule ();
NOT_REACHED ();
intr_set_level (old_level);
}
+/* Invoke function 'func' on all threads, passing along 'aux'.
+ This function must be called with interrupts off. */
+void
+thread_foreach (thread_action_func *func, void *aux)
+{
+ struct list_elem *e;
+
+ ASSERT (intr_get_level () == INTR_OFF);
+
+ for (e = list_begin (&all_list); e != list_end (&all_list);
+ e = list_next (e))
+ {
+ struct thread *t = list_entry (e, struct thread, allelem);
+ func (t, aux);
+ }
+}
+
/* Sets the current thread's priority to NEW_PRIORITY. */
void
thread_set_priority (int new_priority)
t->stack = (uint8_t *) t + PGSIZE;
t->priority = priority;
t->magic = THREAD_MAGIC;
+ list_push_back (&all_list, &t->allelem);
}
/* Allocates a SIZE-byte frame at the top of thread T's stack and
char name[16]; /* Name (for debugging purposes). */
uint8_t *stack; /* Saved stack pointer. */
int priority; /* Priority. */
+ struct list_elem allelem; /* List element for all threads list. */
/* Shared between thread.c and synch.c. */
struct list_elem elem; /* List element. */
void thread_exit (void) NO_RETURN;
void thread_yield (void);
+/* Performs some operation on thread t, given auxiliary data AUX. */
+typedef void thread_action_func (struct thread *t, void *aux);
+void thread_foreach (thread_action_func *, void *);
+
int thread_get_priority (void);
void thread_set_priority (int);