Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  DumpFunctions.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * 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 "js/friend/DumpFunctions.h"

#include "mozilla/Maybe.h"  // mozilla::Maybe

#include <inttypes.h>  // PRIu64
#include <stddef.h>    // size_t
#include <stdio.h>     // fprintf, fflush

#include "jsfriendapi.h"  // js::WeakMapTracer
#include "jstypes.h"      // JS_PUBLIC_API

#include "gc/Cell.h"              // js::gc::Cell, js::gc::TenuredCell
#include "gc/GC.h"                // js::TraceRuntimeWithoutEviction
#include "gc/GCEnum.h"            // js::CanGC
#include "gc/Heap.h"              // js::gc::Arena
#include "gc/Tracer.h"            // js::TraceChildren
#include "gc/WeakMap.h"           // js::IterateHeapUnbarriered, js::WeakMapBase
#include "js/CallAndConstruct.h"  // JS::IsCallable
#include "js/ColumnNumber.h"      // JS::LimitedColumnNumberOneOrigin
#include "js/GCAPI.h"             // JS::GCReason
#include "js/GCVector.h"          // JS::RootedVector
#include "js/HeapAPI.h"           // JS::GCCellPtr, js::gc::IsInsideNursery
#include "js/Id.h"                // JS::PropertyKey
#include "js/Printer.h"     // js::GenericPrinter, js::QuoteString, js::Sprinter
#include "js/RootingAPI.h"  // JS::Handle, JS::Rooted
#include "js/TracingAPI.h"  // JS::CallbackTracer, JS_GetTraceThingInfo
#include "js/UbiNode.h"     // JS::ubi::Node
#include "js/Value.h"       // JS::Value
#include "js/Wrapper.h"     // js::UncheckedUnwrapWithoutExpose
#include "vm/BigIntType.h"  // JS::BigInt::dump
#include "vm/FrameIter.h"   // js::AllFramesIter, js::FrameIter
#include "vm/Interpreter.h"  // GetFunctionThis
#include "vm/JSContext.h"    // JSContext
#include "vm/JSFunction.h"   // JSFunction
#include "vm/JSObject.h"     // JSObject
#include "vm/JSScript.h"     // JSScript
#include "vm/Realm.h"        // JS::Realm
#include "vm/Runtime.h"      // JSRuntime
#include "vm/Scope.h"        // js::PositionalFormalParameterIter
#include "vm/Stack.h"        // js::DONT_CHECK_ALIASING
#include "vm/StringType.h"   // JSAtom, JSString, js::ToString

#include "vm/JSObject-inl.h"  // js::IsCallable
#include "vm/Realm-inl.h"     // js::AutoRealm

namespace JS {

class JS_PUBLIC_API AutoRequireNoGC;
class JS_PUBLIC_API Zone;

}  // namespace JS

using JS::Handle;
using JS::MagicValue;
using JS::PropertyKey;
using JS::Realm;
using JS::Rooted;
using JS::RootedVector;
using JS::UniqueChars;
using JS::Value;
using JS::Zone;

using js::AllFramesIter;
using js::AutoRealm;
using js::CanGC;
using js::DONT_CHECK_ALIASING;
using js::FrameIter;
using js::PositionalFormalParameterIter;
using js::Sprinter;
using js::ToString;
using js::UncheckedUnwrapWithoutExpose;
using js::WeakMapTracer;

// We don't want jsfriendapi.h to depend on GenericPrinter,
// so these functions are declared directly in the cpp.

namespace js {

class InterpreterFrame;

extern JS_PUBLIC_API void DumpString(JSString* str, GenericPrinter& out);

extern JS_PUBLIC_API void DumpAtom(JSAtom* atom, GenericPrinter& out);

extern JS_PUBLIC_API void DumpObject(JSObject* obj, GenericPrinter& out);

extern JS_PUBLIC_API void DumpChars(const char16_t* s, size_t n,
                                    GenericPrinter& out);

extern JS_PUBLIC_API void DumpValue(const JS::Value& val, GenericPrinter& out);

extern JS_PUBLIC_API void DumpId(PropertyKey id, GenericPrinter& out);

extern JS_PUBLIC_API void DumpInterpreterFrame(
    JSContext* cx, GenericPrinter& out, InterpreterFrame* start = nullptr);

extern JS_PUBLIC_API void DumpBigInt(JS::BigInt* bi, GenericPrinter& out);

}  // namespace js

void js::DumpString(JSString* str, GenericPrinter& out) {
#if defined(DEBUG) || defined(JS_JITSPEW)
  str->dump(out);
#endif
}

