Bug 1024774 - Part 3: Serialize heap snapshots. r=jimb

This commit is contained in:
Nick Fitzgerald 2015-05-28 07:37:42 -07:00
parent 730f75784f
commit 967aa5947f
17 changed files with 2312 additions and 39 deletions

View File

@ -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.

View File

@ -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.

View File

@ -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();

View File

@ -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)
{

View File

@ -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

View File

@ -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();

View File

@ -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;
}

View File

@ -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;

View File

@ -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 {

View File

@ -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>

View 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;
}
}
}
}

View 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__

File diff suppressed because it is too large Load Diff

View 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

View 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;
}

View 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

View File

@ -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 += [