Skip to content

Commit

Permalink
Bug 1461555 - Rename PseudoStack to ProfilingStack. r=njn
Browse files Browse the repository at this point in the history
This also changes many references to the 'pseudo stack' to refer to the 'label
stack' instead. The label stack is one of the two stacks that are managed by
the profiling stack, the other stack being the JS interpreter stack.

MozReview-Commit-ID: Ed0YMMeCBY8
  • Loading branch information
mstange committed May 15, 2018
1 parent b0dd333 commit 184da1e
Show file tree
Hide file tree
Showing 24 changed files with 226 additions and 217 deletions.
2 changes: 1 addition & 1 deletion devtools/client/performance/modules/categories.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
const { L10N } = require("devtools/client/performance/modules/global");

/**
* Details about each profile pseudo-stack entry cateogry.
* Details about each label stack frame category.
* @see CATEGORY_MAPPINGS.
*/
const CATEGORIES = [{
Expand Down
69 changes: 36 additions & 33 deletions js/public/ProfilingStack.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,43 +26,46 @@ class JS_PUBLIC_API(JSTracer);
#pragma GCC diagnostic pop
#endif // JS_BROKEN_GCC_ATTRIBUTE_WARNING

class PseudoStack;
class ProfilingStack;

// This file defines the classes PseudoStack and ProfilingStackFrame.
// The PseudoStack manages an array of ProfilingStackFrames.
// This file defines the classes ProfilingStack and ProfilingStackFrame.
// The ProfilingStack manages an array of ProfilingStackFrames.
// It keeps track of the "label stack" and the JS interpreter stack.
// The two stack types are interleaved.
//
// Usage:
//
// PseudoStack* pseudoStack = ...;
// ProfilingStack* profilingStack = ...;
//
// // For label frames:
// pseudoStack->pushLabelFrame(...);
// profilingStack->pushLabelFrame(...);
// // Execute some code. When finished, pop the frame:
// pseudoStack->pop();
// profilingStack->pop();
//
// // For JS stack frames:
// pseudoStack->pushJSFrame(...);
// profilingStack->pushJSFrame(...);
// // Execute some code. When finished, pop the frame:
// pseudoStack->pop();
// profilingStack->pop();
//
//
// Concurrency considerations
//
// A thread's pseudo stack (and the frames inside it) is only modified by
// that thread. However, the pseudo stack can be *read* by a different thread,
// A thread's profiling stack (and the frames inside it) is only modified by
// that thread. However, the profiling stack can be *read* by a different thread,
// the sampler thread: Whenever the profiler wants to sample a given thread A,
// the following happens:
// (1) Thread A is suspended.
// (2) The sampler thread (thread S) reads the PseudoStack of thread A,
// (2) The sampler thread (thread S) reads the ProfilingStack of thread A,
// including all ProfilingStackFrames that are currently in that stack
// (pseudoStack->frames[0..pseudoStack->stackSize()]).
// (profilingStack->frames[0..profilingStack->stackSize()]).
// (3) Thread A is resumed.
//
// Thread suspension is achieved using platform-specific APIs; refer to each
// platform's Sampler::SuspendAndSampleAndResumeThread implementation in
// platform-*.cpp for details.
//
// When the thread is suspended, the values in pseudoStack->stackPointer and in
// the stack frame range pseudoStack->frames[0..pseudoStack->stackPointer] need
// When the thread is suspended, the values in profilingStack->stackPointer and in
// the stack frame range profilingStack->frames[0..profilingStack->stackPointer] need
// to be in a consistent state, so that thread S does not read partially-
// constructed stack frames. More specifically, we have two requirements:
// (1) When adding a new frame at the top of the stack, its ProfilingStackFrame
Expand Down Expand Up @@ -119,7 +122,7 @@ class ProfilingStackFrame
// that writes to these fields are release-writes, which ensures that
// earlier writes in this thread don't get reordered after the writes to
// these fields. In particular, the decrement of the stack pointer in
// PseudoStack::pop() is a write that *must* happen before the values in
// ProfilingStack::pop() is a write that *must* happen before the values in
// this ProfilingStackFrame are changed. Otherwise, the sampler thread might
// see an inconsistent state where the stack pointer still points to a
// ProfilingStackFrame which has already been popped off the stack and whose
Expand Down Expand Up @@ -303,7 +306,7 @@ class ProfilingStackFrame
};

JS_FRIEND_API(void)
SetContextProfilingStack(JSContext* cx, PseudoStack* pseudoStack);
SetContextProfilingStack(JSContext* cx, ProfilingStack* profilingStack);

// GetContextProfilingStack also exists, but it's defined in RootingAPI.h.

Expand All @@ -315,12 +318,12 @@ RegisterContextProfilingEventMarker(JSContext* cx, void (*fn)(const char*));

} // namespace js

