| //===----------------------------------------------------------------------===// |
| // |
| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors |
| // Licensed under Apache License v2.0 with Runtime Library Exception |
| // |
| // See https://swift.org/LICENSE.txt for license information |
| // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include <TargetConditionals.h> |
| #include <Availability.h> |
| #include <CoreFoundation/CoreFoundation.h> |
| #include <dlfcn.h> |
| #include <dispatch/dispatch.h> |
| #include <os/base.h> |
| #include <os/log.h> |
| #include <objc/runtime.h> |
| #include <wchar.h> |
| |
| #include "os_trace_blob.h" |
| #include "thunks.h" |
| |
| #define OST_FORMAT_MAX_STRING_SIZE 1024 |
| #define OS_LOG_FMT_MAX_CMDS 48 |
| #define OS_LOG_FMT_BUF_SIZE (2 + (2 + 16) * OS_LOG_FMT_MAX_CMDS) |
| |
| enum os_trace_int_types_t { |
| T_CHAR = -2, |
| T_SHORT = -1, |
| T_INT = 0, |
| T_LONG = 1, |
| T_LONGLONG = 2, |
| T_SIZE = 3, |
| T_INTMAX = 4, |
| T_PTRDIFF = 5, |
| }; |
| |
| OS_ENUM(os_log_fmt_cmd_flags, uint8_t, |
| OSLF_CMD_FLAG_PRIVATE = 0x1, |
| OSLF_CMD_FLAG_PUBLIC = 0x2, |
| ); |
| |
| OS_ENUM(os_log_fmt_cmd_type, uint8_t, |
| OSLF_CMD_TYPE_SCALAR = 0, |
| OSLF_CMD_TYPE_COUNT = 1, |
| OSLF_CMD_TYPE_STRING = 2, |
| OSLF_CMD_TYPE_DATA = 3, |
| OSLF_CMD_TYPE_OBJECT = 4, |
| OSLF_CMD_TYPE_WIDE_STRING = 5, |
| OSLF_CMD_TYPE_ERRNO = 6, |
| ); |
| |
| OS_ENUM(os_log_fmt_hdr_flags, uint8_t, |
| OSLF_HDR_FLAG_HAS_PRIVATE = 0x01, |
| OSLF_HDR_FLAG_HAS_NON_SCALAR = 0x02, |
| ); |
| |
| enum os_log_int_types_t { |
| OST_CHAR = -2, |
| OST_SHORT = -1, |
| OST_INT = 0, |
| OST_LONG = 1, |
| OST_LONGLONG = 2, |
| OST_SIZE = 3, |
| OST_INTMAX = 4, |
| OST_PTRDIFF = 5, |
| }; |
| |
| typedef struct { |
| os_log_fmt_cmd_flags_t cmd_flags : 4; |
| os_log_fmt_cmd_type_t cmd_type : 4; |
| uint8_t cmd_size; |
| uint8_t cmd_data[]; |
| } os_log_fmt_cmd_s, *os_log_fmt_cmd_t; |
| |
| typedef struct os_log_fmt_hdr_s { |
| os_log_fmt_hdr_flags_t hdr_flags; |
| uint8_t hdr_cmd_cnt; |
| uint8_t hdr_data[]; |
| } os_log_fmt_hdr_s, *os_log_fmt_hdr_t; |
| |
| typedef struct os_log_pack_s { |
| uint64_t olp_continuous_time; |
| struct timespec olp_wall_time; |
| const void *olp_mh; |
| const void *olp_pc; |
| const char *olp_format; |
| uint8_t olp_data[0]; |
| } os_log_pack_s, *os_log_pack_t; |
| |
| API_AVAILABLE(macosx(10.12.4), ios(10.3), tvos(10.2), watchos(3.2)) |
| size_t |
| _os_log_pack_size(size_t os_log_format_buffer_size); |
| |
| API_AVAILABLE(macosx(10.12.4), ios(10.3), tvos(10.2), watchos(3.2)) |
| uint8_t * |
| _os_log_pack_fill(os_log_pack_t pack, size_t size, int saved_errno, const void *dso, const char *fmt); |
| |
| API_AVAILABLE(macosx(10.12.4), ios(10.3), tvos(10.2), watchos(3.2)) |
| void |
| os_log_pack_send(os_log_pack_t pack, os_log_t log, os_log_type_t type); |
| |
| static inline void |
| _os_log_encode_arg(os_trace_blob_t ob, os_log_fmt_cmd_t cmd, const void *data) |
| { |
| os_trace_blob_add(ob, cmd, sizeof(os_log_fmt_cmd_s)); |
| os_trace_blob_add(ob, data, cmd->cmd_size); |
| } |
| |
| static bool |
| _os_log_encode(char buf[OS_LOG_FMT_BUF_SIZE], const char *format, va_list args, int saved_errno, os_trace_blob_t ob) |
| { |
| os_log_fmt_hdr_s hdr = { }; |
| os_trace_blob_add(ob, &hdr, sizeof(hdr)); |
| |
| const char *percent = strchr(format, '%'); |
| |
| while (percent != NULL) { |
| ++percent; |
| if (percent[0] != '%') { |
| os_log_fmt_cmd_s cmd = { }; |
| int type = T_INT; |
| bool long_double = false; |
| int precision = 0; |
| char ch; |
| |
| if (hdr.hdr_cmd_cnt == OS_LOG_FMT_MAX_CMDS) { |
| break; |
| } |
| |
| for (bool done = false; !done; percent++) { |
| switch (ch = percent[0]) { |
| /* type of types or other */ |
| case 'l': type++; break; // longer |
| case 'h': type--; break; // shorter |
| case 'z': type = T_SIZE; break; |
| case 'j': type = T_INTMAX; break; |
| case 't': type = T_PTRDIFF; break; |
| |
| case '.': // precision |
| cmd.cmd_type = OSLF_CMD_TYPE_COUNT; |
| cmd.cmd_size = sizeof(int); |
| |
| if ((percent[1]) == '*') { |
| precision = va_arg(args, int); |
| percent++; |
| } else { |
| while (isdigit(percent[1])) { |
| precision = 10 * precision + (ch - '0'); |
| percent++; |
| } |
| if (precision > 1024) precision = 1024; |
| } |
| _os_log_encode_arg(ob, &cmd, &precision); |
| hdr.hdr_cmd_cnt++; |
| break; |
| |
| case '-': // left-align |
| case '+': // force sign |
| case ' ': // prefix non-negative with space |
| case '#': // alternate |
| case '\'': // group by thousands |
| break; |
| |
| case '{': // annotated symbols |
| for (const char *curr2 = percent + 1; (ch = (*curr2)) != 0; curr2++) { |
| if (ch == '}') { |
| if (strncmp(percent + 1, "private", MIN(curr2 - percent - 1, 7)) == 0) { |
| hdr.hdr_flags |= OSLF_HDR_FLAG_HAS_PRIVATE; |
| cmd.cmd_flags |= OSLF_CMD_FLAG_PRIVATE; |
| } else if (strncmp(percent + 1, "public", MIN(curr2 - percent - 1, 6)) == 0) { |
| cmd.cmd_flags |= OSLF_CMD_FLAG_PUBLIC; |
| } |
| percent = curr2; |
| break; |
| } |
| } |
| break; |
| |
| #define encode_smallint(ty) ({ \ |
| int __var = va_arg(args, int); \ |
| cmd.cmd_size = sizeof(__var); \ |
| _os_log_encode_arg(ob, &cmd, &__var); \ |
| hdr.hdr_cmd_cnt++; }) |
| |
| #define encode(ty) ({ \ |
| ty __var = va_arg(args, ty); \ |
| cmd.cmd_size = sizeof(__var); \ |
| _os_log_encode_arg(ob, &cmd, &__var); \ |
| hdr.hdr_cmd_cnt++; }) |
| |
| /* fixed types */ |
| case 'c': // char |
| case 'd': // integer |
| case 'i': // integer |
| case 'o': // octal |
| case 'u': // unsigned |
| case 'x': // hex |
| case 'X': // upper-hex |
| cmd.cmd_type = OSLF_CMD_TYPE_SCALAR; |
| switch (type) { |
| case T_CHAR: encode_smallint(char); break; |
| case T_SHORT: encode_smallint(short); break; |
| case T_INT: encode(int); break; |
| case T_LONG: encode(long); break; |
| case T_LONGLONG: encode(long long); break; |
| case T_SIZE: encode(size_t); break; |
| case T_INTMAX: encode(intmax_t); break; |
| case T_PTRDIFF: encode(ptrdiff_t); break; |
| default: return false; |
| } |
| done = true; |
| break; |
| |
| case 'P': // pointer data |
| if (precision > 0) { // only encode a pointer if we have been given a length |
| hdr.hdr_flags |= OSLF_HDR_FLAG_HAS_NON_SCALAR; |
| cmd.cmd_type = OSLF_CMD_TYPE_DATA; |
| cmd.cmd_size = precision; |
| void *p = va_arg(args, void *); |
| _os_log_encode_arg(ob, &cmd, p); |
| hdr.hdr_cmd_cnt++; |
| precision = 0; |
| done = true; |
| } |
| break; |
| |
| case 'L': // long double |
| long_double = true; |
| break; |
| |
| case 'a': case 'A': case 'e': case 'E': // floating types |
| case 'f': case 'F': case 'g': case 'G': |
| cmd.cmd_type = OSLF_CMD_TYPE_SCALAR; |
| if (long_double) { |
| encode(long double); |
| } else { |
| encode(double); |
| } |
| done = true; |
| break; |
| |
| #if 0 |
| case 'C': // wide-char |
| value.type.wch = va_arg(args, wint_t); |
| _os_log_encode_arg(&value.type.wch, sizeof(value.type.wch), OS_LOG_BUFFER_VALUE_TYPE_SCALAR, flags, context); |
| done = true; |
| break; |
| #endif |
| |
| #if 0 |
| // String types get sent from Swift as NSString objects. |
| case 's': // string |
| value.type.pch = va_arg(args, char *); |
| context->buffer->flags |= OS_LOG_BUFFER_HAS_NON_SCALAR; |
| _os_log_encode_arg(&value.type.pch, sizeof(value.type.pch), OS_LOG_BUFFER_VALUE_TYPE_STRING, flags, context); |
| prec = 0; |
| done = true; |
| break; |
| #endif |
| |
| case '@': // CFTypeRef aka NSObject * |
| hdr.hdr_flags |= OSLF_HDR_FLAG_HAS_NON_SCALAR; |
| cmd.cmd_type = OSLF_CMD_TYPE_OBJECT; |
| encode(void *); |
| done = true; |
| break; |
| |
| case 'm': |
| cmd.cmd_type = OSLF_CMD_TYPE_SCALAR; |
| cmd.cmd_size = sizeof(int); |
| _os_log_encode_arg(ob, &cmd, &saved_errno); |
| hdr.hdr_cmd_cnt++; |
| done = true; |
| break; |
| |
| default: |
| if (isdigit(ch)) { // [0-9] |
| continue; |
| } |
| done = true; |
| break; |
| } |
| |
| if (done) { |
| percent = strchr(percent, '%'); // Find next format |
| break; |
| } |
| } |
| } else { |
| percent = strchr(percent+1, '%'); // Find next format after %% |
| } |
| } |
| *(os_log_fmt_hdr_t)buf = hdr; |
| return true; |
| } |
| |
| #undef encode_smallint |
| #undef encode |
| |
| __attribute__((__visibility__("default"))) |
| void |
| _swift_os_log(void *dso, void *retaddr, os_log_t oslog, os_log_type_t type, const char *format, va_list args) |
| { |
| int saved_errno = errno; // %m |
| char buf[OS_LOG_FMT_BUF_SIZE]; |
| os_trace_blob_s ob = { |
| .ob_s = buf, |
| .ob_size = OS_LOG_FMT_BUF_SIZE, |
| .ob_binary = true |
| }; |
| |
| if (_os_log_encode(buf, format, args, saved_errno, &ob)) { |
| // Use os_log_pack_send where available. |
| if (os_log_pack_send) { |
| size_t sz = _os_log_pack_size(ob.ob_len); |
| union { os_log_pack_s pack; uint8_t buf[OS_LOG_FMT_BUF_SIZE + sizeof(os_log_pack_s)]; } u; |
| // _os_log_encode has already packed `saved_errno` into a OSLF_CMD_TYPE_SCALAR command |
| // as the OSLF_CMD_TYPE_ERRNO does not deploy backwards, so passes zero for errno here. |
| uint8_t *ptr = _os_log_pack_fill(&u.pack, sz, 0, dso, format); |
| u.pack.olp_pc = retaddr; |
| memcpy(ptr, buf, ob.ob_len); |
| os_log_pack_send(&u.pack, oslog, type); |
| } else { |
| _os_log_impl(dso, oslog, type, format, (uint8_t *)buf, ob.ob_len); |
| } |
| } |
| } |
| |
| __attribute__((__visibility__("default"))) |
| void * |
| _swift_os_log_return_address(void) |
| { |
| return __builtin_return_address(1); |
| } |
| |