123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275 |
- /*
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
- #pragma once
- #ifdef HERMESVM_API_TRACE
- #include <hermes/Public/RuntimeConfig.h>
- #include <hermes/Support/SHA1.h>
- #include <hermes/SynthTrace.h>
- #include <jsi/jsi.h>
- #include <llvm/Support/MemoryBuffer.h>
- #include <llvm/Support/raw_ostream.h>
- #include <map>
- #include <unordered_map>
- #include <vector>
- namespace facebook {
- namespace hermes {
- namespace tracing {
- class TraceInterpreter final {
- public:
- /// A DefAndUse details the location of a definition of an object id, and its
- /// use. It is an index into the global record table.
- struct DefAndUse {
- /// If an object was not used or not defined, its DefAndUse can store this
- /// value.
- static constexpr uint64_t kUnused = std::numeric_limits<uint64_t>::max();
- uint64_t lastDefBeforeFirstUse{kUnused};
- uint64_t lastUse{kUnused};
- };
- /// A Call is a list of Pieces that represent the entire single call
- /// frame, even if it spans multiple control transfers between JS and native.
- /// It also contains a map from ObjectIDs to their last definition before a
- /// first use, and a last use.
- struct Call {
- /// A Piece is a series of contiguous records that are part of the same
- /// native call, and have no transitions to JS in the middle of them.
- struct Piece {
- /// The index of the start of the piece in the global record vector.
- uint64_t start;
- std::vector<const SynthTrace::Record *> records;
- explicit Piece() : start(0) {}
- explicit Piece(int64_t start) : start(start) {}
- };
- /// A list of pieces, where each piece stops when a transition occurs
- /// between JS and Native. Pieces are guaranteed to be sorted according to
- /// their start record (ascending).
- std::vector<Piece> pieces;
- std::unordered_map<SynthTrace::ObjectID, DefAndUse> locals;
- explicit Call() = delete;
- explicit Call(const Piece &piece) {
- pieces.emplace_back(piece);
- }
- explicit Call(Piece &&piece) {
- pieces.emplace_back(std::move(piece));
- }
- };
- /// A HostFunctionToCalls is a mapping from a host function id to the list of
- /// calls associated with that host function's execution. The calls are
- /// ordered by invocation (the 0th element is the 1st call).
- using HostFunctionToCalls =
- std::unordered_map<SynthTrace::ObjectID, std::vector<Call>>;
- /// A PropNameToCalls is a mapping from property names to a list of
- /// calls on that property. The calls are ordered by invocation (the 0th
- /// element is the 1st call).
- using PropNameToCalls = std::unordered_map<std::string, std::vector<Call>>;
- struct HostObjectInfo final {
- explicit HostObjectInfo() = default;
- PropNameToCalls propNameToCalls;
- std::vector<Call> callsToGetPropertyNames;
- std::vector<std::vector<std::string>> resultsOfGetPropertyNames;
- };
- /// A HostObjectToCalls is a mapping from a host object id to the
- /// mapping of property names to calls associated with accessing properties of
- /// that host object and the list of calls associated with getPropertyNames.
- using HostObjectToCalls =
- std::unordered_map<SynthTrace::ObjectID, HostObjectInfo>;
- /// Options for executing the trace.
- /// \param useTraceConfig If true, command-line options override the
- /// config options recorded in the trace. If false, start from the default
- /// config.
- /// \param snapshotMarker If the given marker is seen, take a heap snapshot.
- /// \param snapshotMarkerFileName If the marker given in snapshotMarker
- /// is seen, write the heap snapshot out to this file.
- /// \param warmupReps Number of initial executions whose stats are discarded.
- /// \param reps Number of repetitions of execution. Stats returned are those
- /// for the rep with the median totalTime.
- /// \param minHeapSize if non-zero, the minimum heap size, overriding
- /// the value stored in the trace.
- /// \param maxHeapSize if non-zero, the maximum heap size, overriding
- /// the value stored in the trace.
- /// \param allocInYoung: determines whether the GC initially allocates in
- /// the young generation.
- /// \param revertToYGAtTTI: if true, and if the GC was not allocating in the
- /// young generation, change back to young-gen allocation at TTI.
- struct ExecuteOptions {
- // These are not config params.
- bool useTraceConfig{false};
- int warmupReps{0};
- int reps{1};
- bool forceGCBeforeStats{false};
- bool stabilizeInstructionCount{false};
- std::string marker;
- std::string snapshotMarker;
- std::string snapshotMarkerFileName;
- // These are the config parameters. We wrap them in llvm::Optional
- // to indicate whether the corresponding command line flag was set
- // explicitly. We override the trace's config only when that is true.
- llvm::Optional<bool> shouldPrintGCStats;
- llvm::Optional<::hermes::vm::gcheapsize_t> minHeapSize;
- llvm::Optional<::hermes::vm::gcheapsize_t> initHeapSize;
- llvm::Optional<::hermes::vm::gcheapsize_t> maxHeapSize;
- llvm::Optional<double> occupancyTarget;
- llvm::Optional<::hermes::vm::ReleaseUnused> shouldReleaseUnused;
- llvm::Optional<bool> allocInYoung;
- llvm::Optional<bool> revertToYGAtTTI;
- llvm::Optional<bool> shouldTrackIO;
- llvm::Optional<unsigned> bytecodeWarmupPercent;
- llvm::Optional<double> sanitizeRate;
- llvm::Optional<int64_t> sanitizeRandomSeed;
- };
- private:
- jsi::Runtime &rt_;
- ExecuteOptions options_;
- llvm::raw_ostream *traceStream_;
- // Map from source hash to source file to run.
- std::map<::hermes::SHA1, std::shared_ptr<const jsi::Buffer>> bundles_;
- const SynthTrace &trace_;
- const std::unordered_map<SynthTrace::ObjectID, DefAndUse> &globalDefsAndUses_;
- const HostFunctionToCalls &hostFunctionCalls_;
- const HostObjectToCalls &hostObjectCalls_;
- std::unordered_map<SynthTrace::ObjectID, jsi::Function> hostFunctions_;
- std::unordered_map<SynthTrace::ObjectID, uint64_t> hostFunctionsCallCount_;
- // NOTE: Theoretically a host object property can have both a getter and a
- // setter. Since this doesn't occur in practice currently, this
- // implementation will ignore it. If it does happen, the value of the
- // interior map should turn into a pair of functions, and a pair of function
- // counts.
- std::unordered_map<SynthTrace::ObjectID, jsi::Object> hostObjects_;
- std::unordered_map<
- SynthTrace::ObjectID,
- std::unordered_map<std::string, uint64_t>>
- hostObjectsCallCount_;
- std::unordered_map<SynthTrace::ObjectID, uint64_t>
- hostObjectsPropertyNamesCallCount_;
- std::unordered_map<SynthTrace::ObjectID, jsi::Object> gom_;
- std::string stats_;
- /// Whether the marker was reached.
- bool markerFound_{false};
- /// Whether the snapshot marker was reached.
- bool snapshotMarkerFound_{false};
- /// Depth in the execution stack. Zero is the outermost function.
- uint64_t depth_{0};
- public:
- /// Execute the trace given by \p traceFile, that was the trace of executing
- /// the bundle given by \p bytecodeFile.
- static void exec(
- const std::string &traceFile,
- const std::vector<std::string> &bytecodeFiles,
- const ExecuteOptions &options);
- /// Same as exec, except it prints out the stats of a run.
- /// \return The stats collected by the runtime about times and memory usage.
- static std::string execAndGetStats(
- const std::string &traceFile,
- const std::vector<std::string> &bytecodeFiles,
- const ExecuteOptions &options);
- /// Same as exec, except it additionally traces the execution of the
- /// interpreter, to \p *traceStream. (Requires \p traceStream to be
- /// non-null.) This trace can be compared to the original to detect
- /// correctness issues.
- static void execAndTrace(
- const std::string &traceFile,
- const std::vector<std::string> &bytecodeFiles,
- const ExecuteOptions &options,
- std::unique_ptr<llvm::raw_ostream> traceStream);
- /// \param traceStream If non-null, write a trace of the execution into this
- /// stream.
- static std::string execFromMemoryBuffer(
- std::unique_ptr<llvm::MemoryBuffer> traceBuf,
- std::vector<std::unique_ptr<llvm::MemoryBuffer>> codeBufs,
- const ExecuteOptions &options,
- std::unique_ptr<llvm::raw_ostream> traceStream);
- private:
- TraceInterpreter(
- jsi::Runtime &rt,
- const ExecuteOptions &options,
- const SynthTrace &trace,
- std::map<::hermes::SHA1, std::shared_ptr<const jsi::Buffer>> bundles,
- const std::unordered_map<SynthTrace::ObjectID, DefAndUse>
- &globalDefsAndUses,
- const HostFunctionToCalls &hostFunctionCalls,
- const HostObjectToCalls &hostObjectCalls);
- static std::string execFromFileNames(
- const std::string &traceFile,
- const std::vector<std::string> &bytecodeFiles,
- const ExecuteOptions &options,
- std::unique_ptr<llvm::raw_ostream> traceStream);
- static std::string exec(
- jsi::Runtime &rt,
- const ExecuteOptions &options,
- const SynthTrace &trace,
- std::map<::hermes::SHA1, std::shared_ptr<const jsi::Buffer>> bundles);
- jsi::Function createHostFunction(
- const SynthTrace::CreateHostFunctionRecord &rec);
- jsi::Object createHostObject(SynthTrace::ObjectID objID);
- std::string execEntryFunction(const Call &entryFunc);
- jsi::Value execFunction(
- const Call &entryFunc,
- const jsi::Value &thisVal,
- const jsi::Value *args,
- uint64_t count);
- /// Add \p obj, whose id is \p objID and occurs at \p globalRecordNum, to
- /// either the globals or the \p locals depending on if it is used locally or
- /// not.
- void addObjectToDefs(
- const Call &call,
- SynthTrace::ObjectID objID,
- uint64_t globalRecordNum,
- const jsi::Object &obj,
- std::unordered_map<SynthTrace::ObjectID, jsi::Object> &locals);
- /// Same as above, except it avoids copies on temporary objects.
- void addObjectToDefs(
- const Call &call,
- SynthTrace::ObjectID objID,
- uint64_t globalRecordNum,
- jsi::Object &&obj,
- std::unordered_map<SynthTrace::ObjectID, jsi::Object> &locals);
- std::string printStats();
- LLVM_ATTRIBUTE_NORETURN void crashOnException(
- const std::exception &e,
- ::hermes::OptValue<uint64_t> globalRecordNum);
- };
- } // namespace tracing
- } // namespace hermes
- } // namespace facebook
- #endif
|