// Each thread has its own PseudoStack. That thread modifies the PseudoStack,
// Each thread has its own ProfilingStack. That thread modifies the ProfilingStack,
// pushing and popping elements as necessary.
//
// The PseudoStack is also read periodically by the profiler's sampler thread.
// This happens only when the thread that owns the PseudoStack is suspended. So
// there are no genuine parallel accesses.
// The ProfilingStack is also read periodically by the profiler's sampler thread.
// This happens only when the thread that owns the ProfilingStack is suspended.
// So there are no genuine parallel accesses.
//
// However, it is possible for pushing/popping to be interrupted by a periodic
// sample. Because of this, we need pushing/popping to be effectively atomic.
Expand All @@ -334,14 +337,14 @@ RegisterContextProfilingEventMarker(JSContext* cx, void (*fn)(const char*));
// - When popping an old frame, the only operation is the decrementing of the
// stack pointer, which is obviously atomic.
//
class PseudoStack final
class ProfilingStack final
{
public:
PseudoStack()
ProfilingStack()
: stackPointer(0)
{}

~PseudoStack();
~ProfilingStack();

void pushLabelFrame(const char* label, const char* dynamicString, void* sp,
uint32_t line, js::ProfilingStackFrame::Category category) {
Expand Down Expand Up @@ -402,12 +405,12 @@ class PseudoStack final
MOZ_COLD MOZ_MUST_USE bool ensureCapacitySlow();

// No copying.
PseudoStack(const PseudoStack&) = delete;
void operator=(const PseudoStack&) = delete;
ProfilingStack(const ProfilingStack&) = delete;
void operator=(const ProfilingStack&) = delete;

// No moving either.
PseudoStack(PseudoStack&&) = delete;
void operator=(PseudoStack&&) = delete;
ProfilingStack(ProfilingStack&&) = delete;
void operator=(ProfilingStack&&) = delete;

uint32_t capacity = 0;

Expand Down Expand Up @@ -445,19 +448,19 @@ class GeckoProfilerThread
friend class GeckoProfilerEntryMarker;
friend class GeckoProfilerBaselineOSRMarker;

PseudoStack* pseudoStack_;
ProfilingStack* profilingStack_;

public:
GeckoProfilerThread();

uint32_t stackPointer() { MOZ_ASSERT(installed()); return pseudoStack_->stackPointer; }
ProfilingStackFrame* stack() { return pseudoStack_->frames; }
PseudoStack* getPseudoStack() { return pseudoStack_; }
uint32_t stackPointer() { MOZ_ASSERT(installed()); return profilingStack_->stackPointer; }
ProfilingStackFrame* stack() { return profilingStack_->frames; }
ProfilingStack* getProfilingStack() { return profilingStack_; }

/* management of whether instrumentation is on or off */
bool installed() { return pseudoStack_ != nullptr; }
bool installed() { return profilingStack_ != nullptr; }

void setProfilingStack(PseudoStack* pseudoStack);
void setProfilingStack(ProfilingStack* profilingStack);
void trace(JSTracer* trc);

/*
Expand Down
4 changes: 2 additions & 2 deletions js/public/RootingAPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -1030,10 +1030,10 @@ GetContextZone(const JSContext* cx)
return JS::RootingContext::get(cx)->zone_;
}

inline PseudoStack*
inline ProfilingStack*
GetContextProfilingStack(JSContext* cx)
{
return JS::RootingContext::get(cx)->geckoProfiler().getPseudoStack();
return JS::RootingContext::get(cx)->geckoProfiler().getProfilingStack();
}

/**
Expand Down
6 changes: 3 additions & 3 deletions js/src/gc/GC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6802,7 +6802,7 @@ HeapStateToLabel(JS::HeapState heapState)
return "JS_IterateCompartments";
case JS::HeapState::Idle:
case JS::HeapState::CycleCollecting:
MOZ_CRASH("Should never have an Idle or CC heap state when pushing GC pseudo frames!");
MOZ_CRASH("Should never have an Idle or CC heap state when pushing GC profiling stack frames!");
}
MOZ_ASSERT_UNREACHABLE("Should have exhausted every JS::HeapState variant!");
return nullptr;
Expand All @@ -6812,8 +6812,8 @@ HeapStateToLabel(JS::HeapState heapState)
AutoTraceSession::AutoTraceSession(JSRuntime* rt, JS::HeapState heapState)
: runtime(rt),
prevState(rt->mainContextFromOwnThread()->heapState),
pseudoFrame(rt->mainContextFromOwnThread(), HeapStateToLabel(heapState),
ProfilingStackFrame::Category::GC)
profilingStackFrame(rt->mainContextFromOwnThread(), HeapStateToLabel(heapState),
ProfilingStackFrame::Category::GC)
{
MOZ_ASSERT(prevState == JS::HeapState::Idle);
MOZ_ASSERT(heapState != JS::HeapState::Idle);
Expand Down
2 changes: 1 addition & 1 deletion js/src/gc/GCInternals.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class MOZ_RAII AutoTraceSession
void operator=(const AutoTraceSession&) = delete;

JS::HeapState prevState;
AutoGeckoProfilerEntry pseudoFrame;
AutoGeckoProfilerEntry profilingStackFrame;
};

class MOZ_RAII AutoPrepareForTracing
Expand Down
28 changes: 14 additions & 14 deletions js/src/jsapi-tests/testProfileStrings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@
#include "jsapi-tests/tests.h"
#include "vm/JSContext.h"

static PseudoStack pseudoStack;
static ProfilingStack profilingStack;
static uint32_t peakStackPointer = 0;

static void
reset(JSContext* cx)
{
pseudoStack.stackPointer = 0;
profilingStack.stackPointer = 0;
cx->runtime()->geckoProfiler().stringsReset();
cx->runtime()->geckoProfiler().enableSlowAssertions(true);
js::EnableContextProfilingStack(cx, true);
Expand All @@ -31,7 +31,7 @@ static const JSClass ptestClass = {
static bool
test_fn(JSContext* cx, unsigned argc, JS::Value* vp)
{
peakStackPointer = pseudoStack.stackPointer;
peakStackPointer = profilingStack.stackPointer;
return true;
}

Expand Down Expand Up @@ -79,7 +79,7 @@ static const JSFunctionSpec ptestFunctions[] = {
static JSObject*
initialize(JSContext* cx)
{
js::SetContextProfilingStack(cx, &pseudoStack);
js::SetContextProfilingStack(cx, &profilingStack);
JS::RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
return JS_InitClass(cx, global, nullptr, &ptestClass, Prof, 0,
nullptr, ptestFunctions, nullptr, nullptr);
Expand All @@ -105,14 +105,14 @@ BEGIN_TEST(testProfileStrings_isCalledWithInterpreter)
/* Make sure the stack resets and we have an entry for each stack */
CHECK(JS_CallFunctionName(cx, global, "check", JS::HandleValueArray::empty(),
&rval));
CHECK(pseudoStack.stackPointer == 0);
CHECK(profilingStack.stackPointer == 0);
CHECK(peakStackPointer >= 8);
CHECK(cx->runtime()->geckoProfiler().stringsCount() == 8);
/* Make sure the stack resets and we added no new entries */
peakStackPointer = 0;
CHECK(JS_CallFunctionName(cx, global, "check", JS::HandleValueArray::empty(),
&rval));
CHECK(pseudoStack.stackPointer == 0);
CHECK(profilingStack.stackPointer == 0);
CHECK(peakStackPointer >= 8);
CHECK(cx->runtime()->geckoProfiler().stringsCount() == 8);
}
Expand All @@ -123,7 +123,7 @@ BEGIN_TEST(testProfileStrings_isCalledWithInterpreter)
&rval));
CHECK(cx->runtime()->geckoProfiler().stringsCount() == 5);
CHECK(peakStackPointer >= 6);
CHECK(pseudoStack.stackPointer == 0);
CHECK(profilingStack.stackPointer == 0);
}
return true;
}
Expand Down Expand Up @@ -151,15 +151,15 @@ BEGIN_TEST(testProfileStrings_isCalledWithJIT)
/* Make sure the stack resets and we have an entry for each stack */
CHECK(JS_CallFunctionName(cx, global, "check", JS::HandleValueArray::empty(),
&rval));
CHECK(pseudoStack.stackPointer == 0);
CHECK(profilingStack.stackPointer == 0);
CHECK(peakStackPointer >= 8);

