4 #include <kern/cpu_data.h>
11 #include <vm/vm_kern.h>
12 #include <mach/vm_statistics.h>
13 #include <kern/debug.h>
14 #include <libkern/libkern.h>
15 #include <libkern/kernel_mach_header.h>
16 #include <pexpert/pexpert.h>
17 #include <uuid/uuid.h>
18 #include <sys/msgbuf.h>
20 #include <mach/mach_time.h>
21 #include <kern/thread.h>
22 #include <kern/simple_lock.h>
23 #include <kern/kalloc.h>
24 #include <kern/clock.h>
25 #include <kern/assert.h>
27 #include <firehose/tracepoint_private.h>
28 #include <firehose/chunk_private.h>
29 #include <os/firehose_buffer_private.h>
30 #include <os/firehose.h>
32 #include <os/log_private.h>
33 #include "trace_internal.h"
35 #include "log_encode.h"
41 struct os_log_s _os_log_default
;
42 struct os_log_s _os_log_replay
;
43 extern vm_offset_t kernel_firehose_addr
;
44 extern firehose_chunk_t firehose_boot_chunk
;
46 extern void bsd_log_lock(void);
47 extern void bsd_log_unlock(void);
48 extern void logwakeup(struct msgbuf
*);
50 decl_lck_spin_data(extern, oslog_stream_lock
)
51 extern void oslog_streamwakeup(void);
52 void oslog_streamwrite_locked(firehose_tracepoint_id_u ftid
,
53 uint64_t stamp
, const void *pubdata
, size_t publen
);
54 extern void oslog_streamwrite_metadata_locked(oslog_stream_buf_entry_t m_entry
);
56 extern int oslog_stream_open
;
58 extern void *OSKextKextForAddress(const void *);
60 /* Counters for persistence mode */
61 uint32_t oslog_p_total_msgcount
= 0;
62 uint32_t oslog_p_metadata_saved_msgcount
= 0;
63 uint32_t oslog_p_metadata_dropped_msgcount
= 0;
64 uint32_t oslog_p_error_count
= 0;
65 uint32_t oslog_p_saved_msgcount
= 0;
66 uint32_t oslog_p_dropped_msgcount
= 0;
67 uint32_t oslog_p_boot_dropped_msgcount
= 0;
69 /* Counters for streaming mode */
70 uint32_t oslog_s_total_msgcount
= 0;
71 uint32_t oslog_s_error_count
= 0;
72 uint32_t oslog_s_metadata_msgcount
= 0;
74 static bool oslog_boot_done
= false;
75 extern boolean_t early_boot_complete
;
77 #ifdef XNU_KERNEL_PRIVATE
78 bool startup_serial_logging_active
= true;
79 uint64_t startup_serial_num_procs
= 300;
80 #endif /* XNU_KERNEL_PRIVATE */
83 firehose_tracepoint_id_t
84 firehose_debug_trace(firehose_stream_t stream
, firehose_tracepoint_id_t trace_id
,
85 uint64_t timestamp
, const char *format
, const void *pubdata
, size_t publen
);
87 static inline firehose_tracepoint_id_t
88 _firehose_trace(firehose_stream_t stream
, firehose_tracepoint_id_u ftid
,
89 uint64_t stamp
, const void *pubdata
, size_t publen
);
91 static oslog_stream_buf_entry_t
92 oslog_stream_create_buf_entry(oslog_stream_link_type_t type
, firehose_tracepoint_id_u ftid
,
93 uint64_t stamp
, const void* pubdata
, size_t publen
);
96 _os_log_with_args_internal(os_log_t oslog __unused
, os_log_type_t type __unused
,
97 const char *format
, va_list args
, void *addr
, void *dso
);
100 _os_log_to_msgbuf_internal(const char *format
, va_list args
, bool safe
, bool logging
);
103 _os_log_to_log_internal(os_log_t oslog
, os_log_type_t type
,
104 const char *format
, va_list args
, void *addr
, void *dso
);
108 _os_log_actual(os_log_t oslog
, os_log_type_t type
, const char *format
, void
109 *dso
, void *addr
, os_log_buffer_context_t context
);
112 os_log_info_enabled(os_log_t log __unused
)
118 os_log_debug_enabled(os_log_t log __unused
)
124 os_log_create(const char *subsystem __unused
, const char *category __unused
)
126 return &_os_log_default
;
130 _os_log_string_is_public(const char *str __unused
)
135 __attribute__((noinline
,not_tail_called
)) void
136 _os_log_internal(void *dso
, os_log_t log
, uint8_t type
, const char *message
, ...)
139 void *addr
= __builtin_return_address(0);
141 va_start(args
, message
);
143 _os_log_with_args_internal(log
, type
, message
, args
, addr
, dso
);
150 #pragma mark - shim functions
152 __attribute__((noinline
,not_tail_called
)) void
153 os_log_with_args(os_log_t oslog
, os_log_type_t type
, const char *format
, va_list args
, void *addr
)
155 // if no address passed, look it up
157 addr
= __builtin_return_address(0);
160 _os_log_with_args_internal(oslog
, type
, format
, args
, addr
, NULL
);
164 _os_log_with_args_internal(os_log_t oslog
, os_log_type_t type
,
165 const char *format
, va_list args
, void *addr
, void *dso
)
167 uint32_t logging_config
= atm_get_diagnostic_config();
171 if (format
[0] == '\0') {
175 /* early boot can log to dmesg for later replay (27307943) */
176 safe
= (!early_boot_complete
|| oslog_is_safe());
178 if (logging_config
& ATM_TRACE_DISABLE
|| logging_config
& ATM_TRACE_OFF
) {
184 if (oslog
!= &_os_log_replay
) {
185 _os_log_to_msgbuf_internal(format
, args
, safe
, logging
);
188 if (safe
&& logging
) {
189 _os_log_to_log_internal(oslog
, type
, format
, args
, addr
, dso
);
194 _os_log_to_msgbuf_internal(const char *format
, va_list args
, bool safe
, bool logging
)
196 static int msgbufreplay
= -1;
199 #if DEVELOPMENT || DEBUG
208 if (-1 == msgbufreplay
) msgbufreplay
= msgbufp
->msg_bufx
;
209 } else if (logging
&& (-1 != msgbufreplay
)) {
211 uint32_t localbuff_size
;
213 char *localbuff
, *p
, *s
, *next
, ch
;
215 position
= msgbufreplay
;
217 localbuff_size
= (msgbufp
->msg_size
+ 2); /* + '\n' + '\0' */
218 /* Size for non-blocking */
219 if (localbuff_size
> 4096) localbuff_size
= 4096;
221 /* Allocate a temporary non-circular buffer */
222 if ((localbuff
= (char *)kalloc_noblock(localbuff_size
))) {
223 /* in between here, the log could become bigger, but that's fine */
226 * The message buffer is circular; start at the replay pointer, and
227 * make one loop up to write pointer - 1.
229 p
= msgbufp
->msg_bufc
+ position
;
230 for (i
= newl
= 0; p
!= msgbufp
->msg_bufc
+ msgbufp
->msg_bufx
- 1; ++p
) {
231 if (p
>= msgbufp
->msg_bufc
+ msgbufp
->msg_size
)
232 p
= msgbufp
->msg_bufc
;
234 if (ch
== '\0') continue;
237 if (i
>= (localbuff_size
- 2)) break;
241 if (!newl
) localbuff
[i
++] = '\n';
245 while ((next
= strchr(s
, '\n'))) {
249 os_log(&_os_log_replay
, "%s", s
);
253 kfree(localbuff
, localbuff_size
);
258 va_copy(args_copy
, args
);
259 vprintf_log_locked(format
, args_copy
);
262 #if DEVELOPMENT || DEBUG
269 if (safe
) logwakeup(msgbufp
);
274 _os_log_to_log_internal(os_log_t oslog
, os_log_type_t type
,
275 const char *format
, va_list args
, void *addr
, void *dso
)
277 struct os_log_buffer_context_s context
;
278 unsigned char buffer_data
[OS_LOG_BUFFER_MAX_SIZE
] __attribute__((aligned(8)));
279 os_log_buffer_t buffer
= (os_log_buffer_t
)buffer_data
;
280 uint8_t pubdata
[OS_LOG_BUFFER_MAX_SIZE
];
284 dso
= (void *) OSKextKextForAddress(format
);
290 if (!_os_trace_addr_in_text_segment(dso
, format
)) {
298 void *dso_addr
= (void *) OSKextKextForAddress(addr
);
299 if (dso
!= dso_addr
) {
303 memset(&context
, 0, sizeof(context
));
304 memset(buffer
, 0, OS_LOG_BUFFER_MAX_SIZE
);
306 context
.shimmed
= true;
307 context
.buffer
= buffer
;
308 context
.content_sz
= OS_LOG_BUFFER_MAX_SIZE
- sizeof(*buffer
);
309 context
.pubdata
= pubdata
;
310 context
.pubdata_sz
= sizeof(pubdata
);
312 va_copy(args_copy
, args
);
314 (void)hw_atomic_add(&oslog_p_total_msgcount
, 1);
315 if (_os_log_encode(format
, args_copy
, 0, &context
)) {
316 _os_log_actual(oslog
, type
, format
, dso
, addr
, &context
);
319 (void)hw_atomic_add(&oslog_p_error_count
, 1);
326 _os_trace_write_location_for_address(uint8_t buf
[static sizeof(uint64_t)],
327 void *dso
, const void *address
, firehose_tracepoint_flags_t
*flags
)
329 kernel_mach_header_t
*mh
= dso
;
331 if (mh
->filetype
== MH_EXECUTE
) {
332 *flags
= _firehose_tracepoint_flags_pc_style_main_exe
;
333 memcpy(buf
, (uint32_t[]){ (uintptr_t)address
- (uintptr_t)dso
},
335 return sizeof(uint32_t);
337 *flags
= _firehose_tracepoint_flags_pc_style_absolute
;
338 memcpy(buf
, (uintptr_t[]){ VM_KERNEL_UNSLIDE(address
) }, sizeof(uintptr_t));
340 return 6; // 48 bits are enough
342 return sizeof(uintptr_t);
350 _os_log_buffer_pack(uint8_t *buffdata
, size_t buffdata_sz
,
351 os_log_buffer_context_t ctx
)
353 os_log_buffer_t buffer
= ctx
->buffer
;
354 size_t buffer_sz
= sizeof(*ctx
->buffer
) + ctx
->content_sz
;
355 size_t total_sz
= buffer_sz
+ ctx
->pubdata_sz
;
357 if (total_sz
> buffdata_sz
) {
361 memcpy(buffdata
, buffer
, buffer_sz
);
362 memcpy(&buffdata
[buffer_sz
], ctx
->pubdata
, ctx
->pubdata_sz
);
367 _os_log_actual(os_log_t oslog __unused
, os_log_type_t type
, const char *format
,
368 void *dso
, void *addr
, os_log_buffer_context_t context
)
370 firehose_stream_t stream
;
371 firehose_tracepoint_flags_t flags
= 0;
372 firehose_tracepoint_id_u trace_id
;
373 uint8_t buffdata
[OS_LOG_BUFFER_MAX_SIZE
];
374 size_t addr_len
= 0, buffdata_sz
;
378 // dso == the start of the binary that was loaded
379 addr_len
= _os_trace_write_location_for_address(buffdata
, dso
, addr
, &flags
);
380 buffdata_sz
= _os_log_buffer_pack(buffdata
+ addr_len
,
381 sizeof(buffdata
) - addr_len
, context
);
382 if (buffdata_sz
== 0) {
385 buffdata_sz
+= addr_len
;
387 timestamp
= firehose_tracepoint_time(firehose_activity_flags_default
);
388 thread_id
= thread_tid(current_thread());
390 // create trace_id after we've set additional flags
391 trace_id
.ftid_value
= FIREHOSE_TRACE_ID_MAKE(firehose_tracepoint_namespace_log
,
392 type
, flags
, _os_trace_offset(dso
, format
, flags
));
395 firehose_debug_trace(stream
, trace_id
.ftid_value
, timestamp
,
396 format
, buffdata
, buffdata_sz
);
398 if (type
== OS_LOG_TYPE_INFO
|| type
== OS_LOG_TYPE_DEBUG
) {
399 stream
= firehose_stream_memory
;
402 stream
= firehose_stream_persist
;
404 _firehose_trace(stream
, trace_id
, timestamp
, buffdata
, buffdata_sz
);
407 static inline firehose_tracepoint_id_t
408 _firehose_trace(firehose_stream_t stream
, firehose_tracepoint_id_u ftid
,
409 uint64_t stamp
, const void *pubdata
, size_t publen
)
411 const uint16_t ft_size
= offsetof(struct firehose_tracepoint_s
, ft_data
);
412 const size_t _firehose_chunk_payload_size
=
413 sizeof(((struct firehose_chunk_s
*)0)->fc_data
);
415 firehose_tracepoint_t ft
;
417 if (slowpath(ft_size
+ publen
> _firehose_chunk_payload_size
)) {
418 // We'll need to have some handling here. For now - return 0
419 (void)hw_atomic_add(&oslog_p_error_count
, 1);
423 if (oslog_stream_open
&& (stream
!= firehose_stream_metadata
)) {
425 lck_spin_lock(&oslog_stream_lock
);
426 if (!oslog_stream_open
) {
427 lck_spin_unlock(&oslog_stream_lock
);
431 oslog_s_total_msgcount
++;
432 oslog_streamwrite_locked(ftid
, stamp
, pubdata
, publen
);
433 lck_spin_unlock(&oslog_stream_lock
);
434 oslog_streamwakeup();
438 ft
= __firehose_buffer_tracepoint_reserve(stamp
, stream
, (uint16_t)publen
, 0, NULL
);
440 if (oslog_boot_done
) {
441 if (stream
== firehose_stream_metadata
) {
442 (void)hw_atomic_add(&oslog_p_metadata_dropped_msgcount
, 1);
445 // If we run out of space in the persistence buffer we're
446 // dropping the message.
447 (void)hw_atomic_add(&oslog_p_dropped_msgcount
, 1);
451 firehose_chunk_t fbc
= firehose_boot_chunk
;
454 //only stream available during boot is persist
455 offset
= firehose_chunk_tracepoint_try_reserve(fbc
, stamp
,
456 firehose_stream_persist
, 0, publen
, 0, NULL
);
458 (void)hw_atomic_add(&oslog_p_boot_dropped_msgcount
, 1);
462 ft
= firehose_chunk_tracepoint_begin(fbc
, stamp
, publen
,
463 thread_tid(current_thread()), offset
);
464 memcpy(ft
->ft_data
, pubdata
, publen
);
465 firehose_chunk_tracepoint_end(fbc
, ft
, ftid
);
466 (void)hw_atomic_add(&oslog_p_saved_msgcount
, 1);
467 return ftid
.ftid_value
;
469 if (!oslog_boot_done
) {
470 oslog_boot_done
= true;
472 memcpy(ft
->ft_data
, pubdata
, publen
);
474 __firehose_buffer_tracepoint_flush(ft
, ftid
);
475 if (stream
== firehose_stream_metadata
) {
476 (void)hw_atomic_add(&oslog_p_metadata_saved_msgcount
, 1);
479 (void)hw_atomic_add(&oslog_p_saved_msgcount
, 1);
481 return ftid
.ftid_value
;
484 static oslog_stream_buf_entry_t
485 oslog_stream_create_buf_entry(oslog_stream_link_type_t type
, firehose_tracepoint_id_u ftid
,
486 uint64_t stamp
, const void* pubdata
, size_t publen
)
488 oslog_stream_buf_entry_t m_entry
= NULL
;
489 firehose_tracepoint_t ft
= NULL
;
490 size_t m_entry_len
= 0;
496 m_entry_len
= sizeof(struct oslog_stream_buf_entry_s
) +
497 sizeof(struct firehose_tracepoint_s
) + publen
;
498 m_entry
= (oslog_stream_buf_entry_t
) kalloc(m_entry_len
);
503 m_entry
->type
= type
;
504 m_entry
->timestamp
= stamp
;
505 m_entry
->size
= sizeof(struct firehose_tracepoint_s
) + publen
;
507 ft
= m_entry
->metadata
;
508 ft
->ft_thread
= thread_tid(current_thread());
509 ft
->ft_id
.ftid_value
= ftid
.ftid_value
;
510 ft
->ft_length
= publen
;
511 memcpy(ft
->ft_data
, pubdata
, publen
);
518 firehose_trace_metadata(firehose_stream_t stream
, firehose_tracepoint_id_u ftid
,
519 uint64_t stamp
, const void *pubdata
, size_t publen
)
521 oslog_stream_buf_entry_t m_entry
= NULL
;
523 // If streaming mode is not on, only log the metadata
524 // in the persistence buffer
526 lck_spin_lock(&oslog_stream_lock
);
527 if (!oslog_stream_open
) {
528 lck_spin_unlock(&oslog_stream_lock
);
531 lck_spin_unlock(&oslog_stream_lock
);
533 // Setup and write the stream metadata entry
534 m_entry
= oslog_stream_create_buf_entry(oslog_stream_link_type_metadata
, ftid
,
535 stamp
, pubdata
, publen
);
537 (void)hw_atomic_add(&oslog_s_error_count
, 1);
541 lck_spin_lock(&oslog_stream_lock
);
542 if (!oslog_stream_open
) {
543 lck_spin_unlock(&oslog_stream_lock
);
544 kfree(m_entry
, sizeof(struct oslog_stream_buf_entry_s
) +
545 sizeof(struct firehose_tracepoint_s
) + publen
);
548 oslog_s_metadata_msgcount
++;
549 oslog_streamwrite_metadata_locked(m_entry
);
550 lck_spin_unlock(&oslog_stream_lock
);
553 _firehose_trace(stream
, ftid
, stamp
, pubdata
, publen
);
557 firehose_tracepoint_id_t
558 firehose_debug_trace(firehose_stream_t stream
, firehose_tracepoint_id_t trace_id
,
559 uint64_t timestamp
, const char *format
, const void *pubdata
, size_t publen
)
561 kprintf("[os_log stream 0x%x trace_id 0x%llx timestamp %llu format '%s' data %p len %lu]\n",
562 (unsigned int)stream
, (unsigned long long)trace_id
, timestamp
,
563 format
, pubdata
, publen
);
565 const unsigned char *cdata
= (const unsigned char *)pubdata
;
566 for (i
=0; i
< publen
; i
+= 8) {
567 kprintf(">oslog 0x%08x: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
569 (i
+0) < publen
? cdata
[i
+0] : 0,
570 (i
+1) < publen
? cdata
[i
+1] : 0,
571 (i
+2) < publen
? cdata
[i
+2] : 0,
572 (i
+3) < publen
? cdata
[i
+3] : 0,
573 (i
+4) < publen
? cdata
[i
+4] : 0,
574 (i
+5) < publen
? cdata
[i
+5] : 0,
575 (i
+6) < publen
? cdata
[i
+6] : 0,
576 (i
+7) < publen
? cdata
[i
+7] : 0
583 __firehose_buffer_push_to_logd(firehose_buffer_t fb __unused
, bool for_io __unused
) {
589 __firehose_allocate(vm_offset_t
*addr
, vm_size_t size __unused
)
591 firehose_chunk_t kernel_buffer
= (firehose_chunk_t
)kernel_firehose_addr
;
593 if (kernel_firehose_addr
) {
594 *addr
= kernel_firehose_addr
;
600 // Now that we are done adding logs to this chunk, set the number of writers to 0
601 // Without this, logd won't flush when the page is full
602 firehose_boot_chunk
->fc_pos
.fcp_refcnt
= 0;
603 memcpy(&kernel_buffer
[FIREHOSE_BUFFER_KERNEL_CHUNK_COUNT
- 1], (const void *)firehose_boot_chunk
, FIREHOSE_CHUNK_SIZE
);
606 // There isnt a lock held in this case.
608 __firehose_critical_region_enter(void) {
609 disable_preemption();
614 __firehose_critical_region_leave(void) {