1 // Copyright (c) 2006-2011 The Chromium Authors. All rights reserved.
3 // Redistribution and use in source and binary forms, with or without
4 // modification, are permitted provided that the following conditions
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
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
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"
47 #include "SharedLibraries.h"
53 class ProfilerCodeAddressService
;
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.
88 // These will be called by ActivePS when the profiler starts/stops.
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
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
};
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();
132 static mozilla::baseprofiler::detail::BaseProfilerMutex gMutex
;
133 static mozilla::UniquePtr
<Json::Value
> gLog
;
136 // ----------------------------------------------------------------------------
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
{
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();
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
206 constexpr RunningTimes() = default;
208 // Constructor with only a timestamp, useful when no measurements will be
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() { \
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;
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 {
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
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
322 struct mozilla::ProfileBufferEntryWriter::Serializer
<RunningTimes
> {
323 static Length
Bytes(const RunningTimes
& aRunningTimes
) {
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
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.
363 // This sets all the bits into mKnownBits, we don't need to modify it
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
381 #endif /* ndef TOOLS_PLATFORM_H_ */