]> git.saurik.com Git - apple/xnu.git/blob - libkern/os/log.c
a2638fee590e25e517ce57709682a31359897d7d
[apple/xnu.git] / libkern / os / log.c
1 #include <stddef.h>
2 #undef offset
3
4 #include <kern/cpu_data.h>
5 #include <os/base.h>
6 #include <os/object.h>
7 #include <os/log.h>
8 #include <stdbool.h>
9 #include <stdint.h>
10
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>
19
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>
26
27 #include <firehose/tracepoint_private.h>
28 #include <firehose/chunk_private.h>
29 #include <os/firehose_buffer_private.h>
30 #include <os/firehose.h>
31
32 #include <os/log_private.h>
33 #include "trace_internal.h"
34
35 #include "log_encode.h"
36
37 struct os_log_s {
38 int a;
39 };
40
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;
45
46 extern void bsd_log_lock(void);
47 extern void bsd_log_unlock(void);
48 extern void logwakeup(void);
49
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);
55
56 extern int oslog_stream_open;
57
58 extern void *OSKextKextForAddress(const void *);
59
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;
68
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;
73
74 static bool oslog_boot_done = false;
75 extern boolean_t oslog_early_boot_complete;
76
77 // XXX
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);
81
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);
85
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);
89
90 static void
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);
93
94 static void
95 _os_log_to_msgbuf_internal(const char *format, va_list args, bool safe, bool logging);
96
97 static void
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);
100
101
102 static void
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);
105
106 bool
107 os_log_info_enabled(os_log_t log __unused)
108 {
109 return true;
110 }
111
112 bool
113 os_log_debug_enabled(os_log_t log __unused)
114 {
115 return true;
116 }
117
118 os_log_t
119 os_log_create(const char *subsystem __unused, const char *category __unused)
120 {
121 return &_os_log_default;
122 }
123
124 bool
125 _os_log_string_is_public(const char *str __unused)
126 {
127 return true;
128 }
129
130 __attribute__((noinline,not_tail_called)) void
131 _os_log_internal(void *dso, os_log_t log, uint8_t type, const char *message, ...)
132 {
133 va_list args;
134 void *addr = __builtin_return_address(0);
135
136 va_start(args, message);
137
138 _os_log_with_args_internal(log, type, message, args, addr, dso);
139
140 va_end(args);
141
142 return;
143 }
144
145 #pragma mark - shim functions
146
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)
149 {
150 // if no address passed, look it up
151 if (addr == NULL) {
152 addr = __builtin_return_address(0);
153 }
154
155 _os_log_with_args_internal(oslog, type, format, args, addr, NULL);
156 }
157
158 static void
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)
161 {
162 uint32_t logging_config = atm_get_diagnostic_config();
163 boolean_t safe;
164 boolean_t logging;
165
166 if (format[0] == '\0') {
167 return;
168 }
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));
172
173 if (oslog != &_os_log_replay) {
174 _os_log_to_msgbuf_internal(format, args, safe, logging);
175 }
176
177 if (safe && logging) {
178 _os_log_to_log_internal(oslog, type, format, args, addr, dso);
179 }
180 }
181
182 static void
183 _os_log_to_msgbuf_internal(const char *format, va_list args, bool safe, bool logging)
184 {
185 static int msgbufreplay = -1;
186 va_list args_copy;
187
188 bsd_log_lock();
189
190 if (!safe) {
191 if (-1 == msgbufreplay) msgbufreplay = msgbufp->msg_bufx;
192 } else if (logging && (-1 != msgbufreplay)) {
193 uint32_t i;
194 uint32_t localbuff_size;
195 int newl, position;
196 char *localbuff, *p, *s, *next, ch;
197
198 position = msgbufreplay;
199 msgbufreplay = -1;
200 localbuff_size = (msgbufp->msg_size + 2); /* + '\n' + '\0' */
201 /* Size for non-blocking */
202 if (localbuff_size > 4096) localbuff_size = 4096;
203 bsd_log_unlock();
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 */
207 bsd_log_lock();
208 /*
209 * The message buffer is circular; start at the replay pointer, and
210 * make one loop up to write pointer - 1.
211 */
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;
216 ch = *p;
217 if (ch == '\0') continue;
218 newl = (ch == '\n');
219 localbuff[i++] = ch;
220 if (i >= (localbuff_size - 2)) break;
221 }
222 bsd_log_unlock();
223
224 if (!newl) localbuff[i++] = '\n';
225 localbuff[i++] = 0;
226
227 s = localbuff;
228 while ((next = strchr(s, '\n'))) {
229 next++;
230 ch = next[0];
231 next[0] = 0;
232 os_log(&_os_log_replay, "%s", s);
233 next[0] = ch;
234 s = next;
235 }
236 kfree(localbuff, localbuff_size);
237 }
238 bsd_log_lock();
239 }
240
241 va_copy(args_copy, args);
242 vprintf_log_locked(format, args_copy);
243 va_end(args_copy);
244
245 bsd_log_unlock();
246
247 if (safe) logwakeup();
248 }
249
250 static void
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)
253 {
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];
258 va_list args_copy;
259
260 if (dso == NULL) {
261 dso = (void *) OSKextKextForAddress(format);
262 if (dso == NULL) {
263 return;
264 }
265 }
266
267 if (!_os_trace_addr_in_text_segment(dso, format)) {
268 return;
269 }
270
271 if (addr == NULL) {
272 return;
273 }
274
275 void *dso_addr = (void *) OSKextKextForAddress(addr);
276 if (dso != dso_addr) {
277 return;
278 }
279
280 memset(&context, 0, sizeof(context));
281 memset(buffer, 0, OS_LOG_BUFFER_MAX_SIZE);
282
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);
288
289 va_copy(args_copy, args);
290
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);
294 }
295 else {
296 (void)hw_atomic_add(&oslog_p_error_count, 1);
297 }
298
299 va_end(args_copy);
300 }
301
302 static inline size_t
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)
305 {
306 kernel_mach_header_t *mh = dso;
307
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 },
311 sizeof(uint32_t));
312 return sizeof(uint32_t);
313 } else {
314 *flags = _firehose_tracepoint_flags_pc_style_absolute;
315 memcpy(buf, (uintptr_t[]){ VM_KERNEL_UNSLIDE(address) }, sizeof(uintptr_t));
316 #if __LP64__
317 return 6; // 48 bits are enough
318 #else
319 return sizeof(uintptr_t);
320 #endif
321 }
322 }
323
324
325 OS_ALWAYS_INLINE
326 static inline size_t
327 _os_log_buffer_pack(uint8_t *buffdata, size_t buffdata_sz,
328 os_log_buffer_context_t ctx)
329 {
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;
333
334 if (total_sz > buffdata_sz) {
335 return 0;
336 }
337
338 memcpy(buffdata, buffer, buffer_sz);
339 memcpy(&buffdata[buffer_sz], ctx->pubdata, ctx->pubdata_sz);
340 return total_sz;
341 }
342
343 static void
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)
346 {
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;
352 uint64_t timestamp;
353 uint64_t thread_id;
354
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) {
360 return;
361 }
362 buffdata_sz += addr_len;
363
364 timestamp = firehose_tracepoint_time(firehose_activity_flags_default);
365 thread_id = thread_tid(current_thread());
366
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));
370
371 if (FALSE) {
372 firehose_debug_trace(stream, trace_id.ftid_value, timestamp,
373 format, buffdata, buffdata_sz);
374 }
375 if (type == OS_LOG_TYPE_INFO || type == OS_LOG_TYPE_DEBUG) {
376 stream = firehose_stream_memory;
377 }
378 else {
379 stream = firehose_stream_persist;
380 }
381 _firehose_trace(stream, trace_id, timestamp, buffdata, buffdata_sz);
382 }
383
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)
387 {
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);
391
392 firehose_tracepoint_t ft;
393
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);
397 return 0;
398 }
399
400 if (oslog_stream_open && (stream != firehose_stream_metadata)) {
401
402 lck_spin_lock(&oslog_stream_lock);
403 if (!oslog_stream_open) {
404 lck_spin_unlock(&oslog_stream_lock);
405 goto out;
406 }
407
408 oslog_s_total_msgcount++;
409 oslog_streamwrite_locked(ftid, stamp, pubdata, publen);
410 lck_spin_unlock(&oslog_stream_lock);
411 oslog_streamwakeup();
412 }
413
414 out:
415 ft = __firehose_buffer_tracepoint_reserve(stamp, stream, (uint16_t)publen, 0, NULL);
416 if (!fastpath(ft)) {
417 if (oslog_boot_done) {
418 if (stream == firehose_stream_metadata) {
419 (void)hw_atomic_add(&oslog_p_metadata_dropped_msgcount, 1);
420 }
421 else {
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);
425 }
426 return 0;
427 }
428 firehose_chunk_t fbc = firehose_boot_chunk;
429 long offset;
430
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);
434 if (offset <= 0) {
435 (void)hw_atomic_add(&oslog_p_boot_dropped_msgcount, 1);
436 return 0;
437 }
438
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;
445 }
446 if (!oslog_boot_done) {
447 oslog_boot_done = true;
448 }
449 memcpy(ft->ft_data, pubdata, publen);
450
451 __firehose_buffer_tracepoint_flush(ft, ftid);
452 if (stream == firehose_stream_metadata) {
453 (void)hw_atomic_add(&oslog_p_metadata_saved_msgcount, 1);
454 }
455 else {
456 (void)hw_atomic_add(&oslog_p_saved_msgcount, 1);
457 }
458 return ftid.ftid_value;
459 }
460
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)
464 {
465 oslog_stream_buf_entry_t m_entry = NULL;
466 firehose_tracepoint_t ft = NULL;
467 size_t m_entry_len = 0;
468
469 if (!pubdata) {
470 return NULL;
471 }
472
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);
476 if (!m_entry) {
477 return NULL;
478 }
479
480 m_entry->type = type;
481 m_entry->timestamp = stamp;
482 m_entry->size = sizeof(struct firehose_tracepoint_s) + publen;
483
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);
489
490 return m_entry;
491 }
492
493 #ifdef KERNEL
494 void
495 firehose_trace_metadata(firehose_stream_t stream, firehose_tracepoint_id_u ftid,
496 uint64_t stamp, const void *pubdata, size_t publen)
497 {
498 oslog_stream_buf_entry_t m_entry = NULL;
499
500 // If streaming mode is not on, only log the metadata
501 // in the persistence buffer
502
503 lck_spin_lock(&oslog_stream_lock);
504 if (!oslog_stream_open) {
505 lck_spin_unlock(&oslog_stream_lock);
506 goto finish;
507 }
508 lck_spin_unlock(&oslog_stream_lock);
509
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);
513 if (!m_entry) {
514 (void)hw_atomic_add(&oslog_s_error_count, 1);
515 goto finish;
516 }
517
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);
523 goto finish;
524 }
525 oslog_s_metadata_msgcount++;
526 oslog_streamwrite_metadata_locked(m_entry);
527 lck_spin_unlock(&oslog_stream_lock);
528
529 finish:
530 _firehose_trace(stream, ftid, stamp, pubdata, publen);
531 }
532 #endif
533
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)
537 {
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);
541 size_t i;
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",
545 (unsigned int)i,
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
554 );
555 }
556 return trace_id;
557 }
558
559 void
560 __firehose_buffer_push_to_logd(firehose_buffer_t fb __unused, bool for_io __unused) {
561 oslogwakeup();
562 return;
563 }
564
565 void
566 __firehose_allocate(vm_offset_t *addr, vm_size_t size __unused)
567 {
568 firehose_chunk_t kernel_buffer = (firehose_chunk_t)kernel_firehose_addr;
569
570 if (kernel_firehose_addr) {
571 *addr = kernel_firehose_addr;
572 }
573 else {
574 *addr = 0;
575 return;
576 }
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);
581 return;
582 }
583 // There isnt a lock held in this case.
584 void
585 __firehose_critical_region_enter(void) {
586 disable_preemption();
587 return;
588 }
589
590 void
591 __firehose_critical_region_leave(void) {
592 enable_preemption();
593 return;
594 }
595