Bug 1194424 - Part 1: Serialize JS::ubi::StackFrame allocation stacks into core dumps; r=sfink

This commit is contained in:
Nick Fitzgerald 2015-08-24 09:29:44 -07:00
Родитель b900df940b
Коммит 9d1492a070
1 изменённых файлов: 84 добавлений и 2 удалений

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

@ -19,6 +19,7 @@
#include "mozilla/devtools/ZeroCopyNSIOutputStream.h"
#include "mozilla/dom/ChromeUtils.h"
#include "mozilla/dom/HeapSnapshotBinding.h"
#include "mozilla/RangedPtr.h"
#include "mozilla/Telemetry.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/UniquePtr.h"
@ -381,8 +382,13 @@ EstablishBoundaries(JSContext* cx,
// given `ZeroCopyOutputStream`.
class MOZ_STACK_CLASS StreamWriter : public CoreDumpWriter
{
using Set = js::HashSet<uint64_t>;
JSContext* cx;
bool wantNames;
bool wantNames;
// The set of |JS::ubi::StackFrame::identifier()|s that have already been
// serialized and written to the core dump.
Set framesAlreadySerialized;
::google::protobuf::io::ZeroCopyOutputStream& stream;
@ -396,15 +402,79 @@ class MOZ_STACK_CLASS StreamWriter : public CoreDumpWriter
return !codedStream.HadError();
}
protobuf::StackFrame* getProtobufStackFrame(JS::ubi::StackFrame& frame) {
MOZ_ASSERT(frame,
"null frames should be represented as the lack of a serialized "
"stack frame");
auto id = frame.identifier();
auto protobufStackFrame = MakeUnique<protobuf::StackFrame>();
if (!protobufStackFrame)
return nullptr;
if (framesAlreadySerialized.has(id)) {
protobufStackFrame->set_ref(id);
return protobufStackFrame.release();
}
auto data = MakeUnique<protobuf::StackFrame_Data>();
if (!data)
return nullptr;
data->set_id(id);
data->set_line(frame.line());
data->set_column(frame.column());
data->set_issystem(frame.isSystem());
data->set_isselfhosted(frame.isSelfHosted());
auto source = MakeUnique<std::string>(frame.sourceLength() * sizeof(char16_t),
'\0');
if (!source)
return nullptr;
auto buf = const_cast<char16_t*>(reinterpret_cast<const char16_t*>(source->data()));
frame.source(RangedPtr<char16_t>(buf, frame.sourceLength()),
frame.sourceLength());
data->set_allocated_source(source.release());
auto nameLength = frame.functionDisplayNameLength();
if (nameLength > 0) {
auto functionDisplayName = MakeUnique<std::string>(nameLength * sizeof(char16_t),
'\0');
if (!functionDisplayName)
return nullptr;
auto buf = const_cast<char16_t*>(reinterpret_cast<const char16_t*>(functionDisplayName->data()));
frame.functionDisplayName(RangedPtr<char16_t>(buf, nameLength), nameLength);
data->set_allocated_functiondisplayname(functionDisplayName.release());
}
auto parent = frame.parent();
if (parent) {
auto protobufParent = getProtobufStackFrame(parent);
if (!protobufParent)
return nullptr;
data->set_allocated_parent(protobufParent);
}
protobufStackFrame->set_allocated_data(data.release());
if (!framesAlreadySerialized.put(id))
return nullptr;
return protobufStackFrame.release();
}
public:
StreamWriter(JSContext* cx,
::google::protobuf::io::ZeroCopyOutputStream& stream,
bool wantNames)
: cx(cx)
, wantNames(wantNames)
, framesAlreadySerialized(cx)
, stream(stream)
{ }
bool init() { return framesAlreadySerialized.init(); }
~StreamWriter() override { }
virtual bool writeMetadata(uint64_t timestamp) override {
@ -414,7 +484,7 @@ public:
}
virtual bool writeNode(const JS::ubi::Node& ubiNode,
EdgePolicy includeEdges) override {
EdgePolicy includeEdges) final {
protobuf::Node protobufNode;
protobufNode.set_id(ubiNode.identifier());
@ -427,6 +497,14 @@ public:
MOZ_ASSERT(mallocSizeOf);
protobufNode.set_size(ubiNode.size(mallocSizeOf));
if (ubiNode.hasAllocationStack()) {
auto ubiStackFrame = ubiNode.allocationStack();
auto protoStackFrame = getProtobufStackFrame(ubiStackFrame);
if (NS_WARN_IF(!protoStackFrame))
return false;
protobufNode.set_allocated_allocationstack(protoStackFrame);
}
if (includeEdges) {
auto edges = ubiNode.edges(cx, wantNames);
if (NS_WARN_IF(!edges))
@ -592,6 +670,10 @@ ThreadSafeChromeUtils::SaveHeapSnapshot(GlobalObject& global,
::google::protobuf::io::GzipOutputStream gzipStream(&zeroCopyStream);
StreamWriter writer(cx, gzipStream, wantNames);
if (NS_WARN_IF(!writer.init())) {
rv.Throw(NS_ERROR_OUT_OF_MEMORY);
return;
}
{
Maybe<AutoCheckCannotGC> maybeNoGC;