Bug 1023461 - Add HangStack class to support internal string buffer; r=vladan

This commit is contained in:
Jim Chen 2014-06-20 14:28:10 -04:00
parent 27424ad9cc
commit 798fef8dc2
4 changed files with 123 additions and 15 deletions

View File

@ -2066,7 +2066,7 @@ CreateJSHangHistogram(JSContext* cx, const Telemetry::HangHistogram& hang)
return nullptr;
}
const Telemetry::HangHistogram::Stack& hangStack = hang.GetStack();
const Telemetry::HangStack& hangStack = hang.GetStack();
JS::RootedObject stack(cx,
JS_NewArrayObject(cx, hangStack.length()));
if (!ret) {
@ -3027,15 +3027,63 @@ TimeHistogram::Add(PRIntervalTime aTime)
operator[](index)++;
}
const char*
HangStack::InfallibleAppendViaBuffer(const char* aText, size_t aLength)
{
MOZ_ASSERT(this->canAppendWithoutRealloc(1));
// Include null-terminator in length count.
MOZ_ASSERT(mBuffer.canAppendWithoutRealloc(aLength + 1));
const char* const entry = mBuffer.end();
mBuffer.infallibleAppend(aText, aLength);
mBuffer.infallibleAppend('\0'); // Explicitly append null-terminator
this->infallibleAppend(entry);
return entry;
}
const char*
HangStack::AppendViaBuffer(const char* aText, size_t aLength)
{
if (!this->reserve(this->length() + 1)) {
return nullptr;
}
// Keep track of the previous buffer in case we need to adjust pointers later.
const char* const prevStart = mBuffer.begin();
const char* const prevEnd = mBuffer.end();
// Include null-terminator in length count.
if (!mBuffer.reserve(mBuffer.length() + aLength + 1)) {
return nullptr;
}
if (prevStart != mBuffer.begin()) {
// The buffer has moved; we have to adjust pointers in the stack.
for (const char** entry = this->begin(); entry != this->end(); entry++) {
if (*entry >= prevStart && *entry < prevEnd) {
// Move from old buffer to new buffer.
*entry += mBuffer.begin() - prevStart;
}
}
}
return InfallibleAppendViaBuffer(aText, aLength);
}
uint32_t
HangHistogram::GetHash(const Stack& aStack)
HangHistogram::GetHash(const HangStack& aStack)
{
uint32_t hash = 0;
for (const char* const* label = aStack.begin();
label != aStack.end(); label++) {
/* We only need to hash the pointer instead of the text content
because we are assuming constant pointers */
hash = AddToHash(hash, *label);
/* If the string is within our buffer, we need to hash its content.
Otherwise, the string is statically allocated, and we only need
to hash the pointer instead of the content. */
if (aStack.IsInBuffer(*label)) {
hash = AddToHash(hash, HashString(*label));
} else {
hash = AddToHash(hash, *label);
}
}
return hash;
}
@ -3049,7 +3097,7 @@ HangHistogram::operator==(const HangHistogram& aOther) const
if (mStack.length() != aOther.mStack.length()) {
return false;
}
return PodEqual(mStack.begin(), aOther.mStack.begin(), mStack.length());
return mStack == aOther.mStack;
}

View File

@ -44,22 +44,82 @@ public:
void Add(PRIntervalTime aTime);
};
/* HangStack stores an array of const char pointers,
with optional internal storage for strings. */
class HangStack : public mozilla::Vector<const char*, 8>
{
private:
typedef mozilla::Vector<const char*, 8> Base;
// Stack entries can either be a static const char*
// or a pointer to within this buffer.
mozilla::Vector<char, 0> mBuffer;
public:
HangStack() { }
HangStack(HangStack&& aOther)
: Base(mozilla::Move(aOther))
, mBuffer(mozilla::Move(aOther.mBuffer))
{
}
bool operator==(const HangStack& aOther) const {
for (size_t i = 0; i < length(); i++) {
if (!IsSameAsEntry(operator[](i), aOther[i])) {
return false;
}
}
return true;
}
bool operator!=(const HangStack& aOther) const {
return !operator==(aOther);
}
void clear() {
Base::clear();
mBuffer.clear();
}
bool IsInBuffer(const char* aEntry) const {
return aEntry >= mBuffer.begin() && aEntry < mBuffer.end();
}
bool IsSameAsEntry(const char* aEntry, const char* aOther) const {
// If the entry came from the buffer, we need to compare its content;
// otherwise we only need to compare its pointer.
return IsInBuffer(aEntry) ? !strcmp(aEntry, aOther) : (aEntry == aOther);
}
size_t AvailableBufferSize() const {
return mBuffer.capacity() - mBuffer.length();
}
bool EnsureBufferCapacity(size_t aCapacity) {
// aCapacity is the minimal capacity and Vector may make the actual
// capacity larger, in which case we want to use up all the space.
return mBuffer.reserve(aCapacity) &&
mBuffer.reserve(mBuffer.capacity());
}
const char* InfallibleAppendViaBuffer(const char* aText, size_t aLength);
const char* AppendViaBuffer(const char* aText, size_t aLength);
};
/* A hang histogram consists of a stack associated with the
hang, along with a time histogram of the hang times. */
class HangHistogram : public TimeHistogram
{
public:
typedef mozilla::Vector<const char*, 8> Stack;
private:
static uint32_t GetHash(const Stack& aStack);
static uint32_t GetHash(const HangStack& aStack);
Stack mStack;
HangStack mStack;
// Use a hash to speed comparisons
const uint32_t mHash;
public:
explicit HangHistogram(Stack&& aStack)
explicit HangHistogram(HangStack&& aStack)
: mStack(mozilla::Move(aStack))
, mHash(GetHash(mStack))
{
@ -75,7 +135,7 @@ public:
{
return !operator==(aOther);
}
const Stack& GetStack() const {
const HangStack& GetStack() const {
return mStack;
}
};

View File

@ -137,7 +137,7 @@ public:
// Platform-specific helper to get hang stacks
ThreadStackHelper mStackHelper;
// Stack of current hang
Telemetry::HangHistogram::Stack mHangStack;
Telemetry::HangStack mHangStack;
// Statistics for telemetry
Telemetry::ThreadHangStats mStats;

View File

@ -37,7 +37,7 @@ namespace mozilla {
class ThreadStackHelper
{
public:
typedef Telemetry::HangHistogram::Stack Stack;
typedef Telemetry::HangStack Stack;
private:
#ifdef MOZ_ENABLE_PROFILER_SPS