dyld-421.1.tar.gz
[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 #define MAX_LOG_STR_LEN (512)
36 //const char* kDispatchQueueLogNameKey = "kDispatchQueueLogNameKey";
37 const char* kDispatchWarningArrayKey = "kDispatchWarningArrayKey";
38
39 static dispatch_queue_t log_queue;
40 static dispatch_once_t logQueueInit = 0;
41 static dispatch_queue_t unique_queue;
42 static dispatch_once_t uniqueQueueInit = 0;
43
44 static uint32_t verbose = 0;
45 static bool returnNonZeroIfTerminateCalled = false;
46 static bool terminateCalled = false;
47
48 static const char* warningPrefix = "WARNING: ";
49 static const char* errorPrefix = "ERROR: ";
50
51 LoggingContext::LoggingContext(const std::string& N)
52 : _name(N)
53 , _tainted(false)
54 {
55 }
56
57 LoggingContext::LoggingContext(const std::string& N, WarningTargets T)
58 : _name(N)
59 , _warnings(T)
60 , _tainted(false)
61 {
62 }
63
64 void LoggingContext::taint()
65 {
66 _tainted = true;
67 }
68
69 bool LoggingContext::isTainted()
70 {
71 return _tainted;
72 }
73
74 const std::string& LoggingContext::name()
75 {
76 return _name;
77 }
78
79 const WarningTargets& LoggingContext::targets()
80 {
81 return _warnings;
82 }
83
84
85
86 pthread_key_t getLoggingContextKey(void) {
87 static pthread_key_t logContextKey;
88 static dispatch_once_t logContextToken;
89 dispatch_once(&logContextToken, ^{
90 pthread_key_create(&logContextKey, nullptr);
91 });
92 return logContextKey;
93 }
94
95
96 void setLoggingContext(std::shared_ptr<LoggingContext>& context)
97 {
98 pthread_setspecific(getLoggingContextKey(), (void*)&context);
99
100 if (context && !context->name().empty()) {
101 pthread_setname_np(context->name().substr(0, MAXTHREADNAMESIZE-1).c_str());
102 }
103 }
104
105 std::shared_ptr<LoggingContext> getLoggingContext()
106 {
107 if (void* val = pthread_getspecific(getLoggingContextKey()))
108 return *((std::shared_ptr<LoggingContext>*)val);
109 return nullptr;
110 }
111
112 void runBody(void* Ctx)
113 {
114 std::unique_ptr<std::function<void(void)>>
115 Body(reinterpret_cast<std::function<void(void)>*>(Ctx));
116 (*Body)();
117 }
118
119 static dispatch_queue_t getLogQueue()
120 {
121 dispatch_once(&logQueueInit, ^{
122 log_queue = dispatch_queue_create("com.apple.dyld.cache.logging", DISPATCH_QUEUE_SERIAL);
123 });
124 return log_queue;
125 }
126
127 void setVerbose(bool level)
128 {
129 verbose = level;
130 }
131
132 void setWarnAnErrorPrefixes(const char* warn, const char* err)
133 {
134 warningPrefix = warn;
135 errorPrefix = err;
136 }
137
138 void setReturnNonZeroOnTerminate()
139 {
140 returnNonZeroIfTerminateCalled = true;
141 }
142
143 void queued_print(FILE* __restrict fd, const char* str)
144 {
145 const char* qstr = strdup(str);
146
147 dispatch_async(getLogQueue(), ^{
148 (void)fprintf(fd, "%s", qstr);
149 free((void*)qstr);
150 });
151 }
152
153 #define VLOG(header) \
154 va_list list; \
155 va_start(list, format); \
156 char temp[MAX_LOG_STR_LEN]; \
157 vsprintf(temp, format, list); \
158 auto ctx = getLoggingContext(); \
159 char temp2[MAX_LOG_STR_LEN]; \
160 if (ctx && !ctx->name().empty()) { \
161 snprintf(temp2, MAX_LOG_STR_LEN, "[%s] %s%s\n", ctx->name().c_str(), header, \
162 temp); \
163 } else { \
164 snprintf(temp2, MAX_LOG_STR_LEN, "%s%s\n", header, temp); \
165 } \
166 queued_print(stderr, temp2); \
167 va_end(list);
168
169 void log(const char* __restrict format, ...)
170 {
171 VLOG("");
172 }
173
174 void verboseLog(const char* format, ...)
175 {
176 if (verbose) {
177 VLOG("");
178 }
179 }
180
181 static std::set<std::string> warnings;
182
183 void warning(const char* format, ...)
184 {
185 dispatch_once(&uniqueQueueInit, ^{
186 unique_queue = dispatch_queue_create("com.apple.dyld.cache.logging", DISPATCH_QUEUE_SERIAL);
187 });
188
189 va_list list;
190 va_start(list, format);
191 char temp[MAX_LOG_STR_LEN];
192 vsprintf(temp, format, list);
193 char* blockTemp = strdup(temp);
194
195 auto ctx = getLoggingContext();
196 if (ctx) {
197 for (auto& target : ctx->targets().second) {
198 ctx->targets().first->configurations[target.first].architectures[target.second].results.warnings.push_back(blockTemp);
199 }
200 }
201
202 dispatch_sync(unique_queue, ^{
203 if (warnings.count(blockTemp) == 0) {
204 warnings.insert(blockTemp);
205 }
206
207 free(blockTemp);
208 });
209
210 va_end(list);
211 }
212
213 void terminate(const char* format, ...)
214 {
215 VLOG(errorPrefix);
216
217 terminateCalled = true;
218
219 if (ctx) {
220 // We are a work in a logging context, throw
221 throw std::string(temp);
222 } else {
223 // We are in general handing, let the loggging queue darain and exit
224 dispatch_sync(getLogQueue(), ^{
225 for (auto& warning : warnings) {
226 (void)fprintf(stderr, "%s%s\n", warningPrefix, warning.c_str());
227 }
228 if ( returnNonZeroIfTerminateCalled ) {
229 exit(1);
230 }
231 else {
232 time_t endtime = time(0);
233 (void)fprintf(stderr, "Finished: %s\n", asctime(localtime(&endtime)));
234 (void)fprintf(stderr, "Exiting\n");
235 exit(0);
236 }
237 });
238 }
239
240 // clang can't reason out that we won't hit this due to the dispatch_sync in the exit path
241 __builtin_unreachable();
242 }
243
244 void dumpLogAndExit(bool logFinishTime)
245 {
246 dispatch_async(getLogQueue(), ^{
247 for (auto& warning : warnings) {
248 (void)fprintf(stderr, "%s%s\n", warningPrefix, warning.c_str());
249 }
250 if ( logFinishTime ) {
251 time_t endtime = time(0);
252 (void)fprintf(stderr, "Finished: %s\n", asctime(localtime(&endtime)));
253 (void)fprintf(stderr, "Exiting\n");
254 }
255 exit(returnNonZeroIfTerminateCalled && terminateCalled ? 1 : 0);
256 });
257 }