Logging.h (30859B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef MOZILLA_GFX_LOGGING_H_ 8 #define MOZILLA_GFX_LOGGING_H_ 9 10 #include <string> 11 #include <sstream> 12 #include <stdio.h> 13 #include <vector> 14 15 #ifdef MOZ_LOGGING 16 # include "mozilla/Logging.h" 17 #endif 18 19 #if defined(MOZ_WIDGET_ANDROID) 20 # include "nsDebug.h" 21 #endif 22 #include "2D.h" 23 #include "mozilla/StaticPrefs_gfx.h" 24 #include "Point.h" 25 #include "BaseRect.h" 26 #include "Matrix.h" 27 #include "LoggingConstants.h" 28 29 #if defined(MOZ_LOGGING) 30 extern GFX2D_API mozilla::LogModule* GetGFX2DLog(); 31 #endif 32 33 namespace mozilla { 34 namespace gfx { 35 36 #if defined(MOZ_LOGGING) 37 inline mozilla::LogLevel PRLogLevelForLevel(int aLevel) { 38 switch (aLevel) { 39 case LOG_CRITICAL: 40 return LogLevel::Error; 41 case LOG_WARNING: 42 return LogLevel::Warning; 43 case LOG_DEBUG: 44 return LogLevel::Debug; 45 case LOG_DEBUG_PRLOG: 46 return LogLevel::Debug; 47 case LOG_EVERYTHING: 48 return LogLevel::Error; 49 } 50 return LogLevel::Debug; 51 } 52 #endif 53 54 /// Graphics logging is available in both debug and release builds and is 55 /// controlled with a gfx.logging.level preference. If not set, the default 56 /// for the preference is 5 in the debug builds, 1 in the release builds. 57 /// 58 /// gfxDebug only works in the debug builds, and is used for information 59 /// level messages, helping with debugging. In addition to only working 60 /// in the debug builds, the value of the above preference of 3 or higher 61 /// is required. 62 /// 63 /// gfxWarning messages are available in both debug and release builds, 64 /// on by default in the debug builds, and off by default in the release builds. 65 /// Setting the preference gfx.logging.level to a value of 2 or higher will 66 /// show the warnings. 67 /// 68 /// gfxCriticalError is available in debug and release builds by default. 69 /// It is only unavailable if gfx.logging.level is set to 0 (or less.) 70 /// It outputs the message to stderr or equivalent, like gfxWarning. 71 /// In the event of a crash, the crash report is annotated with first and 72 /// the last few of these errors, under the key GraphicsCriticalError. 73 /// The total number of errors stored in the crash report is controlled 74 /// by preference gfx.logging.crash.length. 75 /// 76 /// On platforms that support MOZ_LOGGING, the story is slightly more involved. 77 /// In that case, unless gfx.logging.level is set to 4 or higher, the output 78 /// is further controlled by the "gfx2d" logging module. However, in the case 79 /// where such module would disable the output, in all but gfxDebug cases, 80 /// we will still send a printf. 81 82 // The range is due to the values set in Histograms.json 83 enum class LogReason : int { 84 MustBeMoreThanThis = -1, 85 // Start. Do not insert, always add at end. If you remove items, 86 // make sure the other items retain their values. 87 D3D11InvalidCallDeviceRemoved = 0, 88 D3D11InvalidCall, 89 D3DLockTimeout, 90 D3D10FinalizeFrame, 91 D3D11FinalizeFrame, 92 D3D10SyncLock, 93 D3D11SyncLock, 94 JobStatusError, 95 FilterInputError, 96 FilterInputData, // 10 97 FilterInputRect, 98 FilterInputSet, 99 FilterInputFormat, 100 SourceSurfaceIncompatible, 101 GlyphAllocFailedCairo, 102 GlyphAllocFailedCG, 103 InvalidRect, 104 CannotDraw3D, // 20 105 IncompatibleBasicTexturedEffect, 106 InvalidFont, 107 PAllocTextureBackendMismatch, 108 GetFontFileDataFailed, 109 MessageChannelCloseFailure, 110 MessageChannelInvalidHandle, 111 TextureAliveAfterShutdown, 112 InvalidContext, 113 InvalidCommandList, 114 AsyncTransactionTimeout, // 30 115 TextureCreation, 116 InvalidCacheSurface, 117 AlphaWithBasicClient, 118 UnbalancedClipStack, 119 ProcessingError, 120 InvalidDrawTarget, 121 NativeFontResourceNotFound, 122 UnscaledFontNotFound, 123 ScaledFontNotFound, 124 InvalidLayerType, // 40 125 // End 126 MustBeLessThanThis = 101, 127 }; 128 129 struct BasicLogger { 130 // For efficiency, this method exists and copies the logic of the 131 // OutputMessage below. If making any changes here, also make it 132 // in the appropriate places in that method. 133 static bool ShouldOutputMessage(int aLevel) { 134 if (StaticPrefs::gfx_logging_level() >= aLevel) { 135 #if defined(MOZ_WIDGET_ANDROID) 136 return true; 137 #else 138 # if defined(MOZ_LOGGING) 139 if (MOZ_LOG_TEST(GetGFX2DLog(), PRLogLevelForLevel(aLevel))) { 140 return true; 141 } else 142 # endif 143 if ((StaticPrefs::gfx_logging_level() >= LOG_DEBUG_PRLOG) || 144 (aLevel < LOG_DEBUG)) { 145 return true; 146 } 147 #endif 148 } 149 return false; 150 } 151 152 // Only for really critical errors. 153 static void CrashAction(LogReason aReason) {} 154 155 static void OutputMessage(const std::string& aString, int aLevel, 156 bool aNoNewline) { 157 // This behavior (the higher the preference, the more we log) 158 // is consistent with what prlog does in general. Note that if prlog 159 // is in the build, but disabled, we will printf if the preferences 160 // requires us to log something. 161 // 162 // If making any logic changes to this method, you should probably 163 // make the corresponding change in the ShouldOutputMessage method 164 // above. 165 if (StaticPrefs::gfx_logging_level() >= aLevel) { 166 #if defined(MOZ_WIDGET_ANDROID) 167 printf_stderr("%s%s", aString.c_str(), aNoNewline ? "" : "\n"); 168 #else 169 # if defined(MOZ_LOGGING) 170 if (MOZ_LOG_TEST(GetGFX2DLog(), PRLogLevelForLevel(aLevel))) { 171 MOZ_LOG(GetGFX2DLog(), PRLogLevelForLevel(aLevel), 172 ("%s%s", aString.c_str(), aNoNewline ? "" : "\n")); 173 } else 174 # endif 175 if ((StaticPrefs::gfx_logging_level() >= LOG_DEBUG_PRLOG) || 176 (aLevel < LOG_DEBUG)) { 177 printf("%s%s", aString.c_str(), aNoNewline ? "" : "\n"); 178 } 179 #endif 180 } 181 } 182 }; 183 184 struct CriticalLogger { 185 static void OutputMessage(const std::string& aString, int aLevel, 186 bool aNoNewline); 187 static void CrashAction(LogReason aReason); 188 }; 189 190 // The int is the index of the Log call; if the number of logs exceeds some 191 // preset capacity we may not get all of them, so the indices help figure out 192 // which ones we did save. The double is expected to be the "TimeDuration", 193 // time in seconds since the process creation. 194 typedef std::tuple<int32_t, std::string, double> LoggingRecordEntry; 195 196 // Implement this interface and init the Factory with an instance to 197 // forward critical logs. 198 typedef std::vector<LoggingRecordEntry> LoggingRecord; 199 class LogForwarder { 200 public: 201 virtual ~LogForwarder() = default; 202 virtual void Log(const std::string& aString) = 0; 203 virtual void CrashAction(LogReason aReason) = 0; 204 virtual bool UpdateStringsVector(const std::string& aString) = 0; 205 206 // Provide a copy of the logs to the caller. 207 virtual LoggingRecord LoggingRecordCopy() = 0; 208 }; 209 210 class NoLog { 211 public: 212 NoLog() = default; 213 ~NoLog() = default; 214 215 // No-op 216 MOZ_IMPLICIT NoLog(const NoLog&) = default; 217 218 template <typename T> 219 NoLog& operator<<(const T& aLogText) { 220 return *this; 221 } 222 }; 223 224 enum class LogOptions : int { 225 NoNewline = 0x01, 226 AutoPrefix = 0x02, 227 AssertOnCall = 0x04, 228 CrashAction = 0x08, 229 }; 230 231 template <typename T> 232 struct Hexa { 233 explicit Hexa(T aVal) : mVal(aVal) {} 234 T mVal; 235 }; 236 template <typename T> 237 Hexa<T> hexa(T val) { 238 return Hexa<T>(val); 239 } 240 241 #ifdef WIN32 242 void LogWStr(const wchar_t* aStr, std::stringstream& aOut); 243 #endif 244 245 template <int L, typename Logger = BasicLogger> 246 class Log final { 247 public: 248 // The default is to have the prefix, have the new line, and for critical 249 // logs assert on each call. 250 static int DefaultOptions(bool aWithAssert = true) { 251 return (int(LogOptions::AutoPrefix) | 252 (aWithAssert ? int(LogOptions::AssertOnCall) : 0)); 253 } 254 255 // Note that we're calling BasicLogger::ShouldOutputMessage, rather than 256 // Logger::ShouldOutputMessage. Since we currently don't have a different 257 // version of that method for different loggers, this is OK. Once we do, 258 // change BasicLogger::ShouldOutputMessage to Logger::ShouldOutputMessage. 259 explicit Log(int aOptions = Log::DefaultOptions(L == LOG_CRITICAL), 260 LogReason aReason = LogReason::MustBeMoreThanThis) 261 : mOptions(0), mLogIt(false) { 262 Init(aOptions, BasicLogger::ShouldOutputMessage(L), aReason); 263 } 264 265 ~Log() { Flush(); } 266 267 void Flush() { 268 if (MOZ_LIKELY(!LogIt())) return; 269 270 std::string str = mMessage.str(); 271 if (!str.empty()) { 272 WriteLog(str); 273 } 274 mMessage.str(""); 275 } 276 277 Log& operator<<(char aChar) { 278 if (MOZ_UNLIKELY(LogIt())) { 279 mMessage << aChar; 280 } 281 return *this; 282 } 283 Log& operator<<(const std::string& aLogText) { 284 if (MOZ_UNLIKELY(LogIt())) { 285 mMessage << aLogText; 286 } 287 return *this; 288 } 289 Log& operator<<(const char aStr[]) { 290 if (MOZ_UNLIKELY(LogIt())) { 291 mMessage << static_cast<const char*>(aStr); 292 } 293 return *this; 294 } 295 #ifdef WIN32 296 Log& operator<<(const wchar_t aWStr[]) { 297 if (MOZ_UNLIKELY(LogIt())) { 298 LogWStr(aWStr, mMessage); 299 } 300 return *this; 301 } 302 #endif 303 Log& operator<<(bool aBool) { 304 if (MOZ_UNLIKELY(LogIt())) { 305 mMessage << (aBool ? "true" : "false"); 306 } 307 return *this; 308 } 309 Log& operator<<(int aInt) { 310 if (MOZ_UNLIKELY(LogIt())) { 311 mMessage << aInt; 312 } 313 return *this; 314 } 315 Log& operator<<(unsigned int aInt) { 316 if (MOZ_UNLIKELY(LogIt())) { 317 mMessage << aInt; 318 } 319 return *this; 320 } 321 Log& operator<<(long aLong) { 322 if (MOZ_UNLIKELY(LogIt())) { 323 mMessage << aLong; 324 } 325 return *this; 326 } 327 Log& operator<<(unsigned long aLong) { 328 if (MOZ_UNLIKELY(LogIt())) { 329 mMessage << aLong; 330 } 331 return *this; 332 } 333 Log& operator<<(long long aLong) { 334 if (MOZ_UNLIKELY(LogIt())) { 335 mMessage << aLong; 336 } 337 return *this; 338 } 339 Log& operator<<(unsigned long long aLong) { 340 if (MOZ_UNLIKELY(LogIt())) { 341 mMessage << aLong; 342 } 343 return *this; 344 } 345 Log& operator<<(Float aFloat) { 346 if (MOZ_UNLIKELY(LogIt())) { 347 mMessage << aFloat; 348 } 349 return *this; 350 } 351 Log& operator<<(double aDouble) { 352 if (MOZ_UNLIKELY(LogIt())) { 353 mMessage << aDouble; 354 } 355 return *this; 356 } 357 Log& operator<<(const sRGBColor& aColor) { 358 if (MOZ_UNLIKELY(LogIt())) { 359 mMessage << "sRGBColor(" << aColor.r << ", " << aColor.g << ", " 360 << aColor.b << ", " << aColor.a << ")"; 361 } 362 return *this; 363 } 364 Log& operator<<(const DeviceColor& aColor) { 365 if (MOZ_UNLIKELY(LogIt())) { 366 mMessage << "DeviceColor(" << aColor.r << ", " << aColor.g << ", " 367 << aColor.b << ", " << aColor.a << ")"; 368 } 369 return *this; 370 } 371 template <typename T, typename Sub, typename Coord> 372 Log& operator<<(const BasePoint<T, Sub, Coord>& aPoint) { 373 if (MOZ_UNLIKELY(LogIt())) { 374 mMessage << "Point" << aPoint; 375 } 376 return *this; 377 } 378 template <typename T, typename Sub, typename Coord> 379 Log& operator<<(const BaseSize<T, Sub, Coord>& aSize) { 380 if (MOZ_UNLIKELY(LogIt())) { 381 mMessage << "Size(" << aSize.width << "," << aSize.height << ")"; 382 } 383 return *this; 384 } 385 template <typename T, typename Sub, typename Point, typename SizeT, 386 typename Margin> 387 Log& operator<<(const BaseRect<T, Sub, Point, SizeT, Margin>& aRect) { 388 if (MOZ_UNLIKELY(LogIt())) { 389 mMessage << "Rect" << aRect; 390 } 391 return *this; 392 } 393 Log& operator<<(const Matrix& aMatrix) { 394 if (MOZ_UNLIKELY(LogIt())) { 395 mMessage << "Matrix(" << aMatrix._11 << " " << aMatrix._12 << " ; " 396 << aMatrix._21 << " " << aMatrix._22 << " ; " << aMatrix._31 397 << " " << aMatrix._32 << ")"; 398 } 399 return *this; 400 } 401 template <typename T> 402 Log& operator<<(Hexa<T> aHex) { 403 if (MOZ_UNLIKELY(LogIt())) { 404 mMessage << std::showbase << std::hex << aHex.mVal << std::noshowbase 405 << std::dec; 406 } 407 return *this; 408 } 409 410 Log& operator<<(const SourceSurface* aSurface) { 411 if (MOZ_UNLIKELY(LogIt())) { 412 mMessage << "SourceSurface(" << (void*)(aSurface) << ")"; 413 } 414 return *this; 415 } 416 Log& operator<<(const Path* aPath) { 417 if (MOZ_UNLIKELY(LogIt())) { 418 mMessage << "Path(" << (void*)(aPath) << ")"; 419 } 420 return *this; 421 } 422 Log& operator<<(const Pattern* aPattern) { 423 if (MOZ_UNLIKELY(LogIt())) { 424 mMessage << "Pattern(" << (void*)(aPattern) << ")"; 425 } 426 return *this; 427 } 428 Log& operator<<(const ScaledFont* aFont) { 429 if (MOZ_UNLIKELY(LogIt())) { 430 mMessage << "ScaledFont(" << (void*)(aFont) << ")"; 431 } 432 return *this; 433 } 434 Log& operator<<(const FilterNode* aFilter) { 435 if (MOZ_UNLIKELY(LogIt())) { 436 mMessage << "FilterNode(" << (void*)(aFilter) << ")"; 437 } 438 return *this; 439 } 440 Log& operator<<(const DrawOptions& aOptions) { 441 if (MOZ_UNLIKELY(LogIt())) { 442 mMessage << "DrawOptions(" << aOptions.mAlpha << ", "; 443 (*this) << aOptions.mCompositionOp; 444 mMessage << ", "; 445 (*this) << aOptions.mAntialiasMode; 446 mMessage << ")"; 447 } 448 return *this; 449 } 450 Log& operator<<(const DrawSurfaceOptions& aOptions) { 451 if (MOZ_UNLIKELY(LogIt())) { 452 mMessage << "DrawSurfaceOptions("; 453 (*this) << aOptions.mSamplingFilter; 454 mMessage << ", "; 455 (*this) << aOptions.mSamplingBounds; 456 mMessage << ")"; 457 } 458 return *this; 459 } 460 461 Log& operator<<(SamplingBounds aBounds) { 462 if (MOZ_UNLIKELY(LogIt())) { 463 switch (aBounds) { 464 case SamplingBounds::UNBOUNDED: 465 mMessage << "SamplingBounds::UNBOUNDED"; 466 break; 467 case SamplingBounds::BOUNDED: 468 mMessage << "SamplingBounds::BOUNDED"; 469 break; 470 default: 471 mMessage << "Invalid SamplingBounds (" << (int)aBounds << ")"; 472 break; 473 } 474 } 475 return *this; 476 } 477 Log& operator<<(SamplingFilter aFilter) { 478 if (MOZ_UNLIKELY(LogIt())) { 479 switch (aFilter) { 480 case SamplingFilter::GOOD: 481 mMessage << "SamplingFilter::GOOD"; 482 break; 483 case SamplingFilter::LINEAR: 484 mMessage << "SamplingFilter::LINEAR"; 485 break; 486 case SamplingFilter::POINT: 487 mMessage << "SamplingFilter::POINT"; 488 break; 489 default: 490 mMessage << "Invalid SamplingFilter (" << (int)aFilter << ")"; 491 break; 492 } 493 } 494 return *this; 495 } 496 Log& operator<<(AntialiasMode aMode) { 497 if (MOZ_UNLIKELY(LogIt())) { 498 switch (aMode) { 499 case AntialiasMode::NONE: 500 mMessage << "AntialiasMode::NONE"; 501 break; 502 case AntialiasMode::GRAY: 503 mMessage << "AntialiasMode::GRAY"; 504 break; 505 case AntialiasMode::SUBPIXEL: 506 mMessage << "AntialiasMode::SUBPIXEL"; 507 break; 508 case AntialiasMode::DEFAULT: 509 mMessage << "AntialiasMode::DEFAULT"; 510 break; 511 default: 512 mMessage << "Invalid AntialiasMode (" << (int)aMode << ")"; 513 break; 514 } 515 } 516 return *this; 517 } 518 Log& operator<<(CompositionOp aOp) { 519 if (MOZ_UNLIKELY(LogIt())) { 520 switch (aOp) { 521 case CompositionOp::OP_CLEAR: 522 mMessage << "CompositionOp::OP_CLEAR"; 523 break; 524 case CompositionOp::OP_OVER: 525 mMessage << "CompositionOp::OP_OVER"; 526 break; 527 case CompositionOp::OP_ADD: 528 mMessage << "CompositionOp::OP_ADD"; 529 break; 530 case CompositionOp::OP_ATOP: 531 mMessage << "CompositionOp::OP_ATOP"; 532 break; 533 case CompositionOp::OP_OUT: 534 mMessage << "CompositionOp::OP_OUT"; 535 break; 536 case CompositionOp::OP_IN: 537 mMessage << "CompositionOp::OP_IN"; 538 break; 539 case CompositionOp::OP_SOURCE: 540 mMessage << "CompositionOp::OP_SOURCE"; 541 break; 542 case CompositionOp::OP_DEST_IN: 543 mMessage << "CompositionOp::OP_DEST_IN"; 544 break; 545 case CompositionOp::OP_DEST_OUT: 546 mMessage << "CompositionOp::OP_DEST_OUT"; 547 break; 548 case CompositionOp::OP_DEST_OVER: 549 mMessage << "CompositionOp::OP_DEST_OVER"; 550 break; 551 case CompositionOp::OP_DEST_ATOP: 552 mMessage << "CompositionOp::OP_DEST_ATOP"; 553 break; 554 case CompositionOp::OP_XOR: 555 mMessage << "CompositionOp::OP_XOR"; 556 break; 557 case CompositionOp::OP_MULTIPLY: 558 mMessage << "CompositionOp::OP_MULTIPLY"; 559 break; 560 case CompositionOp::OP_SCREEN: 561 mMessage << "CompositionOp::OP_SCREEN"; 562 break; 563 case CompositionOp::OP_OVERLAY: 564 mMessage << "CompositionOp::OP_OVERLAY"; 565 break; 566 case CompositionOp::OP_DARKEN: 567 mMessage << "CompositionOp::OP_DARKEN"; 568 break; 569 case CompositionOp::OP_LIGHTEN: 570 mMessage << "CompositionOp::OP_LIGHTEN"; 571 break; 572 case CompositionOp::OP_COLOR_DODGE: 573 mMessage << "CompositionOp::OP_COLOR_DODGE"; 574 break; 575 case CompositionOp::OP_COLOR_BURN: 576 mMessage << "CompositionOp::OP_COLOR_BURN"; 577 break; 578 case CompositionOp::OP_HARD_LIGHT: 579 mMessage << "CompositionOp::OP_HARD_LIGHT"; 580 break; 581 case CompositionOp::OP_SOFT_LIGHT: 582 mMessage << "CompositionOp::OP_SOFT_LIGHT"; 583 break; 584 case CompositionOp::OP_DIFFERENCE: 585 mMessage << "CompositionOp::OP_DIFFERENCE"; 586 break; 587 case CompositionOp::OP_EXCLUSION: 588 mMessage << "CompositionOp::OP_EXCLUSION"; 589 break; 590 case CompositionOp::OP_HUE: 591 mMessage << "CompositionOp::OP_HUE"; 592 break; 593 case CompositionOp::OP_SATURATION: 594 mMessage << "CompositionOp::OP_SATURATION"; 595 break; 596 case CompositionOp::OP_COLOR: 597 mMessage << "CompositionOp::OP_COLOR"; 598 break; 599 case CompositionOp::OP_LUMINOSITY: 600 mMessage << "CompositionOp::OP_LUMINOSITY"; 601 break; 602 case CompositionOp::OP_COUNT: 603 mMessage << "CompositionOp::OP_COUNT"; 604 break; 605 default: 606 mMessage << "Invalid CompositionOp (" << (int)aOp << ")"; 607 break; 608 } 609 } 610 return *this; 611 } 612 Log& operator<<(SurfaceFormat aFormat) { 613 if (MOZ_UNLIKELY(LogIt())) { 614 mMessage << aFormat; 615 } 616 return *this; 617 } 618 619 Log& operator<<(ColorDepth aColorDepth) { 620 if (MOZ_UNLIKELY(LogIt())) { 621 mMessage << aColorDepth; 622 } 623 return *this; 624 } 625 626 Log& operator<<(SurfaceType aType) { 627 if (MOZ_UNLIKELY(LogIt())) { 628 switch (aType) { 629 case SurfaceType::DATA: 630 mMessage << "SurfaceType::DATA"; 631 break; 632 case SurfaceType::CAIRO: 633 mMessage << "SurfaceType::CAIRO"; 634 break; 635 case SurfaceType::CAIRO_IMAGE: 636 mMessage << "SurfaceType::CAIRO_IMAGE"; 637 break; 638 case SurfaceType::COREGRAPHICS_IMAGE: 639 mMessage << "SurfaceType::COREGRAPHICS_IMAGE"; 640 break; 641 case SurfaceType::COREGRAPHICS_CGCONTEXT: 642 mMessage << "SurfaceType::COREGRAPHICS_CGCONTEXT"; 643 break; 644 case SurfaceType::SKIA: 645 mMessage << "SurfaceType::SKIA"; 646 break; 647 case SurfaceType::RECORDING: 648 mMessage << "SurfaceType::RECORDING"; 649 break; 650 case SurfaceType::DATA_SHARED: 651 mMessage << "SurfaceType::DATA_SHARED"; 652 break; 653 case SurfaceType::DATA_RECYCLING_SHARED: 654 mMessage << "SurfaceType::DATA_RECYCLING_SHARED"; 655 break; 656 case SurfaceType::DATA_ALIGNED: 657 mMessage << "SurfaceType::DATA_ALIGNED"; 658 break; 659 case SurfaceType::DATA_SHARED_WRAPPER: 660 mMessage << "SurfaceType::DATA_SHARED_WRAPPER"; 661 break; 662 case SurfaceType::DATA_MAPPED: 663 mMessage << "SurfaceType::DATA_MAPPED"; 664 break; 665 case SurfaceType::WEBGL: 666 mMessage << "SurfaceType::WEBGL"; 667 break; 668 default: 669 mMessage << "Invalid SurfaceType (" << (int)aType << ")"; 670 break; 671 } 672 } 673 return *this; 674 } 675 676 Log& operator<<(DeviceResetReason aReason) { 677 if (MOZ_UNLIKELY(LogIt())) { 678 switch (aReason) { 679 case DeviceResetReason::OK: 680 mMessage << "DeviceResetReason::OK"; 681 break; 682 case DeviceResetReason::HUNG: 683 mMessage << "DeviceResetReason::HUNG"; 684 break; 685 case DeviceResetReason::REMOVED: 686 mMessage << "DeviceResetReason::REMOVED"; 687 break; 688 case DeviceResetReason::RESET: 689 mMessage << "DeviceResetReason::RESET"; 690 break; 691 case DeviceResetReason::DRIVER_ERROR: 692 mMessage << "DeviceResetReason::DRIVER_ERROR"; 693 break; 694 case DeviceResetReason::INVALID_CALL: 695 mMessage << "DeviceResetReason::INVALID_CALL"; 696 break; 697 case DeviceResetReason::OUT_OF_MEMORY: 698 mMessage << "DeviceResetReason::OUT_OF_MEMORY"; 699 break; 700 case DeviceResetReason::FORCED_RESET: 701 mMessage << "DeviceResetReason::FORCED_RESET"; 702 break; 703 case DeviceResetReason::OTHER: 704 mMessage << "DeviceResetReason::OTHER"; 705 break; 706 case DeviceResetReason::NVIDIA_VIDEO: 707 mMessage << "DeviceResetReason::NVIDIA_VIDEO"; 708 break; 709 case DeviceResetReason::UNKNOWN: 710 mMessage << "DeviceResetReason::UNKNOWN"; 711 break; 712 default: 713 mMessage << "DeviceResetReason::UNKNOWN_REASON"; 714 break; 715 } 716 } 717 return *this; 718 } 719 720 Log& operator<<(DeviceResetDetectPlace aPlace) { 721 if (MOZ_UNLIKELY(LogIt())) { 722 switch (aPlace) { 723 case DeviceResetDetectPlace::WR_BEGIN_FRAME: 724 mMessage << "DeviceResetDetectPlace::WR_BEGIN_FRAME"; 725 break; 726 case DeviceResetDetectPlace::WR_WAIT_FOR_GPU: 727 mMessage << "DeviceResetDetectPlace::WR_WAIT_FOR_GPU"; 728 break; 729 case DeviceResetDetectPlace::WR_POST_UPDATE: 730 mMessage << "DeviceResetDetectPlace::WR_POST_UPDATE"; 731 break; 732 case DeviceResetDetectPlace::WR_SYNC_OBJRCT: 733 mMessage << "DeviceResetDetectPlace::WR_SYNC_OBJRCT"; 734 break; 735 case DeviceResetDetectPlace::WR_SIMULATE: 736 mMessage << "DeviceResetDetectPlace::WR_SIMULATE"; 737 break; 738 case DeviceResetDetectPlace::WIDGET: 739 mMessage << "DeviceResetDetectPlace::WIDGET"; 740 break; 741 case DeviceResetDetectPlace::CANVAS_TRANSLATOR: 742 mMessage << "DeviceResetDetectPlace::CANVAS_TRANSLATOR"; 743 break; 744 default: 745 mMessage << "DeviceResetDetectPlace::UNKNOWN_REASON"; 746 break; 747 } 748 } 749 return *this; 750 } 751 752 inline bool LogIt() const { return mLogIt; } 753 inline bool NoNewline() const { 754 return mOptions & int(LogOptions::NoNewline); 755 } 756 inline bool AutoPrefix() const { 757 return mOptions & int(LogOptions::AutoPrefix); 758 } 759 inline bool ValidReason() const { 760 return (int)mReason > (int)LogReason::MustBeMoreThanThis && 761 (int)mReason < (int)LogReason::MustBeLessThanThis; 762 } 763 764 // We do not want this version to do any work, and stringstream can't be 765 // copied anyway. It does come in handy for the "Once" macro defined below. 766 MOZ_IMPLICIT Log(const Log& log) { Init(log.mOptions, false, log.mReason); } 767 768 private: 769 // Initialization common to two constructors 770 void Init(int aOptions, bool aLogIt, LogReason aReason) { 771 mOptions = aOptions; 772 mReason = aReason; 773 mLogIt = aLogIt; 774 if (mLogIt) { 775 if (AutoPrefix()) { 776 if (mOptions & int(LogOptions::AssertOnCall)) { 777 mMessage << "[GFX" << L; 778 } else { 779 mMessage << "[GFX" << L << "-"; 780 } 781 } 782 if ((mOptions & int(LogOptions::CrashAction)) && ValidReason()) { 783 mMessage << " " << (int)mReason; 784 } 785 if (AutoPrefix()) { 786 mMessage << "]: "; 787 } 788 } 789 } 790 791 void WriteLog(const std::string& aString) { 792 if (MOZ_UNLIKELY(LogIt())) { 793 Logger::OutputMessage(aString, L, NoNewline()); 794 // Assert if required. We don't have a three parameter MOZ_ASSERT 795 // so use the underlying functions instead (see bug 1281702): 796 #ifdef DEBUG 797 if (mOptions & int(LogOptions::AssertOnCall)) { 798 MOZ_ReportAssertionFailure(aString.c_str(), __FILE__, __LINE__); 799 MOZ_CRASH("GFX: An assert from the graphics logger"); 800 } 801 #endif 802 if ((mOptions & int(LogOptions::CrashAction)) && ValidReason()) { 803 Logger::CrashAction(mReason); 804 } 805 } 806 } 807 808 std::stringstream mMessage; 809 int mOptions; 810 LogReason mReason; 811 bool mLogIt; 812 }; 813 814 typedef Log<LOG_DEBUG> DebugLog; 815 typedef Log<LOG_WARNING> WarningLog; 816 typedef Log<LOG_CRITICAL, CriticalLogger> CriticalLog; 817 818 // Macro to glue names to get us less chance of name clashing. 819 #if defined GFX_LOGGING_GLUE1 || defined GFX_LOGGING_GLUE 820 # error "Clash of the macro GFX_LOGGING_GLUE1 or GFX_LOGGING_GLUE" 821 #endif 822 #define GFX_LOGGING_GLUE1(x, y) x##y 823 #define GFX_LOGGING_GLUE(x, y) GFX_LOGGING_GLUE1(x, y) 824 825 // This log goes into crash reports, use with care. 826 #define gfxCriticalError mozilla::gfx::CriticalLog 827 #define gfxCriticalErrorOnce \ 828 static gfxCriticalError GFX_LOGGING_GLUE(sOnceAtLine, __LINE__) = \ 829 gfxCriticalError 830 831 // This is a shortcut for errors we want logged in crash reports/about support 832 // but we do not want asserting. These are available in all builds, so it is 833 // not worth trying to do magic to avoid matching the syntax of 834 // gfxCriticalError. 835 // So, this one is used as 836 // gfxCriticalNote << "Something to report and not assert"; 837 // while the critical error is 838 // gfxCriticalError() << "Something to report and assert"; 839 #define gfxCriticalNote \ 840 gfxCriticalError(gfxCriticalError::DefaultOptions(false)) 841 #define gfxCriticalNoteOnce \ 842 static gfxCriticalError GFX_LOGGING_GLUE(sOnceAtLine, __LINE__) = \ 843 gfxCriticalNote 844 845 // The "once" versions will only trigger the first time through. You can do 846 // this: gfxCriticalErrorOnce() << "This message only shows up once; instead of 847 // the usual: static bool firstTime = true; if (firstTime) { 848 // firstTime = false; 849 // gfxCriticalError() << "This message only shows up once; 850 // } 851 #if defined(DEBUG) 852 # define gfxDebug mozilla::gfx::DebugLog 853 # define gfxDebugOnce \ 854 static gfxDebug GFX_LOGGING_GLUE(sOnceAtLine, __LINE__) = gfxDebug 855 #else 856 # define gfxDebug \ 857 if (1) \ 858 ; \ 859 else \ 860 mozilla::gfx::NoLog 861 # define gfxDebugOnce \ 862 if (1) \ 863 ; \ 864 else \ 865 mozilla::gfx::NoLog 866 #endif 867 868 // Have gfxWarning available (behind a runtime preference) 869 #define gfxWarning mozilla::gfx::WarningLog 870 #define gfxWarningOnce \ 871 static gfxWarning GFX_LOGGING_GLUE(sOnceAtLine, __LINE__) = gfxWarning 872 873 // In the debug build, this is equivalent to the default gfxCriticalError. 874 // In the non-debug build, on nightly and dev edition, it will MOZ_CRASH. 875 // On beta and release versions, it will telemetry count, but proceed. 876 // 877 // You should create a (new) enum in the LogReason and use it for the reason 878 // parameter to ensure uniqueness. 879 #define gfxDevCrash(reason) \ 880 gfxCriticalError(int(gfx::LogOptions::AutoPrefix) | \ 881 int(gfx::LogOptions::AssertOnCall) | \ 882 int(gfx::LogOptions::CrashAction), \ 883 (reason)) 884 885 // See nsDebug.h and the NS_WARN_IF macro 886 887 #ifdef __cplusplus 888 // For now, have MOZ2D_ERROR_IF available in debug and non-debug builds 889 inline bool MOZ2D_error_if_impl(bool aCondition, const char* aExpr, 890 const char* aFile, int32_t aLine) { 891 if (MOZ_UNLIKELY(aCondition)) { 892 gfxCriticalError() << aExpr << " at " << aFile << ":" << aLine; 893 } 894 return aCondition; 895 } 896 # define MOZ2D_ERROR_IF(condition) \ 897 MOZ2D_error_if_impl(condition, #condition, __FILE__, __LINE__) 898 899 # ifdef DEBUG 900 inline bool MOZ2D_warn_if_impl(bool aCondition, const char* aExpr, 901 const char* aFile, int32_t aLine) { 902 if (MOZ_UNLIKELY(aCondition)) { 903 gfxWarning() << aExpr << " at " << aFile << ":" << aLine; 904 } 905 return aCondition; 906 } 907 # define MOZ2D_WARN_IF(condition) \ 908 MOZ2D_warn_if_impl(condition, #condition, __FILE__, __LINE__) 909 # else 910 # define MOZ2D_WARN_IF(condition) (bool)(condition) 911 # endif 912 #endif 913 914 const int INDENT_PER_LEVEL = 2; 915 916 template <int Level = LOG_DEBUG> 917 class TreeLog { 918 public: 919 explicit TreeLog(const std::string& aPrefix = "") 920 : mLog(int(LogOptions::NoNewline)), 921 mPrefix(aPrefix), 922 mDepth(0), 923 mStartOfLine(true), 924 mConditionedOnPref(false), 925 mPrefFunction(nullptr) {} 926 927 template <typename T> 928 TreeLog& operator<<(const T& aObject) { 929 if (mConditionedOnPref && !mPrefFunction()) { 930 return *this; 931 } 932 if (mStartOfLine) { 933 if (!mPrefix.empty()) { 934 mLog << '[' << mPrefix << "] "; 935 } 936 mLog << std::string(mDepth * INDENT_PER_LEVEL, ' '); 937 mStartOfLine = false; 938 } 939 mLog << aObject; 940 if (EndsInNewline(aObject)) { 941 // Don't indent right here as the user may change the indent 942 // between now and the first output to the next line. 943 mLog.Flush(); 944 mStartOfLine = true; 945 } 946 return *this; 947 } 948 949 void IncreaseIndent() { ++mDepth; } 950 void DecreaseIndent() { 951 MOZ_ASSERT(mDepth > 0); 952 --mDepth; 953 } 954 955 void ConditionOnPrefFunction(bool (*aPrefFunction)()) { 956 mConditionedOnPref = true; 957 mPrefFunction = aPrefFunction; 958 } 959 960 private: 961 Log<Level> mLog; 962 std::string mPrefix; 963 uint32_t mDepth; 964 bool mStartOfLine; 965 bool mConditionedOnPref; 966 bool (*mPrefFunction)(); 967 968 template <typename T> 969 static bool EndsInNewline(const T& aObject) { 970 return false; 971 } 972 973 static bool EndsInNewline(const std::string& aString) { 974 return !aString.empty() && aString[aString.length() - 1] == '\n'; 975 } 976 977 static bool EndsInNewline(char aChar) { return aChar == '\n'; } 978 979 static bool EndsInNewline(const char* aString) { 980 return EndsInNewline(std::string(aString)); 981 } 982 }; 983 984 template <int Level = LOG_DEBUG> 985 class TreeAutoIndent final { 986 public: 987 explicit TreeAutoIndent(TreeLog<Level>& aTreeLog) : mTreeLog(aTreeLog) { 988 mTreeLog.IncreaseIndent(); 989 } 990 991 TreeAutoIndent(const TreeAutoIndent& aTreeAutoIndent) 992 : mTreeLog(aTreeAutoIndent.mTreeLog) { 993 mTreeLog.IncreaseIndent(); 994 } 995 996 TreeAutoIndent& operator=(const TreeAutoIndent& aTreeAutoIndent) = delete; 997 998 ~TreeAutoIndent() { mTreeLog.DecreaseIndent(); } 999 1000 private: 1001 TreeLog<Level>& mTreeLog; 1002 }; 1003 1004 } // namespace gfx 1005 } // namespace mozilla 1006 1007 #endif /* MOZILLA_GFX_LOGGING_H_ */