void js::DumpAtom(JSAtom* atom, GenericPrinter& out) {
#if defined(DEBUG) || defined(JS_JITSPEW)
  atom->dump(out);
#endif
}

void js::DumpChars(const char16_t* s, size_t n, GenericPrinter& out) {
#if defined(DEBUG) || defined(JS_JITSPEW)
  if (n == SIZE_MAX) {
    n = 0;
    while (s[n]) {
      n++;
    }
  }

  out.printf("char16_t * (%p) = \"", (void*)s);
  JSString::dumpCharsNoQuote(s, n, out);
  out.put("\"\n");
#endif
}

void js::DumpObject(JSObject* obj, GenericPrinter& out) {
#if defined(DEBUG) || defined(JS_JITSPEW)
  if (!obj) {
    out.printf("NULL\n");
    return;
  }
  obj->dump(out);
#endif
}

void js::DumpBigInt(JS::BigInt* bi, GenericPrinter& out) {
#if defined(DEBUG) || defined(JS_JITSPEW)
  bi->dump(out);
#endif
}

void js::DumpString(JSString* str, FILE* fp) {
#if defined(DEBUG) || defined(JS_JITSPEW)
  Fprinter out(fp);
  js::DumpString(str, out);
#endif
}

void js::DumpAtom(JSAtom* atom, FILE* fp) {
#if defined(DEBUG) || defined(JS_JITSPEW)
  Fprinter out(fp);
  js::DumpAtom(atom, out);
#endif
}

void js::DumpChars(const char16_t* s, size_t n, FILE* fp) {
#if defined(DEBUG) || defined(JS_JITSPEW)
  Fprinter out(fp);
  js::DumpChars(s, n, out);
#endif
}

void js::DumpObject(JSObject* obj, FILE* fp) {
#if defined(DEBUG) || defined(JS_JITSPEW)
  Fprinter out(fp);
  js::DumpObject(obj, out);
#endif
}

void js::DumpBigInt(JS::BigInt* bi, FILE* fp) {
#if defined(DEBUG) || defined(JS_JITSPEW)
  Fprinter out(fp);
  js::DumpBigInt(bi, out);
#endif
}

void js::DumpId(PropertyKey id, FILE* fp) {
#if defined(DEBUG) || defined(JS_JITSPEW)
  Fprinter out(fp);
  js::DumpId(id, out);
#endif
}

void js::DumpValue(const JS::Value& val, FILE* fp) {
#if defined(DEBUG) || defined(JS_JITSPEW)
  Fprinter out(fp);
  js::DumpValue(val, out);
#endif
}

void js::DumpString(JSString* str) { DumpString(str, stderr); }
void js::DumpAtom(JSAtom* atom) { DumpAtom(atom, stderr); }
void js::DumpObject(JSObject* obj) { DumpObject(obj, stderr); }
void js::DumpChars(const char16_t* s, size_t n) { DumpChars(s, n, stderr); }
void js::DumpBigInt(JS::BigInt* bi) { DumpBigInt(bi, stderr); }
void js::DumpValue(const JS::Value& val) { DumpValue(val, stderr); }
void js::DumpId(PropertyKey id) { DumpId(id, stderr); }
void js::DumpInterpreterFrame(JSContext* cx, InterpreterFrame* start) {
#if defined(DEBUG) || defined(JS_JITSPEW)
  Fprinter out(stderr);
  DumpInterpreterFrame(cx, out, start);
#endif
}

bool js::DumpPC(JSContext* cx) {
#if defined(DEBUG) || defined(JS_JITSPEW)
  return DumpPC(cx, stdout);
#else
  return true;
#endif
}

bool js::DumpScript(JSContext* cx, JSScript* scriptArg) {
#if defined(DEBUG) || defined(JS_JITSPEW)
  return DumpScript(cx, scriptArg, stdout);
#else
  return true;
#endif
}

static const char* FormatValue(JSContext* cx, Handle<Value> v,
                               UniqueChars& bytes) {
  if (v.isMagic()) {
    MOZ_ASSERT(v.whyMagic() == JS_OPTIMIZED_OUT ||
               v.whyMagic() == JS_UNINITIALIZED_LEXICAL);
    return "[unavailable]";
  }

  if (js::IsCallable(v)) {
    return "[function]";
  }

  if (v.isObject() && js::IsCrossCompartmentWrapper(&v.toObject())) {
    return "[cross-compartment wrapper]";
  }

  JSString* str;
  {
    mozilla::Maybe<AutoRealm> ar;
    if (v.isObject()) {
      ar.emplace(cx, &v.toObject());
    }

    str = ToString<CanGC>(cx, v);
    if (!str) {
      return nullptr;
    }
  }

  bytes = js::QuoteString(cx, str, '"');
  return bytes.get();
}

