mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1024774 - Part 3: Serialize heap snapshots. r=jimb
This commit is contained in:
parent
730f75784f
commit
967aa5947f
@ -262,7 +262,13 @@ class BuilderOrigin : public Builder {
|
||||
|
||||
// Tell Debuggers in |runtime| to use |mallocSizeOf| to find the size of
|
||||
// malloc'd blocks.
|
||||
void SetDebuggerMallocSizeOf(JSRuntime* runtime, mozilla::MallocSizeOf mallocSizeOf);
|
||||
JS_PUBLIC_API(void)
|
||||
SetDebuggerMallocSizeOf(JSRuntime* runtime, mozilla::MallocSizeOf mallocSizeOf);
|
||||
|
||||
// Get the MallocSizeOf function that the given runtime is using to find the
|
||||
// size of malloc'd blocks.
|
||||
JS_PUBLIC_API(mozilla::MallocSizeOf)
|
||||
GetDebuggerMallocSizeOf(JSRuntime* runtime);
|
||||
|
||||
|
||||
|
||||
@ -316,7 +322,12 @@ onPromiseSettled(JSContext* cx, HandleObject promise);
|
||||
|
||||
// Return true if the given value is a Debugger object, false otherwise.
|
||||
JS_PUBLIC_API(bool)
|
||||
IsDebugger(JS::Value val);
|
||||
IsDebugger(const JSObject &obj);
|
||||
|
||||
// Append each of the debuggee global objects observed by the Debugger object
|
||||
// |dbgObj| to |vector|. Returns true on success, false on failure.
|
||||
JS_PUBLIC_API(bool)
|
||||
GetDebuggeeGlobals(JSContext *cx, const JSObject &dbgObj, AutoObjectVector &vector);
|
||||
|
||||
|
||||
// Hooks for reporting where JavaScript execution began.
|
||||
|
@ -266,8 +266,8 @@ struct Concrete {
|
||||
static void construct(void* storage, Referent* referent);
|
||||
};
|
||||
|
||||
// A container for a Base instance; all members simply forward to the contained instance.
|
||||
// This container allows us to pass ubi::Node instances by value.
|
||||
// A container for a Base instance; all members simply forward to the contained
|
||||
// instance. This container allows us to pass ubi::Node instances by value.
|
||||
class Node {
|
||||
// Storage in which we allocate Base subclasses.
|
||||
mozilla::AlignedStorage2<Base> storage;
|
||||
@ -376,6 +376,21 @@ class Node {
|
||||
return base()->edges(cx, wantNames);
|
||||
}
|
||||
|
||||
// An identifier for this node, guaranteed to be stable and unique for as
|
||||
// long as this ubi::Node's referent is alive and at the same address.
|
||||
//
|
||||
// This is probably suitable for use in serializations, as it is an integral
|
||||
// type. It may also help save memory when constructing HashSets of
|
||||
// ubi::Nodes: since a uintptr_t will always be smaller than a ubi::Node, a
|
||||
// HashSet<ubi::Node::Id> will use less space per element than a
|
||||
// HashSet<ubi::Node>.
|
||||
//
|
||||
// (Note that 'unique' only means 'up to equality on ubi::Node'; see the
|
||||
// caveats about multiple objects allocated at the same address for
|
||||
// 'ubi::Node::operator=='.)
|
||||
typedef uintptr_t Id;
|
||||
Id identifier() const { return reinterpret_cast<Id>(base()->ptr); }
|
||||
|
||||
// A hash policy for ubi::Nodes.
|
||||
// This simply uses the stock PointerHasher on the ubi::Node's pointer.
|
||||
// We specialize DefaultHasher below to make this the default.
|
||||
@ -495,6 +510,33 @@ class SimpleEdge : public Edge {
|
||||
|
||||
typedef mozilla::Vector<SimpleEdge, 8, js::TempAllocPolicy> SimpleEdgeVector;
|
||||
|
||||
// An EdgeRange concrete class that holds a pre-existing vector of
|
||||
// SimpleEdges. A PreComputedEdgeRange does not take ownership of its
|
||||
// SimpleEdgeVector; it is up to the PreComputedEdgeRange's consumer to manage
|
||||
// that lifetime.
|
||||
class PreComputedEdgeRange : public EdgeRange {
|
||||
SimpleEdgeVector& edges;
|
||||
size_t i;
|
||||
|
||||
void settle() {
|
||||
front_ = i < edges.length() ? &edges[i] : nullptr;
|
||||
}
|
||||
|
||||
public:
|
||||
explicit PreComputedEdgeRange(JSContext* cx, SimpleEdgeVector& edges)
|
||||
: edges(edges),
|
||||
i(0)
|
||||
{
|
||||
settle();
|
||||
}
|
||||
|
||||
void popFront() override {
|
||||
MOZ_ASSERT(!empty());
|
||||
i++;
|
||||
settle();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// RootList is a class that can be pointed to by a |ubi::Node|, creating a
|
||||
// fictional root-of-roots which has edges to every GC root in the JS
|
||||
@ -541,6 +583,10 @@ class MOZ_STACK_CLASS RootList {
|
||||
// Find only GC roots in the given Debugger object's set of debuggee zones.
|
||||
bool init(HandleObject debuggees);
|
||||
|
||||
// Returns true if the RootList has been initialized successfully, false
|
||||
// otherwise.
|
||||
bool initialized() { return noGC.isSome(); }
|
||||
|
||||
// Explicitly add the given Node as a root in this RootList. If wantNames is
|
||||
// true, you must pass an edgeName. The RootList does not take ownership of
|
||||
// edgeName.
|
||||
|
@ -120,7 +120,7 @@ struct BreadthFirst {
|
||||
MOZ_ASSERT(!traversalBegun);
|
||||
traversalBegun = true;
|
||||
|
||||
// While there are pending nodes, visit them, until we've found a path to the target.
|
||||
// While there are pending nodes, visit them.
|
||||
while (!pending.empty()) {
|
||||
Node origin = pending.front();
|
||||
pending.popFront();
|
||||
|
@ -302,6 +302,12 @@ js::IsAtomsCompartment(JSCompartment* comp)
|
||||
return comp->runtimeFromAnyThread()->isAtomsCompartment(comp);
|
||||
}
|
||||
|
||||
JS_FRIEND_API(bool)
|
||||
js::IsAtomsZone(JS::Zone* zone)
|
||||
{
|
||||
return zone->runtimeFromAnyThread()->isAtomsZone(zone);
|
||||
}
|
||||
|
||||
JS_FRIEND_API(bool)
|
||||
js::IsInNonStrictPropertySet(JSContext* cx)
|
||||
{
|
||||
|
@ -478,6 +478,9 @@ IsSystemZone(JS::Zone* zone);
|
||||
extern JS_FRIEND_API(bool)
|
||||
IsAtomsCompartment(JSCompartment* comp);
|
||||
|
||||
extern JS_FRIEND_API(bool)
|
||||
IsAtomsZone(JS::Zone *zone);
|
||||
|
||||
/*
|
||||
* Returns whether we're in a non-strict property set (in that we're in a
|
||||
* non-strict script and the bytecode we're on is a property set). The return
|
||||
|
@ -27,7 +27,7 @@ js::Debugger::onLeaveFrame(JSContext* cx, AbstractFramePtr frame, bool ok)
|
||||
}
|
||||
|
||||
/* static */ inline js::Debugger*
|
||||
js::Debugger::fromJSObject(JSObject* obj)
|
||||
js::Debugger::fromJSObject(const JSObject* obj)
|
||||
{
|
||||
MOZ_ASSERT(js::GetObjectClass(obj) == &jsclass);
|
||||
return (Debugger*) obj->as<NativeObject>().getPrivate();
|
||||
|
@ -7915,16 +7915,27 @@ JS::dbg::onPromiseSettled(JSContext* cx, HandleObject promise)
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS::dbg::IsDebugger(JS::Value val)
|
||||
JS::dbg::IsDebugger(const JSObject &obj)
|
||||
{
|
||||
if (!val.isObject())
|
||||
return false;
|
||||
return js::GetObjectClass(&obj) == &Debugger::jsclass &&
|
||||
js::Debugger::fromJSObject(&obj) != nullptr;
|
||||
}
|
||||
|
||||
JSObject& obj = val.toObject();
|
||||
if (obj.getClass() != &Debugger::jsclass)
|
||||
return false;
|
||||
JS_PUBLIC_API(bool)
|
||||
JS::dbg::GetDebuggeeGlobals(JSContext *cx, const JSObject &dbgObj, AutoObjectVector &vector)
|
||||
{
|
||||
MOZ_ASSERT(IsDebugger(dbgObj));
|
||||
js::Debugger *dbg = js::Debugger::fromJSObject(&dbgObj);
|
||||
|
||||
return js::Debugger::fromJSObject(&obj) != nullptr;
|
||||
if (!vector.reserve(vector.length() + dbg->debuggees.count())) {
|
||||
JS_ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (WeakGlobalObjectSet::Range r = dbg->allDebuggees(); !r.empty(); r.popFront())
|
||||
vector.infallibleAppend(static_cast<JSObject*>(r.front()));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
@ -188,7 +188,8 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
|
||||
friend class SavedStacks;
|
||||
friend class mozilla::LinkedListElement<Debugger>;
|
||||
friend bool (::JS_DefineDebuggerObject)(JSContext* cx, JS::HandleObject obj);
|
||||
friend bool (::JS::dbg::IsDebugger)(JS::Value val);
|
||||
friend bool (::JS::dbg::IsDebugger)(const JSObject&);
|
||||
friend bool (::JS::dbg::GetDebuggeeGlobals)(JSContext*, const JSObject&, AutoObjectVector&);
|
||||
friend void JS::dbg::onNewPromise(JSContext* cx, HandleObject promise);
|
||||
friend void JS::dbg::onPromiseSettled(JSContext* cx, HandleObject promise);
|
||||
friend bool JS::dbg::FireOnGarbageCollectionHook(JSContext* cx,
|
||||
@ -601,7 +602,7 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
|
||||
bool init(JSContext* cx);
|
||||
inline const js::HeapPtrNativeObject& toJSObject() const;
|
||||
inline js::HeapPtrNativeObject& toJSObjectRef();
|
||||
static inline Debugger* fromJSObject(JSObject* obj);
|
||||
static inline Debugger* fromJSObject(const JSObject* obj);
|
||||
static Debugger* fromChildJSObject(JSObject* obj);
|
||||
|
||||
bool hasMemory() const;
|
||||
|
@ -325,11 +325,18 @@ DebuggerMemory::setOnGarbageCollection(JSContext* cx, unsigned argc, Value* vp)
|
||||
|
||||
/* Debugger.Memory.prototype.takeCensus */
|
||||
|
||||
void
|
||||
JS::dbg::SetDebuggerMallocSizeOf(JSRuntime* rt, mozilla::MallocSizeOf mallocSizeOf) {
|
||||
JS_PUBLIC_API(void)
|
||||
JS::dbg::SetDebuggerMallocSizeOf(JSRuntime* rt, mozilla::MallocSizeOf mallocSizeOf)
|
||||
{
|
||||
rt->debuggerMallocSizeOf = mallocSizeOf;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(mozilla::MallocSizeOf)
|
||||
JS::dbg::GetDebuggerMallocSizeOf(JSRuntime *rt)
|
||||
{
|
||||
return rt->debuggerMallocSizeOf;
|
||||
}
|
||||
|
||||
namespace js {
|
||||
namespace dbg {
|
||||
|
||||
|
@ -328,8 +328,8 @@ RootList::init(ZoneSet& debuggees)
|
||||
bool
|
||||
RootList::init(HandleObject debuggees)
|
||||
{
|
||||
MOZ_ASSERT(debuggees && JS::dbg::IsDebugger(ObjectValue(*debuggees)));
|
||||
js::Debugger* dbg = js::Debugger::fromJSObject(debuggees);
|
||||
MOZ_ASSERT(debuggees && JS::dbg::IsDebugger(*debuggees));
|
||||
js::Debugger* dbg = js::Debugger::fromJSObject(debuggees.get());
|
||||
|
||||
ZoneSet debuggeeZones;
|
||||
if (!debuggeeZones.init())
|
||||
@ -371,26 +371,6 @@ RootList::addRoot(Node node, const char16_t* edgeName)
|
||||
return edges.append(mozilla::Move(SimpleEdge(name.release(), node)));
|
||||
}
|
||||
|
||||
// An EdgeRange concrete class that holds a pre-existing vector of SimpleEdges.
|
||||
class PreComputedEdgeRange : public EdgeRange {
|
||||
SimpleEdgeVector& edges;
|
||||
size_t i;
|
||||
|
||||
void settle() {
|
||||
front_ = i < edges.length() ? &edges[i] : nullptr;
|
||||
}
|
||||
|
||||
public:
|
||||
explicit PreComputedEdgeRange(JSContext* cx, SimpleEdgeVector& edges)
|
||||
: edges(edges),
|
||||
i(0)
|
||||
{
|
||||
settle();
|
||||
}
|
||||
|
||||
void popFront() override { i++; settle(); }
|
||||
};
|
||||
|
||||
const char16_t Concrete<RootList>::concreteTypeName[] = MOZ_UTF16("RootList");
|
||||
|
||||
UniquePtr<EdgeRange>
|
||||
|
386
toolkit/devtools/server/ChromeUtils.cpp
Normal file
386
toolkit/devtools/server/ChromeUtils.cpp
Normal file
@ -0,0 +1,386 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
|
||||
/* 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/. */
|
||||
|
||||
#include "ChromeUtils.h"
|
||||
|
||||
#include <google/protobuf/io/coded_stream.h>
|
||||
#include <google/protobuf/io/gzip_stream.h>
|
||||
|
||||
#include "mozilla/devtools/HeapSnapshot.h"
|
||||
#include "mozilla/devtools/ZeroCopyNSIOutputStream.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
#include "nsCRTGlue.h"
|
||||
#include "nsIOutputStream.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "prerror.h"
|
||||
#include "prio.h"
|
||||
#include "prtypes.h"
|
||||
|
||||
#include "js/Debug.h"
|
||||
#include "js/UbiNodeTraverse.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace devtools {
|
||||
|
||||
using namespace JS;
|
||||
using namespace dom;
|
||||
|
||||
|
||||
// If we are only taking a snapshot of the heap affected by the given set of
|
||||
// globals, find the set of zones the globals are allocated within. Returns
|
||||
// false on OOM failure.
|
||||
static bool
|
||||
PopulateZonesWithGlobals(ZoneSet &zones, AutoObjectVector &globals)
|
||||
{
|
||||
if (!zones.init())
|
||||
return false;
|
||||
|
||||
unsigned length = globals.length();
|
||||
for (unsigned i = 0; i < length; i++) {
|
||||
if (!zones.put(GetTenuredGCThingZone(globals[i])))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Add the given set of globals as explicit roots in the given roots
|
||||
// list. Returns false on OOM failure.
|
||||
static bool
|
||||
AddGlobalsAsRoots(AutoObjectVector &globals, ubi::RootList &roots)
|
||||
{
|
||||
unsigned length = globals.length();
|
||||
for (unsigned i = 0; i < length; i++) {
|
||||
if (!roots.addRoot(ubi::Node(globals[i].get()),
|
||||
MOZ_UTF16("heap snapshot global")))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Choose roots and limits for a traversal, given `boundaries`. Set `roots` to
|
||||
// the set of nodes within the boundaries that are referred to by nodes
|
||||
// outside. If `boundaries` does not include all JS zones, initialize `zones` to
|
||||
// the set of included zones; otherwise, leave `zones` uninitialized. (You can
|
||||
// use zones.initialized() to check.)
|
||||
//
|
||||
// If `boundaries` is incoherent, or we encounter an error while trying to
|
||||
// handle it, or we run out of memory, set `rv` appropriately and return
|
||||
// `false`.
|
||||
static bool
|
||||
EstablishBoundaries(JSContext *cx,
|
||||
ErrorResult &rv,
|
||||
const HeapSnapshotBoundaries &boundaries,
|
||||
ubi::RootList &roots,
|
||||
ZoneSet &zones)
|
||||
{
|
||||
MOZ_ASSERT(!roots.initialized());
|
||||
MOZ_ASSERT(!zones.initialized());
|
||||
|
||||
bool foundBoundaryProperty = false;
|
||||
|
||||
if (boundaries.mRuntime.WasPassed()) {
|
||||
foundBoundaryProperty = true;
|
||||
|
||||
if (!boundaries.mRuntime.Value()) {
|
||||
rv.Throw(NS_ERROR_INVALID_ARG);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!roots.init()) {
|
||||
rv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (boundaries.mDebugger.WasPassed()) {
|
||||
if (foundBoundaryProperty) {
|
||||
rv.Throw(NS_ERROR_INVALID_ARG);
|
||||
return false;
|
||||
}
|
||||
foundBoundaryProperty = true;
|
||||
|
||||
JSObject *dbgObj = boundaries.mDebugger.Value();
|
||||
if (!dbgObj || !dbg::IsDebugger(*dbgObj)) {
|
||||
rv.Throw(NS_ERROR_INVALID_ARG);
|
||||
return false;
|
||||
}
|
||||
|
||||
AutoObjectVector globals(cx);
|
||||
if (!dbg::GetDebuggeeGlobals(cx, *dbgObj, globals) ||
|
||||
!PopulateZonesWithGlobals(zones, globals) ||
|
||||
!roots.init(zones) ||
|
||||
!AddGlobalsAsRoots(globals, roots))
|
||||
{
|
||||
rv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (boundaries.mGlobals.WasPassed()) {
|
||||
if (foundBoundaryProperty) {
|
||||
rv.Throw(NS_ERROR_INVALID_ARG);
|
||||
return false;
|
||||
}
|
||||
foundBoundaryProperty = true;
|
||||
|
||||
uint32_t length = boundaries.mGlobals.Value().Length();
|
||||
if (length == 0) {
|
||||
rv.Throw(NS_ERROR_INVALID_ARG);
|
||||
return false;
|
||||
}
|
||||
|
||||
AutoObjectVector globals(cx);
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
JSObject *global = boundaries.mGlobals.Value().ElementAt(i);
|
||||
if (!JS_IsGlobalObject(global)) {
|
||||
rv.Throw(NS_ERROR_INVALID_ARG);
|
||||
return false;
|
||||
}
|
||||
if (!globals.append(global)) {
|
||||
rv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!PopulateZonesWithGlobals(zones, globals) ||
|
||||
!roots.init(zones) ||
|
||||
!AddGlobalsAsRoots(globals, roots))
|
||||
{
|
||||
rv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundBoundaryProperty) {
|
||||
rv.Throw(NS_ERROR_INVALID_ARG);
|
||||
return false;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(roots.initialized());
|
||||
MOZ_ASSERT_IF(boundaries.mDebugger.WasPassed(), zones.initialized());
|
||||
MOZ_ASSERT_IF(boundaries.mGlobals.WasPassed(), zones.initialized());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// A `CoreDumpWriter` that serializes nodes to protobufs and writes them to
|
||||
// the given `CodedOutputStream`.
|
||||
class MOZ_STACK_CLASS StreamWriter : public CoreDumpWriter
|
||||
{
|
||||
JSContext *cx;
|
||||
bool wantNames;
|
||||
|
||||
::google::protobuf::io::ZeroCopyOutputStream &stream;
|
||||
|
||||
bool writeMessage(const ::google::protobuf::MessageLite &message) {
|
||||
// We have to create a new CodedOutputStream when writing each message so
|
||||
// that the 64MB size limit used by Coded{Output,Input}Stream to prevent
|
||||
// integer overflow is enforced per message rather than on the whole stream.
|
||||
::google::protobuf::io::CodedOutputStream codedStream(&stream);
|
||||
codedStream.WriteVarint32(message.ByteSize());
|
||||
message.SerializeWithCachedSizes(&codedStream);
|
||||
return !codedStream.HadError();
|
||||
}
|
||||
|
||||
public:
|
||||
StreamWriter(JSContext *cx,
|
||||
::google::protobuf::io::ZeroCopyOutputStream &stream,
|
||||
bool wantNames)
|
||||
: cx(cx)
|
||||
, wantNames(wantNames)
|
||||
, stream(stream)
|
||||
{ }
|
||||
|
||||
~StreamWriter() override { }
|
||||
|
||||
virtual bool writeMetadata(uint64_t timestamp) override {
|
||||
protobuf::Metadata metadata;
|
||||
metadata.set_timestamp(timestamp);
|
||||
return writeMessage(metadata);
|
||||
}
|
||||
|
||||
virtual bool writeNode(const JS::ubi::Node &ubiNode,
|
||||
EdgePolicy includeEdges) override {
|
||||
protobuf::Node protobufNode;
|
||||
protobufNode.set_id(ubiNode.identifier());
|
||||
|
||||
const char16_t *typeName = ubiNode.typeName();
|
||||
size_t length = NS_strlen(typeName) * sizeof(char16_t);
|
||||
protobufNode.set_typename_(typeName, length);
|
||||
|
||||
JSRuntime *rt = JS_GetRuntime(cx);
|
||||
mozilla::MallocSizeOf mallocSizeOf = dbg::GetDebuggerMallocSizeOf(rt);
|
||||
MOZ_ASSERT(mallocSizeOf);
|
||||
protobufNode.set_size(ubiNode.size(mallocSizeOf));
|
||||
|
||||
if (includeEdges) {
|
||||
auto edges = ubiNode.edges(cx, wantNames);
|
||||
if (NS_WARN_IF(!edges))
|
||||
return false;
|
||||
|
||||
for ( ; !edges->empty(); edges->popFront()) {
|
||||
const ubi::Edge &ubiEdge = edges->front();
|
||||
|
||||
protobuf::Edge *protobufEdge = protobufNode.add_edges();
|
||||
if (NS_WARN_IF(!protobufEdge)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
protobufEdge->set_referent(ubiEdge.referent.identifier());
|
||||
|
||||
if (wantNames && ubiEdge.name) {
|
||||
size_t length = NS_strlen(ubiEdge.name) * sizeof(char16_t);
|
||||
protobufEdge->set_name(ubiEdge.name, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return writeMessage(protobufNode);
|
||||
}
|
||||
};
|
||||
|
||||
// A JS::ubi::BreadthFirst handler that serializes a snapshot of the heap into a
|
||||
// core dump.
|
||||
class MOZ_STACK_CLASS HeapSnapshotHandler {
|
||||
CoreDumpWriter& writer;
|
||||
JS::ZoneSet* zones;
|
||||
|
||||
public:
|
||||
HeapSnapshotHandler(CoreDumpWriter& writer,
|
||||
JS::ZoneSet* zones)
|
||||
: writer(writer),
|
||||
zones(zones)
|
||||
{ }
|
||||
|
||||
// JS::ubi::BreadthFirst handler interface.
|
||||
|
||||
class NodeData { };
|
||||
typedef JS::ubi::BreadthFirst<HeapSnapshotHandler> Traversal;
|
||||
bool operator() (Traversal &traversal,
|
||||
JS::ubi::Node origin,
|
||||
const JS::ubi::Edge &edge,
|
||||
NodeData *,
|
||||
bool first)
|
||||
{
|
||||
// We're only interested in the first time we reach edge.referent, not in
|
||||
// every edge arriving at that node. "But, don't we want to serialize every
|
||||
// edge in the heap graph?" you ask. Don't worry! This edge is still
|
||||
// serialized into the core dump. Serializing a node also serializes each of
|
||||
// its edges, and if we are traversing a given edge, we must have already
|
||||
// visited and serialized the origin node and its edges.
|
||||
if (!first)
|
||||
return true;
|
||||
|
||||
const JS::ubi::Node &referent = edge.referent;
|
||||
|
||||
if (!zones)
|
||||
// We aren't targeting a particular set of zones, so serialize all the
|
||||
// things!
|
||||
return writer.writeNode(referent, CoreDumpWriter::INCLUDE_EDGES);
|
||||
|
||||
// We are targeting a particular set of zones. If this node is in our target
|
||||
// set, serialize it and all of its edges. If this node is _not_ in our
|
||||
// target set, we also serialize under the assumption that it is a shared
|
||||
// resource being used by something in our target zones since we reached it
|
||||
// by traversing the heap graph. However, we do not serialize its outgoing
|
||||
// edges and we abandon further traversal from this node.
|
||||
|
||||
JS::Zone *zone = referent.zone();
|
||||
|
||||
if (zones->has(zone))
|
||||
return writer.writeNode(referent, CoreDumpWriter::INCLUDE_EDGES);
|
||||
|
||||
traversal.abandonReferent();
|
||||
return writer.writeNode(referent, CoreDumpWriter::EXCLUDE_EDGES);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
bool
|
||||
WriteHeapGraph(JSContext *cx,
|
||||
const JS::ubi::Node &node,
|
||||
CoreDumpWriter &writer,
|
||||
bool wantNames,
|
||||
JS::ZoneSet *zones,
|
||||
JS::AutoCheckCannotGC &noGC)
|
||||
{
|
||||
// Serialize the starting node to the core dump.
|
||||
|
||||
if (NS_WARN_IF(!writer.writeNode(node, CoreDumpWriter::INCLUDE_EDGES))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Walk the heap graph starting from the given node and serialize it into the
|
||||
// core dump.
|
||||
|
||||
HeapSnapshotHandler handler(writer, zones);
|
||||
HeapSnapshotHandler::Traversal traversal(cx, handler, noGC);
|
||||
if (!traversal.init())
|
||||
return false;
|
||||
traversal.wantNames = wantNames;
|
||||
|
||||
return traversal.addStartVisited(node) &&
|
||||
traversal.traverse();
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
ChromeUtils::SaveHeapSnapshot(GlobalObject &global,
|
||||
JSContext *cx,
|
||||
const nsAString &filePath,
|
||||
const HeapSnapshotBoundaries &boundaries,
|
||||
ErrorResult& rv)
|
||||
{
|
||||
bool wantNames = true;
|
||||
ZoneSet zones;
|
||||
Maybe<AutoCheckCannotGC> maybeNoGC;
|
||||
ubi::RootList rootList(cx, maybeNoGC, wantNames);
|
||||
if (!EstablishBoundaries(cx, rv, boundaries, rootList, zones))
|
||||
return;
|
||||
|
||||
MOZ_ASSERT(maybeNoGC.isSome());
|
||||
ubi::Node roots(&rootList);
|
||||
|
||||
nsCOMPtr<nsIFile> file;
|
||||
rv = NS_NewLocalFile(filePath, false, getter_AddRefs(file));
|
||||
if (NS_WARN_IF(rv.Failed()))
|
||||
return;
|
||||
|
||||
nsCOMPtr<nsIOutputStream> outputStream;
|
||||
rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), file,
|
||||
PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
|
||||
-1, 0);
|
||||
if (NS_WARN_IF(rv.Failed()))
|
||||
return;
|
||||
|
||||
ZeroCopyNSIOutputStream zeroCopyStream(outputStream);
|
||||
::google::protobuf::io::GzipOutputStream gzipStream(&zeroCopyStream);
|
||||
|
||||
StreamWriter writer(cx, gzipStream, wantNames);
|
||||
|
||||
// Serialize the initial heap snapshot metadata to the core dump.
|
||||
if (!writer.writeMetadata(PR_Now()) ||
|
||||
// Serialize the heap graph to the core dump, starting from our list of
|
||||
// roots.
|
||||
!WriteHeapGraph(cx,
|
||||
roots,
|
||||
writer,
|
||||
wantNames,
|
||||
zones.initialized() ? &zones : nullptr,
|
||||
maybeNoGC.ref()))
|
||||
{
|
||||
rv.Throw(zeroCopyStream.failed()
|
||||
? zeroCopyStream.result()
|
||||
: NS_ERROR_UNEXPECTED);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
72
toolkit/devtools/server/ChromeUtils.h
Normal file
72
toolkit/devtools/server/ChromeUtils.h
Normal file
@ -0,0 +1,72 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
|
||||
/* 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 mozilla_devtools_ChromeUtils__
|
||||
#define mozilla_devtools_ChromeUtils__
|
||||
|
||||
#include "CoreDump.pb.h"
|
||||
#include "jsapi.h"
|
||||
#include "jsfriendapi.h"
|
||||
|
||||
#include "js/UbiNode.h"
|
||||
#include "js/UbiNodeTraverse.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "mozilla/dom/ChromeUtilsBinding.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace devtools {
|
||||
|
||||
// A `CoreDumpWriter` is given the data we wish to save in a core dump and
|
||||
// serializes it to disk, or memory, or a socket, etc.
|
||||
class CoreDumpWriter
|
||||
{
|
||||
public:
|
||||
virtual ~CoreDumpWriter() { };
|
||||
|
||||
// Write the given bits of metadata we would like to associate with this core
|
||||
// dump.
|
||||
virtual bool writeMetadata(uint64_t timestamp) = 0;
|
||||
|
||||
enum EdgePolicy : bool {
|
||||
INCLUDE_EDGES = true,
|
||||
EXCLUDE_EDGES = false
|
||||
};
|
||||
|
||||
// Write the given `JS::ubi::Node` to the core dump. The given `EdgePolicy`
|
||||
// dictates whether its outgoing edges should also be written to the core
|
||||
// dump, or excluded.
|
||||
virtual bool writeNode(const JS::ubi::Node &node,
|
||||
EdgePolicy includeEdges) = 0;
|
||||
};
|
||||
|
||||
|
||||
// Serialize the heap graph as seen from `node` with the given
|
||||
// `CoreDumpWriter`. If `wantNames` is true, capture edge names. If `zones` is
|
||||
// non-null, only capture the sub-graph within the zone set, otherwise capture
|
||||
// the whole heap graph. Returns false on failure.
|
||||
bool
|
||||
WriteHeapGraph(JSContext *cx,
|
||||
const JS::ubi::Node &node,
|
||||
CoreDumpWriter &writer,
|
||||
bool wantNames,
|
||||
JS::ZoneSet *zones,
|
||||
JS::AutoCheckCannotGC &noGC);
|
||||
|
||||
|
||||
class ChromeUtils
|
||||
{
|
||||
public:
|
||||
static void SaveHeapSnapshot(dom::GlobalObject &global,
|
||||
JSContext *cx,
|
||||
const nsAString &filePath,
|
||||
const dom::HeapSnapshotBoundaries &boundaries,
|
||||
ErrorResult &rv);
|
||||
};
|
||||
|
||||
} // namespace devtools
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_devtools_ChromeUtils__
|
1005
toolkit/devtools/server/CoreDump.pb.cc
Normal file
1005
toolkit/devtools/server/CoreDump.pb.cc
Normal file
File diff suppressed because it is too large
Load Diff
643
toolkit/devtools/server/CoreDump.pb.h
Normal file
643
toolkit/devtools/server/CoreDump.pb.h
Normal file
@ -0,0 +1,643 @@
|
||||
// Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
// source: CoreDump.proto
|
||||
|
||||
#ifndef PROTOBUF_CoreDump_2eproto__INCLUDED
|
||||
#define PROTOBUF_CoreDump_2eproto__INCLUDED
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <google/protobuf/stubs/common.h>
|
||||
|
||||
#if GOOGLE_PROTOBUF_VERSION < 2006000
|
||||
#error This file was generated by a newer version of protoc which is
|
||||
#error incompatible with your Protocol Buffer headers. Please update
|
||||
#error your headers.
|
||||
#endif
|
||||
#if 2006001 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION
|
||||
#error This file was generated by an older version of protoc which is
|
||||
#error incompatible with your Protocol Buffer headers. Please
|
||||
#error regenerate this file with a newer version of protoc.
|
||||
#endif
|
||||
|
||||
#include <google/protobuf/generated_message_util.h>
|
||||
#include <google/protobuf/message.h>
|
||||
#include <google/protobuf/repeated_field.h>
|
||||
#include <google/protobuf/extension_set.h>
|
||||
#include <google/protobuf/unknown_field_set.h>
|
||||
// @@protoc_insertion_point(includes)
|
||||
|
||||
namespace mozilla {
|
||||
namespace devtools {
|
||||
namespace protobuf {
|
||||
|
||||
// Internal implementation detail -- do not call these.
|
||||
void protobuf_AddDesc_CoreDump_2eproto();
|
||||
void protobuf_AssignDesc_CoreDump_2eproto();
|
||||
void protobuf_ShutdownFile_CoreDump_2eproto();
|
||||
|
||||
class Metadata;
|
||||
class Node;
|
||||
class Edge;
|
||||
|
||||
// ===================================================================
|
||||
|
||||
class Metadata : public ::google::protobuf::Message {
|
||||
public:
|
||||
Metadata();
|
||||
virtual ~Metadata();
|
||||
|
||||
Metadata(const Metadata& from);
|
||||
|
||||
inline Metadata& operator=(const Metadata& from) {
|
||||
CopyFrom(from);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const {
|
||||
return _unknown_fields_;
|
||||
}
|
||||
|
||||
inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() {
|
||||
return &_unknown_fields_;
|
||||
}
|
||||
|
||||
static const ::google::protobuf::Descriptor* descriptor();
|
||||
static const Metadata& default_instance();
|
||||
|
||||
void Swap(Metadata* other);
|
||||
|
||||
// implements Message ----------------------------------------------
|
||||
|
||||
Metadata* New() const;
|
||||
void CopyFrom(const ::google::protobuf::Message& from);
|
||||
void MergeFrom(const ::google::protobuf::Message& from);
|
||||
void CopyFrom(const Metadata& from);
|
||||
void MergeFrom(const Metadata& from);
|
||||
void Clear();
|
||||
bool IsInitialized() const;
|
||||
|
||||
int ByteSize() const;
|
||||
bool MergePartialFromCodedStream(
|
||||
::google::protobuf::io::CodedInputStream* input);
|
||||
void SerializeWithCachedSizes(
|
||||
::google::protobuf::io::CodedOutputStream* output) const;
|
||||
::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const;
|
||||
int GetCachedSize() const { return _cached_size_; }
|
||||
private:
|
||||
void SharedCtor();
|
||||
void SharedDtor();
|
||||
void SetCachedSize(int size) const;
|
||||
public:
|
||||
::google::protobuf::Metadata GetMetadata() const;
|
||||
|
||||
// nested types ----------------------------------------------------
|
||||
|
||||
// accessors -------------------------------------------------------
|
||||
|
||||
// optional uint64 timeStamp = 1;
|
||||
inline bool has_timestamp() const;
|
||||
inline void clear_timestamp();
|
||||
static const int kTimeStampFieldNumber = 1;
|
||||
inline ::google::protobuf::uint64 timestamp() const;
|
||||
inline void set_timestamp(::google::protobuf::uint64 value);
|
||||
|
||||
// @@protoc_insertion_point(class_scope:mozilla.devtools.protobuf.Metadata)
|
||||
private:
|
||||
inline void set_has_timestamp();
|
||||
inline void clear_has_timestamp();
|
||||
|
||||
::google::protobuf::UnknownFieldSet _unknown_fields_;
|
||||
|
||||
::google::protobuf::uint32 _has_bits_[1];
|
||||
mutable int _cached_size_;
|
||||
::google::protobuf::uint64 timestamp_;
|
||||
friend void protobuf_AddDesc_CoreDump_2eproto();
|
||||
friend void protobuf_AssignDesc_CoreDump_2eproto();
|
||||
friend void protobuf_ShutdownFile_CoreDump_2eproto();
|
||||
|
||||
void InitAsDefaultInstance();
|
||||
static Metadata* default_instance_;
|
||||
};
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
class Node : public ::google::protobuf::Message {
|
||||
public:
|
||||
Node();
|
||||
virtual ~Node();
|
||||
|
||||
Node(const Node& from);
|
||||
|
||||
inline Node& operator=(const Node& from) {
|
||||
CopyFrom(from);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const {
|
||||
return _unknown_fields_;
|
||||
}
|
||||
|
||||
inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() {
|
||||
return &_unknown_fields_;
|
||||
}
|
||||
|
||||
static const ::google::protobuf::Descriptor* descriptor();
|
||||
static const Node& default_instance();
|
||||
|
||||
void Swap(Node* other);
|
||||
|
||||
// implements Message ----------------------------------------------
|
||||
|
||||
Node* New() const;
|
||||
void CopyFrom(const ::google::protobuf::Message& from);
|
||||
void MergeFrom(const ::google::protobuf::Message& from);
|
||||
void CopyFrom(const Node& from);
|
||||
void MergeFrom(const Node& from);
|
||||
void Clear();
|
||||
bool IsInitialized() const;
|
||||
|
||||
int ByteSize() const;
|
||||
bool MergePartialFromCodedStream(
|
||||
::google::protobuf::io::CodedInputStream* input);
|
||||
void SerializeWithCachedSizes(
|
||||
::google::protobuf::io::CodedOutputStream* output) const;
|
||||
::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const;
|
||||
int GetCachedSize() const { return _cached_size_; }
|
||||
private:
|
||||
void SharedCtor();
|
||||
void SharedDtor();
|
||||
void SetCachedSize(int size) const;
|
||||
public:
|
||||
::google::protobuf::Metadata GetMetadata() const;
|
||||
|
||||
// nested types ----------------------------------------------------
|
||||
|
||||
// accessors -------------------------------------------------------
|
||||
|
||||
// optional uint64 id = 1;
|
||||
inline bool has_id() const;
|
||||
inline void clear_id();
|
||||
static const int kIdFieldNumber = 1;
|
||||
inline ::google::protobuf::uint64 id() const;
|
||||
inline void set_id(::google::protobuf::uint64 value);
|
||||
|
||||
// optional bytes typeName = 2;
|
||||
inline bool has_typename_() const;
|
||||
inline void clear_typename_();
|
||||
static const int kTypeNameFieldNumber = 2;
|
||||
inline const ::std::string& typename_() const;
|
||||
inline void set_typename_(const ::std::string& value);
|
||||
inline void set_typename_(const char* value);
|
||||
inline void set_typename_(const void* value, size_t size);
|
||||
inline ::std::string* mutable_typename_();
|
||||
inline ::std::string* release_typename_();
|
||||
inline void set_allocated_typename_(::std::string* typename_);
|
||||
|
||||
// optional uint64 size = 3;
|
||||
inline bool has_size() const;
|
||||
inline void clear_size();
|
||||
static const int kSizeFieldNumber = 3;
|
||||
inline ::google::protobuf::uint64 size() const;
|
||||
inline void set_size(::google::protobuf::uint64 value);
|
||||
|
||||
// repeated .mozilla.devtools.protobuf.Edge edges = 4;
|
||||
inline int edges_size() const;
|
||||
inline void clear_edges();
|
||||
static const int kEdgesFieldNumber = 4;
|
||||
inline const ::mozilla::devtools::protobuf::Edge& edges(int index) const;
|
||||
inline ::mozilla::devtools::protobuf::Edge* mutable_edges(int index);
|
||||
inline ::mozilla::devtools::protobuf::Edge* add_edges();
|
||||
inline const ::google::protobuf::RepeatedPtrField< ::mozilla::devtools::protobuf::Edge >&
|
||||
edges() const;
|
||||
inline ::google::protobuf::RepeatedPtrField< ::mozilla::devtools::protobuf::Edge >*
|
||||
mutable_edges();
|
||||
|
||||
// @@protoc_insertion_point(class_scope:mozilla.devtools.protobuf.Node)
|
||||
private:
|
||||
inline void set_has_id();
|
||||
inline void clear_has_id();
|
||||
inline void set_has_typename_();
|
||||
inline void clear_has_typename_();
|
||||
inline void set_has_size();
|
||||
inline void clear_has_size();
|
||||
|
||||
::google::protobuf::UnknownFieldSet _unknown_fields_;
|
||||
|
||||
::google::protobuf::uint32 _has_bits_[1];
|
||||
mutable int _cached_size_;
|
||||
::google::protobuf::uint64 id_;
|
||||
::std::string* typename__;
|
||||
::google::protobuf::uint64 size_;
|
||||
::google::protobuf::RepeatedPtrField< ::mozilla::devtools::protobuf::Edge > edges_;
|
||||
friend void protobuf_AddDesc_CoreDump_2eproto();
|
||||
friend void protobuf_AssignDesc_CoreDump_2eproto();
|
||||
friend void protobuf_ShutdownFile_CoreDump_2eproto();
|
||||
|
||||
void InitAsDefaultInstance();
|
||||
static Node* default_instance_;
|
||||
};
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
class Edge : public ::google::protobuf::Message {
|
||||
public:
|
||||
Edge();
|
||||
virtual ~Edge();
|
||||
|
||||
Edge(const Edge& from);
|
||||
|
||||
inline Edge& operator=(const Edge& from) {
|
||||
CopyFrom(from);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const {
|
||||
return _unknown_fields_;
|
||||
}
|
||||
|
||||
inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() {
|
||||
return &_unknown_fields_;
|
||||
}
|
||||
|
||||
static const ::google::protobuf::Descriptor* descriptor();
|
||||
static const Edge& default_instance();
|
||||
|
||||
void Swap(Edge* other);
|
||||
|
||||
// implements Message ----------------------------------------------
|
||||
|
||||
Edge* New() const;
|
||||
void CopyFrom(const ::google::protobuf::Message& from);
|
||||
void MergeFrom(const ::google::protobuf::Message& from);
|
||||
void CopyFrom(const Edge& from);
|
||||
void MergeFrom(const Edge& from);
|
||||
void Clear();
|
||||
bool IsInitialized() const;
|
||||
|
||||
int ByteSize() const;
|
||||
bool MergePartialFromCodedStream(
|
||||
::google::protobuf::io::CodedInputStream* input);
|
||||
void SerializeWithCachedSizes(
|
||||
::google::protobuf::io::CodedOutputStream* output) const;
|
||||
::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const;
|
||||
int GetCachedSize() const { return _cached_size_; }
|
||||
private:
|
||||
void SharedCtor();
|
||||
void SharedDtor();
|
||||
void SetCachedSize(int size) const;
|
||||
public:
|
||||
::google::protobuf::Metadata GetMetadata() const;
|
||||
|
||||
// nested types ----------------------------------------------------
|
||||
|
||||
// accessors -------------------------------------------------------
|
||||
|
||||
// optional uint64 referent = 1;
|
||||
inline bool has_referent() const;
|
||||
inline void clear_referent();
|
||||
static const int kReferentFieldNumber = 1;
|
||||
inline ::google::protobuf::uint64 referent() const;
|
||||
inline void set_referent(::google::protobuf::uint64 value);
|
||||
|
||||
// optional bytes name = 2;
|
||||
inline bool has_name() const;
|
||||
inline void clear_name();
|
||||
static const int kNameFieldNumber = 2;
|
||||
inline const ::std::string& name() const;
|
||||
inline void set_name(const ::std::string& value);
|
||||
inline void set_name(const char* value);
|
||||
inline void set_name(const void* value, size_t size);
|
||||
inline ::std::string* mutable_name();
|
||||
inline ::std::string* release_name();
|
||||
inline void set_allocated_name(::std::string* name);
|
||||
|
||||
// @@protoc_insertion_point(class_scope:mozilla.devtools.protobuf.Edge)
|
||||
private:
|
||||
inline void set_has_referent();
|
||||
inline void clear_has_referent();
|
||||
inline void set_has_name();
|
||||
inline void clear_has_name();
|
||||
|
||||
::google::protobuf::UnknownFieldSet _unknown_fields_;
|
||||
|
||||
::google::protobuf::uint32 _has_bits_[1];
|
||||
mutable int _cached_size_;
|
||||
::google::protobuf::uint64 referent_;
|
||||
::std::string* name_;
|
||||
friend void protobuf_AddDesc_CoreDump_2eproto();
|
||||
friend void protobuf_AssignDesc_CoreDump_2eproto();
|
||||
friend void protobuf_ShutdownFile_CoreDump_2eproto();
|
||||
|
||||
void InitAsDefaultInstance();
|
||||
static Edge* default_instance_;
|
||||
};
|
||||
// ===================================================================
|
||||
|
||||
|
||||
// ===================================================================
|
||||
|
||||
// Metadata
|
||||
|
||||
// optional uint64 timeStamp = 1;
|
||||
inline bool Metadata::has_timestamp() const {
|
||||
return (_has_bits_[0] & 0x00000001u) != 0;
|
||||
}
|
||||
inline void Metadata::set_has_timestamp() {
|
||||
_has_bits_[0] |= 0x00000001u;
|
||||
}
|
||||
inline void Metadata::clear_has_timestamp() {
|
||||
_has_bits_[0] &= ~0x00000001u;
|
||||
}
|
||||
inline void Metadata::clear_timestamp() {
|
||||
timestamp_ = GOOGLE_ULONGLONG(0);
|
||||
clear_has_timestamp();
|
||||
}
|
||||
inline ::google::protobuf::uint64 Metadata::timestamp() const {
|
||||
// @@protoc_insertion_point(field_get:mozilla.devtools.protobuf.Metadata.timeStamp)
|
||||
return timestamp_;
|
||||
}
|
||||
inline void Metadata::set_timestamp(::google::protobuf::uint64 value) {
|
||||
set_has_timestamp();
|
||||
timestamp_ = value;
|
||||
// @@protoc_insertion_point(field_set:mozilla.devtools.protobuf.Metadata.timeStamp)
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
// Node
|
||||
|
||||
// optional uint64 id = 1;
|
||||
inline bool Node::has_id() const {
|
||||
return (_has_bits_[0] & 0x00000001u) != 0;
|
||||
}
|
||||
inline void Node::set_has_id() {
|
||||
_has_bits_[0] |= 0x00000001u;
|
||||
}
|
||||
inline void Node::clear_has_id() {
|
||||
_has_bits_[0] &= ~0x00000001u;
|
||||
}
|
||||
inline void Node::clear_id() {
|
||||
id_ = GOOGLE_ULONGLONG(0);
|
||||
clear_has_id();
|
||||
}
|
||||
inline ::google::protobuf::uint64 Node::id() const {
|
||||
// @@protoc_insertion_point(field_get:mozilla.devtools.protobuf.Node.id)
|
||||
return id_;
|
||||
}
|
||||
inline void Node::set_id(::google::protobuf::uint64 value) {
|
||||
set_has_id();
|
||||
id_ = value;
|
||||
// @@protoc_insertion_point(field_set:mozilla.devtools.protobuf.Node.id)
|
||||
}
|
||||
|
||||
// optional bytes typeName = 2;
|
||||
inline bool Node::has_typename_() const {
|
||||
return (_has_bits_[0] & 0x00000002u) != 0;
|
||||
}
|
||||
inline void Node::set_has_typename_() {
|
||||
_has_bits_[0] |= 0x00000002u;
|
||||
}
|
||||
inline void Node::clear_has_typename_() {
|
||||
_has_bits_[0] &= ~0x00000002u;
|
||||
}
|
||||
inline void Node::clear_typename_() {
|
||||
if (typename__ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
|
||||
typename__->clear();
|
||||
}
|
||||
clear_has_typename_();
|
||||
}
|
||||
inline const ::std::string& Node::typename_() const {
|
||||
// @@protoc_insertion_point(field_get:mozilla.devtools.protobuf.Node.typeName)
|
||||
return *typename__;
|
||||
}
|
||||
inline void Node::set_typename_(const ::std::string& value) {
|
||||
set_has_typename_();
|
||||
if (typename__ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
|
||||
typename__ = new ::std::string;
|
||||
}
|
||||
typename__->assign(value);
|
||||
// @@protoc_insertion_point(field_set:mozilla.devtools.protobuf.Node.typeName)
|
||||
}
|
||||
inline void Node::set_typename_(const char* value) {
|
||||
set_has_typename_();
|
||||
if (typename__ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
|
||||
typename__ = new ::std::string;
|
||||
}
|
||||
typename__->assign(value);
|
||||
// @@protoc_insertion_point(field_set_char:mozilla.devtools.protobuf.Node.typeName)
|
||||
}
|
||||
inline void Node::set_typename_(const void* value, size_t size) {
|
||||
set_has_typename_();
|
||||
if (typename__ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
|
||||
typename__ = new ::std::string;
|
||||
}
|
||||
typename__->assign(reinterpret_cast<const char*>(value), size);
|
||||
// @@protoc_insertion_point(field_set_pointer:mozilla.devtools.protobuf.Node.typeName)
|
||||
}
|
||||
inline ::std::string* Node::mutable_typename_() {
|
||||
set_has_typename_();
|
||||
if (typename__ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
|
||||
typename__ = new ::std::string;
|
||||
}
|
||||
// @@protoc_insertion_point(field_mutable:mozilla.devtools.protobuf.Node.typeName)
|
||||
return typename__;
|
||||
}
|
||||
inline ::std::string* Node::release_typename_() {
|
||||
clear_has_typename_();
|
||||
if (typename__ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
|
||||
return NULL;
|
||||
} else {
|
||||
::std::string* temp = typename__;
|
||||
typename__ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
|
||||
return temp;
|
||||
}
|
||||
}
|
||||
inline void Node::set_allocated_typename_(::std::string* typename_) {
|
||||
if (typename__ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
|
||||
delete typename__;
|
||||
}
|
||||
if (typename_) {
|
||||
set_has_typename_();
|
||||
typename__ = typename_;
|
||||
} else {
|
||||
clear_has_typename_();
|
||||
typename__ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
|
||||
}
|
||||
// @@protoc_insertion_point(field_set_allocated:mozilla.devtools.protobuf.Node.typeName)
|
||||
}
|
||||
|
||||
// optional uint64 size = 3;
|
||||
inline bool Node::has_size() const {
|
||||
return (_has_bits_[0] & 0x00000004u) != 0;
|
||||
}
|
||||
inline void Node::set_has_size() {
|
||||
_has_bits_[0] |= 0x00000004u;
|
||||
}
|
||||
inline void Node::clear_has_size() {
|
||||
_has_bits_[0] &= ~0x00000004u;
|
||||
}
|
||||
inline void Node::clear_size() {
|
||||
size_ = GOOGLE_ULONGLONG(0);
|
||||
clear_has_size();
|
||||
}
|
||||
inline ::google::protobuf::uint64 Node::size() const {
|
||||
// @@protoc_insertion_point(field_get:mozilla.devtools.protobuf.Node.size)
|
||||
return size_;
|
||||
}
|
||||
inline void Node::set_size(::google::protobuf::uint64 value) {
|
||||
set_has_size();
|
||||
size_ = value;
|
||||
// @@protoc_insertion_point(field_set:mozilla.devtools.protobuf.Node.size)
|
||||
}
|
||||
|
||||
// repeated .mozilla.devtools.protobuf.Edge edges = 4;
|
||||
inline int Node::edges_size() const {
|
||||
return edges_.size();
|
||||
}
|
||||
inline void Node::clear_edges() {
|
||||
edges_.Clear();
|
||||
}
|
||||
inline const ::mozilla::devtools::protobuf::Edge& Node::edges(int index) const {
|
||||
// @@protoc_insertion_point(field_get:mozilla.devtools.protobuf.Node.edges)
|
||||
return edges_.Get(index);
|
||||
}
|
||||
inline ::mozilla::devtools::protobuf::Edge* Node::mutable_edges(int index) {
|
||||
// @@protoc_insertion_point(field_mutable:mozilla.devtools.protobuf.Node.edges)
|
||||
return edges_.Mutable(index);
|
||||
}
|
||||
inline ::mozilla::devtools::protobuf::Edge* Node::add_edges() {
|
||||
// @@protoc_insertion_point(field_add:mozilla.devtools.protobuf.Node.edges)
|
||||
return edges_.Add();
|
||||
}
|
||||
inline const ::google::protobuf::RepeatedPtrField< ::mozilla::devtools::protobuf::Edge >&
|
||||
Node::edges() const {
|
||||
// @@protoc_insertion_point(field_list:mozilla.devtools.protobuf.Node.edges)
|
||||
return edges_;
|
||||
}
|
||||
inline ::google::protobuf::RepeatedPtrField< ::mozilla::devtools::protobuf::Edge >*
|
||||
Node::mutable_edges() {
|
||||
// @@protoc_insertion_point(field_mutable_list:mozilla.devtools.protobuf.Node.edges)
|
||||
return &edges_;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
// Edge
|
||||
|
||||
// optional uint64 referent = 1;
|
||||
inline bool Edge::has_referent() const {
|
||||
return (_has_bits_[0] & 0x00000001u) != 0;
|
||||
}
|
||||
inline void Edge::set_has_referent() {
|
||||
_has_bits_[0] |= 0x00000001u;
|
||||
}
|
||||
inline void Edge::clear_has_referent() {
|
||||
_has_bits_[0] &= ~0x00000001u;
|
||||
}
|
||||
inline void Edge::clear_referent() {
|
||||
referent_ = GOOGLE_ULONGLONG(0);
|
||||
clear_has_referent();
|
||||
}
|
||||
inline ::google::protobuf::uint64 Edge::referent() const {
|
||||
// @@protoc_insertion_point(field_get:mozilla.devtools.protobuf.Edge.referent)
|
||||
return referent_;
|
||||
}
|
||||
inline void Edge::set_referent(::google::protobuf::uint64 value) {
|
||||
set_has_referent();
|
||||
referent_ = value;
|
||||
// @@protoc_insertion_point(field_set:mozilla.devtools.protobuf.Edge.referent)
|
||||
}
|
||||
|
||||
// optional bytes name = 2;
|
||||
inline bool Edge::has_name() const {
|
||||
return (_has_bits_[0] & 0x00000002u) != 0;
|
||||
}
|
||||
inline void Edge::set_has_name() {
|
||||
_has_bits_[0] |= 0x00000002u;
|
||||
}
|
||||
inline void Edge::clear_has_name() {
|
||||
_has_bits_[0] &= ~0x00000002u;
|
||||
}
|
||||
inline void Edge::clear_name() {
|
||||
if (name_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
|
||||
name_->clear();
|
||||
}
|
||||
clear_has_name();
|
||||
}
|
||||
inline const ::std::string& Edge::name() const {
|
||||
// @@protoc_insertion_point(field_get:mozilla.devtools.protobuf.Edge.name)
|
||||
return *name_;
|
||||
}
|
||||
inline void Edge::set_name(const ::std::string& value) {
|
||||
set_has_name();
|
||||
if (name_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
|
||||
name_ = new ::std::string;
|
||||
}
|
||||
name_->assign(value);
|
||||
// @@protoc_insertion_point(field_set:mozilla.devtools.protobuf.Edge.name)
|
||||
}
|
||||
inline void Edge::set_name(const char* value) {
|
||||
set_has_name();
|
||||
if (name_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
|
||||
name_ = new ::std::string;
|
||||
}
|
||||
name_->assign(value);
|
||||
// @@protoc_insertion_point(field_set_char:mozilla.devtools.protobuf.Edge.name)
|
||||
}
|
||||
inline void Edge::set_name(const void* value, size_t size) {
|
||||
set_has_name();
|
||||
if (name_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
|
||||
name_ = new ::std::string;
|
||||
}
|
||||
name_->assign(reinterpret_cast<const char*>(value), size);
|
||||
// @@protoc_insertion_point(field_set_pointer:mozilla.devtools.protobuf.Edge.name)
|
||||
}
|
||||
inline ::std::string* Edge::mutable_name() {
|
||||
set_has_name();
|
||||
if (name_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
|
||||
name_ = new ::std::string;
|
||||
}
|
||||
// @@protoc_insertion_point(field_mutable:mozilla.devtools.protobuf.Edge.name)
|
||||
return name_;
|
||||
}
|
||||
inline ::std::string* Edge::release_name() {
|
||||
clear_has_name();
|
||||
if (name_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
|
||||
return NULL;
|
||||
} else {
|
||||
::std::string* temp = name_;
|
||||
name_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
|
||||
return temp;
|
||||
}
|
||||
}
|
||||
inline void Edge::set_allocated_name(::std::string* name) {
|
||||
if (name_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
|
||||
delete name_;
|
||||
}
|
||||
if (name) {
|
||||
set_has_name();
|
||||
name_ = name;
|
||||
} else {
|
||||
clear_has_name();
|
||||
name_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
|
||||
}
|
||||
// @@protoc_insertion_point(field_set_allocated:mozilla.devtools.protobuf.Edge.name)
|
||||
}
|
||||
|
||||
|
||||
// @@protoc_insertion_point(namespace_scope)
|
||||
|
||||
} // namespace protobuf
|
||||
} // namespace devtools
|
||||
} // namespace mozilla
|
||||
|
||||
#ifndef SWIG
|
||||
namespace google {
|
||||
namespace protobuf {
|
||||
|
||||
|
||||
} // namespace google
|
||||
} // namespace protobuf
|
||||
#endif // SWIG
|
||||
|
||||
// @@protoc_insertion_point(global_scope)
|
||||
|
||||
#endif // PROTOBUF_CoreDump_2eproto__INCLUDED
|
69
toolkit/devtools/server/CoreDump.proto
Normal file
69
toolkit/devtools/server/CoreDump.proto
Normal file
@ -0,0 +1,69 @@
|
||||
/* -*- Mode: protobuf; 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/. */
|
||||
|
||||
// # Core Dumps
|
||||
//
|
||||
// A core dump is a serialized snapshot of the heap graph. We serialize the heap
|
||||
// as a series of protobuf messages with each message prefixed by its Varint32
|
||||
// byte size so we can delimit individual protobuf messages (protobuf parsers
|
||||
// cannot determine where a message ends on their own).
|
||||
//
|
||||
// The first protobuf message is an instance of the `Metadata` message. All
|
||||
// subsequent messages will be instances of the `Node` message. The first of
|
||||
// these `Node` messages is the root node of the serialized heap graph. Here is
|
||||
// a diagram of our core dump format:
|
||||
//
|
||||
// +-----------------------------------------------------------------------+
|
||||
// | Varint32: The size of following `Metadata` message. |
|
||||
// +-----------------------------------------------------------------------+
|
||||
// | message: The core dump `Metadata` message. |
|
||||
// +-----------------------------------------------------------------------+
|
||||
// | Varint32: The size of the following `Node` message. |
|
||||
// +-----------------------------------------------------------------------+
|
||||
// | message: The first `Node` message. This is the root node. |
|
||||
// +-----------------------------------------------------------------------+
|
||||
// | Varint32: The size of the following `Node` message. |
|
||||
// +-----------------------------------------------------------------------+
|
||||
// | message: A `Node` message. |
|
||||
// +-----------------------------------------------------------------------+
|
||||
// | Varint32: The size of the following `Node` message. |
|
||||
// +-----------------------------------------------------------------------+
|
||||
// | message: A `Node` message. |
|
||||
// +-----------------------------------------------------------------------+
|
||||
// | . |
|
||||
// | . |
|
||||
// | . |
|
||||
// +-----------------------------------------------------------------------+
|
||||
//
|
||||
// In practice, certain message fields have a lot of duplication (such as type
|
||||
// or edge name strings). Rather than try and de-duplicate this information at
|
||||
// the protobuf message and field level, core dumps should be written with
|
||||
// `google::protobuf::io::GzipOutputStream` and read from
|
||||
// `google::protobuf::io::GzipInputStream`.
|
||||
|
||||
package mozilla.devtools.protobuf;
|
||||
|
||||
// A collection of metadata about this core dump.
|
||||
message Metadata {
|
||||
// Number of microseconds since midnight (00:00:00) 1 January 1970 UTC.
|
||||
optional uint64 timeStamp = 1;
|
||||
}
|
||||
|
||||
// A serialized version of `JS::ubi::Node` and its outgoing edges.
|
||||
message Node {
|
||||
optional uint64 id = 1;
|
||||
// char16_t[]
|
||||
optional bytes typeName = 2;
|
||||
optional uint64 size = 3;
|
||||
repeated Edge edges = 4;
|
||||
}
|
||||
|
||||
// A serialized edge from the heap graph.
|
||||
message Edge {
|
||||
optional uint64 referent = 1;
|
||||
// char16_t[]
|
||||
optional bytes name = 2;
|
||||
}
|
26
toolkit/devtools/server/generate-core-dump-sources.sh
Executable file
26
toolkit/devtools/server/generate-core-dump-sources.sh
Executable file
@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# A script to generate toolkit/devtools/server/CoreDump.pb.{h,cc} from
|
||||
# toolkit/devtools/server/CoreDump.proto. This script assumes you have
|
||||
# downloaded and installed the protocol buffer compiler, and that it is either
|
||||
# on your $PATH or located at $PROTOC_PATH.
|
||||
#
|
||||
# These files were last compiled with libprotoc 2.4.1.
|
||||
|
||||
set -e
|
||||
|
||||
cd $(dirname $0)
|
||||
|
||||
if [ -n $PROTOC_PATH ]; then
|
||||
PROTOC_PATH=`which protoc`
|
||||
fi
|
||||
|
||||
if [ ! -e $PROTOC_PATH ]; then
|
||||
echo You must install the protocol compiler from
|
||||
echo https://code.google.com/p/protobuf/downloads/list
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo Using $PROTOC_PATH as the protocol compiler
|
||||
|
||||
$PROTOC_PATH --cpp_out="." CoreDump.proto
|
@ -15,14 +15,21 @@ XPIDL_SOURCES += [
|
||||
XPIDL_MODULE = 'jsinspector'
|
||||
|
||||
EXPORTS.mozilla.devtools += [
|
||||
'ChromeUtils.h',
|
||||
'CoreDump.pb.h',
|
||||
'ZeroCopyNSIOutputStream.h',
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
'ChromeUtils.cpp',
|
||||
'CoreDump.pb.cc',
|
||||
'nsJSInspector.cpp',
|
||||
'ZeroCopyNSIOutputStream.cpp',
|
||||
]
|
||||
|
||||
# Disable RTTI in google protocol buffer
|
||||
DEFINES['GOOGLE_PROTOBUF_NO_RTTI'] = True
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
EXTRA_JS_MODULES.devtools += [
|
||||
|
Loading…
Reference in New Issue
Block a user