]> git.saurik.com Git - apple/xnu.git/blame - libkern/os/log.c
xnu-7195.81.3.tar.gz
[apple/xnu.git] / libkern / os / log.c
CommitLineData
cb323159
A
1/* * Copyright (c) 2019 Apple Inc. All rights reserved. */
2
39037602
A
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>
f427ee49 28#include <kern/startup.h>
cb323159 29#include <kern/task.h>
39037602
A
30
31#include <firehose/tracepoint_private.h>
813fb2f6 32#include <firehose/chunk_private.h>
39037602
A
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
41struct os_log_s {
42 int a;
43};
44
45struct os_log_s _os_log_default;
46struct os_log_s _os_log_replay;
47extern vm_offset_t kernel_firehose_addr;
813fb2f6 48extern firehose_chunk_t firehose_boot_chunk;
39037602 49
f427ee49 50extern bool bsd_log_lock(bool);
39037602 51extern void bsd_log_unlock(void);
cc8bc92a 52extern void logwakeup(struct msgbuf *);
39037602 53
cb323159 54decl_lck_spin_data(extern, oslog_stream_lock);
0a7de745
A
55#define stream_lock() lck_spin_lock(&oslog_stream_lock)
56#define stream_unlock() lck_spin_unlock(&oslog_stream_lock)
57
39037602
A
58extern void oslog_streamwakeup(void);
59void oslog_streamwrite_locked(firehose_tracepoint_id_u ftid,
0a7de745 60 uint64_t stamp, const void *pubdata, size_t publen);
39037602
A
61extern void oslog_streamwrite_metadata_locked(oslog_stream_buf_entry_t m_entry);
62
63extern int oslog_stream_open;
64
65extern void *OSKextKextForAddress(const void *);
66
67/* Counters for persistence mode */
68uint32_t oslog_p_total_msgcount = 0;
69uint32_t oslog_p_metadata_saved_msgcount = 0;
70uint32_t oslog_p_metadata_dropped_msgcount = 0;
71uint32_t oslog_p_error_count = 0;
72uint32_t oslog_p_saved_msgcount = 0;
73uint32_t oslog_p_dropped_msgcount = 0;
74uint32_t oslog_p_boot_dropped_msgcount = 0;
f427ee49
A
75uint32_t oslog_p_coprocessor_total_msgcount = 0;
76uint32_t oslog_p_coprocessor_dropped_msgcount = 0;
39037602
A
77
78/* Counters for streaming mode */
79uint32_t oslog_s_total_msgcount = 0;
80uint32_t oslog_s_error_count = 0;
81uint32_t oslog_s_metadata_msgcount = 0;
82
f427ee49
A
83/* Counters for msgbuf logging */
84uint32_t oslog_msgbuf_msgcount = 0;
85uint32_t oslog_msgbuf_dropped_msgcount = 0;
86
39037602 87static bool oslog_boot_done = false;
5ba3f43e
A
88
89#ifdef XNU_KERNEL_PRIVATE
90bool startup_serial_logging_active = true;
91uint64_t startup_serial_num_procs = 300;
92#endif /* XNU_KERNEL_PRIVATE */
39037602
A
93
94// XXX
95firehose_tracepoint_id_t
96firehose_debug_trace(firehose_stream_t stream, firehose_tracepoint_id_t trace_id,
0a7de745 97 uint64_t timestamp, const char *format, const void *pubdata, size_t publen);
39037602
A
98
99static inline firehose_tracepoint_id_t
100_firehose_trace(firehose_stream_t stream, firehose_tracepoint_id_u ftid,
f427ee49 101 uint64_t stamp, const void *pubdata, size_t publen, bool use_streaming);
39037602
A
102
103static oslog_stream_buf_entry_t
104oslog_stream_create_buf_entry(oslog_stream_link_type_t type, firehose_tracepoint_id_u ftid,
0a7de745 105 uint64_t stamp, const void* pubdata, size_t publen);
39037602
A
106
107static void
108_os_log_with_args_internal(os_log_t oslog __unused, os_log_type_t type __unused,
cb323159 109 const char *format, va_list args, void *addr, void *dso, bool driverKit, bool addcr);
39037602
A
110
111static void
cb323159 112_os_log_to_msgbuf_internal(const char *format, va_list args, bool safe, bool logging, bool addcr);
39037602
A
113
114static void
115_os_log_to_log_internal(os_log_t oslog, os_log_type_t type,
cb323159 116 const char *format, va_list args, void *addr, void *dso, bool driverKit);
39037602
A
117
118
119static void
120_os_log_actual(os_log_t oslog, os_log_type_t type, const char *format, void
cb323159 121 *dso, void *addr, os_log_buffer_context_t context, bool driverKit);
39037602
A
122
123bool
124os_log_info_enabled(os_log_t log __unused)
125{
126 return true;
127}
128
129bool
130os_log_debug_enabled(os_log_t log __unused)
131{
132 return true;
133}
134
135os_log_t
136os_log_create(const char *subsystem __unused, const char *category __unused)
137{
138 return &_os_log_default;
139}
140
141bool
142_os_log_string_is_public(const char *str __unused)
143{
144 return true;
145}
146
0a7de745 147__attribute__((noinline, not_tail_called)) void
39037602
A
148_os_log_internal(void *dso, os_log_t log, uint8_t type, const char *message, ...)
149{
0a7de745
A
150 va_list args;
151 void *addr = __builtin_return_address(0);
39037602 152
0a7de745 153 va_start(args, message);
39037602 154
cb323159 155 _os_log_with_args_internal(log, type, message, args, addr, dso, FALSE, FALSE);
39037602 156
0a7de745 157 va_end(args);
39037602 158
0a7de745 159 return;
39037602
A
160}
161
cb323159
A
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
39037602
A
212#pragma mark - shim functions
213
0a7de745 214__attribute__((noinline, not_tail_called)) void
39037602
A
215os_log_with_args(os_log_t oslog, os_log_type_t type, const char *format, va_list args, void *addr)
216{
0a7de745
A
217 // if no address passed, look it up
218 if (addr == NULL) {
219 addr = __builtin_return_address(0);
220 }
39037602 221
cb323159 222 _os_log_with_args_internal(oslog, type, format, args, addr, NULL, FALSE, FALSE);
39037602
A
223}
224
225static void
226_os_log_with_args_internal(os_log_t oslog, os_log_type_t type,
cb323159 227 const char *format, va_list args, void *addr, void *dso, bool driverKit, bool addcr)
39037602 228{
0a7de745
A
229 uint32_t logging_config = atm_get_diagnostic_config();
230 boolean_t safe;
231 boolean_t logging;
39037602 232
0a7de745
A
233 if (format[0] == '\0') {
234 return;
235 }
5ba3f43e 236
0a7de745 237 /* early boot can log to dmesg for later replay (27307943) */
f427ee49 238 safe = (startup_phase < STARTUP_SUB_EARLY_BOOT || oslog_is_safe());
5ba3f43e
A
239
240 if (logging_config & ATM_TRACE_DISABLE || logging_config & ATM_TRACE_OFF) {
241 logging = false;
242 } else {
243 logging = true;
244 }
39037602 245
0a7de745 246 if (oslog != &_os_log_replay) {
cb323159 247 _os_log_to_msgbuf_internal(format, args, safe, logging, addcr);
0a7de745 248 }
39037602 249
0a7de745 250 if (safe && logging) {
cb323159 251 _os_log_to_log_internal(oslog, type, format, args, addr, dso, driverKit);
0a7de745 252 }
39037602
A
253}
254
255static void
cb323159 256_os_log_to_msgbuf_internal(const char *format, va_list args, bool safe, bool logging, bool addcr)
39037602 257{
f427ee49
A
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;
0a7de745 266 static int msgbufreplay = -1;
f427ee49 267 static bool newlogline = true;
0a7de745 268 va_list args_copy;
39037602 269
f427ee49
A
270 if (!bsd_log_lock(safe)) {
271 os_atomic_inc(&oslog_msgbuf_dropped_msgcount, relaxed);
272 return;
0a7de745 273 }
39037602 274
0a7de745
A
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 */
f427ee49
A
294 localbuff = kheap_alloc(KHEAP_TEMP, localbuff_size, Z_NOWAIT);
295 if (localbuff != NULL) {
0a7de745 296 /* in between here, the log could become bigger, but that's fine */
f427ee49 297 bsd_log_lock(true);
0a7de745
A
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 }
f427ee49 333 kheap_free(KHEAP_TEMP, localbuff, localbuff_size);
0a7de745 334 }
f427ee49
A
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);
0a7de745 345 }
39037602 346
0a7de745 347 va_copy(args_copy, args);
f427ee49 348 newlogline = vprintf_log_locked(format, args_copy, addcr);
0a7de745 349 va_end(args_copy);
39037602 350
0a7de745 351 bsd_log_unlock();
f427ee49
A
352 logwakeup(msgbufp);
353 os_atomic_inc(&oslog_msgbuf_msgcount, relaxed);
39037602
A
354}
355
356static void
357_os_log_to_log_internal(os_log_t oslog, os_log_type_t type,
cb323159 358 const char *format, va_list args, void *addr, void *dso, bool driverKit)
39037602 359{
f427ee49 360 kc_format_t kcformat = KCFormatUnknown;
0a7de745
A
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 }
d9a64523 370
f427ee49 371 if (!PE_get_primary_kc_format(&kcformat)) {
0a7de745
A
372 return;
373 }
f427ee49
A
374
375 if (kcformat == KCFormatStatic || kcformat == KCFormatKCGEN) {
376 void *baseAddress = PE_get_kc_baseaddress(KCKindPrimary);
377 if (!baseAddress) {
cb323159
A
378 return;
379 }
f427ee49
A
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 }
0a7de745 397 }
39037602 398
0a7de745
A
399 memset(&context, 0, sizeof(context));
400 memset(buffer, 0, OS_LOG_BUFFER_MAX_SIZE);
39037602 401
0a7de745
A
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);
39037602 407
0a7de745 408 va_copy(args_copy, args);
39037602 409
cb323159 410 os_atomic_inc(&oslog_p_total_msgcount, relaxed);
0a7de745 411 if (_os_log_encode(format, args_copy, 0, &context)) {
cb323159 412 _os_log_actual(oslog, type, format, dso, addr, &context, driverKit);
0a7de745 413 } else {
cb323159 414 os_atomic_inc(&oslog_p_error_count, relaxed);
0a7de745 415 }
39037602 416
0a7de745 417 va_end(args_copy);
39037602
A
418}
419
813fb2f6
A
420static inline size_t
421_os_trace_write_location_for_address(uint8_t buf[static sizeof(uint64_t)],
cb323159 422 void *dso, const void *address, firehose_tracepoint_flags_t *flags, __unused bool driverKit)
39037602 423{
f427ee49 424 uintptr_t shift_addr = (uintptr_t)address - (uintptr_t)dso;
d9a64523 425
f427ee49
A
426 kc_format_t kcformat = KCFormatUnknown;
427 __assert_only bool result = PE_get_primary_kc_format(&kcformat);
428 assert(result);
39037602 429
f427ee49
A
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));
813fb2f6 433 return sizeof(uint32_t);
39037602 434 } else {
f427ee49
A
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);
cb323159 447 } else {
f427ee49
A
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));
813fb2f6 455#if __LP64__
f427ee49 456 return 6; // 48 bits are enough
813fb2f6 457#else
f427ee49 458 return sizeof(uintptr_t);
813fb2f6 459#endif
f427ee49 460 }
39037602
A
461 }
462}
463
464
465OS_ALWAYS_INLINE
813fb2f6
A
466static inline size_t
467_os_log_buffer_pack(uint8_t *buffdata, size_t buffdata_sz,
0a7de745 468 os_log_buffer_context_t ctx)
39037602 469{
813fb2f6
A
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;
39037602 473
813fb2f6
A
474 if (total_sz > buffdata_sz) {
475 return 0;
476 }
39037602 477
813fb2f6
A
478 memcpy(buffdata, buffer, buffer_sz);
479 memcpy(&buffdata[buffer_sz], ctx->pubdata, ctx->pubdata_sz);
480 return total_sz;
39037602
A
481}
482
483static void
484_os_log_actual(os_log_t oslog __unused, os_log_type_t type, const char *format,
cb323159 485 void *dso, void *addr, os_log_buffer_context_t context, bool driverKit)
39037602
A
486{
487 firehose_stream_t stream;
488 firehose_tracepoint_flags_t flags = 0;
489 firehose_tracepoint_id_u trace_id;
39037602 490 uint8_t buffdata[OS_LOG_BUFFER_MAX_SIZE];
813fb2f6 491 size_t addr_len = 0, buffdata_sz;
39037602
A
492 uint64_t timestamp;
493 uint64_t thread_id;
494
39037602 495 // dso == the start of the binary that was loaded
cb323159 496 addr_len = _os_trace_write_location_for_address(buffdata, dso, addr, &flags, driverKit);
813fb2f6 497 buffdata_sz = _os_log_buffer_pack(buffdata + addr_len,
0a7de745 498 sizeof(buffdata) - addr_len, context);
813fb2f6
A
499 if (buffdata_sz == 0) {
500 return;
501 }
502 buffdata_sz += addr_len;
39037602
A
503
504 timestamp = firehose_tracepoint_time(firehose_activity_flags_default);
505 thread_id = thread_tid(current_thread());
506
cb323159
A
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,
f427ee49 511 type, flags, (uint32_t)((uintptr_t)addr | FIREHOSE_TRACEPOINT_PC_DYNAMIC_BIT));
cb323159
A
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 }
39037602 517
f427ee49
A
518 if (type == OS_LOG_TYPE_INFO || type == OS_LOG_TYPE_DEBUG) {
519 stream = firehose_stream_memory;
520 } else {
521 stream = firehose_stream_persist;
39037602 522 }
f427ee49
A
523 _firehose_trace(stream, trace_id, timestamp, buffdata, buffdata_sz, true);
524}
525
526bool
527os_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
39037602
A
554 if (type == OS_LOG_TYPE_INFO || type == OS_LOG_TYPE_DEBUG) {
555 stream = firehose_stream_memory;
0a7de745 556 } else {
39037602
A
557 stream = firehose_stream_persist;
558 }
f427ee49
A
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;
39037602
A
571}
572
573static inline firehose_tracepoint_id_t
574_firehose_trace(firehose_stream_t stream, firehose_tracepoint_id_u ftid,
f427ee49 575 uint64_t stamp, const void *pubdata, size_t publen, bool use_streaming)
39037602
A
576{
577 const uint16_t ft_size = offsetof(struct firehose_tracepoint_s, ft_data);
578 const size_t _firehose_chunk_payload_size =
0a7de745 579 sizeof(((struct firehose_chunk_s *)0)->fc_data);
39037602
A
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
cb323159 585 os_atomic_inc(&oslog_p_error_count, relaxed);
39037602
A
586 return 0;
587 }
588
f427ee49 589 if (oslog_stream_open && (stream != firehose_stream_metadata) && use_streaming) {
0a7de745 590 stream_lock();
39037602 591 if (!oslog_stream_open) {
0a7de745 592 stream_unlock();
39037602
A
593 goto out;
594 }
595
596 oslog_s_total_msgcount++;
597 oslog_streamwrite_locked(ftid, stamp, pubdata, publen);
0a7de745 598 stream_unlock();
39037602
A
599 oslog_streamwakeup();
600 }
601
602out:
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) {
cb323159 607 os_atomic_inc(&oslog_p_metadata_dropped_msgcount, relaxed);
0a7de745 608 } else {
39037602
A
609 // If we run out of space in the persistence buffer we're
610 // dropping the message.
cb323159 611 os_atomic_inc(&oslog_p_dropped_msgcount, relaxed);
39037602
A
612 }
613 return 0;
614 }
813fb2f6
A
615 firehose_chunk_t fbc = firehose_boot_chunk;
616 long offset;
39037602
A
617
618 //only stream available during boot is persist
813fb2f6 619 offset = firehose_chunk_tracepoint_try_reserve(fbc, stamp,
f427ee49 620 firehose_stream_persist, 0, (uint16_t)publen, 0, NULL);
813fb2f6 621 if (offset <= 0) {
cb323159 622 os_atomic_inc(&oslog_p_boot_dropped_msgcount, relaxed);
39037602
A
623 return 0;
624 }
813fb2f6 625
f427ee49 626 ft = firehose_chunk_tracepoint_begin(fbc, stamp, (uint16_t)publen,
0a7de745 627 thread_tid(current_thread()), offset);
813fb2f6
A
628 memcpy(ft->ft_data, pubdata, publen);
629 firehose_chunk_tracepoint_end(fbc, ft, ftid);
cb323159 630 os_atomic_inc(&oslog_p_saved_msgcount, relaxed);
813fb2f6 631 return ftid.ftid_value;
39037602
A
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) {
cb323159 640 os_atomic_inc(&oslog_p_metadata_saved_msgcount, relaxed);
0a7de745 641 } else {
cb323159 642 os_atomic_inc(&oslog_p_saved_msgcount, relaxed);
39037602
A
643 }
644 return ftid.ftid_value;
645}
646
647static oslog_stream_buf_entry_t
f427ee49
A
648oslog_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)
39037602 651{
f427ee49
A
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;
39037602 655
f427ee49 656 if (!pubdata || publen > UINT16_MAX || ft_size > UINT16_MAX) {
39037602
A
657 return NULL;
658 }
659
f427ee49 660 m_entry = kheap_alloc(KHEAP_DEFAULT, m_entry_len, Z_WAITOK);
39037602
A
661 if (!m_entry) {
662 return NULL;
663 }
39037602
A
664 m_entry->type = type;
665 m_entry->timestamp = stamp;
f427ee49 666 m_entry->size = (uint16_t)ft_size;
39037602 667
f427ee49 668 firehose_tracepoint_t ft = m_entry->metadata;
39037602
A
669 ft->ft_thread = thread_tid(current_thread());
670 ft->ft_id.ftid_value = ftid.ftid_value;
f427ee49 671 ft->ft_length = (uint16_t)publen;
39037602
A
672 memcpy(ft->ft_data, pubdata, publen);
673
674 return m_entry;
675}
676
f427ee49
A
677void
678os_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
39037602
A
712#ifdef KERNEL
713void
714firehose_trace_metadata(firehose_stream_t stream, firehose_tracepoint_id_u ftid,
0a7de745 715 uint64_t stamp, const void *pubdata, size_t publen)
39037602
A
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
0a7de745 722 stream_lock();
39037602 723 if (!oslog_stream_open) {
0a7de745 724 stream_unlock();
39037602
A
725 goto finish;
726 }
0a7de745 727 stream_unlock();
39037602
A
728
729 // Setup and write the stream metadata entry
730 m_entry = oslog_stream_create_buf_entry(oslog_stream_link_type_metadata, ftid,
0a7de745 731 stamp, pubdata, publen);
39037602 732 if (!m_entry) {
cb323159 733 os_atomic_inc(&oslog_s_error_count, relaxed);
39037602
A
734 goto finish;
735 }
736
0a7de745 737 stream_lock();
39037602 738 if (!oslog_stream_open) {
0a7de745 739 stream_unlock();
f427ee49
A
740 kheap_free(KHEAP_DEFAULT, m_entry,
741 sizeof(struct oslog_stream_buf_entry_s) +
0a7de745 742 sizeof(struct firehose_tracepoint_s) + publen);
39037602
A
743 goto finish;
744 }
745 oslog_s_metadata_msgcount++;
746 oslog_streamwrite_metadata_locked(m_entry);
0a7de745 747 stream_unlock();
39037602
A
748
749finish:
f427ee49 750 _firehose_trace(stream, ftid, stamp, pubdata, publen, true);
39037602
A
751}
752#endif
753
39037602 754void
0a7de745
A
755__firehose_buffer_push_to_logd(firehose_buffer_t fb __unused, bool for_io __unused)
756{
757 oslogwakeup();
758 return;
39037602
A
759}
760
761void
813fb2f6
A
762__firehose_allocate(vm_offset_t *addr, vm_size_t size __unused)
763{
0a7de745
A
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;
39037602
A
777}
778// There isnt a lock held in this case.
779void
0a7de745
A
780__firehose_critical_region_enter(void)
781{
782 disable_preemption();
783 return;
39037602
A
784}
785
786void
0a7de745
A
787__firehose_critical_region_leave(void)
788{
789 enable_preemption();
790 return;
39037602
A
791}
792
d9a64523
A
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
800extern u_int32_t RandomULong(void);
f427ee49 801extern size_t find_pattern_in_buffer(const char *pattern, size_t len, size_t expected_count);
d9a64523
A
802void test_oslog_default_helper(uint32_t uniqid, uint64_t count);
803void test_oslog_info_helper(uint32_t uniqid, uint64_t count);
804void test_oslog_debug_helper(uint32_t uniqid, uint64_t count);
805void test_oslog_error_helper(uint32_t uniqid, uint64_t count);
806void test_oslog_fault_helper(uint32_t uniqid, uint64_t count);
807void _test_log_loop(void * arg __unused, wait_result_t wres __unused);
808void test_oslog_handleOSLogCtl(int32_t * in, int32_t * out, int32_t len);
809kern_return_t test_stresslog_dropmsg(uint32_t uniqid);
810
811kern_return_t test_os_log(void);
812kern_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 { \
0a7de745
A
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 { \
4ba76501 823 datalen = scnprintf(databuffer, sizeof(databuffer), TESTOSLOGFMT(ident), uniqid, i + 1, count); \
0a7de745
A
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 } \
d9a64523
A
828 }
829
830GENOSLOGHELPER(test_oslog_info_helper, "oslog_info_helper", os_log_info);
831GENOSLOGHELPER(test_oslog_fault_helper, "oslog_fault_helper", os_log_fault);
832GENOSLOGHELPER(test_oslog_debug_helper, "oslog_debug_helper", os_log_debug);
833GENOSLOGHELPER(test_oslog_error_helper, "oslog_error_helper", os_log_error);
834GENOSLOGHELPER(test_oslog_default_helper, "oslog_default_helper", os_log);
835
0a7de745
A
836kern_return_t
837test_os_log()
d9a64523 838{
0a7de745
A
839 char databuffer[256];
840 uint32_t uniqid = RandomULong();
f427ee49 841 size_t match_count = 0;
0a7de745
A
842 uint32_t checksum = 0;
843 uint32_t total_msg = 0;
844 uint32_t saved_msg = 0;
845 uint32_t dropped_msg = 0;
f427ee49 846 size_t datalen = 0;
0a7de745
A
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
4ba76501 867 datalen = scnprintf(databuffer, sizeof(databuffer), TESTOSLOGFMT("printf_only"), uniqid, seqno, total_seqno);
0a7de745
A
868 checksum = crc32(0, databuffer, datalen);
869 printf(TESTOSLOG("printf_only") "mat%llu\n", checksum, uniqid, seqno, total_seqno, a);
870
871 seqno += 1;
4ba76501 872 datalen = scnprintf(databuffer, sizeof(databuffer), TESTOSLOGFMT("printf_only"), uniqid, seqno, total_seqno);
0a7de745
A
873 checksum = crc32(0, databuffer, datalen);
874 printf(TESTOSLOG("printf_only") "mat%llu\n", checksum, uniqid, seqno, total_seqno, a);
875
4ba76501 876 datalen = scnprintf(databuffer, sizeof(databuffer), "kernel^0^test^printf_only#mat%llu", a);
0a7de745 877 match_count = find_pattern_in_buffer(databuffer, datalen, total_seqno);
f427ee49 878 T_EXPECT_EQ_ULONG(match_count, total_seqno, "verify printf_only goes to systemlog buffer");
0a7de745
A
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 }
d9a64523 887
0a7de745
A
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");
d9a64523 890
0a7de745
A
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;
4ba76501 897 datalen = scnprintf(databuffer, sizeof(databuffer), TESTOSLOGFMT("oslog_info"), uniqid, seqno, total_seqno);
0a7de745
A
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");
d9a64523 901
4ba76501 902 datalen = scnprintf(databuffer, sizeof(databuffer), "kernel^0^test^oslog_info#mat%llu", a);
0a7de745 903 match_count = find_pattern_in_buffer(databuffer, datalen, total_seqno);
f427ee49 904 T_EXPECT_EQ_ULONG(match_count, total_seqno, "verify oslog_info does not go to systemlog buffer");
d9a64523 905
0a7de745
A
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");
d9a64523 909
0a7de745
A
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");
d9a64523 913
0a7de745
A
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");
d9a64523 917
0a7de745
A
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");
d9a64523 921
0a7de745
A
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");
d9a64523 925
0a7de745
A
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);
d9a64523 928
0a7de745 929 return KERN_SUCCESS;
d9a64523
A
930}
931
932static uint32_t _test_log_loop_count = 0;
0a7de745
A
933void
934_test_log_loop(void * arg __unused, wait_result_t wres __unused)
d9a64523 935{
0a7de745
A
936 uint32_t uniqid = RandomULong();
937 test_oslog_debug_helper(uniqid, 100);
cb323159 938 os_atomic_add(&_test_log_loop_count, 100, relaxed);
d9a64523
A
939}
940
0a7de745
A
941kern_return_t
942test_os_log_parallel(void)
d9a64523 943{
0a7de745
A
944 thread_t thread[2];
945 kern_return_t kr;
946 uint32_t uniqid = RandomULong();
d9a64523 947
0a7de745
A
948 printf("oslog internal counters total %u , saved %u, dropped %u", oslog_p_total_msgcount, oslog_p_saved_msgcount,
949 oslog_p_dropped_msgcount);
d9a64523 950
0a7de745
A
951 kr = kernel_thread_start(_test_log_loop, NULL, &thread[0]);
952 T_ASSERT_EQ_INT(kr, KERN_SUCCESS, "kernel_thread_start returned successfully");
d9a64523 953
0a7de745
A
954 kr = kernel_thread_start(_test_log_loop, NULL, &thread[1]);
955 T_ASSERT_EQ_INT(kr, KERN_SUCCESS, "kernel_thread_start returned successfully");
d9a64523 956
0a7de745 957 test_oslog_info_helper(uniqid, 100);
d9a64523 958
0a7de745
A
959 /* wait until other thread has also finished */
960 while (_test_log_loop_count < 200) {
961 delay(1000);
962 }
d9a64523 963
0a7de745
A
964 thread_deallocate(thread[0]);
965 thread_deallocate(thread[1]);
d9a64523 966
0a7de745
A
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");
d9a64523 970
0a7de745 971 return KERN_SUCCESS;
d9a64523
A
972}
973
0a7de745
A
974void
975test_oslog_handleOSLogCtl(int32_t * in, int32_t * out, int32_t len)
d9a64523 976{
0a7de745
A
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;
d9a64523
A
1032}
1033
0a7de745
A
1034kern_return_t
1035test_stresslog_dropmsg(uint32_t uniqid)
d9a64523 1036{
0a7de745
A
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;
d9a64523
A
1049}
1050
1051#endif