Print statistics at power off.
[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_unlocked (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_unlocked()
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, "console");
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 /* The standard vprintf() function,
89    which is like printf() but uses a va_list.
90    Writes its output to both vga display and serial port. */
91 int
92 vprintf (const char *format, va_list args) 
93 {
94   int char_cnt = 0;
95
96   acquire_console ();
97   __vprintf (format, args, vprintf_helper, &char_cnt);
98   release_console ();
99
100   return char_cnt;
101 }
102
103 /* Writes string S to the console, followed by a new-line
104    character. */
105 int
106 puts (const char *s) 
107 {
108   acquire_console ();
109   while (*s != '\0')
110     putchar_unlocked (*s++);
111   putchar_unlocked ('\n');
112   release_console ();
113
114   return 0;
115 }
116
117 /* Writes the N characters in BUFFER to the console. */
118 void
119 putbuf (const char *buffer, size_t n) 
120 {
121   acquire_console ();
122   while (n-- > 0)
123     putchar_unlocked (*buffer++);
124   release_console ();
125 }
126
127 /* Writes C to the vga display and serial port. */
128 int
129 putchar (int c) 
130 {
131   acquire_console ();
132   putchar_unlocked (c);
133   release_console ();
134   
135   return c;
136 }
137 \f
138 /* Helper function for vprintf(). */
139 static void
140 vprintf_helper (char c, void *char_cnt_) 
141 {
142   int *char_cnt = char_cnt_;
143   (*char_cnt)++;
144   putchar_unlocked (c);
145 }
146
147 /* Writes C to the vga display and serial port.
148    The caller has already acquired the console lock if
149    appropriate. */
150 static void
151 putchar_unlocked (uint8_t c) 
152 {
153   write_cnt++;
154   serial_putc (c);
155   vga_putc (c);
156 }