Bug 1820813 - Keep an unclamped startTime inside PerformanceMark and use it for profiler markers r=sefeng

Differential Revision: https://phabricator.services.mozilla.com/D173002
This commit is contained in:
Nazım Can Altınova
2023-03-22 10:12:50 +00:00
parent 9607a89189
commit 7a443c61f2
5 changed files with 79 additions and 31 deletions

View File

@@ -394,7 +394,7 @@ bool Performance::IsPerformanceTimingAttribute(const nsAString& aName) const {
} }
DOMHighResTimeStamp Performance::ConvertMarkToTimestampWithString( DOMHighResTimeStamp Performance::ConvertMarkToTimestampWithString(
const nsAString& aName, ErrorResult& aRv) { const nsAString& aName, ErrorResult& aRv, bool aReturnUnclamped) {
if (IsPerformanceTimingAttribute(aName)) { if (IsPerformanceTimingAttribute(aName)) {
return ConvertNameToTimestamp(aName, aRv); return ConvertNameToTimestamp(aName, aRv);
} }
@@ -406,6 +406,9 @@ DOMHighResTimeStamp Performance::ConvertMarkToTimestampWithString(
typeParam = &str; typeParam = &str;
GetEntriesByName(aName, typeParam, arr); GetEntriesByName(aName, typeParam, arr);
if (!arr.IsEmpty()) { if (!arr.IsEmpty()) {
if (aReturnUnclamped) {
return arr.LastElement()->UnclampedStartTime();
}
return arr.LastElement()->StartTime(); return arr.LastElement()->StartTime();
} }
@@ -441,10 +444,11 @@ DOMHighResTimeStamp Performance::ConvertMarkToTimestampWithDOMHighResTimeStamp(
DOMHighResTimeStamp Performance::ConvertMarkToTimestamp( DOMHighResTimeStamp Performance::ConvertMarkToTimestamp(
const ResolveTimestampAttribute aAttribute, const ResolveTimestampAttribute aAttribute,
const OwningStringOrDouble& aMarkNameOrTimestamp, ErrorResult& aRv) { const OwningStringOrDouble& aMarkNameOrTimestamp, ErrorResult& aRv,
bool aReturnUnclamped) {
if (aMarkNameOrTimestamp.IsString()) { if (aMarkNameOrTimestamp.IsString()) {
return ConvertMarkToTimestampWithString(aMarkNameOrTimestamp.GetAsString(), return ConvertMarkToTimestampWithString(aMarkNameOrTimestamp.GetAsString(),
aRv); aRv, aReturnUnclamped);
} }
return ConvertMarkToTimestampWithDOMHighResTimeStamp( return ConvertMarkToTimestampWithDOMHighResTimeStamp(
@@ -486,17 +490,21 @@ DOMHighResTimeStamp Performance::ConvertNameToTimestamp(const nsAString& aName,
DOMHighResTimeStamp Performance::ResolveEndTimeForMeasure( DOMHighResTimeStamp Performance::ResolveEndTimeForMeasure(
const Optional<nsAString>& aEndMark, const Optional<nsAString>& aEndMark,
const Maybe<const PerformanceMeasureOptions&>& aOptions, ErrorResult& aRv) { const Maybe<const PerformanceMeasureOptions&>& aOptions, ErrorResult& aRv,
bool aReturnUnclamped) {
DOMHighResTimeStamp endTime; DOMHighResTimeStamp endTime;
if (aEndMark.WasPassed()) { if (aEndMark.WasPassed()) {
endTime = ConvertMarkToTimestampWithString(aEndMark.Value(), aRv); endTime = ConvertMarkToTimestampWithString(aEndMark.Value(), aRv,
aReturnUnclamped);
} else if (aOptions && aOptions->mEnd.WasPassed()) { } else if (aOptions && aOptions->mEnd.WasPassed()) {
endTime = ConvertMarkToTimestamp(ResolveTimestampAttribute::End, endTime =
aOptions->mEnd.Value(), aRv); ConvertMarkToTimestamp(ResolveTimestampAttribute::End,
aOptions->mEnd.Value(), aRv, aReturnUnclamped);
} else if (aOptions && aOptions->mStart.WasPassed() && } else if (aOptions && aOptions->mStart.WasPassed() &&
aOptions->mDuration.WasPassed()) { aOptions->mDuration.WasPassed()) {
const DOMHighResTimeStamp start = ConvertMarkToTimestamp( const DOMHighResTimeStamp start =
ResolveTimestampAttribute::Start, aOptions->mStart.Value(), aRv); ConvertMarkToTimestamp(ResolveTimestampAttribute::Start,
aOptions->mStart.Value(), aRv, aReturnUnclamped);
if (aRv.Failed()) { if (aRv.Failed()) {
return 0; return 0;
} }
@@ -519,11 +527,13 @@ DOMHighResTimeStamp Performance::ResolveEndTimeForMeasure(
DOMHighResTimeStamp Performance::ResolveStartTimeForMeasure( DOMHighResTimeStamp Performance::ResolveStartTimeForMeasure(
const Maybe<const nsAString&>& aStartMark, const Maybe<const nsAString&>& aStartMark,
const Maybe<const PerformanceMeasureOptions&>& aOptions, ErrorResult& aRv) { const Maybe<const PerformanceMeasureOptions&>& aOptions, ErrorResult& aRv,
bool aReturnUnclamped) {
DOMHighResTimeStamp startTime; DOMHighResTimeStamp startTime;
if (aOptions && aOptions->mStart.WasPassed()) { if (aOptions && aOptions->mStart.WasPassed()) {
startTime = ConvertMarkToTimestamp(ResolveTimestampAttribute::Start, startTime =
aOptions->mStart.Value(), aRv); ConvertMarkToTimestamp(ResolveTimestampAttribute::Start,
aOptions->mStart.Value(), aRv, aReturnUnclamped);
} else if (aOptions && aOptions->mDuration.WasPassed() && } else if (aOptions && aOptions->mDuration.WasPassed() &&
aOptions->mEnd.WasPassed()) { aOptions->mEnd.WasPassed()) {
const DOMHighResTimeStamp duration = const DOMHighResTimeStamp duration =
@@ -534,15 +544,17 @@ DOMHighResTimeStamp Performance::ResolveStartTimeForMeasure(
return 0; return 0;
} }
const DOMHighResTimeStamp end = ConvertMarkToTimestamp( const DOMHighResTimeStamp end =
ResolveTimestampAttribute::End, aOptions->mEnd.Value(), aRv); ConvertMarkToTimestamp(ResolveTimestampAttribute::End,
aOptions->mEnd.Value(), aRv, aReturnUnclamped);
if (aRv.Failed()) { if (aRv.Failed()) {
return 0; return 0;
} }
startTime = end - duration; startTime = end - duration;
} else if (aStartMark) { } else if (aStartMark) {
startTime = ConvertMarkToTimestampWithString(*aStartMark, aRv); startTime =
ConvertMarkToTimestampWithString(*aStartMark, aRv, aReturnUnclamped);
} else { } else {
startTime = 0; startTime = 0;
} }
@@ -592,8 +604,8 @@ already_AddRefed<PerformanceMeasure> Performance::Measure(
} }
} }
const DOMHighResTimeStamp endTime = const DOMHighResTimeStamp endTime = ResolveEndTimeForMeasure(
ResolveEndTimeForMeasure(aEndMark, options, aRv); aEndMark, options, aRv, /* aReturnUnclamped */ false);
if (NS_WARN_IF(aRv.Failed())) { if (NS_WARN_IF(aRv.Failed())) {
return nullptr; return nullptr;
} }
@@ -603,8 +615,8 @@ already_AddRefed<PerformanceMeasure> Performance::Measure(
if (aStartOrMeasureOptions.IsString()) { if (aStartOrMeasureOptions.IsString()) {
startMark.emplace(aStartOrMeasureOptions.GetAsString()); startMark.emplace(aStartOrMeasureOptions.GetAsString());
} }
const DOMHighResTimeStamp startTime = const DOMHighResTimeStamp startTime = ResolveStartTimeForMeasure(
ResolveStartTimeForMeasure(startMark, options, aRv); startMark, options, aRv, /* aReturnUnclamped */ false);
if (NS_WARN_IF(aRv.Failed())) { if (NS_WARN_IF(aRv.Failed())) {
return nullptr; return nullptr;
} }
@@ -627,10 +639,16 @@ already_AddRefed<PerformanceMeasure> Performance::Measure(
InsertUserEntry(performanceMeasure); InsertUserEntry(performanceMeasure);
if (profiler_thread_is_being_profiled_for_markers()) { if (profiler_thread_is_being_profiled_for_markers()) {
const DOMHighResTimeStamp unclampedStartTime = ResolveStartTimeForMeasure(
startMark, options, aRv, /* aReturnUnclamped */ true);
const DOMHighResTimeStamp unclampedEndTime =
ResolveEndTimeForMeasure(aEndMark, options, aRv, /* aReturnUnclamped */
true);
TimeStamp startTimeStamp = TimeStamp startTimeStamp =
CreationTimeStamp() + TimeDuration::FromMilliseconds(startTime); CreationTimeStamp() +
TimeDuration::FromMilliseconds(unclampedStartTime);
TimeStamp endTimeStamp = TimeStamp endTimeStamp =
CreationTimeStamp() + TimeDuration::FromMilliseconds(endTime); CreationTimeStamp() + TimeDuration::FromMilliseconds(unclampedEndTime);
Maybe<nsString> endMark; Maybe<nsString> endMark;
if (aEndMark.WasPassed()) { if (aEndMark.WasPassed()) {

View File

@@ -221,25 +221,27 @@ class Performance : public DOMEventTargetHelper {
enum class ResolveTimestampAttribute; enum class ResolveTimestampAttribute;
DOMHighResTimeStamp ConvertMarkToTimestampWithString(const nsAString& aName, DOMHighResTimeStamp ConvertMarkToTimestampWithString(const nsAString& aName,
ErrorResult& aRv); ErrorResult& aRv,
bool aReturnUnclamped);
DOMHighResTimeStamp ConvertMarkToTimestampWithDOMHighResTimeStamp( DOMHighResTimeStamp ConvertMarkToTimestampWithDOMHighResTimeStamp(
const ResolveTimestampAttribute aAttribute, const double aTimestamp, const ResolveTimestampAttribute aAttribute, const double aTimestamp,
ErrorResult& aRv); ErrorResult& aRv);
DOMHighResTimeStamp ConvertMarkToTimestamp( DOMHighResTimeStamp ConvertMarkToTimestamp(
const ResolveTimestampAttribute aAttribute, const ResolveTimestampAttribute aAttribute,
const OwningStringOrDouble& aMarkNameOrTimestamp, ErrorResult& aRv); const OwningStringOrDouble& aMarkNameOrTimestamp, ErrorResult& aRv,
bool aReturnUnclamped);
DOMHighResTimeStamp ConvertNameToTimestamp(const nsAString& aName, DOMHighResTimeStamp ConvertNameToTimestamp(const nsAString& aName,
ErrorResult& aRv); ErrorResult& aRv);
DOMHighResTimeStamp ResolveEndTimeForMeasure( DOMHighResTimeStamp ResolveEndTimeForMeasure(
const Optional<nsAString>& aEndMark, const Optional<nsAString>& aEndMark,
const Maybe<const PerformanceMeasureOptions&>& aOptions, const Maybe<const PerformanceMeasureOptions&>& aOptions, ErrorResult& aRv,
ErrorResult& aRv); bool aReturnUnclamped);
DOMHighResTimeStamp ResolveStartTimeForMeasure( DOMHighResTimeStamp ResolveStartTimeForMeasure(
const Maybe<const nsAString&>& aStartMark, const Maybe<const nsAString&>& aStartMark,
const Maybe<const PerformanceMeasureOptions&>& aOptions, const Maybe<const PerformanceMeasureOptions&>& aOptions, ErrorResult& aRv,
ErrorResult& aRv); bool aReturnUnclamped);
}; };
} // namespace dom } // namespace dom

View File

@@ -54,6 +54,13 @@ class PerformanceEntry : public nsISupports, public nsWrapperCache {
virtual DOMHighResTimeStamp StartTime() const { return 0; } virtual DOMHighResTimeStamp StartTime() const { return 0; }
// This is used by the Gecko Profiler only for adding precise markers.
// It's not exposed to JS.
virtual DOMHighResTimeStamp UnclampedStartTime() const {
MOZ_ASSERT(false, "UnclampedStartTime should not be called on this class.");
return 0;
}
virtual DOMHighResTimeStamp Duration() const { return 0; } virtual DOMHighResTimeStamp Duration() const { return 0; }
virtual const PerformanceResourceTiming* ToResourceTiming() const { virtual const PerformanceResourceTiming* ToResourceTiming() const {

View File

@@ -16,10 +16,12 @@ using namespace mozilla::dom;
PerformanceMark::PerformanceMark(nsISupports* aParent, const nsAString& aName, PerformanceMark::PerformanceMark(nsISupports* aParent, const nsAString& aName,
DOMHighResTimeStamp aStartTime, DOMHighResTimeStamp aStartTime,
const JS::Handle<JS::Value>& aDetail) const JS::Handle<JS::Value>& aDetail,
DOMHighResTimeStamp aUnclampedStartTime)
: PerformanceEntry(aParent, aName, u"mark"_ns), : PerformanceEntry(aParent, aName, u"mark"_ns),
mStartTime(aStartTime), mStartTime(aStartTime),
mDetail(aDetail) { mDetail(aDetail),
mUnclampedStartTime(aUnclampedStartTime) {
mozilla::HoldJSObjects(this); mozilla::HoldJSObjects(this);
} }
@@ -53,6 +55,13 @@ already_AddRefed<PerformanceMark> PerformanceMark::Constructor(
DOMHighResTimeStamp startTime = aMarkOptions.mStartTime.WasPassed() DOMHighResTimeStamp startTime = aMarkOptions.mStartTime.WasPassed()
? aMarkOptions.mStartTime.Value() ? aMarkOptions.mStartTime.Value()
: performance->Now(); : performance->Now();
// We need to get the unclamped start time to be able to add profiler markers
// with precise time/duration. This is not exposed to web and only used by the
// profiler.
// If a mStartTime is passed by the user, we will always have a clamped value.
DOMHighResTimeStamp unclampedStartTime = aMarkOptions.mStartTime.WasPassed()
? startTime
: performance->NowUnclamped();
if (startTime < 0) { if (startTime < 0) {
aRv.ThrowTypeError("Expected startTime >= 0"); aRv.ThrowTypeError("Expected startTime >= 0");
return nullptr; return nullptr;
@@ -71,7 +80,8 @@ already_AddRefed<PerformanceMark> PerformanceMark::Constructor(
} }
} }
return do_AddRef(new PerformanceMark(aGlobal, aMarkName, startTime, detail)); return do_AddRef(new PerformanceMark(aGlobal, aMarkName, startTime, detail,
unclampedStartTime));
} }
PerformanceMark::~PerformanceMark() { mozilla::DropJSObjects(this); } PerformanceMark::~PerformanceMark() { mozilla::DropJSObjects(this); }

View File

@@ -8,6 +8,7 @@
#define mozilla_dom_performancemark_h___ #define mozilla_dom_performancemark_h___
#include "mozilla/dom/PerformanceEntry.h" #include "mozilla/dom/PerformanceEntry.h"
#include "mozilla/ProfilerMarkers.h"
namespace mozilla::dom { namespace mozilla::dom {
@@ -23,7 +24,8 @@ class PerformanceMark final : public PerformanceEntry {
private: private:
PerformanceMark(nsISupports* aParent, const nsAString& aName, PerformanceMark(nsISupports* aParent, const nsAString& aName,
DOMHighResTimeStamp aStartTime, DOMHighResTimeStamp aStartTime,
const JS::Handle<JS::Value>& aDetail); const JS::Handle<JS::Value>& aDetail,
DOMHighResTimeStamp aUnclampedStartTime);
public: public:
static already_AddRefed<PerformanceMark> Constructor( static already_AddRefed<PerformanceMark> Constructor(
@@ -39,6 +41,12 @@ class PerformanceMark final : public PerformanceEntry {
virtual DOMHighResTimeStamp StartTime() const override { return mStartTime; } virtual DOMHighResTimeStamp StartTime() const override { return mStartTime; }
virtual DOMHighResTimeStamp UnclampedStartTime() const override {
MOZ_ASSERT(profiler_thread_is_being_profiled_for_markers(),
"This should only be called when the Gecko Profiler is active.");
return mUnclampedStartTime;
}
void GetDetail(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval); void GetDetail(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval);
size_t SizeOfIncludingThis( size_t SizeOfIncludingThis(
@@ -50,6 +58,9 @@ class PerformanceMark final : public PerformanceEntry {
private: private:
JS::Heap<JS::Value> mDetail; JS::Heap<JS::Value> mDetail;
// This is used by the Gecko Profiler only to be able to add precise markers.
// It's not exposed to JS
DOMHighResTimeStamp mUnclampedStartTime;
}; };
} // namespace mozilla::dom } // namespace mozilla::dom