]> git.saurik.com Git - apple/dyld.git/blob - interlinked-dylibs/Logging.cpp
296b5514488bd15a5ba932c463dd2de7a7b99983
[apple/dyld.git] / interlinked-dylibs / Logging.cpp
1 /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
2 *
3 * Copyright (c) 2016 Apple Inc. All rights reserved.
4 *
5 * @APPLE_LICENSE_HEADER_START@
6 *
7 * This file contains Original Code and/or Modifications of Original Code
8 * as defined in and that are subject to the Apple Public Source License
9 * Version 2.0 (the 'License'). You may not use this file except in
10 * compliance with the License. Please obtain a copy of the License at
11 * http://www.opensource.apple.com/apsl/ and read it before using this
12 * file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
19 * Please see the License for the specific language governing rights and
20 * limitations under the License.
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24
25 #include <dispatch/dispatch.h>
26 #include <string.h>
27 #include <stdlib.h>
28 #include <set>
29
30 #include <assert.h>
31 #include "mega-dylib-utils.h"
32
33 #include "Logging.h"
34
35 //const char* kDispatchQueueLogNameKey = "kDispatchQueueLogNameKey";
36 const char* kDispatchWarningArrayKey = "kDispatchWarningArrayKey";
37
38 static dispatch_queue_t log_queue;
39 static dispatch_once_t logQueueInit = 0;
40 static dispatch_queue_t unique_queue;
41 static dispatch_once_t uniqueQueueInit = 0;
42
43 static uint32_t verbose = 0;
44 static bool returnNonZeroIfTerminateCalled = false;
45 static bool terminateCalled = false;
46
47 static const char* warningPrefix = "WARNING: ";
48 static const char* errorPrefix = "ERROR: ";
49
50 LoggingContext::LoggingContext(const std::string& N)
51 : _name(N)
52 , _tainted(false)
53 {
54 }
55
56 LoggingContext::LoggingContext(const std::string& N, WarningTargets T)
57 : _name(N)
58 , _warnings(T)
59 , _tainted(false)
60 {
61 }
62
63 void LoggingContext::taint()
64 {
65 _tainted = true;
66 }
67
68 bool LoggingContext::isTainted()
69 {
70 return _tainted;
71 }
72
73 const std::string& LoggingContext::name()
74 {
75 return _name;
76 }
77
78 const WarningTargets& LoggingContext::targets()
79 {
80 return _warnings;
81 }
82
83
84
85 pthread_key_t getLoggingContextKey(void) {
86 static pthread_key_t logContextKey;
87 static dispatch_once_t logContextToken;
88 dispatch_once(&logContextToken, ^{
89 pthread_key_create(&logContextKey, nullptr);
90 });
91 return logContextKey;
92 }
93
94
95 void setLoggingContext(std::shared_ptr<LoggingContext>& context)
96 {
97 pthread_setspecific(getLoggingContextKey(), (void*)&context);
98
99 if (context && !context->name().empty()) {
100 pthread_setname_np(context->name().substr(0, MAXTHREADNAMESIZE-1).c_str());
101 }
102 }
103
104 std::shared_ptr<LoggingContext> getLoggingContext()
105 {
106 if (void* val = pthread_getspecific(getLoggingContextKey()))
107 return *((std::shared_ptr<LoggingContext>*)val);
108 return nullptr;
109 }
110
111 void runBody(void* Ctx)
112 {
113 std::unique_ptr<std::function<void(void)>>
114 Body(reinterpret_cast<std::function<void(void)>*>(Ctx));
115 (*Body)();
116 }
117
118 static dispatch_queue_t getLogQueue()
119 {
120 dispatch_once(&logQueueInit, ^{
121 log_queue = dispatch_queue_create("com.apple.dyld.cache.logging", DISPATCH_QUEUE_SERIAL);
122 });
123 return log_queue;
124 }
125
126 void setVerbose(bool level)
127 {
128 verbose = level;
129 }
130
131 void setWarnAnErrorPrefixes(const char* warn, const char* err)
132 {
133 warningPrefix = warn;
134 errorPrefix = err;
135 }
136
137 void setReturnNonZeroOnTerminate()
138 {
139 returnNonZeroIfTerminateCalled = true;
140 }
141
142 void queued_print(FILE* __restrict fd, const char* str)
143 {
144 dispatch_async(getLogQueue(), ^{
145 (void)fprintf(fd, "%s", str);
146 free((void*)str);
147 });
148 }
149
150 #define VLOG(header) \
151 va_list list; \
152 va_start(list, format); \
153 char *temp, *temp2; \
154 vasprintf(&temp, format, list); \
155 auto ctx = getLoggingContext(); \
156 if (ctx && !ctx->name().empty()) { \
157 asprintf(&temp2, "[%s] %s%s\n", ctx->name().c_str(), header, temp); \
158 } else { \
159 asprintf(&temp2, "%s%s\n", header, temp); \
160 } \
161 free(temp); \
162 queued_print(stderr, temp2); \
163 va_end(list);
164
165 void log(const char* __restrict format, ...)
166 {
167 VLOG("");
168 }
169
170 void verboseLog(const char* format, ...)
171 {
172 if (verbose) {
173 VLOG("");
174 }
175 }
176
177 static std::set<std::string> warnings;
178
179 void warning(const char* format, ...)
180 {
181 dispatch_once(&uniqueQueueInit, ^{
182 unique_queue = dispatch_queue_create("com.apple.dyld.cache.logging", DISPATCH_QUEUE_SERIAL);
183 });
184
185 va_list list;
186 va_start(list, format);
187 char* blockTemp;
188 vasprintf(&blockTemp, format, list);
189
190 auto ctx = getLoggingContext();
191 if (ctx) {
192 for (auto& target : ctx->targets().second) {
193 ctx->targets().first->configuration(target.first).architecture(target.second).results.warnings.push_back(blockTemp);
194 }
195 }
196
197 dispatch_sync(unique_queue, ^{
198 if (warnings.count(blockTemp) == 0) {
199 warnings.insert(blockTemp);
200 }
201
202 free(blockTemp);
203 });
204
205 va_end(list);
206 }
207
208 void terminate(const char* format, ...)
209 {
210 VLOG(errorPrefix);
211
212 terminateCalled = true;
213
214 if (ctx) {
215 // We are a work in a logging context, throw
216 throw std::string(temp2);
217 } else {
218 // We are in general handing, let the loggging queue darain and exit
219 dispatch_sync(getLogQueue(), ^{
220 for (auto& warning : warnings) {
221 (void)fprintf(stderr, "%s%s\n", warningPrefix, warning.c_str());
222 }
223 if ( returnNonZeroIfTerminateCalled ) {
224 exit(1);
225 }
226 else {
227 time_t endtime = time(0);
228 (void)fprintf(stderr, "Finished: %s\n", asctime(localtime(&endtime)));
229 (void)fprintf(stderr, "Exiting\n");
230 exit(0);
231 }
232 });
233 }
234
235 // clang can't reason out that we won't hit this due to the dispatch_sync in the exit path
236 __builtin_unreachable();
237 }
238
239 void dumpLogAndExit(bool logFinishTime)
240 {
241 dispatch_async(getLogQueue(), ^{
242 for (auto& warning : warnings) {
243 (void)fprintf(stderr, "%s%s\n", warningPrefix, warning.c_str());
244 }
245 if ( logFinishTime ) {
246 time_t endtime = time(0);
247 (void)fprintf(stderr, "Finished: %s\n", asctime(localtime(&endtime)));
248 (void)fprintf(stderr, "Exiting\n");
249 }
250 exit(returnNonZeroIfTerminateCalled && terminateCalled ? 1 : 0);
251 });
252 }