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