/* Make sure the stack resets and we added no new entries */
uint32_t cnt = cx->runtime()->geckoProfiler().stringsCount();
peakStackPointer = 0;
CHECK(JS_CallFunctionName(cx, global, "check", JS::HandleValueArray::empty(),
&rval));
CHECK(pseudoStack.stackPointer == 0);
CHECK(profilingStack.stackPointer == 0);
CHECK(cx->runtime()->geckoProfiler().stringsCount() == cnt);
CHECK(peakStackPointer >= 8);
}
Expand All @@ -183,7 +183,7 @@ BEGIN_TEST(testProfileStrings_isCalledWhenError)
bool ok = JS_CallFunctionName(cx, global, "check2", JS::HandleValueArray::empty(),
&rval);
CHECK(!ok);
CHECK(pseudoStack.stackPointer == 0);
CHECK(profilingStack.stackPointer == 0);
CHECK(cx->runtime()->geckoProfiler().stringsCount() == 1);

JS_ClearPendingException(cx);
Expand All @@ -207,7 +207,7 @@ BEGIN_TEST(testProfileStrings_worksWhenEnabledOnTheFly)
/* enable it in the middle of JS and make sure things check out */
JS::RootedValue rval(cx);
JS_CallFunctionName(cx, global, "a", JS::HandleValueArray::empty(), &rval);
CHECK(pseudoStack.stackPointer == 0);
CHECK(profilingStack.stackPointer == 0);
CHECK(peakStackPointer >= 1);
CHECK(cx->runtime()->geckoProfiler().stringsCount() == 1);
}
Expand All @@ -219,7 +219,7 @@ BEGIN_TEST(testProfileStrings_worksWhenEnabledOnTheFly)
/* now disable in the middle of js */
JS::RootedValue rval(cx);
JS_CallFunctionName(cx, global, "c", JS::HandleValueArray::empty(), &rval);
CHECK(pseudoStack.stackPointer == 0);
CHECK(profilingStack.stackPointer == 0);
}