static bool FormatFrame(JSContext* cx, const FrameIter& iter, Sprinter& sp,
                        int num, bool showArgs, bool showLocals,
                        bool showThisProps) {
  MOZ_ASSERT(!cx->isExceptionPending());
  Rooted<JSScript*> script(cx, iter.script());
  jsbytecode* pc = iter.pc();

  Rooted<JSObject*> envChain(cx, iter.environmentChain(cx));
  JSAutoRealm ar(cx, envChain);

  const char* filename = script->filename();
  JS::LimitedColumnNumberOneOrigin column;
  unsigned lineno = PCToLineNumber(script, pc, &column);
  Rooted<JSFunction*> fun(cx, iter.maybeCallee(cx));
  Rooted<JSString*> funname(cx);
  if (fun) {
    funname = fun->fullDisplayAtom();
  }

  Rooted<Value> thisVal(cx);
  if (iter.hasUsableAbstractFramePtr() && iter.isFunctionFrame() && fun &&
      !fun->isArrow() && !fun->isDerivedClassConstructor()) {
    if (!GetFunctionThis(cx, iter.abstractFramePtr(), &thisVal)) {
      return false;
    }
  }

  // print the frame number and function name
  if (funname) {
    UniqueChars funbytes = js::QuoteString(cx, funname);
    if (!funbytes) {
      return false;
    }
    sp.printf("%d %s(", num, funbytes.get());
  } else if (fun) {
    sp.printf("%d anonymous(", num);
  } else {
    sp.printf("%d ", num);
  }

  if (showArgs && iter.hasArgs()) {
    PositionalFormalParameterIter fi(script);
    bool first = true;
    for (unsigned i = 0; i < iter.numActualArgs(); i++) {
      Rooted<Value> arg(cx);
      if (i < iter.numFormalArgs() && fi.closedOver()) {
        if (iter.hasInitialEnvironment(cx)) {
          arg = iter.callObj(cx).aliasedBinding(fi);
        } else {
          arg = MagicValue(JS_OPTIMIZED_OUT);
        }
      } else if (iter.hasUsableAbstractFramePtr()) {
        if (script->argsObjAliasesFormals() && iter.hasArgsObj()) {
          arg = iter.argsObj().arg(i);
        } else {
          arg = iter.unaliasedActual(i, DONT_CHECK_ALIASING);
        }
      } else {
        arg = MagicValue(JS_OPTIMIZED_OUT);
      }

      UniqueChars valueBytes;
      const char* value = FormatValue(cx, arg, valueBytes);
      if (!value) {
        if (cx->isThrowingOutOfMemory()) {
          return false;
        }
        cx->clearPendingException();
      }

      UniqueChars nameBytes;
      const char* name = nullptr;

      if (i < iter.numFormalArgs()) {
        MOZ_ASSERT(fi.argumentSlot() == i);
        if (!fi.isDestructured()) {
          nameBytes = StringToNewUTF8CharsZ(cx, *fi.name());
          name = nameBytes.get();
          if (!name) {
            return false;
          }
        } else {
          name = "(destructured parameter)";
        }
        fi++;
      }

      if (value) {
        sp.printf("%s%s%s%s%s%s", !first ? ", " : "", name ? name : "",
                  name ? " = " : "", arg.isString() ? "\"" : "", value,
                  arg.isString() ? "\"" : "");

        first = false;
      } else {
        sp.put(
            "
            "frame>\n");
      }
    }
  }

  // print filename, line number and column
  sp.printf("%s [\"%s\":%u:%u]\n", fun ? ")" : "",
            filename ? filename : "", lineno, column.oneOriginValue());

  // Note: Right now we don't dump the local variables anymore, because
  // that is hard to support across all the JITs etc.

  // print the value of 'this'
  if (showLocals) {
    if (!thisVal.isUndefined()) {
      Rooted<JSString*> thisValStr(cx, ToString<CanGC>(cx, thisVal));
      if (!thisValStr) {
        if (cx->isThrowingOutOfMemory()) {
          return false;
        }
        cx->clearPendingException();
      }
      if (thisValStr) {
        UniqueChars thisValBytes = QuoteString(cx, thisValStr);
        if (!thisValBytes) {
          return false;
        }
        sp.printf(" this = %s\n", thisValBytes.get());
      } else {
        sp.put(" \n");
      }
    }
  }

  if (showThisProps && thisVal.isObject()) {
    Rooted<JSObject*> obj(cx, &thisVal.toObject());

    RootedVector<PropertyKey> keys(cx);
    if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY, &keys)) {
      if (cx->isThrowingOutOfMemory()) {
        return false;
      }
      cx->clearPendingException();
    }

    for (size_t i = 0; i < keys.length(); i++) {
      Rooted<jsid> id(cx, keys[i]);
      Rooted<Value> key(cx, IdToValue(id));
      Rooted<Value> v(cx);

      if (!GetProperty(cx, obj, obj, id, &v)) {
        if (cx->isThrowingOutOfMemory()) {
          return false;
        }
        cx->clearPendingException();
        sp.put(
            "
            "frame>\n");
        continue;
      }

      UniqueChars nameBytes;
      const char* name = FormatValue(cx, key, nameBytes);
      if (!name) {
        if (cx->isThrowingOutOfMemory()) {
          return false;
        }
        cx->clearPendingException();
      }

      UniqueChars valueBytes;
      const char* value = FormatValue(cx, v, valueBytes);
      if (!value) {
        if (cx->isThrowingOutOfMemory()) {
          return false;
        }
        cx->clearPendingException();
      }

      if (name && value) {
        sp.printf(" this.%s = %s%s%s\n", name, v.isString() ? "\"" : "",
                  value, v.isString() ? "\"" : "");
      } else {
        sp.put(
            "
            "frame>\n");
      }
    }
  }

  MOZ_ASSERT(!cx->isExceptionPending());
  return true;
}

