1 //
2 // Copyright 2002 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6
7 // debug.cpp: Debugging utilities.
8
9 #include "common/debug.h"
10
11 #include <stdarg.h>
12
13 #include <array>
14 #include <cstdio>
15 #include <cstring>
16 #include <fstream>
17 #include <ostream>
18 #include <vector>
19
20 #if defined(ANGLE_PLATFORM_ANDROID)
21 # include <android/log.h>
22 #endif
23
24 #if defined(ANGLE_PLATFORM_APPLE)
25 # include <os/log.h>
26 #endif
27
28 #include "anglebase/no_destructor.h"
29 #include "common/Optional.h"
30 #include "common/angleutils.h"
31 #include "common/entry_points_enum_autogen.h"
32 #include "common/system_utils.h"
33
34 namespace gl
35 {
36
37 namespace
38 {
39
40 DebugAnnotator *g_debugAnnotator = nullptr;
41
42 std::mutex *g_debugMutex = nullptr;
43
44 constexpr std::array<const char *, LOG_NUM_SEVERITIES> g_logSeverityNames = {
45 {"EVENT", "INFO", "WARN", "ERR", "FATAL"}};
46
LogSeverityName(int severity)47 constexpr const char *LogSeverityName(int severity)
48 {
49 return (severity >= 0 && severity < LOG_NUM_SEVERITIES) ? g_logSeverityNames[severity]
50 : "UNKNOWN";
51 }
52
ShouldCreateLogMessage(LogSeverity severity)53 bool ShouldCreateLogMessage(LogSeverity severity)
54 {
55 #if defined(ANGLE_TRACE_ENABLED)
56 return true;
57 #elif defined(ANGLE_ENABLE_ASSERTS)
58 return severity == LOG_FATAL || severity == LOG_ERR || severity == LOG_WARN;
59 #else
60 return false;
61 #endif
62 }
63
64 } // namespace
65
66 namespace priv
67 {
68
ShouldCreatePlatformLogMessage(LogSeverity severity)69 bool ShouldCreatePlatformLogMessage(LogSeverity severity)
70 {
71 #if defined(ANGLE_TRACE_ENABLED)
72 return true;
73 #else
74 return severity != LOG_EVENT;
75 #endif
76 }
77
78 // This is never instantiated, it's just used for EAT_STREAM_PARAMETERS to an object of the correct
79 // type on the LHS of the unused part of the ternary operator.
80 std::ostream *gSwallowStream;
81 } // namespace priv
82
DebugAnnotationsActive()83 bool DebugAnnotationsActive()
84 {
85 #if defined(ANGLE_ENABLE_DEBUG_ANNOTATIONS) || defined(ANGLE_ENABLE_DEBUG_TRACE)
86 return g_debugAnnotator != nullptr && g_debugAnnotator->getStatus();
87 #else
88 return false;
89 #endif
90 }
91
ShouldBeginScopedEvent()92 bool ShouldBeginScopedEvent()
93 {
94 #if defined(ANGLE_ENABLE_ANNOTATOR_RUN_TIME_CHECKS)
95 return DebugAnnotationsActive();
96 #else
97 return true;
98 #endif // defined(ANGLE_ENABLE_ANNOTATOR_RUN_TIME_CHECKS)
99 }
100
DebugAnnotationsInitialized()101 bool DebugAnnotationsInitialized()
102 {
103 return g_debugAnnotator != nullptr;
104 }
105
InitializeDebugAnnotations(DebugAnnotator * debugAnnotator)106 void InitializeDebugAnnotations(DebugAnnotator *debugAnnotator)
107 {
108 UninitializeDebugAnnotations();
109 g_debugAnnotator = debugAnnotator;
110 }
111
UninitializeDebugAnnotations()112 void UninitializeDebugAnnotations()
113 {
114 // Pointer is not managed.
115 g_debugAnnotator = nullptr;
116 }
117
InitializeDebugMutexIfNeeded()118 void InitializeDebugMutexIfNeeded()
119 {
120 if (g_debugMutex == nullptr)
121 {
122 g_debugMutex = new std::mutex();
123 }
124 }
125
GetDebugMutex()126 std::mutex &GetDebugMutex()
127 {
128 ASSERT(g_debugMutex);
129 return *g_debugMutex;
130 }
131
ScopedPerfEventHelper(gl::Context * context,angle::EntryPoint entryPoint)132 ScopedPerfEventHelper::ScopedPerfEventHelper(gl::Context *context, angle::EntryPoint entryPoint)
133 : mContext(context), mEntryPoint(entryPoint), mFunctionName(nullptr)
134 {}
135
~ScopedPerfEventHelper()136 ScopedPerfEventHelper::~ScopedPerfEventHelper()
137 {
138 // EGL_Terminate() can set g_debugAnnotator to nullptr; must call DebugAnnotationsActive() here
139 if (mFunctionName && DebugAnnotationsActive())
140 {
141 g_debugAnnotator->endEvent(mContext, mFunctionName, mEntryPoint);
142 }
143 }
144
begin(const char * format,...)145 void ScopedPerfEventHelper::begin(const char *format, ...)
146 {
147 mFunctionName = GetEntryPointName(mEntryPoint);
148
149 va_list vararg;
150 va_start(vararg, format);
151
152 std::vector<char> buffer;
153 size_t len = FormatStringIntoVector(format, vararg, buffer);
154 va_end(vararg);
155
156 ANGLE_LOG(EVENT) << std::string(&buffer[0], len);
157 if (DebugAnnotationsInitialized())
158 {
159 g_debugAnnotator->beginEvent(mContext, mEntryPoint, mFunctionName, buffer.data());
160 }
161 }
162
LogMessage(const char * file,const char * function,int line,LogSeverity severity)163 LogMessage::LogMessage(const char *file, const char *function, int line, LogSeverity severity)
164 : mFile(file), mFunction(function), mLine(line), mSeverity(severity)
165 {
166 // INFO() and EVENT() do not require additional function(line) info.
167 if (mSeverity > LOG_INFO)
168 {
169 const char *slash = std::max(strrchr(mFile, '/'), strrchr(mFile, '\\'));
170 mStream << (slash ? (slash + 1) : mFile) << ":" << mLine << " (" << mFunction << "): ";
171 }
172 }
173
~LogMessage()174 LogMessage::~LogMessage()
175 {
176 std::unique_lock<std::mutex> lock;
177 if (g_debugMutex != nullptr)
178 {
179 lock = std::unique_lock<std::mutex>(*g_debugMutex);
180 }
181
182 if (DebugAnnotationsInitialized() && (mSeverity > LOG_INFO))
183 {
184 g_debugAnnotator->logMessage(*this);
185 }
186 else
187 {
188 Trace(getSeverity(), getMessage().c_str());
189 }
190
191 if (mSeverity == LOG_FATAL)
192 {
193 if (angle::IsDebuggerAttached())
194 {
195 angle::BreakDebugger();
196 }
197 else
198 {
199 ANGLE_CRASH();
200 }
201 }
202 }
203
Trace(LogSeverity severity,const char * message)204 void Trace(LogSeverity severity, const char *message)
205 {
206 if (!ShouldCreateLogMessage(severity))
207 {
208 return;
209 }
210
211 std::string str(message);
212
213 if (DebugAnnotationsActive())
214 {
215
216 switch (severity)
217 {
218 case LOG_EVENT:
219 // Debugging logging done in ScopedPerfEventHelper
220 break;
221 default:
222 g_debugAnnotator->setMarker(message);
223 break;
224 }
225 }
226
227 if (severity == LOG_FATAL || severity == LOG_ERR || severity == LOG_WARN ||
228 #if defined(ANGLE_ENABLE_TRACE_ANDROID_LOGCAT)
229 severity == LOG_EVENT ||
230 #endif
231 severity == LOG_INFO)
232 {
233 #if defined(ANGLE_PLATFORM_ANDROID)
234 android_LogPriority android_priority = ANDROID_LOG_ERROR;
235 switch (severity)
236 {
237 case LOG_INFO:
238 case LOG_EVENT:
239 android_priority = ANDROID_LOG_INFO;
240 break;
241 case LOG_WARN:
242 android_priority = ANDROID_LOG_WARN;
243 break;
244 case LOG_ERR:
245 android_priority = ANDROID_LOG_ERROR;
246 break;
247 case LOG_FATAL:
248 android_priority = ANDROID_LOG_FATAL;
249 break;
250 default:
251 UNREACHABLE();
252 }
253 __android_log_print(android_priority, "ANGLE", "%s: %s\n", LogSeverityName(severity),
254 str.c_str());
255 #elif defined(ANGLE_PLATFORM_APPLE)
256 if (__builtin_available(macOS 10.12, iOS 10.0, *))
257 {
258 os_log_type_t apple_log_type = OS_LOG_TYPE_DEFAULT;
259 switch (severity)
260 {
261 case LOG_INFO:
262 apple_log_type = OS_LOG_TYPE_INFO;
263 break;
264 case LOG_WARN:
265 apple_log_type = OS_LOG_TYPE_DEFAULT;
266 break;
267 case LOG_ERR:
268 apple_log_type = OS_LOG_TYPE_ERROR;
269 break;
270 case LOG_FATAL:
271 // OS_LOG_TYPE_FAULT is too severe - grabs the entire process tree.
272 apple_log_type = OS_LOG_TYPE_ERROR;
273 break;
274 default:
275 UNREACHABLE();
276 }
277 os_log_with_type(OS_LOG_DEFAULT, apple_log_type, "ANGLE: %s: %s\n",
278 LogSeverityName(severity), str.c_str());
279 }
280 #else
281 // Note: we use fprintf because <iostream> includes static initializers.
282 fprintf((severity >= LOG_WARN) ? stderr : stdout, "%s: %s\n", LogSeverityName(severity),
283 str.c_str());
284 #endif
285 }
286
287 #if defined(ANGLE_PLATFORM_WINDOWS) && \
288 (defined(ANGLE_ENABLE_DEBUG_TRACE_TO_DEBUGGER) || !defined(NDEBUG))
289 # if !defined(ANGLE_ENABLE_DEBUG_TRACE_TO_DEBUGGER)
290 if (severity >= LOG_ERR)
291 # endif // !defined(ANGLE_ENABLE_DEBUG_TRACE_TO_DEBUGGER)
292 {
293 OutputDebugStringA(str.c_str());
294 OutputDebugStringA("\n");
295 }
296 #endif
297
298 #if defined(ANGLE_ENABLE_DEBUG_TRACE)
299 # if defined(NDEBUG)
300 if (severity == LOG_EVENT || severity == LOG_WARN || severity == LOG_INFO)
301 {
302 return;
303 }
304 # endif // defined(NDEBUG)
305 static angle::base::NoDestructor<std::ofstream> file(TRACE_OUTPUT_FILE, std::ofstream::app);
306 if (file->good())
307 {
308 if (severity > LOG_EVENT)
309 {
310 *file << LogSeverityName(severity) << ": ";
311 }
312 *file << str << "\n";
313 file->flush();
314 }
315 #endif // defined(ANGLE_ENABLE_DEBUG_TRACE)
316 }
317
getSeverity() const318 LogSeverity LogMessage::getSeverity() const
319 {
320 return mSeverity;
321 }
322
getMessage() const323 std::string LogMessage::getMessage() const
324 {
325 return mStream.str();
326 }
327
328 #if defined(ANGLE_PLATFORM_WINDOWS)
FmtHR(HRESULT value)329 priv::FmtHexHelper<HRESULT> FmtHR(HRESULT value)
330 {
331 return priv::FmtHexHelper<HRESULT>("HRESULT: ", value);
332 }
333
FmtErr(DWORD value)334 priv::FmtHexHelper<DWORD> FmtErr(DWORD value)
335 {
336 return priv::FmtHexHelper<DWORD>("error: ", value);
337 }
338 #endif // defined(ANGLE_PLATFORM_WINDOWS)
339
340 } // namespace gl
341