1 /* * Copyright (c) 2019 Apple Inc. All rights reserved. */
6 #include <kern/cpu_data.h>
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>
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>
31 #include <firehose/tracepoint_private.h>
32 #include <firehose/chunk_private.h>
33 #include <os/firehose_buffer_private.h>
34 #include <os/firehose.h>
36 #include <os/log_private.h>
37 #include "trace_internal.h"
39 #include "log_encode.h"
46 struct os_log_s _os_log_default
;
47 struct os_log_s _os_log_replay
;
49 LOGMEM_STATIC_INIT(os_log_mem
, 14, 9, 10);
51 extern vm_offset_t kernel_firehose_addr
;
52 extern firehose_chunk_t firehose_boot_chunk
;
54 extern bool bsd_log_lock(bool);
55 extern void bsd_log_unlock(void);
56 extern void logwakeup(struct msgbuf
*);
58 decl_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)
62 extern void oslog_streamwakeup(void);
63 void oslog_streamwrite_locked(firehose_tracepoint_id_u ftid
,
64 uint64_t stamp
, const void *pubdata
, size_t publen
);
65 extern void oslog_streamwrite_metadata_locked(oslog_stream_buf_entry_t m_entry
);
67 extern int oslog_stream_open
;
69 extern void *OSKextKextForAddress(const void *);
71 /* Counters for persistence mode */
72 SCALABLE_COUNTER_DEFINE(oslog_p_total_msgcount
);
73 SCALABLE_COUNTER_DEFINE(oslog_p_metadata_saved_msgcount
);
74 SCALABLE_COUNTER_DEFINE(oslog_p_metadata_dropped_msgcount
);
75 SCALABLE_COUNTER_DEFINE(oslog_p_error_count
);
76 SCALABLE_COUNTER_DEFINE(oslog_p_saved_msgcount
);
77 SCALABLE_COUNTER_DEFINE(oslog_p_dropped_msgcount
);
78 SCALABLE_COUNTER_DEFINE(oslog_p_boot_dropped_msgcount
);
79 SCALABLE_COUNTER_DEFINE(oslog_p_coprocessor_total_msgcount
);
80 SCALABLE_COUNTER_DEFINE(oslog_p_coprocessor_dropped_msgcount
);
81 SCALABLE_COUNTER_DEFINE(oslog_p_unresolved_kc_msgcount
);
83 /* Counters for streaming mode */
84 SCALABLE_COUNTER_DEFINE(oslog_s_error_count
);
85 /* Protected by the stream lock */
86 uint32_t oslog_s_total_msgcount
;
87 uint32_t oslog_s_metadata_msgcount
;
89 /* Counters for msgbuf logging */
90 SCALABLE_COUNTER_DEFINE(oslog_msgbuf_msgcount
)
91 SCALABLE_COUNTER_DEFINE(oslog_msgbuf_dropped_msgcount
)
93 static bool oslog_boot_done
= false;
95 #ifdef XNU_KERNEL_PRIVATE
96 bool startup_serial_logging_active
= true;
97 uint64_t startup_serial_num_procs
= 300;
98 #endif /* XNU_KERNEL_PRIVATE */
101 firehose_tracepoint_id_t
102 firehose_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
);
105 static 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
);
109 static oslog_stream_buf_entry_t
110 oslog_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
);
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
);
118 _os_log_to_msgbuf_internal(const char *format
, va_list args
, bool safe
, bool logging
, bool addcr
);
121 _os_log_to_log_internal(os_log_type_t type
, const char *format
, va_list args
, void *addr
, void *dso
, bool driverKit
);
124 os_log_turned_off(void)
126 return atm_get_diagnostic_config() & (ATM_TRACE_DISABLE
| ATM_TRACE_OFF
);
130 os_log_info_enabled(os_log_t log __unused
)
132 return !os_log_turned_off();
136 os_log_debug_enabled(os_log_t log __unused
)
138 return !os_log_turned_off();
142 os_log_disabled(void)
144 return atm_get_diagnostic_config() & ATM_TRACE_DISABLE
;
148 os_log_create(const char *subsystem __unused
, const char *category __unused
)
150 return &_os_log_default
;
153 __attribute__((noinline
, not_tail_called
)) void
154 _os_log_internal(void *dso
, os_log_t log
, uint8_t type
, const char *message
, ...)
157 void *addr
= __builtin_return_address(0);
159 va_start(args
, message
);
161 _os_log_with_args_internal(log
, type
, message
, args
, addr
, dso
, FALSE
, FALSE
);
168 __attribute__((noinline
, not_tail_called
)) int
169 _os_log_internal_driverKit(void *dso
, os_log_t log
, uint8_t type
, const char *message
, ...)
172 void *addr
= __builtin_return_address(0);
173 bool driverKitLog
= FALSE
;
176 * We want to be able to identify dexts from the logs.
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.
183 * Dext however are not loaded in kernel space so they do not
184 * have a kernel range of addresses.
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.
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.
195 task_t self_task
= current_task();
198 * Only dextis are supposed to use this log path.
200 if (!task_is_driver(self_task
)) {
204 uint64_t loadTag
= get_task_loadTag(self_task
);
207 addr
= (void*) loadTag
;
209 va_start(args
, message
);
211 _os_log_with_args_internal(log
, type
, message
, args
, addr
, dso
, driverKitLog
, true);
218 #pragma mark - shim functions
220 __attribute__((noinline
, not_tail_called
)) void
221 os_log_with_args(os_log_t oslog
, os_log_type_t type
, const char *format
, va_list args
, void *addr
)
223 // if no address passed, look it up
225 addr
= __builtin_return_address(0);
228 _os_log_with_args_internal(oslog
, type
, format
, args
, addr
, NULL
, FALSE
, FALSE
);
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
)
235 if (format
[0] == '\0') {
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();
243 if (oslog
!= &_os_log_replay
) {
244 _os_log_to_msgbuf_internal(format
, args
, safe
, logging
, addcr
);
247 if (safe
&& logging
) {
248 _os_log_to_log_internal(type
, format
, args
, addr
, dso
, driverKit
);
253 _os_log_to_msgbuf_internal(const char *format
, va_list args
, bool safe
, bool logging
, bool addcr
)
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
262 const int MSGBUF_TIMESTAMP_THRESHOLD
= 4096;
263 static int msgbufreplay
= -1;
264 static bool newlogline
= true;
267 if (!bsd_log_lock(safe
)) {
268 counter_inc(&oslog_msgbuf_dropped_msgcount
);
273 if (-1 == msgbufreplay
) {
274 msgbufreplay
= msgbufp
->msg_bufx
;
276 } else if (logging
&& (-1 != msgbufreplay
)) {
278 uint32_t localbuff_size
;
280 char *localbuff
, *p
, *s
, *next
, ch
;
282 position
= msgbufreplay
;
284 localbuff_size
= (msgbufp
->msg_size
+ 2); /* + '\n' + '\0' */
285 /* Size for non-blocking */
286 if (localbuff_size
> 4096) {
287 localbuff_size
= 4096;
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 */
296 * The message buffer is circular; start at the replay pointer, and
297 * make one loop up to write pointer - 1.
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
;
310 if (i
>= (localbuff_size
- 2)) {
317 localbuff
[i
++] = '\n';
322 while ((next
= strchr(s
, '\n'))) {
326 os_log(&_os_log_replay
, "%s", s
);
330 kheap_free(KHEAP_TEMP
, localbuff
, localbuff_size
);
335 /* Do not prepend timestamps when we are memory-constricted */
336 if (newlogline
&& (msgbufp
->msg_size
> MSGBUF_TIMESTAMP_THRESHOLD
)) {
338 clock_usec_t microsecs
;
339 const uint64_t timestamp
= firehose_tracepoint_time(firehose_activity_flags_default
);
340 absolutetime_to_microtime(timestamp
, &secs
, µsecs
);
341 printf_log_locked(FALSE
, "[%5lu.%06u]: ", (unsigned long)secs
, microsecs
);
344 va_copy(args_copy
, args
);
345 newlogline
= vprintf_log_locked(format
, args_copy
, addcr
);
350 counter_inc(&oslog_msgbuf_msgcount
);
353 static firehose_stream_t
354 firehose_stream(os_log_type_t type
)
356 return (type
== OS_LOG_TYPE_INFO
|| type
== OS_LOG_TYPE_DEBUG
) ?
357 firehose_stream_memory
: firehose_stream_persist
;
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
)
364 firehose_tracepoint_id_u trace_id
;
366 firehose_stream_t stream
= firehose_stream(type
);
367 uint64_t timestamp
= firehose_tracepoint_time(firehose_activity_flags_default
);
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
));
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
));
381 _firehose_trace(stream
, trace_id
, timestamp
, logdata
, logdata_sz
, true);
385 resolve_dso(const char *fmt
, void *dso
, void *addr
, bool driverKit
)
387 kc_format_t kcformat
= KCFormatUnknown
;
389 if (!PE_get_primary_kc_format(&kcformat
)) {
396 dso
= PE_get_kc_baseaddress(KCKindPrimary
);
398 case KCFormatDynamic
:
399 case KCFormatFileset
:
400 if (!dso
&& (dso
= (void *)OSKextKextForAddress(fmt
)) == NULL
) {
403 if (!_os_trace_addr_in_text_segment(dso
, fmt
)) {
406 if (!driverKit
&& (dso
!= (void *)OSKextKextForAddress(addr
))) {
411 panic("unknown KC format type");
418 _os_log_to_log_internal(os_log_type_t type
, const char *fmt
, va_list args
, void *addr
, void *dso
, bool driverKit
)
420 counter_inc(&oslog_p_total_msgcount
);
423 counter_inc(&oslog_p_unresolved_kc_msgcount
);
427 if ((dso
= resolve_dso(fmt
, dso
, addr
, driverKit
)) == NULL
) {
428 counter_inc(&oslog_p_unresolved_kc_msgcount
);
432 uint8_t buffer
[OS_LOG_BUFFER_MAX_SIZE
] __attribute__((aligned(8))) = { 0 };
433 struct os_log_context_s ctx
;
435 os_log_context_init(&ctx
, &os_log_mem
, buffer
, sizeof(buffer
));
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
);
441 counter_inc(&oslog_p_error_count
);
444 os_log_context_free(&ctx
);
448 os_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
)
451 firehose_tracepoint_id_u trace_id
;
452 firehose_tracepoint_id_t return_id
= 0;
453 uint8_t pubdata
[OS_LOG_BUFFER_MAX_SIZE
];
456 if (os_log_turned_off()) {
460 if (buff_len
+ 16 + sizeof(uint32_t) > OS_LOG_BUFFER_MAX_SIZE
) {
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
;
468 memcpy(pubdata
, &offset
, sizeof(uint32_t));
469 wr_pos
+= sizeof(uint32_t);
470 memcpy(pubdata
+ wr_pos
, uuid
, 16);
473 memcpy(pubdata
+ wr_pos
, buff
, buff_len
);
475 // create firehose trace id
476 trace_id
.ftid_value
= FIREHOSE_TRACE_ID_MAKE(firehose_tracepoint_namespace_log
,
477 type
, flags
, offset
);
479 counter_inc(&oslog_p_coprocessor_total_msgcount
);
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
);
485 if (return_id
== 0) {
486 counter_inc(&oslog_p_coprocessor_dropped_msgcount
);
492 static 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
)
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
);
500 firehose_tracepoint_t ft
;
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
);
508 if (oslog_stream_open
&& (stream
!= firehose_stream_metadata
) && use_streaming
) {
510 if (!oslog_stream_open
) {
515 oslog_s_total_msgcount
++;
516 oslog_streamwrite_locked(ftid
, stamp
, pubdata
, publen
);
518 oslog_streamwakeup();
522 ft
= __firehose_buffer_tracepoint_reserve(stamp
, stream
, (uint16_t)publen
, 0, NULL
);
524 if (oslog_boot_done
) {
525 if (stream
== firehose_stream_metadata
) {
526 counter_inc(&oslog_p_metadata_dropped_msgcount
);
528 // If we run out of space in the persistence buffer we're
529 // dropping the message.
530 counter_inc(&oslog_p_dropped_msgcount
);
534 firehose_chunk_t fbc
= firehose_boot_chunk
;
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
);
541 counter_inc(&oslog_p_boot_dropped_msgcount
);
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
;
552 if (!oslog_boot_done
) {
553 oslog_boot_done
= true;
555 memcpy(ft
->ft_data
, pubdata
, publen
);
557 __firehose_buffer_tracepoint_flush(ft
, ftid
);
558 if (stream
== firehose_stream_metadata
) {
559 counter_inc(&oslog_p_metadata_saved_msgcount
);
561 counter_inc(&oslog_p_saved_msgcount
);
563 return ftid
.ftid_value
;
566 static oslog_stream_buf_entry_t
567 oslog_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
)
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
;
575 if (!pubdata
|| publen
> UINT16_MAX
|| ft_size
> UINT16_MAX
) {
579 m_entry
= kheap_alloc(KHEAP_DEFAULT
, m_entry_len
, Z_WAITOK
);
583 m_entry
->type
= type
;
584 m_entry
->timestamp
= stamp
;
585 m_entry
->size
= (uint16_t)ft_size
;
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
);
597 os_log_coprocessor_register(const char *uuid
, const char *file_path
, bool copy
)
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
;
604 struct firehose_trace_uuid_info_s uuid_info
;
605 char path
[PATH_MAX
+ sizeof(struct firehose_trace_uuid_info_s
)];
608 if (os_log_disabled()) {
612 if (path_size
> PATH_MAX
) {
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;
621 stamp
= firehose_tracepoint_time(firehose_activity_flags_default
);
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
);
627 // write path to buffer
628 memcpy(buf
.uuid_info
.ftui_path
, file_path
, path_size
);
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
);
637 firehose_trace_metadata(firehose_stream_t stream
, firehose_tracepoint_id_u ftid
,
638 uint64_t stamp
, const void *pubdata
, size_t publen
)
640 oslog_stream_buf_entry_t m_entry
= NULL
;
642 if (os_log_disabled()) {
646 // If streaming mode is not on, only log the metadata
647 // in the persistence buffer
650 if (!oslog_stream_open
) {
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
);
660 counter_inc(&oslog_s_error_count
);
665 if (!oslog_stream_open
) {
667 kheap_free(KHEAP_DEFAULT
, m_entry
,
668 sizeof(struct oslog_stream_buf_entry_s
) +
669 sizeof(struct firehose_tracepoint_s
) + publen
);
672 oslog_s_metadata_msgcount
++;
673 oslog_streamwrite_metadata_locked(m_entry
);
677 _firehose_trace(stream
, ftid
, stamp
, pubdata
, publen
, true);
682 __firehose_buffer_push_to_logd(firehose_buffer_t fb __unused
, bool for_io __unused
)
689 __firehose_allocate(vm_offset_t
*addr
, vm_size_t size __unused
)
691 firehose_chunk_t kernel_buffer
= (firehose_chunk_t
)kernel_firehose_addr
;
693 if (kernel_firehose_addr
) {
694 *addr
= kernel_firehose_addr
;
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
);
705 // There isnt a lock held in this case.
707 __firehose_critical_region_enter(void)
709 disable_preemption();
714 __firehose_critical_region_leave(void)
720 #ifdef CONFIG_XNUPOST
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 "#")
727 extern u_int32_t
RandomULong(void);
728 extern size_t find_pattern_in_buffer(const char *pattern
, size_t len
, size_t expected_count
);
729 void test_oslog_default_helper(uint32_t uniqid
, uint64_t count
);
730 void test_oslog_info_helper(uint32_t uniqid
, uint64_t count
);
731 void test_oslog_debug_helper(uint32_t uniqid
, uint64_t count
);
732 void test_oslog_error_helper(uint32_t uniqid
, uint64_t count
);
733 void test_oslog_fault_helper(uint32_t uniqid
, uint64_t count
);
734 void _test_log_loop(void * arg __unused
, wait_result_t wres __unused
);
735 void test_oslog_handleOSLogCtl(int32_t * in
, int32_t * out
, int32_t len
);
736 kern_return_t
test_stresslog_dropmsg(uint32_t uniqid
);
738 kern_return_t
test_os_log(void);
739 kern_return_t
test_os_log_parallel(void);
741 #define GENOSLOGHELPER(fname, ident, callout_f) \
742 void fname(uint32_t uniqid, uint64_t count) \
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++) \
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);*/ \
757 GENOSLOGHELPER(test_oslog_info_helper
, "oslog_info_helper", os_log_info
);
758 GENOSLOGHELPER(test_oslog_fault_helper
, "oslog_fault_helper", os_log_fault
);
759 GENOSLOGHELPER(test_oslog_debug_helper
, "oslog_debug_helper", os_log_debug
);
760 GENOSLOGHELPER(test_oslog_error_helper
, "oslog_error_helper", os_log_error
);
761 GENOSLOGHELPER(test_oslog_default_helper
, "oslog_default_helper", os_log
);
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;
774 uint64_t a
= mach_absolute_time();
776 uint64_t total_seqno
= 2;
778 os_log_t log_handle
= os_log_create("com.apple.xnu.test.t1", "kpost");
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");
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
);
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");
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
);
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
);
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");
807 uint32_t logging_config
= atm_get_diagnostic_config();
808 T_LOG("checking atm_diagnostic_config 0x%X", logging_config
);
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.");
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");
818 a
= mach_absolute_time();
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");
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");
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");
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");
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");
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");
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");
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
));
859 static uint32_t _test_log_loop_count
= 0;
861 _test_log_loop(void * arg __unused
, wait_result_t wres __unused
)
863 uint32_t uniqid
= RandomULong();
864 test_oslog_debug_helper(uniqid
, 100);
865 os_atomic_add(&_test_log_loop_count
, 100, relaxed
);
869 test_os_log_parallel(void)
873 uint32_t uniqid
= RandomULong();
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
));
878 kr
= kernel_thread_start(_test_log_loop
, NULL
, &thread
[0]);
879 T_ASSERT_EQ_INT(kr
, KERN_SUCCESS
, "kernel_thread_start returned successfully");
881 kr
= kernel_thread_start(_test_log_loop
, NULL
, &thread
[1]);
882 T_ASSERT_EQ_INT(kr
, KERN_SUCCESS
, "kernel_thread_start returned successfully");
884 test_oslog_info_helper(uniqid
, 100);
886 /* wait until other thread has also finished */
887 while (_test_log_loop_count
< 200) {
891 thread_deallocate(thread
[0]);
892 thread_deallocate(thread
[1]);
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");
902 test_oslog_handleOSLogCtl(int32_t * in
, int32_t * out
, int32_t len
)
904 if (!in
|| !out
|| len
!= 4) {
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
;
919 /* mini stress run */
920 out
[0] = test_os_log_parallel();
926 out
[1] = RandomULong();
927 out
[0] = test_stresslog_dropmsg(out
[1]);
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
;
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;
948 out
[0] = KERN_SUCCESS
;
954 out
[0] = KERN_INVALID_VALUE
;
962 test_stresslog_dropmsg(uint32_t uniqid
)
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);
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
);