source: mainline/kernel/generic/src/log/log.c@ bf9cb2f

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since bf9cb2f was 91db0280, checked in by Martin Sucha <sucha14@…>, 12 years ago

Cherrypick kernel logging facility

  • Property mode set to 100644
File size: 9.2 KB
RevLine 
[91db0280]1/*
2 * Copyright (c) 2013 Martin Sucha
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup genericlog
30 * @{
31 */
32/** @file
33 */
34
35#include <sysinfo/sysinfo.h>
36#include <synch/spinlock.h>
37#include <typedefs.h>
38#include <ddi/irq.h>
39#include <ddi/ddi.h>
40#include <ipc/event.h>
41#include <ipc/irq.h>
42#include <arch.h>
43#include <panic.h>
44#include <putchar.h>
45#include <atomic.h>
46#include <syscall/copy.h>
47#include <errno.h>
48#include <str.h>
49#include <print.h>
50#include <printf/printf_core.h>
51#include <stdarg.h>
52#include <log.h>
53#include <console/console.h>
54#include <abi/log.h>
55
56#define LOG_PAGES 8
57#define LOG_LENGTH (LOG_PAGES * PAGE_SIZE)
58#define LOG_ENTRY_HEADER_LENGTH (sizeof(size_t) + sizeof(uint32_t))
59
60/** Cyclic buffer holding the data for kernel log */
61uint8_t log_buffer[LOG_LENGTH] __attribute__((aligned(PAGE_SIZE)));
62
63/** Kernel log initialized */
64static atomic_t log_inited = {false};
65
66/** Position in the cyclic buffer where the first log entry starts */
67size_t log_start = 0;
68
69/** Sum of length of all log entries currently stored in the cyclic buffer */
70size_t log_used = 0;
71
72/** Log spinlock */
73SPINLOCK_STATIC_INITIALIZE_NAME(log_lock, "log_lock");
74
75/** Overall count of logged messages, which may overflow as needed */
76static uint32_t log_counter = 0;
77
78/** Starting position of the entry currently being written to the log */
79static size_t log_current_start = 0;
80
81/** Length (including header) of the entry currently being written to the log */
82static size_t log_current_len = 0;
83
84/** Start of the next entry to be handed to uspace starting from log_start */
85static size_t next_for_uspace = 0;
86
87static void log_update(void *);
88
89/** Initialize kernel logging facility
90 *
91 */
92void log_init(void)
93{
94 event_set_unmask_callback(EVENT_KLOG, log_update);
95 atomic_set(&log_inited, true);
96}
97
98static size_t log_copy_from(uint8_t *data, size_t pos, size_t len) {
99 for (size_t i = 0; i < len; i++, pos = (pos + 1) % LOG_LENGTH) {
100 data[i] = log_buffer[pos];
101 }
102 return pos;
103}
104
105static size_t log_copy_to(const uint8_t *data, size_t pos, size_t len) {
106 for (size_t i = 0; i < len; i++, pos = (pos + 1) % LOG_LENGTH) {
107 log_buffer[pos] = data[i];
108 }
109 return pos;
110}
111
112/** Append data to the currently open log entry.
113 *
114 * This function requires that the log_lock is acquired by the caller.
115 */
116static void log_append(const uint8_t *data, size_t len)
117{
118 /* Cap the length so that the entry entirely fits into the buffer */
119 if (len > LOG_LENGTH - log_current_len) {
120 len = LOG_LENGTH - log_current_len;
121 }
122
123 if (len == 0)
124 return;
125
126 size_t log_free = LOG_LENGTH - log_used - log_current_len;
127
128 /* Discard older entries to make space, if necessary */
129 while (len > log_free) {
130 size_t entry_len;
131 log_copy_from((uint8_t *) &entry_len, log_start, sizeof(size_t));
132 log_start = (log_start + entry_len) % LOG_LENGTH;
133 log_used -= entry_len;
134 log_free += entry_len;
135 next_for_uspace -= entry_len;
136 }
137
138 size_t pos = (log_current_start + log_current_len) % LOG_LENGTH;
139 log_copy_to(data, pos, len);
140 log_current_len += len;
141}
142
143/** Begin writing an entry to the log.
144 *
145 * This acquires the log and output buffer locks, so only calls to log_* functions should
146 * be used until calling log_end.
147 */
148void log_begin(log_facility_t fac, log_level_t level)
149{
150 spinlock_lock(&log_lock);
151 spinlock_lock(&kio_lock);
152
153 log_current_start = (log_start + log_used) % LOG_LENGTH;
154 log_current_len = 0;
155
156 /* Write header of the log entry, the length will be written in log_end() */
157 log_append((uint8_t *) &log_current_len, sizeof(size_t));
158 log_append((uint8_t *) &log_counter, sizeof(uint32_t));
159 uint32_t fac32 = fac;
160 uint32_t lvl32 = level;
161 log_append((uint8_t *) &fac32, sizeof(uint32_t));
162 log_append((uint8_t *) &lvl32, sizeof(uint32_t));
163
164 log_counter++;
165}
166
167/** Finish writing an entry to the log.
168 *
169 * This releases the log and output buffer locks.
170 */
171void log_end(void) {
172 /* Set the length in the header to correct value */
173 log_copy_to((uint8_t *) &log_current_len, log_current_start, sizeof(size_t));
174 log_used += log_current_len;
175
176 kio_push_char('\n');
177 spinlock_unlock(&kio_lock);
178 spinlock_unlock(&log_lock);
179
180 /* This has to be called after we released the locks above */
181 kio_flush();
182 kio_update(NULL);
183 log_update(NULL);
184}
185
186static void log_update(void *event)
187{
188 if (!atomic_get(&log_inited))
189 return;
190
191 spinlock_lock(&log_lock);
192 if (next_for_uspace < log_used)
193 event_notify_0(EVENT_KLOG, true);
194 spinlock_unlock(&log_lock);
195}
196
197static int log_printf_str_write(const char *str, size_t size, void *data)
198{
199 size_t offset = 0;
200 size_t chars = 0;
201
202 while (offset < size) {
203 kio_push_char(str_decode(str, &offset, size));
204 chars++;
205 }
206
207 log_append((const uint8_t *)str, size);
208
209 return chars;
210}
211
212static int log_printf_wstr_write(const wchar_t *wstr, size_t size, void *data)
213{
214 char buffer[16];
215 size_t offset = 0;
216 size_t chars = 0;
217
218 for (offset = 0; offset < size; offset += sizeof(wchar_t), chars++) {
219 kio_push_char(wstr[chars]);
220
221 size_t buffer_offset = 0;
222 int rc = chr_encode(wstr[chars], buffer, &buffer_offset, 16);
223 if (rc != EOK) {
224 return rc;
225 }
226
227 log_append((const uint8_t *)buffer, buffer_offset);
228 }
229
230 return chars;
231}
232
233/** Append a message to the currently being written entry.
234 *
235 * Requires that an entry has been started using log_begin()
236 */
237int log_vprintf(const char *fmt, va_list args)
238{
239 int ret;
240
241 printf_spec_t ps = {
242 log_printf_str_write,
243 log_printf_wstr_write,
244 NULL
245 };
246
247
248 ret = printf_core(fmt, &ps, args);
249
250 return ret;
251}
252
253/** Append a message to the currently being written entry.
254 *
255 * Requires that an entry has been started using log_begin()
256 */
257int log_printf(const char *fmt, ...)
258{
259 int ret;
260 va_list args;
261
262 va_start(args, fmt);
263 ret = log_vprintf(fmt, args);
264 va_end(args);
265
266 return ret;
267}
268
269/** Log a message to the kernel log.
270 *
271 * This atomically appends a log entry.
272 * The resulting message should not contain a trailing newline, as the log
273 * entries are explicitly delimited when stored in the log.
274 */
275int log(log_facility_t fac, log_level_t level, const char *fmt, ...)
276{
277 int ret;
278 va_list args;
279
280 log_begin(fac, level);
281
282 va_start(args, fmt);
283 ret = log_vprintf(fmt, args);
284 va_end(args);
285
286 log_end();
287
288 return ret;
289}
290
291/** Control of the log from uspace
292 *
293 */
294sysarg_t sys_klog(sysarg_t operation, void *buf, size_t size,
295 sysarg_t level)
296{
297 char *data;
298 int rc;
299
300 if (size > PAGE_SIZE)
301 return (sysarg_t) ELIMIT;
302
303 switch (operation) {
304 case KLOG_WRITE:
305 data = (char *) malloc(size + 1, 0);
306 if (!data)
307 return (sysarg_t) ENOMEM;
308
309 rc = copy_from_uspace(data, buf, size);
310 if (rc) {
311 free(data);
312 return (sysarg_t) rc;
313 }
314 data[size] = 0;
315
316 if (level >= LVL_LIMIT)
317 level = LVL_NOTE;
318
319 log(LF_USPACE, level, "%s", data);
320
321 free(data);
322 return EOK;
323 case KLOG_READ:
324 data = (char *) malloc(size, 0);
325 if (!data)
326 return (sysarg_t) ENOMEM;
327
328 size_t entry_len = 0;
329 size_t copied = 0;
330
331 rc = EOK;
332
333 spinlock_lock(&log_lock);
334
335 while (next_for_uspace < log_used) {
336 size_t pos = (log_start + next_for_uspace) % LOG_LENGTH;
337 log_copy_from((uint8_t *) &entry_len, pos, sizeof(size_t));
338
339 if (entry_len > PAGE_SIZE) {
340 /*
341 * Since we limit data transfer
342 * to uspace to a maximum of PAGE_SIZE
343 * bytes, skip any entries larger
344 * than this limit to prevent
345 * userspace being stuck trying to
346 * read them.
347 */
348 next_for_uspace += entry_len;
349 continue;
350 }
351
352 if (size < copied + entry_len) {
353 if (copied == 0)
354 rc = EOVERFLOW;
355 break;
356 }
357
358 log_copy_from((uint8_t *) (data + copied), pos, entry_len);
359 copied += entry_len;
360 next_for_uspace += entry_len;
361 }
362
363 spinlock_unlock(&log_lock);
364
365 if (rc != EOK) {
366 free(data);
367 return (sysarg_t) rc;
368 }
369
370 rc = copy_to_uspace(buf, data, size);
371
372 free(data);
373
374 if (rc != EOK)
375 return (sysarg_t) rc;
376
377 return copied;
378 default:
379 return (sysarg_t) ENOTSUP;
380 }
381}
382
383
384/** @}
385 */
Note: See TracBrowser for help on using the repository browser.