Bug 1395509 - Track malloc memory used by TypedObject trace lists r=sfink
authorJon Coppeard <jcoppeard@mozilla.com>
Wed, 12 Jun 2019 16:23:04 +0100
changeset 479066 19f38c9cdd671aac3fc57ccde19560f337d7d992
parent 479065 219909ea3e4f3888a50771d86ad0a853a549fe39
child 479067 51440617724b12fec25ba05b0b2e25241c92683e
push id88046
push usershindli@mozilla.com
push dateSat, 15 Jun 2019 21:52:09 +0000
treeherderautoland@556f3b5ab4c5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink
bugs1395509
milestone69.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1395509 - Track malloc memory used by TypedObject trace lists r=sfink This changes the format of the trace list from using -1 as a delimter to storing the list lengths up front so that we have length information. Differential Revision: https://phabricator.services.mozilla.com/D34731
js/src/builtin/TypedObject.cpp
js/src/builtin/TypedObject.h
js/src/gc/GCEnum.h
js/src/gc/Marking.cpp
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -2852,17 +2852,17 @@ void TypeDescr::traceInstances(JSTracer*
 namespace {
 
 struct TraceListVisitor {
   typedef Vector<int32_t, 0, SystemAllocPolicy> VectorType;
   VectorType stringOffsets, objectOffsets, valueOffsets;
 
   void visitReference(ReferenceTypeDescr& descr, uint8_t* mem);
 
-  bool fillList(Vector<int32_t>& entries);
+  bool fillList(Vector<uint32_t>& entries);
 };
 
 }  // namespace
 
 void TraceListVisitor::visitReference(ReferenceTypeDescr& descr, uint8_t* mem) {
   VectorType* offsets;
   // TODO/AnyRef-boxing: Once a WasmAnyRef is no longer just a JSObject*
   // we must revisit this structure.
@@ -2884,55 +2884,62 @@ void TraceListVisitor::visitReference(Re
   }
 
   AutoEnterOOMUnsafeRegion oomUnsafe;
   if (!offsets->append((uintptr_t)mem)) {
     oomUnsafe.crash("TraceListVisitor::visitReference");
   }
 }
 
-bool TraceListVisitor::fillList(Vector<int32_t>& entries) {
-  return entries.appendAll(stringOffsets) && entries.append(-1) &&
-         entries.appendAll(objectOffsets) && entries.append(-1) &&
-         entries.appendAll(valueOffsets) && entries.append(-1);
+bool TraceListVisitor::fillList(Vector<uint32_t>& entries) {
+  return entries.append(stringOffsets.length()) &&
+         entries.append(objectOffsets.length()) &&
+         entries.append(valueOffsets.length()) &&
+         entries.appendAll(stringOffsets) && entries.appendAll(objectOffsets) &&
+         entries.appendAll(valueOffsets);
 }
 
 static bool CreateTraceList(JSContext* cx, HandleTypeDescr descr) {
   // Trace lists are only used for inline typed objects. We don't use them
   // for larger objects, both to limit the size of the trace lists and
   // because tracing outline typed objects is considerably more complicated
   // than inline ones.
   if (!InlineTypedObject::canAccommodateType(descr) || descr->transparent()) {
     return true;
   }
 
   TraceListVisitor visitor;
   visitReferences(*descr, nullptr, visitor);
 
-  Vector<int32_t> entries(cx);
+  Vector<uint32_t> entries(cx);
   if (!visitor.fillList(entries)) {
     return false;
   }
 
   // Trace lists aren't necessary for descriptors with no references.
   MOZ_ASSERT(entries.length() >= 3);
   if (entries.length() == 3) {
+    MOZ_ASSERT(entries[0] == 0 && entries[1] == 0 && entries[2] == 0);
     return true;
   }
 
-  int32_t* list = cx->pod_malloc<int32_t>(entries.length());
+  uint32_t* list = cx->pod_malloc<uint32_t>(entries.length());
   if (!list) {
     return false;
   }
 
   PodCopy(list, entries.begin(), entries.length());
 
-  descr->initReservedSlot(JS_DESCR_SLOT_TRACE_LIST, PrivateValue(list));
+  size_t size = entries.length() * sizeof(uint32_t);
+  InitReservedSlot(descr, JS_DESCR_SLOT_TRACE_LIST, list, size,
+                   MemoryUse::TypeDescrTraceList);
   return true;
 }
 
 /* static */
 void TypeDescr::finalize(FreeOp* fop, JSObject* obj) {
   TypeDescr& descr = obj->as<TypeDescr>();
   if (descr.hasTraceList()) {
-    fop->free_(const_cast<int32_t*>(descr.traceList()));
+    auto list = const_cast<uint32_t*>(descr.traceList());
+    size_t size = (3 + list[0] + list[1] + list[2]) * sizeof(uint32_t);
+    fop->free_(obj, list, size, MemoryUse::TypeDescrTraceList);
   }
 }
--- a/js/src/builtin/TypedObject.h
+++ b/js/src/builtin/TypedObject.h
@@ -179,27 +179,28 @@ class TypeDescr : public NativeObject {
 
   // Type descriptors may contain a list of their references for use during
   // scanning. Marking code is optimized to use this list to mark inline
   // typed objects, rather than the slower trace hook. This list is only
   // specified when (a) the descriptor is short enough that it can fit in an
   // InlineTypedObject, and (b) the descriptor contains at least one
   // reference. Otherwise its value is undefined.
   //
-  // The list is three consecutive arrays of int32_t offsets, with each array
-  // terminated by -1. The arrays store offsets of string, object/anyref, and
-  // value references in the descriptor, in that order.
+  // The list is three consecutive arrays of uint32_t offsets, preceded by a
+  // header consisting of the length of each array. The arrays store offsets of
+  // string, object/anyref, and value references in the descriptor, in that
+  // order.
   // TODO/AnyRef-boxing: once anyref has a more complicated structure, we must
   // revisit this.
   MOZ_MUST_USE bool hasTraceList() const {
     return !getFixedSlot(JS_DESCR_SLOT_TRACE_LIST).isUndefined();
   }
-  const int32_t* traceList() const {
+  const uint32_t* traceList() const {
     MOZ_ASSERT(hasTraceList());
-    return reinterpret_cast<int32_t*>(
+    return reinterpret_cast<uint32_t*>(
         getFixedSlot(JS_DESCR_SLOT_TRACE_LIST).toPrivate());
   }
 
   void initInstances(const JSRuntime* rt, uint8_t* mem, size_t length);
   void traceInstances(JSTracer* trace, uint8_t* mem, size_t length);
 
   static void finalize(FreeOp* fop, JSObject* obj);
 };
--- a/js/src/gc/GCEnum.h
+++ b/js/src/gc/GCEnum.h
@@ -106,17 +106,18 @@ enum class ZealMode {
   _(ShapeCache)                            \
   _(ModuleBindingMap)                      \
   _(BaselineScript)                        \
   _(IonScript)                             \
   _(ArgumentsData)                         \
   _(RareArgumentsData)                     \
   _(RegExpStatics)                         \
   _(RegExpSharedBytecode)                  \
-  _(TypedArrayElements)
+  _(TypedArrayElements)                    \
+  _(TypeDescrTraceList)
 
 #define JS_FOR_EACH_MEMORY_USE(_)  \
   JS_FOR_EACH_PUBLIC_MEMORY_USE(_) \
   JS_FOR_EACH_INTERNAL_MEMORY_USE(_)
 
 enum class MemoryUse : uint8_t {
 #define DEFINE_MEMORY_USE(Name) Name,
   JS_FOR_EACH_MEMORY_USE(DEFINE_MEMORY_USE)
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -1404,17 +1404,17 @@ void js::GCMarker::lazilyMarkChildren(Ob
   if (JSFunction* fun = group->maybeInterpretedFunction()) {
     traverseEdge(group, static_cast<JSObject*>(fun));
   }
 }
 
 void JS::BigInt::traceChildren(JSTracer* trc) { return; }
 
 template <typename Functor>
-static void VisitTraceList(const Functor& f, const int32_t* traceList,
+static void VisitTraceList(const Functor& f, const uint32_t* traceList,
                            uint8_t* memory);
 
 // Call the trace hook set on the object, if present. If further tracing of
 // NativeObject fields is required, this will return the native object.
 enum class CheckGeneration { DoChecks, NoChecks };
 template <typename Functor>
 static inline NativeObject* CallTraceHook(Functor&& f, JSTracer* trc,
                                           JSObject* obj,
@@ -1444,32 +1444,33 @@ static inline NativeObject* CallTraceHoo
 
   if (!clasp->isNative()) {
     return nullptr;
   }
   return &obj->as<NativeObject>();
 }
 
 template <typename Functor>
-static void VisitTraceList(const Functor& f, const int32_t* traceList,
+static void VisitTraceList(const Functor& f, const uint32_t* traceList,
                            uint8_t* memory) {
-  while (*traceList != -1) {
+  size_t stringCount = *traceList++;
+  size_t objectCount = *traceList++;
+  size_t valueCount = *traceList++;
+  for (size_t i = 0; i < stringCount; i++) {
     f(reinterpret_cast<JSString**>(memory + *traceList));
     traceList++;
   }
-  traceList++;
-  while (*traceList != -1) {
+  for (size_t i = 0; i < objectCount; i++) {
     JSObject** objp = reinterpret_cast<JSObject**>(memory + *traceList);
     if (*objp) {
       f(objp);
     }
     traceList++;
   }
-  traceList++;
-  while (*traceList != -1) {
+  for (size_t i = 0; i < valueCount; i++) {
     f(reinterpret_cast<Value*>(memory + *traceList));
     traceList++;
   }
 }
 
 /*** Mark-stack Marking *****************************************************/
 
 GCMarker::MarkQueueProgress GCMarker::processMarkQueue() {