xref: /aosp_15_r20/external/angle/src/common/log_utils.h (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1 //
2 // Copyright 2024 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 // log_utils.h: Logging and assert utilities. A lot of the logging code is adapted from Chromium's
8 // base/logging.h.
9 
10 #ifndef COMMON_LOG_UTILS_H_
11 #define COMMON_LOG_UTILS_H_
12 
13 #include <assert.h>
14 #include <stdio.h>
15 
16 #include <iomanip>
17 #include <ios>
18 #include <mutex>
19 #include <sstream>
20 #include <string>
21 
22 #include "common/angleutils.h"
23 #include "common/entry_points_enum_autogen.h"
24 #include "common/platform.h"
25 
26 namespace gl
27 {
28 class Context;
29 
30 using LogSeverity = int;
31 // Note: the log severities are used to index into the array of names,
32 // see g_logSeverityNames.
33 constexpr LogSeverity LOG_EVENT          = 0;
34 constexpr LogSeverity LOG_INFO           = 1;
35 constexpr LogSeverity LOG_WARN           = 2;
36 constexpr LogSeverity LOG_ERR            = 3;
37 constexpr LogSeverity LOG_FATAL          = 4;
38 constexpr LogSeverity LOG_NUM_SEVERITIES = 5;
39 
40 void Trace(LogSeverity severity, const char *message);
41 
42 // This class more or less represents a particular log message.  You
43 // create an instance of LogMessage and then stream stuff to it.
44 // When you finish streaming to it, ~LogMessage is called and the
45 // full message gets streamed to the appropriate destination.
46 //
47 // You shouldn't actually use LogMessage's constructor to log things,
48 // though.  You should use the ERR() and WARN() macros.
49 class LogMessage : angle::NonCopyable
50 {
51   public:
52     // Used for ANGLE_LOG(severity).
53     LogMessage(const char *file, const char *function, int line, LogSeverity severity);
54     ~LogMessage();
stream()55     std::ostream &stream() { return mStream; }
56 
57     LogSeverity getSeverity() const;
58     std::string getMessage() const;
59 
60   private:
61     const char *mFile;
62     const char *mFunction;
63     const int mLine;
64     const LogSeverity mSeverity;
65 
66     std::ostringstream mStream;
67 };
68 
69 bool ShouldBeginScopedEvent(const gl::Context *context);
70 
71 namespace priv
72 {
73 // This class is used to explicitly ignore values in the conditional logging macros. This avoids
74 // compiler warnings like "value computed is not used" and "statement has no effect".
75 class LogMessageVoidify
76 {
77   public:
LogMessageVoidify()78     LogMessageVoidify() {}
79     // This has to be an operator with a precedence lower than << but higher than ?:
80     void operator&(std::ostream &) {}
81 };
82 
83 extern std::ostream *gSwallowStream;
84 
85 // Used by ANGLE_LOG_IS_ON to lazy-evaluate stream arguments.
86 bool ShouldCreatePlatformLogMessage(LogSeverity severity);
87 
88 // N is the width of the output to the stream. The output is padded with zeros
89 // if value is less than N characters.
90 // S is the stream type, either ostream for ANSI or wostream for wide character.
91 // T is the type of the value to output to the stream.
92 // C is the type of characters - either char for ANSI or wchar_t for wide char.
93 template <int N, typename S, typename T, typename C>
FmtHex(S & stream,T value,const C * zeroX,C zero)94 S &FmtHex(S &stream, T value, const C *zeroX, C zero)
95 {
96     stream << zeroX;
97 
98     std::ios_base::fmtflags oldFlags = stream.flags();
99     std::streamsize oldWidth         = stream.width();
100     typename S::char_type oldFill    = stream.fill();
101 
102     stream << std::hex << std::uppercase << std::setw(N) << std::setfill(zero) << value;
103 
104     stream.flags(oldFlags);
105     stream.width(oldWidth);
106     stream.fill(oldFill);
107 
108     return stream;
109 }
110 
111 template <typename S, typename T, typename C>
FmtHexAutoSized(S & stream,T value,const C * prefix,const C * zeroX,C zero)112 S &FmtHexAutoSized(S &stream, T value, const C *prefix, const C *zeroX, C zero)
113 {
114     if (prefix)
115     {
116         stream << prefix;
117     }
118 
119     constexpr int N = sizeof(T) * 2;
120     return priv::FmtHex<N>(stream, value, zeroX, zero);
121 }
122 
123 template <typename T, typename C>
124 class FmtHexHelper
125 {
126   public:
FmtHexHelper(const C * prefix,T value)127     FmtHexHelper(const C *prefix, T value) : mPrefix(prefix), mValue(value) {}
FmtHexHelper(T value)128     explicit FmtHexHelper(T value) : mPrefix(nullptr), mValue(value) {}
129 
130   private:
131     const C *mPrefix;
132     T mValue;
133 
134     friend std::ostream &operator<<(std::ostream &os, const FmtHexHelper &fmt)
135     {
136         return FmtHexAutoSized(os, fmt.mValue, fmt.mPrefix, "0x", '0');
137     }
138 
139     friend std::wostream &operator<<(std::wostream &wos, const FmtHexHelper &fmt)
140     {
141         return FmtHexAutoSized(wos, fmt.mValue, fmt.mPrefix, L"0x", L'0');
142     }
143 };
144 
145 }  // namespace priv
146 
147 template <typename T, typename C = char>
FmtHex(T value)148 priv::FmtHexHelper<T, C> FmtHex(T value)
149 {
150     return priv::FmtHexHelper<T, C>(value);
151 }
152 
153 #if defined(ANGLE_PLATFORM_WINDOWS)
154 priv::FmtHexHelper<HRESULT, char> FmtHR(HRESULT value);
155 priv::FmtHexHelper<DWORD, char> FmtErr(DWORD value);
156 #endif  // defined(ANGLE_PLATFORM_WINDOWS)
157 
158 template <typename T>
FmtHex(std::ostream & os,T value)159 std::ostream &FmtHex(std::ostream &os, T value)
160 {
161     return priv::FmtHexAutoSized(os, value, "", "0x", '0');
162 }
163 
164 // A few definitions of macros that don't generate much code. These are used
165 // by ANGLE_LOG(). Since these are used all over our code, it's
166 // better to have compact code for these operations.
167 #define COMPACT_ANGLE_LOG_EX_EVENT(ClassName, ...) \
168     ::gl::ClassName(__FILE__, __FUNCTION__, __LINE__, ::gl::LOG_EVENT, ##__VA_ARGS__)
169 #define COMPACT_ANGLE_LOG_EX_INFO(ClassName, ...) \
170     ::gl::ClassName(__FILE__, __FUNCTION__, __LINE__, ::gl::LOG_INFO, ##__VA_ARGS__)
171 #define COMPACT_ANGLE_LOG_EX_WARN(ClassName, ...) \
172     ::gl::ClassName(__FILE__, __FUNCTION__, __LINE__, ::gl::LOG_WARN, ##__VA_ARGS__)
173 #define COMPACT_ANGLE_LOG_EX_ERR(ClassName, ...) \
174     ::gl::ClassName(__FILE__, __FUNCTION__, __LINE__, ::gl::LOG_ERR, ##__VA_ARGS__)
175 #define COMPACT_ANGLE_LOG_EX_FATAL(ClassName, ...) \
176     ::gl::ClassName(__FILE__, __FUNCTION__, __LINE__, ::gl::LOG_FATAL, ##__VA_ARGS__)
177 
178 #define COMPACT_ANGLE_LOG_EVENT COMPACT_ANGLE_LOG_EX_EVENT(LogMessage)
179 #define COMPACT_ANGLE_LOG_INFO COMPACT_ANGLE_LOG_EX_INFO(LogMessage)
180 #define COMPACT_ANGLE_LOG_WARN COMPACT_ANGLE_LOG_EX_WARN(LogMessage)
181 #define COMPACT_ANGLE_LOG_ERR COMPACT_ANGLE_LOG_EX_ERR(LogMessage)
182 #define COMPACT_ANGLE_LOG_FATAL COMPACT_ANGLE_LOG_EX_FATAL(LogMessage)
183 
184 #define ANGLE_LOG_IS_ON(severity) (::gl::priv::ShouldCreatePlatformLogMessage(::gl::LOG_##severity))
185 
186 // Helper macro which avoids evaluating the arguments to a stream if the condition doesn't hold.
187 // Condition is evaluated once and only once.
188 #define ANGLE_LAZY_STREAM(stream, condition) \
189     !(condition) ? static_cast<void>(0) : ::gl::priv::LogMessageVoidify() & (stream)
190 
191 // We use the preprocessor's merging operator, "##", so that, e.g.,
192 // ANGLE_LOG(EVENT) becomes the token COMPACT_ANGLE_LOG_EVENT.  There's some funny
193 // subtle difference between ostream member streaming functions (e.g.,
194 // ostream::operator<<(int) and ostream non-member streaming functions
195 // (e.g., ::operator<<(ostream&, string&): it turns out that it's
196 // impossible to stream something like a string directly to an unnamed
197 // ostream. We employ a neat hack by calling the stream() member
198 // function of LogMessage which seems to avoid the problem.
199 #define ANGLE_LOG_STREAM(severity) COMPACT_ANGLE_LOG_##severity.stream()
200 
201 #define ANGLE_LOG(severity) ANGLE_LAZY_STREAM(ANGLE_LOG_STREAM(severity), ANGLE_LOG_IS_ON(severity))
202 
203 }  // namespace gl
204 
205 #if defined(ANGLE_ENABLE_DEBUG_TRACE) || defined(ANGLE_ENABLE_DEBUG_ANNOTATIONS)
206 #    define ANGLE_TRACE_ENABLED
207 #endif
208 
209 #if !defined(NDEBUG) || defined(ANGLE_ASSERT_ALWAYS_ON)
210 #    define ANGLE_ENABLE_ASSERTS
211 #endif
212 
213 #define INFO() ANGLE_LOG(INFO)
214 #define WARN() ANGLE_LOG(WARN)
215 #define ERR() ANGLE_LOG(ERR)
216 #define FATAL() ANGLE_LOG(FATAL)
217 
218 // A macro to log a performance event around a scope.
219 #if defined(ANGLE_TRACE_ENABLED)
220 #    if defined(_MSC_VER)
221 #        define EVENT(context, entryPoint, message, ...)                                     \
222             gl::ScopedPerfEventHelper scopedPerfEventHelper##__LINE__(                       \
223                 context, angle::EntryPoint::entryPoint);                                     \
224             do                                                                               \
225             {                                                                                \
226                 if (gl::ShouldBeginScopedEvent(context))                                     \
227                 {                                                                            \
228                     scopedPerfEventHelper##__LINE__.begin(                                   \
229                         "%s(" message ")", GetEntryPointName(angle::EntryPoint::entryPoint), \
230                         __VA_ARGS__);                                                        \
231                 }                                                                            \
232             } while (0)
233 #    else
234 #        define EVENT(context, entryPoint, message, ...)                                          \
235             gl::ScopedPerfEventHelper scopedPerfEventHelper(context,                              \
236                                                             angle::EntryPoint::entryPoint);       \
237             do                                                                                    \
238             {                                                                                     \
239                 if (gl::ShouldBeginScopedEvent(context))                                          \
240                 {                                                                                 \
241                     scopedPerfEventHelper.begin("%s(" message ")",                                \
242                                                 GetEntryPointName(angle::EntryPoint::entryPoint), \
243                                                 ##__VA_ARGS__);                                   \
244                 }                                                                                 \
245             } while (0)
246 #    endif  // _MSC_VER
247 #else
248 #    define EVENT(message, ...) (void(0))
249 #endif
250 
251 // Note that gSwallowStream is used instead of an arbitrary LOG() stream to avoid the creation of an
252 // object with a non-trivial destructor (LogMessage). On MSVC x86 (checked on 2015 Update 3), this
253 // causes a few additional pointless instructions to be emitted even at full optimization level,
254 // even though the : arm of the ternary operator is clearly never executed. Using a simpler object
255 // to be &'d with Voidify() avoids these extra instructions. Using a simpler POD object with a
256 // templated operator<< also works to avoid these instructions. However, this causes warnings on
257 // statically defined implementations of operator<<(std::ostream, ...) in some .cpp files, because
258 // they become defined-but-unreferenced functions. A reinterpret_cast of 0 to an ostream* also is
259 // not suitable, because some compilers warn of undefined behavior.
260 #define ANGLE_EAT_STREAM_PARAMETERS \
261     true ? static_cast<void>(0) : ::gl::priv::LogMessageVoidify() & (*::gl::priv::gSwallowStream)
262 
263 // A macro asserting a condition and outputting failures to the debug log
264 #if defined(ANGLE_ENABLE_ASSERTS)
265 #    define ASSERT(expression)                                                                \
266         (expression ? static_cast<void>(0)                                                    \
267                     : (FATAL() << "\t! Assert failed in " << __FUNCTION__ << " (" << __FILE__ \
268                                << ":" << __LINE__ << "): " << #expression))
269 #else
270 #    define ASSERT(condition) ANGLE_EAT_STREAM_PARAMETERS << !(condition)
271 #endif  // defined(ANGLE_ENABLE_ASSERTS)
272 
273 // A macro to indicate unimplemented functionality
274 #ifndef NOASSERT_UNIMPLEMENTED
275 #    define NOASSERT_UNIMPLEMENTED 1
276 #endif
277 
278 #if defined(ANGLE_TRACE_ENABLED) || defined(ANGLE_ENABLE_ASSERTS)
279 #    define UNIMPLEMENTED()                                                                       \
280         do                                                                                        \
281         {                                                                                         \
282             WARN() << "\t! Unimplemented: " << __FUNCTION__ << "(" << __FILE__ << ":" << __LINE__ \
283                    << ")";                                                                        \
284             ASSERT(NOASSERT_UNIMPLEMENTED);                                                       \
285         } while (0)
286 
287 // A macro for code which is not expected to be reached under valid assumptions
288 #    define UNREACHABLE()                                                                    \
289         do                                                                                   \
290         {                                                                                    \
291             FATAL() << "\t! Unreachable reached: " << __FUNCTION__ << "(" << __FILE__ << ":" \
292                     << __LINE__ << ")";                                                      \
293         } while (0)
294 #else
295 #    define UNIMPLEMENTED()                 \
296         do                                  \
297         {                                   \
298             ASSERT(NOASSERT_UNIMPLEMENTED); \
299         } while (0)
300 
301 // A macro for code which is not expected to be reached under valid assumptions
302 #    define UNREACHABLE()  \
303         do                 \
304         {                  \
305             ASSERT(false); \
306         } while (0)
307 #endif  // defined(ANGLE_TRACE_ENABLED) || defined(ANGLE_ENABLE_ASSERTS)
308 
309 #if defined(ANGLE_PLATFORM_WINDOWS)
310 #    define ANGLE_FUNCTION __FUNCTION__
311 #else
312 #    define ANGLE_FUNCTION __func__
313 #endif
314 
315 #endif  // COMMON_LOG_UTILS_H_
316