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"
45 struct os_log_s _os_log_default
;
46 struct os_log_s _os_log_replay
;
47 extern vm_offset_t kernel_firehose_addr
;
48 extern firehose_chunk_t firehose_boot_chunk
;
50 extern bool bsd_log_lock(bool);
51 extern void bsd_log_unlock(void);
52 extern void logwakeup(struct msgbuf
*);
54 decl_lck_spin_data(extern, oslog_stream_lock
);
55 #define stream_lock() lck_spin_lock(&oslog_stream_lock)
56 #define stream_unlock() lck_spin_unlock(&oslog_stream_lock)
58 extern void oslog_streamwakeup(void);
59 void oslog_streamwrite_locked(firehose_tracepoint_id_u ftid
,
60 uint64_t stamp
, const void *pubdata
, size_t publen
);
61 extern void oslog_streamwrite_metadata_locked(oslog_stream_buf_entry_t m_entry
);
63 extern int oslog_stream_open
;
65 extern void *OSKextKextForAddress(const void *);
67 /* Counters for persistence mode */
68 uint32_t oslog_p_total_msgcount
= 0;
69 uint32_t oslog_p_metadata_saved_msgcount
= 0;
70 uint32_t oslog_p_metadata_dropped_msgcount
= 0;
71 uint32_t oslog_p_error_count
= 0;
72 uint32_t oslog_p_saved_msgcount
= 0;
73 uint32_t oslog_p_dropped_msgcount
= 0;
74 uint32_t oslog_p_boot_dropped_msgcount
= 0;
75 uint32_t oslog_p_coprocessor_total_msgcount
= 0;
76 uint32_t oslog_p_coprocessor_dropped_msgcount
= 0;
78 /* Counters for streaming mode */
79 uint32_t oslog_s_total_msgcount
= 0;
80 uint32_t oslog_s_error_count
= 0;
81 uint32_t oslog_s_metadata_msgcount
= 0;
83 /* Counters for msgbuf logging */
84 uint32_t oslog_msgbuf_msgcount
= 0;
85 uint32_t oslog_msgbuf_dropped_msgcount
= 0;
87 static bool oslog_boot_done
= false;
89 #ifdef XNU_KERNEL_PRIVATE
90 bool startup_serial_logging_active
= true;
91 uint64_t startup_serial_num_procs
= 300;
92 #endif /* XNU_KERNEL_PRIVATE */
95 firehose_tracepoint_id_t
96 firehose_debug_trace(firehose_stream_t stream
, firehose_tracepoint_id_t trace_id
,
97 uint64_t timestamp
, const char *format
, const void *pubdata
, size_t publen
);
99 static inline firehose_tracepoint_id_t
100 _firehose_trace(firehose_stream_t stream
, firehose_tracepoint_id_u ftid
,
101 uint64_t stamp
, const void *pubdata
, size_t publen
, bool use_streaming
);
103 static oslog_stream_buf_entry_t
104 oslog_stream_create_buf_entry(oslog_stream_link_type_t type
, firehose_tracepoint_id_u ftid
,
105 uint64_t stamp
, const void* pubdata
, size_t publen
);
108 _os_log_with_args_internal(os_log_t oslog __unused
, os_log_type_t type __unused
,
109 const char *format
, va_list args
, void *addr
, void *dso
, bool driverKit
, bool addcr
);
112 _os_log_to_msgbuf_internal(const char *format
, va_list args
, bool safe
, bool logging
, bool addcr
);
115 _os_log_to_log_internal(os_log_t oslog
, os_log_type_t type
,
116 const char *format
, va_list args
, void *addr
, void *dso
, bool driverKit
);
120 _os_log_actual(os_log_t oslog
, os_log_type_t type
, const char *format
, void
121 *dso
, void *addr
, os_log_buffer_context_t context
, bool driverKit
);
124 os_log_info_enabled(os_log_t log __unused
)
130 os_log_debug_enabled(os_log_t log __unused
)
136 os_log_create(const char *subsystem __unused
, const char *category __unused
)
138 return &_os_log_default
;
142 _os_log_string_is_public(const char *str __unused
)
147 __attribute__((noinline
, not_tail_called
)) void
148 _os_log_internal(void *dso
, os_log_t log
, uint8_t type
, const char *message
, ...)
151 void *addr
= __builtin_return_address(0);
153 va_start(args
, message
);
155 _os_log_with_args_internal(log
, type
, message
, args
, addr
, dso
, FALSE
, FALSE
);
162 __attribute__((noinline
, not_tail_called
)) int
163 _os_log_internal_driverKit(void *dso
, os_log_t log
, uint8_t type
, const char *message
, ...)
166 void *addr
= __builtin_return_address(0);
167 bool driverKitLog
= FALSE
;
170 * We want to be able to identify dexts from the logs.
172 * Usually the addr is used to understand if the log line
173 * was generated by a kext or the kernel main executable.
174 * Logd uses copyKextUUIDForAddress with the addr specified
175 * in the log line to retrieve the kext UUID of the sender.
177 * Dext however are not loaded in kernel space so they do not
178 * have a kernel range of addresses.
180 * To make the same mechanism work, OSKext fakes a kernel
181 * address range for dexts using the loadTag,
182 * so we just need to use the loadTag as addr here
183 * to allow logd to retrieve the correct UUID.
185 * NOTE: loadTag is populated in the task when the dext is matching,
186 * so if log lines are generated before the matching they will be
187 * identified as kernel main executable.
189 task_t self_task
= current_task();
192 * Only dextis are supposed to use this log path.
194 if (!task_is_driver(self_task
)) {
198 uint64_t loadTag
= get_task_loadTag(self_task
);
201 addr
= (void*) loadTag
;
203 va_start(args
, message
);
205 _os_log_with_args_internal(log
, type
, message
, args
, addr
, dso
, driverKitLog
, true);
212 #pragma mark - shim functions
214 __attribute__((noinline
, not_tail_called
)) void
215 os_log_with_args(os_log_t oslog
, os_log_type_t type
, const char *format
, va_list args
, void *addr
)
217 // if no address passed, look it up
219 addr
= __builtin_return_address(0);
222 _os_log_with_args_internal(oslog
, type
, format
, args
, addr
, NULL
, FALSE
, FALSE
);
226 _os_log_with_args_internal(os_log_t oslog
, os_log_type_t type
,
227 const char *format
, va_list args
, void *addr
, void *dso
, bool driverKit
, bool addcr
)
229 uint32_t logging_config
= atm_get_diagnostic_config();
233 if (format
[0] == '\0') {
237 /* early boot can log to dmesg for later replay (27307943) */
238 safe
= (startup_phase
< STARTUP_SUB_EARLY_BOOT
|| oslog_is_safe());
240 if (logging_config
& ATM_TRACE_DISABLE
|| logging_config
& ATM_TRACE_OFF
) {
246 if (oslog
!= &_os_log_replay
) {
247 _os_log_to_msgbuf_internal(format
, args
, safe
, logging
, addcr
);
250 if (safe
&& logging
) {
251 _os_log_to_log_internal(oslog
, type
, format
, args
, addr
, dso
, driverKit
);
256 _os_log_to_msgbuf_internal(const char *format
, va_list args
, bool safe
, bool logging
, bool addcr
)
259 * The following threshold was determined empirically as the point where
260 * it would be more advantageous to be able to fit in more log lines than
261 * to know exactly when a log line was printed out. We don't want to use up
262 * a large percentage of the log buffer on timestamps in a memory-constricted
265 const int MSGBUF_TIMESTAMP_THRESHOLD
= 4096;
266 static int msgbufreplay
= -1;
267 static bool newlogline
= true;
270 if (!bsd_log_lock(safe
)) {
271 os_atomic_inc(&oslog_msgbuf_dropped_msgcount
, relaxed
);
276 if (-1 == msgbufreplay
) {
277 msgbufreplay
= msgbufp
->msg_bufx
;
279 } else if (logging
&& (-1 != msgbufreplay
)) {
281 uint32_t localbuff_size
;
283 char *localbuff
, *p
, *s
, *next
, ch
;
285 position
= msgbufreplay
;
287 localbuff_size
= (msgbufp
->msg_size
+ 2); /* + '\n' + '\0' */
288 /* Size for non-blocking */
289 if (localbuff_size
> 4096) {
290 localbuff_size
= 4096;
293 /* Allocate a temporary non-circular buffer */
294 localbuff
= kheap_alloc(KHEAP_TEMP
, localbuff_size
, Z_NOWAIT
);
295 if (localbuff
!= NULL
) {
296 /* in between here, the log could become bigger, but that's fine */
299 * The message buffer is circular; start at the replay pointer, and
300 * make one loop up to write pointer - 1.
302 p
= msgbufp
->msg_bufc
+ position
;
303 for (i
= newl
= 0; p
!= msgbufp
->msg_bufc
+ msgbufp
->msg_bufx
- 1; ++p
) {
304 if (p
>= msgbufp
->msg_bufc
+ msgbufp
->msg_size
) {
305 p
= msgbufp
->msg_bufc
;
313 if (i
>= (localbuff_size
- 2)) {
320 localbuff
[i
++] = '\n';
325 while ((next
= strchr(s
, '\n'))) {
329 os_log(&_os_log_replay
, "%s", s
);
333 kheap_free(KHEAP_TEMP
, localbuff
, localbuff_size
);
338 /* Do not prepend timestamps when we are memory-constricted */
339 if (newlogline
&& (msgbufp
->msg_size
> MSGBUF_TIMESTAMP_THRESHOLD
)) {
341 clock_usec_t microsecs
;
342 const uint64_t timestamp
= firehose_tracepoint_time(firehose_activity_flags_default
);
343 absolutetime_to_microtime(timestamp
, &secs
, µsecs
);
344 printf_log_locked(FALSE
, "[%5lu.%06u]: ", (unsigned long)secs
, microsecs
);
347 va_copy(args_copy
, args
);
348 newlogline
= vprintf_log_locked(format
, args_copy
, addcr
);
353 os_atomic_inc(&oslog_msgbuf_msgcount
, relaxed
);
357 _os_log_to_log_internal(os_log_t oslog
, os_log_type_t type
,
358 const char *format
, va_list args
, void *addr
, void *dso
, bool driverKit
)
360 kc_format_t kcformat
= KCFormatUnknown
;
361 struct os_log_buffer_context_s context
;
362 unsigned char buffer_data
[OS_LOG_BUFFER_MAX_SIZE
] __attribute__((aligned(8)));
363 os_log_buffer_t buffer
= (os_log_buffer_t
)buffer_data
;
364 uint8_t pubdata
[OS_LOG_BUFFER_MAX_SIZE
];
371 if (!PE_get_primary_kc_format(&kcformat
)) {
375 if (kcformat
== KCFormatStatic
|| kcformat
== KCFormatKCGEN
) {
376 void *baseAddress
= PE_get_kc_baseaddress(KCKindPrimary
);
381 } else if (kcformat
== KCFormatDynamic
|| kcformat
== KCFormatFileset
) {
383 dso
= (void *) OSKextKextForAddress(format
);
388 if (!_os_trace_addr_in_text_segment(dso
, format
)) {
392 void *dso_addr
= (void *) OSKextKextForAddress(addr
);
393 if (dso
!= dso_addr
) {
399 memset(&context
, 0, sizeof(context
));
400 memset(buffer
, 0, OS_LOG_BUFFER_MAX_SIZE
);
402 context
.shimmed
= true;
403 context
.buffer
= buffer
;
404 context
.content_sz
= OS_LOG_BUFFER_MAX_SIZE
- sizeof(*buffer
);
405 context
.pubdata
= pubdata
;
406 context
.pubdata_sz
= sizeof(pubdata
);
408 va_copy(args_copy
, args
);
410 os_atomic_inc(&oslog_p_total_msgcount
, relaxed
);
411 if (_os_log_encode(format
, args_copy
, 0, &context
)) {
412 _os_log_actual(oslog
, type
, format
, dso
, addr
, &context
, driverKit
);
414 os_atomic_inc(&oslog_p_error_count
, relaxed
);
421 _os_trace_write_location_for_address(uint8_t buf
[static sizeof(uint64_t)],
422 void *dso
, const void *address
, firehose_tracepoint_flags_t
*flags
, __unused
bool driverKit
)
424 uintptr_t shift_addr
= (uintptr_t)address
- (uintptr_t)dso
;
426 kc_format_t kcformat
= KCFormatUnknown
;
427 __assert_only
bool result
= PE_get_primary_kc_format(&kcformat
);
430 if (kcformat
== KCFormatStatic
|| kcformat
== KCFormatKCGEN
) {
431 *flags
= _firehose_tracepoint_flags_pc_style_shared_cache
;
432 memcpy(buf
, (uint32_t[]){ (uint32_t)shift_addr
}, sizeof(uint32_t));
433 return sizeof(uint32_t);
435 kernel_mach_header_t
*mh
= dso
;
438 * driverKit will have the dso set as MH_EXECUTE
439 * (it is logging from a syscall in the kernel)
440 * but needs logd to parse the address as an
443 if (mh
->filetype
== MH_EXECUTE
&& !driverKit
) {
444 *flags
= _firehose_tracepoint_flags_pc_style_main_exe
;
445 memcpy(buf
, (uint32_t[]){ (uint32_t)shift_addr
}, sizeof(uint32_t));
446 return sizeof(uint32_t);
448 *flags
= _firehose_tracepoint_flags_pc_style_absolute
;
450 shift_addr
= VM_KERNEL_UNSLIDE(address
);
452 shift_addr
= (uintptr_t) address
;
454 memcpy(buf
, (uintptr_t[]){ shift_addr
}, sizeof(uintptr_t));
456 return 6; // 48 bits are enough
458 return sizeof(uintptr_t);
467 _os_log_buffer_pack(uint8_t *buffdata
, size_t buffdata_sz
,
468 os_log_buffer_context_t ctx
)
470 os_log_buffer_t buffer
= ctx
->buffer
;
471 size_t buffer_sz
= sizeof(*ctx
->buffer
) + ctx
->content_sz
;
472 size_t total_sz
= buffer_sz
+ ctx
->pubdata_sz
;
474 if (total_sz
> buffdata_sz
) {
478 memcpy(buffdata
, buffer
, buffer_sz
);
479 memcpy(&buffdata
[buffer_sz
], ctx
->pubdata
, ctx
->pubdata_sz
);
484 _os_log_actual(os_log_t oslog __unused
, os_log_type_t type
, const char *format
,
485 void *dso
, void *addr
, os_log_buffer_context_t context
, bool driverKit
)
487 firehose_stream_t stream
;
488 firehose_tracepoint_flags_t flags
= 0;
489 firehose_tracepoint_id_u trace_id
;
490 uint8_t buffdata
[OS_LOG_BUFFER_MAX_SIZE
];
491 size_t addr_len
= 0, buffdata_sz
;
495 // dso == the start of the binary that was loaded
496 addr_len
= _os_trace_write_location_for_address(buffdata
, dso
, addr
, &flags
, driverKit
);
497 buffdata_sz
= _os_log_buffer_pack(buffdata
+ addr_len
,
498 sizeof(buffdata
) - addr_len
, context
);
499 if (buffdata_sz
== 0) {
502 buffdata_sz
+= addr_len
;
504 timestamp
= firehose_tracepoint_time(firehose_activity_flags_default
);
505 thread_id
= thread_tid(current_thread());
508 // set FIREHOSE_TRACEPOINT_PC_DYNAMIC_BIT so logd will not try to find the format string in
509 // the executable text
510 trace_id
.ftid_value
= FIREHOSE_TRACE_ID_MAKE(firehose_tracepoint_namespace_log
,
511 type
, flags
, (uint32_t)((uintptr_t)addr
| FIREHOSE_TRACEPOINT_PC_DYNAMIC_BIT
));
513 // create trace_id after we've set additional flags
514 trace_id
.ftid_value
= FIREHOSE_TRACE_ID_MAKE(firehose_tracepoint_namespace_log
,
515 type
, flags
, _os_trace_offset(dso
, format
, (_firehose_tracepoint_flags_activity_t
)flags
));
518 if (type
== OS_LOG_TYPE_INFO
|| type
== OS_LOG_TYPE_DEBUG
) {
519 stream
= firehose_stream_memory
;
521 stream
= firehose_stream_persist
;
523 _firehose_trace(stream
, trace_id
, timestamp
, buffdata
, buffdata_sz
, true);
527 os_log_coprocessor(void *buff
, uint64_t buff_len
, os_log_type_t type
,
528 const char *uuid
, uint64_t timestamp
, uint32_t offset
, bool stream_log
)
530 firehose_tracepoint_id_u trace_id
;
531 firehose_tracepoint_id_t return_id
= 0;
532 firehose_stream_t stream
;
533 uint8_t pubdata
[OS_LOG_BUFFER_MAX_SIZE
];
536 if (buff_len
+ 16 + sizeof(uint32_t) > OS_LOG_BUFFER_MAX_SIZE
) {
540 // unlike kext, where pc is used to find uuid, in coprocessor logs the uuid is passed as part of the tracepoint
541 firehose_tracepoint_flags_t flags
= _firehose_tracepoint_flags_pc_style_uuid_relative
;
543 memcpy(pubdata
, &offset
, sizeof(uint32_t));
544 wr_pos
+= sizeof(uint32_t);
545 memcpy(pubdata
+ wr_pos
, uuid
, 16);
548 memcpy(pubdata
+ wr_pos
, buff
, buff_len
);
550 // create firehose trace id
551 trace_id
.ftid_value
= FIREHOSE_TRACE_ID_MAKE(firehose_tracepoint_namespace_log
,
552 type
, flags
, offset
);
554 if (type
== OS_LOG_TYPE_INFO
|| type
== OS_LOG_TYPE_DEBUG
) {
555 stream
= firehose_stream_memory
;
557 stream
= firehose_stream_persist
;
560 os_atomic_inc(&oslog_p_coprocessor_total_msgcount
, relaxed
);
562 // send firehose tracepoint containing os log to firehose buffer
563 return_id
= _firehose_trace(stream
, trace_id
, timestamp
, pubdata
,
564 buff_len
+ wr_pos
, stream_log
);
566 if (return_id
== 0) {
567 os_atomic_inc(&oslog_p_coprocessor_dropped_msgcount
, relaxed
);
573 static inline firehose_tracepoint_id_t
574 _firehose_trace(firehose_stream_t stream
, firehose_tracepoint_id_u ftid
,
575 uint64_t stamp
, const void *pubdata
, size_t publen
, bool use_streaming
)
577 const uint16_t ft_size
= offsetof(struct firehose_tracepoint_s
, ft_data
);
578 const size_t _firehose_chunk_payload_size
=
579 sizeof(((struct firehose_chunk_s
*)0)->fc_data
);
581 firehose_tracepoint_t ft
;
583 if (slowpath(ft_size
+ publen
> _firehose_chunk_payload_size
)) {
584 // We'll need to have some handling here. For now - return 0
585 os_atomic_inc(&oslog_p_error_count
, relaxed
);
589 if (oslog_stream_open
&& (stream
!= firehose_stream_metadata
) && use_streaming
) {
591 if (!oslog_stream_open
) {
596 oslog_s_total_msgcount
++;
597 oslog_streamwrite_locked(ftid
, stamp
, pubdata
, publen
);
599 oslog_streamwakeup();
603 ft
= __firehose_buffer_tracepoint_reserve(stamp
, stream
, (uint16_t)publen
, 0, NULL
);
605 if (oslog_boot_done
) {
606 if (stream
== firehose_stream_metadata
) {
607 os_atomic_inc(&oslog_p_metadata_dropped_msgcount
, relaxed
);
609 // If we run out of space in the persistence buffer we're
610 // dropping the message.
611 os_atomic_inc(&oslog_p_dropped_msgcount
, relaxed
);
615 firehose_chunk_t fbc
= firehose_boot_chunk
;
618 //only stream available during boot is persist
619 offset
= firehose_chunk_tracepoint_try_reserve(fbc
, stamp
,
620 firehose_stream_persist
, 0, (uint16_t)publen
, 0, NULL
);
622 os_atomic_inc(&oslog_p_boot_dropped_msgcount
, relaxed
);
626 ft
= firehose_chunk_tracepoint_begin(fbc
, stamp
, (uint16_t)publen
,
627 thread_tid(current_thread()), offset
);
628 memcpy(ft
->ft_data
, pubdata
, publen
);
629 firehose_chunk_tracepoint_end(fbc
, ft
, ftid
);
630 os_atomic_inc(&oslog_p_saved_msgcount
, relaxed
);
631 return ftid
.ftid_value
;
633 if (!oslog_boot_done
) {
634 oslog_boot_done
= true;
636 memcpy(ft
->ft_data
, pubdata
, publen
);
638 __firehose_buffer_tracepoint_flush(ft
, ftid
);
639 if (stream
== firehose_stream_metadata
) {
640 os_atomic_inc(&oslog_p_metadata_saved_msgcount
, relaxed
);
642 os_atomic_inc(&oslog_p_saved_msgcount
, relaxed
);
644 return ftid
.ftid_value
;
647 static oslog_stream_buf_entry_t
648 oslog_stream_create_buf_entry(oslog_stream_link_type_t type
,
649 firehose_tracepoint_id_u ftid
, uint64_t stamp
,
650 const void* pubdata
, size_t publen
)
652 const size_t ft_size
= sizeof(struct firehose_tracepoint_s
) + publen
;
653 const size_t m_entry_len
= sizeof(struct oslog_stream_buf_entry_s
) + ft_size
;
654 oslog_stream_buf_entry_t m_entry
;
656 if (!pubdata
|| publen
> UINT16_MAX
|| ft_size
> UINT16_MAX
) {
660 m_entry
= kheap_alloc(KHEAP_DEFAULT
, m_entry_len
, Z_WAITOK
);
664 m_entry
->type
= type
;
665 m_entry
->timestamp
= stamp
;
666 m_entry
->size
= (uint16_t)ft_size
;
668 firehose_tracepoint_t ft
= m_entry
->metadata
;
669 ft
->ft_thread
= thread_tid(current_thread());
670 ft
->ft_id
.ftid_value
= ftid
.ftid_value
;
671 ft
->ft_length
= (uint16_t)publen
;
672 memcpy(ft
->ft_data
, pubdata
, publen
);
678 os_log_coprocessor_register(const char *uuid
, const char *file_path
, bool copy
)
681 size_t path_size
= strlen(file_path
) + 1;
682 firehose_tracepoint_id_u trace_id
;
683 size_t uuid_info_len
= sizeof(struct firehose_trace_uuid_info_s
) + path_size
;
685 struct firehose_trace_uuid_info_s uuid_info
;
686 char path
[PATH_MAX
+ sizeof(struct firehose_trace_uuid_info_s
)];
689 if (path_size
> PATH_MAX
) {
693 // write metadata to uuid_info
694 memcpy(buf
.uuid_info
.ftui_uuid
, uuid
, sizeof(uuid_t
));
695 buf
.uuid_info
.ftui_size
= 1;
696 buf
.uuid_info
.ftui_address
= 1;
698 stamp
= firehose_tracepoint_time(firehose_activity_flags_default
);
700 // create tracepoint id
701 trace_id
.ftid_value
= FIREHOSE_TRACE_ID_MAKE(firehose_tracepoint_namespace_metadata
, _firehose_tracepoint_type_metadata_coprocessor
,
702 (firehose_tracepoint_flags_t
)0, copy
? firehose_tracepoint_code_load_memory
: firehose_tracepoint_code_load_filesystem
);
704 // write path to buffer
705 memcpy(buf
.uuid_info
.ftui_path
, file_path
, path_size
);
707 // send metadata tracepoint to firehose for coprocessor registration in logd
708 firehose_trace_metadata(firehose_stream_metadata
, trace_id
, stamp
, (void *)&buf
, uuid_info_len
);
714 firehose_trace_metadata(firehose_stream_t stream
, firehose_tracepoint_id_u ftid
,
715 uint64_t stamp
, const void *pubdata
, size_t publen
)
717 oslog_stream_buf_entry_t m_entry
= NULL
;
719 // If streaming mode is not on, only log the metadata
720 // in the persistence buffer
723 if (!oslog_stream_open
) {
729 // Setup and write the stream metadata entry
730 m_entry
= oslog_stream_create_buf_entry(oslog_stream_link_type_metadata
, ftid
,
731 stamp
, pubdata
, publen
);
733 os_atomic_inc(&oslog_s_error_count
, relaxed
);
738 if (!oslog_stream_open
) {
740 kheap_free(KHEAP_DEFAULT
, m_entry
,
741 sizeof(struct oslog_stream_buf_entry_s
) +
742 sizeof(struct firehose_tracepoint_s
) + publen
);
745 oslog_s_metadata_msgcount
++;
746 oslog_streamwrite_metadata_locked(m_entry
);
750 _firehose_trace(stream
, ftid
, stamp
, pubdata
, publen
, true);
755 __firehose_buffer_push_to_logd(firehose_buffer_t fb __unused
, bool for_io __unused
)
762 __firehose_allocate(vm_offset_t
*addr
, vm_size_t size __unused
)
764 firehose_chunk_t kernel_buffer
= (firehose_chunk_t
)kernel_firehose_addr
;
766 if (kernel_firehose_addr
) {
767 *addr
= kernel_firehose_addr
;
772 // Now that we are done adding logs to this chunk, set the number of writers to 0
773 // Without this, logd won't flush when the page is full
774 firehose_boot_chunk
->fc_pos
.fcp_refcnt
= 0;
775 memcpy(&kernel_buffer
[FIREHOSE_BUFFER_KERNEL_CHUNK_COUNT
- 1], (const void *)firehose_boot_chunk
, FIREHOSE_CHUNK_SIZE
);
778 // There isnt a lock held in this case.
780 __firehose_critical_region_enter(void)
782 disable_preemption();
787 __firehose_critical_region_leave(void)
793 #ifdef CONFIG_XNUPOST
795 #include <tests/xnupost.h>
796 #define TESTOSLOGFMT(fn_name) "%u^%llu/%llu^kernel^0^test^" fn_name
797 #define TESTOSLOGPFX "TESTLOG:%u#"
798 #define TESTOSLOG(fn_name) TESTOSLOGPFX TESTOSLOGFMT(fn_name "#")
800 extern u_int32_t
RandomULong(void);
801 extern size_t find_pattern_in_buffer(const char *pattern
, size_t len
, size_t expected_count
);
802 void test_oslog_default_helper(uint32_t uniqid
, uint64_t count
);
803 void test_oslog_info_helper(uint32_t uniqid
, uint64_t count
);
804 void test_oslog_debug_helper(uint32_t uniqid
, uint64_t count
);
805 void test_oslog_error_helper(uint32_t uniqid
, uint64_t count
);
806 void test_oslog_fault_helper(uint32_t uniqid
, uint64_t count
);
807 void _test_log_loop(void * arg __unused
, wait_result_t wres __unused
);
808 void test_oslog_handleOSLogCtl(int32_t * in
, int32_t * out
, int32_t len
);
809 kern_return_t
test_stresslog_dropmsg(uint32_t uniqid
);
811 kern_return_t
test_os_log(void);
812 kern_return_t
test_os_log_parallel(void);
814 #define GENOSLOGHELPER(fname, ident, callout_f) \
815 void fname(uint32_t uniqid, uint64_t count) \
817 int32_t datalen = 0; \
818 uint32_t checksum = 0; \
819 char databuffer[256]; \
820 T_LOG("Doing os_log of %llu TESTLOG msgs for fn " ident, count); \
821 for (uint64_t i = 0; i < count; i++) \
823 datalen = scnprintf(databuffer, sizeof(databuffer), TESTOSLOGFMT(ident), uniqid, i + 1, count); \
824 checksum = crc32(0, databuffer, datalen); \
825 callout_f(OS_LOG_DEFAULT, TESTOSLOG(ident), checksum, uniqid, i + 1, count); \
826 /*T_LOG(TESTOSLOG(ident), checksum, uniqid, i + 1, count);*/ \
830 GENOSLOGHELPER(test_oslog_info_helper
, "oslog_info_helper", os_log_info
);
831 GENOSLOGHELPER(test_oslog_fault_helper
, "oslog_fault_helper", os_log_fault
);
832 GENOSLOGHELPER(test_oslog_debug_helper
, "oslog_debug_helper", os_log_debug
);
833 GENOSLOGHELPER(test_oslog_error_helper
, "oslog_error_helper", os_log_error
);
834 GENOSLOGHELPER(test_oslog_default_helper
, "oslog_default_helper", os_log
);
839 char databuffer
[256];
840 uint32_t uniqid
= RandomULong();
841 size_t match_count
= 0;
842 uint32_t checksum
= 0;
843 uint32_t total_msg
= 0;
844 uint32_t saved_msg
= 0;
845 uint32_t dropped_msg
= 0;
847 uint64_t a
= mach_absolute_time();
849 uint64_t total_seqno
= 2;
851 os_log_t log_handle
= os_log_create("com.apple.xnu.test.t1", "kpost");
853 T_ASSERT_EQ_PTR(&_os_log_default
, log_handle
, "os_log_create returns valid value.");
854 T_ASSERT_EQ_INT(TRUE
, os_log_info_enabled(log_handle
), "os_log_info is enabled");
855 T_ASSERT_EQ_INT(TRUE
, os_log_debug_enabled(log_handle
), "os_log_debug is enabled");
856 T_ASSERT_EQ_PTR(&_os_log_default
, OS_LOG_DEFAULT
, "ensure OS_LOG_DEFAULT is _os_log_default");
858 total_msg
= oslog_p_total_msgcount
;
859 saved_msg
= oslog_p_saved_msgcount
;
860 dropped_msg
= oslog_p_dropped_msgcount
;
861 T_LOG("oslog internal counters total %u , saved %u, dropped %u", total_msg
, saved_msg
, dropped_msg
);
863 T_LOG("Validating with uniqid %u u64 %llu", uniqid
, a
);
864 T_ASSERT_NE_UINT(0, uniqid
, "random number should not be zero");
865 T_ASSERT_NE_ULLONG(0, a
, "absolute time should not be zero");
867 datalen
= scnprintf(databuffer
, sizeof(databuffer
), TESTOSLOGFMT("printf_only"), uniqid
, seqno
, total_seqno
);
868 checksum
= crc32(0, databuffer
, datalen
);
869 printf(TESTOSLOG("printf_only") "mat%llu\n", checksum
, uniqid
, seqno
, total_seqno
, a
);
872 datalen
= scnprintf(databuffer
, sizeof(databuffer
), TESTOSLOGFMT("printf_only"), uniqid
, seqno
, total_seqno
);
873 checksum
= crc32(0, databuffer
, datalen
);
874 printf(TESTOSLOG("printf_only") "mat%llu\n", checksum
, uniqid
, seqno
, total_seqno
, a
);
876 datalen
= scnprintf(databuffer
, sizeof(databuffer
), "kernel^0^test^printf_only#mat%llu", a
);
877 match_count
= find_pattern_in_buffer(databuffer
, datalen
, total_seqno
);
878 T_EXPECT_EQ_ULONG(match_count
, total_seqno
, "verify printf_only goes to systemlog buffer");
880 uint32_t logging_config
= atm_get_diagnostic_config();
881 T_LOG("checking atm_diagnostic_config 0x%X", logging_config
);
883 if ((logging_config
& ATM_TRACE_OFF
) || (logging_config
& ATM_TRACE_DISABLE
)) {
884 T_LOG("ATM_TRACE_OFF / ATM_TRACE_DISABLE is set. Would not see oslog messages. skipping the rest of test.");
888 /* for enabled logging printfs should be saved in oslog as well */
889 T_EXPECT_GE_UINT((oslog_p_total_msgcount
- total_msg
), 2, "atleast 2 msgs should be seen by oslog system");
891 a
= mach_absolute_time();
894 total_msg
= oslog_p_total_msgcount
;
895 saved_msg
= oslog_p_saved_msgcount
;
896 dropped_msg
= oslog_p_dropped_msgcount
;
897 datalen
= scnprintf(databuffer
, sizeof(databuffer
), TESTOSLOGFMT("oslog_info"), uniqid
, seqno
, total_seqno
);
898 checksum
= crc32(0, databuffer
, datalen
);
899 os_log_info(log_handle
, TESTOSLOG("oslog_info") "mat%llu", checksum
, uniqid
, seqno
, total_seqno
, a
);
900 T_EXPECT_GE_UINT((oslog_p_total_msgcount
- total_msg
), 1, "total message count in buffer");
902 datalen
= scnprintf(databuffer
, sizeof(databuffer
), "kernel^0^test^oslog_info#mat%llu", a
);
903 match_count
= find_pattern_in_buffer(databuffer
, datalen
, total_seqno
);
904 T_EXPECT_EQ_ULONG(match_count
, total_seqno
, "verify oslog_info does not go to systemlog buffer");
906 total_msg
= oslog_p_total_msgcount
;
907 test_oslog_info_helper(uniqid
, 10);
908 T_EXPECT_GE_UINT(oslog_p_total_msgcount
- total_msg
, 10, "test_oslog_info_helper: Should have seen 10 msgs");
910 total_msg
= oslog_p_total_msgcount
;
911 test_oslog_debug_helper(uniqid
, 10);
912 T_EXPECT_GE_UINT(oslog_p_total_msgcount
- total_msg
, 10, "test_oslog_debug_helper:Should have seen 10 msgs");
914 total_msg
= oslog_p_total_msgcount
;
915 test_oslog_error_helper(uniqid
, 10);
916 T_EXPECT_GE_UINT(oslog_p_total_msgcount
- total_msg
, 10, "test_oslog_error_helper:Should have seen 10 msgs");
918 total_msg
= oslog_p_total_msgcount
;
919 test_oslog_default_helper(uniqid
, 10);
920 T_EXPECT_GE_UINT(oslog_p_total_msgcount
- total_msg
, 10, "test_oslog_default_helper:Should have seen 10 msgs");
922 total_msg
= oslog_p_total_msgcount
;
923 test_oslog_fault_helper(uniqid
, 10);
924 T_EXPECT_GE_UINT(oslog_p_total_msgcount
- total_msg
, 10, "test_oslog_fault_helper:Should have seen 10 msgs");
926 T_LOG("oslog internal counters total %u , saved %u, dropped %u", oslog_p_total_msgcount
, oslog_p_saved_msgcount
,
927 oslog_p_dropped_msgcount
);
932 static uint32_t _test_log_loop_count
= 0;
934 _test_log_loop(void * arg __unused
, wait_result_t wres __unused
)
936 uint32_t uniqid
= RandomULong();
937 test_oslog_debug_helper(uniqid
, 100);
938 os_atomic_add(&_test_log_loop_count
, 100, relaxed
);
942 test_os_log_parallel(void)
946 uint32_t uniqid
= RandomULong();
948 printf("oslog internal counters total %u , saved %u, dropped %u", oslog_p_total_msgcount
, oslog_p_saved_msgcount
,
949 oslog_p_dropped_msgcount
);
951 kr
= kernel_thread_start(_test_log_loop
, NULL
, &thread
[0]);
952 T_ASSERT_EQ_INT(kr
, KERN_SUCCESS
, "kernel_thread_start returned successfully");
954 kr
= kernel_thread_start(_test_log_loop
, NULL
, &thread
[1]);
955 T_ASSERT_EQ_INT(kr
, KERN_SUCCESS
, "kernel_thread_start returned successfully");
957 test_oslog_info_helper(uniqid
, 100);
959 /* wait until other thread has also finished */
960 while (_test_log_loop_count
< 200) {
964 thread_deallocate(thread
[0]);
965 thread_deallocate(thread
[1]);
967 T_LOG("oslog internal counters total %u , saved %u, dropped %u", oslog_p_total_msgcount
, oslog_p_saved_msgcount
,
968 oslog_p_dropped_msgcount
);
969 T_PASS("parallel_logging tests is now complete");
975 test_oslog_handleOSLogCtl(int32_t * in
, int32_t * out
, int32_t len
)
977 if (!in
|| !out
|| len
!= 4) {
983 /* send out counters */
984 out
[1] = oslog_p_total_msgcount
;
985 out
[2] = oslog_p_saved_msgcount
;
986 out
[3] = oslog_p_dropped_msgcount
;
987 out
[0] = KERN_SUCCESS
;
992 /* mini stress run */
993 out
[0] = test_os_log_parallel();
999 out
[1] = RandomULong();
1000 out
[0] = test_stresslog_dropmsg(out
[1]);
1005 /* invoke log helpers */
1006 uint32_t uniqid
= in
[3];
1007 int32_t msgcount
= in
[2];
1008 if (uniqid
== 0 || msgcount
== 0) {
1009 out
[0] = KERN_INVALID_VALUE
;
1014 case OS_LOG_TYPE_INFO
: test_oslog_info_helper(uniqid
, msgcount
); break;
1015 case OS_LOG_TYPE_DEBUG
: test_oslog_debug_helper(uniqid
, msgcount
); break;
1016 case OS_LOG_TYPE_ERROR
: test_oslog_error_helper(uniqid
, msgcount
); break;
1017 case OS_LOG_TYPE_FAULT
: test_oslog_fault_helper(uniqid
, msgcount
); break;
1018 case OS_LOG_TYPE_DEFAULT
:
1019 default: test_oslog_default_helper(uniqid
, msgcount
); break;
1021 out
[0] = KERN_SUCCESS
;
1027 out
[0] = KERN_INVALID_VALUE
;
1035 test_stresslog_dropmsg(uint32_t uniqid
)
1037 uint32_t total
, saved
, dropped
;
1038 total
= oslog_p_total_msgcount
;
1039 saved
= oslog_p_saved_msgcount
;
1040 dropped
= oslog_p_dropped_msgcount
;
1041 uniqid
= RandomULong();
1042 test_oslog_debug_helper(uniqid
, 100);
1043 while ((oslog_p_dropped_msgcount
- dropped
) == 0) {
1044 test_oslog_debug_helper(uniqid
, 100);
1046 printf("test_stresslog_dropmsg: logged %u msgs, saved %u and caused a drop of %u msgs. \n", oslog_p_total_msgcount
- total
,
1047 oslog_p_saved_msgcount
- saved
, oslog_p_dropped_msgcount
- dropped
);
1048 return KERN_SUCCESS
;