1 /* Copyright (c) 2012-2013 Apple Inc. All rights reserved.
3 * @APPLE_LICENSE_HEADER_START@
5 * This file contains Original Code and/or Modifications of Original Code
6 * as defined in and that are subject to the Apple Public Source License
7 * Version 2.0 (the 'License'). You may not use this file except in
8 * compliance with the License. Please obtain a copy of the License at
9 * http://www.opensource.apple.com/apsl/ and read it before using this
12 * The Original Code and all software distributed under the License are
13 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
17 * Please see the License for the specific language governing rights and
18 * limitations under the License.
20 * @APPLE_LICENSE_HEADER_END@
27 #include <mach/mach_time.h>
28 #include <os/alloc_once_private.h>
29 #include <os/assumes.h>
30 #include <os/debug_private.h>
41 struct os_debug_log_globals_s
{
43 os_redirect_t redirect
;
45 bool prepend_timestamp
: 1;
49 // If user picked a filename, use it and only it.
50 // Otherwise, first try /var/tmp, then $TMPDIR, then give up.
53 _os_debug_log_open_file(const char *suggestion
)
56 return open(suggestion
, O_WRONLY
| O_APPEND
| O_CREAT
| O_NOFOLLOW
|
57 O_EXCL
| O_CLOEXEC
, 0644);
61 char filename
[PATH_MAX
];
64 snprintf(filename
, sizeof(filename
), "os_debug_log.%s.%d.log", getprogname(),
67 strlcpy(path
, "/var/tmp/", sizeof(path
));
68 if (access(path
, W_OK
) == 0) {
69 strlcat(path
, filename
, sizeof(path
));
70 fd
= open(path
, O_WRONLY
| O_APPEND
| O_CREAT
| O_NOFOLLOW
| O_EXCL
|
77 const char *tmpdir
= getenv("TMPDIR");
79 strlcpy(path
, tmpdir
, sizeof(path
));
80 if (access(path
, W_OK
) == 0) {
81 strlcat(path
, filename
, sizeof(path
));
82 fd
= open(path
, O_WRONLY
| O_APPEND
| O_CREAT
| O_NOFOLLOW
|
83 O_EXCL
| O_CLOEXEC
, 0644);
95 _os_debug_log_init(void *globals
)
97 struct os_debug_log_globals_s
*g
= globals
;
99 g
->errors_only
= false;
101 g
->redirect
= dlsym(RTLD_MAIN_ONLY
, "_os_debug_log_redirect_func");
103 // This is a bit of a hack. LIBDISPATCH_LOG is part of dispatch's API.
104 // But now all dispatch logging goes through os_debug_log. So we have to
105 // recognize this env var here in Libc.
106 // rdar://problem/11685359 tracks deprecating LIBDISPATCH_LOG from dispatch.
107 char *e
= getenv("LIBDISPATCH_LOG");
109 e
= getenv("OS_DEBUG_LOG");
112 // Default log destination
113 if (!e
|| strcmp(e
, "YES") == 0) {
121 if (strcmp(e
, "NO") == 0) {
123 g
->errors_only
= true;
124 } else if (strcmp(e
, "syslog") == 0) {
126 } else if (strcmp(e
, "stderr") == 0) {
127 g
->logfd
= STDERR_FILENO
;
128 } else if (strcmp(e
, "stdout") == 0) {
129 g
->logfd
= STDOUT_FILENO
;
130 } else if (strcmp(e
, "file") == 0) {
131 g
->logfd
= _os_debug_log_open_file(NULL
);
132 if (g
->logfd
== -1) {
133 g
->errors_only
= true;
136 g
->logfd
= _os_debug_log_open_file(e
);
137 if (g
->logfd
== -1) {
138 g
->errors_only
= true;
142 // From now on, g->logfd == -1 means syslog; anything >= 0 is the
143 // fd to use. Remember that file descriptor 0 is a perfectly valid
144 // value for open() to return if you closed (or never had) stdin.
146 // Timestamp every log message if logging directly to file and no
147 // redirector is set up.
148 if (g
->logfd
>= 0 && !g
->redirect
) {
149 g
->prepend_timestamp
= true;
152 gettimeofday(&tv
, NULL
);
154 g
->start
= mach_absolute_time();
157 "=== os_debug_log log file opened for %s[%u] at %ld.%06u",
158 getprogname(), getpid(),
159 tv
.tv_sec
, tv
.tv_usec
);
160 if (g
->prepend_timestamp
) {
161 mach_timebase_info_data_t tbi
;
162 if (mach_timebase_info(&tbi
) == 0) {
163 dprintf(g
->logfd
, " [ns=ticks*%u/%u]",
164 tbi
.numer
, tbi
.denom
);
167 dprintf(g
->logfd
, " ===\n");
171 #ifndef OS_ALLOC_ONCE_KEY_OS_DEBUG_LOG
172 #define OS_ALLOC_ONCE_KEY_OS_DEBUG_LOG OS_ALLOC_ONCE_KEY_OS_TRACE
175 static inline OS_CONST
176 struct os_debug_log_globals_s
*
177 os_debug_log_globals(void)
179 return (struct os_debug_log_globals_s
*)
180 os_alloc_once(OS_ALLOC_ONCE_KEY_OS_DEBUG_LOG
,
181 sizeof(struct os_debug_log_globals_s
),
185 static __attribute__((always_inline
))
187 _os_debug_log_ticks_since_start(void)
189 return mach_absolute_time() - os_debug_log_globals()->start
;
192 // False on error writing to file
195 _os_debug_log_write_fd(int level
__attribute__((__unused__
)),
198 size_t len
= strlen(str
);
200 str
[len
++] = '\n'; // overwrite null - don't use str*() anymore
202 ssize_t rc
, wlen
= 0;
204 rc
= write(fd
, &str
[wlen
], len
- wlen
);
205 if (os_slowpath(rc
== -1)) {
213 } while (wlen
< len
);
218 static __attribute__((__noinline__
))
220 _os_debug_log_write_error(void)
223 const char *pfx
= "os_debug_log() :";
224 size_t pfxlen
= strlen(pfx
);
226 strlcpy(err_str
, pfx
, sizeof(err_str
));
227 strerror_r(errno
, err_str
+pfxlen
, sizeof(err_str
)-pfxlen
);
228 _simple_asl_log(LOG_ERR
, "com.apple.os_debug_log", err_str
);
233 _os_debug_log_write(int level
, char *str
)
235 int fd
= os_debug_log_globals()->logfd
;
236 os_redirect_t rdr
= os_debug_log_globals()->redirect
;
237 // true = redirect has fully handled, don't log
238 if (os_slowpath(rdr
) && os_fastpath(rdr(str
))) {
241 if (os_slowpath(fd
>= 0)) {
242 if (os_fastpath(_os_debug_log_write_fd(level
, str
, fd
))) {
245 _os_debug_log_write_error();
246 os_debug_log_globals()->logfd
= -1;
247 // Don't return, fall out to syslog().
250 _simple_asl_log(level
, "com.apple.os_debug_log", str
);
253 static __attribute__((always_inline
))
255 _os_debug_logv(int level
, const char *msg
, va_list ap
)
257 if (os_slowpath((bool)os_debug_log_globals()->errors_only
) && level
> LOG_ERR
) {
258 // more important = lower integer
264 len
= vasprintf(&buf
, msg
, ap
);
270 // The os_debug_log macros prepend many spaces to the format string.
271 // Overwrite them with a timestamp, *or* skip them.
272 const size_t pfxlen
= strlen(_OS_DEBUG_LOG_PREFIX
);
273 const size_t timelen
= 16;
274 __OS_COMPILETIME_ASSERT__(pfxlen
>= timelen
);
276 if (os_fastpath(len
> pfxlen
)) {
277 if (os_slowpath((bool)os_debug_log_globals()->prepend_timestamp
)) {
278 char tmp
= buf
[timelen
];
279 snprintf(buf
, timelen
+ 1, "%16llu", _os_debug_log_ticks_since_start());
280 buf
[timelen
] = tmp
; // snprintf's null
286 _os_debug_log_write(level
, buf
);
291 _os_debug_log_error_str(char *msg
)
293 _os_debug_log_write(LOG_ERR
, msg
);
296 OS_FORMAT_PRINTF(1, 2)
298 _os_debug_log(const char *msg
, ...)
302 _os_debug_logv(LOG_DEBUG
, msg
, ap
);