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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since e3f819f was a35b458, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 8 years ago

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

  • Property mode set to 100644
File size: 9.3 KB
Line 
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#include <mm/slab.h>
56
57#define LOG_PAGES 8
58#define LOG_LENGTH (LOG_PAGES * PAGE_SIZE)
59#define LOG_ENTRY_HEADER_LENGTH (sizeof(size_t) + sizeof(uint32_t))
60
61/** Cyclic buffer holding the data for kernel log */
62uint8_t log_buffer[LOG_LENGTH] __attribute__((aligned(PAGE_SIZE)));
63
64/** Kernel log initialized */
65static atomic_t log_inited = {false};
66
67/** Position in the cyclic buffer where the first log entry starts */
68size_t log_start = 0;
69
70/** Sum of length of all log entries currently stored in the cyclic buffer */
71size_t log_used = 0;
72
73/** Log spinlock */
74SPINLOCK_STATIC_INITIALIZE_NAME(log_lock, "log_lock");
75
76/** Overall count of logged messages, which may overflow as needed */
77static uint32_t log_counter = 0;
78
79/** Starting position of the entry currently being written to the log */
80static size_t log_current_start = 0;
81
82/** Length (including header) of the entry currently being written to the log */
83static size_t log_current_len = 0;
84
85/** Start of the next entry to be handed to uspace starting from log_start */
86static size_t next_for_uspace = 0;
87
88static void log_update(void *);
89
90/** Initialize kernel logging facility
91 *
92 */
93void log_init(void)
94{
95 event_set_unmask_callback(EVENT_KLOG, log_update);
96 atomic_set(&log_inited, true);
97}
98
99static size_t log_copy_from(uint8_t *data, size_t pos, size_t len) {
100 for (size_t i = 0; i < len; i++, pos = (pos + 1) % LOG_LENGTH) {
101 data[i] = log_buffer[pos];
102 }
103 return pos;
104}
105
106static size_t log_copy_to(const uint8_t *data, size_t pos, size_t len) {
107 for (size_t i = 0; i < len; i++, pos = (pos + 1) % LOG_LENGTH) {
108 log_buffer[pos] = data[i];
109 }
110 return pos;
111}
112
113/** Append data to the currently open log entry.
114 *
115 * This function requires that the log_lock is acquired by the caller.
116 */
117static void log_append(const uint8_t *data, size_t len)
118{
119 /* Cap the length so that the entry entirely fits into the buffer */
120 if (len > LOG_LENGTH - log_current_len) {
121 len = LOG_LENGTH - log_current_len;
122 }
123
124 if (len == 0)
125 return;
126
127 size_t log_free = LOG_LENGTH - log_used - log_current_len;
128
129 /* Discard older entries to make space, if necessary */
130 while (len > log_free) {
131 size_t entry_len;
132 log_copy_from((uint8_t *) &entry_len, log_start, sizeof(size_t));
133 log_start = (log_start + entry_len) % LOG_LENGTH;
134 log_used -= entry_len;
135 log_free += entry_len;
136 next_for_uspace -= entry_len;
137 }
138
139 size_t pos = (log_current_start + log_current_len) % LOG_LENGTH;
140 log_copy_to(data, pos, len);
141 log_current_len += len;
142}
143
144/** Begin writing an entry to the log.
145 *
146 * This acquires the log and output buffer locks, so only calls to log_* functions should
147 * be used until calling log_end.
148 */
149void log_begin(log_facility_t fac, log_level_t level)
150{
151 spinlock_lock(&log_lock);
152 spinlock_lock(&kio_lock);
153
154 log_current_start = (log_start + log_used) % LOG_LENGTH;
155 log_current_len = 0;
156
157 /* Write header of the log entry, the length will be written in log_end() */
158 log_append((uint8_t *) &log_current_len, sizeof(size_t));
159 log_append((uint8_t *) &log_counter, sizeof(uint32_t));
160 uint32_t fac32 = fac;
161 uint32_t lvl32 = level;
162 log_append((uint8_t *) &fac32, sizeof(uint32_t));
163 log_append((uint8_t *) &lvl32, sizeof(uint32_t));
164
165 log_counter++;
166}
167
168/** Finish writing an entry to the log.
169 *
170 * This releases the log and output buffer locks.
171 */
172void log_end(void) {
173 /* Set the length in the header to correct value */
174 log_copy_to((uint8_t *) &log_current_len, log_current_start, sizeof(size_t));
175 log_used += log_current_len;
176
177 kio_push_char('\n');
178 spinlock_unlock(&kio_lock);
179 spinlock_unlock(&log_lock);
180
181 /* This has to be called after we released the locks above */
182 kio_flush();
183 kio_update(NULL);
184 log_update(NULL);
185}
186
187static void log_update(void *event)
188{
189 if (!atomic_get(&log_inited))
190 return;
191
192 spinlock_lock(&log_lock);
193 if (next_for_uspace < log_used)
194 event_notify_0(EVENT_KLOG, true);
195 spinlock_unlock(&log_lock);
196}
197
198static int log_printf_str_write(const char *str, size_t size, void *data)
199{
200 size_t offset = 0;
201 size_t chars = 0;
202
203 while (offset < size) {
204 kio_push_char(str_decode(str, &offset, size));
205 chars++;
206 }
207
208 log_append((const uint8_t *)str, size);
209
210 return chars;
211}
212
213static int log_printf_wstr_write(const wchar_t *wstr, size_t size, void *data)
214{
215 char buffer[16];
216 size_t offset = 0;
217 size_t chars = 0;
218
219 for (offset = 0; offset < size; offset += sizeof(wchar_t), chars++) {
220 kio_push_char(wstr[chars]);
221
222 size_t buffer_offset = 0;
223 errno_t rc = chr_encode(wstr[chars], buffer, &buffer_offset, 16);
224 if (rc != EOK) {
225 return EOF;
226 }
227
228 log_append((const uint8_t *)buffer, buffer_offset);
229 }
230
231 return chars;
232}
233
234/** Append a message to the currently being written entry.
235 *
236 * Requires that an entry has been started using log_begin()
237 */
238int log_vprintf(const char *fmt, va_list args)
239{
240 int ret;
241
242 printf_spec_t ps = {
243 log_printf_str_write,
244 log_printf_wstr_write,
245 NULL
246 };
247
248
249 ret = printf_core(fmt, &ps, args);
250
251 return ret;
252}
253
254/** Append a message to the currently being written entry.
255 *
256 * Requires that an entry has been started using log_begin()
257 */
258int log_printf(const char *fmt, ...)
259{
260 int ret;
261 va_list args;
262
263 va_start(args, fmt);
264 ret = log_vprintf(fmt, args);
265 va_end(args);
266
267 return ret;
268}
269
270/** Log a message to the kernel log.
271 *
272 * This atomically appends a log entry.
273 * The resulting message should not contain a trailing newline, as the log
274 * entries are explicitly delimited when stored in the log.
275 */
276int log(log_facility_t fac, log_level_t level, const char *fmt, ...)
277{
278 int ret;
279 va_list args;
280
281 log_begin(fac, level);
282
283 va_start(args, fmt);
284 ret = log_vprintf(fmt, args);
285 va_end(args);
286
287 log_end();
288
289 return ret;
290}
291
292/** Control of the log from uspace
293 *
294 */
295sys_errno_t sys_klog(sysarg_t operation, void *buf, size_t size,
296 sysarg_t level, size_t *uspace_nread)
297{
298 char *data;
299 errno_t rc;
300
301 if (size > PAGE_SIZE)
302 return (sys_errno_t) ELIMIT;
303
304 switch (operation) {
305 case KLOG_WRITE:
306 data = (char *) malloc(size + 1, 0);
307 if (!data)
308 return (sys_errno_t) ENOMEM;
309
310 rc = copy_from_uspace(data, buf, size);
311 if (rc) {
312 free(data);
313 return (sys_errno_t) rc;
314 }
315 data[size] = 0;
316
317 if (level >= LVL_LIMIT)
318 level = LVL_NOTE;
319
320 log(LF_USPACE, level, "%s", data);
321
322 free(data);
323 return EOK;
324 case KLOG_READ:
325 data = (char *) malloc(size, 0);
326 if (!data)
327 return (sys_errno_t) ENOMEM;
328
329 size_t entry_len = 0;
330 size_t copied = 0;
331
332 rc = EOK;
333
334 spinlock_lock(&log_lock);
335
336 while (next_for_uspace < log_used) {
337 size_t pos = (log_start + next_for_uspace) % LOG_LENGTH;
338 log_copy_from((uint8_t *) &entry_len, pos, sizeof(size_t));
339
340 if (entry_len > PAGE_SIZE) {
341 /*
342 * Since we limit data transfer
343 * to uspace to a maximum of PAGE_SIZE
344 * bytes, skip any entries larger
345 * than this limit to prevent
346 * userspace being stuck trying to
347 * read them.
348 */
349 next_for_uspace += entry_len;
350 continue;
351 }
352
353 if (size < copied + entry_len) {
354 if (copied == 0)
355 rc = EOVERFLOW;
356 break;
357 }
358
359 log_copy_from((uint8_t *) (data + copied), pos, entry_len);
360 copied += entry_len;
361 next_for_uspace += entry_len;
362 }
363
364 spinlock_unlock(&log_lock);
365
366 if (rc != EOK) {
367 free(data);
368 return (sys_errno_t) rc;
369 }
370
371 rc = copy_to_uspace(buf, data, size);
372
373 free(data);
374
375 if (rc != EOK)
376 return (sys_errno_t) rc;
377
378 return copy_to_uspace(uspace_nread, &copied, sizeof(copied));
379 return EOK;
380 default:
381 return (sys_errno_t) ENOTSUP;
382 }
383}
384
385
386/** @}
387 */
Note: See TracBrowser for help on using the repository browser.