• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2015 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: Defines debug state used for GL_KHR_debug
8 
9 #include "libANGLE/Debug.h"
10 
11 #include "common/debug.h"
12 
13 #include <algorithm>
14 #include <tuple>
15 
16 namespace
17 {
GLSeverityToString(GLenum severity)18 const char *GLSeverityToString(GLenum severity)
19 {
20     switch (severity)
21     {
22         case GL_DEBUG_SEVERITY_HIGH:
23             return "HIGH";
24         case GL_DEBUG_SEVERITY_MEDIUM:
25             return "MEDIUM";
26         case GL_DEBUG_SEVERITY_LOW:
27             return "LOW";
28         case GL_DEBUG_SEVERITY_NOTIFICATION:
29         default:
30             return "NOTIFICATION";
31     }
32 }
33 
EGLMessageTypeToString(egl::MessageType messageType)34 const char *EGLMessageTypeToString(egl::MessageType messageType)
35 {
36     switch (messageType)
37     {
38         case egl::MessageType::Critical:
39             return "CRITICAL";
40         case egl::MessageType::Error:
41             return "ERROR";
42         case egl::MessageType::Warn:
43             return "WARNING";
44         case egl::MessageType::Info:
45         default:
46             return "INFO";
47     }
48 }
49 
GLMessageTypeToString(GLenum type)50 const char *GLMessageTypeToString(GLenum type)
51 {
52     switch (type)
53     {
54         case GL_DEBUG_TYPE_ERROR:
55             return "error";
56         case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
57             return "deprecated behavior";
58         case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
59             return "undefined behavior";
60         case GL_DEBUG_TYPE_PORTABILITY:
61             return "portability";
62         case GL_DEBUG_TYPE_PERFORMANCE:
63             return "performance";
64         case GL_DEBUG_TYPE_MARKER:
65             return "marker";
66         case GL_DEBUG_TYPE_PUSH_GROUP:
67             return "start of group";
68         case GL_DEBUG_TYPE_POP_GROUP:
69             return "end of group";
70         case GL_DEBUG_TYPE_OTHER:
71         default:
72             return "other message";
73     }
74 }
75 }  // namespace
76 
77 namespace gl
78 {
79 
Control()80 Debug::Control::Control() {}
81 
~Control()82 Debug::Control::~Control() {}
83 
84 Debug::Control::Control(const Control &other) = default;
85 
Group()86 Debug::Group::Group() {}
87 
~Group()88 Debug::Group::~Group() {}
89 
90 Debug::Group::Group(const Group &other) = default;
91 
Debug(bool initialDebugState)92 Debug::Debug(bool initialDebugState)
93     : mOutputEnabled(initialDebugState),
94       mCallbackFunction(nullptr),
95       mCallbackUserParam(nullptr),
96       mMessages(),
97       mMaxLoggedMessages(0),
98       mOutputSynchronous(false),
99       mGroups()
100 {
101     pushDefaultGroup();
102 }
103 
~Debug()104 Debug::~Debug() {}
105 
setMaxLoggedMessages(GLuint maxLoggedMessages)106 void Debug::setMaxLoggedMessages(GLuint maxLoggedMessages)
107 {
108     mMaxLoggedMessages = maxLoggedMessages;
109 }
110 
setOutputEnabled(bool enabled)111 void Debug::setOutputEnabled(bool enabled)
112 {
113     mOutputEnabled = enabled;
114 }
115 
isOutputEnabled() const116 bool Debug::isOutputEnabled() const
117 {
118     return mOutputEnabled;
119 }
120 
setOutputSynchronous(bool synchronous)121 void Debug::setOutputSynchronous(bool synchronous)
122 {
123     mOutputSynchronous = synchronous;
124 }
125 
isOutputSynchronous() const126 bool Debug::isOutputSynchronous() const
127 {
128     return mOutputSynchronous;
129 }
130 
setCallback(GLDEBUGPROCKHR callback,const void * userParam)131 void Debug::setCallback(GLDEBUGPROCKHR callback, const void *userParam)
132 {
133     mCallbackFunction  = callback;
134     mCallbackUserParam = userParam;
135 }
136 
getCallback() const137 GLDEBUGPROCKHR Debug::getCallback() const
138 {
139     return mCallbackFunction;
140 }
141 
getUserParam() const142 const void *Debug::getUserParam() const
143 {
144     return mCallbackUserParam;
145 }
146 
insertMessage(GLenum source,GLenum type,GLuint id,GLenum severity,const std::string & message,gl::LogSeverity logSeverity,angle::EntryPoint entryPoint) const147 void Debug::insertMessage(GLenum source,
148                           GLenum type,
149                           GLuint id,
150                           GLenum severity,
151                           const std::string &message,
152                           gl::LogSeverity logSeverity,
153                           angle::EntryPoint entryPoint) const
154 {
155     std::string messageCopy(message);
156     insertMessage(source, type, id, severity, std::move(messageCopy), logSeverity, entryPoint);
157 }
158 
insertMessage(GLenum source,GLenum type,GLuint id,GLenum severity,std::string && message,gl::LogSeverity logSeverity,angle::EntryPoint entryPoint) const159 void Debug::insertMessage(GLenum source,
160                           GLenum type,
161                           GLuint id,
162                           GLenum severity,
163                           std::string &&message,
164                           gl::LogSeverity logSeverity,
165                           angle::EntryPoint entryPoint) const
166 {
167     {
168         // output all messages to the debug log
169         const char *messageTypeString = GLMessageTypeToString(type);
170         const char *severityString    = GLSeverityToString(severity);
171         std::ostringstream messageStream;
172         if (entryPoint != angle::EntryPoint::Invalid)
173         {
174             messageStream << GetEntryPointName(entryPoint) << ": ";
175         }
176         messageStream << "GL " << messageTypeString << ": " << severityString << ": " << message;
177         switch (logSeverity)
178         {
179             case gl::LOG_FATAL:
180                 FATAL() << messageStream.str();
181                 break;
182             case gl::LOG_ERR:
183                 ERR() << messageStream.str();
184                 break;
185             case gl::LOG_WARN:
186                 WARN() << messageStream.str();
187                 break;
188             case gl::LOG_INFO:
189                 INFO() << messageStream.str();
190                 break;
191             case gl::LOG_EVENT:
192                 ANGLE_LOG(EVENT) << messageStream.str();
193                 break;
194         }
195     }
196 
197     if (!isMessageEnabled(source, type, id, severity))
198     {
199         return;
200     }
201 
202     // TODO(geofflang) Check the synchronous flag and potentially flush messages from another
203     // thread.
204     // If isOutputSynchronous(), mMutex does not need to be held, but instead the message should be
205     // dropped/queued if it doesn't originate from the current context.  If !isOutputSynchronous(),
206     // the callback is expected to be thread-safe per spec, so there is no need for locking.
207     if (mCallbackFunction != nullptr)
208     {
209         mCallbackFunction(source, type, id, severity, static_cast<GLsizei>(message.length()),
210                           message.c_str(), mCallbackUserParam);
211     }
212     else
213     {
214         std::lock_guard<std::mutex> lock(mMutex);
215 
216         if (mMessages.size() >= mMaxLoggedMessages)
217         {
218             // Drop messages over the limit
219             return;
220         }
221 
222         Message m;
223         m.source   = source;
224         m.type     = type;
225         m.id       = id;
226         m.severity = severity;
227         m.message  = std::move(message);
228 
229         mMessages.push_back(std::move(m));
230     }
231 }
232 
getMessages(GLuint count,GLsizei bufSize,GLenum * sources,GLenum * types,GLuint * ids,GLenum * severities,GLsizei * lengths,GLchar * messageLog)233 size_t Debug::getMessages(GLuint count,
234                           GLsizei bufSize,
235                           GLenum *sources,
236                           GLenum *types,
237                           GLuint *ids,
238                           GLenum *severities,
239                           GLsizei *lengths,
240                           GLchar *messageLog)
241 {
242     std::lock_guard<std::mutex> lock(mMutex);
243 
244     size_t messageCount       = 0;
245     size_t messageStringIndex = 0;
246     while (messageCount <= count && !mMessages.empty())
247     {
248         const Message &m = mMessages.front();
249 
250         if (messageLog != nullptr)
251         {
252             // Check that this message can fit in the message buffer
253             if (messageStringIndex + m.message.length() + 1 > static_cast<size_t>(bufSize))
254             {
255                 break;
256             }
257 
258             std::copy(m.message.begin(), m.message.end(), messageLog + messageStringIndex);
259             messageStringIndex += m.message.length();
260 
261             messageLog[messageStringIndex] = '\0';
262             messageStringIndex += 1;
263         }
264 
265         if (sources != nullptr)
266         {
267             sources[messageCount] = m.source;
268         }
269 
270         if (types != nullptr)
271         {
272             types[messageCount] = m.type;
273         }
274 
275         if (ids != nullptr)
276         {
277             ids[messageCount] = m.id;
278         }
279 
280         if (severities != nullptr)
281         {
282             severities[messageCount] = m.severity;
283         }
284 
285         if (lengths != nullptr)
286         {
287             lengths[messageCount] = static_cast<GLsizei>(m.message.length()) + 1;
288         }
289 
290         mMessages.pop_front();
291 
292         messageCount++;
293     }
294 
295     return messageCount;
296 }
297 
getNextMessageLength() const298 size_t Debug::getNextMessageLength() const
299 {
300     std::lock_guard<std::mutex> lock(mMutex);
301     return mMessages.empty() ? 0 : mMessages.front().message.length() + 1;
302 }
303 
getMessageCount() const304 size_t Debug::getMessageCount() const
305 {
306     std::lock_guard<std::mutex> lock(mMutex);
307     return mMessages.size();
308 }
309 
setMessageControl(GLenum source,GLenum type,GLenum severity,std::vector<GLuint> && ids,bool enabled)310 void Debug::setMessageControl(GLenum source,
311                               GLenum type,
312                               GLenum severity,
313                               std::vector<GLuint> &&ids,
314                               bool enabled)
315 {
316     Control c;
317     c.source   = source;
318     c.type     = type;
319     c.severity = severity;
320     c.ids      = std::move(ids);
321     c.enabled  = enabled;
322 
323     auto &controls = mGroups.back().controls;
324     controls.push_back(std::move(c));
325 }
326 
pushGroup(GLenum source,GLuint id,std::string && message)327 void Debug::pushGroup(GLenum source, GLuint id, std::string &&message)
328 {
329     insertMessage(source, GL_DEBUG_TYPE_PUSH_GROUP, id, GL_DEBUG_SEVERITY_NOTIFICATION,
330                   std::string(message), gl::LOG_INFO, angle::EntryPoint::GLPushDebugGroup);
331 
332     Group g;
333     g.source  = source;
334     g.id      = id;
335     g.message = std::move(message);
336     mGroups.push_back(std::move(g));
337 }
338 
popGroup()339 void Debug::popGroup()
340 {
341     // Make sure the default group is not about to be popped
342     ASSERT(mGroups.size() > 1);
343 
344     Group g = mGroups.back();
345     mGroups.pop_back();
346 
347     insertMessage(g.source, GL_DEBUG_TYPE_POP_GROUP, g.id, GL_DEBUG_SEVERITY_NOTIFICATION,
348                   g.message, gl::LOG_INFO, angle::EntryPoint::GLPopDebugGroup);
349 }
350 
getGroupStackDepth() const351 size_t Debug::getGroupStackDepth() const
352 {
353     return mGroups.size();
354 }
355 
insertPerfWarning(GLenum severity,const char * message,uint32_t * repeatCount) const356 void Debug::insertPerfWarning(GLenum severity, const char *message, uint32_t *repeatCount) const
357 {
358     bool repeatLast;
359 
360     {
361         constexpr uint32_t kMaxRepeat = 4;
362         std::lock_guard<std::mutex> lock(GetDebugMutex());
363 
364         if (*repeatCount >= kMaxRepeat)
365         {
366             return;
367         }
368 
369         ++*repeatCount;
370         repeatLast = (*repeatCount == kMaxRepeat);
371     }
372 
373     std::string msg = message;
374     if (repeatLast)
375     {
376         msg += " (this message will no longer repeat)";
377     }
378 
379     // Note: insertMessage will acquire GetDebugMutex(), so it must be released before this call.
380     insertMessage(GL_DEBUG_SOURCE_API, GL_DEBUG_TYPE_PERFORMANCE, 0, severity, std::move(msg),
381                   gl::LOG_INFO, angle::EntryPoint::Invalid);
382 }
383 
isMessageEnabled(GLenum source,GLenum type,GLuint id,GLenum severity) const384 bool Debug::isMessageEnabled(GLenum source, GLenum type, GLuint id, GLenum severity) const
385 {
386     if (!mOutputEnabled)
387     {
388         return false;
389     }
390 
391     for (auto groupIter = mGroups.rbegin(); groupIter != mGroups.rend(); groupIter++)
392     {
393         const auto &controls = groupIter->controls;
394         for (auto controlIter = controls.rbegin(); controlIter != controls.rend(); controlIter++)
395         {
396             const auto &control = *controlIter;
397 
398             if (control.source != GL_DONT_CARE && control.source != source)
399             {
400                 continue;
401             }
402 
403             if (control.type != GL_DONT_CARE && control.type != type)
404             {
405                 continue;
406             }
407 
408             if (control.severity != GL_DONT_CARE && control.severity != severity)
409             {
410                 continue;
411             }
412 
413             if (!control.ids.empty() &&
414                 std::find(control.ids.begin(), control.ids.end(), id) == control.ids.end())
415             {
416                 continue;
417             }
418 
419             return control.enabled;
420         }
421     }
422 
423     return true;
424 }
425 
pushDefaultGroup()426 void Debug::pushDefaultGroup()
427 {
428     Group g;
429     g.source  = GL_NONE;
430     g.id      = 0;
431     g.message = "";
432 
433     Control c0;
434     c0.source   = GL_DONT_CARE;
435     c0.type     = GL_DONT_CARE;
436     c0.severity = GL_DONT_CARE;
437     c0.enabled  = true;
438     g.controls.push_back(std::move(c0));
439 
440     Control c1;
441     c1.source   = GL_DONT_CARE;
442     c1.type     = GL_DONT_CARE;
443     c1.severity = GL_DEBUG_SEVERITY_LOW;
444     c1.enabled  = false;
445     g.controls.push_back(std::move(c1));
446 
447     mGroups.push_back(std::move(g));
448 }
449 }  // namespace gl
450 
451 namespace egl
452 {
453 
454 namespace
455 {
GetDefaultMessageTypeBits()456 angle::PackedEnumBitSet<MessageType> GetDefaultMessageTypeBits()
457 {
458     angle::PackedEnumBitSet<MessageType> result;
459     result.set(MessageType::Critical);
460     result.set(MessageType::Error);
461     return result;
462 }
463 }  // anonymous namespace
464 
Debug()465 Debug::Debug() : mCallback(nullptr), mEnabledMessageTypes(GetDefaultMessageTypeBits()) {}
466 
setCallback(EGLDEBUGPROCKHR callback,const AttributeMap & attribs)467 void Debug::setCallback(EGLDEBUGPROCKHR callback, const AttributeMap &attribs)
468 {
469     mCallback = callback;
470 
471     const angle::PackedEnumBitSet<MessageType> defaultMessageTypes = GetDefaultMessageTypeBits();
472     if (mCallback != nullptr)
473     {
474         for (MessageType messageType : angle::AllEnums<MessageType>())
475         {
476             mEnabledMessageTypes[messageType] =
477                 (attribs.getAsInt(egl::ToEGLenum(messageType), defaultMessageTypes[messageType]) ==
478                  EGL_TRUE);
479         }
480     }
481 }
482 
getCallback() const483 EGLDEBUGPROCKHR Debug::getCallback() const
484 {
485     return mCallback;
486 }
487 
isMessageTypeEnabled(MessageType type) const488 bool Debug::isMessageTypeEnabled(MessageType type) const
489 {
490     return mEnabledMessageTypes[type];
491 }
492 
insertMessage(EGLenum error,const char * command,MessageType messageType,EGLLabelKHR threadLabel,EGLLabelKHR objectLabel,const std::string & message) const493 void Debug::insertMessage(EGLenum error,
494                           const char *command,
495                           MessageType messageType,
496                           EGLLabelKHR threadLabel,
497                           EGLLabelKHR objectLabel,
498                           const std::string &message) const
499 {
500     {
501         // output all messages to the debug log
502         const char *messageTypeString = EGLMessageTypeToString(messageType);
503         std::ostringstream messageStream;
504         messageStream << "EGL " << messageTypeString << ": " << command << ": " << message;
505         INFO() << messageStream.str();
506     }
507 
508     // TODO(geofflang): Lock before checking the callback. http://anglebug.com/2464
509     if (mCallback && isMessageTypeEnabled(messageType))
510     {
511         mCallback(error, command, egl::ToEGLenum(messageType), threadLabel, objectLabel,
512                   message.c_str());
513     }
514 }
515 
516 }  // namespace egl
517