зеркало из https://github.com/mozilla/gecko-dev.git
Backed out 4 changesets (bug 1545582) for causing spidermonkey bustages CLOSED TREE
Backed out changeset c53f9e22d5f7 (bug 1545582) Backed out changeset 6640b7f3d7e0 (bug 1545582) Backed out changeset c65de5ec10da (bug 1545582) Backed out changeset 3224107774b1 (bug 1545582)
This commit is contained in:
Родитель
21ee03d9f3
Коммит
217ab9d060
|
@ -226,7 +226,6 @@ function intializeState() {
|
|||
tasktracer: false,
|
||||
trackopts: false,
|
||||
jstracer: false,
|
||||
jsallocations: false,
|
||||
};
|
||||
|
||||
if (AppConstants.platform === "android") {
|
||||
|
|
|
@ -169,10 +169,6 @@
|
|||
id="perf-settings-feature-checkbox-jstracer" type="checkbox" value="jstracer" />
|
||||
<div class="perf-settings-feature-name">JSTracer</div>
|
||||
<div class="perf-settings-feature-title">Trace JS engine (Experimental, requires custom build.)</div>
|
||||
</label><label class="perf-settings-checkbox-label perf-settings-feature-label"><input class="perf-settings-checkbox"
|
||||
id="perf-settings-feature-checkbox-jsallocations" type="checkbox" value="jsallocations" />
|
||||
<div class="perf-settings-feature-name">JS Allocations</div>
|
||||
<div class="perf-settings-feature-title">Track JavaScript allocations (Experimental.)</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -32,7 +32,6 @@ const features = [
|
|||
"stackwalk",
|
||||
"tasktracer",
|
||||
"jstracer",
|
||||
"jsallocations",
|
||||
"trackopts",
|
||||
];
|
||||
const threadPrefix = "perf-settings-thread-checkbox-";
|
||||
|
@ -258,7 +257,6 @@ function calculateOverhead(state) {
|
|||
const overheadFromSeqStyle = state.seqstyle ? 0.05 : 0;
|
||||
const overheadFromTaskTracer = state.tasktracer ? 0.05 : 0;
|
||||
const overheadFromJSTracer = state.jstracer ? 0.05 : 0;
|
||||
const overheadFromJSAllocations = state.jsallocations ? 0.05 : 0;
|
||||
return clamp(
|
||||
overheadFromSampling +
|
||||
overheadFromBuffersize +
|
||||
|
@ -267,8 +265,7 @@ function calculateOverhead(state) {
|
|||
overheadFromJavaScrpt +
|
||||
overheadFromSeqStyle +
|
||||
overheadFromTaskTracer +
|
||||
overheadFromJSTracer +
|
||||
overheadFromJSAllocations,
|
||||
overheadFromJSTracer,
|
||||
0,
|
||||
1
|
||||
);
|
||||
|
|
|
@ -1,75 +0,0 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef js_AllocationRecording_h
|
||||
#define js_AllocationRecording_h
|
||||
|
||||
#include "js/TypeDecls.h"
|
||||
#include "js/Utility.h"
|
||||
|
||||
namespace JS {
|
||||
|
||||
/**
|
||||
* This struct holds the information needed to create a profiler marker payload
|
||||
* that can represent a JS allocation. It translates JS engine specific classes,
|
||||
* into something that can be used in the profiler.
|
||||
*/
|
||||
struct RecordAllocationInfo {
|
||||
RecordAllocationInfo(const char16_t* typeName, const char* className,
|
||||
const char16_t* descriptiveTypeName,
|
||||
const char* scriptFilename, const char* coarseType,
|
||||
uint64_t size, bool inNursery)
|
||||
: typeName(typeName),
|
||||
className(className),
|
||||
descriptiveTypeName(descriptiveTypeName),
|
||||
scriptFilename(scriptFilename),
|
||||
coarseType(coarseType),
|
||||
size(size),
|
||||
inNursery(inNursery) {}
|
||||
|
||||
// These pointers are borrowed from the UbiNode, and can point to live data.
|
||||
// It is important for the consumers of this struct to correctly
|
||||
// duplicate the strings to take ownership of them.
|
||||
const char16_t* typeName;
|
||||
const char* className;
|
||||
const char16_t* descriptiveTypeName;
|
||||
const char* scriptFilename;
|
||||
|
||||
// The coarseType points to a string literal, so does not need to be
|
||||
// duplicated.
|
||||
const char* coarseType;
|
||||
|
||||
// The size in bytes of the allocation.
|
||||
uint64_t size;
|
||||
|
||||
// Whether or not the allocation is in the nursery or not.
|
||||
bool inNursery;
|
||||
};
|
||||
|
||||
typedef void (*RecordAllocationsCallback)(RecordAllocationInfo&& info);
|
||||
|
||||
/**
|
||||
* Enable recording JS allocations. This feature hooks into the object creation
|
||||
* in the JavaScript engine, and reports back the allocation info through the
|
||||
* callback. This allocation tracking is turned on for all encountered realms.
|
||||
* The JS Debugger API can also turn on allocation tracking with its own
|
||||
* probability. If both allocation tracking mechanisms are turned on at the same
|
||||
* time, the Debugger's probability defers to the EnableRecordingAllocations's
|
||||
* probability setting.
|
||||
*/
|
||||
JS_FRIEND_API void EnableRecordingAllocations(
|
||||
JSContext* cx, RecordAllocationsCallback callback, double probability);
|
||||
|
||||
/**
|
||||
* Turn off JS allocation recording. If any JS Debuggers are also recording
|
||||
* allocations, then the probability will be reset to the Debugger's desired
|
||||
* setting.
|
||||
*/
|
||||
JS_FRIEND_API void DisableRecordingAllocations(JSContext* cx);
|
||||
|
||||
} // namespace JS
|
||||
|
||||
#endif /* js_AllocationRecording_h */
|
|
@ -509,11 +509,6 @@ enum class CoarseType : uint32_t {
|
|||
LAST = DOMNode
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert a CoarseType enum into a string. The string is statically allocated.
|
||||
*/
|
||||
JS_PUBLIC_API const char* CoarseTypeToString(CoarseType type);
|
||||
|
||||
inline uint32_t CoarseTypeToUint32(CoarseType type) {
|
||||
return static_cast<uint32_t>(type);
|
||||
}
|
||||
|
|
|
@ -1707,7 +1707,6 @@ JS_PUBLIC_API void JS_FireOnNewGlobalObject(JSContext* cx,
|
|||
cx->check(global);
|
||||
Rooted<js::GlobalObject*> globalObject(cx, &global->as<GlobalObject>());
|
||||
Debugger::onNewGlobalObject(cx, globalObject);
|
||||
cx->runtime()->ensureRealmIsRecordingAllocations(globalObject);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API JSObject* JS_NewObject(JSContext* cx, const JSClass* jsclasp) {
|
||||
|
|
|
@ -116,7 +116,6 @@ EXPORTS += [
|
|||
]
|
||||
|
||||
EXPORTS.js += [
|
||||
'../public/AllocationRecording.h',
|
||||
'../public/AllocPolicy.h',
|
||||
'../public/ArrayBuffer.h',
|
||||
'../public/BuildId.h',
|
||||
|
|
|
@ -3327,12 +3327,7 @@ void Debugger::removeAllocationsTracking(GlobalObject& global) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!global.realm()->runtimeFromMainThread()->recordAllocationCallback) {
|
||||
// Something like the Gecko Profiler could request from the the JS runtime
|
||||
// to record allocations. If it is recording allocations, then do not
|
||||
// destroy the allocation metadata builder at this time.
|
||||
global.realm()->forgetAllocationMetadataBuilder();
|
||||
}
|
||||
global.realm()->forgetAllocationMetadataBuilder();
|
||||
}
|
||||
|
||||
bool Debugger::addAllocationsTrackingForAllDebuggees(JSContext* cx) {
|
||||
|
|
|
@ -87,6 +87,7 @@ enum class ResumeMode {
|
|||
Return,
|
||||
};
|
||||
|
||||
|
||||
typedef HashSet<WeakHeapPtrGlobalObject,
|
||||
MovableCellHasher<WeakHeapPtrGlobalObject>, ZoneAllocPolicy>
|
||||
WeakGlobalObjectSet;
|
||||
|
@ -390,12 +391,6 @@ class Debugger : private mozilla::LinkedListElement<Debugger> {
|
|||
#ifdef DEBUG
|
||||
static void assertThingIsNotGray(Debugger* dbg) { return; }
|
||||
#endif
|
||||
/*
|
||||
* Return true if the given global is being observed by at least one
|
||||
* Debugger that is tracking allocations.
|
||||
*/
|
||||
static bool isObservedByDebuggerTrackingAllocations(
|
||||
const GlobalObject& debuggee);
|
||||
|
||||
private:
|
||||
GCPtrNativeObject object; /* The Debugger object. Strong reference. */
|
||||
|
@ -454,6 +449,13 @@ class Debugger : private mozilla::LinkedListElement<Debugger> {
|
|||
*/
|
||||
static bool cannotTrackAllocations(const GlobalObject& global);
|
||||
|
||||
/*
|
||||
* Return true if the given global is being observed by at least one
|
||||
* Debugger that is tracking allocations.
|
||||
*/
|
||||
static bool isObservedByDebuggerTrackingAllocations(
|
||||
const GlobalObject& global);
|
||||
|
||||
/*
|
||||
* Add allocations tracking for objects allocated within the given
|
||||
* debuggee's compartment. The given debuggee global must be observed by at
|
||||
|
|
|
@ -613,11 +613,6 @@ void Realm::clearTables() {
|
|||
varNames_.clear();
|
||||
}
|
||||
|
||||
// Check to see if this individual realm is recording allocations. Debuggers or
|
||||
// runtimes can try and record allocations, so this method can check to see if
|
||||
// any initialization is needed.
|
||||
bool Realm::isRecordingAllocations() { return !!allocationMetadataBuilder_; }
|
||||
|
||||
void Realm::setAllocationMetadataBuilder(
|
||||
const js::AllocationMetadataBuilder* builder) {
|
||||
// Clear any jitcode in the runtime, which behaves differently depending on
|
||||
|
@ -628,8 +623,6 @@ void Realm::setAllocationMetadataBuilder(
|
|||
}
|
||||
|
||||
void Realm::forgetAllocationMetadataBuilder() {
|
||||
MOZ_ASSERT(allocationMetadataBuilder_);
|
||||
|
||||
// Unlike setAllocationMetadataBuilder, we don't have to discard all JIT
|
||||
// code here (code is still valid, just a bit slower because it doesn't do
|
||||
// inline GC allocations when a metadata builder is present), but we do want
|
||||
|
|
|
@ -605,7 +605,6 @@ class JS::Realm : public JS::shadow::Realm {
|
|||
const void* addressOfMetadataBuilder() const {
|
||||
return &allocationMetadataBuilder_;
|
||||
}
|
||||
bool isRecordingAllocations();
|
||||
void setAllocationMetadataBuilder(
|
||||
const js::AllocationMetadataBuilder* builder);
|
||||
void forgetAllocationMetadataBuilder();
|
||||
|
@ -798,9 +797,8 @@ class JS::Realm : public JS::shadow::Realm {
|
|||
|
||||
// Recompute the probability with which this realm should record
|
||||
// profiling data (stack traces, allocations log, etc.) about each
|
||||
// allocation. We first consult the JS runtime to see if it is recording
|
||||
// allocations, and if not then check the probabilities requested by the
|
||||
// Debugger instances observing us, if any.
|
||||
// allocation. We consult the probabilities requested by the Debugger
|
||||
// instances observing us, if any.
|
||||
void chooseAllocationSamplingProbability() {
|
||||
savedStacks_.chooseSamplingProbability(this);
|
||||
}
|
||||
|
|
|
@ -847,62 +847,7 @@ JS_FRIEND_API bool JS::IsProfilingEnabledForContext(JSContext* cx) {
|
|||
return cx->runtime()->geckoProfiler().enabled();
|
||||
}
|
||||
|
||||
JS_FRIEND_API void JS::EnableRecordingAllocations(
|
||||
JSContext* cx, JS::RecordAllocationsCallback callback, double probability) {
|
||||
MOZ_ASSERT(cx);
|
||||
MOZ_ASSERT(cx->isMainThreadContext());
|
||||
cx->runtime()->startRecordingAllocations(probability, callback);
|
||||
}
|
||||
|
||||
JS_FRIEND_API void JS::DisableRecordingAllocations(JSContext* cx) {
|
||||
MOZ_ASSERT(cx);
|
||||
MOZ_ASSERT(cx->isMainThreadContext());
|
||||
cx->runtime()->stopRecordingAllocations();
|
||||
}
|
||||
|
||||
JS_PUBLIC_API void JS::shadow::RegisterWeakCache(
|
||||
JSRuntime* rt, detail::WeakCacheBase* cachep) {
|
||||
rt->registerWeakCache(cachep);
|
||||
}
|
||||
|
||||
void JSRuntime::startRecordingAllocations(
|
||||
double probability, JS::RecordAllocationsCallback callback) {
|
||||
allocationSamplingProbability = probability;
|
||||
recordAllocationCallback = callback;
|
||||
|
||||
// Go through all of the existing realms, and turn on allocation tracking.
|
||||
for (RealmsIter realm(this); !realm.done(); realm.next()) {
|
||||
realm->setAllocationMetadataBuilder(&SavedStacks::metadataBuilder);
|
||||
realm->chooseAllocationSamplingProbability();
|
||||
}
|
||||
}
|
||||
|
||||
void JSRuntime::stopRecordingAllocations() {
|
||||
recordAllocationCallback = nullptr;
|
||||
// Go through all of the existing realms, and turn on allocation tracking.
|
||||
for (RealmsIter realm(this); !realm.done(); realm.next()) {
|
||||
js::GlobalObject* global = realm->maybeGlobal();
|
||||
if (!realm->isDebuggee() || !global ||
|
||||
!Debugger::isObservedByDebuggerTrackingAllocations(*global)) {
|
||||
// Only remove the allocation metadata builder if no Debuggers are
|
||||
// tracking allocations.
|
||||
realm->forgetAllocationMetadataBuilder();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This function can run to ensure that when new realms are created
|
||||
// they have allocation logging turned on.
|
||||
void JSRuntime::ensureRealmIsRecordingAllocations(
|
||||
Handle<GlobalObject*> global) {
|
||||
if (recordAllocationCallback) {
|
||||
if (!global->realm()->isRecordingAllocations()) {
|
||||
// This is a new realm, turn on allocations for it.
|
||||
global->realm()->setAllocationMetadataBuilder(
|
||||
&SavedStacks::metadataBuilder);
|
||||
}
|
||||
// Ensure the probability is up to date with the current combination of
|
||||
// debuggers and runtime profiling.
|
||||
global->realm()->chooseAllocationSamplingProbability();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
#include "gc/GCRuntime.h"
|
||||
#include "gc/Tracer.h"
|
||||
#include "irregexp/RegExpStack.h"
|
||||
#include "js/AllocationRecording.h"
|
||||
#include "js/BuildId.h" // JS::BuildIdOp
|
||||
#include "js/Debug.h"
|
||||
#include "js/experimental/SourceHook.h" // js::SourceHook
|
||||
|
@ -525,11 +524,6 @@ struct JSRuntime : public js::MallocProvider<JSRuntime> {
|
|||
// number of realms visited by RealmsIter.
|
||||
js::MainThreadData<size_t> numRealms;
|
||||
|
||||
// The Gecko Profiler may want to sample the allocations happening across the
|
||||
// browser. This callback can be registered to record the allocation.
|
||||
js::MainThreadData<JS::RecordAllocationsCallback> recordAllocationCallback;
|
||||
js::MainThreadData<double> allocationSamplingProbability;
|
||||
|
||||
private:
|
||||
// Number of debuggee realms in the runtime.
|
||||
js::MainThreadData<size_t> numDebuggeeRealms_;
|
||||
|
@ -546,11 +540,6 @@ struct JSRuntime : public js::MallocProvider<JSRuntime> {
|
|||
void incrementNumDebuggeeRealmsObservingCoverage();
|
||||
void decrementNumDebuggeeRealmsObservingCoverage();
|
||||
|
||||
void startRecordingAllocations(double probability,
|
||||
JS::RecordAllocationsCallback callback);
|
||||
void stopRecordingAllocations();
|
||||
void ensureRealmIsRecordingAllocations(JS::Handle<js::GlobalObject*> global);
|
||||
|
||||
/* Locale-specific callbacks for string conversion. */
|
||||
js::MainThreadData<const JSLocaleCallbacks*> localeCallbacks;
|
||||
|
||||
|
|
|
@ -1798,16 +1798,6 @@ bool SavedStacks::getLocation(JSContext* cx, const FrameIter& iter,
|
|||
}
|
||||
|
||||
void SavedStacks::chooseSamplingProbability(Realm* realm) {
|
||||
{
|
||||
JSRuntime* runtime = realm->runtimeFromMainThread();
|
||||
if (runtime->recordAllocationCallback) {
|
||||
// The runtime is tracking allocations across all realms, in this case
|
||||
// ignore all of the debugger values, and use the runtime's probability.
|
||||
this->setSamplingProbability(runtime->allocationSamplingProbability);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Use unbarriered version to prevent triggering read barrier while
|
||||
// collecting, this is safe as long as global does not escape.
|
||||
GlobalObject* global = realm->unsafeUnbarrieredMaybeGlobal();
|
||||
|
@ -1839,10 +1829,6 @@ void SavedStacks::chooseSamplingProbability(Realm* realm) {
|
|||
}
|
||||
MOZ_ASSERT(foundAnyDebuggers);
|
||||
|
||||
this->setSamplingProbability(probability);
|
||||
}
|
||||
|
||||
void SavedStacks::setSamplingProbability(double probability) {
|
||||
if (!bernoulliSeeded) {
|
||||
mozilla::Array<uint64_t, 2> seed;
|
||||
GenerateXorShift128PlusSeed(seed);
|
||||
|
@ -1873,29 +1859,6 @@ JSObject* SavedStacks::MetadataBuilder::build(
|
|||
oomUnsafe.crash("SavedStacksMetadataBuilder");
|
||||
}
|
||||
|
||||
auto recordAllocationCallback =
|
||||
cx->realm()->runtimeFromMainThread()->recordAllocationCallback;
|
||||
if (recordAllocationCallback) {
|
||||
// The following code translates the JS-specific information, into an
|
||||
// RecordAllocationInfo object that can be consumed outside of SpiderMonkey.
|
||||
|
||||
// Do not GC during this operation, strings are being copied out of the JS
|
||||
// engine.
|
||||
AutoCheckCannotGC nogc;
|
||||
|
||||
auto node = JS::ubi::Node(obj.get());
|
||||
|
||||
// Pass the non-SpiderMonkey specific information back to the
|
||||
// callback to get it out of the JS engine. Strings will need to be
|
||||
// copied by the callback. After it is done we release the
|
||||
// AutoCheckCannotGC.
|
||||
recordAllocationCallback(JS::RecordAllocationInfo{
|
||||
node.typeName(), node.jsObjectClassName(), node.descriptiveTypeName(),
|
||||
node.scriptFilename(), JS::ubi::CoarseTypeToString(node.coarseType()),
|
||||
node.size(cx->runtime()->debuggerMallocSizeOf),
|
||||
gc::IsInsideNursery(obj)});
|
||||
}
|
||||
|
||||
MOZ_ASSERT_IF(frame, !frame->is<WrapperObject>());
|
||||
return frame;
|
||||
}
|
||||
|
|
|
@ -227,7 +227,6 @@ class SavedStacks {
|
|||
Handle<SavedFrame::Lookup> lookup);
|
||||
SavedFrame* createFrameFromLookup(JSContext* cx,
|
||||
Handle<SavedFrame::Lookup> lookup);
|
||||
void setSamplingProbability(double probability);
|
||||
|
||||
// Cache for memoizing PCToLineNumber lookups.
|
||||
|
||||
|
|
|
@ -524,22 +524,5 @@ void SetConstructUbiNodeForDOMObjectCallback(JSContext* cx,
|
|||
cx->runtime()->constructUbiNodeForDOMObjectCallback = callback;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API const char* CoarseTypeToString(CoarseType type) {
|
||||
switch (type) {
|
||||
case CoarseType::Other:
|
||||
return "Other";
|
||||
case CoarseType::Object:
|
||||
return "Object";
|
||||
case CoarseType::Script:
|
||||
return "Script";
|
||||
case CoarseType::String:
|
||||
return "String";
|
||||
case CoarseType::DOMNode:
|
||||
return "DOMNode";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace ubi
|
||||
} // namespace JS
|
||||
|
|
|
@ -116,12 +116,11 @@ void profiler_get_profile_json_into_lazily_allocated_buffer(
|
|||
const std::function<char*(size_t)>& aAllocator, double aSinceTime,
|
||||
bool aIsShuttingDown);
|
||||
|
||||
// Flags to conveniently track various JS instrumentations.
|
||||
enum class JSInstrumentationFlags {
|
||||
// Flags to conveniently track various JS features.
|
||||
enum class JSSamplingFlags {
|
||||
StackSampling = 0x1,
|
||||
TrackOptimizations = 0x2,
|
||||
TraceLogging = 0x4,
|
||||
Allocations = 0x8
|
||||
TraceLogging = 0x4
|
||||
};
|
||||
|
||||
// Record an exit profile from a child process.
|
||||
|
|
|
@ -36,8 +36,7 @@
|
|||
"tasktracer",
|
||||
"threads",
|
||||
"trackopts",
|
||||
"jstracer",
|
||||
"jsallocations"
|
||||
"jstracer"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
@ -296,28 +296,3 @@ void LongTaskMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter,
|
|||
aUniqueStacks);
|
||||
aWriter.StringProperty("category", "LongTask");
|
||||
}
|
||||
|
||||
void JsAllocationMarkerPayload::StreamPayload(
|
||||
SpliceableJSONWriter& aWriter, const TimeStamp& aProcessStartTime,
|
||||
UniqueStacks& aUniqueStacks) {
|
||||
StreamCommonProps("JS allocation", aWriter, aProcessStartTime, aUniqueStacks);
|
||||
|
||||
if (mClassName) {
|
||||
aWriter.StringProperty("className", mClassName.get());
|
||||
}
|
||||
if (mScriptFilename) {
|
||||
aWriter.StringProperty("scriptFilename", mScriptFilename.get());
|
||||
}
|
||||
if (mTypeName) {
|
||||
aWriter.StringProperty("typeName",
|
||||
NS_ConvertUTF16toUTF8(mTypeName.get()).get());
|
||||
}
|
||||
if (mDescriptiveTypeName) {
|
||||
aWriter.StringProperty(
|
||||
"descriptiveTypeName",
|
||||
NS_ConvertUTF16toUTF8(mDescriptiveTypeName.get()).get());
|
||||
}
|
||||
aWriter.StringProperty("coarseType", mCoarseType);
|
||||
aWriter.IntProperty("size", mSize);
|
||||
aWriter.BoolProperty("inNursery", mInNursery);
|
||||
}
|
||||
|
|
|
@ -250,11 +250,6 @@ class RegisteredThread final {
|
|||
if (JSTracerEnabled()) {
|
||||
JS::StartTraceLogger(mContext);
|
||||
}
|
||||
if (JSAllocationsEnabled()) {
|
||||
// TODO - This probability should not be hardcoded. See Bug 1547284.
|
||||
JS::EnableRecordingAllocations(
|
||||
mContext, profiler_add_js_allocation_marker, 0.01);
|
||||
}
|
||||
js::RegisterContextProfilingEventMarker(mContext,
|
||||
profiler_add_js_marker);
|
||||
|
||||
|
@ -264,9 +259,6 @@ class RegisteredThread final {
|
|||
if (JSTracerEnabled()) {
|
||||
JS::StopTraceLogger(mContext);
|
||||
}
|
||||
if (JSAllocationsEnabled()) {
|
||||
JS::DisableRecordingAllocations(mContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -334,15 +326,11 @@ class RegisteredThread final {
|
|||
uint32_t mJSFlags;
|
||||
|
||||
bool TrackOptimizationsEnabled() {
|
||||
return mJSFlags & uint32_t(JSInstrumentationFlags::TrackOptimizations);
|
||||
return mJSFlags & uint32_t(JSSamplingFlags::TrackOptimizations);
|
||||
}
|
||||
|
||||
bool JSTracerEnabled() {
|
||||
return mJSFlags & uint32_t(JSInstrumentationFlags::TraceLogging);
|
||||
}
|
||||
|
||||
bool JSAllocationsEnabled() {
|
||||
return mJSFlags & uint32_t(JSInstrumentationFlags::Allocations);
|
||||
return mJSFlags & uint32_t(JSSamplingFlags::TraceLogging);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -43,7 +43,6 @@
|
|||
#include "VTuneProfiler.h"
|
||||
|
||||
#include "js/TraceLoggerAPI.h"
|
||||
#include "js/ProfilingFrameIterator.h"
|
||||
#include "memory_hooks.h"
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/Atomics.h"
|
||||
|
@ -655,17 +654,12 @@ class ActivePS {
|
|||
|
||||
static uint32_t JSFlags(PSLockRef aLock) {
|
||||
uint32_t Flags = 0;
|
||||
Flags |=
|
||||
FeatureJS(aLock) ? uint32_t(JSInstrumentationFlags::StackSampling) : 0;
|
||||
Flags |= FeatureJS(aLock) ? uint32_t(JSSamplingFlags::StackSampling) : 0;
|
||||
Flags |= FeatureTrackOptimizations(aLock)
|
||||
? uint32_t(JSInstrumentationFlags::TrackOptimizations)
|
||||
: 0;
|
||||
Flags |= FeatureJSTracer(aLock)
|
||||
? uint32_t(JSInstrumentationFlags::TraceLogging)
|
||||
: 0;
|
||||
Flags |= FeatureJSAllocations(aLock)
|
||||
? uint32_t(JSInstrumentationFlags::Allocations)
|
||||
? uint32_t(JSSamplingFlags::TrackOptimizations)
|
||||
: 0;
|
||||
Flags |=
|
||||
FeatureJSTracer(aLock) ? uint32_t(JSSamplingFlags::TraceLogging) : 0;
|
||||
return Flags;
|
||||
}
|
||||
|
||||
|
@ -4012,13 +4006,6 @@ void profiler_add_js_marker(const char* aMarkerName) {
|
|||
profiler_add_marker(aMarkerName, JS::ProfilingCategoryPair::JS, nullptr);
|
||||
}
|
||||
|
||||
void profiler_add_js_allocation_marker(JS::RecordAllocationInfo&& info) {
|
||||
profiler_add_marker(
|
||||
"JS allocation", JS::ProfilingCategoryPair::JS,
|
||||
MakeUnique<JsAllocationMarkerPayload>(TimeStamp::Now(), std::move(info),
|
||||
profiler_get_backtrace()));
|
||||
}
|
||||
|
||||
void profiler_add_network_marker(
|
||||
nsIURI* aURI, int32_t aPriority, uint64_t aChannelId, NetworkLoadType aType,
|
||||
mozilla::TimeStamp aStart, mozilla::TimeStamp aEnd, int64_t aCount,
|
||||
|
|
|
@ -88,12 +88,11 @@ void profiler_get_profile_json_into_lazily_allocated_buffer(
|
|||
const std::function<char*(size_t)>& aAllocator, double aSinceTime,
|
||||
bool aIsShuttingDown);
|
||||
|
||||
// Flags to conveniently track various JS instrumentations.
|
||||
enum class JSInstrumentationFlags {
|
||||
// Flags to conveniently track various JS features.
|
||||
enum class JSSamplingFlags {
|
||||
StackSampling = 0x1,
|
||||
TrackOptimizations = 0x2,
|
||||
TraceLogging = 0x4,
|
||||
Allocations = 0x8
|
||||
TraceLogging = 0x4
|
||||
};
|
||||
|
||||
// Record an exit profile from a child process.
|
||||
|
|
|
@ -69,8 +69,6 @@
|
|||
|
||||
#else // !MOZ_GECKO_PROFILER
|
||||
|
||||
# include "js/AllocationRecording.h"
|
||||
# include "js/ProfilingFrameIterator.h"
|
||||
# include "js/ProfilingStack.h"
|
||||
# include "js/RootingAPI.h"
|
||||
# include "js/TypeDecls.h"
|
||||
|
@ -120,47 +118,44 @@ class Vector;
|
|||
// values are used internally only and so can be changed without consequence.
|
||||
// Any changes to this list should also be applied to the feature list in
|
||||
// toolkit/components/extensions/schemas/geckoProfiler.json.
|
||||
# define PROFILER_FOR_EACH_FEATURE(MACRO) \
|
||||
MACRO(0, "java", Java, "Profile Java code, Android only") \
|
||||
\
|
||||
MACRO(1, "js", JS, \
|
||||
"Get the JS engine to expose the JS stack to the profiler") \
|
||||
\
|
||||
/* The DevTools profiler doesn't want the native addresses. */ \
|
||||
MACRO(2, "leaf", Leaf, "Include the C++ leaf node if not stackwalking") \
|
||||
\
|
||||
MACRO(3, "mainthreadio", MainThreadIO, \
|
||||
"Add main thread I/O to the profile") \
|
||||
\
|
||||
MACRO(4, "memory", Memory, "Add memory measurements") \
|
||||
\
|
||||
MACRO(5, "privacy", Privacy, \
|
||||
"Do not include user-identifiable information") \
|
||||
\
|
||||
MACRO(6, "responsiveness", Responsiveness, \
|
||||
"Collect thread responsiveness information") \
|
||||
\
|
||||
MACRO(7, "screenshots", Screenshots, \
|
||||
"Take a snapshot of the window on every composition") \
|
||||
\
|
||||
MACRO(8, "seqstyle", SequentialStyle, \
|
||||
"Disable parallel traversal in styling") \
|
||||
\
|
||||
MACRO(9, "stackwalk", StackWalk, \
|
||||
"Walk the C++ stack, not available on all platforms") \
|
||||
\
|
||||
MACRO(10, "tasktracer", TaskTracer, \
|
||||
"Start profiling with feature TaskTracer") \
|
||||
\
|
||||
MACRO(11, "threads", Threads, "Profile the registered secondary threads") \
|
||||
\
|
||||
MACRO(12, "trackopts", TrackOptimizations, \
|
||||
"Have the JavaScript engine track JIT optimizations") \
|
||||
\
|
||||
MACRO(13, "jstracer", JSTracer, "Enable tracing of the JavaScript engine") \
|
||||
\
|
||||
MACRO(14, "jsallocations", JSAllocations, \
|
||||
"Have the JavaScript engine track allocations")
|
||||
# define PROFILER_FOR_EACH_FEATURE(MACRO) \
|
||||
MACRO(0, "java", Java, "Profile Java code, Android only") \
|
||||
\
|
||||
MACRO(1, "js", JS, \
|
||||
"Get the JS engine to expose the JS stack to the profiler") \
|
||||
\
|
||||
/* The DevTools profiler doesn't want the native addresses. */ \
|
||||
MACRO(2, "leaf", Leaf, "Include the C++ leaf node if not stackwalking") \
|
||||
\
|
||||
MACRO(3, "mainthreadio", MainThreadIO, \
|
||||
"Add main thread I/O to the profile") \
|
||||
\
|
||||
MACRO(4, "memory", Memory, "Add memory measurements") \
|
||||
\
|
||||
MACRO(5, "privacy", Privacy, \
|
||||
"Do not include user-identifiable information") \
|
||||
\
|
||||
MACRO(6, "responsiveness", Responsiveness, \
|
||||
"Collect thread responsiveness information") \
|
||||
\
|
||||
MACRO(7, "screenshots", Screenshots, \
|
||||
"Take a snapshot of the window on every composition") \
|
||||
\
|
||||
MACRO(8, "seqstyle", SequentialStyle, \
|
||||
"Disable parallel traversal in styling") \
|
||||
\
|
||||
MACRO(9, "stackwalk", StackWalk, \
|
||||
"Walk the C++ stack, not available on all platforms") \
|
||||
\
|
||||
MACRO(10, "tasktracer", TaskTracer, \
|
||||
"Start profiling with feature TaskTracer") \
|
||||
\
|
||||
MACRO(11, "threads", Threads, "Profile the registered secondary threads") \
|
||||
\
|
||||
MACRO(12, "trackopts", TrackOptimizations, \
|
||||
"Have the JavaScript engine track JIT optimizations") \
|
||||
\
|
||||
MACRO(13, "jstracer", JSTracer, "Enable tracing of the JavaScript engine")
|
||||
|
||||
struct ProfilerFeature {
|
||||
# define DECLARE(n_, str_, Name_, desc_) \
|
||||
|
@ -648,7 +643,6 @@ void profiler_add_marker(const char* aMarkerName,
|
|||
JS::ProfilingCategoryPair aCategoryPair,
|
||||
mozilla::UniquePtr<ProfilerMarkerPayload> aPayload);
|
||||
void profiler_add_js_marker(const char* aMarkerName);
|
||||
void profiler_add_js_allocation_marker(JS::RecordAllocationInfo&& info);
|
||||
|
||||
// Insert a marker in the profile timeline for a specified thread.
|
||||
void profiler_add_marker_for_thread(
|
||||
|
|
|
@ -16,12 +16,9 @@
|
|||
#include "mozilla/net/TimingStruct.h"
|
||||
|
||||
#include "nsString.h"
|
||||
#include "nsCRTGlue.h"
|
||||
#include "GeckoProfiler.h"
|
||||
|
||||
#include "js/Utility.h"
|
||||
#include "js/AllocationRecording.h"
|
||||
#include "js/ProfilingFrameIterator.h"
|
||||
#include "gfxASurface.h"
|
||||
#include "mozilla/ServoTraversalStatistics.h"
|
||||
|
||||
|
@ -416,42 +413,4 @@ class LogMarkerPayload : public ProfilerMarkerPayload {
|
|||
nsCString mText;
|
||||
};
|
||||
|
||||
class JsAllocationMarkerPayload : public ProfilerMarkerPayload {
|
||||
public:
|
||||
JsAllocationMarkerPayload(const mozilla::TimeStamp& aStartTime,
|
||||
const JS::RecordAllocationInfo& aInfo,
|
||||
UniqueProfilerBacktrace aStack)
|
||||
: ProfilerMarkerPayload(aStartTime, aStartTime, mozilla::Nothing(),
|
||||
mozilla::Nothing(), std::move(aStack)),
|
||||
// Copy the strings, and take ownership of them.
|
||||
mTypeName(aInfo.typeName ? NS_xstrdup(aInfo.typeName) : nullptr),
|
||||
mClassName(aInfo.className ? strdup(aInfo.className) : nullptr),
|
||||
mDescriptiveTypeName(aInfo.descriptiveTypeName
|
||||
? NS_xstrdup(aInfo.descriptiveTypeName)
|
||||
: nullptr),
|
||||
mScriptFilename(aInfo.scriptFilename ? strdup(aInfo.scriptFilename)
|
||||
: nullptr),
|
||||
// The coarseType points to a string literal, so does not need to be
|
||||
// duplicated.
|
||||
mCoarseType(aInfo.coarseType),
|
||||
mSize(aInfo.size),
|
||||
mInNursery(aInfo.inNursery) {}
|
||||
|
||||
DECL_STREAM_PAYLOAD
|
||||
|
||||
private:
|
||||
mozilla::UniqueFreePtr<const char16_t> mTypeName;
|
||||
mozilla::UniqueFreePtr<const char> mClassName;
|
||||
mozilla::UniqueFreePtr<const char16_t> mDescriptiveTypeName;
|
||||
mozilla::UniqueFreePtr<const char> mScriptFilename;
|
||||
// Points to a string literal, so does not need to be freed.
|
||||
const char* mCoarseType;
|
||||
|
||||
// The size in bytes of the allocation.
|
||||
uint64_t mSize;
|
||||
|
||||
// Whether or not the allocation is in the nursery or not.
|
||||
bool mInNursery;
|
||||
};
|
||||
|
||||
#endif // ProfilerMarkerPayload_h
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
[DEFAULT]
|
||||
support-files =
|
||||
head.js
|
||||
do_work_500ms.html
|
||||
multi_frame.html
|
||||
single_frame.html
|
||||
single_frame_pushstate.html
|
||||
single_frame_replacestate.html
|
||||
|
||||
[browser_test_feature_jsallocations.js]
|
||||
[browser_test_profile_page_info.js]
|
||||
[browser_test_profile_history_page_info.js]
|
||||
|
|
|
@ -1,105 +0,0 @@
|
|||
/* 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/. */
|
||||
|
||||
/**
|
||||
* Test the JS Allocations feature. This is done as a browser test to ensure that
|
||||
* we realistically try out how the JS allocations are running. This ensures that
|
||||
* we are collecting allocations for the content process and the parent process.
|
||||
*/
|
||||
add_task(async function test_profile_feature_jsallocations() {
|
||||
if (!AppConstants.MOZ_GECKO_PROFILER) {
|
||||
return;
|
||||
}
|
||||
Assert.ok(!Services.profiler.IsActive(), "The profiler is not currently active");
|
||||
|
||||
startProfiler({ features: ["threads", "js", "jsallocations"] });
|
||||
|
||||
const url = BASE_URL + "do_work_500ms.html";
|
||||
await BrowserTestUtils.withNewTab(url, async (contentBrowser) => {
|
||||
const contentPid = await ContentTask.spawn(contentBrowser, null,
|
||||
() => Services.appinfo.processID);
|
||||
|
||||
// Wait 500ms so that the tab finishes executing.
|
||||
await wait(500);
|
||||
|
||||
// Check that we can get some allocations when the feature is turned on.
|
||||
{
|
||||
const { parentThread, contentThread } = await stopProfilerAndGetThreads(contentPid);
|
||||
Assert.greater(getPayloadsOfType(parentThread, "JS allocation").length, 0,
|
||||
"Allocations were recorded for the parent process' main thread when the " +
|
||||
"JS Allocation feature was turned on.");
|
||||
Assert.greater(getPayloadsOfType(contentThread, "JS allocation").length, 0,
|
||||
"Allocations were recorded for the content process' main thread when the " +
|
||||
"JS Allocation feature was turned on.");
|
||||
}
|
||||
|
||||
// Flush out any straggling allocation markers that may have not been collected
|
||||
// yet by starting and stopping the profiler once.
|
||||
startProfiler({ features: ["threads", "js"] });
|
||||
await stopProfilerAndGetThreads(contentPid);
|
||||
|
||||
// Now reload the tab with a clean run.
|
||||
gBrowser.reload();
|
||||
await wait(500);
|
||||
startProfiler({ features: ["threads", "js"] });
|
||||
|
||||
// Check that no allocations were recorded, and allocation tracking was correctly
|
||||
// turned off.
|
||||
{
|
||||
const { parentThread, contentThread } = await stopProfilerAndGetThreads(contentPid);
|
||||
Assert.equal(
|
||||
getPayloadsOfType(parentThread, "JS allocation").length, 0,
|
||||
"No allocations were recorded for the parent processes' main thread when " +
|
||||
"JS allocation was not turned on.");
|
||||
|
||||
Assert.equal(
|
||||
getPayloadsOfType(contentThread, "JS allocation").length, 0,
|
||||
"No allocations were recorded for the content processes' main thread when " +
|
||||
"JS allocation was not turned on.");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Markers are collected only after a periodic sample. This function ensures that
|
||||
* at least one periodic sample has been done.
|
||||
*/
|
||||
async function doAtLeastOnePeriodicSample() {
|
||||
async function getProfileSampleCount() {
|
||||
const profile = await Services.profiler.getProfileDataAsync();
|
||||
return profile.threads[0].samples.data.length;
|
||||
}
|
||||
|
||||
const sampleCount = await getProfileSampleCount();
|
||||
// Create an infinite loop until a sample has been collected.
|
||||
while (true) {
|
||||
if (sampleCount < await getProfileSampleCount()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function stopProfilerAndGetThreads(contentPid) {
|
||||
await doAtLeastOnePeriodicSample();
|
||||
|
||||
const profile = await Services.profiler.getProfileDataAsync();
|
||||
Services.profiler.StopProfiler();
|
||||
|
||||
const parentThread = profile.threads[0];
|
||||
const contentProcess = profile.processes.find(p => p.threads[0].pid == contentPid);
|
||||
if (!contentProcess) {
|
||||
throw new Error("Could not find the content process.");
|
||||
}
|
||||
const contentThread = contentProcess.threads[0];
|
||||
|
||||
if (!parentThread) {
|
||||
throw new Error("The parent thread was not found in the profile.");
|
||||
}
|
||||
|
||||
if (!contentThread) {
|
||||
throw new Error("The content thread was not found in the profile.");
|
||||
}
|
||||
|
||||
return { parentThread, contentThread };
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Do some work for 500ms</title>
|
||||
<script>
|
||||
const milliseconds = 500;
|
||||
const end = Date.now() + milliseconds;
|
||||
window.total = 0;
|
||||
let i = 0;
|
||||
// Do some work for a set amount of time.
|
||||
while (Date.now() < end) {
|
||||
// Do some kind of work that is non-deterministic to guard against optimizations.
|
||||
window.total += Math.random();
|
||||
i++;
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
Do some work for 500ms.
|
||||
</body>
|
||||
</html>
|
|
@ -3,15 +3,13 @@ const { BrowserTestUtils } = ChromeUtils.import("resource://testing-common/Brows
|
|||
|
||||
const BASE_URL = "http://example.com/browser/tools/profiler/tests/browser/";
|
||||
|
||||
const defaultSettings = {
|
||||
entries: 1000000, // 9MB
|
||||
interval: 1, // ms
|
||||
features: ["threads"],
|
||||
threads: ["GeckoMain"],
|
||||
};
|
||||
|
||||
function startProfiler(callersSettings) {
|
||||
const settings = Object.assign({}, defaultSettings, callersSettings);
|
||||
function startProfiler() {
|
||||
const settings = {
|
||||
entries: 1000000, // 9MB
|
||||
interval: 1, // ms
|
||||
features: ["threads"],
|
||||
threads: ["GeckoMain"],
|
||||
};
|
||||
Services.profiler.StartProfiler(
|
||||
settings.entries,
|
||||
settings.interval,
|
||||
|
@ -20,37 +18,3 @@ function startProfiler(callersSettings) {
|
|||
settings.duration
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a helper function be able to run `await wait(500)`. Unfortunately this
|
||||
* is needed as the act of collecting functions relies on the periodic sampling of
|
||||
* the threads. See: https://bugzilla.mozilla.org/show_bug.cgi?id=1529053
|
||||
*
|
||||
* @param {number} time
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function wait(time) {
|
||||
return new Promise(resolve => {
|
||||
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
|
||||
setTimeout(resolve, time);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the payloads of a type from a single thread.
|
||||
*
|
||||
* @param {Object} thread The thread from a profile.
|
||||
* @param {string} type The marker payload type, e.g. "DiskIO".
|
||||
* @return {Array} The payloads.
|
||||
*/
|
||||
function getPayloadsOfType(thread, type) {
|
||||
const {markers} = thread;
|
||||
const results = [];
|
||||
for (const markerTuple of markers.data) {
|
||||
const payload = markerTuple[markers.schema.data];
|
||||
if (payload && payload.type === type) {
|
||||
results.push(payload);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче