1 | /* |
2 | * Copyright (c) 2015-2023 Apple Inc. All rights reserved. |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ |
5 | * |
6 | * This file contains Original Code and/or Modifications of Original Code |
7 | * as defined in and that are subject to the Apple Public Source License |
8 | * Version 2.0 (the 'License'). You may not use this file except in |
9 | * compliance with the License. Please obtain a copy of the License at |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this |
11 | * file. |
12 | * |
13 | * The Original Code and all software distributed under the License are |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. |
18 | * Please see the License for the specific language governing rights and |
19 | * limitations under the License. |
20 | * |
21 | * @APPLE_LICENSE_HEADER_END@ |
22 | */ |
23 | |
24 | #ifndef __os_log_h |
25 | #define __os_log_h |
26 | |
27 | #include <os/object.h> |
28 | #include <stdint.h> |
29 | #include <stdbool.h> |
30 | |
31 | #ifndef __has_attribute |
32 | #define __has_attribute(x) 0 |
33 | #endif |
34 | |
35 | #ifndef __has_builtin |
36 | #define __has_builtin(x) 0 |
37 | #endif |
38 | |
39 | #if __has_attribute(not_tail_called) |
40 | #define OS_LOG_NOTAILCALL __attribute__((not_tail_called)) |
41 | #define OS_LOG_NOTAILCALL_MARKER |
42 | #else |
43 | #define OS_LOG_NOTAILCALL |
44 | #define OS_LOG_NOTAILCALL_MARKER __asm__("") |
45 | #endif |
46 | |
47 | __BEGIN_DECLS |
48 | |
49 | extern void *__dso_handle; |
50 | |
51 | #ifdef XNU_KERNEL_PRIVATE |
52 | extern bool startup_serial_logging_active; |
53 | extern uint64_t startup_serial_num_procs; |
54 | #endif /* XNU_KERNEL_PRIVATE */ |
55 | |
56 | #ifdef KERNEL |
57 | #define OS_LOG_BUFFER_MAX_SIZE 256 |
58 | #else |
59 | #define OS_LOG_BUFFER_MAX_SIZE 1024 |
60 | #endif |
61 | |
62 | // The OS_LOG_BUFFER_MAX_SIZE limit includes the metadata that |
63 | // must be included in the os_log firehose buffer |
64 | #define OS_LOG_DATA_MAX_SIZE (OS_LOG_BUFFER_MAX_SIZE - 16) |
65 | |
66 | OS_ALWAYS_INLINE |
67 | static inline void |
68 | _os_log_verify_format_str(__unused const char *msg, ...) |
69 | __osloglike(1, 2); |
70 | |
71 | OS_ALWAYS_INLINE |
72 | static inline void |
73 | _os_log_verify_format_str(__unused const char *msg, ...) /* placeholder */ |
74 | { |
75 | } |
76 | |
77 | #if OS_OBJECT_USE_OBJC |
78 | OS_OBJECT_DECL(os_log); |
79 | #else |
80 | typedef struct os_log_s *os_log_t; |
81 | #endif /* OS_OBJECT_USE_OBJC */ |
82 | |
83 | /*! |
84 | * @const OS_LOG_DISABLED |
85 | * |
86 | * @discussion |
87 | * Use this to disable a specific log message. |
88 | */ |
89 | #define OS_LOG_DISABLED NULL |
90 | |
91 | /*! |
92 | * @const OS_LOG_DEFAULT |
93 | * |
94 | * @discussion |
95 | * Use this to log a message in accordance with current system settings. |
96 | */ |
97 | #define OS_LOG_DEFAULT OS_OBJECT_GLOBAL_OBJECT(os_log_t, _os_log_default) |
98 | OS_EXPORT |
99 | struct os_log_s _os_log_default; |
100 | |
101 | /*! |
102 | * @enum os_log_type_t |
103 | * |
104 | * @discussion |
105 | * Supported log message types. |
106 | * |
107 | * @constant OS_LOG_TYPE_DEFAULT |
108 | * Equivalent type for "os_log()" messages, i.e., default messages that are always |
109 | * captured to memory or disk. |
110 | * |
111 | * @constant OS_LOG_TYPE_INFO |
112 | * Equivalent type for "os_log_info()" messages, i.e., Additional informational messages. |
113 | * |
114 | * @constant OS_LOG_TYPE_DEBUG |
115 | * Equivalent type for "os_log_debug()" messages, i.e., Debug messages. |
116 | * |
117 | * @constant OS_LOG_TYPE_ERROR |
118 | * Equivalent type for "os_log_error()" messages, i.e., local process error messages. |
119 | * |
120 | * @constant OS_LOG_TYPE_FAULT |
121 | * Equivalent type for "os_log_fault()" messages, i.e., a system error that involves |
122 | * potentially more than one process, usually used by daemons and services. |
123 | */ |
124 | OS_ENUM(os_log_type, uint8_t, |
125 | OS_LOG_TYPE_DEFAULT = 0x00, |
126 | OS_LOG_TYPE_INFO = 0x01, |
127 | OS_LOG_TYPE_DEBUG = 0x02, |
128 | OS_LOG_TYPE_ERROR = 0x10, |
129 | OS_LOG_TYPE_FAULT = 0x11); |
130 | |
131 | /*! |
132 | * @function os_log_create |
133 | * |
134 | * @abstract |
135 | * Creates a log object to be used with other log related functions. |
136 | * |
137 | * @discussion |
138 | * Creates a log object to be used with other log related functions. The log |
139 | * object serves two purposes: (1) tag related messages by subsystem and |
140 | * category name for easy filtering, and (2) control logging system behavior for |
141 | * messages. |
142 | * |
143 | * @param subsystem |
144 | * The identifier of the given subsystem should be in reverse DNS form (i.e., |
145 | * com.company.mysubsystem). This string must be a constant string, not |
146 | * dynamically generated. |
147 | * |
148 | * @param category |
149 | * The category within the given subsystem that specifies the settings for the |
150 | * log object. This string must be a constant string, not dynamically generated. |
151 | * |
152 | * @result |
153 | * Returns an os_log_t value to be passed to other os_log API calls. This should |
154 | * be called once at log initialization and rely on system to detect changes to |
155 | * settings. |
156 | * |
157 | * A value will always be returned to allow for dynamic enablement. |
158 | */ |
159 | OS_EXPORT OS_NOTHROW OS_WARN_RESULT OS_OBJECT_RETURNS_RETAINED |
160 | os_log_t |
161 | os_log_create(const char *subsystem, const char *category); |
162 | |
163 | /*! |
164 | * @function os_log_info_enabled |
165 | * |
166 | * @abstract |
167 | * Returns if additional information log messages are enabled for a particular |
168 | * log object. |
169 | * |
170 | * @discussion |
171 | * Returns if additional information log messages are enabled for a particular |
172 | * log object. |
173 | * |
174 | * @param log |
175 | * Pass OS_LOG_DEFAULT or a log object previously created with os_log_create. |
176 | * |
177 | * @result |
178 | * Returns ‘true’ if additional information log messages are enabled. |
179 | */ |
180 | OS_EXPORT OS_NOTHROW OS_WARN_RESULT |
181 | bool |
182 | os_log_info_enabled(os_log_t log); |
183 | |
184 | /*! |
185 | * @function os_log_debug_enabled |
186 | * |
187 | * @abstract |
188 | * Returns if debug log messages are enabled for a particular log object. |
189 | * |
190 | * @discussion |
191 | * Returns if debug log messages are enabled for a particular log object. |
192 | * |
193 | * @param log |
194 | * Pass OS_LOG_DEFAULT or a log object previously created with os_log_create. |
195 | * |
196 | * @result |
197 | * Returns ‘true’ if debug log messages are enabled. |
198 | */ |
199 | OS_EXPORT OS_NOTHROW OS_WARN_RESULT |
200 | bool |
201 | os_log_debug_enabled(os_log_t log); |
202 | |
203 | /*! |
204 | * @function os_log |
205 | * |
206 | * @abstract |
207 | * Insert a log message into the Unified Logging and Tracing system. |
208 | * |
209 | * @discussion |
210 | * Insert a log message into the Unified Logging and Tracing system in |
211 | * accordance with the preferences specified by the provided log object. |
212 | * These messages cannot be disabled and therefore always captured either |
213 | * to memory or disk. |
214 | * |
215 | * When an os_activity_id_t is present, the log message will also be scoped by |
216 | * that identifier. Activities provide granular filtering of log messages |
217 | * across threads and processes. |
218 | * |
219 | * There is a physical cap of 256 bytes per entry for dynamic content, |
220 | * i.e., %s and %@, that can be written to the persistence store. As such, |
221 | * all content exceeding the limit will be truncated before written to disk. |
222 | * Live streams will continue to show the full content. |
223 | * |
224 | * @param log |
225 | * Pass OS_LOG_DEFAULT or a log object previously created with os_log_create. |
226 | * |
227 | * @param format |
228 | * A format string to generate a human-readable log message when the log |
229 | * line is decoded. This string must be a constant string, not dynamically |
230 | * generated. Supports all standard printf types and %@ (objects). |
231 | */ |
232 | #define os_log(log, format, ...) __extension__({ \ |
233 | _Static_assert(__builtin_constant_p(format), "format string must be constant"); \ |
234 | __attribute__((section("__TEXT,__os_log"))) static const char _os_log_fmt[] = format; \ |
235 | _os_log_verify_format_str(format, ##__VA_ARGS__); \ |
236 | _os_log_internal(&__dso_handle, log, OS_LOG_TYPE_DEFAULT, _os_log_fmt, ##__VA_ARGS__); \ |
237 | __asm__(""); /* avoid tailcall */ \ |
238 | }) |
239 | |
240 | /*! |
241 | * @function os_log_info |
242 | * |
243 | * @abstract |
244 | * Insert a development log message into the Unified Logging and Tracing system. |
245 | * |
246 | * @discussion |
247 | * Insert a log message into the Unified Logging and Tracing system in |
248 | * accordance with the preferences specified by the provided log object. |
249 | * |
250 | * When an os_activity_id_t is present, the log message will also be scoped by |
251 | * that identifier. Activities provide granular filtering of log messages |
252 | * across threads and processes. |
253 | * |
254 | * There is a physical cap of 256 bytes per entry for dynamic content, |
255 | * i.e., %s and %@, that can be written to the persistence store. As such, |
256 | * all content exceeding the limit will be truncated before written to disk. |
257 | * Live streams will continue to show the full content. |
258 | * |
259 | * @param log |
260 | * Pass OS_LOG_DEFAULT or a log object previously created with os_log_create. |
261 | * |
262 | * @param format |
263 | * A format string to generate a human-readable log message when the log |
264 | * line is decoded. This string must be a constant string, not dynamically |
265 | * generated. Supports all standard printf types and %@ (objects). |
266 | */ |
267 | #define os_log_info(log, format, ...) __extension__({ \ |
268 | _Static_assert(__builtin_constant_p(format), "format string must be constant"); \ |
269 | __attribute__((section("__TEXT,__os_log"))) static const char _os_log_fmt[] = format; \ |
270 | _os_log_verify_format_str(format, ##__VA_ARGS__); \ |
271 | _os_log_internal(&__dso_handle, log, OS_LOG_TYPE_INFO, _os_log_fmt, ##__VA_ARGS__); \ |
272 | __asm__(""); /* avoid tailcall */ \ |
273 | }) |
274 | |
275 | /*! |
276 | * @function os_log_debug |
277 | * |
278 | * @abstract |
279 | * Insert a debug log message into the Unified Logging and Tracing system. |
280 | * |
281 | * @discussion |
282 | * Insert a debug log message into the Unified Logging and Tracing system in |
283 | * accordance with the preferences specified by the provided log object. |
284 | * |
285 | * When an os_activity_id_t is present, the log message will also be scoped by |
286 | * that identifier. Activities provide granular filtering of log messages |
287 | * across threads and processes. |
288 | * |
289 | * There is a physical cap of 256 bytes per entry for dynamic content, |
290 | * i.e., %s and %@, that can be written to the persistence store. As such, |
291 | * all content exceeding the limit will be truncated before written to disk. |
292 | * Live streams will continue to show the full content. |
293 | * |
294 | * @param log |
295 | * Pass OS_LOG_DEFAULT or a log object previously created with os_log_create. |
296 | * |
297 | * @param format |
298 | * A format string to generate a human-readable log message when the log |
299 | * line is decoded. This string must be a constant string, not dynamically |
300 | * generated. Supports all standard printf types and %@ (objects). |
301 | */ |
302 | #define os_log_debug(log, format, ...) __extension__({ \ |
303 | _Static_assert(__builtin_constant_p(format), "format string must be constant"); \ |
304 | __attribute__((section("__TEXT,__os_log"))) static const char _os_log_fmt[] = format; \ |
305 | _os_log_verify_format_str(format, ##__VA_ARGS__); \ |
306 | _os_log_internal(&__dso_handle, log, OS_LOG_TYPE_DEBUG, _os_log_fmt, ##__VA_ARGS__); \ |
307 | __asm__(""); /* avoid tailcall */ \ |
308 | }) |
309 | |
310 | /*! |
311 | * @function os_log_error |
312 | * |
313 | * @abstract |
314 | * Insert an error log message into the Unified Logging and Tracing system. |
315 | * |
316 | * @discussion |
317 | * Insert an error log message into the Unified Logging and Tracing system. |
318 | * |
319 | * When an os_activity_id_t is present, the log message will also be scoped by |
320 | * that identifier. Activities provide granular filtering of log messages |
321 | * across threads and processes. |
322 | * |
323 | * There is a physical cap of 256 bytes per entry for dynamic content, |
324 | * i.e., %s and %@, that can be written to the persistence store. As such, |
325 | * all content exceeding the limit will be truncated before written to disk. |
326 | * Live streams will continue to show the full content. |
327 | * |
328 | * @param log |
329 | * Pass OS_LOG_DEFAULT or a log object previously created with os_log_create. |
330 | * |
331 | * @param format |
332 | * A format string to generate a human-readable log message when the log |
333 | * line is decoded. This string must be a constant string, not dynamically |
334 | * generated. Supports all standard printf types and %@ (objects). |
335 | */ |
336 | #define os_log_error(log, format, ...) __extension__({ \ |
337 | _Static_assert(__builtin_constant_p(format), "format string must be constant"); \ |
338 | __attribute__((section("__TEXT,__os_log"))) static const char _os_log_fmt[] = format; \ |
339 | _os_log_verify_format_str(format, ##__VA_ARGS__); \ |
340 | _os_log_internal(&__dso_handle, log, OS_LOG_TYPE_ERROR, _os_log_fmt, ##__VA_ARGS__); \ |
341 | __asm__(""); /* avoid tailcall */ \ |
342 | }) |
343 | |
344 | /*! |
345 | * @function os_log_fault |
346 | * |
347 | * @abstract |
348 | * Insert a fault log message into the Unified Logging and Tracing system. |
349 | * |
350 | * @discussion |
351 | * Log a fault message issue into the Unified Logging and Tracing system |
352 | * signifying a multi-process (i.e., system error) related issue, either |
353 | * due to interaction via IPC or some other. Faults will gather information |
354 | * from the entire process chain and record it for later inspection. |
355 | * |
356 | * When an os_activity_id_t is present, the log message will also be scoped by |
357 | * that identifier. Activities provide granular filtering of log messages |
358 | * across threads and processes. |
359 | * |
360 | * There is a physical cap of 256 bytes per entry for dynamic content, |
361 | * i.e., %s and %@, that can be written to the persistence store. As such, |
362 | * all content exceeding the limit will be truncated before written to disk. |
363 | * Live streams will continue to show the full content. |
364 | * |
365 | * @param log |
366 | * Pass OS_LOG_DEFAULT or a log object previously created with os_log_create. |
367 | * |
368 | * @param format |
369 | * A format string to generate a human-readable log message when the log |
370 | * line is decoded. This string must be a constant string, not dynamically |
371 | * generated. Supports all standard printf types and %@ (objects). |
372 | */ |
373 | #define os_log_fault(log, format, ...) __extension__({ \ |
374 | _Static_assert(__builtin_constant_p(format), "format string must be constant"); \ |
375 | __attribute__((section("__TEXT,__os_log"))) static const char _os_log_fmt[] = format; \ |
376 | _os_log_verify_format_str(format, ##__VA_ARGS__); \ |
377 | _os_log_internal(&__dso_handle, log, OS_LOG_TYPE_FAULT, _os_log_fmt, ##__VA_ARGS__); \ |
378 | __asm__(""); /* avoid tailcall */ \ |
379 | }) |
380 | |
381 | /*! |
382 | * @function os_log_with_type |
383 | * |
384 | * @abstract |
385 | * Log a message using a specific type. |
386 | * |
387 | * @discussion |
388 | * Will log a message with the provided os_log_type_t. |
389 | * |
390 | * @param log |
391 | * Pass OS_LOG_DEFAULT or a log object previously created with os_log_create. |
392 | * |
393 | * @param type |
394 | * Pass a valid type from os_log_type_t. |
395 | * |
396 | * @param format |
397 | * A format string to generate a human-readable log message when the log |
398 | * line is decoded. This string must be a constant string, not dynamically |
399 | * generated. Supports all standard printf types and %@ (objects). |
400 | */ |
401 | #define os_log_with_type(log, type, format, ...) __extension__({ \ |
402 | _Static_assert(__builtin_constant_p(format), "format string must be constant"); \ |
403 | __attribute__((section("__TEXT,__os_log"))) static const char _os_log_fmt[] = format; \ |
404 | _os_log_verify_format_str(format, ##__VA_ARGS__); \ |
405 | _os_log_internal(&__dso_handle, log, type, _os_log_fmt, ##__VA_ARGS__); \ |
406 | __asm__(""); /* avoid tailcall */ \ |
407 | }) |
408 | |
409 | /*! |
410 | * @function os_log_at_time |
411 | * |
412 | * @abstract |
413 | * Log a message using a specific type and a timestamp. |
414 | * |
415 | * @discussion |
416 | * Will log a message with the provided os_log_type_t and a timestamp |
417 | * signifying a moment of a log message creation. |
418 | * |
419 | * @param log |
420 | * Pass OS_LOG_DEFAULT or a log object previously created with os_log_create. |
421 | * |
422 | * @param type |
423 | * Pass a valid type from os_log_type_t. |
424 | * |
425 | * @param ts |
426 | * Pass a uint64_t value (timestamp) of mach continuous time clock. |
427 | * |
428 | * @param format |
429 | * A format string to generate a human-readable log message when the log |
430 | * line is decoded. This string must be a constant string, not dynamically |
431 | * generated. Supports all standard printf types. |
432 | */ |
433 | #define os_log_at_time(log, type, ts, format, ...) __extension__({ \ |
434 | _Static_assert(__builtin_constant_p(format), "format string must be constant"); \ |
435 | __attribute__((section("__TEXT,__os_log"))) static const char _os_log_fmt[] = format; \ |
436 | _os_log_verify_format_str(format, ##__VA_ARGS__); \ |
437 | _os_log_at_time(&__dso_handle, log, type, ts, _os_log_fmt, ##__VA_ARGS__); \ |
438 | __asm__(""); /* avoid tailcall */ \ |
439 | }) |
440 | |
441 | /*! |
442 | * @function os_log_driverKit |
443 | * |
444 | * @abstract |
445 | * Log a message using a specific type. This variant should be called only from dexts. |
446 | * |
447 | * @discussion |
448 | * Will log a message with the provided os_log_type_t. |
449 | * |
450 | * @param log |
451 | * Pass OS_LOG_DEFAULT or a log object previously created with os_log_create. |
452 | * |
453 | * @param type |
454 | * Pass a valid type from os_log_type_t. |
455 | * |
456 | * @param format |
457 | * A format string to generate a human-readable log message when the log |
458 | * line is decoded. This string must be a constant string, not dynamically |
459 | * generated. Supports all standard printf types and %@ (objects). |
460 | * |
461 | * @result |
462 | * Returns EPERM if the caller is not a driverKit process, 0 in case of success. |
463 | */ |
464 | #define os_log_driverKit(out, log, type, format, ...) __extension__({ \ |
465 | _Static_assert(__builtin_constant_p(format), "format string must be constant"); \ |
466 | __attribute__((section("__TEXT,__os_log"))) static const char _os_log_fmt[] = format; \ |
467 | _os_log_verify_format_str(format, ##__VA_ARGS__); \ |
468 | (*(out)) = _os_log_internal_driverKit(&__dso_handle, log, type, _os_log_fmt, ##__VA_ARGS__); \ |
469 | __asm__(""); /* avoid tailcall */ \ |
470 | }) |
471 | |
472 | /*! |
473 | * @function os_log_coprocessor |
474 | * |
475 | * @abstract |
476 | * IOP logging function, intended for use by RTBuddy for coprocessor os log |
477 | * functionality only. |
478 | */ |
479 | bool |
480 | os_log_coprocessor(void *buff, uint64_t buff_len, os_log_type_t type, |
481 | const char *uuid, uint64_t timestamp, uint32_t offset, bool stream_log); |
482 | |
483 | /*! |
484 | * @function os_log_coprocessor_register |
485 | * |
486 | * @abstract |
487 | * IOP metadata registration, intended for use by RTBuddy for coprocessor os log |
488 | * functionality only. Will be removed after all user code will be updated to |
489 | * use os_log_coprocessor_register_with_type. |
490 | */ |
491 | void |
492 | os_log_coprocessor_register(const char *uuid, const char *file_path, bool copy); |
493 | |
494 | typedef enum { |
495 | os_log_coproc_register_memory, |
496 | os_log_coproc_register_harvest_fs_ftab, |
497 | } os_log_coproc_reg_t; |
498 | |
499 | /*! |
500 | * @function os_log_coprocessor_register_with_type |
501 | * |
502 | * @abstract |
503 | * IOP metadata registration, intended for use by RTBuddy for coprocessor os log |
504 | * functionality only. |
505 | */ |
506 | void |
507 | os_log_coprocessor_register_with_type(const char *uuid, const char *file_path, os_log_coproc_reg_t register_type); |
508 | |
509 | #ifdef XNU_KERNEL_PRIVATE |
510 | #define os_log_with_startup_serial_and_type(log, type, format, ...) __extension__({ \ |
511 | if (startup_serial_logging_active) { printf(format, ##__VA_ARGS__); } \ |
512 | else { os_log_with_type(log, type, format, ##__VA_ARGS__); } \ |
513 | }) |
514 | #define os_log_with_startup_serial(log, format, ...) \ |
515 | os_log_with_startup_serial_and_type(log, OS_LOG_TYPE_DEFAULT, format, ##__VA_ARGS__) |
516 | #define os_log_info_with_startup_serial(log, format, ...) \ |
517 | os_log_with_startup_serial_and_type(log, OS_LOG_TYPE_INFO, format, ##__VA_ARGS__) |
518 | #define os_log_debug_with_startup_serial(log, format, ...) \ |
519 | os_log_with_startup_serial_and_type(log, OS_LOG_TYPE_DEBUG, format, ##__VA_ARGS__) |
520 | #define os_log_error_with_startup_serial(log, format, ...) \ |
521 | os_log_with_startup_serial_and_type(log, OS_LOG_TYPE_ERROR, format, ##__VA_ARGS__) |
522 | #define os_log_fault_with_startup_serial(log, format, ...) \ |
523 | os_log_with_startup_serial_and_type(log, OS_LOG_TYPE_FAULT, format, ##__VA_ARGS__) |
524 | #endif /* XNU_KERNEL_PRIVATE */ |
525 | |
526 | /*! |
527 | * @function _os_log_internal |
528 | * |
529 | * @abstract |
530 | * Internal function used by macros. |
531 | */ |
532 | OS_EXPORT OS_NOTHROW |
533 | void |
534 | _os_log_internal(void *dso, os_log_t log, os_log_type_t type, const char *message, ...) |
535 | __osloglike(4, 5); |
536 | |
537 | /*! |
538 | * @function _os_log_internal_driverKit |
539 | * |
540 | * @abstract |
541 | * Internal function used by macros. |
542 | */ |
543 | OS_EXPORT OS_NOTHROW |
544 | int |
545 | _os_log_internal_driverKit(void *dso, os_log_t log, os_log_type_t type, const char *message, ...) |
546 | __osloglike(4, 5); |
547 | |
548 | /*! |
549 | * @function _os_log_internal_props |
550 | * |
551 | * @abstract |
552 | * Internal function used by macros. |
553 | */ |
554 | OS_EXPORT OS_NOTHROW |
555 | void |
556 | _os_log_at_time(void *dso, os_log_t log, os_log_type_t type, uint64_t ts, const char *message, ...) |
557 | __osloglike(5, 6); |
558 | |
559 | __END_DECLS |
560 | |
561 | #endif /* __os_log_h */ |
562 | |