1 /* Copyright (c) 2012 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>
40 // Move this to trace.h when it's removed from assumes.h
41 typedef bool (*os_redirect_t
)(const char *);
43 struct os_trace_globals_s
{
45 os_redirect_t redirect
;
47 bool prepend_timestamp
: 1;
51 // If user picked a filename, use it and only it.
52 // Otherwise, first try /var/tmp, then $TMPDIR, then give up.
55 _os_trace_open_file(const char *suggestion
)
58 return open(suggestion
, O_WRONLY
| O_APPEND
| O_CREAT
| O_NOFOLLOW
|
59 O_EXCL
| O_CLOEXEC
, 0644);
63 char filename
[PATH_MAX
];
66 snprintf(filename
, sizeof(filename
), "os_trace.%s.%d.log", getprogname(),
69 strlcpy(path
, "/var/tmp/", sizeof(path
));
70 if (access(path
, W_OK
) == 0) {
71 strlcat(path
, filename
, sizeof(path
));
72 fd
= open(path
, O_WRONLY
| O_APPEND
| O_CREAT
| O_NOFOLLOW
| O_EXCL
|
79 const char *tmpdir
= getenv("TMPDIR");
81 strlcpy(path
, tmpdir
, sizeof(path
));
82 if (access(path
, W_OK
) == 0) {
83 strlcat(path
, filename
, sizeof(path
));
84 fd
= open(path
, O_WRONLY
| O_APPEND
| O_CREAT
| O_NOFOLLOW
|
85 O_EXCL
| O_CLOEXEC
, 0644);
97 _os_trace_init(void *globals
)
99 struct os_trace_globals_s
*g
= globals
;
101 g
->errors_only
= false;
103 g
->redirect
= dlsym(RTLD_MAIN_ONLY
, "_os_trace_redirect_func");
105 // This is a bit of a hack. LIBDISPATCH_LOG is part of dispatch's API.
106 // But now all dispatch logging goes through os_trace. So we have to
107 // recognize this env var here in Libc.
108 // rdar://problem/11685359 tracks deprecating LIBDISPATCH_LOG from dispatch.
109 char *e
= getenv("LIBDISPATCH_LOG");
111 e
= getenv("OS_TRACE");
114 // Default log destination
115 if (!e
|| strcmp(e
, "YES") == 0) {
123 if (strcmp(e
, "NO") == 0) {
125 g
->errors_only
= true;
126 } else if (strcmp(e
, "syslog") == 0) {
128 } else if (strcmp(e
, "stderr") == 0) {
129 g
->logfd
= STDERR_FILENO
;
130 } else if (strcmp(e
, "stdout") == 0) {
131 g
->logfd
= STDOUT_FILENO
;
132 } else if (strcmp(e
, "file") == 0) {
133 g
->logfd
= _os_trace_open_file(NULL
);
134 if (g
->logfd
== -1) {
135 g
->errors_only
= true;
138 g
->logfd
= _os_trace_open_file(e
);
139 if (g
->logfd
== -1) {
140 g
->errors_only
= true;
144 // From now on, g->logfd == -1 means syslog; anything >= 0 is the
145 // fd to use. Remember that file descriptor 0 is a perfectly valid
146 // value for open() to return if you closed (or never had) stdin.
148 // Timestamp every log message if logging directly to file and no
149 // redirector is set up.
150 if (g
->logfd
>= 0 && !g
->redirect
) {
151 g
->prepend_timestamp
= true;
154 gettimeofday(&tv
, NULL
);
156 g
->start
= mach_absolute_time();
159 "=== os_trace log file opened for %s[%u] at %ld.%06u",
160 getprogname(), getpid(),
161 tv
.tv_sec
, tv
.tv_usec
);
162 if (g
->prepend_timestamp
) {
163 mach_timebase_info_data_t tbi
;
164 if (mach_timebase_info(&tbi
) == 0) {
165 dprintf(g
->logfd
, " [ns=ticks*%u/%u]",
166 tbi
.numer
, tbi
.denom
);
169 dprintf(g
->logfd
, " ===\n");
173 static inline __OS_CONST
174 struct os_trace_globals_s
*
175 os_trace_globals(void)
177 return (struct os_trace_globals_s
*)
178 os_alloc_once(OS_ALLOC_ONCE_KEY_OS_TRACE
,
179 sizeof(struct os_trace_globals_s
),
183 static __attribute__((always_inline
))
185 _os_trace_ticks_since_start(void)
187 return mach_absolute_time() - os_trace_globals()->start
;
190 // False on error writing to file
193 _os_trace_write_fd(int level
__attribute__((__unused__
)),
196 size_t len
= strlen(str
);
198 str
[len
++] = '\n'; // overwrite null - don't use str*() anymore
200 ssize_t rc
, wlen
= 0;
202 rc
= write(fd
, &str
[wlen
], len
- wlen
);
203 if (os_slowpath(rc
== -1)) {
211 } while (wlen
< len
);
216 static __attribute__((__noinline__
))
218 _os_trace_write_error(void)
221 const char *pfx
= "os_trace() :";
222 size_t pfxlen
= strlen(pfx
);
224 strlcpy(err_str
, pfx
, sizeof(err_str
));
225 strerror_r(errno
, err_str
+pfxlen
, sizeof(err_str
)-pfxlen
);
226 _simple_asl_log(LOG_ERR
, "com.apple.os_trace", err_str
);
231 _os_trace_write(int level
, char *str
)
233 int fd
= os_trace_globals()->logfd
;
234 os_redirect_t rdr
= os_trace_globals()->redirect
;
235 // true = redirect has fully handled, don't log
236 if (os_slowpath(rdr
) && os_fastpath(rdr(str
))) {
239 if (os_slowpath(fd
>= 0)) {
240 if (os_fastpath(_os_trace_write_fd(level
, str
, fd
))) {
243 _os_trace_write_error();
244 os_trace_globals()->logfd
= -1;
245 // Don't return, fall out to syslog().
248 _simple_asl_log(level
, "com.apple.os_trace", str
);
251 static __attribute__((always_inline
))
253 _os_tracev(int level
, const char *msg
, va_list ap
)
255 if (os_slowpath(os_trace_globals()->errors_only
) && level
> LOG_ERR
) {
256 // more important = lower integer
262 len
= vasprintf(&buf
, msg
, ap
);
268 // The os_trace macros prepend many spaces to the format string.
269 // Overwrite them with a timestamp, *or* skip them.
270 const size_t pfxlen
= strlen(_OS_TRACE_PREFIX
);
271 const size_t timelen
= 16;
272 __OS_COMPILETIME_ASSERT__(pfxlen
>= timelen
);
274 if (os_fastpath(len
> pfxlen
)) {
275 if (os_slowpath(os_trace_globals()->prepend_timestamp
)) {
276 char tmp
= buf
[timelen
];
277 snprintf(buf
, timelen
+ 1, "%16llu", _os_trace_ticks_since_start());
278 buf
[timelen
] = tmp
; // snprintf's null
284 _os_trace_write(level
, buf
);
289 _os_trace_error_str(char *msg
)
291 _os_trace_write(LOG_ERR
, msg
);
294 __OS_PRINTFLIKE(1, 2)
296 _os_trace(const char *msg
, ...)
300 _os_tracev(LOG_DEBUG
, msg
, ap
);