static bool FormatWasmFrame(JSContext* cx, const FrameIter& iter, Sprinter& sp,
                            int num) {
  UniqueChars nameStr;
  if (JSAtom* functionDisplayAtom = iter.maybeFunctionDisplayAtom()) {
    nameStr = StringToNewUTF8CharsZ(cx, *functionDisplayAtom);
    if (!nameStr) {
      return false;
    }
  }

  sp.printf("%d %s()", num, nameStr ? nameStr.get() : "");
  sp.printf(" [\"%s\":wasm-function[%u]:0x%x]\n",
            iter.filename() ? iter.filename() : "",
            iter.wasmFuncIndex(), iter.wasmBytecodeOffset());

  MOZ_ASSERT(!cx->isExceptionPending());
  return true;
}

JS::UniqueChars JS::FormatStackDump(JSContext* cx, bool showArgs,
                                    bool showLocals, bool showThisProps) {
  int num = 0;

  Sprinter sp(cx);
  if (!sp.init()) {
    return nullptr;
  }

  for (AllFramesIter i(cx); !i.done(); ++i) {
    bool ok = i.hasScript() ? FormatFrame(cx, i, sp, num, showArgs, showLocals,
                                          showThisProps)
                            : FormatWasmFrame(cx, i, sp, num);
    if (!ok) {
      return nullptr;
    }
    num++;
  }

  if (num == 0) {
    sp.put("JavaScript stack is empty\n");
  }

  return sp.release();
}

struct DumpHeapTracer final : public JS::CallbackTracer, public WeakMapTracer {
  const char* prefix;
  FILE* output;
  mozilla::MallocSizeOf mallocSizeOf;

  DumpHeapTracer(FILE* fp, JSContext* cx, mozilla::MallocSizeOf mallocSizeOf)
      : JS::CallbackTracer(cx, JS::TracerKind::Callback,
                           JS::WeakMapTraceAction::Skip),
        WeakMapTracer(cx->runtime()),
        prefix(""),
        output(fp),
        mallocSizeOf(mallocSizeOf) {}

 private:
  void trace(JSObject* map, JS::GCCellPtr key, JS::GCCellPtr value) override {
    JSObject* kdelegate = nullptr;
    if (key.is<JSObject>()) {
      kdelegate = UncheckedUnwrapWithoutExpose(&key.as<JSObject>());
    }

    fprintf(output, "WeakMapEntry map=%p key=%p keyDelegate=%p value=%p\n", map,
            key.asCell(), kdelegate, value.asCell());
  }

  void onChild(JS::GCCellPtr thing, const char* name) override;
};

static char MarkDescriptor(js::gc::Cell* thing) {
  js::gc::TenuredCell* cell = &thing->asTenured();
  if (cell->isMarkedBlack()) {
    return 'B';
  }
  if (cell->isMarkedGray()) {
    return 'G';
  }
  if (cell->isMarkedAny()) {
    return 'X';
  }
  return 'W';
}

