Bug 1196498 - Include objects' [[class]] names in heap snapshots; r=sfink

This commit is contained in:
Nick Fitzgerald 2015-08-24 09:29:44 -07:00
parent 93acb863e9
commit aca9830bfe
7 changed files with 181 additions and 8 deletions

View File

@ -108,12 +108,13 @@ void protobuf_AssignDesc_CoreDump_2eproto() {
::google::protobuf::MessageFactory::generated_factory(),
sizeof(StackFrame_Data));
Node_descriptor_ = file->message_type(2);
static const int Node_offsets_[5] = {
static const int Node_offsets_[6] = {
GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Node, id_),
GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Node, typename__),
GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Node, size_),
GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Node, edges_),
GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Node, allocationstack_),
GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Node, jsobjectclassname_),
};
Node_reflection_ =
new ::google::protobuf::internal::GeneratedMessageReflection(
@ -198,12 +199,13 @@ void protobuf_AddDesc_CoreDump_2eproto() {
"\022\014\n\004line\030\003 \001(\r\022\016\n\006column\030\004 \001(\r\022\016\n\006source"
"\030\005 \001(\014\022\033\n\023functionDisplayName\030\006 \001(\014\022\020\n\010i"
"sSystem\030\007 \001(\010\022\024\n\014isSelfHosted\030\010 \001(\010B\020\n\016S"
"tackFrameType\"\242\001\n\004Node\022\n\n\002id\030\001 \001(\004\022\020\n\010ty"
"tackFrameType\"\275\001\n\004Node\022\n\n\002id\030\001 \001(\004\022\020\n\010ty"
"peName\030\002 \001(\014\022\014\n\004size\030\003 \001(\004\022.\n\005edges\030\004 \003("
"\0132\037.mozilla.devtools.protobuf.Edge\022>\n\017al"
"locationStack\030\005 \001(\0132%.mozilla.devtools.p"
"rotobuf.StackFrame\"&\n\004Edge\022\020\n\010referent\030\001"
" \001(\004\022\014\n\004name\030\002 \001(\014", 578);
"rotobuf.StackFrame\022\031\n\021jsObjectClassName\030"
"\006 \001(\014\"&\n\004Edge\022\020\n\010referent\030\001 \001(\004\022\014\n\004name\030"
"\002 \001(\014", 605);
::google::protobuf::MessageFactory::InternalRegisterGeneratedFile(
"CoreDump.proto", &protobuf_RegisterTypes);
Metadata::default_instance_ = new Metadata();
@ -1276,6 +1278,7 @@ const int Node::kTypeNameFieldNumber;
const int Node::kSizeFieldNumber;
const int Node::kEdgesFieldNumber;
const int Node::kAllocationStackFieldNumber;
const int Node::kJsObjectClassNameFieldNumber;
#endif // !_MSC_VER
Node::Node()
@ -1302,6 +1305,7 @@ void Node::SharedCtor() {
typename__ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
size_ = GOOGLE_ULONGLONG(0);
allocationstack_ = NULL;
jsobjectclassname_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
::memset(_has_bits_, 0, sizeof(_has_bits_));
}
@ -1314,6 +1318,9 @@ void Node::SharedDtor() {
if (typename__ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
delete typename__;
}
if (jsobjectclassname_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
delete jsobjectclassname_;
}
if (this != default_instance_) {
delete allocationstack_;
}
@ -1341,7 +1348,7 @@ Node* Node::New() const {
}
void Node::Clear() {
if (_has_bits_[0 / 32] & 23) {
if (_has_bits_[0 / 32] & 55) {
id_ = GOOGLE_ULONGLONG(0);
if (has_typename_()) {
if (typename__ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
@ -1352,6 +1359,11 @@ void Node::Clear() {
if (has_allocationstack()) {
if (allocationstack_ != NULL) allocationstack_->::mozilla::devtools::protobuf::StackFrame::Clear();
}
if (has_jsobjectclassname()) {
if (jsobjectclassname_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
jsobjectclassname_->clear();
}
}
}
edges_.Clear();
::memset(_has_bits_, 0, sizeof(_has_bits_));
@ -1433,6 +1445,19 @@ bool Node::MergePartialFromCodedStream(
} else {
goto handle_unusual;
}
if (input->ExpectTag(50)) goto parse_jsObjectClassName;
break;
}
// optional bytes jsObjectClassName = 6;
case 6: {
if (tag == 50) {
parse_jsObjectClassName:
DO_(::google::protobuf::internal::WireFormatLite::ReadBytes(
input, this->mutable_jsobjectclassname()));
} else {
goto handle_unusual;
}
if (input->ExpectAtEnd()) goto success;
break;
}
@ -1490,6 +1515,12 @@ void Node::SerializeWithCachedSizes(
5, this->allocationstack(), output);
}
// optional bytes jsObjectClassName = 6;
if (has_jsobjectclassname()) {
::google::protobuf::internal::WireFormatLite::WriteBytesMaybeAliased(
6, this->jsobjectclassname(), output);
}
if (!unknown_fields().empty()) {
::google::protobuf::internal::WireFormat::SerializeUnknownFields(
unknown_fields(), output);
@ -1531,6 +1562,13 @@ void Node::SerializeWithCachedSizes(
5, this->allocationstack(), target);
}
// optional bytes jsObjectClassName = 6;
if (has_jsobjectclassname()) {
target =
::google::protobuf::internal::WireFormatLite::WriteBytesToArray(
6, this->jsobjectclassname(), target);
}
if (!unknown_fields().empty()) {
target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray(
unknown_fields(), target);
@ -1571,6 +1609,13 @@ int Node::ByteSize() const {
this->allocationstack());
}
// optional bytes jsObjectClassName = 6;
if (has_jsobjectclassname()) {
total_size += 1 +
::google::protobuf::internal::WireFormatLite::BytesSize(
this->jsobjectclassname());
}
}
// repeated .mozilla.devtools.protobuf.Edge edges = 4;
total_size += 1 * this->edges_size();
@ -1619,6 +1664,9 @@ void Node::MergeFrom(const Node& from) {
if (from.has_allocationstack()) {
mutable_allocationstack()->::mozilla::devtools::protobuf::StackFrame::MergeFrom(from.allocationstack());
}
if (from.has_jsobjectclassname()) {
set_jsobjectclassname(from.jsobjectclassname());
}
}
mutable_unknown_fields()->MergeFrom(from.unknown_fields());
}
@ -1647,6 +1695,7 @@ void Node::Swap(Node* other) {
std::swap(size_, other->size_);
edges_.Swap(&other->edges_);
std::swap(allocationstack_, other->allocationstack_);
std::swap(jsobjectclassname_, other->jsobjectclassname_);
std::swap(_has_bits_[0], other->_has_bits_[0]);
_unknown_fields_.Swap(&other->_unknown_fields_);
std::swap(_cached_size_, other->_cached_size_);

View File

@ -489,6 +489,18 @@ class Node : public ::google::protobuf::Message {
inline ::mozilla::devtools::protobuf::StackFrame* release_allocationstack();
inline void set_allocated_allocationstack(::mozilla::devtools::protobuf::StackFrame* allocationstack);
// optional bytes jsObjectClassName = 6;
inline bool has_jsobjectclassname() const;
inline void clear_jsobjectclassname();
static const int kJsObjectClassNameFieldNumber = 6;
inline const ::std::string& jsobjectclassname() const;
inline void set_jsobjectclassname(const ::std::string& value);
inline void set_jsobjectclassname(const char* value);
inline void set_jsobjectclassname(const void* value, size_t size);
inline ::std::string* mutable_jsobjectclassname();
inline ::std::string* release_jsobjectclassname();
inline void set_allocated_jsobjectclassname(::std::string* jsobjectclassname);
// @@protoc_insertion_point(class_scope:mozilla.devtools.protobuf.Node)
private:
inline void set_has_id();
@ -499,6 +511,8 @@ class Node : public ::google::protobuf::Message {
inline void clear_has_size();
inline void set_has_allocationstack();
inline void clear_has_allocationstack();
inline void set_has_jsobjectclassname();
inline void clear_has_jsobjectclassname();
::google::protobuf::UnknownFieldSet _unknown_fields_;
@ -509,6 +523,7 @@ class Node : public ::google::protobuf::Message {
::google::protobuf::uint64 size_;
::google::protobuf::RepeatedPtrField< ::mozilla::devtools::protobuf::Edge > edges_;
::mozilla::devtools::protobuf::StackFrame* allocationstack_;
::std::string* jsobjectclassname_;
friend void protobuf_AddDesc_CoreDump_2eproto();
friend void protobuf_AssignDesc_CoreDump_2eproto();
friend void protobuf_ShutdownFile_CoreDump_2eproto();
@ -1240,6 +1255,82 @@ inline void Node::set_allocated_allocationstack(::mozilla::devtools::protobuf::S
// @@protoc_insertion_point(field_set_allocated:mozilla.devtools.protobuf.Node.allocationStack)
}
// optional bytes jsObjectClassName = 6;
inline bool Node::has_jsobjectclassname() const {
return (_has_bits_[0] & 0x00000020u) != 0;
}
inline void Node::set_has_jsobjectclassname() {
_has_bits_[0] |= 0x00000020u;
}
inline void Node::clear_has_jsobjectclassname() {
_has_bits_[0] &= ~0x00000020u;
}
inline void Node::clear_jsobjectclassname() {
if (jsobjectclassname_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
jsobjectclassname_->clear();
}
clear_has_jsobjectclassname();
}
inline const ::std::string& Node::jsobjectclassname() const {
// @@protoc_insertion_point(field_get:mozilla.devtools.protobuf.Node.jsObjectClassName)
return *jsobjectclassname_;
}
inline void Node::set_jsobjectclassname(const ::std::string& value) {
set_has_jsobjectclassname();
if (jsobjectclassname_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
jsobjectclassname_ = new ::std::string;
}
jsobjectclassname_->assign(value);
// @@protoc_insertion_point(field_set:mozilla.devtools.protobuf.Node.jsObjectClassName)
}
inline void Node::set_jsobjectclassname(const char* value) {
set_has_jsobjectclassname();
if (jsobjectclassname_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
jsobjectclassname_ = new ::std::string;
}
jsobjectclassname_->assign(value);
// @@protoc_insertion_point(field_set_char:mozilla.devtools.protobuf.Node.jsObjectClassName)
}
inline void Node::set_jsobjectclassname(const void* value, size_t size) {
set_has_jsobjectclassname();
if (jsobjectclassname_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
jsobjectclassname_ = new ::std::string;
}
jsobjectclassname_->assign(reinterpret_cast<const char*>(value), size);
// @@protoc_insertion_point(field_set_pointer:mozilla.devtools.protobuf.Node.jsObjectClassName)
}
inline ::std::string* Node::mutable_jsobjectclassname() {
set_has_jsobjectclassname();
if (jsobjectclassname_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
jsobjectclassname_ = new ::std::string;
}
// @@protoc_insertion_point(field_mutable:mozilla.devtools.protobuf.Node.jsObjectClassName)
return jsobjectclassname_;
}
inline ::std::string* Node::release_jsobjectclassname() {
clear_has_jsobjectclassname();
if (jsobjectclassname_ == &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
return NULL;
} else {
::std::string* temp = jsobjectclassname_;
jsobjectclassname_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
return temp;
}
}
inline void Node::set_allocated_jsobjectclassname(::std::string* jsobjectclassname) {
if (jsobjectclassname_ != &::google::protobuf::internal::GetEmptyStringAlreadyInited()) {
delete jsobjectclassname_;
}
if (jsobjectclassname) {
set_has_jsobjectclassname();
jsobjectclassname_ = jsobjectclassname;
} else {
clear_has_jsobjectclassname();
jsobjectclassname_ = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyStringAlreadyInited());
}
// @@protoc_insertion_point(field_set_allocated:mozilla.devtools.protobuf.Node.jsObjectClassName)
}
// -------------------------------------------------------------------
// Edge

View File

@ -86,6 +86,8 @@ message Node {
optional uint64 size = 3;
repeated Edge edges = 4;
optional StackFrame allocationStack = 5;
// char[]
optional bytes jsObjectClassName = 6;
}
// A serialized edge from the heap graph.
@ -93,4 +95,4 @@ message Edge {
optional uint64 referent = 1;
// char16_t[]
optional bytes name = 2;
}
}

View File

@ -61,6 +61,7 @@ DeserializedNode::DeserializedNode(DeserializedNode&& rhs)
rhs.size = 0;
edges = Move(rhs.edges);
jsObjectClassName = Move(rhs.jsObjectClassName);
owner = rhs.owner;
rhs.owner = nullptr;

View File

@ -64,6 +64,7 @@ struct DeserializedNode {
uint64_t size;
EdgeVector edges;
Maybe<StackFrameId> allocationStack;
UniquePtr<char[]> jsObjectClassName;
// A weak pointer to this node's owning `HeapSnapshot`. Safe without
// AddRef'ing because this node's lifetime is equal to that of its owner.
HeapSnapshot* owner;
@ -73,12 +74,14 @@ struct DeserializedNode {
uint64_t size,
EdgeVector&& edges,
Maybe<StackFrameId> allocationStack,
UniquePtr<char[]>&& className,
HeapSnapshot& owner)
: id(id)
, typeName(typeName)
, size(size)
, edges(Move(edges))
, allocationStack(allocationStack)
, jsObjectClassName(Move(className))
, owner(&owner)
{ }
virtual ~DeserializedNode() { }
@ -226,6 +229,7 @@ public:
bool isLive() const override { return false; }
const char16_t* typeName() const override;
size_t size(mozilla::MallocSizeOf mallocSizeof) const override;
const char* jsObjectClassName() const override { return get().jsObjectClassName.get(); }
// We ignore the `bool wantNames` parameter because we can't control whether
// the core dump was serialized with edge names or not.

View File

@ -123,6 +123,10 @@ HeapSnapshot::saveNode(const protobuf::Node& node)
return false;
NodeId id = node.id();
// Should only deserialize each node once.
if (nodes.has(id))
return false;
if (!node.has_typename_())
return false;
@ -157,8 +161,21 @@ HeapSnapshot::saveNode(const protobuf::Node& node)
allocationStack = Some(id);
}
DeserializedNode dn(id, typeName, size, Move(edges), allocationStack, *this);
return nodes.putNew(id, Move(dn));
UniquePtr<char[]> jsObjectClassName;
if (node.has_jsobjectclassname()) {
auto length = node.jsobjectclassname().length();
jsObjectClassName.reset(static_cast<char*>(malloc(length + 1)));
if (!jsObjectClassName)
return false;
strncpy(jsObjectClassName.get(), node.jsobjectclassname().data(),
length);
jsObjectClassName.get()[length] = '\0';
}
return nodes.putNew(id, DeserializedNode(id, typeName, size, Move(edges),
allocationStack,
Move(jsObjectClassName),
*this));
}
bool
@ -594,6 +611,11 @@ public:
protobufNode.set_allocated_allocationstack(protoStackFrame);
}
if (auto className = ubiNode.jsObjectClassName()) {
size_t length = strlen(className);
protobufNode.set_jsobjectclassname(className, length);
}
if (includeEdges) {
auto edges = ubiNode.edges(cx, wantNames);
if (NS_WARN_IF(!edges))

View File

@ -38,10 +38,13 @@ size_t fakeMallocSizeOf(const void*) {
DEF_TEST(DeserializedNodeUbiNodes, {
const char16_t* typeName = MOZ_UTF16("TestTypeName");
const char* className = "MyObjectClassName";
NodeId id = 1L << 33;
uint64_t size = 1L << 60;
MockDeserializedNode mocked(id, typeName, size);
mocked.jsObjectClassName = mozilla::UniquePtr<char[]>(strdup(className));
ASSERT_TRUE(!!mocked.jsObjectClassName);
DeserializedNode& deserialized = mocked;
JS::ubi::Node ubi(&deserialized);
@ -52,6 +55,7 @@ DEF_TEST(DeserializedNodeUbiNodes, {
EXPECT_EQ(typeName, ubi.typeName());
EXPECT_EQ(id, ubi.identifier());
EXPECT_FALSE(ubi.isLive());
EXPECT_EQ(strcmp(ubi.jsObjectClassName(), className), 0);
// Test the ubi::Node's edges.