mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
- When a count was part of the format string, we looked at the wrong character when forming the count. - The OSLF_CMD_TYPE_DATA command type expects a pointer preceded by a OSLF_CMD_TYPE_COUNT command, not 'count' bytes inline. Fixes <rdar://problem/38080623>.
340 lines
10 KiB
Objective-C
340 lines
10 KiB
Objective-C
//===----------------------------------------------------------------------===//
|
|
//
|
|
// 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 + (percent[1] - '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 = sizeof(void *);
|
|
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);
|
|
}
|
|
|