EXEC("function e() { var p = new Prof(); d(p); p.enable(); b(p); }");
Expand All @@ -228,7 +228,7 @@ BEGIN_TEST(testProfileStrings_worksWhenEnabledOnTheFly)
/* now disable in the middle of js, but re-enable before final exit */
JS::RootedValue rval(cx);
JS_CallFunctionName(cx, global, "e", JS::HandleValueArray::empty(), &rval);
CHECK(pseudoStack.stackPointer == 0);
CHECK(profilingStack.stackPointer == 0);
CHECK(peakStackPointer >= 3);
}

Expand All @@ -242,7 +242,7 @@ BEGIN_TEST(testProfileStrings_worksWhenEnabledOnTheFly)
/* disable, and make sure that if we try to re-enter the JIT the pop
* will still happen */
JS_CallFunctionName(cx, global, "f", JS::HandleValueArray::empty(), &rval);
CHECK(pseudoStack.stackPointer == 0);
CHECK(profilingStack.stackPointer == 0);
}
return true;
}
Expand Down
2 changes: 1 addition & 1 deletion js/src/shell/js.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3534,7 +3534,7 @@ EnsureGeckoProfilingStackInstalled(JSContext* cx, ShellContext* sc)
}

MOZ_ASSERT(!sc->geckoProfilingStack);
sc->geckoProfilingStack = MakeUnique<PseudoStack>();
sc->geckoProfilingStack = MakeUnique<ProfilingStack>();
if (!sc->geckoProfilingStack) {
JS_ReportOutOfMemory(cx);
return false;
Expand Down
2 changes: 1 addition & 1 deletion js/src/shell/jsshell.h
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ struct ShellContext
js::shell::RCFile** errFilePtr;
js::shell::RCFile** outFilePtr;

UniquePtr<PseudoStack> geckoProfilingStack;
UniquePtr<ProfilingStack> geckoProfilingStack;

JS::UniqueChars moduleLoadPath;
UniquePtr<MarkBitObservers> markObservers;
Expand Down
Loading

0 comments on commit 184da1e

Please sign in to comment.