]> git.saurik.com Git - apple/xnu.git/blame_incremental - libkern/os/log.c
xnu-7195.101.1.tar.gz
[apple/xnu.git] / libkern / os / log.c
... / ...
CommitLineData
1/* * Copyright (c) 2019 Apple Inc. All rights reserved. */
2
3#include <stddef.h>
4#undef offset
5
6#include <kern/cpu_data.h>
7#include <os/base.h>
8#include <os/object.h>
9#include <os/log.h>
10#include <stdbool.h>
11#include <stdint.h>
12
13#include <vm/vm_kern.h>
14#include <mach/vm_statistics.h>
15#include <kern/debug.h>
16#include <libkern/libkern.h>
17#include <libkern/kernel_mach_header.h>
18#include <pexpert/pexpert.h>
19#include <uuid/uuid.h>
20#include <sys/msgbuf.h>
21
22#include <mach/mach_time.h>
23#include <kern/thread.h>
24#include <kern/simple_lock.h>
25#include <kern/kalloc.h>
26#include <kern/clock.h>
27#include <kern/assert.h>
28#include <kern/startup.h>
29#include <kern/task.h>
30
31#include <firehose/tracepoint_private.h>
32#include <firehose/chunk_private.h>
33#include <os/firehose_buffer_private.h>
34#include <os/firehose.h>
35
36#include <os/log_private.h>
37#include "trace_internal.h"
38
39#include "log_encode.h"
40#include "log_mem.h"
41
42struct os_log_s {
43 int a;
44};
45
46struct os_log_s _os_log_default;
47struct os_log_s _os_log_replay;
48
49LOGMEM_STATIC_INIT(os_log_mem, 14, 9, 10);
50
51extern vm_offset_t kernel_firehose_addr;
52extern firehose_chunk_t firehose_boot_chunk;
53
54extern bool bsd_log_lock(bool);
55extern void bsd_log_unlock(void);
56extern void logwakeup(struct msgbuf *);
57
58decl_lck_spin_data(extern, oslog_stream_lock);
59#define stream_lock() lck_spin_lock(&oslog_stream_lock)
60#define stream_unlock() lck_spin_unlock(&oslog_stream_lock)
61
62extern void oslog_streamwakeup(void);
63void oslog_streamwrite_locked(firehose_tracepoint_id_u ftid,
64 uint64_t stamp, const void *pubdata, size_t publen);
65extern void oslog_streamwrite_metadata_locked(oslog_stream_buf_entry_t m_entry);
66
67extern int oslog_stream_open;
68
69extern void *OSKextKextForAddress(const void *);
70
71/* Counters for persistence mode */
72SCALABLE_COUNTER_DEFINE(oslog_p_total_msgcount);
73SCALABLE_COUNTER_DEFINE(oslog_p_metadata_saved_msgcount);
74SCALABLE_COUNTER_DEFINE(oslog_p_metadata_dropped_msgcount);
75SCALABLE_COUNTER_DEFINE(oslog_p_error_count);
76SCALABLE_COUNTER_DEFINE(oslog_p_saved_msgcount);
77SCALABLE_COUNTER_DEFINE(oslog_p_dropped_msgcount);
78SCALABLE_COUNTER_DEFINE(oslog_p_boot_dropped_msgcount);
79SCALABLE_COUNTER_DEFINE(oslog_p_coprocessor_total_msgcount);
80SCALABLE_COUNTER_DEFINE(oslog_p_coprocessor_dropped_msgcount);
81SCALABLE_COUNTER_DEFINE(oslog_p_unresolved_kc_msgcount);
82
83/* Counters for streaming mode */
84SCALABLE_COUNTER_DEFINE(oslog_s_error_count);
85/* Protected by the stream lock */
86uint32_t oslog_s_total_msgcount;
87uint32_t oslog_s_metadata_msgcount;
88
89/* Counters for msgbuf logging */
90SCALABLE_COUNTER_DEFINE(oslog_msgbuf_msgcount)
91SCALABLE_COUNTER_DEFINE(oslog_msgbuf_dropped_msgcount)
92
93static bool oslog_boot_done = false;
94
95#ifdef XNU_KERNEL_PRIVATE
96bool startup_serial_logging_active = true;
97uint64_t startup_serial_num_procs = 300;
98#endif /* XNU_KERNEL_PRIVATE */
99
100// XXX
101firehose_tracepoint_id_t
102firehose_debug_trace(firehose_stream_t stream, firehose_tracepoint_id_t trace_id,
103 uint64_t timestamp, const char *format, const void *pubdata, size_t publen);
104
105static inline firehose_tracepoint_id_t
106_firehose_trace(firehose_stream_t stream, firehose_tracepoint_id_u ftid,
107 uint64_t stamp, const void *pubdata, size_t publen, bool use_streaming);
108
109static oslog_stream_buf_entry_t
110oslog_stream_create_buf_entry(oslog_stream_link_type_t type, firehose_tracepoint_id_u ftid,
111 uint64_t stamp, const void* pubdata, size_t publen);
112
113static void
114_os_log_with_args_internal(os_log_t oslog __unused, os_log_type_t type __unused,
115 const char *format, va_list args, void *addr, void *dso, bool driverKit, bool addcr);
116
117static void
118_os_log_to_msgbuf_internal(const char *format, va_list args, bool safe, bool logging, bool addcr);
119
120static void
121_os_log_to_log_internal(os_log_type_t type, const char *format, va_list args, void *addr, void *dso, bool driverKit);
122
123static bool
124os_log_turned_off(void)
125{
126 return atm_get_diagnostic_config() & (ATM_TRACE_DISABLE | ATM_TRACE_OFF);
127}
128
129bool
130os_log_info_enabled(os_log_t log __unused)
131{
132 return !os_log_turned_off();
133}
134
135bool
136os_log_debug_enabled(os_log_t log __unused)
137{
138 return !os_log_turned_off();
139}
140
141static bool
142os_log_disabled(void)
143{
144 return atm_get_diagnostic_config() & ATM_TRACE_DISABLE;
145}
146
147os_log_t
148os_log_create(const char *subsystem __unused, const char *category __unused)
149{
150 return &_os_log_default;
151}
152
153__attribute__((noinline, not_tail_called)) void
154_os_log_internal(void *dso, os_log_t log, uint8_t type, const char *message, ...)
155{
156 va_list args;
157 void *addr = __builtin_return_address(0);
158
159 va_start(args, message);
160
161 _os_log_with_args_internal(log, type, message, args, addr, dso, FALSE, FALSE);
162
163 va_end(args);
164
165 return;
166}
167
168__attribute__((noinline, not_tail_called)) int
169_os_log_internal_driverKit(void *dso, os_log_t log, uint8_t type, const char *message, ...)
170{
171 va_list args;
172 void *addr = __builtin_return_address(0);
173 bool driverKitLog = FALSE;
174
175 /*
176 * We want to be able to identify dexts from the logs.
177 *
178 * Usually the addr is used to understand if the log line
179 * was generated by a kext or the kernel main executable.
180 * Logd uses copyKextUUIDForAddress with the addr specified
181 * in the log line to retrieve the kext UUID of the sender.
182 *
183 * Dext however are not loaded in kernel space so they do not
184 * have a kernel range of addresses.
185 *
186 * To make the same mechanism work, OSKext fakes a kernel
187 * address range for dexts using the loadTag,
188 * so we just need to use the loadTag as addr here
189 * to allow logd to retrieve the correct UUID.
190 *
191 * NOTE: loadTag is populated in the task when the dext is matching,
192 * so if log lines are generated before the matching they will be
193 * identified as kernel main executable.
194 */
195 task_t self_task = current_task();
196
197 /*
198 * Only dextis are supposed to use this log path.
199 */
200 if (!task_is_driver(self_task)) {
201 return EPERM;
202 }
203
204 uint64_t loadTag = get_task_loadTag(self_task);
205 if (loadTag != 0) {
206 driverKitLog = TRUE;
207 addr = (void*) loadTag;
208 }
209 va_start(args, message);
210
211 _os_log_with_args_internal(log, type, message, args, addr, dso, driverKitLog, true);
212
213 va_end(args);
214
215 return 0;
216}
217
218#pragma mark - shim functions
219
220__attribute__((noinline, not_tail_called)) void
221os_log_with_args(os_log_t oslog, os_log_type_t type, const char *format, va_list args, void *addr)
222{
223 // if no address passed, look it up
224 if (addr == NULL) {
225 addr = __builtin_return_address(0);
226 }
227
228 _os_log_with_args_internal(oslog, type, format, args, addr, NULL, FALSE, FALSE);
229}
230
231static void
232_os_log_with_args_internal(os_log_t oslog, os_log_type_t type,
233 const char *format, va_list args, void *addr, void *dso, bool driverKit, bool addcr)
234{
235 if (format[0] == '\0') {
236 return;
237 }
238
239 /* early boot can log to dmesg for later replay (27307943) */
240 bool safe = (startup_phase < STARTUP_SUB_EARLY_BOOT || oslog_is_safe());
241 bool logging = !os_log_turned_off();
242
243 if (oslog != &_os_log_replay) {
244 _os_log_to_msgbuf_internal(format, args, safe, logging, addcr);
245 }
246
247 if (safe && logging) {
248 _os_log_to_log_internal(type, format, args, addr, dso, driverKit);
249 }
250}
251
252static void
253_os_log_to_msgbuf_internal(const char *format, va_list args, bool safe, bool logging, bool addcr)
254{
255 /*
256 * The following threshold was determined empirically as the point where
257 * it would be more advantageous to be able to fit in more log lines than
258 * to know exactly when a log line was printed out. We don't want to use up
259 * a large percentage of the log buffer on timestamps in a memory-constricted
260 * environment.
261 */
262 const int MSGBUF_TIMESTAMP_THRESHOLD = 4096;
263 static int msgbufreplay = -1;
264 static bool newlogline = true;
265 va_list args_copy;
266
267 if (!bsd_log_lock(safe)) {
268 counter_inc(&oslog_msgbuf_dropped_msgcount);
269 return;
270 }
271
272 if (!safe) {
273 if (-1 == msgbufreplay) {
274 msgbufreplay = msgbufp->msg_bufx;
275 }
276 } else if (logging && (-1 != msgbufreplay)) {
277 uint32_t i;
278 uint32_t localbuff_size;
279 int newl, position;
280 char *localbuff, *p, *s, *next, ch;
281
282 position = msgbufreplay;
283 msgbufreplay = -1;
284 localbuff_size = (msgbufp->msg_size + 2); /* + '\n' + '\0' */
285 /* Size for non-blocking */
286 if (localbuff_size > 4096) {
287 localbuff_size = 4096;
288 }
289 bsd_log_unlock();
290 /* Allocate a temporary non-circular buffer */
291 localbuff = kheap_alloc(KHEAP_TEMP, localbuff_size, Z_NOWAIT);
292 if (localbuff != NULL) {
293 /* in between here, the log could become bigger, but that's fine */
294 bsd_log_lock(true);
295 /*
296 * The message buffer is circular; start at the replay pointer, and
297 * make one loop up to write pointer - 1.
298 */
299 p = msgbufp->msg_bufc + position;
300 for (i = newl = 0; p != msgbufp->msg_bufc + msgbufp->msg_bufx - 1; ++p) {
301 if (p >= msgbufp->msg_bufc + msgbufp->msg_size) {
302 p = msgbufp->msg_bufc;
303 }
304 ch = *p;
305 if (ch == '\0') {
306 continue;
307 }
308 newl = (ch == '\n');
309 localbuff[i++] = ch;
310 if (i >= (localbuff_size - 2)) {
311 break;
312 }
313 }
314 bsd_log_unlock();
315
316 if (!newl) {
317 localbuff[i++] = '\n';
318 }
319 localbuff[i++] = 0;
320
321 s = localbuff;
322 while ((next = strchr(s, '\n'))) {
323 next++;
324 ch = next[0];
325 next[0] = 0;
326 os_log(&_os_log_replay, "%s", s);
327 next[0] = ch;
328 s = next;
329 }
330 kheap_free(KHEAP_TEMP, localbuff, localbuff_size);
331 }
332 bsd_log_lock(true);
333 }
334
335 /* Do not prepend timestamps when we are memory-constricted */
336 if (newlogline && (msgbufp->msg_size > MSGBUF_TIMESTAMP_THRESHOLD)) {
337 clock_sec_t secs;
338 clock_usec_t microsecs;
339 const uint64_t timestamp = firehose_tracepoint_time(firehose_activity_flags_default);
340 absolutetime_to_microtime(timestamp, &secs, &microsecs);
341 printf_log_locked(FALSE, "[%5lu.%06u]: ", (unsigned long)secs, microsecs);
342 }
343
344 va_copy(args_copy, args);
345 newlogline = vprintf_log_locked(format, args_copy, addcr);
346 va_end(args_copy);
347
348 bsd_log_unlock();
349 logwakeup(msgbufp);
350 counter_inc(&oslog_msgbuf_msgcount);
351}
352
353static firehose_stream_t
354firehose_stream(os_log_type_t type)
355{
356 return (type == OS_LOG_TYPE_INFO || type == OS_LOG_TYPE_DEBUG) ?
357 firehose_stream_memory : firehose_stream_persist;
358}
359
360static void
361_os_log_actual(os_log_type_t type, const char *format, void *dso, void *addr, uint8_t *logdata, size_t logdata_sz,
362 firehose_tracepoint_flags_t flags, bool driverKit)
363{
364 firehose_tracepoint_id_u trace_id;
365
366 firehose_stream_t stream = firehose_stream(type);
367 uint64_t timestamp = firehose_tracepoint_time(firehose_activity_flags_default);
368
369 if (driverKit) {
370 // set FIREHOSE_TRACEPOINT_PC_DYNAMIC_BIT so logd will not try to find the format string in
371 // the executable text
372 trace_id.ftid_value = FIREHOSE_TRACE_ID_MAKE(firehose_tracepoint_namespace_log,
373 type, flags, (uint32_t)((uintptr_t)addr | FIREHOSE_TRACEPOINT_PC_DYNAMIC_BIT));
374 } else {
375 // create trace_id after we've set additional flags
376 trace_id.ftid_value = FIREHOSE_TRACE_ID_MAKE(firehose_tracepoint_namespace_log,
377 type, flags, _os_trace_offset(dso, format, (_firehose_tracepoint_flags_activity_t)flags));
378 }
379
380
381 _firehose_trace(stream, trace_id, timestamp, logdata, logdata_sz, true);
382}
383
384static void *
385resolve_dso(const char *fmt, void *dso, void *addr, bool driverKit)
386{
387 kc_format_t kcformat = KCFormatUnknown;
388
389 if (!PE_get_primary_kc_format(&kcformat)) {
390 return NULL;
391 }
392
393 switch (kcformat) {
394 case KCFormatStatic:
395 case KCFormatKCGEN:
396 dso = PE_get_kc_baseaddress(KCKindPrimary);
397 break;
398 case KCFormatDynamic:
399 case KCFormatFileset:
400 if (!dso && (dso = (void *)OSKextKextForAddress(fmt)) == NULL) {
401 return NULL;
402 }
403 if (!_os_trace_addr_in_text_segment(dso, fmt)) {
404 return NULL;
405 }
406 if (!driverKit && (dso != (void *)OSKextKextForAddress(addr))) {
407 return NULL;
408 }
409 break;
410 default:
411 panic("unknown KC format type");
412 }
413
414 return dso;
415}
416
417static void
418_os_log_to_log_internal(os_log_type_t type, const char *fmt, va_list args, void *addr, void *dso, bool driverKit)
419{
420 counter_inc(&oslog_p_total_msgcount);
421
422 if (addr == NULL) {
423 counter_inc(&oslog_p_unresolved_kc_msgcount);
424 return;
425 }
426
427 if ((dso = resolve_dso(fmt, dso, addr, driverKit)) == NULL) {
428 counter_inc(&oslog_p_unresolved_kc_msgcount);
429 return;
430 }
431
432 uint8_t buffer[OS_LOG_BUFFER_MAX_SIZE] __attribute__((aligned(8))) = { 0 };
433 struct os_log_context_s ctx;
434
435 os_log_context_init(&ctx, &os_log_mem, buffer, sizeof(buffer));
436
437 if (os_log_context_encode(&ctx, fmt, args, addr, dso, driverKit)) {
438 _os_log_actual(type, fmt, dso, addr, ctx.ctx_buffer, ctx.ctx_content_sz,
439 ctx.ctx_ft_flags, driverKit);
440 } else {
441 counter_inc(&oslog_p_error_count);
442 }
443
444 os_log_context_free(&ctx);
445}
446
447bool
448os_log_coprocessor(void *buff, uint64_t buff_len, os_log_type_t type,
449 const char *uuid, uint64_t timestamp, uint32_t offset, bool stream_log)
450{
451 firehose_tracepoint_id_u trace_id;
452 firehose_tracepoint_id_t return_id = 0;
453 uint8_t pubdata[OS_LOG_BUFFER_MAX_SIZE];
454 size_t wr_pos = 0;
455
456 if (os_log_turned_off()) {
457 return false;
458 }
459
460 if (buff_len + 16 + sizeof(uint32_t) > OS_LOG_BUFFER_MAX_SIZE) {
461 return false;
462 }
463
464 firehose_stream_t stream = firehose_stream(type);
465 // unlike kext, where pc is used to find uuid, in coprocessor logs the uuid is passed as part of the tracepoint
466 firehose_tracepoint_flags_t flags = _firehose_tracepoint_flags_pc_style_uuid_relative;
467
468 memcpy(pubdata, &offset, sizeof(uint32_t));
469 wr_pos += sizeof(uint32_t);
470 memcpy(pubdata + wr_pos, uuid, 16);
471 wr_pos += 16;
472
473 memcpy(pubdata + wr_pos, buff, buff_len);
474
475 // create firehose trace id
476 trace_id.ftid_value = FIREHOSE_TRACE_ID_MAKE(firehose_tracepoint_namespace_log,
477 type, flags, offset);
478
479 counter_inc(&oslog_p_coprocessor_total_msgcount);
480
481 // send firehose tracepoint containing os log to firehose buffer
482 return_id = _firehose_trace(stream, trace_id, timestamp, pubdata,
483 buff_len + wr_pos, stream_log);
484
485 if (return_id == 0) {
486 counter_inc(&oslog_p_coprocessor_dropped_msgcount);
487 return false;
488 }
489 return true;
490}
491
492static inline firehose_tracepoint_id_t
493_firehose_trace(firehose_stream_t stream, firehose_tracepoint_id_u ftid,
494 uint64_t stamp, const void *pubdata, size_t publen, bool use_streaming)
495{
496 const uint16_t ft_size = offsetof(struct firehose_tracepoint_s, ft_data);
497 const size_t _firehose_chunk_payload_size =
498 sizeof(((struct firehose_chunk_s *)0)->fc_data);
499
500 firehose_tracepoint_t ft;
501
502 if (slowpath(ft_size + publen > _firehose_chunk_payload_size)) {
503 // We'll need to have some handling here. For now - return 0
504 counter_inc(&oslog_p_error_count);
505 return 0;
506 }
507
508 if (oslog_stream_open && (stream != firehose_stream_metadata) && use_streaming) {
509 stream_lock();
510 if (!oslog_stream_open) {
511 stream_unlock();
512 goto out;
513 }
514
515 oslog_s_total_msgcount++;
516 oslog_streamwrite_locked(ftid, stamp, pubdata, publen);
517 stream_unlock();
518 oslog_streamwakeup();
519 }
520
521out:
522 ft = __firehose_buffer_tracepoint_reserve(stamp, stream, (uint16_t)publen, 0, NULL);
523 if (!fastpath(ft)) {
524 if (oslog_boot_done) {
525 if (stream == firehose_stream_metadata) {
526 counter_inc(&oslog_p_metadata_dropped_msgcount);
527 } else {
528 // If we run out of space in the persistence buffer we're
529 // dropping the message.
530 counter_inc(&oslog_p_dropped_msgcount);
531 }
532 return 0;
533 }
534 firehose_chunk_t fbc = firehose_boot_chunk;
535 long offset;
536
537 //only stream available during boot is persist
538 offset = firehose_chunk_tracepoint_try_reserve(fbc, stamp,
539 firehose_stream_persist, 0, (uint16_t)publen, 0, NULL);
540 if (offset <= 0) {
541 counter_inc(&oslog_p_boot_dropped_msgcount);
542 return 0;
543 }
544
545 ft = firehose_chunk_tracepoint_begin(fbc, stamp, (uint16_t)publen,
546 thread_tid(current_thread()), offset);
547 memcpy(ft->ft_data, pubdata, publen);
548 firehose_chunk_tracepoint_end(fbc, ft, ftid);
549 counter_inc(&oslog_p_saved_msgcount);
550 return ftid.ftid_value;
551 }
552 if (!oslog_boot_done) {
553 oslog_boot_done = true;
554 }
555 memcpy(ft->ft_data, pubdata, publen);
556
557 __firehose_buffer_tracepoint_flush(ft, ftid);
558 if (stream == firehose_stream_metadata) {
559 counter_inc(&oslog_p_metadata_saved_msgcount);
560 } else {
561 counter_inc(&oslog_p_saved_msgcount);
562 }
563 return ftid.ftid_value;
564}
565
566static oslog_stream_buf_entry_t
567oslog_stream_create_buf_entry(oslog_stream_link_type_t type,
568 firehose_tracepoint_id_u ftid, uint64_t stamp,
569 const void* pubdata, size_t publen)
570{
571 const size_t ft_size = sizeof(struct firehose_tracepoint_s) + publen;
572 const size_t m_entry_len = sizeof(struct oslog_stream_buf_entry_s) + ft_size;
573 oslog_stream_buf_entry_t m_entry;
574
575 if (!pubdata || publen > UINT16_MAX || ft_size > UINT16_MAX) {
576 return NULL;
577 }
578
579 m_entry = kheap_alloc(KHEAP_DEFAULT, m_entry_len, Z_WAITOK);
580 if (!m_entry) {
581 return NULL;
582 }
583 m_entry->type = type;
584 m_entry->timestamp = stamp;
585 m_entry->size = (uint16_t)ft_size;
586
587 firehose_tracepoint_t ft = m_entry->metadata;
588 ft->ft_thread = thread_tid(current_thread());
589 ft->ft_id.ftid_value = ftid.ftid_value;
590 ft->ft_length = (uint16_t)publen;
591 memcpy(ft->ft_data, pubdata, publen);
592
593 return m_entry;
594}
595
596void
597os_log_coprocessor_register(const char *uuid, const char *file_path, bool copy)
598{
599 uint64_t stamp;
600 size_t path_size = strlen(file_path) + 1;
601 firehose_tracepoint_id_u trace_id;
602 size_t uuid_info_len = sizeof(struct firehose_trace_uuid_info_s) + path_size;
603 union {
604 struct firehose_trace_uuid_info_s uuid_info;
605 char path[PATH_MAX + sizeof(struct firehose_trace_uuid_info_s)];
606 } buf;
607
608 if (os_log_disabled()) {
609 return;
610 }
611
612 if (path_size > PATH_MAX) {
613 return;
614 }
615
616 // write metadata to uuid_info
617 memcpy(buf.uuid_info.ftui_uuid, uuid, sizeof(uuid_t));
618 buf.uuid_info.ftui_size = 1;
619 buf.uuid_info.ftui_address = 1;
620
621 stamp = firehose_tracepoint_time(firehose_activity_flags_default);
622
623 // create tracepoint id
624 trace_id.ftid_value = FIREHOSE_TRACE_ID_MAKE(firehose_tracepoint_namespace_metadata, _firehose_tracepoint_type_metadata_coprocessor,
625 (firehose_tracepoint_flags_t)0, copy ? firehose_tracepoint_code_load_memory : firehose_tracepoint_code_load_filesystem);
626
627 // write path to buffer
628 memcpy(buf.uuid_info.ftui_path, file_path, path_size);
629
630 // send metadata tracepoint to firehose for coprocessor registration in logd
631 firehose_trace_metadata(firehose_stream_metadata, trace_id, stamp, (void *)&buf, uuid_info_len);
632 return;
633}
634
635#ifdef KERNEL
636void
637firehose_trace_metadata(firehose_stream_t stream, firehose_tracepoint_id_u ftid,
638 uint64_t stamp, const void *pubdata, size_t publen)
639{
640 oslog_stream_buf_entry_t m_entry = NULL;
641
642 if (os_log_disabled()) {
643 return;
644 }
645
646 // If streaming mode is not on, only log the metadata
647 // in the persistence buffer
648
649 stream_lock();
650 if (!oslog_stream_open) {
651 stream_unlock();
652 goto finish;
653 }
654 stream_unlock();
655
656 // Setup and write the stream metadata entry
657 m_entry = oslog_stream_create_buf_entry(oslog_stream_link_type_metadata, ftid,
658 stamp, pubdata, publen);
659 if (!m_entry) {
660 counter_inc(&oslog_s_error_count);
661 goto finish;
662 }
663
664 stream_lock();
665 if (!oslog_stream_open) {
666 stream_unlock();
667 kheap_free(KHEAP_DEFAULT, m_entry,
668 sizeof(struct oslog_stream_buf_entry_s) +
669 sizeof(struct firehose_tracepoint_s) + publen);
670 goto finish;
671 }
672 oslog_s_metadata_msgcount++;
673 oslog_streamwrite_metadata_locked(m_entry);
674 stream_unlock();
675
676finish:
677 _firehose_trace(stream, ftid, stamp, pubdata, publen, true);
678}
679#endif
680
681void
682__firehose_buffer_push_to_logd(firehose_buffer_t fb __unused, bool for_io __unused)
683{
684 oslogwakeup();
685 return;
686}
687
688void
689__firehose_allocate(vm_offset_t *addr, vm_size_t size __unused)
690{
691 firehose_chunk_t kernel_buffer = (firehose_chunk_t)kernel_firehose_addr;
692
693 if (kernel_firehose_addr) {
694 *addr = kernel_firehose_addr;
695 } else {
696 *addr = 0;
697 return;
698 }
699 // Now that we are done adding logs to this chunk, set the number of writers to 0
700 // Without this, logd won't flush when the page is full
701 firehose_boot_chunk->fc_pos.fcp_refcnt = 0;
702 memcpy(&kernel_buffer[FIREHOSE_BUFFER_KERNEL_CHUNK_COUNT - 1], (const void *)firehose_boot_chunk, FIREHOSE_CHUNK_SIZE);
703 return;
704}
705// There isnt a lock held in this case.
706void
707__firehose_critical_region_enter(void)
708{
709 disable_preemption();
710 return;
711}
712
713void
714__firehose_critical_region_leave(void)
715{
716 enable_preemption();
717 return;
718}
719
720#ifdef CONFIG_XNUPOST
721
722#include <tests/xnupost.h>
723#define TESTOSLOGFMT(fn_name) "%u^%llu/%llu^kernel^0^test^" fn_name
724#define TESTOSLOGPFX "TESTLOG:%u#"
725#define TESTOSLOG(fn_name) TESTOSLOGPFX TESTOSLOGFMT(fn_name "#")
726
727extern u_int32_t RandomULong(void);
728extern size_t find_pattern_in_buffer(const char *pattern, size_t len, size_t expected_count);
729void test_oslog_default_helper(uint32_t uniqid, uint64_t count);
730void test_oslog_info_helper(uint32_t uniqid, uint64_t count);
731void test_oslog_debug_helper(uint32_t uniqid, uint64_t count);
732void test_oslog_error_helper(uint32_t uniqid, uint64_t count);
733void test_oslog_fault_helper(uint32_t uniqid, uint64_t count);
734void _test_log_loop(void * arg __unused, wait_result_t wres __unused);
735void test_oslog_handleOSLogCtl(int32_t * in, int32_t * out, int32_t len);
736kern_return_t test_stresslog_dropmsg(uint32_t uniqid);
737
738kern_return_t test_os_log(void);
739kern_return_t test_os_log_parallel(void);
740
741#define GENOSLOGHELPER(fname, ident, callout_f) \
742 void fname(uint32_t uniqid, uint64_t count) \
743 { \
744 int32_t datalen = 0; \
745 uint32_t checksum = 0; \
746 char databuffer[256]; \
747 T_LOG("Doing os_log of %llu TESTLOG msgs for fn " ident, count); \
748 for (uint64_t i = 0; i < count; i++) \
749 { \
750 datalen = scnprintf(databuffer, sizeof(databuffer), TESTOSLOGFMT(ident), uniqid, i + 1, count); \
751 checksum = crc32(0, databuffer, datalen); \
752 callout_f(OS_LOG_DEFAULT, TESTOSLOG(ident), checksum, uniqid, i + 1, count); \
753 /*T_LOG(TESTOSLOG(ident), checksum, uniqid, i + 1, count);*/ \
754 } \
755 }
756
757GENOSLOGHELPER(test_oslog_info_helper, "oslog_info_helper", os_log_info);
758GENOSLOGHELPER(test_oslog_fault_helper, "oslog_fault_helper", os_log_fault);
759GENOSLOGHELPER(test_oslog_debug_helper, "oslog_debug_helper", os_log_debug);
760GENOSLOGHELPER(test_oslog_error_helper, "oslog_error_helper", os_log_error);
761GENOSLOGHELPER(test_oslog_default_helper, "oslog_default_helper", os_log);
762
763kern_return_t
764test_os_log()
765{
766 char databuffer[256];
767 uint32_t uniqid = RandomULong();
768 size_t match_count = 0;
769 uint32_t checksum = 0;
770 uint32_t total_msg = 0;
771 uint32_t saved_msg = 0;
772 uint32_t dropped_msg = 0;
773 size_t datalen = 0;
774 uint64_t a = mach_absolute_time();
775 uint64_t seqno = 1;
776 uint64_t total_seqno = 2;
777
778 os_log_t log_handle = os_log_create("com.apple.xnu.test.t1", "kpost");
779
780 T_ASSERT_EQ_PTR(&_os_log_default, log_handle, "os_log_create returns valid value.");
781 T_ASSERT_EQ_INT(TRUE, os_log_info_enabled(log_handle), "os_log_info is enabled");
782 T_ASSERT_EQ_INT(TRUE, os_log_debug_enabled(log_handle), "os_log_debug is enabled");
783 T_ASSERT_EQ_PTR(&_os_log_default, OS_LOG_DEFAULT, "ensure OS_LOG_DEFAULT is _os_log_default");
784
785 total_msg = counter_load(&oslog_p_total_msgcount);
786 saved_msg = counter_load(&oslog_p_saved_msgcount);
787 dropped_msg = counter_load(&oslog_p_dropped_msgcount);
788 T_LOG("oslog internal counters total %u , saved %u, dropped %u", total_msg, saved_msg, dropped_msg);
789
790 T_LOG("Validating with uniqid %u u64 %llu", uniqid, a);
791 T_ASSERT_NE_UINT(0, uniqid, "random number should not be zero");
792 T_ASSERT_NE_ULLONG(0, a, "absolute time should not be zero");
793
794 datalen = scnprintf(databuffer, sizeof(databuffer), TESTOSLOGFMT("printf_only"), uniqid, seqno, total_seqno);
795 checksum = crc32(0, databuffer, datalen);
796 printf(TESTOSLOG("printf_only") "mat%llu\n", checksum, uniqid, seqno, total_seqno, a);
797
798 seqno += 1;
799 datalen = scnprintf(databuffer, sizeof(databuffer), TESTOSLOGFMT("printf_only"), uniqid, seqno, total_seqno);
800 checksum = crc32(0, databuffer, datalen);
801 printf(TESTOSLOG("printf_only") "mat%llu\n", checksum, uniqid, seqno, total_seqno, a);
802
803 datalen = scnprintf(databuffer, sizeof(databuffer), "kernel^0^test^printf_only#mat%llu", a);
804 match_count = find_pattern_in_buffer(databuffer, datalen, total_seqno);
805 T_EXPECT_EQ_ULONG(match_count, total_seqno, "verify printf_only goes to systemlog buffer");
806
807 uint32_t logging_config = atm_get_diagnostic_config();
808 T_LOG("checking atm_diagnostic_config 0x%X", logging_config);
809
810 if ((logging_config & ATM_TRACE_OFF) || (logging_config & ATM_TRACE_DISABLE)) {
811 T_LOG("ATM_TRACE_OFF / ATM_TRACE_DISABLE is set. Would not see oslog messages. skipping the rest of test.");
812 return KERN_SUCCESS;
813 }
814
815 /* for enabled logging printfs should be saved in oslog as well */
816 T_EXPECT_GE_UINT((counter_load(&oslog_p_total_msgcount) - total_msg), 2, "atleast 2 msgs should be seen by oslog system");
817
818 a = mach_absolute_time();
819 total_seqno = 1;
820 seqno = 1;
821 total_msg = counter_load(&oslog_p_total_msgcount);
822 saved_msg = counter_load(&oslog_p_saved_msgcount);
823 dropped_msg = counter_load(&oslog_p_dropped_msgcount);
824 datalen = scnprintf(databuffer, sizeof(databuffer), TESTOSLOGFMT("oslog_info"), uniqid, seqno, total_seqno);
825 checksum = crc32(0, databuffer, datalen);
826 os_log_info(log_handle, TESTOSLOG("oslog_info") "mat%llu", checksum, uniqid, seqno, total_seqno, a);
827 T_EXPECT_GE_UINT((counter_load(&oslog_p_total_msgcount) - total_msg), 1, "total message count in buffer");
828
829 datalen = scnprintf(databuffer, sizeof(databuffer), "kernel^0^test^oslog_info#mat%llu", a);
830 match_count = find_pattern_in_buffer(databuffer, datalen, total_seqno);
831 T_EXPECT_EQ_ULONG(match_count, total_seqno, "verify oslog_info does not go to systemlog buffer");
832
833 total_msg = counter_load(&oslog_p_total_msgcount);
834 test_oslog_info_helper(uniqid, 10);
835 T_EXPECT_GE_UINT(counter_load(&oslog_p_total_msgcount) - total_msg, 10, "test_oslog_info_helper: Should have seen 10 msgs");
836
837 total_msg = counter_load(&oslog_p_total_msgcount);
838 test_oslog_debug_helper(uniqid, 10);
839 T_EXPECT_GE_UINT(counter_load(&oslog_p_total_msgcount) - total_msg, 10, "test_oslog_debug_helper:Should have seen 10 msgs");
840
841 total_msg = counter_load(&oslog_p_total_msgcount);
842 test_oslog_error_helper(uniqid, 10);
843 T_EXPECT_GE_UINT(counter_load(&oslog_p_total_msgcount) - total_msg, 10, "test_oslog_error_helper:Should have seen 10 msgs");
844
845 total_msg = counter_load(&oslog_p_total_msgcount);
846 test_oslog_default_helper(uniqid, 10);
847 T_EXPECT_GE_UINT(counter_load(&oslog_p_total_msgcount) - total_msg, 10, "test_oslog_default_helper:Should have seen 10 msgs");
848
849 total_msg = counter_load(&oslog_p_total_msgcount);
850 test_oslog_fault_helper(uniqid, 10);
851 T_EXPECT_GE_UINT(counter_load(&oslog_p_total_msgcount) - total_msg, 10, "test_oslog_fault_helper:Should have seen 10 msgs");
852
853 T_LOG("oslog internal counters total %u , saved %u, dropped %u", counter_load(&oslog_p_total_msgcount), counter_load(&oslog_p_saved_msgcount),
854 counter_load(&oslog_p_dropped_msgcount));
855
856 return KERN_SUCCESS;
857}
858
859static uint32_t _test_log_loop_count = 0;
860void
861_test_log_loop(void * arg __unused, wait_result_t wres __unused)
862{
863 uint32_t uniqid = RandomULong();
864 test_oslog_debug_helper(uniqid, 100);
865 os_atomic_add(&_test_log_loop_count, 100, relaxed);
866}
867
868kern_return_t
869test_os_log_parallel(void)
870{
871 thread_t thread[2];
872 kern_return_t kr;
873 uint32_t uniqid = RandomULong();
874
875 printf("oslog internal counters total %lld , saved %lld, dropped %lld", counter_load(&oslog_p_total_msgcount), counter_load(&oslog_p_saved_msgcount),
876 counter_load(&oslog_p_dropped_msgcount));
877
878 kr = kernel_thread_start(_test_log_loop, NULL, &thread[0]);
879 T_ASSERT_EQ_INT(kr, KERN_SUCCESS, "kernel_thread_start returned successfully");
880
881 kr = kernel_thread_start(_test_log_loop, NULL, &thread[1]);
882 T_ASSERT_EQ_INT(kr, KERN_SUCCESS, "kernel_thread_start returned successfully");
883
884 test_oslog_info_helper(uniqid, 100);
885
886 /* wait until other thread has also finished */
887 while (_test_log_loop_count < 200) {
888 delay(1000);
889 }
890
891 thread_deallocate(thread[0]);
892 thread_deallocate(thread[1]);
893
894 T_LOG("oslog internal counters total %lld , saved %lld, dropped %lld", counter_load(&oslog_p_total_msgcount), counter_load(&oslog_p_saved_msgcount),
895 counter_load(&oslog_p_dropped_msgcount));
896 T_PASS("parallel_logging tests is now complete");
897
898 return KERN_SUCCESS;
899}
900
901void
902test_oslog_handleOSLogCtl(int32_t * in, int32_t * out, int32_t len)
903{
904 if (!in || !out || len != 4) {
905 return;
906 }
907 switch (in[0]) {
908 case 1:
909 {
910 /* send out counters */
911 out[1] = counter_load(&oslog_p_total_msgcount);
912 out[2] = counter_load(&oslog_p_saved_msgcount);
913 out[3] = counter_load(&oslog_p_dropped_msgcount);
914 out[0] = KERN_SUCCESS;
915 break;
916 }
917 case 2:
918 {
919 /* mini stress run */
920 out[0] = test_os_log_parallel();
921 break;
922 }
923 case 3:
924 {
925 /* drop msg tests */
926 out[1] = RandomULong();
927 out[0] = test_stresslog_dropmsg(out[1]);
928 break;
929 }
930 case 4:
931 {
932 /* invoke log helpers */
933 uint32_t uniqid = in[3];
934 int32_t msgcount = in[2];
935 if (uniqid == 0 || msgcount == 0) {
936 out[0] = KERN_INVALID_VALUE;
937 return;
938 }
939
940 switch (in[1]) {
941 case OS_LOG_TYPE_INFO: test_oslog_info_helper(uniqid, msgcount); break;
942 case OS_LOG_TYPE_DEBUG: test_oslog_debug_helper(uniqid, msgcount); break;
943 case OS_LOG_TYPE_ERROR: test_oslog_error_helper(uniqid, msgcount); break;
944 case OS_LOG_TYPE_FAULT: test_oslog_fault_helper(uniqid, msgcount); break;
945 case OS_LOG_TYPE_DEFAULT:
946 default: test_oslog_default_helper(uniqid, msgcount); break;
947 }
948 out[0] = KERN_SUCCESS;
949 break;
950 /* end of case 4 */
951 }
952 default:
953 {
954 out[0] = KERN_INVALID_VALUE;
955 break;
956 }
957 }
958 return;
959}
960
961kern_return_t
962test_stresslog_dropmsg(uint32_t uniqid)
963{
964 uint32_t total, saved, dropped;
965 total = counter_load(&oslog_p_total_msgcount);
966 saved = counter_load(&oslog_p_saved_msgcount);
967 dropped = counter_load(&oslog_p_dropped_msgcount);
968 uniqid = RandomULong();
969 test_oslog_debug_helper(uniqid, 100);
970 while ((counter_load(&oslog_p_dropped_msgcount) - dropped) == 0) {
971 test_oslog_debug_helper(uniqid, 100);
972 }
973 printf("test_stresslog_dropmsg: logged %lld msgs, saved %lld and caused a drop of %lld msgs. \n", counter_load(&oslog_p_total_msgcount) - total,
974 counter_load(&oslog_p_saved_msgcount) - saved, counter_load(&oslog_p_dropped_msgcount) - dropped);
975 return KERN_SUCCESS;
976}
977
978#endif