diff options
Diffstat (limited to 'js/src/vm/Instrumentation.cpp')
-rw-r--r-- | js/src/vm/Instrumentation.cpp | 289 |
1 files changed, 289 insertions, 0 deletions
diff --git a/js/src/vm/Instrumentation.cpp b/js/src/vm/Instrumentation.cpp new file mode 100644 index 0000000000..8d9f2863ee --- /dev/null +++ b/js/src/vm/Instrumentation.cpp @@ -0,0 +1,289 @@ +/* -*- 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 "vm/Instrumentation.h" + +#include <iterator> + +#include "jsapi.h" + +#include "debugger/DebugAPI.h" +#include "frontend/ParserAtom.h" +#include "js/Object.h" // JS::GetReservedSlot +#include "proxy/DeadObjectProxy.h" + +#include "vm/JSObject-inl.h" + +namespace js { + +RealmInstrumentation::RealmInstrumentation(Zone* zone, JSObject* callback, + JSObject* dbgObject, uint32_t kinds) + : callback(callback), dbgObject(dbgObject), kinds(kinds) {} + +void RealmInstrumentation::trace(JSTracer* trc) { + TraceEdge(trc, &callback, "RealmInstrumentation::callback"); + TraceEdge(trc, &dbgObject, "RealmInstrumentation::dbgObject"); +} + +enum InstrumentationHolderSlots { + RealmInstrumentationSlot, + ReservedSlotCount, +}; + +static RealmInstrumentation* GetInstrumentation(JSObject* obj) { + Value v = JS::GetReservedSlot(obj, RealmInstrumentationSlot); + return static_cast<RealmInstrumentation*>(v.isUndefined() ? nullptr + : v.toPrivate()); +} + +/* static */ +void RealmInstrumentation::holderFinalize(JSFreeOp* fop, JSObject* obj) { + RealmInstrumentation* instrumentation = GetInstrumentation(obj); + fop->delete_(obj, instrumentation, MemoryUse::RealmInstrumentation); +} + +/* static */ +void RealmInstrumentation::holderTrace(JSTracer* trc, JSObject* obj) { + RealmInstrumentation* instrumentation = GetInstrumentation(obj); + if (instrumentation) { + instrumentation->trace(trc); + } +} + +static const JSClassOps InstrumentationHolderClassOps = { + nullptr, // addProperty + nullptr, // delProperty + nullptr, // enumerate + nullptr, // newEnumerate + nullptr, // resolve + nullptr, // mayResolve + RealmInstrumentation::holderFinalize, // finalize + nullptr, // call + nullptr, // hasInstance + nullptr, // construct + RealmInstrumentation::holderTrace, // trace +}; + +static const JSClass InstrumentationHolderClass = { + "Instrumentation Holder", + JSCLASS_HAS_RESERVED_SLOTS(ReservedSlotCount) | JSCLASS_FOREGROUND_FINALIZE, + &InstrumentationHolderClassOps, JS_NULL_CLASS_SPEC, JS_NULL_CLASS_EXT}; + +static const char* instrumentationNames[] = { +#define DEFINE_INSTRUMENTATION_STRING(_1, String, _2) String, + FOR_EACH_INSTRUMENTATION_KIND(DEFINE_INSTRUMENTATION_STRING) +#undef DEFINE_INSTRUMENTATION_STRING +}; + +static bool StringToInstrumentationKind(JSContext* cx, HandleString str, + InstrumentationKind* result) { + for (size_t i = 0; i < std::size(instrumentationNames); i++) { + bool match; + if (!JS_StringEqualsAscii(cx, str, instrumentationNames[i], &match)) { + return false; + } + if (match) { + *result = (InstrumentationKind)(1 << i); + return true; + } + } + + JS_ReportErrorASCII(cx, "Unknown instrumentation kind"); + return false; +} + +/* static */ +const frontend::ParserAtom* RealmInstrumentation::getInstrumentationKindName( + JSContext* cx, frontend::ParserAtomsTable& parserAtoms, + InstrumentationKind kind) { + for (size_t i = 0; i < std::size(instrumentationNames); i++) { + if (kind == (InstrumentationKind)(1 << i)) { + return parserAtoms.internAscii(cx, instrumentationNames[i], + strlen(instrumentationNames[i])); + } + } + MOZ_CRASH("Unexpected instrumentation kind"); +} + +/* static */ +bool RealmInstrumentation::install(JSContext* cx, Handle<GlobalObject*> global, + HandleObject callbackArg, + HandleObject dbgObjectArg, + Handle<StringVector> kindStrings) { + MOZ_ASSERT(global == cx->global()); + + if (global->getInstrumentationHolder()) { + JS_ReportErrorASCII(cx, "Global already has instrumentation specified"); + return false; + } + + RootedObject callback(cx, callbackArg); + if (!cx->compartment()->wrap(cx, &callback)) { + return false; + } + + RootedObject dbgObject(cx, dbgObjectArg); + if (!cx->compartment()->wrap(cx, &dbgObject)) { + return false; + } + + uint32_t kinds = 0; + for (size_t i = 0; i < kindStrings.length(); i++) { + HandleString str = kindStrings[i]; + InstrumentationKind kind; + if (!StringToInstrumentationKind(cx, str, &kind)) { + return false; + } + kinds |= (uint32_t)kind; + } + + Rooted<UniquePtr<RealmInstrumentation>> instrumentation( + cx, + MakeUnique<RealmInstrumentation>(cx->zone(), callback, dbgObject, kinds)); + if (!instrumentation) { + ReportOutOfMemory(cx); + return false; + } + + JSObject* holder = NewBuiltinClassInstance(cx, &InstrumentationHolderClass); + if (!holder) { + return false; + } + + InitReservedSlot(&holder->as<NativeObject>(), RealmInstrumentationSlot, + instrumentation.release(), MemoryUse::RealmInstrumentation); + + global->setInstrumentationHolder(holder); + return true; +} + +/* static */ +bool RealmInstrumentation::setActive(JSContext* cx, + Handle<GlobalObject*> global, + Debugger* dbg, bool active) { + MOZ_ASSERT(global == cx->global()); + + RootedObject holder(cx, global->getInstrumentationHolder()); + if (!holder) { + JS_ReportErrorASCII(cx, "Global does not have instrumentation specified"); + return false; + } + + RealmInstrumentation* instrumentation = GetInstrumentation(holder); + MOZ_ASSERT(instrumentation); + if (active != instrumentation->active) { + instrumentation->active = active; + + // For simplicity, discard all Ion code in the entire zone when + // instrumentation activity changes. + js::CancelOffThreadIonCompile(cx->runtime()); + cx->zone()->setPreservingCode(false); + cx->zone()->discardJitCode(cx->runtime()->defaultFreeOp(), + Zone::KeepBaselineCode); + } + + return true; +} + +/* static */ +bool RealmInstrumentation::isActive(GlobalObject* global) { + JSObject* holder = global->getInstrumentationHolder(); + MOZ_ASSERT(holder); + + RealmInstrumentation* instrumentation = GetInstrumentation(holder); + MOZ_ASSERT(instrumentation); + return instrumentation->active; +} + +/* static */ +const int32_t* RealmInstrumentation::addressOfActive(GlobalObject* global) { + JSObject* holder = global->getInstrumentationHolder(); + MOZ_ASSERT(holder); + + RealmInstrumentation* instrumentation = GetInstrumentation(holder); + MOZ_ASSERT(instrumentation); + return &instrumentation->active; +} + +/* static */ +JSObject* RealmInstrumentation::getCallback(GlobalObject* global) { + JSObject* holder = global->getInstrumentationHolder(); + MOZ_ASSERT(holder); + + RealmInstrumentation* instrumentation = GetInstrumentation(holder); + MOZ_ASSERT(instrumentation); + return instrumentation->callback; +} + +/* static */ +uint32_t RealmInstrumentation::getInstrumentationKinds(GlobalObject* global) { + JSObject* holder = global->getInstrumentationHolder(); + if (!holder) { + return 0; + } + + RealmInstrumentation* instrumentation = GetInstrumentation(holder); + MOZ_ASSERT(instrumentation); + return instrumentation->kinds; +} + +/* static */ +bool RealmInstrumentation::getScriptId(JSContext* cx, + Handle<GlobalObject*> global, + HandleScript script, int32_t* id) { + MOZ_ASSERT(global == cx->global()); + RootedObject holder(cx, global->getInstrumentationHolder()); + RealmInstrumentation* instrumentation = GetInstrumentation(holder); + MOZ_ASSERT(instrumentation); + + RootedObject dbgObject(cx, UncheckedUnwrap(instrumentation->dbgObject)); + + if (IsDeadProxyObject(dbgObject)) { + JS_ReportErrorASCII(cx, "Instrumentation debugger object is dead"); + return false; + } + + AutoRealm ar(cx, dbgObject); + + RootedValue idValue(cx); + if (!DebugAPI::getScriptInstrumentationId(cx, dbgObject, script, &idValue)) { + return false; + } + + if (!idValue.isNumber()) { + JS_ReportErrorASCII(cx, "Instrumentation ID not set for script"); + return false; + } + + *id = idValue.toNumber(); + return true; +} + +bool InstrumentationActiveOperation(JSContext* cx, MutableHandleValue rv) { + rv.setBoolean(RealmInstrumentation::isActive(cx->global())); + return true; +} + +JSObject* InstrumentationCallbackOperation(JSContext* cx) { + return RealmInstrumentation::getCallback(cx->global()); +} + +bool InstrumentationScriptIdOperation(JSContext* cx, HandleScript script, + MutableHandleValue rv) { + int32_t id; + if (!RealmInstrumentation::getScriptId(cx, cx->global(), script, &id)) { + return false; + } + rv.setInt32(id); + return true; +} + +bool GlobalHasInstrumentation(JSObject* global) { + return global->is<js::GlobalObject>() && + global->as<js::GlobalObject>().getInstrumentationHolder(); +} + +} // namespace js |