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(void);
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;
202 if (-1 == msgbufreplay
) msgbufreplay
= msgbufp
->msg_bufx
;
203 } else if (logging
&& (-1 != msgbufreplay
)) {
205 uint32_t localbuff_size
;
207 char *localbuff
, *p
, *s
, *next
, ch
;
209 position
= msgbufreplay
;
211 localbuff_size
= (msgbufp
->msg_size
+ 2); /* + '\n' + '\0' */
212 /* Size for non-blocking */
213 if (localbuff_size
> 4096) localbuff_size
= 4096;
215 /* Allocate a temporary non-circular buffer */
216 if ((localbuff
= (char *)kalloc_noblock(localbuff_size
))) {
217 /* in between here, the log could become bigger, but that's fine */
220 * The message buffer is circular; start at the replay pointer, and
221 * make one loop up to write pointer - 1.
223 p
= msgbufp
->msg_bufc
+ position
;
224 for (i
= newl
= 0; p
!= msgbufp
->msg_bufc
+ msgbufp
->msg_bufx
- 1; ++p
) {
225 if (p
>= msgbufp
->msg_bufc
+ msgbufp
->msg_size
)
226 p
= msgbufp
->msg_bufc
;
228 if (ch
== '\0') continue;
231 if (i
>= (localbuff_size
- 2)) break;
235 if (!newl
) localbuff
[i
++] = '\n';
239 while ((next
= strchr(s
, '\n'))) {
243 os_log(&_os_log_replay
, "%s", s
);
247 kfree(localbuff
, localbuff_size
);
252 va_copy(args_copy
, args
);
253 vprintf_log_locked(format
, args_copy
);
258 if (safe
) logwakeup();
262 _os_log_to_log_internal(os_log_t oslog
, os_log_type_t type
,
263 const char *format
, va_list args
, void *addr
, void *dso
)
265 struct os_log_buffer_context_s context
;
266 unsigned char buffer_data
[OS_LOG_BUFFER_MAX_SIZE
] __attribute__((aligned(8)));
267 os_log_buffer_t buffer
= (os_log_buffer_t
)buffer_data
;
268 uint8_t pubdata
[OS_LOG_BUFFER_MAX_SIZE
];
272 dso
= (void *) OSKextKextForAddress(format
);
278 if (!_os_trace_addr_in_text_segment(dso
, format
)) {
286 void *dso_addr
= (void *) OSKextKextForAddress(addr
);
287 if (dso
!= dso_addr
) {
291 memset(&context
, 0, sizeof(context
));
292 memset(buffer
, 0, OS_LOG_BUFFER_MAX_SIZE
);
294 context
.shimmed
= true;
295 context
.buffer
= buffer
;
296 context
.content_sz
= OS_LOG_BUFFER_MAX_SIZE
- sizeof(*buffer
);
297 context
.pubdata
= pubdata
;
298 context
.pubdata_sz
= sizeof(pubdata
);
300 va_copy(args_copy
, args
);
302 (void)hw_atomic_add(&oslog_p_total_msgcount
, 1);
303 if (_os_log_encode(format
, args_copy
, 0, &context
)) {
304 _os_log_actual(oslog
, type
, format
, dso
, addr
, &context
);
307 (void)hw_atomic_add(&oslog_p_error_count
, 1);
314 _os_trace_write_location_for_address(uint8_t buf
[static sizeof(uint64_t)],
315 void *dso
, const void *address
, firehose_tracepoint_flags_t
*flags
)
317 kernel_mach_header_t
*mh
= dso
;
319 if (mh
->filetype
== MH_EXECUTE
) {
320 *flags
= _firehose_tracepoint_flags_pc_style_main_exe
;
321 memcpy(buf
, (uint32_t[]){ (uintptr_t)address
- (uintptr_t)dso
},
323 return sizeof(uint32_t);
325 *flags
= _firehose_tracepoint_flags_pc_style_absolute
;
326 memcpy(buf
, (uintptr_t[]){ VM_KERNEL_UNSLIDE(address
) }, sizeof(uintptr_t));
328 return 6; // 48 bits are enough
330 return sizeof(uintptr_t);
338 _os_log_buffer_pack(uint8_t *buffdata
, size_t buffdata_sz
,
339 os_log_buffer_context_t ctx
)
341 os_log_buffer_t buffer
= ctx
->buffer
;
342 size_t buffer_sz
= sizeof(*ctx
->buffer
) + ctx
->content_sz
;
343 size_t total_sz
= buffer_sz
+ ctx
->pubdata_sz
;
345 if (total_sz
> buffdata_sz
) {
349 memcpy(buffdata
, buffer
, buffer_sz
);
350 memcpy(&buffdata
[buffer_sz
], ctx
->pubdata
, ctx
->pubdata_sz
);
355 _os_log_actual(os_log_t oslog __unused
, os_log_type_t type
, const char *format
,
356 void *dso
, void *addr
, os_log_buffer_context_t context
)
358 firehose_stream_t stream
;
359 firehose_tracepoint_flags_t flags
= 0;
360 firehose_tracepoint_id_u trace_id
;
361 uint8_t buffdata
[OS_LOG_BUFFER_MAX_SIZE
];
362 size_t addr_len
= 0, buffdata_sz
;
366 // dso == the start of the binary that was loaded
367 addr_len
= _os_trace_write_location_for_address(buffdata
, dso
, addr
, &flags
);
368 buffdata_sz
= _os_log_buffer_pack(buffdata
+ addr_len
,
369 sizeof(buffdata
) - addr_len
, context
);
370 if (buffdata_sz
== 0) {
373 buffdata_sz
+= addr_len
;
375 timestamp
= firehose_tracepoint_time(firehose_activity_flags_default
);
376 thread_id
= thread_tid(current_thread());
378 // create trace_id after we've set additional flags
379 trace_id
.ftid_value
= FIREHOSE_TRACE_ID_MAKE(firehose_tracepoint_namespace_log
,
380 type
, flags
, _os_trace_offset(dso
, format
, flags
));
383 firehose_debug_trace(stream
, trace_id
.ftid_value
, timestamp
,
384 format
, buffdata
, buffdata_sz
);
386 if (type
== OS_LOG_TYPE_INFO
|| type
== OS_LOG_TYPE_DEBUG
) {
387 stream
= firehose_stream_memory
;
390 stream
= firehose_stream_persist
;
392 _firehose_trace(stream
, trace_id
, timestamp
, buffdata
, buffdata_sz
);
395 static inline firehose_tracepoint_id_t
396 _firehose_trace(firehose_stream_t stream
, firehose_tracepoint_id_u ftid
,
397 uint64_t stamp
, const void *pubdata
, size_t publen
)
399 const uint16_t ft_size
= offsetof(struct firehose_tracepoint_s
, ft_data
);
400 const size_t _firehose_chunk_payload_size
=
401 sizeof(((struct firehose_chunk_s
*)0)->fc_data
);
403 firehose_tracepoint_t ft
;
405 if (slowpath(ft_size
+ publen
> _firehose_chunk_payload_size
)) {
406 // We'll need to have some handling here. For now - return 0
407 (void)hw_atomic_add(&oslog_p_error_count
, 1);
411 if (oslog_stream_open
&& (stream
!= firehose_stream_metadata
)) {
413 lck_spin_lock(&oslog_stream_lock
);
414 if (!oslog_stream_open
) {
415 lck_spin_unlock(&oslog_stream_lock
);
419 oslog_s_total_msgcount
++;
420 oslog_streamwrite_locked(ftid
, stamp
, pubdata
, publen
);
421 lck_spin_unlock(&oslog_stream_lock
);
422 oslog_streamwakeup();
426 ft
= __firehose_buffer_tracepoint_reserve(stamp
, stream
, (uint16_t)publen
, 0, NULL
);
428 if (oslog_boot_done
) {
429 if (stream
== firehose_stream_metadata
) {
430 (void)hw_atomic_add(&oslog_p_metadata_dropped_msgcount
, 1);
433 // If we run out of space in the persistence buffer we're
434 // dropping the message.
435 (void)hw_atomic_add(&oslog_p_dropped_msgcount
, 1);
439 firehose_chunk_t fbc
= firehose_boot_chunk
;
442 //only stream available during boot is persist
443 offset
= firehose_chunk_tracepoint_try_reserve(fbc
, stamp
,
444 firehose_stream_persist
, 0, publen
, 0, NULL
);
446 (void)hw_atomic_add(&oslog_p_boot_dropped_msgcount
, 1);
450 ft
= firehose_chunk_tracepoint_begin(fbc
, stamp
, publen
,
451 thread_tid(current_thread()), offset
);
452 memcpy(ft
->ft_data
, pubdata
, publen
);
453 firehose_chunk_tracepoint_end(fbc
, ft
, ftid
);
454 (void)hw_atomic_add(&oslog_p_saved_msgcount
, 1);
455 return ftid
.ftid_value
;
457 if (!oslog_boot_done
) {
458 oslog_boot_done
= true;
460 memcpy(ft
->ft_data
, pubdata
, publen
);
462 __firehose_buffer_tracepoint_flush(ft
, ftid
);
463 if (stream
== firehose_stream_metadata
) {
464 (void)hw_atomic_add(&oslog_p_metadata_saved_msgcount
, 1);
467 (void)hw_atomic_add(&oslog_p_saved_msgcount
, 1);
469 return ftid
.ftid_value
;
472 static oslog_stream_buf_entry_t
473 oslog_stream_create_buf_entry(oslog_stream_link_type_t type
, firehose_tracepoint_id_u ftid
,
474 uint64_t stamp
, const void* pubdata
, size_t publen
)
476 oslog_stream_buf_entry_t m_entry
= NULL
;
477 firehose_tracepoint_t ft
= NULL
;
478 size_t m_entry_len
= 0;
484 m_entry_len
= sizeof(struct oslog_stream_buf_entry_s
) +
485 sizeof(struct firehose_tracepoint_s
) + publen
;
486 m_entry
= (oslog_stream_buf_entry_t
) kalloc(m_entry_len
);
491 m_entry
->type
= type
;
492 m_entry
->timestamp
= stamp
;
493 m_entry
->size
= sizeof(struct firehose_tracepoint_s
) + publen
;
495 ft
= m_entry
->metadata
;
496 ft
->ft_thread
= thread_tid(current_thread());
497 ft
->ft_id
.ftid_value
= ftid
.ftid_value
;
498 ft
->ft_length
= publen
;
499 memcpy(ft
->ft_data
, pubdata
, publen
);
506 firehose_trace_metadata(firehose_stream_t stream
, firehose_tracepoint_id_u ftid
,
507 uint64_t stamp
, const void *pubdata
, size_t publen
)
509 oslog_stream_buf_entry_t m_entry
= NULL
;
511 // If streaming mode is not on, only log the metadata
512 // in the persistence buffer
514 lck_spin_lock(&oslog_stream_lock
);
515 if (!oslog_stream_open
) {
516 lck_spin_unlock(&oslog_stream_lock
);
519 lck_spin_unlock(&oslog_stream_lock
);
521 // Setup and write the stream metadata entry
522 m_entry
= oslog_stream_create_buf_entry(oslog_stream_link_type_metadata
, ftid
,
523 stamp
, pubdata
, publen
);
525 (void)hw_atomic_add(&oslog_s_error_count
, 1);
529 lck_spin_lock(&oslog_stream_lock
);
530 if (!oslog_stream_open
) {
531 lck_spin_unlock(&oslog_stream_lock
);
532 kfree(m_entry
, sizeof(struct oslog_stream_buf_entry_s
) +
533 sizeof(struct firehose_tracepoint_s
) + publen
);
536 oslog_s_metadata_msgcount
++;
537 oslog_streamwrite_metadata_locked(m_entry
);
538 lck_spin_unlock(&oslog_stream_lock
);
541 _firehose_trace(stream
, ftid
, stamp
, pubdata
, publen
);
545 firehose_tracepoint_id_t
546 firehose_debug_trace(firehose_stream_t stream
, firehose_tracepoint_id_t trace_id
,
547 uint64_t timestamp
, const char *format
, const void *pubdata
, size_t publen
)
549 kprintf("[os_log stream 0x%x trace_id 0x%llx timestamp %llu format '%s' data %p len %lu]\n",
550 (unsigned int)stream
, (unsigned long long)trace_id
, timestamp
,
551 format
, pubdata
, publen
);
553 const unsigned char *cdata
= (const unsigned char *)pubdata
;
554 for (i
=0; i
< publen
; i
+= 8) {
555 kprintf(">oslog 0x%08x: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
557 (i
+0) < publen
? cdata
[i
+0] : 0,
558 (i
+1) < publen
? cdata
[i
+1] : 0,
559 (i
+2) < publen
? cdata
[i
+2] : 0,
560 (i
+3) < publen
? cdata
[i
+3] : 0,
561 (i
+4) < publen
? cdata
[i
+4] : 0,
562 (i
+5) < publen
? cdata
[i
+5] : 0,
563 (i
+6) < publen
? cdata
[i
+6] : 0,
564 (i
+7) < publen
? cdata
[i
+7] : 0
571 __firehose_buffer_push_to_logd(firehose_buffer_t fb __unused
, bool for_io __unused
) {
577 __firehose_allocate(vm_offset_t
*addr
, vm_size_t size __unused
)
579 firehose_chunk_t kernel_buffer
= (firehose_chunk_t
)kernel_firehose_addr
;
581 if (kernel_firehose_addr
) {
582 *addr
= kernel_firehose_addr
;
588 // Now that we are done adding logs to this chunk, set the number of writers to 0
589 // Without this, logd won't flush when the page is full
590 firehose_boot_chunk
->fc_pos
.fcp_refcnt
= 0;
591 memcpy(&kernel_buffer
[FIREHOSE_BUFFER_KERNEL_CHUNK_COUNT
- 1], (const void *)firehose_boot_chunk
, FIREHOSE_CHUNK_SIZE
);
594 // There isnt a lock held in this case.
596 __firehose_critical_region_enter(void) {
597 disable_preemption();
602 __firehose_critical_region_leave(void) {