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