]> git.saurik.com Git - apple/xnu.git/blob - libkern/os/log.c
xnu-7195.60.75.tar.gz
[apple/xnu.git] / libkern / os / log.c
1 /* * Copyright (c) 2019 Apple Inc. All rights reserved. */
2
3 #include <stddef.h>
4 #undef offset
5
6 #include <kern/cpu_data.h>
7 #include <os/base.h>
8 #include <os/object.h>
9 #include <os/log.h>
10 #include <stdbool.h>
11 #include <stdint.h>
12
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>
21
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>
30
31 #include <firehose/tracepoint_private.h>
32 #include <firehose/chunk_private.h>
33 #include <os/firehose_buffer_private.h>
34 #include <os/firehose.h>
35
36 #include <os/log_private.h>
37 #include "trace_internal.h"
38
39 #include "log_encode.h"
40
41 struct os_log_s {
42 int a;
43 };
44
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;
49
50 extern bool bsd_log_lock(bool);
51 extern void bsd_log_unlock(void);
52 extern void logwakeup(struct msgbuf *);
53
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)
57
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);
62
63 extern int oslog_stream_open;
64
65 extern void *OSKextKextForAddress(const void *);
66
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;
77
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;
82
83 /* Counters for msgbuf logging */
84 uint32_t oslog_msgbuf_msgcount = 0;
85 uint32_t oslog_msgbuf_dropped_msgcount = 0;
86
87 static bool oslog_boot_done = false;
88
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 */
93
94 // XXX
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);
98
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);
102
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);
106
107 static void
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);
110
111 static void
112 _os_log_to_msgbuf_internal(const char *format, va_list args, bool safe, bool logging, bool addcr);
113
114 static void
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);
117
118
119 static void
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);
122
123 bool
124 os_log_info_enabled(os_log_t log __unused)
125 {
126 return true;
127 }
128
129 bool
130 os_log_debug_enabled(os_log_t log __unused)
131 {
132 return true;
133 }
134
135 os_log_t
136 os_log_create(const char *subsystem __unused, const char *category __unused)
137 {
138 return &_os_log_default;
139 }
140
141 bool
142 _os_log_string_is_public(const char *str __unused)
143 {
144 return true;
145 }
146
147 __attribute__((noinline, not_tail_called)) void
148 _os_log_internal(void *dso, os_log_t log, uint8_t type, const char *message, ...)
149 {
150 va_list args;
151 void *addr = __builtin_return_address(0);
152
153 va_start(args, message);
154
155 _os_log_with_args_internal(log, type, message, args, addr, dso, FALSE, FALSE);
156
157 va_end(args);
158
159 return;
160 }
161
162 __attribute__((noinline, not_tail_called)) int
163 _os_log_internal_driverKit(void *dso, os_log_t log, uint8_t type, const char *message, ...)
164 {
165 va_list args;
166 void *addr = __builtin_return_address(0);
167 bool driverKitLog = FALSE;
168
169 /*
170 * We want to be able to identify dexts from the logs.
171 *
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.
176 *
177 * Dext however are not loaded in kernel space so they do not
178 * have a kernel range of addresses.
179 *
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.
184 *
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.
188 */
189 task_t self_task = current_task();
190
191 /*
192 * Only dextis are supposed to use this log path.
193 */
194 if (!task_is_driver(self_task)) {
195 return EPERM;
196 }
197
198 uint64_t loadTag = get_task_loadTag(self_task);
199 if (loadTag != 0) {
200 driverKitLog = TRUE;
201 addr = (void*) loadTag;
202 }
203 va_start(args, message);
204
205 _os_log_with_args_internal(log, type, message, args, addr, dso, driverKitLog, true);
206
207 va_end(args);
208
209 return 0;
210 }
211
212 #pragma mark - shim functions
213
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)
216 {
217 // if no address passed, look it up
218 if (addr == NULL) {
219 addr = __builtin_return_address(0);
220 }
221
222 _os_log_with_args_internal(oslog, type, format, args, addr, NULL, FALSE, FALSE);
223 }
224
225 static void
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)
228 {
229 uint32_t logging_config = atm_get_diagnostic_config();
230 boolean_t safe;
231 boolean_t logging;
232
233 if (format[0] == '\0') {
234 return;
235 }
236
237 /* early boot can log to dmesg for later replay (27307943) */
238 safe = (startup_phase < STARTUP_SUB_EARLY_BOOT || oslog_is_safe());
239
240 if (logging_config & ATM_TRACE_DISABLE || logging_config & ATM_TRACE_OFF) {
241 logging = false;
242 } else {
243 logging = true;
244 }
245
246 if (oslog != &_os_log_replay) {
247 _os_log_to_msgbuf_internal(format, args, safe, logging, addcr);
248 }
249
250 if (safe && logging) {
251 _os_log_to_log_internal(oslog, type, format, args, addr, dso, driverKit);
252 }
253 }
254
255 static void
256 _os_log_to_msgbuf_internal(const char *format, va_list args, bool safe, bool logging, bool addcr)
257 {
258 /*
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
263 * environment.
264 */
265 const int MSGBUF_TIMESTAMP_THRESHOLD = 4096;
266 static int msgbufreplay = -1;
267 static bool newlogline = true;
268 va_list args_copy;
269
270 if (!bsd_log_lock(safe)) {
271 os_atomic_inc(&oslog_msgbuf_dropped_msgcount, relaxed);
272 return;
273 }
274
275 if (!safe) {
276 if (-1 == msgbufreplay) {
277 msgbufreplay = msgbufp->msg_bufx;
278 }
279 } else if (logging && (-1 != msgbufreplay)) {
280 uint32_t i;
281 uint32_t localbuff_size;
282 int newl, position;
283 char *localbuff, *p, *s, *next, ch;
284
285 position = msgbufreplay;
286 msgbufreplay = -1;
287 localbuff_size = (msgbufp->msg_size + 2); /* + '\n' + '\0' */
288 /* Size for non-blocking */
289 if (localbuff_size > 4096) {
290 localbuff_size = 4096;
291 }
292 bsd_log_unlock();
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 */
297 bsd_log_lock(true);
298 /*
299 * The message buffer is circular; start at the replay pointer, and
300 * make one loop up to write pointer - 1.
301 */
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;
306 }
307 ch = *p;
308 if (ch == '\0') {
309 continue;
310 }
311 newl = (ch == '\n');
312 localbuff[i++] = ch;
313 if (i >= (localbuff_size - 2)) {
314 break;
315 }
316 }
317 bsd_log_unlock();
318
319 if (!newl) {
320 localbuff[i++] = '\n';
321 }
322 localbuff[i++] = 0;
323
324 s = localbuff;
325 while ((next = strchr(s, '\n'))) {
326 next++;
327 ch = next[0];
328 next[0] = 0;
329 os_log(&_os_log_replay, "%s", s);
330 next[0] = ch;
331 s = next;
332 }
333 kheap_free(KHEAP_TEMP, localbuff, localbuff_size);
334 }
335 bsd_log_lock(true);
336 }
337
338 /* Do not prepend timestamps when we are memory-constricted */
339 if (newlogline && (msgbufp->msg_size > MSGBUF_TIMESTAMP_THRESHOLD)) {
340 clock_sec_t secs;
341 clock_usec_t microsecs;
342 const uint64_t timestamp = firehose_tracepoint_time(firehose_activity_flags_default);
343 absolutetime_to_microtime(timestamp, &secs, &microsecs);
344 printf_log_locked(FALSE, "[%5lu.%06u]: ", (unsigned long)secs, microsecs);
345 }
346
347 va_copy(args_copy, args);
348 newlogline = vprintf_log_locked(format, args_copy, addcr);
349 va_end(args_copy);
350
351 bsd_log_unlock();
352 logwakeup(msgbufp);
353 os_atomic_inc(&oslog_msgbuf_msgcount, relaxed);
354 }
355
356 static void
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)
359 {
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];
365 va_list args_copy;
366
367 if (addr == NULL) {
368 return;
369 }
370
371 if (!PE_get_primary_kc_format(&kcformat)) {
372 return;
373 }
374
375 if (kcformat == KCFormatStatic || kcformat == KCFormatKCGEN) {
376 void *baseAddress = PE_get_kc_baseaddress(KCKindPrimary);
377 if (!baseAddress) {
378 return;
379 }
380 dso = baseAddress;
381 } else if (kcformat == KCFormatDynamic || kcformat == KCFormatFileset) {
382 if (dso == NULL) {
383 dso = (void *) OSKextKextForAddress(format);
384 if (dso == NULL) {
385 return;
386 }
387 }
388 if (!_os_trace_addr_in_text_segment(dso, format)) {
389 return;
390 }
391 if (!driverKit) {
392 void *dso_addr = (void *) OSKextKextForAddress(addr);
393 if (dso != dso_addr) {
394 return;
395 }
396 }
397 }
398
399 memset(&context, 0, sizeof(context));
400 memset(buffer, 0, OS_LOG_BUFFER_MAX_SIZE);
401
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);
407
408 va_copy(args_copy, args);
409
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);
413 } else {
414 os_atomic_inc(&oslog_p_error_count, relaxed);
415 }
416
417 va_end(args_copy);
418 }
419
420 static inline size_t
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)
423 {
424 uintptr_t shift_addr = (uintptr_t)address - (uintptr_t)dso;
425
426 kc_format_t kcformat = KCFormatUnknown;
427 __assert_only bool result = PE_get_primary_kc_format(&kcformat);
428 assert(result);
429
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);
434 } else {
435 kernel_mach_header_t *mh = dso;
436
437 /*
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
441 * absolute pc.
442 */
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);
447 } else {
448 *flags = _firehose_tracepoint_flags_pc_style_absolute;
449 if (!driverKit) {
450 shift_addr = VM_KERNEL_UNSLIDE(address);
451 } else {
452 shift_addr = (uintptr_t) address;
453 }
454 memcpy(buf, (uintptr_t[]){ shift_addr }, sizeof(uintptr_t));
455 #if __LP64__
456 return 6; // 48 bits are enough
457 #else
458 return sizeof(uintptr_t);
459 #endif
460 }
461 }
462 }
463
464
465 OS_ALWAYS_INLINE
466 static inline size_t
467 _os_log_buffer_pack(uint8_t *buffdata, size_t buffdata_sz,
468 os_log_buffer_context_t ctx)
469 {
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;
473
474 if (total_sz > buffdata_sz) {
475 return 0;
476 }
477
478 memcpy(buffdata, buffer, buffer_sz);
479 memcpy(&buffdata[buffer_sz], ctx->pubdata, ctx->pubdata_sz);
480 return total_sz;
481 }
482
483 static void
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)
486 {
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;
492 uint64_t timestamp;
493 uint64_t thread_id;
494
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) {
500 return;
501 }
502 buffdata_sz += addr_len;
503
504 timestamp = firehose_tracepoint_time(firehose_activity_flags_default);
505 thread_id = thread_tid(current_thread());
506
507 if (driverKit) {
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));
512 } else {
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));
516 }
517
518 if (type == OS_LOG_TYPE_INFO || type == OS_LOG_TYPE_DEBUG) {
519 stream = firehose_stream_memory;
520 } else {
521 stream = firehose_stream_persist;
522 }
523 _firehose_trace(stream, trace_id, timestamp, buffdata, buffdata_sz, true);
524 }
525
526 bool
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)
529 {
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];
534 size_t wr_pos = 0;
535
536 if (buff_len + 16 + sizeof(uint32_t) > OS_LOG_BUFFER_MAX_SIZE) {
537 return false;
538 }
539
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;
542
543 memcpy(pubdata, &offset, sizeof(uint32_t));
544 wr_pos += sizeof(uint32_t);
545 memcpy(pubdata + wr_pos, uuid, 16);
546 wr_pos += 16;
547
548 memcpy(pubdata + wr_pos, buff, buff_len);
549
550 // create firehose trace id
551 trace_id.ftid_value = FIREHOSE_TRACE_ID_MAKE(firehose_tracepoint_namespace_log,
552 type, flags, offset);
553
554 if (type == OS_LOG_TYPE_INFO || type == OS_LOG_TYPE_DEBUG) {
555 stream = firehose_stream_memory;
556 } else {
557 stream = firehose_stream_persist;
558 }
559
560 os_atomic_inc(&oslog_p_coprocessor_total_msgcount, relaxed);
561
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);
565
566 if (return_id == 0) {
567 os_atomic_inc(&oslog_p_coprocessor_dropped_msgcount, relaxed);
568 return false;
569 }
570 return true;
571 }
572
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)
576 {
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);
580
581 firehose_tracepoint_t ft;
582
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);
586 return 0;
587 }
588
589 if (oslog_stream_open && (stream != firehose_stream_metadata) && use_streaming) {
590 stream_lock();
591 if (!oslog_stream_open) {
592 stream_unlock();
593 goto out;
594 }
595
596 oslog_s_total_msgcount++;
597 oslog_streamwrite_locked(ftid, stamp, pubdata, publen);
598 stream_unlock();
599 oslog_streamwakeup();
600 }
601
602 out:
603 ft = __firehose_buffer_tracepoint_reserve(stamp, stream, (uint16_t)publen, 0, NULL);
604 if (!fastpath(ft)) {
605 if (oslog_boot_done) {
606 if (stream == firehose_stream_metadata) {
607 os_atomic_inc(&oslog_p_metadata_dropped_msgcount, relaxed);
608 } else {
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);
612 }
613 return 0;
614 }
615 firehose_chunk_t fbc = firehose_boot_chunk;
616 long offset;
617
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);
621 if (offset <= 0) {
622 os_atomic_inc(&oslog_p_boot_dropped_msgcount, relaxed);
623 return 0;
624 }
625
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;
632 }
633 if (!oslog_boot_done) {
634 oslog_boot_done = true;
635 }
636 memcpy(ft->ft_data, pubdata, publen);
637
638 __firehose_buffer_tracepoint_flush(ft, ftid);
639 if (stream == firehose_stream_metadata) {
640 os_atomic_inc(&oslog_p_metadata_saved_msgcount, relaxed);
641 } else {
642 os_atomic_inc(&oslog_p_saved_msgcount, relaxed);
643 }
644 return ftid.ftid_value;
645 }
646
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)
651 {
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;
655
656 if (!pubdata || publen > UINT16_MAX || ft_size > UINT16_MAX) {
657 return NULL;
658 }
659
660 m_entry = kheap_alloc(KHEAP_DEFAULT, m_entry_len, Z_WAITOK);
661 if (!m_entry) {
662 return NULL;
663 }
664 m_entry->type = type;
665 m_entry->timestamp = stamp;
666 m_entry->size = (uint16_t)ft_size;
667
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);
673
674 return m_entry;
675 }
676
677 void
678 os_log_coprocessor_register(const char *uuid, const char *file_path, bool copy)
679 {
680 uint64_t stamp;
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;
684 union {
685 struct firehose_trace_uuid_info_s uuid_info;
686 char path[PATH_MAX + sizeof(struct firehose_trace_uuid_info_s)];
687 } buf;
688
689 if (path_size > PATH_MAX) {
690 return;
691 }
692
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;
697
698 stamp = firehose_tracepoint_time(firehose_activity_flags_default);
699
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);
703
704 // write path to buffer
705 memcpy(buf.uuid_info.ftui_path, file_path, path_size);
706
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);
709 return;
710 }
711
712 #ifdef KERNEL
713 void
714 firehose_trace_metadata(firehose_stream_t stream, firehose_tracepoint_id_u ftid,
715 uint64_t stamp, const void *pubdata, size_t publen)
716 {
717 oslog_stream_buf_entry_t m_entry = NULL;
718
719 // If streaming mode is not on, only log the metadata
720 // in the persistence buffer
721
722 stream_lock();
723 if (!oslog_stream_open) {
724 stream_unlock();
725 goto finish;
726 }
727 stream_unlock();
728
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);
732 if (!m_entry) {
733 os_atomic_inc(&oslog_s_error_count, relaxed);
734 goto finish;
735 }
736
737 stream_lock();
738 if (!oslog_stream_open) {
739 stream_unlock();
740 kheap_free(KHEAP_DEFAULT, m_entry,
741 sizeof(struct oslog_stream_buf_entry_s) +
742 sizeof(struct firehose_tracepoint_s) + publen);
743 goto finish;
744 }
745 oslog_s_metadata_msgcount++;
746 oslog_streamwrite_metadata_locked(m_entry);
747 stream_unlock();
748
749 finish:
750 _firehose_trace(stream, ftid, stamp, pubdata, publen, true);
751 }
752 #endif
753
754 void
755 __firehose_buffer_push_to_logd(firehose_buffer_t fb __unused, bool for_io __unused)
756 {
757 oslogwakeup();
758 return;
759 }
760
761 void
762 __firehose_allocate(vm_offset_t *addr, vm_size_t size __unused)
763 {
764 firehose_chunk_t kernel_buffer = (firehose_chunk_t)kernel_firehose_addr;
765
766 if (kernel_firehose_addr) {
767 *addr = kernel_firehose_addr;
768 } else {
769 *addr = 0;
770 return;
771 }
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);
776 return;
777 }
778 // There isnt a lock held in this case.
779 void
780 __firehose_critical_region_enter(void)
781 {
782 disable_preemption();
783 return;
784 }
785
786 void
787 __firehose_critical_region_leave(void)
788 {
789 enable_preemption();
790 return;
791 }
792
793 #ifdef CONFIG_XNUPOST
794
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 "#")
799
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);
810
811 kern_return_t test_os_log(void);
812 kern_return_t test_os_log_parallel(void);
813
814 #define GENOSLOGHELPER(fname, ident, callout_f) \
815 void fname(uint32_t uniqid, uint64_t count) \
816 { \
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++) \
822 { \
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);*/ \
827 } \
828 }
829
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);
835
836 kern_return_t
837 test_os_log()
838 {
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;
846 size_t datalen = 0;
847 uint64_t a = mach_absolute_time();
848 uint64_t seqno = 1;
849 uint64_t total_seqno = 2;
850
851 os_log_t log_handle = os_log_create("com.apple.xnu.test.t1", "kpost");
852
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");
857
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);
862
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");
866
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);
870
871 seqno += 1;
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);
875
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");
879
880 uint32_t logging_config = atm_get_diagnostic_config();
881 T_LOG("checking atm_diagnostic_config 0x%X", logging_config);
882
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.");
885 return KERN_SUCCESS;
886 }
887
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");
890
891 a = mach_absolute_time();
892 total_seqno = 1;
893 seqno = 1;
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");
901
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");
905
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");
909
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");
913
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");
917
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");
921
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");
925
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);
928
929 return KERN_SUCCESS;
930 }
931
932 static uint32_t _test_log_loop_count = 0;
933 void
934 _test_log_loop(void * arg __unused, wait_result_t wres __unused)
935 {
936 uint32_t uniqid = RandomULong();
937 test_oslog_debug_helper(uniqid, 100);
938 os_atomic_add(&_test_log_loop_count, 100, relaxed);
939 }
940
941 kern_return_t
942 test_os_log_parallel(void)
943 {
944 thread_t thread[2];
945 kern_return_t kr;
946 uint32_t uniqid = RandomULong();
947
948 printf("oslog internal counters total %u , saved %u, dropped %u", oslog_p_total_msgcount, oslog_p_saved_msgcount,
949 oslog_p_dropped_msgcount);
950
951 kr = kernel_thread_start(_test_log_loop, NULL, &thread[0]);
952 T_ASSERT_EQ_INT(kr, KERN_SUCCESS, "kernel_thread_start returned successfully");
953
954 kr = kernel_thread_start(_test_log_loop, NULL, &thread[1]);
955 T_ASSERT_EQ_INT(kr, KERN_SUCCESS, "kernel_thread_start returned successfully");
956
957 test_oslog_info_helper(uniqid, 100);
958
959 /* wait until other thread has also finished */
960 while (_test_log_loop_count < 200) {
961 delay(1000);
962 }
963
964 thread_deallocate(thread[0]);
965 thread_deallocate(thread[1]);
966
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");
970
971 return KERN_SUCCESS;
972 }
973
974 void
975 test_oslog_handleOSLogCtl(int32_t * in, int32_t * out, int32_t len)
976 {
977 if (!in || !out || len != 4) {
978 return;
979 }
980 switch (in[0]) {
981 case 1:
982 {
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;
988 break;
989 }
990 case 2:
991 {
992 /* mini stress run */
993 out[0] = test_os_log_parallel();
994 break;
995 }
996 case 3:
997 {
998 /* drop msg tests */
999 out[1] = RandomULong();
1000 out[0] = test_stresslog_dropmsg(out[1]);
1001 break;
1002 }
1003 case 4:
1004 {
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;
1010 return;
1011 }
1012
1013 switch (in[1]) {
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;
1020 }
1021 out[0] = KERN_SUCCESS;
1022 break;
1023 /* end of case 4 */
1024 }
1025 default:
1026 {
1027 out[0] = KERN_INVALID_VALUE;
1028 break;
1029 }
1030 }
1031 return;
1032 }
1033
1034 kern_return_t
1035 test_stresslog_dropmsg(uint32_t uniqid)
1036 {
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);
1045 }
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;
1049 }
1050
1051 #endif