mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
423 lines
12 KiB
Plaintext
423 lines
12 KiB
Plaintext
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See http://swift.org/LICENSE.txt for license information
|
|
// See http://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>
|
|
|
|
#ifndef os_fastpath
|
|
#define os_fastpath(x) ((__typeof__(x))OS_EXPECT((long)(x), ~0l))
|
|
#endif
|
|
#ifndef os_slowpath
|
|
#define os_slowpath(x) ((__typeof__(x))OS_EXPECT((long)(x), 0l))
|
|
#endif
|
|
#ifndef os_likely
|
|
#define os_likely(x) OS_EXPECT(!!(x), 1)
|
|
#endif
|
|
#ifndef os_unlikely
|
|
#define os_unlikely(x) OS_EXPECT(!!(x), 0)
|
|
#endif
|
|
|
|
#ifndef MIN
|
|
#define MIN(a, b) (((a)<(b))?(a):(b))
|
|
#endif
|
|
|
|
#define OST_FORMAT_MAX_STRING_SIZE 1024
|
|
|
|
#define OS_LOG_PRIVACY_OPTION_DEFAULT 0
|
|
#define OS_LOG_PRIVACY_OPTION_PRIVATE 1
|
|
#define OS_LOG_PRIVACY_OPTION_PUBLIC 2
|
|
|
|
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_value_type, uint8_t,
|
|
OS_LOG_BUFFER_VALUE_TYPE_SCALAR = 0,
|
|
OS_LOG_BUFFER_VALUE_TYPE_COUNT = 1,
|
|
OS_LOG_BUFFER_VALUE_TYPE_STRING = 2,
|
|
OS_LOG_BUFFER_VALUE_TYPE_POINTER = 3,
|
|
OS_LOG_BUFFER_VALUE_TYPE_OBJECT = 4,
|
|
);
|
|
|
|
OS_ENUM(os_log_value_subtype, uint8_t,
|
|
OS_LOG_BUFFER_VALUE_SUBTYPE_NONE = 0,
|
|
OS_LOG_BUFFER_VALUE_SUBTYPE_INTEGER = 1,
|
|
OS_LOG_BUFFER_VALUE_SUBTYPE_FLOAT = 2,
|
|
);
|
|
|
|
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,
|
|
};
|
|
|
|
union os_log_format_types_u {
|
|
uint16_t u16;
|
|
uint32_t u32;
|
|
uint64_t u64;
|
|
char ch;
|
|
short s;
|
|
int i;
|
|
void *p;
|
|
char *pch;
|
|
wchar_t wch;
|
|
wchar_t *pwch;
|
|
size_t z;
|
|
intmax_t im;
|
|
ptrdiff_t pd;
|
|
long l;
|
|
long long ll;
|
|
double d;
|
|
float f;
|
|
long double ld;
|
|
};
|
|
|
|
typedef struct os_log_format_value_s {
|
|
union os_log_format_types_u type;
|
|
os_log_value_type_t ctype;
|
|
uint16_t size;
|
|
} *os_log_format_value_t;
|
|
|
|
typedef struct os_log_buffer_value_s {
|
|
#define OS_LOG_CONTENT_FLAG_PRIVATE 0x1
|
|
#define OS_LOG_CONTENT_FLAG_PUBLIC 0x2
|
|
uint8_t flags : 4;
|
|
os_log_value_type_t type : 4;
|
|
uint8_t size;
|
|
uint8_t value[];
|
|
} *os_log_buffer_value_t;
|
|
|
|
typedef struct os_log_buffer_s {
|
|
#define OS_LOG_BUFFER_HAS_PRIVATE 0x1
|
|
#define OS_LOG_BUFFER_HAS_NON_SCALAR 0x2
|
|
#define OS_LOG_BUFFER_MAX_SIZE 1024
|
|
uint8_t flags;
|
|
uint8_t arg_cnt;
|
|
uint8_t content[];
|
|
} *os_log_buffer_t;
|
|
|
|
typedef struct os_log_buffer_context_s {
|
|
os_log_t log;
|
|
os_log_buffer_t buffer;
|
|
|
|
// sizes and offsets
|
|
uint16_t content_off; // offset into buffer->content
|
|
uint16_t content_sz; // size not including the header
|
|
uint8_t arg_idx;
|
|
} *os_log_buffer_context_t;
|
|
|
|
static bool
|
|
_os_log_encode_arg(const void *arg, uint16_t arg_len, os_log_value_type_t ctype, uint8_t flags, os_log_buffer_context_t context)
|
|
{
|
|
os_log_buffer_value_t content = (os_log_buffer_value_t) &context->buffer->content[context->content_off];
|
|
size_t content_sz = sizeof(*content) + arg_len;
|
|
|
|
content->type = ctype;
|
|
content->flags = flags;
|
|
|
|
switch (ctype) {
|
|
case OS_LOG_BUFFER_VALUE_TYPE_COUNT:
|
|
case OS_LOG_BUFFER_VALUE_TYPE_SCALAR:
|
|
if ((context->content_off + content_sz) > context->content_sz) {
|
|
return false;
|
|
}
|
|
|
|
memcpy(content->value, arg, arg_len);
|
|
content->size = arg_len;
|
|
context->content_off += content_sz;
|
|
break;
|
|
|
|
case OS_LOG_BUFFER_VALUE_TYPE_STRING:
|
|
case OS_LOG_BUFFER_VALUE_TYPE_POINTER:
|
|
case OS_LOG_BUFFER_VALUE_TYPE_OBJECT:
|
|
memcpy(content->value, arg, arg_len);
|
|
context->buffer->flags |= OS_LOG_BUFFER_HAS_NON_SCALAR;
|
|
content->size = arg_len;
|
|
context->content_off += content_sz;
|
|
break;
|
|
|
|
}
|
|
|
|
if (content->flags & OS_LOG_CONTENT_FLAG_PRIVATE) {
|
|
context->buffer->flags |= OS_LOG_BUFFER_HAS_PRIVATE;
|
|
}
|
|
|
|
context->arg_idx++;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
_os_log_encode(const char *format, va_list args, int saved_errno, os_log_buffer_context_t context)
|
|
{
|
|
const char *percent = strchr(format, '%');
|
|
|
|
while (percent != NULL) {
|
|
++percent;
|
|
if (percent[0] != '%') {
|
|
struct os_log_format_value_s value;
|
|
uint8_t flags = 0;
|
|
int type = T_INT;
|
|
bool long_double = false;
|
|
int prec = 0;
|
|
char ch;
|
|
|
|
for (bool done = false; !done; percent++) {
|
|
switch (ch = percent[0]) {
|
|
/* type of types or other */
|
|
case 'l': // longer
|
|
type++;
|
|
break;
|
|
|
|
case 'h': // shorter
|
|
type--;
|
|
break;
|
|
|
|
case 'z':
|
|
type = T_SIZE;
|
|
break;
|
|
|
|
case 'j':
|
|
type = T_INTMAX;
|
|
break;
|
|
|
|
case 't':
|
|
type = T_PTRDIFF;
|
|
break;
|
|
|
|
case '.': // precision
|
|
if ((percent[1]) == '*') {
|
|
prec = va_arg(args, int);
|
|
_os_log_encode_arg(&prec, sizeof(prec), OS_LOG_BUFFER_VALUE_TYPE_COUNT, flags, context);
|
|
percent++;
|
|
continue;
|
|
} else {
|
|
// we have to read the precision and do the right thing
|
|
const char *fmt = percent + 1;
|
|
prec = 0;
|
|
while (isdigit(ch = *fmt++)) {
|
|
prec = 10 * prec + (ch - '0');
|
|
}
|
|
|
|
if (prec > 1024) {
|
|
prec = 1024;
|
|
}
|
|
|
|
_os_log_encode_arg(&prec, sizeof(prec), OS_LOG_BUFFER_VALUE_TYPE_COUNT, flags, context);
|
|
}
|
|
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) {
|
|
flags |= OS_LOG_CONTENT_FLAG_PRIVATE;
|
|
}
|
|
percent = curr2;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
/* fixed types */
|
|
case 'd': // integer
|
|
case 'i': // integer
|
|
case 'o': // octal
|
|
case 'u': // unsigned
|
|
case 'x': // hex
|
|
case 'X': // upper-hex
|
|
switch (type) {
|
|
case T_CHAR:
|
|
value.type.ch = va_arg(args, int);
|
|
_os_log_encode_arg(&value.type.ch, sizeof(value.type.ch), OS_LOG_BUFFER_VALUE_TYPE_SCALAR, flags, context);
|
|
break;
|
|
|
|
case T_SHORT:
|
|
value.type.s = va_arg(args, int);
|
|
_os_log_encode_arg(&value.type.s, sizeof(value.type.s), OS_LOG_BUFFER_VALUE_TYPE_SCALAR, flags, context);
|
|
break;
|
|
|
|
case T_INT:
|
|
value.type.i = va_arg(args, int);
|
|
_os_log_encode_arg(&value.type.i, sizeof(value.type.i), OS_LOG_BUFFER_VALUE_TYPE_SCALAR, flags, context);
|
|
break;
|
|
|
|
case T_LONG:
|
|
value.type.l = va_arg(args, long);
|
|
_os_log_encode_arg(&value.type.l, sizeof(value.type.l), OS_LOG_BUFFER_VALUE_TYPE_SCALAR, flags, context);
|
|
break;
|
|
|
|
case T_LONGLONG:
|
|
value.type.ll = va_arg(args, long long);
|
|
_os_log_encode_arg(&value.type.ll, sizeof(value.type.ll), OS_LOG_BUFFER_VALUE_TYPE_SCALAR, flags, context);
|
|
break;
|
|
|
|
case T_SIZE:
|
|
value.type.z = va_arg(args, size_t);
|
|
_os_log_encode_arg(&value.type.z, sizeof(value.type.z), OS_LOG_BUFFER_VALUE_TYPE_SCALAR, flags, context);
|
|
break;
|
|
|
|
case T_INTMAX:
|
|
value.type.im = va_arg(args, intmax_t);
|
|
_os_log_encode_arg(&value.type.im, sizeof(value.type.im), OS_LOG_BUFFER_VALUE_TYPE_SCALAR, flags, context);
|
|
break;
|
|
|
|
case T_PTRDIFF:
|
|
value.type.pd = va_arg(args, ptrdiff_t);
|
|
_os_log_encode_arg(&value.type.pd, sizeof(value.type.pd), OS_LOG_BUFFER_VALUE_TYPE_SCALAR, flags, context);
|
|
break;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
done = true;
|
|
break;
|
|
|
|
case 'P': // pointer data
|
|
if (prec > 0) { // only encode a pointer if we have been given a length
|
|
context->buffer->flags |= OS_LOG_BUFFER_HAS_NON_SCALAR;
|
|
value.type.p = va_arg(args, void *);
|
|
|
|
_os_log_encode_arg(value.type.p, prec, OS_LOG_BUFFER_VALUE_TYPE_POINTER, flags, context);
|
|
prec = 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':
|
|
if (long_double) {
|
|
value.type.ld = va_arg(args, long double);
|
|
_os_log_encode_arg(&value.type.ld, sizeof(value.type.ld), OS_LOG_BUFFER_VALUE_TYPE_SCALAR, flags, context);
|
|
} else {
|
|
value.type.d = va_arg(args, double);
|
|
_os_log_encode_arg(&value.type.d, sizeof(value.type.d), OS_LOG_BUFFER_VALUE_TYPE_SCALAR, flags, context);
|
|
}
|
|
done = true;
|
|
break;
|
|
|
|
case 'c': // char
|
|
value.type.ch = va_arg(args, int);
|
|
_os_log_encode_arg(&value.type.ch, sizeof(value.type.ch), OS_LOG_BUFFER_VALUE_TYPE_SCALAR, flags, context);
|
|
done = true;
|
|
break;
|
|
|
|
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;
|
|
|
|
#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 *
|
|
value.type.p = va_arg(args, void *);
|
|
context->buffer->flags |= OS_LOG_BUFFER_HAS_NON_SCALAR;
|
|
_os_log_encode_arg(&value.type.p, sizeof(value.type.p), OS_LOG_BUFFER_VALUE_TYPE_OBJECT, flags, context);
|
|
done = true;
|
|
break;
|
|
|
|
case 'm':
|
|
value.type.i = saved_errno;
|
|
_os_log_encode_arg(&value.type.i, sizeof(value.type.i), OS_LOG_BUFFER_VALUE_TYPE_SCALAR, flags, context);
|
|
done = true;
|
|
break;
|
|
|
|
default:
|
|
if (isdigit(ch)) { // [0-9]
|
|
continue;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
if (done) {
|
|
percent = strchr(percent, '%'); // Find next format
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
percent = strchr(percent+1, '%'); // Find next format after %%
|
|
}
|
|
}
|
|
|
|
context->buffer->arg_cnt = context->arg_idx;
|
|
context->content_sz = context->content_off;
|
|
context->arg_idx = context->content_off = 0;
|
|
|
|
return true;
|
|
}
|
|
|
|
#include "swift/Runtime/Config.h"
|
|
|
|
SWIFT_CC(swift) __attribute__((__visibility__("default")))
|
|
extern "C" void
|
|
_swift_os_log(void *dso, os_log_t oslog, os_log_type_t type, const char *format, va_list args)
|
|
{
|
|
struct os_log_buffer_context_s context = { 0, 0, 0, 0, 0 };
|
|
os_log_buffer_t buffer = (os_log_buffer_t)alloca(OS_LOG_BUFFER_MAX_SIZE);
|
|
int save_errno = errno; // %m
|
|
|
|
memset(buffer, 0, OS_LOG_BUFFER_MAX_SIZE);
|
|
|
|
context.buffer = buffer;
|
|
context.content_sz = OS_LOG_BUFFER_MAX_SIZE - sizeof(*buffer);
|
|
|
|
if (_os_log_encode(format, args, save_errno, &context)) {
|
|
_os_log_impl(dso, oslog, type, format, (uint8_t *)buffer, context.content_sz);
|
|
}
|
|
}
|
|
|
|
SWIFT_CC(swift) __attribute__((__visibility__("default")))
|
|
extern "C" os_log_t
|
|
_swift_os_log_default(void) {
|
|
return OS_LOG_DEFAULT;
|
|
}
|