Bug 1334927 - Handle multiple contexts per runtime in the Gecko profiler, r=shu.

--HG--
extra : rebase_source : 8673bc042c7d04b5595583c229a13a1e18b7f73e
This commit is contained in:
Brian Hackett 2017-02-08 11:18:41 -07:00
Родитель 6cce3a3af9
Коммит 3601646de2
14 изменённых файлов: 157 добавлений и 75 удалений

Просмотреть файл

@ -46,7 +46,7 @@ struct ForEachTrackedOptimizationTypeInfoOp;
// contents to become out of date.
class JS_PUBLIC_API(ProfilingFrameIterator)
{
JSRuntime* rt_;
JSContext* cx_;
uint32_t sampleBufferGen_;
js::Activation* activation_;

Просмотреть файл

@ -94,7 +94,7 @@ CompileRuntime::wellKnownSymbols()
const void*
CompileRuntime::addressOfActiveJSContext()
{
return &runtime()->activeContext;
return runtime()->addressOfActiveContext();
}
#ifdef DEBUG

Просмотреть файл

@ -284,7 +284,7 @@ class JitProfilingFrameIterator
void moveToNextFrame(CommonFrameLayout* frame);
public:
JitProfilingFrameIterator(JSRuntime* rt,
JitProfilingFrameIterator(JSContext* cx,
const JS::ProfilingFrameIterator::RegisterState& state);
explicit JitProfilingFrameIterator(void* exitFrame);

Просмотреть файл

@ -2832,11 +2832,10 @@ JitFrameIterator::verifyReturnAddressUsingNativeToBytecodeMap()
#endif // DEBUG
JitProfilingFrameIterator::JitProfilingFrameIterator(
JSRuntime* rt, const JS::ProfilingFrameIterator::RegisterState& state)
JSContext* cx, const JS::ProfilingFrameIterator::RegisterState& state)
{
// If no profilingActivation is live, initialize directly to
// end-of-iteration state.
JSContext* cx = rt->unsafeContextFromAnyThread();
if (!cx->profilingActivation()) {
type_ = JitFrame_Entry;
fp_ = nullptr;
@ -2862,7 +2861,7 @@ JitProfilingFrameIterator::JitProfilingFrameIterator(
fp_ = (uint8_t*) act->lastProfilingFrame();
void* lastCallSite = act->lastProfilingCallSite();
JitcodeGlobalTable* table = rt->jitRuntime()->getJitcodeGlobalTable();
JitcodeGlobalTable* table = cx->runtime()->jitRuntime()->getJitcodeGlobalTable();
// Profiler sampling must NOT be suppressed if we are here.
MOZ_ASSERT(cx->isProfilerSamplingEnabled());
@ -2872,7 +2871,7 @@ JitProfilingFrameIterator::JitProfilingFrameIterator(
return;
// Try initializing with sampler pc using native=>bytecode table.
if (tryInitWithTable(table, state.pc, rt, /* forLastCallSite = */ false))
if (tryInitWithTable(table, state.pc, cx->runtime(), /* forLastCallSite = */ false))
return;
// Try initializing with lastProfilingCallSite pc
@ -2881,7 +2880,7 @@ JitProfilingFrameIterator::JitProfilingFrameIterator(
return;
// Try initializing with lastProfilingCallSite pc using native=>bytecode table.
if (tryInitWithTable(table, lastCallSite, rt, /* forLastCallSite = */ true))
if (tryInitWithTable(table, lastCallSite, cx->runtime(), /* forLastCallSite = */ true))
return;
}

Просмотреть файл

@ -25,6 +25,7 @@
#include "jsscriptinlines.h"
#include "vm/GeckoProfiler-inl.h"
#include "vm/TypeInference-inl.h"
using mozilla::Maybe;

Просмотреть файл

@ -243,6 +243,7 @@
#include "jsscriptinlines.h"
#include "gc/Heap-inl.h"
#include "vm/GeckoProfiler-inl.h"
#include "vm/Stack-inl.h"
#include "vm/String-inl.h"
@ -5517,7 +5518,7 @@ GCRuntime::compactPhase(JS::gcreason::Reason reason, SliceBudget& sliceBudget,
// middle of relocating an arena, invalid JSScript pointers may be
// accessed. Suppress all sampling until a finer-grained solution can be
// found. See bug 1295775.
AutoSuppressProfilerSampling suppressSampling(rt);
AutoSuppressProfilerSampling suppressSampling(TlsContext.get());
ZoneList relocatedZones;
Arena* relocatedArenas = nullptr;

Просмотреть файл

@ -47,7 +47,7 @@ CheckActiveThread<Background>::check() const
return;
JSContext* cx = TlsContext.get();
MOZ_ASSERT(cx == cx->runtime()->activeContext);
MOZ_ASSERT(cx == cx->runtime()->activeContext());
#endif // XP_WIN
}

Просмотреть файл

@ -45,6 +45,7 @@
#include "jsopcodeinlines.h"
#include "jsscriptinlines.h"
#include "vm/GeckoProfiler-inl.h"
#include "vm/NativeObject-inl.h"
#include "vm/Stack-inl.h"

Просмотреть файл

@ -0,0 +1,36 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* 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 vm_GeckoProfiler_inl_h
#define vm_GeckoProfiler_inl_h
#include "vm/GeckoProfiler.h"
#include "vm/Runtime.h"
namespace js {
/*
* This class is used to suppress profiler sampling during
* critical sections where stack state is not valid.
*/
class MOZ_RAII AutoSuppressProfilerSampling
{
public:
explicit AutoSuppressProfilerSampling(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
~AutoSuppressProfilerSampling();
private:
JSContext* cx_;
bool previouslyEnabled_;
JSRuntime::AutoProhibitActiveContextChange prohibitContextChange_;
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
};
} // namespace js
#endif // vm_GeckoProfiler_inl_h

Просмотреть файл

@ -4,7 +4,7 @@
* 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 "vm/GeckoProfiler.h"
#include "vm/GeckoProfiler-inl.h"
#include "mozilla/DebugOnly.h"
@ -86,10 +86,13 @@ GeckoProfiler::enable(bool enabled)
rt->resetProfilerSampleBufferGen();
rt->resetProfilerSampleBufferLapCount();
// Ensure that lastProfilingFrame is null before 'enabled' becomes true.
if (rt->contextFromMainThread()->jitActivation) {
rt->contextFromMainThread()->jitActivation->setLastProfilingFrame(nullptr);
rt->contextFromMainThread()->jitActivation->setLastProfilingCallSite(nullptr);
// Ensure that lastProfilingFrame is null for all threads before 'enabled' becomes true.
for (size_t i = 0; i < rt->cooperatingContexts().length(); i++) {
JSContext* cx = rt->cooperatingContexts()[i];
if (cx->jitActivation) {
cx->jitActivation->setLastProfilingFrame(nullptr);
cx->jitActivation->setLastProfilingCallSite(nullptr);
}
}
enabled_ = enabled;
@ -104,24 +107,27 @@ GeckoProfiler::enable(bool enabled)
/* Update lastProfilingFrame to point to the top-most JS jit-frame currently on
* stack.
*/
if (rt->contextFromMainThread()->jitActivation) {
// Walk through all activations, and set their lastProfilingFrame appropriately.
if (enabled) {
void* lastProfilingFrame = GetTopProfilingJitFrame(rt->contextFromMainThread()->jitTop);
jit::JitActivation* jitActivation = rt->contextFromMainThread()->jitActivation;
while (jitActivation) {
jitActivation->setLastProfilingFrame(lastProfilingFrame);
jitActivation->setLastProfilingCallSite(nullptr);
for (size_t i = 0; i < rt->cooperatingContexts().length(); i++) {
JSContext* cx = rt->cooperatingContexts()[i];
if (cx->jitActivation) {
// Walk through all activations, and set their lastProfilingFrame appropriately.
if (enabled) {
void* lastProfilingFrame = GetTopProfilingJitFrame(cx->jitTop);
jit::JitActivation* jitActivation = cx->jitActivation;
while (jitActivation) {
jitActivation->setLastProfilingFrame(lastProfilingFrame);
jitActivation->setLastProfilingCallSite(nullptr);
lastProfilingFrame = GetTopProfilingJitFrame(jitActivation->prevJitTop());
jitActivation = jitActivation->prevJitActivation();
}
} else {
jit::JitActivation* jitActivation = rt->contextFromMainThread()->jitActivation;
while (jitActivation) {
jitActivation->setLastProfilingFrame(nullptr);
jitActivation->setLastProfilingCallSite(nullptr);
jitActivation = jitActivation->prevJitActivation();
lastProfilingFrame = GetTopProfilingJitFrame(jitActivation->prevJitTop());
jitActivation = jitActivation->prevJitActivation();
}
} else {
jit::JitActivation* jitActivation = cx->jitActivation;
while (jitActivation) {
jitActivation->setLastProfilingFrame(nullptr);
jitActivation->setLastProfilingCallSite(nullptr);
jitActivation = jitActivation->prevJitActivation();
}
}
}
}
@ -495,8 +501,12 @@ ProfileEntry::script() const volatile
// If profiling is supressed then we can't trust the script pointers to be
// valid as they could be in the process of being moved by a compacting GC
// (although it's still OK to get the runtime from them).
JSRuntime* rt = script->zoneFromAnyThread()->runtimeFromAnyThread();
if (!rt->unsafeContextFromAnyThread()->isProfilerSamplingEnabled())
//
// We only need to check the active context here, as
// AutoSuppressProfilerSampling prohibits the runtime's active context from
// being changed while it exists.
JSContext* cx = script->runtimeFromAnyThread()->activeContext();
if (!cx->isProfilerSamplingEnabled())
return nullptr;
MOZ_ASSERT(!IsForwarded(script));
@ -550,28 +560,19 @@ js::ProfilingGetPC(JSContext* cx, JSScript* script, void* ip)
AutoSuppressProfilerSampling::AutoSuppressProfilerSampling(JSContext* cx
MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
: rt_(cx->runtime()),
previouslyEnabled_(cx->isProfilerSamplingEnabled())
: cx_(cx),
previouslyEnabled_(cx->isProfilerSamplingEnabled()),
prohibitContextChange_(cx->runtime())
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
if (previouslyEnabled_)
rt_->contextFromMainThread()->disableProfilerSampling();
}
AutoSuppressProfilerSampling::AutoSuppressProfilerSampling(JSRuntime* rt
MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
: rt_(rt),
previouslyEnabled_(rt_->contextFromMainThread()->isProfilerSamplingEnabled())
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
if (previouslyEnabled_)
rt_->contextFromMainThread()->disableProfilerSampling();
cx_->disableProfilerSampling();
}
AutoSuppressProfilerSampling::~AutoSuppressProfilerSampling()
{
if (previouslyEnabled_)
rt_->contextFromMainThread()->enableProfilerSampling();
cx_->enableProfilerSampling();
}
void*

Просмотреть файл

@ -217,24 +217,6 @@ class GeckoProfiler
#endif
};
/*
* This class is used to suppress profiler sampling during
* critical sections where stack state is not valid.
*/
class MOZ_RAII AutoSuppressProfilerSampling
{
public:
explicit AutoSuppressProfilerSampling(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
explicit AutoSuppressProfilerSampling(JSRuntime* rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
~AutoSuppressProfilerSampling();
private:
JSRuntime* rt_;
bool previouslyEnabled_;
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
};
inline size_t
GeckoProfiler::stringsCount()
{

Просмотреть файл

@ -95,7 +95,8 @@ JSRuntime::JSRuntime(JSRuntime* parentRuntime)
#ifdef DEBUG
updateChildRuntimeCount(parentRuntime),
#endif
activeContext(nullptr),
activeContext_(nullptr),
activeContextChangeProhibited_(0),
profilerSampleBufferGen_(0),
profilerSampleBufferLapCount_(1),
telemetryCallback(nullptr),
@ -192,7 +193,9 @@ JSRuntime::init(JSContext* cx, uint32_t maxbytes, uint32_t maxNurseryBytes)
if (CanUseExtraThreads() && !EnsureHelperThreadsInitialized())
return false;
activeContext = cx;
activeContext_ = cx;
if (!cooperatingContexts().append(cx))
return false;
singletonContext = cx;
@ -344,6 +347,15 @@ JSRuntime::destroyRuntime()
js_delete(zoneGroupFromMainThread());
}
void
JSRuntime::setActiveContext(JSContext* cx)
{
MOZ_ASSERT_IF(cx, isCooperatingContext(cx));
MOZ_RELEASE_ASSERT(!activeContextChangeProhibited());
activeContext_ = cx;
}
void
JSRuntime::addTelemetry(int id, uint32_t sample, const char* key)
{

Просмотреть файл

@ -297,10 +297,57 @@ struct JSRuntime : public js::MallocProvider<JSRuntime>
AutoUpdateChildRuntimeCount updateChildRuntimeCount;
#endif
private:
// The context for the thread which currently has exclusive access to most
// contents of the runtime. When execution on the runtime is cooperatively
// scheduled, this is the thread which is currently running.
mozilla::Atomic<JSContext*, mozilla::ReleaseAcquire> activeContext;
mozilla::Atomic<JSContext*, mozilla::ReleaseAcquire> activeContext_;
// All contexts participating in cooperative scheduling. All threads other
// than |activeContext_| are suspended.
js::ActiveThreadData<js::Vector<JSContext*, 4, js::SystemAllocPolicy>> cooperatingContexts_;
// Count of AutoProhibitActiveContextChange instances on the active context.
js::ActiveThreadData<size_t> activeContextChangeProhibited_;
public:
JSContext* activeContext() { return activeContext_; }
const void* addressOfActiveContext() { return &activeContext_; }
void setActiveContext(JSContext* cx);
js::Vector<JSContext*, 4, js::SystemAllocPolicy>& cooperatingContexts() {
return cooperatingContexts_.ref();
}
#ifdef DEBUG
bool isCooperatingContext(JSContext* cx) {
for (size_t i = 0; i < cooperatingContexts().length(); i++) {
if (cooperatingContexts()[i] == cx)
return true;
}
return false;
}
#endif
class MOZ_RAII AutoProhibitActiveContextChange
{
JSRuntime* rt;
public:
explicit AutoProhibitActiveContextChange(JSRuntime* rt)
: rt(rt)
{
rt->activeContextChangeProhibited_++;
}
~AutoProhibitActiveContextChange()
{
rt->activeContextChangeProhibited_--;
}
};
bool activeContextChangeProhibited() { return activeContextChangeProhibited_; }
/*
* The profiler sampler generation after the latest sample.

Просмотреть файл

@ -26,6 +26,7 @@
using namespace js;
using mozilla::ArrayLength;
using mozilla::Maybe;
using mozilla::PodCopy;
@ -1739,7 +1740,7 @@ ActivationIterator::settle()
JS::ProfilingFrameIterator::ProfilingFrameIterator(JSContext* cx, const RegisterState& state,
uint32_t sampleBufferGen)
: rt_(cx->runtime()),
: cx_(cx),
sampleBufferGen_(sampleBufferGen),
activation_(nullptr),
savedPrevJitTop_(nullptr)
@ -1821,7 +1822,7 @@ JS::ProfilingFrameIterator::iteratorConstruct(const RegisterState& state)
}
MOZ_ASSERT(activation_->asJit()->isActive());
new (storage_.addr()) jit::JitProfilingFrameIterator(rt_, state);
new (storage_.addr()) jit::JitProfilingFrameIterator(cx_, state);
}
void
@ -1899,9 +1900,9 @@ JS::ProfilingFrameIterator::getPhysicalFrameAndEntry(jit::JitcodeGlobalEntry* en
// Look up an entry for the return address.
void* returnAddr = jitIter().returnAddressToFp();
jit::JitcodeGlobalTable* table = rt_->jitRuntime()->getJitcodeGlobalTable();
jit::JitcodeGlobalTable* table = cx_->runtime()->jitRuntime()->getJitcodeGlobalTable();
if (hasSampleBufferGen())
*entry = table->lookupForSamplerInfallible(returnAddr, rt_, sampleBufferGen_);
*entry = table->lookupForSamplerInfallible(returnAddr, cx_->runtime(), sampleBufferGen_);
else
*entry = table->lookupInfallible(returnAddr);
@ -1941,8 +1942,9 @@ JS::ProfilingFrameIterator::extractStack(Frame* frames, uint32_t offset, uint32_
// Extract the stack for the entry. Assume maximum inlining depth is <64
const char* labels[64];
uint32_t depth = entry.callStackAtAddr(rt_, jitIter().returnAddressToFp(), labels, 64);
MOZ_ASSERT(depth < 64);
uint32_t depth = entry.callStackAtAddr(cx_->runtime(), jitIter().returnAddressToFp(),
labels, ArrayLength(labels));
MOZ_ASSERT(depth < ArrayLength(labels));
for (uint32_t i = 0; i < depth; i++) {
if (offset + i >= end)
return i;