Backed out changeset 3923ce220df3 (bug 1380286) for hazard failures

This commit is contained in:
Carsten "Tomcat" Book 2017-07-25 08:44:13 +02:00
Родитель 8408f943c1
Коммит 965777ef3a
6 изменённых файлов: 62 добавлений и 289 удалений

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

@ -8,8 +8,6 @@
#include "ProfilerMarker.h"
using namespace mozilla;
ProfileBuffer::ProfileBuffer(int aEntrySize)
: mEntries(mozilla::MakeUnique<ProfileBufferEntry[]>(aEntrySize))
, mWritePos(0)
@ -57,6 +55,24 @@ ProfileBuffer::AddThreadIdEntry(int aThreadId, LastSample* aLS)
AddEntry(ProfileBufferEntry::ThreadId(aThreadId));
}
void
ProfileBuffer::AddDynamicStringEntry(const char* aStr)
{
size_t strLen = strlen(aStr) + 1; // +1 for the null terminator
for (size_t j = 0; j < strLen; ) {
// Store up to kNumChars characters in the entry.
char chars[ProfileBufferEntry::kNumChars];
size_t len = ProfileBufferEntry::kNumChars;
if (j + len >= strLen) {
len = strLen - j;
}
memcpy(chars, &aStr[j], len);
j += ProfileBufferEntry::kNumChars;
AddEntry(ProfileBufferEntry::DynamicStringFragment(chars));
}
}
void
ProfileBuffer::AddStoredMarker(ProfilerMarker *aStoredMarker)
{
@ -64,51 +80,6 @@ ProfileBuffer::AddStoredMarker(ProfilerMarker *aStoredMarker)
mStoredMarkers.insert(aStoredMarker);
}
void
ProfileBuffer::CollectNativeLeafAddr(void* aAddr)
{
AddEntry(ProfileBufferEntry::NativeLeafAddr(aAddr));
}
void
ProfileBuffer::CollectJitReturnAddr(void* aAddr)
{
AddEntry(ProfileBufferEntry::JitReturnAddr(aAddr));
}
void
ProfileBuffer::CollectCodeLocation(
const char* aLabel, const char* aStr, int aLineNumber,
const Maybe<js::ProfileEntry::Category>& aCategory)
{
AddEntry(ProfileBufferEntry::Label(aLabel));
if (aStr) {
// Store the string using one or more DynamicStringFragment entries.
size_t strLen = strlen(aStr) + 1; // +1 for the null terminator
for (size_t j = 0; j < strLen; ) {
// Store up to kNumChars characters in the entry.
char chars[ProfileBufferEntry::kNumChars];
size_t len = ProfileBufferEntry::kNumChars;
if (j + len >= strLen) {
len = strLen - j;
}
memcpy(chars, &aStr[j], len);
j += ProfileBufferEntry::kNumChars;
AddEntry(ProfileBufferEntry::DynamicStringFragment(chars));
}
}
if (aLineNumber != -1) {
AddEntry(ProfileBufferEntry::LineNumber(aLineNumber));
}
if (aCategory.isSome()) {
AddEntry(ProfileBufferEntry::Category(int(*aCategory)));
}
}
void
ProfileBuffer::DeleteExpiredStoredMarkers()
{

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

@ -13,7 +13,7 @@
#include "mozilla/RefPtr.h"
#include "mozilla/RefCounted.h"
class ProfileBuffer final : public ProfilerStackCollector
class ProfileBuffer final
{
public:
explicit ProfileBuffer(int aEntrySize);
@ -42,16 +42,9 @@ public:
// record the resulting generation and index in |aLS| if it's non-null.
void AddThreadIdEntry(int aThreadId, LastSample* aLS = nullptr);
virtual mozilla::Maybe<uint32_t> Generation() override
{
return mozilla::Some(mGeneration);
}
virtual void CollectNativeLeafAddr(void* aAddr) override;
virtual void CollectJitReturnAddr(void* aAddr) override;
virtual void CollectCodeLocation(
const char* aLabel, const char* aStr, int aLineNumber,
const mozilla::Maybe<js::ProfileEntry::Category>& aCategory) override;
// Add to the buffer a dynamic string. It'll be spread across one or more
// DynamicStringFragment entries.
void AddDynamicStringEntry(const char* aStr);
// Maximum size of a frameKey string that we'll handle.
static const size_t kMaxFrameKeyLength = 512;

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

@ -22,7 +22,7 @@
//
// - A "backtrace" sample is the simplest kind. It is done in response to an
// API call (profiler_suspend_and_sample_thread()). It involves getting a
// stack trace via a ProfilerStackCollector; it does not write to a
// stack trace and passing it to a callback function; it does not write to a
// ProfileBuffer. The sampling is done from off-thread, and so uses
// SuspendAndSampleAndResumeThread() to get the register values.
@ -54,9 +54,7 @@
#include "nsIXULRuntime.h"
#include "nsDirectoryServiceUtils.h"
#include "nsDirectoryServiceDefs.h"
#include "nsJSPrincipals.h"
#include "nsMemoryReporterManager.h"
#include "nsScriptSecurityManager.h"
#include "nsXULAppAPI.h"
#include "nsProfilerStartParams.h"
#include "ProfilerParent.h"
@ -659,31 +657,17 @@ public:
#endif
};
static bool
IsChromeJSScript(JSScript* aScript)
{
// WARNING: this function runs within the profiler's "critical section".
nsIScriptSecurityManager* const secman =
nsScriptSecurityManager::GetScriptSecurityManager();
NS_ENSURE_TRUE(secman, false);
JSPrincipals* const principals = JS_GetScriptPrincipals(aScript);
return secman->IsSystemPrincipal(nsJSPrincipals::get(principals));
}
static void
AddPseudoEntry(uint32_t aFeatures, NotNull<RacyThreadInfo*> aRacyInfo,
const js::ProfileEntry& entry,
ProfilerStackCollector& aCollector)
AddPseudoEntry(PSLockRef aLock, NotNull<RacyThreadInfo*> aRacyInfo,
const js::ProfileEntry& entry, ProfileBuffer& aBuffer)
{
// WARNING: this function runs within the profiler's "critical section".
// WARNING: this function might be called while the profiler is inactive, and
// cannot rely on ActivePS.
MOZ_ASSERT(entry.kind() == js::ProfileEntry::Kind::CPP_NORMAL ||
entry.kind() == js::ProfileEntry::Kind::JS_NORMAL);
aBuffer.AddEntry(ProfileBufferEntry::Label(entry.label()));
const char* dynamicString = entry.dynamicString();
int lineno = -1;
@ -691,11 +675,18 @@ AddPseudoEntry(uint32_t aFeatures, NotNull<RacyThreadInfo*> aRacyInfo,
// |dynamicString|. Perhaps it shouldn't?
if (dynamicString) {
bool isChromeJSEntry = false;
// Adjust the dynamic string as necessary.
if (ActivePS::FeaturePrivacy(aLock)) {
dynamicString = "(private)";
} else if (strlen(dynamicString) >= ProfileBuffer::kMaxFrameKeyLength) {
dynamicString = "(too long)";
}
// Store the string using one or more DynamicStringFragment entries.
aBuffer.AddDynamicStringEntry(dynamicString);
if (entry.isJs()) {
JSScript* script = entry.script();
if (script) {
isChromeJSEntry = IsChromeJSScript(script);
if (!entry.pc()) {
// The JIT only allows the top-most entry to have a nullptr pc.
MOZ_ASSERT(&entry == &aRacyInfo->entries[aRacyInfo->stackSize() - 1]);
@ -706,14 +697,6 @@ AddPseudoEntry(uint32_t aFeatures, NotNull<RacyThreadInfo*> aRacyInfo,
} else {
lineno = entry.line();
}
// Adjust the dynamic string as necessary.
if (ProfilerFeature::HasPrivacy(aFeatures) && !isChromeJSEntry) {
dynamicString = "(private)";
} else if (strlen(dynamicString) >= ProfileBuffer::kMaxFrameKeyLength) {
dynamicString = "(too long)";
}
} else {
// XXX: Bug 1010578. Don't assume a CPP entry and try to get the line for
// js entries as well.
@ -722,8 +705,11 @@ AddPseudoEntry(uint32_t aFeatures, NotNull<RacyThreadInfo*> aRacyInfo,
}
}
aCollector.CollectCodeLocation(entry.label(), dynamicString, lineno,
Some(entry.category()));
if (lineno != -1) {
aBuffer.AddEntry(ProfileBufferEntry::LineNumber(lineno));
}
aBuffer.AddEntry(ProfileBufferEntry::Category(int(entry.category())));
}
// Setting MAX_NATIVE_FRAMES too high risks the unwinder wasting a lot of time
@ -761,17 +747,12 @@ struct AutoWalkJSStack
}
};
// Merges the pseudo-stack, native stack, and JS stack, outputting the details
// to aCollector.
static void
MergeStacks(uint32_t aFeatures, bool aIsSynchronous,
const ThreadInfo& aThreadInfo, const Registers& aRegs,
const NativeStack& aNativeStack,
ProfilerStackCollector& aCollector)
MergeStacksIntoProfile(PSLockRef aLock, bool aIsSynchronous,
const ThreadInfo& aThreadInfo, const Registers& aRegs,
const NativeStack& aNativeStack, ProfileBuffer& aBuffer)
{
// WARNING: this function runs within the profiler's "critical section".
// WARNING: this function might be called while the profiler is inactive, and
// cannot rely on ActivePS.
NotNull<RacyThreadInfo*> racyInfo = aThreadInfo.RacyInfo();
js::ProfileEntry* pseudoEntries = racyInfo->entries;
@ -786,10 +767,10 @@ MergeStacks(uint32_t aFeatures, bool aIsSynchronous,
// ProfilingFrameIterator to avoid incorrectly resetting the generation of
// sampled JIT entries inside the JS engine. See note below concerning 'J'
// entries.
uint32_t startBufferGen = UINT32_MAX;
if (!aIsSynchronous && aCollector.Generation().isSome()) {
startBufferGen = *aCollector.Generation();
}
uint32_t startBufferGen;
startBufferGen = aIsSynchronous
? UINT32_MAX
: aBuffer.mGeneration;
uint32_t jsCount = 0;
JS::ProfilingFrameIterator::Frame jsFrames[MAX_JS_FRAMES];
@ -901,7 +882,7 @@ MergeStacks(uint32_t aFeatures, bool aIsSynchronous,
// Pseudo-frames with the CPP_MARKER_FOR_JS kind are just annotations and
// should not be recorded in the profile.
if (pseudoEntry.kind() != js::ProfileEntry::Kind::CPP_MARKER_FOR_JS) {
AddPseudoEntry(aFeatures, racyInfo, pseudoEntry, aCollector);
AddPseudoEntry(aLock, racyInfo, pseudoEntry, aBuffer);
}
pseudoIndex++;
continue;
@ -927,11 +908,13 @@ MergeStacks(uint32_t aFeatures, bool aIsSynchronous,
// with stale JIT code return addresses.
if (aIsSynchronous ||
jsFrame.kind == JS::ProfilingFrameIterator::Frame_Wasm) {
aCollector.CollectCodeLocation("", jsFrame.label, -1, Nothing());
aBuffer.AddEntry(ProfileBufferEntry::Label(""));
aBuffer.AddDynamicStringEntry(jsFrame.label);
} else {
MOZ_ASSERT(jsFrame.kind == JS::ProfilingFrameIterator::Frame_Ion ||
jsFrame.kind == JS::ProfilingFrameIterator::Frame_Baseline);
aCollector.CollectJitReturnAddr(jsFrames[jsIndex].returnAddress);
aBuffer.AddEntry(
ProfileBufferEntry::JitReturnAddr(jsFrames[jsIndex].returnAddress));
}
jsIndex--;
@ -943,7 +926,7 @@ MergeStacks(uint32_t aFeatures, bool aIsSynchronous,
if (nativeStackAddr) {
MOZ_ASSERT(nativeIndex >= 0);
void* addr = (void*)aNativeStack.mPCs[nativeIndex];
aCollector.CollectNativeLeafAddr(addr);
aBuffer.AddEntry(ProfileBufferEntry::NativeLeafAddr(addr));
}
if (nativeIndex >= 0) {
nativeIndex--;
@ -954,11 +937,10 @@ MergeStacks(uint32_t aFeatures, bool aIsSynchronous,
//
// Do not do this for synchronous samples, which use their own
// ProfileBuffers instead of the global one in CorePS.
if (!aIsSynchronous && context && aCollector.Generation().isSome()) {
MOZ_ASSERT(*aCollector.Generation() >= startBufferGen);
uint32_t lapCount = *aCollector.Generation() - startBufferGen;
JS::UpdateJSContextProfilerSampleBufferGen(context,
*aCollector.Generation(),
if (!aIsSynchronous && context) {
MOZ_ASSERT(aBuffer.mGeneration >= startBufferGen);
uint32_t lapCount = aBuffer.mGeneration - startBufferGen;
JS::UpdateJSContextProfilerSampleBufferGen(context, aBuffer.mGeneration,
lapCount);
}
}
@ -983,8 +965,6 @@ DoNativeBacktrace(PSLockRef aLock, const ThreadInfo& aThreadInfo,
const Registers& aRegs, NativeStack& aNativeStack)
{
// WARNING: this function runs within the profiler's "critical section".
// WARNING: this function might be called while the profiler is inactive, and
// cannot rely on ActivePS.
// Start with the current function. We use 0 as the frame number here because
// the FramePointerStackWalk() and MozStackWalk() calls below will use 1..N.
@ -1018,8 +998,6 @@ DoNativeBacktrace(PSLockRef aLock, const ThreadInfo& aThreadInfo,
const Registers& aRegs, NativeStack& aNativeStack)
{
// WARNING: this function runs within the profiler's "critical section".
// WARNING: this function might be called while the profiler is inactive, and
// cannot rely on ActivePS.
const mcontext_t* mcontext = &aRegs.mContext->uc_mcontext;
mcontext_t savedContext;
@ -1099,8 +1077,6 @@ DoNativeBacktrace(PSLockRef aLock, const ThreadInfo& aThreadInfo,
const Registers& aRegs, NativeStack& aNativeStack)
{
// WARNING: this function runs within the profiler's "critical section".
// WARNING: this function might be called while the profiler is inactive, and
// cannot rely on ActivePS.
const mcontext_t* mc = &aRegs.mContext->uc_mcontext;
@ -1246,13 +1222,13 @@ DoSharedSample(PSLockRef aLock, bool aIsSynchronous,
if (ActivePS::FeatureStackWalk(aLock)) {
DoNativeBacktrace(aLock, aThreadInfo, aRegs, nativeStack);
MergeStacks(ActivePS::Features(aLock), aIsSynchronous, aThreadInfo, aRegs,
nativeStack, aBuffer);
MergeStacksIntoProfile(aLock, aIsSynchronous, aThreadInfo, aRegs,
nativeStack, aBuffer);
} else
#endif
{
MergeStacks(ActivePS::Features(aLock), aIsSynchronous, aThreadInfo, aRegs,
nativeStack, aBuffer);
MergeStacksIntoProfile(aLock, aIsSynchronous, aThreadInfo, aRegs,
nativeStack, aBuffer);
if (ActivePS::FeatureLeaf(aLock)) {
aBuffer.AddEntry(ProfileBufferEntry::NativeLeafAddr((void*)aRegs.mPC));
@ -3070,62 +3046,5 @@ profiler_suspend_and_sample_thread(
}
}
// NOTE: aCollector's methods will be called while the target thread is paused.
// Doing things in those methods like allocating -- which may try to claim
// locks -- is a surefire way to deadlock.
void
profiler_suspend_and_sample_thread(int aThreadId,
uint32_t aFeatures,
ProfilerStackCollector& aCollector,
bool aSampleNative /* = true */)
{
// Lock the profiler mutex
PSAutoLock lock(gPSMutex);
const CorePS::ThreadVector& liveThreads = CorePS::LiveThreads(lock);
for (uint32_t i = 0; i < liveThreads.size(); i++) {
ThreadInfo* info = liveThreads.at(i);
if (info->ThreadId() == aThreadId) {
if (info->IsMainThread()) {
aCollector.SetIsMainThread();
}
// Allocate the space for the native stack
NativeStack nativeStack;
// Suspend, sample, and then resume the target thread.
Sampler sampler(lock);
sampler.SuspendAndSampleAndResumeThread(lock, *info,
[&](const Registers& aRegs) {
// The target thread is now suspended. Collect a native backtrace, and
// call the callback.
bool isSynchronous = false;
#if defined(HAVE_NATIVE_UNWIND)
if (aSampleNative) {
DoNativeBacktrace(lock, *info, aRegs, nativeStack);
MergeStacks(aFeatures, isSynchronous, *info, aRegs, nativeStack,
aCollector);
} else
#endif
{
MergeStacks(aFeatures, isSynchronous, *info, aRegs, nativeStack,
aCollector);
if (ProfilerFeature::HasLeaf(aFeatures)) {
aCollector.CollectNativeLeafAddr((void*)aRegs.mPC);
}
}
});
// NOTE: Make sure to disable the sampler before it is destroyed, in case
// the profiler is running at the same time.
sampler.Disable(lock);
break;
}
}
}
// END externally visible functions
////////////////////////////////////////////////////////////////////////

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

@ -81,7 +81,6 @@ if CONFIG['MOZ_GECKO_PROFILER']:
]
LOCAL_INCLUDES += [
'/caps',
'/docshell/base',
'/ipc/chromium/src',
'/mozglue/linker',

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

@ -25,7 +25,6 @@
#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include "mozilla/GuardObjects.h"
#include "mozilla/Maybe.h"
#include "mozilla/Sprintf.h"
#include "mozilla/ThreadLocal.h"
#include "mozilla/UniquePtr.h"
@ -268,52 +267,11 @@ typedef void ProfilerStackCallback(void** aPCs, size_t aCount, bool aIsMainThrea
// WARNING: The target thread is suspended during the callback. Do not try to
// allocate or acquire any locks, or you could deadlock. The target thread will
// have resumed by the time this function returns.
//
// XXX: this function is in the process of being replaced with the other profiler_suspend_and_sample_thread() function.
PROFILER_FUNC_VOID(
profiler_suspend_and_sample_thread(int aThreadId,
const std::function<ProfilerStackCallback>& aCallback,
bool aSampleNative = true))
// An object of this class is passed to profiler_suspend_and_sample_thread().
// For each stack frame, one of the Collect methods will be called.
class ProfilerStackCollector
{
public:
// Some collectors need to worry about possibly overwriting previous
// generations of data. If that's not an issue, this can return Nothing,
// which is the default behaviour.
virtual mozilla::Maybe<uint32_t> Generation() { return mozilla::Nothing(); }
// This method will be called once if the thread being suspended is the main
// thread. Default behaviour is to do nothing.
virtual void SetIsMainThread() {}
// WARNING: The target thread is suspended when the Collect methods are
// called. Do not try to allocate or acquire any locks, or you could
// deadlock. The target thread will have resumed by the time this function
// returns.
virtual void CollectNativeLeafAddr(void* aAddr) = 0;
virtual void CollectJitReturnAddr(void* aAddr) = 0;
// aLabel is static and never null. aStr may be null. aLineNumber may be -1.
virtual void CollectCodeLocation(
const char* aLabel, const char* aStr, int aLineNumber,
const mozilla::Maybe<js::ProfileEntry::Category>& aCategory) = 0;
};
// This method suspends the thread identified by aThreadId, samples its
// pseudo-stack, JS stack, and (optionally) native stack, passing the collected
// frames into aCollector. aFeatures dictates which compiler features are used.
// |Privacy| and |Leaf| are the only relevant ones.
PROFILER_FUNC_VOID(
profiler_suspend_and_sample_thread(int aThreadId,
uint32_t aFeatures,
ProfilerStackCollector& aCollector,
bool aSampleNative = true))
struct ProfilerBacktraceDestructor
{
#ifdef MOZ_GECKO_PROFILER

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

@ -664,70 +664,3 @@ TEST(GeckoProfiler, Bug1355807)
profiler_stop();
}
class GTestStackCollector final : public ProfilerStackCollector
{
public:
GTestStackCollector()
: mSetIsMainThread(0)
, mFrames(0)
{}
virtual void SetIsMainThread() { mSetIsMainThread++; }
virtual void CollectNativeLeafAddr(void* aAddr) { mFrames++; }
virtual void CollectJitReturnAddr(void* aAddr) { mFrames++; }
virtual void CollectCodeLocation(
const char* aLabel, const char* aStr, int aLineNumber,
const mozilla::Maybe<js::ProfileEntry::Category>& aCategory) { mFrames++; }
int mSetIsMainThread;
int mFrames;
};
void DoSuspendAndSample(int aTid, nsIThread* aThread)
{
aThread->Dispatch(
NS_NewRunnableFunction(
"GeckoProfiler_SuspendAndSample_Test::TestBody",
[&]() {
uint32_t features = ProfilerFeature::Leaf;
GTestStackCollector collector;
profiler_suspend_and_sample_thread(aTid, features, collector,
/* sampleNative = */ true);
ASSERT_TRUE(collector.mSetIsMainThread == 1);
ASSERT_TRUE(collector.mFrames > 5); // approximate; must be > 0
}),
NS_DISPATCH_SYNC);
}
TEST(GeckoProfiler, SuspendAndSample)
{
nsCOMPtr<nsIThread> thread;
nsresult rv = NS_NewNamedThread("GeckoProfGTest", getter_AddRefs(thread));
ASSERT_TRUE(NS_SUCCEEDED(rv));
int tid = Thread::GetCurrentId();
ASSERT_TRUE(!profiler_is_active());
// Suspend and sample while the profiler is inactive.
DoSuspendAndSample(tid, thread);
uint32_t features = ProfilerFeature::JS | ProfilerFeature::Threads;
const char* filters[] = { "GeckoMain", "Compositor" };
profiler_start(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
features, filters, MOZ_ARRAY_LENGTH(filters));
ASSERT_TRUE(profiler_is_active());
// Suspend and sample while the profiler is active.
DoSuspendAndSample(tid, thread);
profiler_stop();
ASSERT_TRUE(!profiler_is_active());
}