static void DumpHeapVisitZone(JSRuntime* rt, void* data, Zone* zone,
                              const JS::AutoRequireNoGC& nogc) {
  DumpHeapTracer* dtrc = static_cast<DumpHeapTracer*>(data);
  fprintf(dtrc->output, "# zone %p\n"static_cast<void*>(zone));
}

static void DumpHeapVisitRealm(JSContext* cx, void* data, Realm* realm,
                               const JS::AutoRequireNoGC& nogc) {
  char name[1024];
  if (auto nameCallback = cx->runtime()->realmNameCallback) {
    nameCallback(cx, realm, name, sizeof(name), nogc);
  } else {
    strcpy(name, "");
  }

  DumpHeapTracer* dtrc = static_cast<DumpHeapTracer*>(data);
  fprintf(dtrc->output, "# realm %s [in compartment %p, zone %p]\n", name,
          static_cast<void*>(realm->compartment()),
          static_cast<void*>(realm->zone()));
}

static void DumpHeapVisitArena(JSRuntime* rt, void* data, js::gc::Arena* arena,
                               JS::TraceKind traceKind, size_t thingSize,
                               const JS::AutoRequireNoGC& nogc) {
  DumpHeapTracer* dtrc = static_cast<DumpHeapTracer*>(data);
  fprintf(dtrc->output, "# arena allockind=%u size=%u\n",
          unsigned(arena->getAllocKind()), unsigned(thingSize));
}

static void DumpHeapVisitCell(JSRuntime* rt, void* data, JS::GCCellPtr cellptr,
                              size_t thingSize,
                              const JS::AutoRequireNoGC& nogc) {
  DumpHeapTracer* dtrc = static_cast<DumpHeapTracer*>(data);
  char cellDesc[1024 * 32];
  js::gc::GetTraceThingInfo(cellDesc, sizeof(cellDesc), cellptr.asCell(),
                            cellptr.kind(), true);

  fprintf(dtrc->output, "%p %c %s", cellptr.asCell(),
          MarkDescriptor(cellptr.asCell()), cellDesc);
  if (dtrc->mallocSizeOf) {
    auto size = JS::ubi::Node(cellptr).size(dtrc->mallocSizeOf);
    fprintf(dtrc->output, " SIZE:: %" PRIu64 "\n", size);
  } else {
    fprintf(dtrc->output, "\n");
  }

  JS::TraceChildren(dtrc, cellptr);
}

void DumpHeapTracer::onChild(JS::GCCellPtr thing, const char* name) {
  if (js::gc::IsInsideNursery(thing.asCell())) {
    return;
  }

  char buffer[1024];
  context().getEdgeName(name, buffer, sizeof(buffer));
  fprintf(output, "%s%p %c %s\n", prefix, thing.asCell(),
          MarkDescriptor(thing.asCell()), buffer);
}

void js::DumpHeap(JSContext* cx, FILE* fp,
                  DumpHeapNurseryBehaviour nurseryBehaviour,
                  mozilla::MallocSizeOf mallocSizeOf) {
  if (nurseryBehaviour == CollectNurseryBeforeDump) {
    cx->runtime()->gc.evictNursery(JS::GCReason::API);
  }

  DumpHeapTracer dtrc(fp, cx, mallocSizeOf);

  fprintf(dtrc.output, "# Roots.\n");
  TraceRuntimeWithoutEviction(&dtrc);

  fprintf(dtrc.output, "# Weak maps.\n");
  WeakMapBase::traceAllMappings(&dtrc);

  fprintf(dtrc.output, "==========\n");

  dtrc.prefix = "> ";
  IterateHeapUnbarriered(cx, &dtrc, DumpHeapVisitZone, DumpHeapVisitRealm,
                         DumpHeapVisitArena, DumpHeapVisitCell);

  fflush(dtrc.output);
}

void DumpFmtV(FILE* fp, const char* fmt, va_list args) {
  js::Fprinter out(fp);
  out.vprintf(fmt, args);
}

void js::DumpFmt(FILE* fp, const char* fmt, ...) {
  va_list args;
  va_start(args, fmt);
  DumpFmtV(fp, fmt, args);
  va_end(args);
}

void js::DumpFmt(const char* fmt, ...) {
  va_list args;
  va_start(args, fmt);
  DumpFmtV(stderr, fmt, args);
  va_end(args);
}

Messung V0.5
C=79 H=97 G=88

¤ Dauer der Verarbeitung: 0.6 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge