]> git.saurik.com Git - apple/xnu.git/blob - libkern/os/log.c
xnu-7195.101.1.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 #include "log_mem.h"
41
42 struct os_log_s {
43 int a;
44 };
45
46 struct os_log_s _os_log_default;
47 struct os_log_s _os_log_replay;
48
49 LOGMEM_STATIC_INIT(os_log_mem, 14, 9, 10);
50
51 extern vm_offset_t kernel_firehose_addr;
52 extern firehose_chunk_t firehose_boot_chunk;
53
54 extern bool bsd_log_lock(bool);
55 extern void bsd_log_unlock(void);
56 extern void logwakeup(struct msgbuf *);
57
58 decl_lck_spin_data(extern, oslog_stream_lock);
59 #define stream_lock() lck_spin_lock(&oslog_stream_lock)
60 #define stream_unlock() lck_spin_unlock(&oslog_stream_lock)
61
62 extern void oslog_streamwakeup(void);
63 void oslog_streamwrite_locked(firehose_tracepoint_id_u ftid,
64 uint64_t stamp, const void *pubdata, size_t publen);
65 extern void oslog_streamwrite_metadata_locked(oslog_stream_buf_entry_t m_entry);
66
67 extern int oslog_stream_open;
68
69 extern void *OSKextKextForAddress(const void *);
70
71 /* Counters for persistence mode */
72 SCALABLE_COUNTER_DEFINE(oslog_p_total_msgcount);
73 SCALABLE_COUNTER_DEFINE(oslog_p_metadata_saved_msgcount);
74 SCALABLE_COUNTER_DEFINE(oslog_p_metadata_dropped_msgcount);
75 SCALABLE_COUNTER_DEFINE(oslog_p_error_count);
76 SCALABLE_COUNTER_DEFINE(oslog_p_saved_msgcount);
77 SCALABLE_COUNTER_DEFINE(oslog_p_dropped_msgcount);
78 SCALABLE_COUNTER_DEFINE(oslog_p_boot_dropped_msgcount);
79 SCALABLE_COUNTER_DEFINE(oslog_p_coprocessor_total_msgcount);
80 SCALABLE_COUNTER_DEFINE(oslog_p_coprocessor_dropped_msgcount);
81 SCALABLE_COUNTER_DEFINE(oslog_p_unresolved_kc_msgcount);
82
83 /* Counters for streaming mode */
84 SCALABLE_COUNTER_DEFINE(oslog_s_error_count);
85 /* Protected by the stream lock */
86 uint32_t oslog_s_total_msgcount;
87 uint32_t oslog_s_metadata_msgcount;
88
89 /* Counters for msgbuf logging */
90 SCALABLE_COUNTER_DEFINE(oslog_msgbuf_msgcount)
91 SCALABLE_COUNTER_DEFINE(oslog_msgbuf_dropped_msgcount)
92
93 static bool oslog_boot_done = false;
94
95 #ifdef XNU_KERNEL_PRIVATE
96 bool startup_serial_logging_active = true;
97 uint64_t startup_serial_num_procs = 300;
98 #endif /* XNU_KERNEL_PRIVATE */
99
100 // XXX
101 firehose_tracepoint_id_t
102 firehose_debug_trace(firehose_stream_t stream, firehose_tracepoint_id_t trace_id,
103 uint64_t timestamp, const char *format, const void *pubdata, size_t publen);
104
105 static inline firehose_tracepoint_id_t
106 _firehose_trace(firehose_stream_t stream, firehose_tracepoint_id_u ftid,
107 uint64_t stamp, const void *pubdata, size_t publen, bool use_streaming);
108
109 static oslog_stream_buf_entry_t
110 oslog_stream_create_buf_entry(oslog_stream_link_type_t type, firehose_tracepoint_id_u ftid,
111 uint64_t stamp, const void* pubdata, size_t publen);
112
113 static void
114 _os_log_with_args_internal(os_log_t oslog __unused, os_log_type_t type __unused,
115 const char *format, va_list args, void *addr, void *dso, bool driverKit, bool addcr);
116
117 static void
118 _os_log_to_msgbuf_internal(const char *format, va_list args, bool safe, bool logging, bool addcr);
119
120 static void
121 _os_log_to_log_internal(os_log_type_t type, const char *format, va_list args, void *addr, void *dso, bool driverKit);
122
123 static bool
124 os_log_turned_off(void)
125 {
126 return atm_get_diagnostic_config() & (ATM_TRACE_DISABLE | ATM_TRACE_OFF);
127 }
128
129 bool
130 os_log_info_enabled(os_log_t log __unused)
131 {
132 return !os_log_turned_off();
133 }
134
135 bool
136 os_log_debug_enabled(os_log_t log __unused)
137 {
138 return !os_log_turned_off();
139 }
140
141 static bool
142 os_log_disabled(void)
143 {
144 return atm_get_diagnostic_config() & ATM_TRACE_DISABLE;
145 }
146
147 os_log_t
148 os_log_create(const char *subsystem __unused, const char *category __unused)
149 {
150 return &_os_log_default;
151 }
152
153 __attribute__((noinline, not_tail_called)) void
154 _os_log_internal(void *dso, os_log_t log, uint8_t type, const char *message, ...)
155 {
156 va_list args;
157 void *addr = __builtin_return_address(0);
158
159 va_start(args, message);
160
161 _os_log_with_args_internal(log, type, message, args, addr, dso, FALSE, FALSE);
162
163 va_end(args);
164
165 return;
166 }
167
168 __attribute__((noinline, not_tail_called)) int
169 _os_log_internal_driverKit(void *dso, os_log_t log, uint8_t type, const char *message, ...)
170 {
171 va_list args;
172 void *addr = __builtin_return_address(0);
173 bool driverKitLog = FALSE;
174
175 /*
176 * We want to be able to identify dexts from the logs.
177 *
178 * Usually the addr is used to understand if the log line
179 * was generated by a kext or the kernel main executable.
180 * Logd uses copyKextUUIDForAddress with the addr specified
181 * in the log line to retrieve the kext UUID of the sender.
182 *
183 * Dext however are not loaded in kernel space so they do not
184 * have a kernel range of addresses.
185 *
186 * To make the same mechanism work, OSKext fakes a kernel
187 * address range for dexts using the loadTag,
188 * so we just need to use the loadTag as addr here
189 * to allow logd to retrieve the correct UUID.
190 *
191 * NOTE: loadTag is populated in the task when the dext is matching,
192 * so if log lines are generated before the matching they will be
193 * identified as kernel main executable.
194 */
195 task_t self_task = current_task();
196
197 /*
198 * Only dextis are supposed to use this log path.
199 */
200 if (!task_is_driver(self_task)) {
201 return EPERM;
202 }
203
204 uint64_t loadTag = get_task_loadTag(self_task);
205 if (loadTag != 0) {
206 driverKitLog = TRUE;
207 addr = (void*) loadTag;
208 }
209 va_start(args, message);
210
211 _os_log_with_args_internal(log, type, message, args, addr, dso, driverKitLog, true);
212
213 va_end(args);
214
215 return 0;
216 }
217
218 #pragma mark - shim functions
219
220 __attribute__((noinline, not_tail_called)) void
221 os_log_with_args(os_log_t oslog, os_log_type_t type, const char *format, va_list args, void *addr)
222 {
223 // if no address passed, look it up
224 if (addr == NULL) {
225 addr = __builtin_return_address(0);
226 }
227
228 _os_log_with_args_internal(oslog, type, format, args, addr, NULL, FALSE, FALSE);
229 }
230
231 static void
232 _os_log_with_args_internal(os_log_t oslog, os_log_type_t type,
233 const char *format, va_list args, void *addr, void *dso, bool driverKit, bool addcr)
234 {
235 if (format[0] == '\0') {
236 return;
237 }
238
239 /* early boot can log to dmesg for later replay (27307943) */
240 bool safe = (startup_phase < STARTUP_SUB_EARLY_BOOT || oslog_is_safe());
241 bool logging = !os_log_turned_off();
242
243 if (oslog != &_os_log_replay) {
244 _os_log_to_msgbuf_internal(format, args, safe, logging, addcr);
245 }
246
247 if (safe && logging) {
248 _os_log_to_log_internal(type, format, args, addr, dso, driverKit);
249 }
250 }
251
252 static void
253 _os_log_to_msgbuf_internal(const char *format, va_list args, bool safe, bool logging, bool addcr)
254 {
255 /*
256 * The following threshold was determined empirically as the point where
257 * it would be more advantageous to be able to fit in more log lines than
258 * to know exactly when a log line was printed out. We don't want to use up
259 * a large percentage of the log buffer on timestamps in a memory-constricted
260 * environment.
261 */
262 const int MSGBUF_TIMESTAMP_THRESHOLD = 4096;
263 static int msgbufreplay = -1;
264 static bool newlogline = true;
265 va_list args_copy;
266
267 if (!bsd_log_lock(safe)) {
268 counter_inc(&oslog_msgbuf_dropped_msgcount);
269 return;
270 }
271
272 if (!safe) {
273 if (-1 == msgbufreplay) {
274 msgbufreplay = msgbufp->msg_bufx;
275 }
276 } else if (logging && (-1 != msgbufreplay)) {
277 uint32_t i;
278 uint32_t localbuff_size;
279 int newl, position;
280 char *localbuff, *p, *s, *next, ch;
281
282 position = msgbufreplay;
283 msgbufreplay = -1;
284 localbuff_size = (msgbufp->msg_size + 2); /* + '\n' + '\0' */
285 /* Size for non-blocking */
286 if (localbuff_size > 4096) {
287 localbuff_size = 4096;
288 }
289 bsd_log_unlock();
290 /* Allocate a temporary non-circular buffer */
291 localbuff = kheap_alloc(KHEAP_TEMP, localbuff_size, Z_NOWAIT);
292 if (localbuff != NULL) {
293 /* in between here, the log could become bigger, but that's fine */
294 bsd_log_lock(true);
295 /*
296 * The message buffer is circular; start at the replay pointer, and
297 * make one loop up to write pointer - 1.
298 */
299 p = msgbufp->msg_bufc + position;
300 for (i = newl = 0; p != msgbufp->msg_bufc + msgbufp->msg_bufx - 1; ++p) {
301 if (p >= msgbufp->msg_bufc + msgbufp->msg_size) {
302 p = msgbufp->msg_bufc;
303 }
304 ch = *p;
305 if (ch == '\0') {
306 continue;
307 }
308 newl = (ch == '\n');
309 localbuff[i++] = ch;
310 if (i >= (localbuff_size - 2)) {
311 break;
312 }
313 }
314 bsd_log_unlock();
315
316 if (!newl) {
317 localbuff[i++] = '\n';
318 }
319 localbuff[i++] = 0;
320
321 s = localbuff;
322 while ((next = strchr(s, '\n'))) {
323 next++;
324 ch = next[0];
325 next[0] = 0;
326 os_log(&_os_log_replay, "%s", s);
327 next[0] = ch;
328 s = next;
329 }
330 kheap_free(KHEAP_TEMP, localbuff, localbuff_size);
331 }
332 bsd_log_lock(true);
333 }
334
335 /* Do not prepend timestamps when we are memory-constricted */
336 if (newlogline && (msgbufp->msg_size > MSGBUF_TIMESTAMP_THRESHOLD)) {
337 clock_sec_t secs;
338 clock_usec_t microsecs;
339 const uint64_t timestamp = firehose_tracepoint_time(firehose_activity_flags_default);
340 absolutetime_to_microtime(timestamp, &secs, &microsecs);
341 printf_log_locked(FALSE, "[%5lu.%06u]: ", (unsigned long)secs, microsecs);
342 }
343
344 va_copy(args_copy, args);
345 newlogline = vprintf_log_locked(format, args_copy, addcr);
346 va_end(args_copy);
347
348 bsd_log_unlock();
349 logwakeup(msgbufp);
350 counter_inc(&oslog_msgbuf_msgcount);
351 }
352
353 static firehose_stream_t
354 firehose_stream(os_log_type_t type)
355 {
356 return (type == OS_LOG_TYPE_INFO || type == OS_LOG_TYPE_DEBUG) ?
357 firehose_stream_memory : firehose_stream_persist;
358 }
359
360 static void
361 _os_log_actual(os_log_type_t type, const char *format, void *dso, void *addr, uint8_t *logdata, size_t logdata_sz,
362 firehose_tracepoint_flags_t flags, bool driverKit)
363 {
364 firehose_tracepoint_id_u trace_id;
365
366 firehose_stream_t stream = firehose_stream(type);
367 uint64_t timestamp = firehose_tracepoint_time(firehose_activity_flags_default);
368
369 if (driverKit) {
370 // set FIREHOSE_TRACEPOINT_PC_DYNAMIC_BIT so logd will not try to find the format string in
371 // the executable text
372 trace_id.ftid_value = FIREHOSE_TRACE_ID_MAKE(firehose_tracepoint_namespace_log,
373 type, flags, (uint32_t)((uintptr_t)addr | FIREHOSE_TRACEPOINT_PC_DYNAMIC_BIT));
374 } else {
375 // create trace_id after we've set additional flags
376 trace_id.ftid_value = FIREHOSE_TRACE_ID_MAKE(firehose_tracepoint_namespace_log,
377 type, flags, _os_trace_offset(dso, format, (_firehose_tracepoint_flags_activity_t)flags));
378 }
379
380
381 _firehose_trace(stream, trace_id, timestamp, logdata, logdata_sz, true);
382 }
383
384 static void *
385 resolve_dso(const char *fmt, void *dso, void *addr, bool driverKit)
386 {
387 kc_format_t kcformat = KCFormatUnknown;
388
389 if (!PE_get_primary_kc_format(&kcformat)) {
390 return NULL;
391 }
392
393 switch (kcformat) {
394 case KCFormatStatic:
395 case KCFormatKCGEN:
396 dso = PE_get_kc_baseaddress(KCKindPrimary);
397 break;
398 case KCFormatDynamic:
399 case KCFormatFileset:
400 if (!dso && (dso = (void *)OSKextKextForAddress(fmt)) == NULL) {
401 return NULL;
402 }
403 if (!_os_trace_addr_in_text_segment(dso, fmt)) {
404 return NULL;
405 }
406 if (!driverKit && (dso != (void *)OSKextKextForAddress(addr))) {
407 return NULL;
408 }
409 break;
410 default:
411 panic("unknown KC format type");
412 }
413
414 return dso;
415 }
416
417 static void
418 _os_log_to_log_internal(os_log_type_t type, const char *fmt, va_list args, void *addr, void *dso, bool driverKit)
419 {
420 counter_inc(&oslog_p_total_msgcount);
421
422 if (addr == NULL) {
423 counter_inc(&oslog_p_unresolved_kc_msgcount);
424 return;
425 }
426
427 if ((dso = resolve_dso(fmt, dso, addr, driverKit)) == NULL) {
428 counter_inc(&oslog_p_unresolved_kc_msgcount);
429 return;
430 }
431
432 uint8_t buffer[OS_LOG_BUFFER_MAX_SIZE] __attribute__((aligned(8))) = { 0 };
433 struct os_log_context_s ctx;
434
435 os_log_context_init(&ctx, &os_log_mem, buffer, sizeof(buffer));
436
437 if (os_log_context_encode(&ctx, fmt, args, addr, dso, driverKit)) {
438 _os_log_actual(type, fmt, dso, addr, ctx.ctx_buffer, ctx.ctx_content_sz,
439 ctx.ctx_ft_flags, driverKit);
440 } else {
441 counter_inc(&oslog_p_error_count);
442 }
443
444 os_log_context_free(&ctx);
445 }
446
447 bool
448 os_log_coprocessor(void *buff, uint64_t buff_len, os_log_type_t type,
449 const char *uuid, uint64_t timestamp, uint32_t offset, bool stream_log)
450 {
451 firehose_tracepoint_id_u trace_id;
452 firehose_tracepoint_id_t return_id = 0;
453 uint8_t pubdata[OS_LOG_BUFFER_MAX_SIZE];
454 size_t wr_pos = 0;
455
456 if (os_log_turned_off()) {
457 return false;
458 }
459
460 if (buff_len + 16 + sizeof(uint32_t) > OS_LOG_BUFFER_MAX_SIZE) {
461 return false;
462 }
463
464 firehose_stream_t stream = firehose_stream(type);
465 // unlike kext, where pc is used to find uuid, in coprocessor logs the uuid is passed as part of the tracepoint
466 firehose_tracepoint_flags_t flags = _firehose_tracepoint_flags_pc_style_uuid_relative;
467
468 memcpy(pubdata, &offset, sizeof(uint32_t));
469 wr_pos += sizeof(uint32_t);
470 memcpy(pubdata + wr_pos, uuid, 16);
471 wr_pos += 16;
472
473 memcpy(pubdata + wr_pos, buff, buff_len);
474
475 // create firehose trace id
476 trace_id.ftid_value = FIREHOSE_TRACE_ID_MAKE(firehose_tracepoint_namespace_log,
477 type, flags, offset);
478
479 counter_inc(&oslog_p_coprocessor_total_msgcount);
480
481 // send firehose tracepoint containing os log to firehose buffer
482 return_id = _firehose_trace(stream, trace_id, timestamp, pubdata,
483 buff_len + wr_pos, stream_log);
484
485 if (return_id == 0) {
486 counter_inc(&oslog_p_coprocessor_dropped_msgcount);
487 return false;
488 }
489 return true;
490 }
491
492 static inline firehose_tracepoint_id_t
493 _firehose_trace(firehose_stream_t stream, firehose_tracepoint_id_u ftid,
494 uint64_t stamp, const void *pubdata, size_t publen, bool use_streaming)
495 {
496 const uint16_t ft_size = offsetof(struct firehose_tracepoint_s, ft_data);
497 const size_t _firehose_chunk_payload_size =
498 sizeof(((struct firehose_chunk_s *)0)->fc_data);
499
500 firehose_tracepoint_t ft;
501
502 if (slowpath(ft_size + publen > _firehose_chunk_payload_size)) {
503 // We'll need to have some handling here. For now - return 0
504 counter_inc(&oslog_p_error_count);
505 return 0;
506 }
507
508 if (oslog_stream_open && (stream != firehose_stream_metadata) && use_streaming) {
509 stream_lock();
510 if (!oslog_stream_open) {
511 stream_unlock();
512 goto out;
513 }
514
515 oslog_s_total_msgcount++;
516 oslog_streamwrite_locked(ftid, stamp, pubdata, publen);
517 stream_unlock();
518 oslog_streamwakeup();
519 }
520
521 out:
522 ft = __firehose_buffer_tracepoint_reserve(stamp, stream, (uint16_t)publen, 0, NULL);
523 if (!fastpath(ft)) {
524 if (oslog_boot_done) {
525 if (stream == firehose_stream_metadata) {
526 counter_inc(&oslog_p_metadata_dropped_msgcount);
527 } else {
528 // If we run out of space in the persistence buffer we're
529 // dropping the message.
530 counter_inc(&oslog_p_dropped_msgcount);
531 }
532 return 0;
533 }
534 firehose_chunk_t fbc = firehose_boot_chunk;
535 long offset;
536
537 //only stream available during boot is persist
538 offset = firehose_chunk_tracepoint_try_reserve(fbc, stamp,
539 firehose_stream_persist, 0, (uint16_t)publen, 0, NULL);
540 if (offset <= 0) {
541 counter_inc(&oslog_p_boot_dropped_msgcount);
542 return 0;
543 }
544
545 ft = firehose_chunk_tracepoint_begin(fbc, stamp, (uint16_t)publen,
546 thread_tid(current_thread()), offset);
547 memcpy(ft->ft_data, pubdata, publen);
548 firehose_chunk_tracepoint_end(fbc, ft, ftid);
549 counter_inc(&oslog_p_saved_msgcount);
550 return ftid.ftid_value;
551 }
552 if (!oslog_boot_done) {
553 oslog_boot_done = true;
554 }
555 memcpy(ft->ft_data, pubdata, publen);
556
557 __firehose_buffer_tracepoint_flush(ft, ftid);
558 if (stream == firehose_stream_metadata) {
559 counter_inc(&oslog_p_metadata_saved_msgcount);
560 } else {
561 counter_inc(&oslog_p_saved_msgcount);
562 }
563 return ftid.ftid_value;
564 }
565
566 static oslog_stream_buf_entry_t
567 oslog_stream_create_buf_entry(oslog_stream_link_type_t type,
568 firehose_tracepoint_id_u ftid, uint64_t stamp,
569 const void* pubdata, size_t publen)
570 {
571 const size_t ft_size = sizeof(struct firehose_tracepoint_s) + publen;
572 const size_t m_entry_len = sizeof(struct oslog_stream_buf_entry_s) + ft_size;
573 oslog_stream_buf_entry_t m_entry;
574
575 if (!pubdata || publen > UINT16_MAX || ft_size > UINT16_MAX) {
576 return NULL;
577 }
578
579 m_entry = kheap_alloc(KHEAP_DEFAULT, m_entry_len, Z_WAITOK);
580 if (!m_entry) {
581 return NULL;
582 }
583 m_entry->type = type;
584 m_entry->timestamp = stamp;
585 m_entry->size = (uint16_t)ft_size;
586
587 firehose_tracepoint_t ft = m_entry->metadata;
588 ft->ft_thread = thread_tid(current_thread());
589 ft->ft_id.ftid_value = ftid.ftid_value;
590 ft->ft_length = (uint16_t)publen;
591 memcpy(ft->ft_data, pubdata, publen);
592
593 return m_entry;
594 }
595
596 void
597 os_log_coprocessor_register(const char *uuid, const char *file_path, bool copy)
598 {
599 uint64_t stamp;
600 size_t path_size = strlen(file_path) + 1;
601 firehose_tracepoint_id_u trace_id;
602 size_t uuid_info_len = sizeof(struct firehose_trace_uuid_info_s) + path_size;
603 union {
604 struct firehose_trace_uuid_info_s uuid_info;
605 char path[PATH_MAX + sizeof(struct firehose_trace_uuid_info_s)];
606 } buf;
607
608 if (os_log_disabled()) {
609 return;
610 }
611
612 if (path_size > PATH_MAX) {
613 return;
614 }
615
616 // write metadata to uuid_info
617 memcpy(buf.uuid_info.ftui_uuid, uuid, sizeof(uuid_t));
618 buf.uuid_info.ftui_size = 1;
619 buf.uuid_info.ftui_address = 1;
620
621 stamp = firehose_tracepoint_time(firehose_activity_flags_default);
622
623 // create tracepoint id
624 trace_id.ftid_value = FIREHOSE_TRACE_ID_MAKE(firehose_tracepoint_namespace_metadata, _firehose_tracepoint_type_metadata_coprocessor,
625 (firehose_tracepoint_flags_t)0, copy ? firehose_tracepoint_code_load_memory : firehose_tracepoint_code_load_filesystem);
626
627 // write path to buffer
628 memcpy(buf.uuid_info.ftui_path, file_path, path_size);
629
630 // send metadata tracepoint to firehose for coprocessor registration in logd
631 firehose_trace_metadata(firehose_stream_metadata, trace_id, stamp, (void *)&buf, uuid_info_len);
632 return;
633 }
634
635 #ifdef KERNEL
636 void
637 firehose_trace_metadata(firehose_stream_t stream, firehose_tracepoint_id_u ftid,
638 uint64_t stamp, const void *pubdata, size_t publen)
639 {
640 oslog_stream_buf_entry_t m_entry = NULL;
641
642 if (os_log_disabled()) {
643 return;
644 }
645
646 // If streaming mode is not on, only log the metadata
647 // in the persistence buffer
648
649 stream_lock();
650 if (!oslog_stream_open) {
651 stream_unlock();
652 goto finish;
653 }
654 stream_unlock();
655
656 // Setup and write the stream metadata entry
657 m_entry = oslog_stream_create_buf_entry(oslog_stream_link_type_metadata, ftid,
658 stamp, pubdata, publen);
659 if (!m_entry) {
660 counter_inc(&oslog_s_error_count);
661 goto finish;
662 }
663
664 stream_lock();
665 if (!oslog_stream_open) {
666 stream_unlock();
667 kheap_free(KHEAP_DEFAULT, m_entry,
668 sizeof(struct oslog_stream_buf_entry_s) +
669 sizeof(struct firehose_tracepoint_s) + publen);
670 goto finish;
671 }
672 oslog_s_metadata_msgcount++;
673 oslog_streamwrite_metadata_locked(m_entry);
674 stream_unlock();
675
676 finish:
677 _firehose_trace(stream, ftid, stamp, pubdata, publen, true);
678 }
679 #endif
680
681 void
682 __firehose_buffer_push_to_logd(firehose_buffer_t fb __unused, bool for_io __unused)
683 {
684 oslogwakeup();
685 return;
686 }
687
688 void
689 __firehose_allocate(vm_offset_t *addr, vm_size_t size __unused)
690 {
691 firehose_chunk_t kernel_buffer = (firehose_chunk_t)kernel_firehose_addr;
692
693 if (kernel_firehose_addr) {
694 *addr = kernel_firehose_addr;
695 } else {
696 *addr = 0;
697 return;
698 }
699 // Now that we are done adding logs to this chunk, set the number of writers to 0
700 // Without this, logd won't flush when the page is full
701 firehose_boot_chunk->fc_pos.fcp_refcnt = 0;
702 memcpy(&kernel_buffer[FIREHOSE_BUFFER_KERNEL_CHUNK_COUNT - 1], (const void *)firehose_boot_chunk, FIREHOSE_CHUNK_SIZE);
703 return;
704 }
705 // There isnt a lock held in this case.
706 void
707 __firehose_critical_region_enter(void)
708 {
709 disable_preemption();
710 return;
711 }
712
713 void
714 __firehose_critical_region_leave(void)
715 {
716 enable_preemption();
717 return;
718 }
719
720 #ifdef CONFIG_XNUPOST
721
722 #include <tests/xnupost.h>
723 #define TESTOSLOGFMT(fn_name) "%u^%llu/%llu^kernel^0^test^" fn_name
724 #define TESTOSLOGPFX "TESTLOG:%u#"
725 #define TESTOSLOG(fn_name) TESTOSLOGPFX TESTOSLOGFMT(fn_name "#")
726
727 extern u_int32_t RandomULong(void);
728 extern size_t find_pattern_in_buffer(const char *pattern, size_t len, size_t expected_count);
729 void test_oslog_default_helper(uint32_t uniqid, uint64_t count);
730 void test_oslog_info_helper(uint32_t uniqid, uint64_t count);
731 void test_oslog_debug_helper(uint32_t uniqid, uint64_t count);
732 void test_oslog_error_helper(uint32_t uniqid, uint64_t count);
733 void test_oslog_fault_helper(uint32_t uniqid, uint64_t count);
734 void _test_log_loop(void * arg __unused, wait_result_t wres __unused);
735 void test_oslog_handleOSLogCtl(int32_t * in, int32_t * out, int32_t len);
736 kern_return_t test_stresslog_dropmsg(uint32_t uniqid);
737
738 kern_return_t test_os_log(void);
739 kern_return_t test_os_log_parallel(void);
740
741 #define GENOSLOGHELPER(fname, ident, callout_f) \
742 void fname(uint32_t uniqid, uint64_t count) \
743 { \
744 int32_t datalen = 0; \
745 uint32_t checksum = 0; \
746 char databuffer[256]; \
747 T_LOG("Doing os_log of %llu TESTLOG msgs for fn " ident, count); \
748 for (uint64_t i = 0; i < count; i++) \
749 { \
750 datalen = scnprintf(databuffer, sizeof(databuffer), TESTOSLOGFMT(ident), uniqid, i + 1, count); \
751 checksum = crc32(0, databuffer, datalen); \
752 callout_f(OS_LOG_DEFAULT, TESTOSLOG(ident), checksum, uniqid, i + 1, count); \
753 /*T_LOG(TESTOSLOG(ident), checksum, uniqid, i + 1, count);*/ \
754 } \
755 }
756
757 GENOSLOGHELPER(test_oslog_info_helper, "oslog_info_helper", os_log_info);
758 GENOSLOGHELPER(test_oslog_fault_helper, "oslog_fault_helper", os_log_fault);
759 GENOSLOGHELPER(test_oslog_debug_helper, "oslog_debug_helper", os_log_debug);
760 GENOSLOGHELPER(test_oslog_error_helper, "oslog_error_helper", os_log_error);
761 GENOSLOGHELPER(test_oslog_default_helper, "oslog_default_helper", os_log);
762
763 kern_return_t
764 test_os_log()
765 {
766 char databuffer[256];
767 uint32_t uniqid = RandomULong();
768 size_t match_count = 0;
769 uint32_t checksum = 0;
770 uint32_t total_msg = 0;
771 uint32_t saved_msg = 0;
772 uint32_t dropped_msg = 0;
773 size_t datalen = 0;
774 uint64_t a = mach_absolute_time();
775 uint64_t seqno = 1;
776 uint64_t total_seqno = 2;
777
778 os_log_t log_handle = os_log_create("com.apple.xnu.test.t1", "kpost");
779
780 T_ASSERT_EQ_PTR(&_os_log_default, log_handle, "os_log_create returns valid value.");
781 T_ASSERT_EQ_INT(TRUE, os_log_info_enabled(log_handle), "os_log_info is enabled");
782 T_ASSERT_EQ_INT(TRUE, os_log_debug_enabled(log_handle), "os_log_debug is enabled");
783 T_ASSERT_EQ_PTR(&_os_log_default, OS_LOG_DEFAULT, "ensure OS_LOG_DEFAULT is _os_log_default");
784
785 total_msg = counter_load(&oslog_p_total_msgcount);
786 saved_msg = counter_load(&oslog_p_saved_msgcount);
787 dropped_msg = counter_load(&oslog_p_dropped_msgcount);
788 T_LOG("oslog internal counters total %u , saved %u, dropped %u", total_msg, saved_msg, dropped_msg);
789
790 T_LOG("Validating with uniqid %u u64 %llu", uniqid, a);
791 T_ASSERT_NE_UINT(0, uniqid, "random number should not be zero");
792 T_ASSERT_NE_ULLONG(0, a, "absolute time should not be zero");
793
794 datalen = scnprintf(databuffer, sizeof(databuffer), TESTOSLOGFMT("printf_only"), uniqid, seqno, total_seqno);
795 checksum = crc32(0, databuffer, datalen);
796 printf(TESTOSLOG("printf_only") "mat%llu\n", checksum, uniqid, seqno, total_seqno, a);
797
798 seqno += 1;
799 datalen = scnprintf(databuffer, sizeof(databuffer), TESTOSLOGFMT("printf_only"), uniqid, seqno, total_seqno);
800 checksum = crc32(0, databuffer, datalen);
801 printf(TESTOSLOG("printf_only") "mat%llu\n", checksum, uniqid, seqno, total_seqno, a);
802
803 datalen = scnprintf(databuffer, sizeof(databuffer), "kernel^0^test^printf_only#mat%llu", a);
804 match_count = find_pattern_in_buffer(databuffer, datalen, total_seqno);
805 T_EXPECT_EQ_ULONG(match_count, total_seqno, "verify printf_only goes to systemlog buffer");
806
807 uint32_t logging_config = atm_get_diagnostic_config();
808 T_LOG("checking atm_diagnostic_config 0x%X", logging_config);
809
810 if ((logging_config & ATM_TRACE_OFF) || (logging_config & ATM_TRACE_DISABLE)) {
811 T_LOG("ATM_TRACE_OFF / ATM_TRACE_DISABLE is set. Would not see oslog messages. skipping the rest of test.");
812 return KERN_SUCCESS;
813 }
814
815 /* for enabled logging printfs should be saved in oslog as well */
816 T_EXPECT_GE_UINT((counter_load(&oslog_p_total_msgcount) - total_msg), 2, "atleast 2 msgs should be seen by oslog system");
817
818 a = mach_absolute_time();
819 total_seqno = 1;
820 seqno = 1;
821 total_msg = counter_load(&oslog_p_total_msgcount);
822 saved_msg = counter_load(&oslog_p_saved_msgcount);
823 dropped_msg = counter_load(&oslog_p_dropped_msgcount);
824 datalen = scnprintf(databuffer, sizeof(databuffer), TESTOSLOGFMT("oslog_info"), uniqid, seqno, total_seqno);
825 checksum = crc32(0, databuffer, datalen);
826 os_log_info(log_handle, TESTOSLOG("oslog_info") "mat%llu", checksum, uniqid, seqno, total_seqno, a);
827 T_EXPECT_GE_UINT((counter_load(&oslog_p_total_msgcount) - total_msg), 1, "total message count in buffer");
828
829 datalen = scnprintf(databuffer, sizeof(databuffer), "kernel^0^test^oslog_info#mat%llu", a);
830 match_count = find_pattern_in_buffer(databuffer, datalen, total_seqno);
831 T_EXPECT_EQ_ULONG(match_count, total_seqno, "verify oslog_info does not go to systemlog buffer");
832
833 total_msg = counter_load(&oslog_p_total_msgcount);
834 test_oslog_info_helper(uniqid, 10);
835 T_EXPECT_GE_UINT(counter_load(&oslog_p_total_msgcount) - total_msg, 10, "test_oslog_info_helper: Should have seen 10 msgs");
836
837 total_msg = counter_load(&oslog_p_total_msgcount);
838 test_oslog_debug_helper(uniqid, 10);
839 T_EXPECT_GE_UINT(counter_load(&oslog_p_total_msgcount) - total_msg, 10, "test_oslog_debug_helper:Should have seen 10 msgs");
840
841 total_msg = counter_load(&oslog_p_total_msgcount);
842 test_oslog_error_helper(uniqid, 10);
843 T_EXPECT_GE_UINT(counter_load(&oslog_p_total_msgcount) - total_msg, 10, "test_oslog_error_helper:Should have seen 10 msgs");
844
845 total_msg = counter_load(&oslog_p_total_msgcount);
846 test_oslog_default_helper(uniqid, 10);
847 T_EXPECT_GE_UINT(counter_load(&oslog_p_total_msgcount) - total_msg, 10, "test_oslog_default_helper:Should have seen 10 msgs");
848
849 total_msg = counter_load(&oslog_p_total_msgcount);
850 test_oslog_fault_helper(uniqid, 10);
851 T_EXPECT_GE_UINT(counter_load(&oslog_p_total_msgcount) - total_msg, 10, "test_oslog_fault_helper:Should have seen 10 msgs");
852
853 T_LOG("oslog internal counters total %u , saved %u, dropped %u", counter_load(&oslog_p_total_msgcount), counter_load(&oslog_p_saved_msgcount),
854 counter_load(&oslog_p_dropped_msgcount));
855
856 return KERN_SUCCESS;
857 }
858
859 static uint32_t _test_log_loop_count = 0;
860 void
861 _test_log_loop(void * arg __unused, wait_result_t wres __unused)
862 {
863 uint32_t uniqid = RandomULong();
864 test_oslog_debug_helper(uniqid, 100);
865 os_atomic_add(&_test_log_loop_count, 100, relaxed);
866 }
867
868 kern_return_t
869 test_os_log_parallel(void)
870 {
871 thread_t thread[2];
872 kern_return_t kr;
873 uint32_t uniqid = RandomULong();
874
875 printf("oslog internal counters total %lld , saved %lld, dropped %lld", counter_load(&oslog_p_total_msgcount), counter_load(&oslog_p_saved_msgcount),
876 counter_load(&oslog_p_dropped_msgcount));
877
878 kr = kernel_thread_start(_test_log_loop, NULL, &thread[0]);
879 T_ASSERT_EQ_INT(kr, KERN_SUCCESS, "kernel_thread_start returned successfully");
880
881 kr = kernel_thread_start(_test_log_loop, NULL, &thread[1]);
882 T_ASSERT_EQ_INT(kr, KERN_SUCCESS, "kernel_thread_start returned successfully");
883
884 test_oslog_info_helper(uniqid, 100);
885
886 /* wait until other thread has also finished */
887 while (_test_log_loop_count < 200) {
888 delay(1000);
889 }
890
891 thread_deallocate(thread[0]);
892 thread_deallocate(thread[1]);
893
894 T_LOG("oslog internal counters total %lld , saved %lld, dropped %lld", counter_load(&oslog_p_total_msgcount), counter_load(&oslog_p_saved_msgcount),
895 counter_load(&oslog_p_dropped_msgcount));
896 T_PASS("parallel_logging tests is now complete");
897
898 return KERN_SUCCESS;
899 }
900
901 void
902 test_oslog_handleOSLogCtl(int32_t * in, int32_t * out, int32_t len)
903 {
904 if (!in || !out || len != 4) {
905 return;
906 }
907 switch (in[0]) {
908 case 1:
909 {
910 /* send out counters */
911 out[1] = counter_load(&oslog_p_total_msgcount);
912 out[2] = counter_load(&oslog_p_saved_msgcount);
913 out[3] = counter_load(&oslog_p_dropped_msgcount);
914 out[0] = KERN_SUCCESS;
915 break;
916 }
917 case 2:
918 {
919 /* mini stress run */
920 out[0] = test_os_log_parallel();
921 break;
922 }
923 case 3:
924 {
925 /* drop msg tests */
926 out[1] = RandomULong();
927 out[0] = test_stresslog_dropmsg(out[1]);
928 break;
929 }
930 case 4:
931 {
932 /* invoke log helpers */
933 uint32_t uniqid = in[3];
934 int32_t msgcount = in[2];
935 if (uniqid == 0 || msgcount == 0) {
936 out[0] = KERN_INVALID_VALUE;
937 return;
938 }
939
940 switch (in[1]) {
941 case OS_LOG_TYPE_INFO: test_oslog_info_helper(uniqid, msgcount); break;
942 case OS_LOG_TYPE_DEBUG: test_oslog_debug_helper(uniqid, msgcount); break;
943 case OS_LOG_TYPE_ERROR: test_oslog_error_helper(uniqid, msgcount); break;
944 case OS_LOG_TYPE_FAULT: test_oslog_fault_helper(uniqid, msgcount); break;
945 case OS_LOG_TYPE_DEFAULT:
946 default: test_oslog_default_helper(uniqid, msgcount); break;
947 }
948 out[0] = KERN_SUCCESS;
949 break;
950 /* end of case 4 */
951 }
952 default:
953 {
954 out[0] = KERN_INVALID_VALUE;
955 break;
956 }
957 }
958 return;
959 }
960
961 kern_return_t
962 test_stresslog_dropmsg(uint32_t uniqid)
963 {
964 uint32_t total, saved, dropped;
965 total = counter_load(&oslog_p_total_msgcount);
966 saved = counter_load(&oslog_p_saved_msgcount);
967 dropped = counter_load(&oslog_p_dropped_msgcount);
968 uniqid = RandomULong();
969 test_oslog_debug_helper(uniqid, 100);
970 while ((counter_load(&oslog_p_dropped_msgcount) - dropped) == 0) {
971 test_oslog_debug_helper(uniqid, 100);
972 }
973 printf("test_stresslog_dropmsg: logged %lld msgs, saved %lld and caused a drop of %lld msgs. \n", counter_load(&oslog_p_total_msgcount) - total,
974 counter_load(&oslog_p_saved_msgcount) - saved, counter_load(&oslog_p_dropped_msgcount) - dropped);
975 return KERN_SUCCESS;
976 }
977
978 #endif