Line data Source code
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_logging_h
8 : #define mozilla_logging_h
9 :
10 : #include <string.h>
11 : #include <stdarg.h>
12 :
13 : #include "mozilla/Assertions.h"
14 : #include "mozilla/Atomics.h"
15 : #include "mozilla/Attributes.h"
16 : #include "mozilla/Likely.h"
17 : #include "mozilla/Poison.h"
18 :
19 : // We normally have logging enabled everywhere, but measurements showed that
20 : // having logging enabled on Android is quite expensive (hundreds of kilobytes
21 : // for both the format strings for logging and the code to perform all the
22 : // logging calls). Because retrieving logs from a mobile device is
23 : // comparatively more difficult for Android than it is for desktop and because
24 : // desktop machines tend to be less space/bandwidth-constrained than Android
25 : // devices, we've chosen to leave logging enabled on desktop, but disabled on
26 : // Android. Given that logging can still be useful for development purposes,
27 : // however, we leave logging enabled on Android developer builds.
28 : #if !defined(ANDROID) || !defined(RELEASE_OR_BETA)
29 : #define MOZ_LOGGING_ENABLED 1
30 : #else
31 : #define MOZ_LOGGING_ENABLED 0
32 : #endif
33 :
34 : namespace mozilla {
35 :
36 : // While not a 100% mapping to PR_LOG's numeric values, mozilla::LogLevel does
37 : // maintain a direct mapping for the Disabled, Debug and Verbose levels.
38 : //
39 : // Mappings of LogLevel to PR_LOG's numeric values:
40 : //
41 : // +---------+------------------+-----------------+
42 : // | Numeric | NSPR Logging | Mozilla Logging |
43 : // +---------+------------------+-----------------+
44 : // | 0 | PR_LOG_NONE | Disabled |
45 : // | 1 | PR_LOG_ALWAYS | Error |
46 : // | 2 | PR_LOG_ERROR | Warning |
47 : // | 3 | PR_LOG_WARNING | Info |
48 : // | 4 | PR_LOG_DEBUG | Debug |
49 : // | 5 | PR_LOG_DEBUG + 1 | Verbose |
50 : // +---------+------------------+-----------------+
51 : //
52 : enum class LogLevel {
53 : Disabled = 0,
54 : Error,
55 : Warning,
56 : Info,
57 : Debug,
58 : Verbose,
59 : };
60 :
61 : /**
62 : * Safely converts an integer into a valid LogLevel.
63 : */
64 : LogLevel ToLogLevel(int32_t aLevel);
65 :
66 : class LogModule
67 : {
68 : public:
69 0 : ~LogModule() { ::free(mName); }
70 :
71 : /**
72 : * Retrieves the module with the given name. If it does not already exist
73 : * it will be created.
74 : *
75 : * @param aName The name of the module.
76 : * @return A log module for the given name. This may be shared.
77 : */
78 : static LogModule* Get(const char* aName);
79 :
80 : /**
81 : * Logging processes -MOZ_LOG and -MOZ_LOG_FILE command line arguments
82 : * to override or set modules and the file as if passed through MOZ_LOG
83 : * and MOZ_LOG_FILE env vars. It's fine to pass (0, nullptr) if args
84 : * are not accessible in the caller's context, it will just do nothing.
85 : * Note that the args take effect (are processed) only when this function
86 : * is called the first time.
87 : */
88 : static void Init(int argc, char* argv[]);
89 :
90 : /**
91 : * Sets the log file to the given filename.
92 : */
93 : static void SetLogFile(const char* aFilename);
94 :
95 : /**
96 : * @param aBuffer - pointer to a buffer
97 : * @param aLength - the length of the buffer
98 : *
99 : * @return the actual length of the filepath.
100 : */
101 : static uint32_t GetLogFile(char *aBuffer, size_t aLength);
102 :
103 : /**
104 : * @param aAddTimestamp If we should log a time stamp with every message.
105 : */
106 : static void SetAddTimestamp(bool aAddTimestamp);
107 :
108 : /**
109 : * @param aIsSync If we should flush the file after every logged message.
110 : */
111 : static void SetIsSync(bool aIsSync);
112 :
113 : /**
114 : * Indicates whether or not the given log level is enabled.
115 : */
116 399003 : bool ShouldLog(LogLevel aLevel) const { return mLevel >= aLevel; }
117 :
118 : /**
119 : * Retrieves the log module's current level.
120 : */
121 1468 : LogLevel Level() const { return mLevel; }
122 :
123 : /**
124 : * Sets the log module's level.
125 : */
126 0 : void SetLevel(LogLevel level) { mLevel = level; }
127 :
128 : /**
129 : * Print a log message for this module.
130 : */
131 : void Printv(LogLevel aLevel, const char* aFmt, va_list aArgs) const MOZ_FORMAT_PRINTF(3, 0);
132 :
133 : /**
134 : * Retrieves the module name.
135 : */
136 : const char* Name() const { return mName; }
137 :
138 : private:
139 : friend class LogModuleManager;
140 :
141 : explicit LogModule(const char* aName, LogLevel aLevel)
142 198 : : mName(strdup(aName)), mLevel(aLevel)
143 : {
144 : }
145 :
146 : LogModule(LogModule&) = delete;
147 : LogModule& operator=(const LogModule&) = delete;
148 :
149 : char* mName;
150 : Atomic<LogLevel, Relaxed> mLevel;
151 : };
152 :
153 : /**
154 : * Helper class that lazy loads the given log module. This is safe to use for
155 : * declaring static references to log modules and can be used as a replacement
156 : * for accessing a LogModule directly.
157 : *
158 : * Example usage:
159 : * static LazyLogModule sLayoutLog("layout");
160 : *
161 : * void Foo() {
162 : * MOZ_LOG(sLayoutLog, LogLevel::Verbose, ("Entering foo"));
163 : * }
164 : */
165 : class LazyLogModule final
166 : {
167 : public:
168 : explicit constexpr LazyLogModule(const char* aLogName)
169 0 : : mLogName(aLogName)
170 3 : , mLog(nullptr)
171 : {
172 : }
173 :
174 : operator LogModule*();
175 :
176 : private:
177 : const char* const mLogName;
178 : const CorruptionCanaryForStatics mCanary;
179 : Atomic<LogModule*, ReleaseAcquire> mLog;
180 : };
181 :
182 : namespace detail {
183 :
184 0 : inline bool log_test(const LogModule* module, LogLevel level) {
185 : MOZ_ASSERT(level != LogLevel::Disabled);
186 : return module && module->ShouldLog(level);
187 : }
188 :
189 : void log_print(const LogModule* aModule,
190 : LogLevel aLevel,
191 : const char* aFmt, ...) MOZ_FORMAT_PRINTF(3, 4);
192 : } // namespace detail
193 :
194 : } // namespace mozilla
195 :
196 : // Helper macro used convert MOZ_LOG's third parameter, |_args|, from a
197 : // parenthesized form to a varargs form. For example:
198 : // ("%s", "a message") => "%s", "a message"
199 : #define MOZ_LOG_EXPAND_ARGS(...) __VA_ARGS__
200 :
201 : #if MOZ_LOGGING_ENABLED
202 : #define MOZ_LOG_TEST(_module,_level) mozilla::detail::log_test(_module, _level)
203 : #else
204 : // Define away MOZ_LOG_TEST here so the compiler will fold away entire
205 : // logging blocks via dead code elimination, e.g.:
206 : //
207 : // if (MOZ_LOG_TEST(...)) {
208 : // ...compute things to log and log them...
209 : // }
210 : #define MOZ_LOG_TEST(_module,_level) false
211 : #endif
212 :
213 : // The natural definition of the MOZ_LOG macro would expand to:
214 : //
215 : // do {
216 : // if (MOZ_LOG_TEST(_module, _level)) {
217 : // mozilla::detail::log_print(_module, ...);
218 : // }
219 : // } while (0)
220 : //
221 : // However, since _module is a LazyLogModule, and we need to call
222 : // LazyLogModule::operator() to get a LogModule* for the MOZ_LOG_TEST
223 : // macro and for the logging call, we'll wind up doing *two* calls, one
224 : // for each, rather than a single call. The compiler is not able to
225 : // fold the two calls into one, and the extra call can have a
226 : // significant effect on code size. (Making LazyLogModule::operator() a
227 : // `const` function does not have any effect.)
228 : //
229 : // Therefore, we will have to make a single call ourselves. But again,
230 : // the natural definition:
231 : //
232 : // do {
233 : // ::mozilla::LogModule* real_module = _module;
234 : // if (MOZ_LOG_TEST(real_module, _level)) {
235 : // mozilla::detail::log_print(real_module, ...);
236 : // }
237 : // } while (0)
238 : //
239 : // also has a problem: if logging is disabled, then we will call
240 : // LazyLogModule::operator() unnecessarily, and the compiler will not be
241 : // able to optimize away the call as dead code. We would like to avoid
242 : // such a scenario, as the whole point of disabling logging is for the
243 : // logging statements to not generate any code.
244 : //
245 : // Therefore, we need different definitions of MOZ_LOG, depending on
246 : // whether logging is enabled or not. (We need an actual definition of
247 : // MOZ_LOG even when logging is disabled to ensure the compiler sees that
248 : // variables only used during logging code are actually used, even if the
249 : // code will never be executed.) Hence, the following code.
250 : #if MOZ_LOGGING_ENABLED
251 : #define MOZ_LOG(_module,_level,_args) \
252 : do { \
253 : const ::mozilla::LogModule* moz_real_module = _module; \
254 : if (MOZ_LOG_TEST(moz_real_module,_level)) { \
255 : mozilla::detail::log_print(moz_real_module, _level, MOZ_LOG_EXPAND_ARGS _args); \
256 : } \
257 : } while (0)
258 : #else
259 : #define MOZ_LOG(_module,_level,_args) \
260 : do { \
261 : if (MOZ_LOG_TEST(_module,_level)) { \
262 : mozilla::detail::log_print(_module, _level, MOZ_LOG_EXPAND_ARGS _args); \
263 : } \
264 : } while (0)
265 : #endif
266 :
267 : // This #define is a Logging.h-only knob! Don't encourage people to get fancy
268 : // with their log definitions by exporting it outside of Logging.h.
269 : #undef MOZ_LOGGING_ENABLED
270 :
271 : #endif // mozilla_logging_h
|