/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include"Performance.h"
#include <sstream>
#ifdefined(XP_LINUX) # include <fcntl.h> # include <sys/mman.h> #endif
// Used to dump performance timing information to a local file. // Only defined when the env variable MOZ_USE_PERFORMANCE_MARKER_FILE // is set and initialized by MaybeOpenMarkerFile(). static FILE* sMarkerFile = nullptr;
DOMHighResTimeStamp Performance::TimeStampToDOMHighResForRendering(
TimeStamp aTimeStamp) const {
DOMHighResTimeStamp stamp = GetDOMTiming()->TimeStampToDOMHighRes(aTimeStamp); // 0 is an inappropriate mixin for this this area; however CSS Animations // needs to have it's Time Reduction Logic refactored, so it's currently // only clamping for RFP mode. RFP mode gives a much lower time precision, // so we accept the security leak here for now. return nsRFPService::ReduceTimePrecisionAsMSecsRFPOnly(stamp, 0,
mRTPCallerType);
}
// XXX: Removing this caused functions in pkcs11f.h to fail. // Bug 1628021 investigates the root cause - it involves initializing // the RNG service (part of GetRandomTimelineSeed()) off-main-thread // but the underlying cause hasn't been identified yet. if (mRTPCallerType == RTPCallerType::SystemPrincipal) { return rawTime;
}
DOMHighResTimeStamp Performance::TimeOrigin() { if (!mPerformanceService) {
mPerformanceService = PerformanceService::GetOrCreate();
}
MOZ_ASSERT(mPerformanceService);
DOMHighResTimeStamp rawTimeOrigin =
mPerformanceService->TimeOrigin(CreationTimeStamp()); // Time Origin is an absolute timestamp, so we supply a 0 context mix-in return nsRFPService::ReduceTimePrecisionAsMSecs(rawTimeOrigin, 0,
mRTPCallerType);
}
if (entryType) { if (entryType == nsGkAtoms::mark || entryType == nsGkAtoms::measure) { for (PerformanceEntry* entry : mUserEntries) { if (entry->GetName() == name && entry->GetEntryType() == entryType) {
aRetval.AppendElement(entry);
}
} return;
} if (entryType == nsGkAtoms::resource) { for (PerformanceEntry* entry : mResourceEntries) {
MOZ_ASSERT(entry->GetEntryType() == entryType); if (entry->GetName() == name) {
aRetval.AppendElement(entry);
}
} return;
} // Invalid entryType return;
}
nsTArray<PerformanceEntry*> qualifiedResourceEntries;
nsTArray<PerformanceEntry*> qualifiedUserEntries; // ::Measure expects that results from this function are already // passed through ReduceTimePrecision. mResourceEntries and mUserEntries // are, so the invariant holds. for (PerformanceEntry* entry : mResourceEntries) { if (entry->GetName() == name) {
qualifiedResourceEntries.AppendElement(entry);
}
}
for (PerformanceEntry* entry : mUserEntries) { if (entry->GetName() == name) {
qualifiedUserEntries.AppendElement(entry);
}
}
struct UserTimingMarker : public BaseMarkerType<UserTimingMarker> { static constexpr constchar* Name = "UserTiming"; static constexpr constchar* Description = "UserTimingMeasure is created using the DOM API performance.measure().";
RefPtr<nsAtom> name = NS_Atomize(aName); // Just loop over the user entries for (const PerformanceEntry* entry : Reversed(mUserEntries)) { if (entry->GetName() == name && entry->GetEntryType() == nsGkAtoms::mark) { if (aReturnUnclamped) { return entry->UnclampedStartTime();
} return entry->StartTime();
}
}
nsPrintfCString errorMsg("Given mark name, %s, is unknown",
NS_ConvertUTF16toUTF8(aName).get());
aRv.ThrowSyntaxError(errorMsg); return 0;
}
DOMHighResTimeStamp Performance::ConvertMarkToTimestampWithDOMHighResTimeStamp( const ResolveTimestampAttribute aAttribute, const DOMHighResTimeStamp aTimestamp, ErrorResult& aRv) { if (aTimestamp < 0) {
nsAutoCString attributeName; switch (aAttribute) { case ResolveTimestampAttribute::Start:
attributeName = "start"; break; case ResolveTimestampAttribute::End:
attributeName = "end"; break; case ResolveTimestampAttribute::Duration:
attributeName = "duration"; break;
}
DOMHighResTimeStamp Performance::ConvertNameToTimestamp(const nsAString& aName,
ErrorResult& aRv) { if (!IsGlobalObjectWindow()) {
nsPrintfCString errorMsg( "Cannot get PerformanceTiming attribute values for non-Window global " "object. Given: %s",
NS_ConvertUTF16toUTF8(aName).get());
aRv.ThrowTypeError(errorMsg); return 0;
}
if (aName.EqualsASCII("navigationStart")) { return 0;
}
// We use GetPerformanceTimingFromString, rather than calling the // navigationStart method timing function directly, because the former handles // reducing precision against timing attacks. const DOMHighResTimeStamp startTime =
GetPerformanceTimingFromString(u"navigationStart"_ns); const DOMHighResTimeStamp endTime = GetPerformanceTimingFromString(aName);
MOZ_ASSERT(endTime >= 0); if (endTime == 0) {
nsPrintfCString errorMsg( "Given PerformanceTiming attribute, %s, isn't available yet",
NS_ConvertUTF16toUTF8(aName).get());
aRv.ThrowInvalidAccessError(errorMsg); return 0;
}
// Try to open the marker file for writing performance markers. // If successful, returns true and sMarkerFile will be defined // to the file handle. Otherwise, return false and sMarkerFile // is NULL. staticbool MaybeOpenMarkerFile() { if (!getenv("MOZ_USE_PERFORMANCE_MARKER_FILE")) { returnfalse;
}
// Check if it's already open. if (sMarkerFile) { returntrue;
}
#ifdef XP_LINUX // We treat marker files similar to Jitdump files (see PerfSpewer.cpp) and // mmap them if needed. int fd = open(GetMarkerFilename().c_str(), O_CREAT | O_TRUNC | O_RDWR, 0666);
sMarkerFile = fdopen(fd, "w+"); if (!sMarkerFile) { returnfalse;
}
// On Linux and Android, we need to mmap the file so that the path makes it // into the perf.data file or into samply. // On non-Android, make the mapping executable, otherwise the MMAP event may // not be recorded by perf (see perf_event_open mmap_data). // But on Android, don't make the mapping executable, because doing so can // make the mmap call fail on some Android devices. It's also not required on // Android because simpleperf sets mmap_data = 1 for unrelated reasons (it // wants to know about vdex files for Java JIT profiling, see // SetRecordNotExecutableMaps). int protection = PROT_READ; # ifndef ANDROID
protection |= PROT_EXEC; # endif
// Mmap just the first page - that's enough to ensure the path makes it into // the recording. long page_size = sysconf(_SC_PAGESIZE); void* mmap_address = mmap(nullptr, page_size, protection, MAP_PRIVATE, fd, 0); if (mmap_address == MAP_FAILED) {
fclose(sMarkerFile);
sMarkerFile = nullptr; returnfalse;
} #else // On macOS, we just need to `open` or `fopen` the marker file, and samply // will know its path because it hooks those functions - no mmap needed. // On Windows, there's no need to use MOZ_USE_PERFORMANCE_MARKER_FILE because // we have ETW trace events for UserTiming measures. Still, we want this code // to compile successfully on Windows, so we use fopen rather than // open+fdopen.
sMarkerFile = fopen(GetMarkerFilename().c_str(), "w+"); if (!sMarkerFile) { returnfalse;
} #endif returntrue;
}
// This emits markers to an external marker-[pid].txt file for use by an // external profiler like samply or etw-gecko void Performance::MaybeEmitExternalProfilerMarker( const nsAString& aName, Maybe<const PerformanceMeasureOptions&> aOptions,
Maybe<const nsAString&> aStartMark, const Optional<nsAString>& aEndMark) { if (!MaybeOpenMarkerFile()) { return;
}
#ifdef XP_LINUX
uint64_t rawStart = startTimeStamp.RawClockMonotonicNanosecondsSinceBoot();
uint64_t rawEnd = endTimeStamp.RawClockMonotonicNanosecondsSinceBoot(); #elif XP_WIN
uint64_t rawStart = startTimeStamp.RawQueryPerformanceCounterValue().value();
uint64_t rawEnd = endTimeStamp.RawQueryPerformanceCounterValue().value(); #elif XP_MACOSX
uint64_t rawStart = startTimeStamp.RawMachAbsoluteTimeNanoseconds();
uint64_t rawEnd = endTimeStamp.RawMachAbsoluteTimeNanoseconds(); #else
uint64_t rawStart = 0;
uint64_t rawEnd = 0;
MOZ_CRASH("no timestamp"); #endif // Write a line for this measure to the marker file. The marker file uses a // text-based format where every line is one marker, and each line has the // format: // `<raw_start_timestamp> <raw_end_timestamp> <measure_name>` // // The timestamp value is OS specific.
fprintf(sMarkerFile, "%" PRIu64 " %" PRIu64 " %s\n", rawStart, rawEnd,
NS_ConvertUTF16toUTF8(aName).get());
fflush(sMarkerFile);
}
// Maybe is more readable than using the union type directly.
Maybe<const PerformanceMeasureOptions&> options; if (aStartOrMeasureOptions.IsPerformanceMeasureOptions()) {
options.emplace(aStartOrMeasureOptions.GetAsPerformanceMeasureOptions());
}
constbool isOptionsNotEmpty =
options.isSome() &&
(!options->mDetail.isUndefined() || options->mStart.WasPassed() ||
options->mEnd.WasPassed() || options->mDuration.WasPassed()); if (isOptionsNotEmpty) { if (aEndMark.WasPassed()) {
aRv.ThrowTypeError( "Cannot provide separate endMark argument if " "PerformanceMeasureOptions argument is given"); return nullptr;
}
if (!options->mStart.WasPassed() && !options->mEnd.WasPassed()) {
aRv.ThrowTypeError( "PerformanceMeasureOptions must have start and/or end member"); return nullptr;
}
if (options->mStart.WasPassed() && options->mDuration.WasPassed() &&
options->mEnd.WasPassed()) {
aRv.ThrowTypeError( "PerformanceMeasureOptions cannot have all of the following members: " "start, duration, and end"); return nullptr;
}
}
#ifdef MOZ_PERFETTO // Perfetto requires that events are properly nested within each category. // Since this is not a guarantee here, we need to define a dynamic category // for each measurement so it's not prematurely ended by another measurement // that overlaps. We also use the usertiming category to guard these markers // so it's easy to toggle. if (TRACE_EVENT_CATEGORY_ENABLED("usertiming")) {
NS_ConvertUTF16toUTF8 str(aName);
perfetto::DynamicCategory category{str.get()};
TimeStamp startTimeStamp =
CreationTimeStamp() + TimeDuration::FromMilliseconds(startTime);
TimeStamp endTimeStamp =
CreationTimeStamp() + TimeDuration::FromMilliseconds(endTime);
PERFETTO_TRACE_EVENT_BEGIN(category, perfetto::DynamicString{str.get()},
startTimeStamp);
PERFETTO_TRACE_EVENT_END(category, endTimeStamp);
} #endif
/* * Steps are labeled according to the description found at * https://w3c.github.io/resource-timing/#sec-extensions-performance-interface. * * Buffer Full Event
*/ void Performance::BufferEvent() { /* * While resource timing secondary buffer is not empty, * run the following substeps:
*/ while (!mSecondaryResourceEntries.IsEmpty()) {
uint32_t secondaryResourceEntriesBeforeCount = 0;
uint32_t secondaryResourceEntriesAfterCount = 0;
/* * Let number of excess entries before be resource * timing secondary buffer current size.
*/
secondaryResourceEntriesBeforeCount = mSecondaryResourceEntries.Length();
/* * If can add resource timing entry returns false, * then fire an event named resourcetimingbufferfull * at the Performance object.
*/ if (!CanAddResourceTimingEntry()) {
DispatchBufferFullEvent();
}
/* * Run copy secondary buffer. * * While resource timing secondary buffer is not * empty and can add resource timing entry returns * true ...
*/ while (!mSecondaryResourceEntries.IsEmpty() &&
CanAddResourceTimingEntry()) { /* * Let entry be the oldest PerformanceResourceTiming * in resource timing secondary buffer. Add entry to * the end of performance entry buffer. Increment * resource timing buffer current size by 1.
*/
mResourceEntries.InsertElementSorted(
mSecondaryResourceEntries.ElementAt(0), PerformanceEntryComparator()); /* * Remove entry from resource timing secondary buffer. * Decrement resource timing secondary buffer current * size by 1.
*/
mSecondaryResourceEntries.RemoveElementAt(0);
}
/* * Let number of excess entries after be resource * timing secondary buffer current size.
*/
secondaryResourceEntriesAfterCount = mSecondaryResourceEntries.Length();
/* * If number of excess entries before is lower than * or equals number of excess entries after, then * remove all entries from resource timing secondary * buffer, set resource timing secondary buffer current * size to 0, and abort these steps.
*/ if (secondaryResourceEntriesBeforeCount <=
secondaryResourceEntriesAfterCount) {
mSecondaryResourceEntries.Clear(); break;
}
} /* * Set resource timing buffer full event pending flag * to false.
*/
mPendingResourceTimingBufferFullEvent = false;
}
/* * Steps are labeled according to the description found at * https://w3c.github.io/resource-timing/#sec-extensions-performance-interface. * * Can Add Resource Timing Entry
*/
MOZ_ALWAYS_INLINE bool Performance::CanAddResourceTimingEntry() { /* * If resource timing buffer current size is smaller than resource timing * buffer size limit, return true. [Otherwise,] [r]eturn false.
*/ return mResourceEntries.Length() < mResourceTimingBufferSize;
}
/* * Let new entry be the input PerformanceEntry to be added. * * If can add resource timing entry returns true and resource * timing buffer full event pending flag is false ...
*/ if (CanAddResourceTimingEntry() && !mPendingResourceTimingBufferFullEvent) { /* * Add new entry to the performance entry buffer. * Increase resource timing buffer current size by 1.
*/
mResourceEntries.InsertElementSorted(aEntry, PerformanceEntryComparator()); return;
}
/* * If resource timing buffer full event pending flag is * false ...
*/ if (!mPendingResourceTimingBufferFullEvent) { /* * Set resource timing buffer full event pending flag * to true.
*/
mPendingResourceTimingBufferFullEvent = true;
/* * Queue a task to run fire a buffer full event.
*/
NS_DispatchToCurrentThread(NewCancelableRunnableMethod( "Performance::BufferEvent", this, &Performance::BufferEvent));
} /* * Add new entry to the resource timing secondary buffer. * Increase resource timing secondary buffer current size * by 1.
*/
mSecondaryResourceEntries.InsertElementSorted(aEntry,
PerformanceEntryComparator());
}
if (!interestedObservers.IsEmpty()) {
QueueNotificationObserversTask();
}
}
// We could clear User entries here, but doing so could break sites that call // performance.measure() if the marks disappeared without warning. Chrome // allows "infinite" entries. void Performance::MemoryPressure() {}
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.4Bemerkung:
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.