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 oslog_early_boot_complete
;
78 firehose_tracepoint_id_t
79 firehose_debug_trace(firehose_stream_t stream
, firehose_tracepoint_id_t trace_id
,
80 uint64_t timestamp
, const char *format
, const void *pubdata
, size_t publen
);
82 static inline firehose_tracepoint_id_t
83 _firehose_trace(firehose_stream_t stream
, firehose_tracepoint_id_u ftid
,
84 uint64_t stamp
, const void *pubdata
, size_t publen
);
86 static oslog_stream_buf_entry_t
87 oslog_stream_create_buf_entry(oslog_stream_link_type_t type
, firehose_tracepoint_id_u ftid
,
88 uint64_t stamp
, const void* pubdata
, size_t publen
);
91 _os_log_with_args_internal(os_log_t oslog __unused
, os_log_type_t type __unused
,
92 const char *format
, va_list args
, void *addr
, void *dso
);
95 _os_log_to_msgbuf_internal(const char *format
, va_list args
, bool safe
, bool logging
);
98 _os_log_to_log_internal(os_log_t oslog
, os_log_type_t type
,
99 const char *format
, va_list args
, void *addr
, void *dso
);
103 _os_log_actual(os_log_t oslog
, os_log_type_t type
, const char *format
, void
104 *dso
, void *addr
, os_log_buffer_context_t context
);
107 os_log_info_enabled(os_log_t log __unused
)
113 os_log_debug_enabled(os_log_t log __unused
)
119 os_log_create(const char *subsystem __unused
, const char *category __unused
)
121 return &_os_log_default
;
125 _os_log_string_is_public(const char *str __unused
)
130 __attribute__((noinline
,not_tail_called
)) void
131 _os_log_internal(void *dso
, os_log_t log
, uint8_t type
, const char *message
, ...)
134 void *addr
= __builtin_return_address(0);
136 va_start(args
, message
);
138 _os_log_with_args_internal(log
, type
, message
, args
, addr
, dso
);
145 #pragma mark - shim functions
147 __attribute__((noinline
,not_tail_called
)) void
148 os_log_with_args(os_log_t oslog
, os_log_type_t type
, const char *format
, va_list args
, void *addr
)
150 // if no address passed, look it up
152 addr
= __builtin_return_address(0);
155 _os_log_with_args_internal(oslog
, type
, format
, args
, addr
, NULL
);
159 _os_log_with_args_internal(os_log_t oslog
, os_log_type_t type
,
160 const char *format
, va_list args
, void *addr
, void *dso
)
162 uint32_t logging_config
= atm_get_diagnostic_config();
166 if (format
[0] == '\0') {
169 /* cf. r24974766 & r25201228*/
170 safe
= (!oslog_early_boot_complete
|| oslog_is_safe());
171 logging
= (!(logging_config
& ATM_TRACE_DISABLE
) || !(logging_config
& ATM_TRACE_OFF
));
173 if (oslog
!= &_os_log_replay
) {
174 _os_log_to_msgbuf_internal(format
, args
, safe
, logging
);
177 if (safe
&& logging
) {
178 _os_log_to_log_internal(oslog
, type
, format
, args
, addr
, dso
);
183 _os_log_to_msgbuf_internal(const char *format
, va_list args
, bool safe
, bool logging
)
185 static int msgbufreplay
= -1;
191 if (-1 == msgbufreplay
) msgbufreplay
= msgbufp
->msg_bufx
;
192 } else if (logging
&& (-1 != msgbufreplay
)) {
194 uint32_t localbuff_size
;
196 char *localbuff
, *p
, *s
, *next
, ch
;
198 position
= msgbufreplay
;
200 localbuff_size
= (msgbufp
->msg_size
+ 2); /* + '\n' + '\0' */
201 /* Size for non-blocking */
202 if (localbuff_size
> 4096) localbuff_size
= 4096;
204 /* Allocate a temporary non-circular buffer */
205 if ((localbuff
= (char *)kalloc_noblock(localbuff_size
))) {
206 /* in between here, the log could become bigger, but that's fine */
209 * The message buffer is circular; start at the replay pointer, and
210 * make one loop up to write pointer - 1.
212 p
= msgbufp
->msg_bufc
+ position
;
213 for (i
= newl
= 0; p
!= msgbufp
->msg_bufc
+ msgbufp
->msg_bufx
- 1; ++p
) {
214 if (p
>= msgbufp
->msg_bufc
+ msgbufp
->msg_size
)
215 p
= msgbufp
->msg_bufc
;
217 if (ch
== '\0') continue;
220 if (i
>= (localbuff_size
- 2)) break;
224 if (!newl
) localbuff
[i
++] = '\n';
228 while ((next
= strchr(s
, '\n'))) {
232 os_log(&_os_log_replay
, "%s", s
);
236 kfree(localbuff
, localbuff_size
);
241 va_copy(args_copy
, args
);
242 vprintf_log_locked(format
, args_copy
);
247 if (safe
) logwakeup();
251 _os_log_to_log_internal(os_log_t oslog
, os_log_type_t type
,
252 const char *format
, va_list args
, void *addr
, void *dso
)
254 struct os_log_buffer_context_s context
;
255 unsigned char buffer_data
[OS_LOG_BUFFER_MAX_SIZE
] __attribute__((aligned(8)));
256 os_log_buffer_t buffer
= (os_log_buffer_t
)buffer_data
;
257 uint8_t pubdata
[OS_LOG_BUFFER_MAX_SIZE
];
261 dso
= (void *) OSKextKextForAddress(format
);
267 if (!_os_trace_addr_in_text_segment(dso
, format
)) {
275 void *dso_addr
= (void *) OSKextKextForAddress(addr
);
276 if (dso
!= dso_addr
) {
280 memset(&context
, 0, sizeof(context
));
281 memset(buffer
, 0, OS_LOG_BUFFER_MAX_SIZE
);
283 context
.shimmed
= true;
284 context
.buffer
= buffer
;
285 context
.content_sz
= OS_LOG_BUFFER_MAX_SIZE
- sizeof(*buffer
);
286 context
.pubdata
= pubdata
;
287 context
.pubdata_sz
= sizeof(pubdata
);
289 va_copy(args_copy
, args
);
291 (void)hw_atomic_add(&oslog_p_total_msgcount
, 1);
292 if (_os_log_encode(format
, args_copy
, 0, &context
)) {
293 _os_log_actual(oslog
, type
, format
, dso
, addr
, &context
);
296 (void)hw_atomic_add(&oslog_p_error_count
, 1);
303 _os_trace_write_location_for_address(uint8_t buf
[static sizeof(uint64_t)],
304 void *dso
, const void *address
, firehose_tracepoint_flags_t
*flags
)
306 kernel_mach_header_t
*mh
= dso
;
308 if (mh
->filetype
== MH_EXECUTE
) {
309 *flags
= _firehose_tracepoint_flags_pc_style_main_exe
;
310 memcpy(buf
, (uint32_t[]){ (uintptr_t)address
- (uintptr_t)dso
},
312 return sizeof(uint32_t);
314 *flags
= _firehose_tracepoint_flags_pc_style_absolute
;
315 memcpy(buf
, (uintptr_t[]){ VM_KERNEL_UNSLIDE(address
) }, sizeof(uintptr_t));
317 return 6; // 48 bits are enough
319 return sizeof(uintptr_t);
327 _os_log_buffer_pack(uint8_t *buffdata
, size_t buffdata_sz
,
328 os_log_buffer_context_t ctx
)
330 os_log_buffer_t buffer
= ctx
->buffer
;
331 size_t buffer_sz
= sizeof(*ctx
->buffer
) + ctx
->content_sz
;
332 size_t total_sz
= buffer_sz
+ ctx
->pubdata_sz
;
334 if (total_sz
> buffdata_sz
) {
338 memcpy(buffdata
, buffer
, buffer_sz
);
339 memcpy(&buffdata
[buffer_sz
], ctx
->pubdata
, ctx
->pubdata_sz
);
344 _os_log_actual(os_log_t oslog __unused
, os_log_type_t type
, const char *format
,
345 void *dso
, void *addr
, os_log_buffer_context_t context
)
347 firehose_stream_t stream
;
348 firehose_tracepoint_flags_t flags
= 0;
349 firehose_tracepoint_id_u trace_id
;
350 uint8_t buffdata
[OS_LOG_BUFFER_MAX_SIZE
];
351 size_t addr_len
= 0, buffdata_sz
;
355 // dso == the start of the binary that was loaded
356 addr_len
= _os_trace_write_location_for_address(buffdata
, dso
, addr
, &flags
);
357 buffdata_sz
= _os_log_buffer_pack(buffdata
+ addr_len
,
358 sizeof(buffdata
) - addr_len
, context
);
359 if (buffdata_sz
== 0) {
362 buffdata_sz
+= addr_len
;
364 timestamp
= firehose_tracepoint_time(firehose_activity_flags_default
);
365 thread_id
= thread_tid(current_thread());
367 // create trace_id after we've set additional flags
368 trace_id
.ftid_value
= FIREHOSE_TRACE_ID_MAKE(firehose_tracepoint_namespace_log
,
369 type
, flags
, _os_trace_offset(dso
, format
, flags
));
372 firehose_debug_trace(stream
, trace_id
.ftid_value
, timestamp
,
373 format
, buffdata
, buffdata_sz
);
375 if (type
== OS_LOG_TYPE_INFO
|| type
== OS_LOG_TYPE_DEBUG
) {
376 stream
= firehose_stream_memory
;
379 stream
= firehose_stream_persist
;
381 _firehose_trace(stream
, trace_id
, timestamp
, buffdata
, buffdata_sz
);
384 static inline firehose_tracepoint_id_t
385 _firehose_trace(firehose_stream_t stream
, firehose_tracepoint_id_u ftid
,
386 uint64_t stamp
, const void *pubdata
, size_t publen
)
388 const uint16_t ft_size
= offsetof(struct firehose_tracepoint_s
, ft_data
);
389 const size_t _firehose_chunk_payload_size
=
390 sizeof(((struct firehose_chunk_s
*)0)->fc_data
);
392 firehose_tracepoint_t ft
;
394 if (slowpath(ft_size
+ publen
> _firehose_chunk_payload_size
)) {
395 // We'll need to have some handling here. For now - return 0
396 (void)hw_atomic_add(&oslog_p_error_count
, 1);
400 if (oslog_stream_open
&& (stream
!= firehose_stream_metadata
)) {
402 lck_spin_lock(&oslog_stream_lock
);
403 if (!oslog_stream_open
) {
404 lck_spin_unlock(&oslog_stream_lock
);
408 oslog_s_total_msgcount
++;
409 oslog_streamwrite_locked(ftid
, stamp
, pubdata
, publen
);
410 lck_spin_unlock(&oslog_stream_lock
);
411 oslog_streamwakeup();
415 ft
= __firehose_buffer_tracepoint_reserve(stamp
, stream
, (uint16_t)publen
, 0, NULL
);
417 if (oslog_boot_done
) {
418 if (stream
== firehose_stream_metadata
) {
419 (void)hw_atomic_add(&oslog_p_metadata_dropped_msgcount
, 1);
422 // If we run out of space in the persistence buffer we're
423 // dropping the message.
424 (void)hw_atomic_add(&oslog_p_dropped_msgcount
, 1);
428 firehose_chunk_t fbc
= firehose_boot_chunk
;
431 //only stream available during boot is persist
432 offset
= firehose_chunk_tracepoint_try_reserve(fbc
, stamp
,
433 firehose_stream_persist
, 0, publen
, 0, NULL
);
435 (void)hw_atomic_add(&oslog_p_boot_dropped_msgcount
, 1);
439 ft
= firehose_chunk_tracepoint_begin(fbc
, stamp
, publen
,
440 thread_tid(current_thread()), offset
);
441 memcpy(ft
->ft_data
, pubdata
, publen
);
442 firehose_chunk_tracepoint_end(fbc
, ft
, ftid
);
443 (void)hw_atomic_add(&oslog_p_saved_msgcount
, 1);
444 return ftid
.ftid_value
;
446 if (!oslog_boot_done
) {
447 oslog_boot_done
= true;
449 memcpy(ft
->ft_data
, pubdata
, publen
);
451 __firehose_buffer_tracepoint_flush(ft
, ftid
);
452 if (stream
== firehose_stream_metadata
) {
453 (void)hw_atomic_add(&oslog_p_metadata_saved_msgcount
, 1);
456 (void)hw_atomic_add(&oslog_p_saved_msgcount
, 1);
458 return ftid
.ftid_value
;
461 static oslog_stream_buf_entry_t
462 oslog_stream_create_buf_entry(oslog_stream_link_type_t type
, firehose_tracepoint_id_u ftid
,
463 uint64_t stamp
, const void* pubdata
, size_t publen
)
465 oslog_stream_buf_entry_t m_entry
= NULL
;
466 firehose_tracepoint_t ft
= NULL
;
467 size_t m_entry_len
= 0;
473 m_entry_len
= sizeof(struct oslog_stream_buf_entry_s
) +
474 sizeof(struct firehose_tracepoint_s
) + publen
;
475 m_entry
= (oslog_stream_buf_entry_t
) kalloc(m_entry_len
);
480 m_entry
->type
= type
;
481 m_entry
->timestamp
= stamp
;
482 m_entry
->size
= sizeof(struct firehose_tracepoint_s
) + publen
;
484 ft
= m_entry
->metadata
;
485 ft
->ft_thread
= thread_tid(current_thread());
486 ft
->ft_id
.ftid_value
= ftid
.ftid_value
;
487 ft
->ft_length
= publen
;
488 memcpy(ft
->ft_data
, pubdata
, publen
);
495 firehose_trace_metadata(firehose_stream_t stream
, firehose_tracepoint_id_u ftid
,
496 uint64_t stamp
, const void *pubdata
, size_t publen
)
498 oslog_stream_buf_entry_t m_entry
= NULL
;
500 // If streaming mode is not on, only log the metadata
501 // in the persistence buffer
503 lck_spin_lock(&oslog_stream_lock
);
504 if (!oslog_stream_open
) {
505 lck_spin_unlock(&oslog_stream_lock
);
508 lck_spin_unlock(&oslog_stream_lock
);
510 // Setup and write the stream metadata entry
511 m_entry
= oslog_stream_create_buf_entry(oslog_stream_link_type_metadata
, ftid
,
512 stamp
, pubdata
, publen
);
514 (void)hw_atomic_add(&oslog_s_error_count
, 1);
518 lck_spin_lock(&oslog_stream_lock
);
519 if (!oslog_stream_open
) {
520 lck_spin_unlock(&oslog_stream_lock
);
521 kfree(m_entry
, sizeof(struct oslog_stream_buf_entry_s
) +
522 sizeof(struct firehose_tracepoint_s
) + publen
);
525 oslog_s_metadata_msgcount
++;
526 oslog_streamwrite_metadata_locked(m_entry
);
527 lck_spin_unlock(&oslog_stream_lock
);
530 _firehose_trace(stream
, ftid
, stamp
, pubdata
, publen
);
534 firehose_tracepoint_id_t
535 firehose_debug_trace(firehose_stream_t stream
, firehose_tracepoint_id_t trace_id
,
536 uint64_t timestamp
, const char *format
, const void *pubdata
, size_t publen
)
538 kprintf("[os_log stream 0x%x trace_id 0x%llx timestamp %llu format '%s' data %p len %lu]\n",
539 (unsigned int)stream
, (unsigned long long)trace_id
, timestamp
,
540 format
, pubdata
, publen
);
542 const unsigned char *cdata
= (const unsigned char *)pubdata
;
543 for (i
=0; i
< publen
; i
+= 8) {
544 kprintf(">oslog 0x%08x: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
546 (i
+0) < publen
? cdata
[i
+0] : 0,
547 (i
+1) < publen
? cdata
[i
+1] : 0,
548 (i
+2) < publen
? cdata
[i
+2] : 0,
549 (i
+3) < publen
? cdata
[i
+3] : 0,
550 (i
+4) < publen
? cdata
[i
+4] : 0,
551 (i
+5) < publen
? cdata
[i
+5] : 0,
552 (i
+6) < publen
? cdata
[i
+6] : 0,
553 (i
+7) < publen
? cdata
[i
+7] : 0
560 __firehose_buffer_push_to_logd(firehose_buffer_t fb __unused
, bool for_io __unused
) {
566 __firehose_allocate(vm_offset_t
*addr
, vm_size_t size __unused
)
568 firehose_chunk_t kernel_buffer
= (firehose_chunk_t
)kernel_firehose_addr
;
570 if (kernel_firehose_addr
) {
571 *addr
= kernel_firehose_addr
;
577 // Now that we are done adding logs to this chunk, set the number of writers to 0
578 // Without this, logd won't flush when the page is full
579 firehose_boot_chunk
->fc_pos
.fcp_refcnt
= 0;
580 memcpy(&kernel_buffer
[FIREHOSE_BUFFER_KERNEL_CHUNK_COUNT
- 1], (const void *)firehose_boot_chunk
, FIREHOSE_CHUNK_SIZE
);
583 // There isnt a lock held in this case.
585 __firehose_critical_region_enter(void) {
586 disable_preemption();
591 __firehose_critical_region_leave(void) {