]>
Commit | Line | Data |
---|---|---|
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 | ||
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; | |
813fb2f6 | 48 | extern firehose_chunk_t firehose_boot_chunk; |
39037602 | 49 | |
f427ee49 | 50 | extern bool bsd_log_lock(bool); |
39037602 | 51 | extern void bsd_log_unlock(void); |
cc8bc92a | 52 | extern void logwakeup(struct msgbuf *); |
39037602 | 53 | |
cb323159 | 54 | decl_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 |
58 | extern void oslog_streamwakeup(void); |
59 | void oslog_streamwrite_locked(firehose_tracepoint_id_u ftid, | |
0a7de745 | 60 | uint64_t stamp, const void *pubdata, size_t publen); |
39037602 A |
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; | |
f427ee49 A |
75 | uint32_t oslog_p_coprocessor_total_msgcount = 0; |
76 | uint32_t oslog_p_coprocessor_dropped_msgcount = 0; | |
39037602 A |
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 | ||
f427ee49 A |
83 | /* Counters for msgbuf logging */ |
84 | uint32_t oslog_msgbuf_msgcount = 0; | |
85 | uint32_t oslog_msgbuf_dropped_msgcount = 0; | |
86 | ||
39037602 | 87 | static bool oslog_boot_done = false; |
5ba3f43e A |
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 */ | |
39037602 A |
93 | |
94 | // XXX | |
95 | firehose_tracepoint_id_t | |
96 | firehose_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 | |
99 | static 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 | |
103 | static oslog_stream_buf_entry_t | |
104 | oslog_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 | |
107 | static 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 | |
111 | static void | |
cb323159 | 112 | _os_log_to_msgbuf_internal(const char *format, va_list args, bool safe, bool logging, bool addcr); |
39037602 A |
113 | |
114 | static 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 | ||
119 | static 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 | |
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 | ||
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 |
215 | os_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 | ||
225 | static 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 | ||
255 | static 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, µsecs); | |
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 | ||
356 | static 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 |
420 | static 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 | ||
465 | OS_ALWAYS_INLINE | |
813fb2f6 A |
466 | static 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 | ||
483 | static 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 | ||
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 | ||
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 | ||
573 | static 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 | ||
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) { | |
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 | ||
647 | static oslog_stream_buf_entry_t | |
f427ee49 A |
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) | |
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 |
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 | ||
39037602 A |
712 | #ifdef KERNEL |
713 | void | |
714 | firehose_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 | |
749 | finish: | |
f427ee49 | 750 | _firehose_trace(stream, ftid, stamp, pubdata, publen, true); |
39037602 A |
751 | } |
752 | #endif | |
753 | ||
39037602 | 754 | void |
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 | ||
761 | void | |
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. | |
779 | void | |
0a7de745 A |
780 | __firehose_critical_region_enter(void) |
781 | { | |
782 | disable_preemption(); | |
783 | return; | |
39037602 A |
784 | } |
785 | ||
786 | void | |
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 | ||
800 | extern u_int32_t RandomULong(void); | |
f427ee49 | 801 | extern size_t find_pattern_in_buffer(const char *pattern, size_t len, size_t expected_count); |
d9a64523 A |
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 | { \ | |
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 | ||
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 | ||
0a7de745 A |
836 | kern_return_t |
837 | test_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 | ||
932 | static uint32_t _test_log_loop_count = 0; | |
0a7de745 A |
933 | void |
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 |
941 | kern_return_t |
942 | test_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 |
974 | void |
975 | test_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 |
1034 | kern_return_t |
1035 | test_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 |