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