Bug 1931425 - Limit how often moz-label's #setStyles runs r=reusable-components-revie...
[gecko.git] / tools / profiler / core / platform.h
blob4a3835c33f8ab53f57ea76e6b51b9a42a7ab719e
1 // Copyright (c) 2006-2011 The Chromium Authors. All rights reserved.
2 //
3 // Redistribution and use in source and binary forms, with or without
4 // modification, are permitted provided that the following conditions
5 // are met:
6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above copyright
9 // notice, this list of conditions and the following disclaimer in
10 // the documentation and/or other materials provided with the
11 // distribution.
12 // * Neither the name of Google, Inc. nor the names of its contributors
13 // may be used to endorse or promote products derived from this
14 // software without specific prior written permission.
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
20 // COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
21 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22 // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
23 // OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 // AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
26 // OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 // SUCH DAMAGE.
29 #ifndef TOOLS_PLATFORM_H_
30 #define TOOLS_PLATFORM_H_
32 #include "PlatformMacros.h"
34 #include "json/json.h"
35 #include "mozilla/Atomics.h"
36 #include "mozilla/BaseProfilerDetail.h"
37 #include "mozilla/Logging.h"
38 #include "mozilla/MathAlgorithms.h"
39 #include "mozilla/ProfileBufferEntrySerialization.h"
40 #include "mozilla/ProfileJSONWriter.h"
41 #include "mozilla/ProfilerUtils.h"
42 #include "mozilla/ProgressLogger.h"
43 #include "mozilla/TimeStamp.h"
44 #include "mozilla/UniquePtr.h"
45 #include "mozilla/Vector.h"
46 #include "nsString.h"
47 #include "SharedLibraries.h"
49 #include <cstddef>
50 #include <cstdint>
51 #include <functional>
53 class ProfilerCodeAddressService;
55 namespace mozilla {
56 struct SymbolTable;
59 extern mozilla::LazyLogModule gProfilerLog;
61 // These are for MOZ_LOG="prof:3" or higher. It's the default logging level for
62 // the profiler, and should be used sparingly.
63 #define LOG_TEST MOZ_LOG_TEST(gProfilerLog, mozilla::LogLevel::Info)
64 #define LOG(arg, ...) \
65 MOZ_LOG(gProfilerLog, mozilla::LogLevel::Info, \
66 ("[%" PRIu64 "] " arg, \
67 uint64_t(profiler_current_process_id().ToNumber()), ##__VA_ARGS__))
69 // These are for MOZ_LOG="prof:4" or higher. It should be used for logging that
70 // is somewhat more verbose than LOG.
71 #define DEBUG_LOG_TEST MOZ_LOG_TEST(gProfilerLog, mozilla::LogLevel::Debug)
72 #define DEBUG_LOG(arg, ...) \
73 MOZ_LOG(gProfilerLog, mozilla::LogLevel::Debug, \
74 ("[%" PRIu64 "] " arg, \
75 uint64_t(profiler_current_process_id().ToNumber()), ##__VA_ARGS__))
77 typedef uint8_t* Address;
79 // Stringify the given JSON value, in the most compact format.
80 // Note: Numbers are limited to a precision of 6 decimal digits, so that
81 // timestamps in ms have a precision in ns.
82 Json::String ToCompactString(const Json::Value& aJsonValue);
84 // Profiling log stored in a Json::Value. The actual log only exists while the
85 // profiler is running, and will be inserted at the end of the JSON profile.
86 class ProfilingLog {
87 public:
88 // These will be called by ActivePS when the profiler starts/stops.
89 static void Init();
90 static void Destroy();
92 // Access the profiling log JSON object, in order to modify it.
93 // Only calls the given function if the profiler is active.
94 // Thread-safe. But `aF` must not call other locking profiler functions.
95 // This is intended to capture some internal logging that doesn't belong in
96 // other places like markers. The log is accessible through the JS console on
97 // profiler.firefox.com, in the `profile.profilingLog` object; the data format
98 // is intentionally not defined, and not intended to be shown in the
99 // front-end.
100 // Please use caution not to output too much data.
101 template <typename F>
102 static void Access(F&& aF) {
103 mozilla::baseprofiler::detail::BaseProfilerAutoLock lock{gMutex};
104 if (gLog) {
105 std::forward<F>(aF)(*gLog);
109 #define DURATION_JSON_SUFFIX "_ms"
111 // Convert a TimeDuration to the value to be stored in the log.
112 // Use DURATION_JSON_SUFFIX as suffix in the property name.
113 static Json::Value Duration(const mozilla::TimeDuration& aDuration) {
114 return Json::Value{aDuration.ToMilliseconds()};
117 #define TIMESTAMP_JSON_SUFFIX "_TSms"
119 // Convert a TimeStamp to the value to be stored in the log.
120 // Use TIMESTAMP_JSON_SUFFIX as suffix in the property name.
121 static Json::Value Timestamp(
122 const mozilla::TimeStamp& aTimestamp = mozilla::TimeStamp::Now()) {
123 if (aTimestamp.IsNull()) {
124 return Json::Value{0.0};
126 return Duration(aTimestamp - mozilla::TimeStamp::ProcessCreation());
129 static bool IsLockedOnCurrentThread();
131 private:
132 static mozilla::baseprofiler::detail::BaseProfilerMutex gMutex;
133 static mozilla::UniquePtr<Json::Value> gLog;
136 // ----------------------------------------------------------------------------
137 // Miscellaneous
139 // If positive, skip stack-sampling in the sampler thread loop.
140 // Users should increment it atomically when samplings should be avoided, and
141 // later decrement it back. Multiple uses can overlap.
142 // There could be a sampling in progress when this is first incremented, so if
143 // it is critical to prevent any sampling, lock the profiler mutex instead.
144 // Relaxed ordering, because it's used to request that the profiler pause
145 // future sampling; this is not time critical, nor dependent on anything else.
146 extern mozilla::Atomic<int, mozilla::MemoryOrdering::Relaxed> gSkipSampling;
148 void AppendSharedLibraries(mozilla::JSONWriter& aWriter,
149 const SharedLibraryInfo& aInfo);
151 // Convert the array of strings to a bitfield.
152 uint32_t ParseFeaturesFromStringArray(const char** aFeatures,
153 uint32_t aFeatureCount,
154 bool aIsStartup = false);
156 // Add the begin/end 'Awake' markers for the thread.
157 void profiler_mark_thread_awake();
159 void profiler_mark_thread_asleep();
161 [[nodiscard]] bool profiler_get_profile_json(
162 SpliceableChunkedJSONWriter& aSpliceableChunkedJSONWriter,
163 double aSinceTime, bool aIsShuttingDown,
164 mozilla::ProgressLogger aProgressLogger);
166 // Flags to conveniently track various JS instrumentations.
167 enum class JSInstrumentationFlags {
168 StackSampling = 0x1,
169 Allocations = 0x2,
172 // Write out the information of the active profiling configuration.
173 void profiler_write_active_configuration(mozilla::JSONWriter& aWriter);
175 // Extract all received exit profiles that have not yet expired (i.e., they
176 // still intersect with this process' buffer range).
177 mozilla::Vector<nsCString> profiler_move_exit_profiles();
179 // If the "MOZ_PROFILER_SYMBOLICATE" env-var is set, we return a new
180 // ProfilerCodeAddressService object to use for local symbolication of profiles.
181 // This is off by default, and mainly intended for local development.
182 mozilla::UniquePtr<ProfilerCodeAddressService>
183 profiler_code_address_service_for_presymbolication();
185 extern "C" {
186 // This function is defined in the profiler rust module at
187 // tools/profiler/rust-helper. mozilla::SymbolTable and CompactSymbolTable
188 // have identical memory layout.
189 bool profiler_get_symbol_table(const char* debug_path, const char* breakpad_id,
190 mozilla::SymbolTable* symbol_table);
192 bool profiler_demangle_rust(const char* mangled, char* buffer, size_t len);
195 // For each running times value, call MACRO(index, name, unit, jsonProperty)
196 #define PROFILER_FOR_EACH_RUNNING_TIME(MACRO) \
197 MACRO(0, ThreadCPU, Delta, threadCPUDelta)
199 // This class contains all "running times" such as CPU usage measurements.
200 // All measurements are listed in `PROFILER_FOR_EACH_RUNNING_TIME` above.
201 // Each measurement is optional and only takes a value when explicitly set.
202 // Two RunningTimes object may be subtracted, to get the difference between
203 // known values.
204 class RunningTimes {
205 public:
206 constexpr RunningTimes() = default;
208 // Constructor with only a timestamp, useful when no measurements will be
209 // taken.
210 constexpr explicit RunningTimes(const mozilla::TimeStamp& aTimeStamp)
211 : mPostMeasurementTimeStamp(aTimeStamp) {}
213 constexpr void Clear() { *this = RunningTimes{}; }
215 constexpr bool IsEmpty() const { return mKnownBits == 0; }
217 // This should be called right after CPU measurements have been taken.
218 void SetPostMeasurementTimeStamp(const mozilla::TimeStamp& aTimeStamp) {
219 mPostMeasurementTimeStamp = aTimeStamp;
222 const mozilla::TimeStamp& PostMeasurementTimeStamp() const {
223 return mPostMeasurementTimeStamp;
226 // Should be filled for any registered thread.
228 #define RUNNING_TIME_MEMBER(index, name, unit, jsonProperty) \
229 constexpr bool Is##name##unit##Known() const { \
230 return (mKnownBits & mGot##name##unit) != 0; \
233 constexpr void Clear##name##unit() { \
234 m##name##unit = 0; \
235 mKnownBits &= ~mGot##name##unit; \
238 constexpr void Reset##name##unit(uint64_t a##name##unit) { \
239 m##name##unit = a##name##unit; \
240 mKnownBits |= mGot##name##unit; \
243 constexpr void Set##name##unit(uint64_t a##name##unit) { \
244 MOZ_ASSERT(!Is##name##unit##Known(), #name #unit " already set"); \
245 Reset##name##unit(a##name##unit); \
248 constexpr mozilla::Maybe<uint64_t> Get##name##unit() const { \
249 if (Is##name##unit##Known()) { \
250 return mozilla::Some(m##name##unit); \
252 return mozilla::Nothing{}; \
255 constexpr mozilla::Maybe<uint64_t> GetJson##name##unit() const { \
256 if (Is##name##unit##Known()) { \
257 return mozilla::Some(ConvertRawToJson(m##name##unit)); \
259 return mozilla::Nothing{}; \
262 PROFILER_FOR_EACH_RUNNING_TIME(RUNNING_TIME_MEMBER)
264 #undef RUNNING_TIME_MEMBER
266 // Take values from another RunningTimes.
267 RunningTimes& TakeFrom(RunningTimes& aOther) {
268 if (!aOther.IsEmpty()) {
269 #define RUNNING_TIME_TAKE(index, name, unit, jsonProperty) \
270 if (aOther.Is##name##unit##Known()) { \
271 Set##name##unit(std::exchange(aOther.m##name##unit, 0)); \
274 PROFILER_FOR_EACH_RUNNING_TIME(RUNNING_TIME_TAKE)
276 #undef RUNNING_TIME_TAKE
278 aOther.mKnownBits = 0;
280 return *this;
283 // Difference from `aBefore` to `this`. Any unknown makes the result unknown.
284 // PostMeasurementTimeStamp set to `this` PostMeasurementTimeStamp, to keep
285 // the most recent timestamp associated with the end of the interval over
286 // which the difference applies.
287 RunningTimes operator-(const RunningTimes& aBefore) const {
288 RunningTimes diff;
289 diff.mPostMeasurementTimeStamp = mPostMeasurementTimeStamp;
290 #define RUNNING_TIME_SUB(index, name, unit, jsonProperty) \
291 if (Is##name##unit##Known() && aBefore.Is##name##unit##Known()) { \
292 diff.Set##name##unit(m##name##unit - aBefore.m##name##unit); \
295 PROFILER_FOR_EACH_RUNNING_TIME(RUNNING_TIME_SUB)
297 #undef RUNNING_TIME_SUB
298 return diff;
301 private:
302 friend mozilla::ProfileBufferEntryWriter::Serializer<RunningTimes>;
303 friend mozilla::ProfileBufferEntryReader::Deserializer<RunningTimes>;
305 // Platform-dependent.
306 static uint64_t ConvertRawToJson(uint64_t aRawValue);
308 mozilla::TimeStamp mPostMeasurementTimeStamp;
310 uint32_t mKnownBits = 0u;
312 #define RUNNING_TIME_MEMBER(index, name, unit, jsonProperty) \
313 static constexpr uint32_t mGot##name##unit = 1u << index; \
314 uint64_t m##name##unit = 0;
316 PROFILER_FOR_EACH_RUNNING_TIME(RUNNING_TIME_MEMBER)
318 #undef RUNNING_TIME_MEMBER
321 template <>
322 struct mozilla::ProfileBufferEntryWriter::Serializer<RunningTimes> {
323 static Length Bytes(const RunningTimes& aRunningTimes) {
324 Length bytes = 0;
326 #define RUNNING_TIME_SERIALIZATION_BYTES(index, name, unit, jsonProperty) \
327 if (aRunningTimes.Is##name##unit##Known()) { \
328 bytes += ULEB128Size(aRunningTimes.m##name##unit); \
331 PROFILER_FOR_EACH_RUNNING_TIME(RUNNING_TIME_SERIALIZATION_BYTES)
333 #undef RUNNING_TIME_SERIALIZATION_BYTES
334 return ULEB128Size(aRunningTimes.mKnownBits) + bytes;
337 static void Write(ProfileBufferEntryWriter& aEW,
338 const RunningTimes& aRunningTimes) {
339 aEW.WriteULEB128(aRunningTimes.mKnownBits);
341 #define RUNNING_TIME_SERIALIZE(index, name, unit, jsonProperty) \
342 if (aRunningTimes.Is##name##unit##Known()) { \
343 aEW.WriteULEB128(aRunningTimes.m##name##unit); \
346 PROFILER_FOR_EACH_RUNNING_TIME(RUNNING_TIME_SERIALIZE)
348 #undef RUNNING_TIME_SERIALIZE
352 template <>
353 struct mozilla::ProfileBufferEntryReader::Deserializer<RunningTimes> {
354 static void ReadInto(ProfileBufferEntryReader& aER,
355 RunningTimes& aRunningTimes) {
356 aRunningTimes = Read(aER);
359 static RunningTimes Read(ProfileBufferEntryReader& aER) {
360 // Start with empty running times, everything is cleared.
361 RunningTimes times;
363 // This sets all the bits into mKnownBits, we don't need to modify it
364 // further.
365 times.mKnownBits = aER.ReadULEB128<uint32_t>();
367 // For each member that should be known, read its value.
368 #define RUNNING_TIME_DESERIALIZE(index, name, unit, jsonProperty) \
369 if (times.Is##name##unit##Known()) { \
370 times.m##name##unit = aER.ReadULEB128<decltype(times.m##name##unit)>(); \
373 PROFILER_FOR_EACH_RUNNING_TIME(RUNNING_TIME_DESERIALIZE)
375 #undef RUNNING_TIME_DESERIALIZE
377 return times;
381 #endif /* ndef TOOLS_PLATFORM_H_ */