TraceInterpreter.h 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. /*
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. */
  7. #pragma once
  8. #ifdef HERMESVM_API_TRACE
  9. #include <hermes/Public/RuntimeConfig.h>
  10. #include <hermes/Support/SHA1.h>
  11. #include <hermes/SynthTrace.h>
  12. #include <jsi/jsi.h>
  13. #include <llvm/Support/MemoryBuffer.h>
  14. #include <llvm/Support/raw_ostream.h>
  15. #include <map>
  16. #include <unordered_map>
  17. #include <vector>
  18. namespace facebook {
  19. namespace hermes {
  20. namespace tracing {
  21. class TraceInterpreter final {
  22. public:
  23. /// A DefAndUse details the location of a definition of an object id, and its
  24. /// use. It is an index into the global record table.
  25. struct DefAndUse {
  26. /// If an object was not used or not defined, its DefAndUse can store this
  27. /// value.
  28. static constexpr uint64_t kUnused = std::numeric_limits<uint64_t>::max();
  29. uint64_t lastDefBeforeFirstUse{kUnused};
  30. uint64_t lastUse{kUnused};
  31. };
  32. /// A Call is a list of Pieces that represent the entire single call
  33. /// frame, even if it spans multiple control transfers between JS and native.
  34. /// It also contains a map from ObjectIDs to their last definition before a
  35. /// first use, and a last use.
  36. struct Call {
  37. /// A Piece is a series of contiguous records that are part of the same
  38. /// native call, and have no transitions to JS in the middle of them.
  39. struct Piece {
  40. /// The index of the start of the piece in the global record vector.
  41. uint64_t start;
  42. std::vector<const SynthTrace::Record *> records;
  43. explicit Piece() : start(0) {}
  44. explicit Piece(int64_t start) : start(start) {}
  45. };
  46. /// A list of pieces, where each piece stops when a transition occurs
  47. /// between JS and Native. Pieces are guaranteed to be sorted according to
  48. /// their start record (ascending).
  49. std::vector<Piece> pieces;
  50. std::unordered_map<SynthTrace::ObjectID, DefAndUse> locals;
  51. explicit Call() = delete;
  52. explicit Call(const Piece &piece) {
  53. pieces.emplace_back(piece);
  54. }
  55. explicit Call(Piece &&piece) {
  56. pieces.emplace_back(std::move(piece));
  57. }
  58. };
  59. /// A HostFunctionToCalls is a mapping from a host function id to the list of
  60. /// calls associated with that host function's execution. The calls are
  61. /// ordered by invocation (the 0th element is the 1st call).
  62. using HostFunctionToCalls =
  63. std::unordered_map<SynthTrace::ObjectID, std::vector<Call>>;
  64. /// A PropNameToCalls is a mapping from property names to a list of
  65. /// calls on that property. The calls are ordered by invocation (the 0th
  66. /// element is the 1st call).
  67. using PropNameToCalls = std::unordered_map<std::string, std::vector<Call>>;
  68. struct HostObjectInfo final {
  69. explicit HostObjectInfo() = default;
  70. PropNameToCalls propNameToCalls;
  71. std::vector<Call> callsToGetPropertyNames;
  72. std::vector<std::vector<std::string>> resultsOfGetPropertyNames;
  73. };
  74. /// A HostObjectToCalls is a mapping from a host object id to the
  75. /// mapping of property names to calls associated with accessing properties of
  76. /// that host object and the list of calls associated with getPropertyNames.
  77. using HostObjectToCalls =
  78. std::unordered_map<SynthTrace::ObjectID, HostObjectInfo>;
  79. /// Options for executing the trace.
  80. /// \param useTraceConfig If true, command-line options override the
  81. /// config options recorded in the trace. If false, start from the default
  82. /// config.
  83. /// \param snapshotMarker If the given marker is seen, take a heap snapshot.
  84. /// \param snapshotMarkerFileName If the marker given in snapshotMarker
  85. /// is seen, write the heap snapshot out to this file.
  86. /// \param warmupReps Number of initial executions whose stats are discarded.
  87. /// \param reps Number of repetitions of execution. Stats returned are those
  88. /// for the rep with the median totalTime.
  89. /// \param minHeapSize if non-zero, the minimum heap size, overriding
  90. /// the value stored in the trace.
  91. /// \param maxHeapSize if non-zero, the maximum heap size, overriding
  92. /// the value stored in the trace.
  93. /// \param allocInYoung: determines whether the GC initially allocates in
  94. /// the young generation.
  95. /// \param revertToYGAtTTI: if true, and if the GC was not allocating in the
  96. /// young generation, change back to young-gen allocation at TTI.
  97. struct ExecuteOptions {
  98. // These are not config params.
  99. bool useTraceConfig{false};
  100. int warmupReps{0};
  101. int reps{1};
  102. bool forceGCBeforeStats{false};
  103. bool stabilizeInstructionCount{false};
  104. std::string marker;
  105. std::string snapshotMarker;
  106. std::string snapshotMarkerFileName;
  107. // These are the config parameters. We wrap them in llvm::Optional
  108. // to indicate whether the corresponding command line flag was set
  109. // explicitly. We override the trace's config only when that is true.
  110. llvm::Optional<bool> shouldPrintGCStats;
  111. llvm::Optional<::hermes::vm::gcheapsize_t> minHeapSize;
  112. llvm::Optional<::hermes::vm::gcheapsize_t> initHeapSize;
  113. llvm::Optional<::hermes::vm::gcheapsize_t> maxHeapSize;
  114. llvm::Optional<double> occupancyTarget;
  115. llvm::Optional<::hermes::vm::ReleaseUnused> shouldReleaseUnused;
  116. llvm::Optional<bool> allocInYoung;
  117. llvm::Optional<bool> revertToYGAtTTI;
  118. llvm::Optional<bool> shouldTrackIO;
  119. llvm::Optional<unsigned> bytecodeWarmupPercent;
  120. llvm::Optional<double> sanitizeRate;
  121. llvm::Optional<int64_t> sanitizeRandomSeed;
  122. };
  123. private:
  124. jsi::Runtime &rt_;
  125. ExecuteOptions options_;
  126. llvm::raw_ostream *traceStream_;
  127. // Map from source hash to source file to run.
  128. std::map<::hermes::SHA1, std::shared_ptr<const jsi::Buffer>> bundles_;
  129. const SynthTrace &trace_;
  130. const std::unordered_map<SynthTrace::ObjectID, DefAndUse> &globalDefsAndUses_;
  131. const HostFunctionToCalls &hostFunctionCalls_;
  132. const HostObjectToCalls &hostObjectCalls_;
  133. std::unordered_map<SynthTrace::ObjectID, jsi::Function> hostFunctions_;
  134. std::unordered_map<SynthTrace::ObjectID, uint64_t> hostFunctionsCallCount_;
  135. // NOTE: Theoretically a host object property can have both a getter and a
  136. // setter. Since this doesn't occur in practice currently, this
  137. // implementation will ignore it. If it does happen, the value of the
  138. // interior map should turn into a pair of functions, and a pair of function
  139. // counts.
  140. std::unordered_map<SynthTrace::ObjectID, jsi::Object> hostObjects_;
  141. std::unordered_map<
  142. SynthTrace::ObjectID,
  143. std::unordered_map<std::string, uint64_t>>
  144. hostObjectsCallCount_;
  145. std::unordered_map<SynthTrace::ObjectID, uint64_t>
  146. hostObjectsPropertyNamesCallCount_;
  147. std::unordered_map<SynthTrace::ObjectID, jsi::Object> gom_;
  148. std::string stats_;
  149. /// Whether the marker was reached.
  150. bool markerFound_{false};
  151. /// Whether the snapshot marker was reached.
  152. bool snapshotMarkerFound_{false};
  153. /// Depth in the execution stack. Zero is the outermost function.
  154. uint64_t depth_{0};
  155. public:
  156. /// Execute the trace given by \p traceFile, that was the trace of executing
  157. /// the bundle given by \p bytecodeFile.
  158. static void exec(
  159. const std::string &traceFile,
  160. const std::vector<std::string> &bytecodeFiles,
  161. const ExecuteOptions &options);
  162. /// Same as exec, except it prints out the stats of a run.
  163. /// \return The stats collected by the runtime about times and memory usage.
  164. static std::string execAndGetStats(
  165. const std::string &traceFile,
  166. const std::vector<std::string> &bytecodeFiles,
  167. const ExecuteOptions &options);
  168. /// Same as exec, except it additionally traces the execution of the
  169. /// interpreter, to \p *traceStream. (Requires \p traceStream to be
  170. /// non-null.) This trace can be compared to the original to detect
  171. /// correctness issues.
  172. static void execAndTrace(
  173. const std::string &traceFile,
  174. const std::vector<std::string> &bytecodeFiles,
  175. const ExecuteOptions &options,
  176. std::unique_ptr<llvm::raw_ostream> traceStream);
  177. /// \param traceStream If non-null, write a trace of the execution into this
  178. /// stream.
  179. static std::string execFromMemoryBuffer(
  180. std::unique_ptr<llvm::MemoryBuffer> traceBuf,
  181. std::vector<std::unique_ptr<llvm::MemoryBuffer>> codeBufs,
  182. const ExecuteOptions &options,
  183. std::unique_ptr<llvm::raw_ostream> traceStream);
  184. private:
  185. TraceInterpreter(
  186. jsi::Runtime &rt,
  187. const ExecuteOptions &options,
  188. const SynthTrace &trace,
  189. std::map<::hermes::SHA1, std::shared_ptr<const jsi::Buffer>> bundles,
  190. const std::unordered_map<SynthTrace::ObjectID, DefAndUse>
  191. &globalDefsAndUses,
  192. const HostFunctionToCalls &hostFunctionCalls,
  193. const HostObjectToCalls &hostObjectCalls);
  194. static std::string execFromFileNames(
  195. const std::string &traceFile,
  196. const std::vector<std::string> &bytecodeFiles,
  197. const ExecuteOptions &options,
  198. std::unique_ptr<llvm::raw_ostream> traceStream);
  199. static std::string exec(
  200. jsi::Runtime &rt,
  201. const ExecuteOptions &options,
  202. const SynthTrace &trace,
  203. std::map<::hermes::SHA1, std::shared_ptr<const jsi::Buffer>> bundles);
  204. jsi::Function createHostFunction(
  205. const SynthTrace::CreateHostFunctionRecord &rec);
  206. jsi::Object createHostObject(SynthTrace::ObjectID objID);
  207. std::string execEntryFunction(const Call &entryFunc);
  208. jsi::Value execFunction(
  209. const Call &entryFunc,
  210. const jsi::Value &thisVal,
  211. const jsi::Value *args,
  212. uint64_t count);
  213. /// Add \p obj, whose id is \p objID and occurs at \p globalRecordNum, to
  214. /// either the globals or the \p locals depending on if it is used locally or
  215. /// not.
  216. void addObjectToDefs(
  217. const Call &call,
  218. SynthTrace::ObjectID objID,
  219. uint64_t globalRecordNum,
  220. const jsi::Object &obj,
  221. std::unordered_map<SynthTrace::ObjectID, jsi::Object> &locals);
  222. /// Same as above, except it avoids copies on temporary objects.
  223. void addObjectToDefs(
  224. const Call &call,
  225. SynthTrace::ObjectID objID,
  226. uint64_t globalRecordNum,
  227. jsi::Object &&obj,
  228. std::unordered_map<SynthTrace::ObjectID, jsi::Object> &locals);
  229. std::string printStats();
  230. LLVM_ATTRIBUTE_NORETURN void crashOnException(
  231. const std::exception &e,
  232. ::hermes::OptValue<uint64_t> globalRecordNum);
  233. };
  234. } // namespace tracing
  235. } // namespace hermes
  236. } // namespace facebook
  237. #endif