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
16namespace
17{
18const 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
34const 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
50const 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
77namespace gl
78{
79
80Debug::Control::Control() {}
81
82Debug::Control::~Control() {}
83
84Debug::Control::Control(const Control &other) = default;
85
86Debug::Group::Group() {}
87
88Debug::Group::~Group() {}
89
90Debug::Group::Group(const Group &other) = default;
91
92Debug::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
104Debug::~Debug() {}
105
106void Debug::setMaxLoggedMessages(GLuint maxLoggedMessages)
107{
108    mMaxLoggedMessages = maxLoggedMessages;
109}
110
111void Debug::setOutputEnabled(bool enabled)
112{
113    mOutputEnabled = enabled;
114}
115
116bool Debug::isOutputEnabled() const
117{
118    return mOutputEnabled;
119}
120
121void Debug::setOutputSynchronous(bool synchronous)
122{
123    mOutputSynchronous = synchronous;
124}
125
126bool Debug::isOutputSynchronous() const
127{
128    return mOutputSynchronous;
129}
130
131void Debug::setCallback(GLDEBUGPROCKHR callback, const void *userParam)
132{
133    mCallbackFunction  = callback;
134    mCallbackUserParam = userParam;
135}
136
137GLDEBUGPROCKHR Debug::getCallback() const
138{
139    return mCallbackFunction;
140}
141
142const void *Debug::getUserParam() const
143{
144    return mCallbackUserParam;
145}
146
147void 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
159void 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::GLInvalid)
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    if (mCallbackFunction != nullptr)
203    {
204        // TODO(geofflang) Check the synchronous flag and potentially flush messages from another
205        // thread.
206        mCallbackFunction(source, type, id, severity, static_cast<GLsizei>(message.length()),
207                          message.c_str(), mCallbackUserParam);
208    }
209    else
210    {
211        if (mMessages.size() >= mMaxLoggedMessages)
212        {
213            // Drop messages over the limit
214            return;
215        }
216
217        Message m;
218        m.source   = source;
219        m.type     = type;
220        m.id       = id;
221        m.severity = severity;
222        m.message  = std::move(message);
223
224        mMessages.push_back(std::move(m));
225    }
226}
227
228size_t Debug::getMessages(GLuint count,
229                          GLsizei bufSize,
230                          GLenum *sources,
231                          GLenum *types,
232                          GLuint *ids,
233                          GLenum *severities,
234                          GLsizei *lengths,
235                          GLchar *messageLog)
236{
237    size_t messageCount       = 0;
238    size_t messageStringIndex = 0;
239    while (messageCount <= count && !mMessages.empty())
240    {
241        const Message &m = mMessages.front();
242
243        if (messageLog != nullptr)
244        {
245            // Check that this message can fit in the message buffer
246            if (messageStringIndex + m.message.length() + 1 > static_cast<size_t>(bufSize))
247            {
248                break;
249            }
250
251            std::copy(m.message.begin(), m.message.end(), messageLog + messageStringIndex);
252            messageStringIndex += m.message.length();
253
254            messageLog[messageStringIndex] = '\0';
255            messageStringIndex += 1;
256        }
257
258        if (sources != nullptr)
259        {
260            sources[messageCount] = m.source;
261        }
262
263        if (types != nullptr)
264        {
265            types[messageCount] = m.type;
266        }
267
268        if (ids != nullptr)
269        {
270            ids[messageCount] = m.id;
271        }
272
273        if (severities != nullptr)
274        {
275            severities[messageCount] = m.severity;
276        }
277
278        if (lengths != nullptr)
279        {
280            lengths[messageCount] = static_cast<GLsizei>(m.message.length()) + 1;
281        }
282
283        mMessages.pop_front();
284
285        messageCount++;
286    }
287
288    return messageCount;
289}
290
291size_t Debug::getNextMessageLength() const
292{
293    return mMessages.empty() ? 0 : mMessages.front().message.length() + 1;
294}
295
296size_t Debug::getMessageCount() const
297{
298    return mMessages.size();
299}
300
301void Debug::setMessageControl(GLenum source,
302                              GLenum type,
303                              GLenum severity,
304                              std::vector<GLuint> &&ids,
305                              bool enabled)
306{
307    Control c;
308    c.source   = source;
309    c.type     = type;
310    c.severity = severity;
311    c.ids      = std::move(ids);
312    c.enabled  = enabled;
313
314    auto &controls = mGroups.back().controls;
315    controls.push_back(std::move(c));
316}
317
318void Debug::pushGroup(GLenum source, GLuint id, std::string &&message)
319{
320    insertMessage(source, GL_DEBUG_TYPE_PUSH_GROUP, id, GL_DEBUG_SEVERITY_NOTIFICATION,
321                  std::string(message), gl::LOG_INFO, angle::EntryPoint::GLPushDebugGroup);
322
323    Group g;
324    g.source  = source;
325    g.id      = id;
326    g.message = std::move(message);
327    mGroups.push_back(std::move(g));
328}
329
330void Debug::popGroup()
331{
332    // Make sure the default group is not about to be popped
333    ASSERT(mGroups.size() > 1);
334
335    Group g = mGroups.back();
336    mGroups.pop_back();
337
338    insertMessage(g.source, GL_DEBUG_TYPE_POP_GROUP, g.id, GL_DEBUG_SEVERITY_NOTIFICATION,
339                  g.message, gl::LOG_INFO, angle::EntryPoint::GLPopDebugGroup);
340}
341
342size_t Debug::getGroupStackDepth() const
343{
344    return mGroups.size();
345}
346
347void Debug::insertPerfWarning(GLenum severity, const char *message, uint32_t *repeatCount) const
348{
349    bool repeatLast;
350
351    {
352        constexpr uint32_t kMaxRepeat = 4;
353        std::lock_guard<std::mutex> lock(GetDebugMutex());
354
355        if (*repeatCount >= kMaxRepeat)
356        {
357            return;
358        }
359
360        ++*repeatCount;
361        repeatLast = (*repeatCount == kMaxRepeat);
362    }
363
364    std::string msg = message;
365    if (repeatLast)
366    {
367        msg += " (this message will no longer repeat)";
368    }
369
370    // Release the lock before we call insertMessage. It will re-acquire the lock.
371    insertMessage(GL_DEBUG_SOURCE_API, GL_DEBUG_TYPE_PERFORMANCE, 0, severity, std::move(msg),
372                  gl::LOG_INFO, angle::EntryPoint::GLInvalid);
373}
374
375bool Debug::isMessageEnabled(GLenum source, GLenum type, GLuint id, GLenum severity) const
376{
377    if (!mOutputEnabled)
378    {
379        return false;
380    }
381
382    for (auto groupIter = mGroups.rbegin(); groupIter != mGroups.rend(); groupIter++)
383    {
384        const auto &controls = groupIter->controls;
385        for (auto controlIter = controls.rbegin(); controlIter != controls.rend(); controlIter++)
386        {
387            const auto &control = *controlIter;
388
389            if (control.source != GL_DONT_CARE && control.source != source)
390            {
391                continue;
392            }
393
394            if (control.type != GL_DONT_CARE && control.type != type)
395            {
396                continue;
397            }
398
399            if (control.severity != GL_DONT_CARE && control.severity != severity)
400            {
401                continue;
402            }
403
404            if (!control.ids.empty() &&
405                std::find(control.ids.begin(), control.ids.end(), id) == control.ids.end())
406            {
407                continue;
408            }
409
410            return control.enabled;
411        }
412    }
413
414    return true;
415}
416
417void Debug::pushDefaultGroup()
418{
419    Group g;
420    g.source  = GL_NONE;
421    g.id      = 0;
422    g.message = "";
423
424    Control c0;
425    c0.source   = GL_DONT_CARE;
426    c0.type     = GL_DONT_CARE;
427    c0.severity = GL_DONT_CARE;
428    c0.enabled  = true;
429    g.controls.push_back(std::move(c0));
430
431    Control c1;
432    c1.source   = GL_DONT_CARE;
433    c1.type     = GL_DONT_CARE;
434    c1.severity = GL_DEBUG_SEVERITY_LOW;
435    c1.enabled  = false;
436    g.controls.push_back(std::move(c1));
437
438    mGroups.push_back(std::move(g));
439}
440}  // namespace gl
441
442namespace egl
443{
444
445namespace
446{
447angle::PackedEnumBitSet<MessageType> GetDefaultMessageTypeBits()
448{
449    angle::PackedEnumBitSet<MessageType> result;
450    result.set(MessageType::Critical);
451    result.set(MessageType::Error);
452    return result;
453}
454}  // anonymous namespace
455
456Debug::Debug() : mCallback(nullptr), mEnabledMessageTypes(GetDefaultMessageTypeBits()) {}
457
458void Debug::setCallback(EGLDEBUGPROCKHR callback, const AttributeMap &attribs)
459{
460    mCallback = callback;
461
462    const angle::PackedEnumBitSet<MessageType> defaultMessageTypes = GetDefaultMessageTypeBits();
463    if (mCallback != nullptr)
464    {
465        for (MessageType messageType : angle::AllEnums<MessageType>())
466        {
467            mEnabledMessageTypes[messageType] =
468                (attribs.getAsInt(egl::ToEGLenum(messageType), defaultMessageTypes[messageType]) ==
469                 EGL_TRUE);
470        }
471    }
472}
473
474EGLDEBUGPROCKHR Debug::getCallback() const
475{
476    return mCallback;
477}
478
479bool Debug::isMessageTypeEnabled(MessageType type) const
480{
481    return mEnabledMessageTypes[type];
482}
483
484void Debug::insertMessage(EGLenum error,
485                          const char *command,
486                          MessageType messageType,
487                          EGLLabelKHR threadLabel,
488                          EGLLabelKHR objectLabel,
489                          const std::string &message) const
490{
491    {
492        // output all messages to the debug log
493        const char *messageTypeString = EGLMessageTypeToString(messageType);
494        std::ostringstream messageStream;
495        messageStream << "EGL " << messageTypeString << ": " << command << ": " << message;
496        INFO() << messageStream.str();
497    }
498
499    // TODO(geofflang): Lock before checking the callback. http://anglebug.com/2464
500    if (mCallback && isMessageTypeEnabled(messageType))
501    {
502        mCallback(error, command, egl::ToEGLenum(messageType), threadLabel, objectLabel,
503                  message.c_str());
504    }
505}
506
507}  // namespace egl
508