f62e7f34c246b0e64183d8a40c1621a32b348597
[pintos-anon] / src / lib / kernel / console.c
1 #include <console.h>
2 #include <stdarg.h>
3 #include <stdio.h>
4 #include "devices/serial.h"
5 #include "devices/vga.h"
6 #include "threads/interrupt.h"
7 #include "threads/synch.h"
8
9 static void vprintf_helper (char, void *);
10 static void putchar_have_lock (uint8_t c);
11
12 /* The console lock.
13    Both the vga and serial layers do their own locking, so it's
14    safe to call them at any time.
15    But this lock is useful to prevent simultaneous printf() calls
16    from mixing their output, which looks confusing. */
17 static struct lock console_lock;
18
19 /* It's possible, if you add enough debug output to Pintos, to
20    try to recursively grab console_lock from a single thread.  As
21    a real example, I added a printf() call to palloc_free().
22    Here's a real backtrace that resulted:
23
24    lock_console()
25    vprintf()
26    printf()             - palloc() tries to grab the lock again
27    palloc_free()        
28    schedule_tail()      - another thread dying as we switch threads
29    schedule()
30    thread_yield()
31    intr_handler()       - timer interrupt
32    intr_set_level()
33    serial_putc()
34    putchar_have_lock()
35    putbuf()
36    sys_write()          - one process writing to the console
37    syscall_handler()
38    intr_handler()
39
40    This kind of thing is very difficult to debug, so we avoid the
41    problem by simulating a recursive lock with a depth
42    counter. */
43 static int console_lock_depth;
44
45 /* Number of characters written to console. */
46 static int64_t write_cnt;
47
48 /* Initializes the console. */
49 void
50 console_init (void) 
51 {
52   lock_init (&console_lock);
53 }
54
55 /* Prints console statistics. */
56 void
57 console_print_stats (void) 
58 {
59   printf ("Console: %lld characters output\n", write_cnt);
60 }
61
62 /* Acquires the console lock. */
63 static void
64 acquire_console (void) 
65 {
66   if (!intr_context ()) 
67     {
68       if (lock_held_by_current_thread (&console_lock)) 
69         console_lock_depth++; 
70       else
71         lock_acquire (&console_lock); 
72     }
73 }
74
75 /* Releases the console lock. */
76 static void
77 release_console (void) 
78 {
79   if (!intr_context ()) 
80     {
81       if (console_lock_depth > 0)
82         console_lock_depth--;
83       else
84         lock_release (&console_lock); 
85     }
86 }
87
88 /* Returns true if the current thread has the console lock,
89    false otherwise. */
90 static bool
91 console_locked_by_current_thread (void) 
92 {
93   return intr_context () || lock_held_by_current_thread (&console_lock);
94 }
95
96 /* The standard vprintf() function,
97    which is like printf() but uses a va_list.
98    Writes its output to both vga display and serial port. */
99 int
100 vprintf (const char *format, va_list args) 
101 {
102   int char_cnt = 0;
103
104   acquire_console ();
105   __vprintf (format, args, vprintf_helper, &char_cnt);
106   release_console ();
107
108   return char_cnt;
109 }
110
111 /* Writes string S to the console, followed by a new-line
112    character. */
113 int
114 puts (const char *s) 
115 {
116   acquire_console ();
117   while (*s != '\0')
118     putchar_have_lock (*s++);
119   putchar_have_lock ('\n');
120   release_console ();
121
122   return 0;
123 }
124
125 /* Writes the N characters in BUFFER to the console. */
126 void
127 putbuf (const char *buffer, size_t n) 
128 {
129   acquire_console ();
130   while (n-- > 0)
131     putchar_have_lock (*buffer++);
132   release_console ();
133 }
134
135 /* Writes C to the vga display and serial port. */
136 int
137 putchar (int c) 
138 {
139   acquire_console ();
140   putchar_have_lock (c);
141   release_console ();
142   
143   return c;
144 }
145 \f
146 /* Helper function for vprintf(). */
147 static void
148 vprintf_helper (char c, void *char_cnt_) 
149 {
150   int *char_cnt = char_cnt_;
151   (*char_cnt)++;
152   putchar_have_lock (c);
153 }
154
155 /* Writes C to the vga display and serial port.
156    The caller has already acquired the console lock if
157    appropriate. */
158 static void
159 putchar_have_lock (uint8_t c) 
160 {
161   ASSERT (console_locked_by_current_thread ());
162   write_cnt++;
163   serial_putc (c);
164   vga_putc (c);
165 }