tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

DumpFunctions.cpp (18796B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      3 * This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "js/friend/DumpFunctions.h"
      8 
      9 #include "mozilla/Maybe.h"  // mozilla::Maybe
     10 
     11 #include <inttypes.h>  // PRIu64
     12 #include <stddef.h>    // size_t
     13 #include <stdio.h>     // fprintf, fflush
     14 
     15 #include "jsfriendapi.h"  // js::WeakMapTracer
     16 #include "jstypes.h"      // JS_PUBLIC_API
     17 
     18 #include "gc/Cell.h"              // js::gc::Cell, js::gc::TenuredCell
     19 #include "gc/GC.h"                // js::TraceRuntimeWithoutEviction
     20 #include "gc/GCEnum.h"            // js::CanGC
     21 #include "gc/Heap.h"              // js::gc::Arena
     22 #include "gc/Tracer.h"            // js::TraceChildren
     23 #include "gc/WeakMap.h"           // js::IterateHeapUnbarriered, js::WeakMapBase
     24 #include "js/CallAndConstruct.h"  // JS::IsCallable
     25 #include "js/ColumnNumber.h"      // JS::LimitedColumnNumberOneOrigin
     26 #include "js/GCAPI.h"             // JS::GCReason
     27 #include "js/GCVector.h"          // JS::RootedVector
     28 #include "js/HeapAPI.h"           // JS::GCCellPtr, js::gc::IsInsideNursery
     29 #include "js/Id.h"                // JS::PropertyKey
     30 #include "js/Printer.h"     // js::GenericPrinter, js::QuoteString, js::Sprinter
     31 #include "js/RootingAPI.h"  // JS::Handle, JS::Rooted
     32 #include "js/TracingAPI.h"  // JS::CallbackTracer, JS_GetTraceThingInfo
     33 #include "js/UbiNode.h"     // JS::ubi::Node
     34 #include "js/Value.h"       // JS::Value
     35 #include "js/Wrapper.h"     // js::UncheckedUnwrapWithoutExpose
     36 #include "vm/BigIntType.h"  // JS::BigInt::dump
     37 #include "vm/FrameIter.h"   // js::AllFramesIter, js::FrameIter
     38 #include "vm/Interpreter.h"  // GetFunctionThis
     39 #include "vm/JSContext.h"    // JSContext
     40 #include "vm/JSFunction.h"   // JSFunction
     41 #include "vm/JSObject.h"     // JSObject
     42 #include "vm/JSScript.h"     // JSScript
     43 #include "vm/Realm.h"        // JS::Realm
     44 #include "vm/Runtime.h"      // JSRuntime
     45 #include "vm/Scope.h"        // js::PositionalFormalParameterIter
     46 #include "vm/Stack.h"        // js::DONT_CHECK_ALIASING
     47 #include "vm/StringType.h"   // JSAtom, JSString, js::ToString
     48 
     49 #include "vm/JSObject-inl.h"  // js::IsCallable
     50 #include "vm/Realm-inl.h"     // js::AutoRealm
     51 
     52 namespace JS {
     53 
     54 class JS_PUBLIC_API AutoRequireNoGC;
     55 class JS_PUBLIC_API Zone;
     56 
     57 }  // namespace JS
     58 
     59 using JS::Handle;
     60 using JS::MagicValue;
     61 using JS::PropertyKey;
     62 using JS::Realm;
     63 using JS::Rooted;
     64 using JS::RootedVector;
     65 using JS::UniqueChars;
     66 using JS::Value;
     67 using JS::Zone;
     68 
     69 using js::AllFramesIter;
     70 using js::AutoRealm;
     71 using js::CanGC;
     72 using js::DONT_CHECK_ALIASING;
     73 using js::FrameIter;
     74 using js::PositionalFormalParameterIter;
     75 using js::Sprinter;
     76 using js::ToString;
     77 using js::UncheckedUnwrapWithoutExpose;
     78 using js::WeakMapTracer;
     79 
     80 // We don't want jsfriendapi.h to depend on GenericPrinter,
     81 // so these functions are declared directly in the cpp.
     82 
     83 namespace js {
     84 
     85 class InterpreterFrame;
     86 
     87 extern JS_PUBLIC_API void DumpString(JSString* str, GenericPrinter& out);
     88 
     89 extern JS_PUBLIC_API void DumpAtom(JSAtom* atom, GenericPrinter& out);
     90 
     91 extern JS_PUBLIC_API void DumpObject(JSObject* obj, GenericPrinter& out);
     92 
     93 extern JS_PUBLIC_API void DumpChars(const char16_t* s, size_t n,
     94                                    GenericPrinter& out);
     95 
     96 extern JS_PUBLIC_API void DumpValue(const JS::Value& val, GenericPrinter& out);
     97 
     98 extern JS_PUBLIC_API void DumpId(PropertyKey id, GenericPrinter& out);
     99 
    100 extern JS_PUBLIC_API void DumpInterpreterFrame(
    101    JSContext* cx, GenericPrinter& out, InterpreterFrame* start = nullptr);
    102 
    103 extern JS_PUBLIC_API void DumpBigInt(JS::BigInt* bi, GenericPrinter& out);
    104 
    105 }  // namespace js
    106 
    107 void js::DumpString(JSString* str, GenericPrinter& out) {
    108 #if defined(DEBUG) || defined(JS_JITSPEW)
    109  str->dump(out);
    110 #endif
    111 }
    112 
    113 void js::DumpAtom(JSAtom* atom, GenericPrinter& out) {
    114 #if defined(DEBUG) || defined(JS_JITSPEW)
    115  atom->dump(out);
    116 #endif
    117 }
    118 
    119 void js::DumpChars(const char16_t* s, size_t n, GenericPrinter& out) {
    120 #if defined(DEBUG) || defined(JS_JITSPEW)
    121  if (n == SIZE_MAX) {
    122    n = 0;
    123    while (s[n]) {
    124      n++;
    125    }
    126  }
    127 
    128  out.printf("char16_t * (%p) = \"", (void*)s);
    129  JSString::dumpCharsNoQuote(s, n, out);
    130  out.put("\"\n");
    131 #endif
    132 }
    133 
    134 void js::DumpObject(JSObject* obj, GenericPrinter& out) {
    135 #if defined(DEBUG) || defined(JS_JITSPEW)
    136  if (!obj) {
    137    out.printf("NULL\n");
    138    return;
    139  }
    140  obj->dump(out);
    141 #endif
    142 }
    143 
    144 void js::DumpBigInt(JS::BigInt* bi, GenericPrinter& out) {
    145 #if defined(DEBUG) || defined(JS_JITSPEW)
    146  bi->dump(out);
    147 #endif
    148 }
    149 
    150 void js::DumpString(JSString* str, FILE* fp) {
    151 #if defined(DEBUG) || defined(JS_JITSPEW)
    152  Fprinter out(fp);
    153  js::DumpString(str, out);
    154 #endif
    155 }
    156 
    157 void js::DumpAtom(JSAtom* atom, FILE* fp) {
    158 #if defined(DEBUG) || defined(JS_JITSPEW)
    159  Fprinter out(fp);
    160  js::DumpAtom(atom, out);
    161 #endif
    162 }
    163 
    164 void js::DumpChars(const char16_t* s, size_t n, FILE* fp) {
    165 #if defined(DEBUG) || defined(JS_JITSPEW)
    166  Fprinter out(fp);
    167  js::DumpChars(s, n, out);
    168 #endif
    169 }
    170 
    171 void js::DumpObject(JSObject* obj, FILE* fp) {
    172 #if defined(DEBUG) || defined(JS_JITSPEW)
    173  Fprinter out(fp);
    174  js::DumpObject(obj, out);
    175 #endif
    176 }
    177 
    178 void js::DumpBigInt(JS::BigInt* bi, FILE* fp) {
    179 #if defined(DEBUG) || defined(JS_JITSPEW)
    180  Fprinter out(fp);
    181  js::DumpBigInt(bi, out);
    182 #endif
    183 }
    184 
    185 void js::DumpId(PropertyKey id, FILE* fp) {
    186 #if defined(DEBUG) || defined(JS_JITSPEW)
    187  Fprinter out(fp);
    188  js::DumpId(id, out);
    189 #endif
    190 }
    191 
    192 void js::DumpValue(const JS::Value& val, FILE* fp) {
    193 #if defined(DEBUG) || defined(JS_JITSPEW)
    194  Fprinter out(fp);
    195  js::DumpValue(val, out);
    196 #endif
    197 }
    198 
    199 void js::DumpString(JSString* str) { DumpString(str, stderr); }
    200 void js::DumpAtom(JSAtom* atom) { DumpAtom(atom, stderr); }
    201 void js::DumpObject(JSObject* obj) { DumpObject(obj, stderr); }
    202 void js::DumpChars(const char16_t* s, size_t n) { DumpChars(s, n, stderr); }
    203 void js::DumpBigInt(JS::BigInt* bi) { DumpBigInt(bi, stderr); }
    204 void js::DumpValue(const JS::Value& val) { DumpValue(val, stderr); }
    205 void js::DumpId(PropertyKey id) { DumpId(id, stderr); }
    206 void js::DumpInterpreterFrame(JSContext* cx, InterpreterFrame* start) {
    207 #if defined(DEBUG) || defined(JS_JITSPEW)
    208  Fprinter out(stderr);
    209  DumpInterpreterFrame(cx, out, start);
    210 #endif
    211 }
    212 
    213 bool js::DumpPC(JSContext* cx) {
    214 #if defined(DEBUG) || defined(JS_JITSPEW)
    215  return DumpPC(cx, stdout);
    216 #else
    217  return true;
    218 #endif
    219 }
    220 
    221 bool js::DumpScript(JSContext* cx, JSScript* scriptArg) {
    222 #if defined(DEBUG) || defined(JS_JITSPEW)
    223  return DumpScript(cx, scriptArg, stdout);
    224 #else
    225  return true;
    226 #endif
    227 }
    228 
    229 static const char* FormatValue(JSContext* cx, Handle<Value> v,
    230                               UniqueChars& bytes) {
    231  if (v.isMagic()) {
    232    MOZ_ASSERT(v.whyMagic() == JS_OPTIMIZED_OUT ||
    233               v.whyMagic() == JS_UNINITIALIZED_LEXICAL);
    234    return "[unavailable]";
    235  }
    236 
    237  if (js::IsCallable(v)) {
    238    return "[function]";
    239  }
    240 
    241  if (v.isObject() && js::IsCrossCompartmentWrapper(&v.toObject())) {
    242    return "[cross-compartment wrapper]";
    243  }
    244 
    245  JSString* str;
    246  {
    247    mozilla::Maybe<AutoRealm> ar;
    248    if (v.isObject()) {
    249      ar.emplace(cx, &v.toObject());
    250    }
    251 
    252    str = ToString<CanGC>(cx, v);
    253    if (!str) {
    254      return nullptr;
    255    }
    256  }
    257 
    258  bytes = js::QuoteString(cx, str, '"');
    259  return bytes.get();
    260 }
    261 
    262 static bool FormatFrame(JSContext* cx, const FrameIter& iter, Sprinter& sp,
    263                        int num, bool showArgs, bool showLocals,
    264                        bool showThisProps) {
    265  MOZ_ASSERT(!cx->isExceptionPending());
    266  Rooted<JSScript*> script(cx, iter.script());
    267  jsbytecode* pc = iter.pc();
    268 
    269  Rooted<JSObject*> envChain(cx, iter.environmentChain(cx));
    270  JSAutoRealm ar(cx, envChain);
    271 
    272  const char* filename = script->filename();
    273  JS::LimitedColumnNumberOneOrigin column;
    274  unsigned lineno = PCToLineNumber(script, pc, &column);
    275  Rooted<JSFunction*> fun(cx, iter.maybeCallee(cx));
    276  Rooted<JSString*> funname(cx);
    277  if (fun) {
    278    funname = fun->fullDisplayAtom();
    279  }
    280 
    281  Rooted<Value> thisVal(cx);
    282  if (iter.hasUsableAbstractFramePtr() && iter.isFunctionFrame() && fun &&
    283      !fun->isArrow() && !fun->isDerivedClassConstructor()) {
    284    if (!GetFunctionThis(cx, iter.abstractFramePtr(), &thisVal)) {
    285      return false;
    286    }
    287  }
    288 
    289  // print the frame number and function name
    290  if (funname) {
    291    UniqueChars funbytes = js::QuoteString(cx, funname);
    292    if (!funbytes) {
    293      return false;
    294    }
    295    sp.printf("%d %s(", num, funbytes.get());
    296  } else if (fun) {
    297    sp.printf("%d anonymous(", num);
    298  } else {
    299    sp.printf("%d <TOP LEVEL>", num);
    300  }
    301 
    302  if (showArgs && iter.hasArgs()) {
    303    PositionalFormalParameterIter fi(script);
    304    bool first = true;
    305    for (unsigned i = 0; i < iter.numActualArgs(); i++) {
    306      Rooted<Value> arg(cx);
    307      if (i < iter.numFormalArgs() && fi.closedOver()) {
    308        if (iter.hasInitialEnvironment(cx)) {
    309          arg = iter.callObj(cx).aliasedBinding(fi);
    310        } else {
    311          arg = MagicValue(JS_OPTIMIZED_OUT);
    312        }
    313      } else if (iter.hasUsableAbstractFramePtr()) {
    314        if (script->argsObjAliasesFormals() && iter.hasArgsObj()) {
    315          arg = iter.argsObj().arg(i);
    316        } else {
    317          arg = iter.unaliasedActual(i, DONT_CHECK_ALIASING);
    318        }
    319      } else {
    320        arg = MagicValue(JS_OPTIMIZED_OUT);
    321      }
    322 
    323      UniqueChars valueBytes;
    324      const char* value = FormatValue(cx, arg, valueBytes);
    325      if (!value) {
    326        if (cx->isThrowingOutOfMemory()) {
    327          return false;
    328        }
    329        cx->clearPendingException();
    330      }
    331 
    332      UniqueChars nameBytes;
    333      const char* name = nullptr;
    334 
    335      if (i < iter.numFormalArgs()) {
    336        MOZ_ASSERT(fi.argumentSlot() == i);
    337        if (!fi.isDestructured()) {
    338          nameBytes = StringToNewUTF8CharsZ(cx, *fi.name());
    339          name = nameBytes.get();
    340          if (!name) {
    341            return false;
    342          }
    343        } else {
    344          name = "(destructured parameter)";
    345        }
    346        fi++;
    347      }
    348 
    349      if (value) {
    350        sp.printf("%s%s%s%s%s%s", !first ? ", " : "", name ? name : "",
    351                  name ? " = " : "", arg.isString() ? "\"" : "", value,
    352                  arg.isString() ? "\"" : "");
    353 
    354        first = false;
    355      } else {
    356        sp.put(
    357            "    <Failed to get argument while inspecting stack "
    358            "frame>\n");
    359      }
    360    }
    361  }
    362 
    363  // print filename, line number and column
    364  sp.printf("%s [\"%s\":%u:%u]\n", fun ? ")" : "",
    365            filename ? filename : "<unknown>", lineno, column.oneOriginValue());
    366 
    367  // Note: Right now we don't dump the local variables anymore, because
    368  // that is hard to support across all the JITs etc.
    369 
    370  // print the value of 'this'
    371  if (showLocals) {
    372    if (!thisVal.isUndefined()) {
    373      Rooted<JSString*> thisValStr(cx, ToString<CanGC>(cx, thisVal));
    374      if (!thisValStr) {
    375        if (cx->isThrowingOutOfMemory()) {
    376          return false;
    377        }
    378        cx->clearPendingException();
    379      }
    380      if (thisValStr) {
    381        UniqueChars thisValBytes = QuoteString(cx, thisValStr);
    382        if (!thisValBytes) {
    383          return false;
    384        }
    385        sp.printf("    this = %s\n", thisValBytes.get());
    386      } else {
    387        sp.put("    <failed to get 'this' value>\n");
    388      }
    389    }
    390  }
    391 
    392  if (showThisProps && thisVal.isObject()) {
    393    Rooted<JSObject*> obj(cx, &thisVal.toObject());
    394 
    395    RootedVector<PropertyKey> keys(cx);
    396    if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY, &keys)) {
    397      if (cx->isThrowingOutOfMemory()) {
    398        return false;
    399      }
    400      cx->clearPendingException();
    401    }
    402 
    403    for (size_t i = 0; i < keys.length(); i++) {
    404      Rooted<jsid> id(cx, keys[i]);
    405      Rooted<Value> key(cx, IdToValue(id));
    406      Rooted<Value> v(cx);
    407 
    408      if (!GetProperty(cx, obj, obj, id, &v)) {
    409        if (cx->isThrowingOutOfMemory()) {
    410          return false;
    411        }
    412        cx->clearPendingException();
    413        sp.put(
    414            "    <Failed to fetch property while inspecting stack "
    415            "frame>\n");
    416        continue;
    417      }
    418 
    419      UniqueChars nameBytes;
    420      const char* name = FormatValue(cx, key, nameBytes);
    421      if (!name) {
    422        if (cx->isThrowingOutOfMemory()) {
    423          return false;
    424        }
    425        cx->clearPendingException();
    426      }
    427 
    428      UniqueChars valueBytes;
    429      const char* value = FormatValue(cx, v, valueBytes);
    430      if (!value) {
    431        if (cx->isThrowingOutOfMemory()) {
    432          return false;
    433        }
    434        cx->clearPendingException();
    435      }
    436 
    437      if (name && value) {
    438        sp.printf("    this.%s = %s%s%s\n", name, v.isString() ? "\"" : "",
    439                  value, v.isString() ? "\"" : "");
    440      } else {
    441        sp.put(
    442            "    <Failed to format values while inspecting stack "
    443            "frame>\n");
    444      }
    445    }
    446  }
    447 
    448  MOZ_ASSERT(!cx->isExceptionPending());
    449  return true;
    450 }
    451 
    452 static bool FormatWasmFrame(JSContext* cx, const FrameIter& iter, Sprinter& sp,
    453                            int num) {
    454  UniqueChars nameStr;
    455  if (JSAtom* functionDisplayAtom = iter.maybeFunctionDisplayAtom()) {
    456    nameStr = StringToNewUTF8CharsZ(cx, *functionDisplayAtom);
    457    if (!nameStr) {
    458      return false;
    459    }
    460  }
    461 
    462  sp.printf("%d %s()", num, nameStr ? nameStr.get() : "<wasm-function>");
    463  sp.printf(" [\"%s\":wasm-function[%u]:0x%x]\n",
    464            iter.filename() ? iter.filename() : "<unknown>",
    465            iter.wasmFuncIndex(), iter.wasmBytecodeOffset());
    466 
    467  MOZ_ASSERT(!cx->isExceptionPending());
    468  return true;
    469 }
    470 
    471 JS::UniqueChars JS::FormatStackDump(JSContext* cx, bool showArgs,
    472                                    bool showLocals, bool showThisProps) {
    473  int num = 0;
    474 
    475  Sprinter sp(cx);
    476  if (!sp.init()) {
    477    return nullptr;
    478  }
    479 
    480  for (AllFramesIter i(cx); !i.done(); ++i) {
    481    bool ok = i.hasScript() ? FormatFrame(cx, i, sp, num, showArgs, showLocals,
    482                                          showThisProps)
    483                            : FormatWasmFrame(cx, i, sp, num);
    484    if (!ok) {
    485      return nullptr;
    486    }
    487    num++;
    488  }
    489 
    490  if (num == 0) {
    491    sp.put("JavaScript stack is empty\n");
    492  }
    493 
    494  return sp.release();
    495 }
    496 
    497 struct DumpHeapTracer final : public JS::CallbackTracer, public WeakMapTracer {
    498  const char* prefix;
    499  FILE* output;
    500  mozilla::MallocSizeOf mallocSizeOf;
    501 
    502  DumpHeapTracer(FILE* fp, JSContext* cx, mozilla::MallocSizeOf mallocSizeOf)
    503      : JS::CallbackTracer(cx, JS::TracerKind::Callback,
    504                           JS::WeakMapTraceAction::Skip),
    505        WeakMapTracer(cx->runtime()),
    506        prefix(""),
    507        output(fp),
    508        mallocSizeOf(mallocSizeOf) {}
    509 
    510 private:
    511  void trace(JSObject* map, JS::GCCellPtr key, JS::GCCellPtr value) override {
    512    JSObject* kdelegate = nullptr;
    513    if (key.is<JSObject>()) {
    514      kdelegate = UncheckedUnwrapWithoutExpose(&key.as<JSObject>());
    515    }
    516 
    517    fprintf(output, "WeakMapEntry map=%p key=%p keyDelegate=%p value=%p\n", map,
    518            key.asCell(), kdelegate, value.asCell());
    519  }
    520 
    521  void onChild(JS::GCCellPtr thing, const char* name) override;
    522 };
    523 
    524 static char MarkDescriptor(js::gc::Cell* thing) {
    525  js::gc::TenuredCell* cell = &thing->asTenured();
    526  if (cell->isMarkedBlack()) {
    527    return 'B';
    528  }
    529  if (cell->isMarkedGray()) {
    530    return 'G';
    531  }
    532  if (cell->isMarkedAny()) {
    533    return 'X';
    534  }
    535  return 'W';
    536 }
    537 
    538 static void DumpHeapVisitZone(JSRuntime* rt, void* data, Zone* zone,
    539                              const JS::AutoRequireNoGC& nogc) {
    540  DumpHeapTracer* dtrc = static_cast<DumpHeapTracer*>(data);
    541  fprintf(dtrc->output, "# zone %p\n", static_cast<void*>(zone));
    542 }
    543 
    544 static void DumpHeapVisitRealm(JSContext* cx, void* data, Realm* realm,
    545                               const JS::AutoRequireNoGC& nogc) {
    546  char name[1024];
    547  if (auto nameCallback = cx->runtime()->realmNameCallback) {
    548    nameCallback(cx, realm, name, sizeof(name), nogc);
    549  } else {
    550    strcpy(name, "<unknown>");
    551  }
    552 
    553  DumpHeapTracer* dtrc = static_cast<DumpHeapTracer*>(data);
    554  fprintf(dtrc->output, "# realm %s [in compartment %p, zone %p]\n", name,
    555          static_cast<void*>(realm->compartment()),
    556          static_cast<void*>(realm->zone()));
    557 }
    558 
    559 static void DumpHeapVisitArena(JSRuntime* rt, void* data, js::gc::Arena* arena,
    560                               JS::TraceKind traceKind, size_t thingSize,
    561                               const JS::AutoRequireNoGC& nogc) {
    562  DumpHeapTracer* dtrc = static_cast<DumpHeapTracer*>(data);
    563  fprintf(dtrc->output, "# arena allockind=%u size=%u\n",
    564          unsigned(arena->getAllocKind()), unsigned(thingSize));
    565 }
    566 
    567 static void DumpHeapVisitCell(JSRuntime* rt, void* data, JS::GCCellPtr cellptr,
    568                              size_t thingSize,
    569                              const JS::AutoRequireNoGC& nogc) {
    570  DumpHeapTracer* dtrc = static_cast<DumpHeapTracer*>(data);
    571  char cellDesc[1024 * 32];
    572  js::gc::GetTraceThingInfo(cellDesc, sizeof(cellDesc), cellptr.asCell(),
    573                            cellptr.kind(), true);
    574 
    575  fprintf(dtrc->output, "%p %c %s", cellptr.asCell(),
    576          MarkDescriptor(cellptr.asCell()), cellDesc);
    577  if (dtrc->mallocSizeOf) {
    578    auto size = JS::ubi::Node(cellptr).size(dtrc->mallocSizeOf);
    579    fprintf(dtrc->output, " SIZE:: %" PRIu64 "\n", size);
    580  } else {
    581    fprintf(dtrc->output, "\n");
    582  }
    583 
    584  JS::TraceChildren(dtrc, cellptr);
    585 }
    586 
    587 void DumpHeapTracer::onChild(JS::GCCellPtr thing, const char* name) {
    588  if (js::gc::IsInsideNursery(thing.asCell())) {
    589    return;
    590  }
    591 
    592  char buffer[1024];
    593  context().getEdgeName(name, buffer, sizeof(buffer));
    594  fprintf(output, "%s%p %c %s\n", prefix, thing.asCell(),
    595          MarkDescriptor(thing.asCell()), buffer);
    596 }
    597 
    598 void js::DumpHeap(JSContext* cx, FILE* fp,
    599                  DumpHeapNurseryBehaviour nurseryBehaviour,
    600                  mozilla::MallocSizeOf mallocSizeOf) {
    601  if (nurseryBehaviour == CollectNurseryBeforeDump) {
    602    cx->runtime()->gc.evictNursery(JS::GCReason::API);
    603  }
    604 
    605  DumpHeapTracer dtrc(fp, cx, mallocSizeOf);
    606 
    607  fprintf(dtrc.output, "# Roots.\n");
    608  TraceRuntimeWithoutEviction(&dtrc);
    609 
    610  fprintf(dtrc.output, "# Weak maps.\n");
    611  WeakMapBase::traceAllMappings(&dtrc);
    612 
    613  fprintf(dtrc.output, "==========\n");
    614 
    615  dtrc.prefix = "> ";
    616  gc::AutoPrepareForTracing session(cx);
    617  IterateHeapUnbarriered(cx, &dtrc, DumpHeapVisitZone, DumpHeapVisitRealm,
    618                         DumpHeapVisitArena, DumpHeapVisitCell, session);
    619 
    620  fflush(dtrc.output);
    621 }
    622 
    623 void DumpFmtV(FILE* fp, const char* fmt, va_list args) {
    624  js::Fprinter out(fp);
    625  out.vprintf(fmt, args);
    626 }
    627 
    628 void js::DumpFmt(FILE* fp, const char* fmt, ...) {
    629  va_list args;
    630  va_start(args, fmt);
    631  DumpFmtV(fp, fmt, args);
    632  va_end(args);
    633 }
    634 
    635 void js::DumpFmt(const char* fmt, ...) {
    636  va_list args;
    637  va_start(args, fmt);
    638  DumpFmtV(stderr, fmt, args);
    639  va_end(args);
    640 }