]> git.saurik.com Git - apple/xnu.git/blob - libkern/os/log_encode.h
xnu-3789.21.4.tar.gz
[apple/xnu.git] / libkern / os / log_encode.h
1 /*
2 * Copyright (c) 2015-2016 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 log_encode_h
25 #define log_encode_h
26
27 #include "log_encode_types.h"
28 #include <sys/param.h>
29
30 #if KERNEL
31 #define isdigit(ch) (((ch) >= '0') && ((ch) <= '9'))
32 #endif
33
34 static bool
35 _encode_data(os_log_buffer_value_t content, const void *arg, uint16_t arg_len, os_log_buffer_context_t context)
36 {
37 struct os_log_arginfo_s arginfo;
38 void *databuf;
39
40 if (content->flags & OS_LOG_CONTENT_FLAG_PRIVATE) {
41 databuf = context->privdata + context->privdata_off;
42 arginfo.length = MIN(arg_len, (context->privdata_sz - context->privdata_off));
43 arginfo.offset = context->privdata_off;
44 } else {
45 databuf = context->pubdata + context->pubdata_off;
46 arginfo.length = MIN(arg_len, (context->pubdata_sz - context->pubdata_off));
47 arginfo.offset = context->pubdata_off;
48 }
49
50 if (context->arg_content_sz > 0) {
51 arginfo.length = MIN(context->arg_content_sz, arginfo.length);
52 }
53
54 memcpy(content->value, &arginfo, sizeof(arginfo));
55 content->size = sizeof(arginfo);
56
57 if (arginfo.length) {
58 if (content->type == OS_LOG_BUFFER_VALUE_TYPE_STRING
59 #ifndef KERNEL
60 || content->type == OS_LOG_BUFFER_VALUE_TYPE_OBJECT
61 #endif
62 ) {
63 strlcpy(databuf, arg, arginfo.length);
64 } else {
65 memcpy(databuf, arg, arginfo.length);
66 }
67 }
68
69 if (content->flags & OS_LOG_CONTENT_FLAG_PRIVATE) {
70 context->privdata_off += arginfo.length;
71 } else {
72 context->pubdata_off += arginfo.length;
73 }
74
75 context->content_off += sizeof(*content) + content->size;
76 context->arg_content_sz = 0;
77
78 return true;
79 }
80
81 #ifndef KERNEL
82 static void
83 _os_log_parse_annotated(char *annotated, const char **visibility, const char **library, const char **type)
84 {
85 char *values[3] = { NULL };
86 int cnt = 0;
87 int idx = 0;
88
89 for (; cnt < 3;) {
90 char *token = strsep(&annotated, ", {}");
91 if (token == NULL) {
92 break;
93 }
94
95 if (*token == '\0') {
96 continue;
97 }
98
99 values[cnt++] = token;
100 }
101
102 if ((cnt > 0) && (!strcmp(values[0], "public") || !strcmp(values[0], "private"))) {
103 if (visibility != NULL) {
104 (*visibility) = values[0];
105 }
106
107 idx++;
108 }
109
110 if (idx < cnt && (library != NULL) && (type != NULL)) {
111 char *decoder = values[idx];
112
113 for (cnt = 0; cnt < 3; ) {
114 char *token = strsep(&decoder, ": {}");
115 if (token == NULL) {
116 break;
117 }
118
119 if (*token == '\0') {
120 continue;
121 }
122
123 values[cnt++] = token;
124 }
125
126 if (cnt == 2) {
127 (*library) = values[0];
128 (*type) = values[1];
129 }
130
131 if (cnt == 1) {
132 (*library) = "builtin";
133 (*type) = values[0];
134 }
135 }
136 }
137 #endif /* !KERNEL */
138
139 OS_ALWAYS_INLINE
140 static inline bool
141 _os_log_encode_arg(const void *arg, uint16_t arg_len, os_log_value_type_t ctype, bool is_private, os_log_buffer_context_t context)
142 {
143 os_log_buffer_value_t content = (os_log_buffer_value_t) &context->buffer->content[context->content_off];
144 size_t content_sz = sizeof(*content) + arg_len;
145 char tempString[OS_LOG_BUFFER_MAX_SIZE] = {};
146 #ifndef KERNEL
147 bool obj_private = true;
148 #endif
149
150 content->type = ctype;
151 content->flags = (is_private ? OS_LOG_CONTENT_FLAG_PRIVATE : 0);
152
153 #ifndef KERNEL
154 if (context->annotated != NULL) {
155 const char *visibility = NULL;
156
157 _os_log_parse_annotated(context->annotated, &visibility, NULL, NULL);
158 if (visibility) {
159 if (!strcasecmp(visibility, "private")) {
160 content->flags |= OS_LOG_CONTENT_FLAG_PRIVATE;
161 } else if (!strcasecmp(visibility, "public")) {
162 content->flags &= ~OS_LOG_CONTENT_FLAG_PRIVATE;
163 }
164 }
165
166 context->annotated = NULL;
167 }
168 #endif /* !KERNEL */
169
170 switch (ctype) {
171 case OS_LOG_BUFFER_VALUE_TYPE_COUNT:
172 case OS_LOG_BUFFER_VALUE_TYPE_SCALAR:
173 if (is_private) {
174 _encode_data(content, tempString, strlen(tempString) + 1, context);
175 } else {
176 if ((context->content_off + content_sz) > context->content_sz) {
177 return false;
178 }
179
180 memcpy(content->value, arg, arg_len);
181 content->size = arg_len;
182 context->content_off += content_sz;
183 }
184 break;
185
186 case OS_LOG_BUFFER_VALUE_TYPE_STRING:
187 context->buffer->flags |= OS_LOG_BUFFER_HAS_NON_SCALAR;
188 if (_os_log_string_is_public(arg)) {
189 content->flags &= ~OS_LOG_CONTENT_FLAG_PRIVATE;
190 }
191
192 _encode_data(content, arg, arg_len, context);
193 break;
194
195 #ifndef KERNEL
196 case OS_LOG_BUFFER_VALUE_TYPE_POINTER:
197 context->buffer->flags |= OS_LOG_BUFFER_HAS_NON_SCALAR;
198 _encode_data(content, arg, arg_len, context);
199 break;
200
201 case OS_LOG_BUFFER_VALUE_TYPE_OBJECT:
202 context->buffer->flags |= OS_LOG_BUFFER_HAS_NON_SCALAR;
203 if (!_NSCF2data(arg, tempString, sizeof(tempString), &obj_private)) {
204 tempString[0] = '\0';
205 }
206
207 if (!obj_private) {
208 content->flags &= ~OS_LOG_CONTENT_FLAG_PRIVATE;
209 }
210
211 _encode_data(content, tempString, strlen(tempString) + 1, context);
212 break;
213 #endif /* !KERNEL */
214 }
215
216 if (content->flags & OS_LOG_CONTENT_FLAG_PRIVATE) {
217 context->buffer->flags |= OS_LOG_BUFFER_HAS_PRIVATE;
218 }
219
220 context->arg_idx++;
221
222 return true;
223 }
224
225 static bool
226 _os_log_encode(const char *format, va_list args, int saved_errno, os_log_buffer_context_t context)
227 {
228 const char *percent = strchr(format, '%');
229 #ifndef KERNEL
230 char annotated[256];
231 #endif
232
233 while (percent != NULL) {
234 ++percent;
235 if (percent[0] != '%') {
236 struct os_log_format_value_s value;
237 int type = OST_INT;
238 #ifndef KERNEL
239 bool long_double = false;
240 #endif
241 int prec = 0;
242 char ch;
243
244 for (bool done = false; !done; percent++) {
245 switch (ch = percent[0]) {
246 /* type of types or other */
247 case 'l': // longer
248 type++;
249 break;
250
251 case 'h': // shorter
252 type--;
253 break;
254
255 case 'z':
256 type = OST_SIZE;
257 break;
258
259 case 'j':
260 type = OST_INTMAX;
261 break;
262
263 case 't':
264 type = OST_PTRDIFF;
265 break;
266
267 case '.': // precision
268 if ((percent[1]) == '*') {
269 prec = va_arg(args, int);
270 _os_log_encode_arg(&prec, sizeof(prec), OS_LOG_BUFFER_VALUE_TYPE_COUNT, false, context);
271 percent++;
272 continue;
273 } else {
274 // we have to read the precision and do the right thing
275 const char *fmt = percent + 1;
276 prec = 0;
277 while (isdigit(ch = *fmt++)) {
278 prec = 10 * prec + (ch - '0');
279 }
280
281 if (prec > 1024) {
282 prec = 1024;
283 }
284
285 _os_log_encode_arg(&prec, sizeof(prec), OS_LOG_BUFFER_VALUE_TYPE_COUNT, false, context);
286 }
287 break;
288
289 case '-': // left-align
290 case '+': // force sign
291 case ' ': // prefix non-negative with space
292 case '#': // alternate
293 case '\'': // group by thousands
294 break;
295
296 /* fixed types */
297 case 'd': // integer
298 case 'i': // integer
299 case 'o': // octal
300 case 'u': // unsigned
301 case 'x': // hex
302 case 'X': // upper-hex
303 switch (type) {
304 case OST_CHAR:
305 value.type.ch = va_arg(args, int);
306 _os_log_encode_arg(&value.type.ch, sizeof(value.type.ch), OS_LOG_BUFFER_VALUE_TYPE_SCALAR, false, context);
307 break;
308
309 case OST_SHORT:
310 value.type.s = va_arg(args, int);
311 _os_log_encode_arg(&value.type.s, sizeof(value.type.s), OS_LOG_BUFFER_VALUE_TYPE_SCALAR, false, context);
312 break;
313
314 case OST_INT:
315 value.type.i = va_arg(args, int);
316 _os_log_encode_arg(&value.type.i, sizeof(value.type.i), OS_LOG_BUFFER_VALUE_TYPE_SCALAR, false, context);
317 break;
318
319 case OST_LONG:
320 value.type.l = va_arg(args, long);
321 _os_log_encode_arg(&value.type.l, sizeof(value.type.l), OS_LOG_BUFFER_VALUE_TYPE_SCALAR, false, context);
322 break;
323
324 case OST_LONGLONG:
325 value.type.ll = va_arg(args, long long);
326 _os_log_encode_arg(&value.type.ll, sizeof(value.type.ll), OS_LOG_BUFFER_VALUE_TYPE_SCALAR, false, context);
327 break;
328
329 case OST_SIZE:
330 value.type.z = va_arg(args, size_t);
331 _os_log_encode_arg(&value.type.z, sizeof(value.type.z), OS_LOG_BUFFER_VALUE_TYPE_SCALAR, false, context);
332 break;
333
334 case OST_INTMAX:
335 value.type.im = va_arg(args, intmax_t);
336 _os_log_encode_arg(&value.type.im, sizeof(value.type.im), OS_LOG_BUFFER_VALUE_TYPE_SCALAR, false, context);
337 break;
338
339 case OST_PTRDIFF:
340 value.type.pd = va_arg(args, ptrdiff_t);
341 _os_log_encode_arg(&value.type.pd, sizeof(value.type.pd), OS_LOG_BUFFER_VALUE_TYPE_SCALAR, false, context);
342 break;
343
344 default:
345 return false;
346 }
347 done = true;
348 break;
349
350 #ifndef KERNEL
351 case '{':
352 // we do not support this for shimmed code
353 if (context->shimmed) {
354 return false;
355 }
356
357 for (const char *curr2 = percent + 1; (ch = (*curr2)) != NUL; curr2++) {
358 if (ch == '}') {
359 strlcpy(annotated, percent, MIN(curr2 - (percent + 1), sizeof(annotated)));
360 context->annotated = annotated;
361 percent = curr2;
362 break;
363 }
364 }
365 break;
366 #endif /* !KERNEL */
367
368 case 'p': // pointer
369 value.type.p = va_arg(args, void *);
370 _os_log_encode_arg(&value.type.p, sizeof(value.type.p), OS_LOG_BUFFER_VALUE_TYPE_SCALAR, false, context);
371 done = true;
372 break;
373
374 #ifndef KERNEL
375 case 'P': // pointer data
376 if (context->shimmed) { // we do not support this for shimmed code
377 return false;
378 }
379
380 context->buffer->flags |= OS_LOG_BUFFER_HAS_NON_SCALAR;
381 value.type.p = va_arg(args, void *);
382
383 // capture the string pointer to generate a symptom
384 if (context->log && context->log->generate_symptoms && context->arg_idx == 1 && value.type.pch && prec) {
385 context->symptom_ptr = value.type.p;
386 context->symptom_ptr_len = prec;
387 }
388
389 _os_log_encode_arg(value.type.p, prec, OS_LOG_BUFFER_VALUE_TYPE_POINTER, false, context);
390 prec = 0;
391 done = true;
392 break;
393 #endif /* !KERNEL */
394
395 #ifndef KERNEL
396 case 'L': // long double
397 long_double = true;
398 break;
399
400 case 'a': case 'A': case 'e': case 'E': // floating types
401 case 'f': case 'F': case 'g': case 'G':
402 if (long_double) {
403 value.type.ld = va_arg(args, long double);
404 _os_log_encode_arg(&value.type.ld, sizeof(value.type.ld), OS_LOG_BUFFER_VALUE_TYPE_SCALAR, false, context);
405 } else {
406 value.type.d = va_arg(args, double);
407 _os_log_encode_arg(&value.type.d, sizeof(value.type.d), OS_LOG_BUFFER_VALUE_TYPE_SCALAR, false, context);
408 }
409 done = true;
410 break;
411 #endif /* !KERNEL */
412
413 case 'c': // char
414 value.type.ch = va_arg(args, int);
415 _os_log_encode_arg(&value.type.ch, sizeof(value.type.ch), OS_LOG_BUFFER_VALUE_TYPE_SCALAR, false, context);
416 done = true;
417 break;
418
419 #ifndef KERNEL
420 case 'C': // wide-char
421 value.type.wch = va_arg(args, wint_t);
422 _os_log_encode_arg(&value.type.wch, sizeof(value.type.wch), OS_LOG_BUFFER_VALUE_TYPE_SCALAR, false, context);
423 done = true;
424 break;
425 #endif /* !KERNEL */
426
427 case 's': // string
428 value.type.pch = va_arg(args, char *);
429 if (!prec && value.type.pch != NULL) {
430 prec = (int) strlen(value.type.pch) + 1;
431 }
432
433 #ifndef KERNEL
434 // capture the string pointer to generate a symptom
435 if (context->log && context->log->generate_symptoms && context->arg_idx == 0 && value.type.pch) {
436 context->symptom_str = value.type.pch;
437 }
438 #endif
439
440 context->buffer->flags |= OS_LOG_BUFFER_HAS_NON_SCALAR;
441 _os_log_encode_arg(value.type.pch, prec, OS_LOG_BUFFER_VALUE_TYPE_STRING, false, context);
442 prec = 0;
443 done = true;
444 break;
445
446 #ifndef KERNEL
447 case 'S': // wide-string
448 value.type.pwch = va_arg(args, wchar_t *);
449 if (!prec && value.type.pwch != NULL) {
450 prec = (int) wcslen(value.type.pwch) + 1;
451 }
452
453 context->buffer->flags |= OS_LOG_BUFFER_HAS_NON_SCALAR;
454 _os_log_encode_arg(value.type.pwch, prec, OS_LOG_BUFFER_VALUE_TYPE_STRING, false, context);
455 prec = 0;
456 done = true;
457 break;
458 #endif /* !KERNEL */
459
460 #ifndef KERNEL
461 case '@': // CFTypeRef aka NSObject *
462 context->buffer->flags |= OS_LOG_BUFFER_HAS_NON_SCALAR;
463 _os_log_encode_arg(va_arg(args, void *), 0, OS_LOG_BUFFER_VALUE_TYPE_OBJECT, false, context);
464 done = true;
465 break;
466 #endif /* !KERNEL */
467
468 case 'm':
469 value.type.i = saved_errno;
470 _os_log_encode_arg(&value.type.i, sizeof(value.type.i), OS_LOG_BUFFER_VALUE_TYPE_SCALAR, false, context);
471 done = true;
472 break;
473
474 default:
475 if (isdigit(ch)) { // [0-9]
476 continue;
477 }
478 return false;
479 }
480
481 if (done) {
482 percent = strchr(percent, '%'); // Find next format
483 break;
484 }
485 }
486 } else {
487 percent = strchr(percent+1, '%'); // Find next format after %%
488 }
489 }
490
491 context->buffer->arg_cnt = context->arg_idx;
492 context->content_sz = context->content_off;
493 context->pubdata_sz = context->pubdata_off;
494 context->privdata_sz = context->privdata_off;
495 context->arg_idx = context->content_off = context->pubdata_off = context->privdata_off = 0;
496
497 return true;
498 }
499
500 #endif /* log_encode_h */