X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Flib%2Fkernel%2Fconsole.c;h=d09801d984a6f2250b18f15d819dc781c521f741;hb=0ec49949304b13ff22287d7d9e3dcb05090c61a4;hp=7ce9cbfc12dab8af872dfafaf5c9b665849661e2;hpb=e45f3adbd21cd675c38b1d29394ec60b9cec2e33;p=pintos-anon diff --git a/src/lib/kernel/console.c b/src/lib/kernel/console.c index 7ce9cbf..d09801d 100644 --- a/src/lib/kernel/console.c +++ b/src/lib/kernel/console.c @@ -1,3 +1,4 @@ +#include #include #include #include "devices/serial.h" @@ -15,12 +16,75 @@ static void putchar_unlocked (uint8_t c); from mixing their output, which looks confusing. */ static struct lock console_lock; +/* It's possible, if you add enough debug output to Pintos, to + try to recursively grab console_lock from a single thread. As + a real example, I added a printf() call to palloc_free(). + Here's a real backtrace that resulted: + + lock_console() + vprintf() + printf() - palloc() tries to grab the lock again + palloc_free() + schedule_tail() - another thread dying as we switch threads + schedule() + thread_yield() + intr_handler() - timer interrupt + intr_set_level() + serial_putc() + putchar_unlocked() + putbuf() + sys_write() - one process writing to the console + syscall_handler() + intr_handler() + + This kind of thing is very difficult to debug, so we avoid the + problem by simulating a recursive lock with a depth + counter. */ +static int console_lock_depth; + +/* Number of characters written to console. */ +static int64_t write_cnt; + +/* Initializes the console. */ void console_init (void) { lock_init (&console_lock, "console"); } +/* Prints console statistics. */ +void +console_print_stats (void) +{ + printf ("Console: %lld characters output\n", write_cnt); +} + +/* Acquires the console lock. */ +static void +acquire_console (void) +{ + if (!intr_context ()) + { + if (lock_held_by_current_thread (&console_lock)) + console_lock_depth++; + else + lock_acquire (&console_lock); + } +} + +/* Releases the console lock. */ +static void +release_console (void) +{ + if (!intr_context ()) + { + if (console_lock_depth > 0) + console_lock_depth--; + else + lock_release (&console_lock); + } +} + /* The standard vprintf() function, which is like printf() but uses a va_list. Writes its output to both vga display and serial port. */ @@ -29,13 +93,9 @@ vprintf (const char *format, va_list args) { int char_cnt = 0; - if (!intr_context ()) - lock_acquire (&console_lock); - + acquire_console (); __vprintf (format, args, vprintf_helper, &char_cnt); - - if (!intr_context ()) - lock_release (&console_lock); + release_console (); return char_cnt; } @@ -45,31 +105,33 @@ vprintf (const char *format, va_list args) int puts (const char *s) { - if (!intr_context ()) - lock_acquire (&console_lock); - + acquire_console (); while (*s != '\0') putchar_unlocked (*s++); putchar_unlocked ('\n'); - - if (!intr_context ()) - lock_release (&console_lock); + release_console (); return 0; } +/* Writes the N characters in BUFFER to the console. */ +void +putbuf (const char *buffer, size_t n) +{ + acquire_console (); + while (n-- > 0) + putchar_unlocked (*buffer++); + release_console (); +} + /* Writes C to the vga display and serial port. */ int putchar (int c) { - if (!intr_context ()) - lock_acquire (&console_lock); - + acquire_console (); putchar_unlocked (c); - - if (!intr_context ()) - lock_release (&console_lock); - + release_console (); + return c; } @@ -88,6 +150,7 @@ vprintf_helper (char c, void *char_cnt_) static void putchar_unlocked (uint8_t c) { + write_cnt++; serial_putc (c); vga_putc (c); }