SynthTrace.h 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865
  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. #ifndef HERMES_SYNTHTRACE_H
  8. #define HERMES_SYNTHTRACE_H
  9. #ifdef HERMESVM_API_TRACE
  10. #include "hermes/Public/RuntimeConfig.h"
  11. #include "hermes/Support/JSONEmitter.h"
  12. #include "hermes/Support/StringSetVector.h"
  13. #include "hermes/VM/HermesValue.h"
  14. #include "hermes/VM/MockedEnvironment.h"
  15. #include "hermes/VM/Operations.h"
  16. #include <chrono>
  17. #include <cstdlib>
  18. #include <memory>
  19. #include <string>
  20. #include <vector>
  21. namespace llvm {
  22. // Forward declaration to avoid including llvm headers.
  23. class raw_ostream;
  24. } // namespace llvm
  25. namespace facebook {
  26. namespace hermes {
  27. namespace tracing {
  28. /// A SynthTrace is a list of events that occur in a run of a JS file by a
  29. /// runtime that uses JSI.
  30. /// It can be serialized into JSON and written to a llvm::raw_ostream.
  31. class SynthTrace {
  32. public:
  33. using ObjectID = uint64_t;
  34. /// A tagged union representing different types available in the trace.
  35. /// HermesValue doesn't have to be used, but it is an efficient way
  36. /// of encoding a type tag and a value. std::variant is another option.
  37. /// NOTE: Since HermesValue can only store 64-bit values, strings need to be
  38. /// changed into a table index.
  39. using TraceValue = ::hermes::vm::HermesValue;
  40. /// A TimePoint is a time when some event occurred.
  41. using TimePoint = std::chrono::steady_clock::time_point;
  42. using TimeSinceStart = std::chrono::milliseconds;
  43. static constexpr size_t kHashNumBytes = 20;
  44. /// RecordType is a tag used to differentiate which type of record it is.
  45. /// There should be a unique tag for each record type.
  46. enum class RecordType {
  47. BeginExecJS,
  48. EndExecJS,
  49. Marker,
  50. CreateObject,
  51. CreateHostObject,
  52. CreateHostFunction,
  53. GetProperty,
  54. SetProperty,
  55. HasProperty,
  56. GetPropertyNames,
  57. CreateArray,
  58. ArrayRead,
  59. ArrayWrite,
  60. CallFromNative,
  61. ConstructFromNative,
  62. ReturnFromNative,
  63. ReturnToNative,
  64. CallToNative,
  65. GetPropertyNative,
  66. GetPropertyNativeReturn,
  67. SetPropertyNative,
  68. SetPropertyNativeReturn,
  69. GetNativePropertyNames,
  70. GetNativePropertyNamesReturn,
  71. };
  72. /// A Record is one element of a trace.
  73. struct Record {
  74. /// The time at which this event occurred with respect to the start of
  75. /// execution.
  76. /// NOTE: This is not compared in the \c operator= in order for tests to
  77. /// pass.
  78. const TimeSinceStart time_;
  79. explicit Record() = delete;
  80. explicit Record(TimeSinceStart time) : time_(time) {}
  81. virtual ~Record() = default;
  82. /// Write out a serialization of this Record.
  83. /// \param json An emitter connected to an ostream which will write out
  84. /// JSON.
  85. void toJSON(::hermes::JSONEmitter &json, const SynthTrace &trace) const;
  86. virtual RecordType getType() const = 0;
  87. /// \return A list of object ids that are defined by this record.
  88. /// Defined means that the record would produce that object as a locally
  89. /// accessible value if it were executed.
  90. virtual std::vector<ObjectID> defs() const {
  91. return {};
  92. }
  93. /// \return A list of object ids that are used by this record.
  94. /// Used means that the record would use that object as a value if it were
  95. /// executed.
  96. /// If a record uses an object, then some preceding record (either in the
  97. /// same function invocation, or somewhere globally) must provide a
  98. /// definition.
  99. virtual std::vector<ObjectID> uses() const {
  100. return {};
  101. }
  102. protected:
  103. /// Compare records for equality. Derived classes should override this, call
  104. /// their parent, and mark any public versions as "final".
  105. virtual bool operator==(const Record &that) const {
  106. return getType() == that.getType();
  107. }
  108. /// Emit JSON fields into \p os, excluding the closing curly brace.
  109. /// NOTE: This is overridable, and non-abstract children should call the
  110. /// parent.
  111. virtual void toJSONInternal(
  112. ::hermes::JSONEmitter &json,
  113. const SynthTrace &trace) const;
  114. };
  115. /// If \p traceStream is non-null, the trace will be written to that
  116. /// stream. Otherwise, no trace is written.
  117. explicit SynthTrace(
  118. ObjectID globalObjID,
  119. const ::hermes::vm::RuntimeConfig &conf,
  120. std::unique_ptr<llvm::raw_ostream> traceStream = nullptr);
  121. template <typename T, typename... Args>
  122. void emplace_back(Args &&... args) {
  123. records_.emplace_back(new T(std::forward<Args>(args)...));
  124. flushRecordsIfNecessary();
  125. }
  126. const std::vector<std::unique_ptr<Record>> &records() const {
  127. return records_;
  128. }
  129. ObjectID globalObjID() const {
  130. return globalObjID_;
  131. }
  132. /// Given a trace value, turn it into its typed string.
  133. std::string encode(TraceValue value) const;
  134. /// Encode an undefined JS value for the trace.
  135. static TraceValue encodeUndefined();
  136. /// Encode a null JS value for the trace.
  137. static TraceValue encodeNull();
  138. /// Encode a boolean JS value for the trace.
  139. static TraceValue encodeBool(bool value);
  140. /// Encodes a numeric value for the trace.
  141. static TraceValue encodeNumber(double value);
  142. /// Encodes an object for the trace as a unique id.
  143. static TraceValue encodeObject(ObjectID objID);
  144. /// Encodes a string for the trace. Adds it to the string table if it hasn't
  145. /// been seen before.
  146. TraceValue encodeString(const std::string &value);
  147. /// Decodes a string into a trace value. It can add to the string table.
  148. TraceValue decode(const std::string &);
  149. /// Extracts an object ID from a trace value.
  150. /// \pre The value must be an object.
  151. static ObjectID decodeObject(TraceValue value);
  152. /// Extracts a string from a trace value.
  153. /// \pre The value must be a string.
  154. const std::string &decodeString(TraceValue value) const;
  155. static bool equal(TraceValue x, TraceValue y) {
  156. // We are encoding random numbers into strings, and can't use the library
  157. // functions.
  158. // For now, ignore differences that result from NaN, +0/-0.
  159. return x.getRaw() == y.getRaw();
  160. }
  161. /// The version of the Synth Benchmark
  162. constexpr static uint32_t synthVersion() {
  163. return 2;
  164. }
  165. static const char *nameFromReleaseUnused(::hermes::vm::ReleaseUnused ru);
  166. static ::hermes::vm::ReleaseUnused releaseUnusedFromName(const char *name);
  167. private:
  168. llvm::raw_ostream &os() const {
  169. return (*traceStream_);
  170. }
  171. /// If we're tracing to a file, and the number of accumulated
  172. /// records has reached the limit kTraceRecordsToFlush, below,
  173. /// flush the records to the file, and reset the accumulated records
  174. /// to be empty.
  175. void flushRecordsIfNecessary();
  176. /// Assumes we're tracing to a file; flush accumulated records to
  177. /// the file, and reset the accumulated records to be empty.
  178. void flushRecords();
  179. static constexpr unsigned kTraceRecordsToFlush = 100;
  180. /// If we're tracing to a file, pointer to a stream onto
  181. /// traceFilename_. Null otherwise.
  182. std::unique_ptr<llvm::raw_ostream> traceStream_;
  183. /// If we're tracing to a file, pointer to a JSONEmitter writting
  184. /// into *traceStream_. Null otherwise.
  185. std::unique_ptr<::hermes::JSONEmitter> json_;
  186. /// The records currently being accumulated in the trace. If we are
  187. /// tracing to a file, these will be only the records not yet
  188. /// written to the file.
  189. std::vector<std::unique_ptr<Record>> records_;
  190. /// The id of the global object.
  191. const ObjectID globalObjID_;
  192. /// A table of strings to avoid repeated strings taking up memory. Similar to
  193. /// the IdentifierTable, except it doesn't need to be collected (it stores
  194. /// strings forever).
  195. /// Strings are stored in the trace objects as an index into this table.
  196. ::hermes::StringSetVector stringTable_;
  197. public:
  198. /// @name Record classes
  199. /// @{
  200. /// A MarkerRecord is an event that simply records an interesting event that
  201. /// is not necessarily meaningful to the interpreter. It comes with a tag that
  202. /// says what type of marker it was.
  203. struct MarkerRecord : public Record {
  204. static constexpr RecordType type{RecordType::Marker};
  205. const std::string tag_;
  206. explicit MarkerRecord(TimeSinceStart time, const std::string &tag)
  207. : Record(time), tag_(tag) {}
  208. RecordType getType() const override {
  209. return type;
  210. }
  211. protected:
  212. void toJSONInternal(::hermes::JSONEmitter &json, const SynthTrace &trace)
  213. const override;
  214. bool operator==(const Record &that) const override;
  215. };
  216. /// A BeginExecJSRecord is an event where execution begins of JS source
  217. /// code. This is not necessarily the first record, since native code can
  218. /// inject values into the VM before any source code is run.
  219. struct BeginExecJSRecord final : public Record {
  220. static constexpr RecordType type{RecordType::BeginExecJS};
  221. explicit BeginExecJSRecord(
  222. TimeSinceStart time,
  223. std::string sourceURL,
  224. ::hermes::SHA1 sourceHash)
  225. : Record(time),
  226. sourceURL_(std::move(sourceURL)),
  227. sourceHash_(std::move(sourceHash)) {}
  228. RecordType getType() const override {
  229. return type;
  230. }
  231. const std::string &sourceURL() const {
  232. return sourceURL_;
  233. }
  234. const ::hermes::SHA1 &sourceHash() const {
  235. return sourceHash_;
  236. }
  237. private:
  238. void toJSONInternal(::hermes::JSONEmitter &json, const SynthTrace &trace)
  239. const override;
  240. /// The URL providing the source file mapping for the file being executed.
  241. /// Can be empty.
  242. std::string sourceURL_;
  243. /// A hash of the source that was executed. The source hash must match up
  244. /// when the file is replayed.
  245. /// The hash is optional, and will be all zeros if not provided.
  246. ::hermes::SHA1 sourceHash_;
  247. };
  248. struct ReturnMixin {
  249. const TraceValue retVal_;
  250. explicit ReturnMixin(TraceValue value) : retVal_(value) {}
  251. void toJSONInternal(::hermes::JSONEmitter &json, const SynthTrace &trace)
  252. const;
  253. bool operator==(const ReturnMixin &that) const;
  254. };
  255. /// A EndExecJSRecord is an event where execution of JS source code stops.
  256. /// This does not mean that the source code will never be entered again, just
  257. /// that it has an entered a phase where it is waiting for native code to call
  258. /// into the JS. This event is not guaranteed to be the last event, for the
  259. /// aforementioned reason. The logged retVal is the result of the evaluation
  260. /// ("undefined" in the majority of cases).
  261. struct EndExecJSRecord final : public MarkerRecord, public ReturnMixin {
  262. static constexpr RecordType type{RecordType::EndExecJS};
  263. EndExecJSRecord(TimeSinceStart time, TraceValue retVal)
  264. : MarkerRecord(time, "end_global_code"), ReturnMixin(retVal) {}
  265. RecordType getType() const override {
  266. return type;
  267. }
  268. bool operator==(const Record &that) const final;
  269. virtual void toJSONInternal(
  270. ::hermes::JSONEmitter &json,
  271. const SynthTrace &trace) const final;
  272. std::vector<ObjectID> defs() const override {
  273. auto defs = MarkerRecord::defs();
  274. if (retVal_.isObject()) {
  275. defs.push_back(decodeObject(retVal_));
  276. }
  277. return defs;
  278. }
  279. };
  280. /// A CreateObjectRecord is an event where an empty object is created by the
  281. /// native code.
  282. struct CreateObjectRecord : public Record {
  283. static constexpr RecordType type{RecordType::CreateObject};
  284. const ObjectID objID_;
  285. explicit CreateObjectRecord(TimeSinceStart time, ObjectID objID)
  286. : Record(time), objID_(objID) {}
  287. bool operator==(const Record &that) const override;
  288. void toJSONInternal(::hermes::JSONEmitter &json, const SynthTrace &trace)
  289. const override;
  290. RecordType getType() const override {
  291. return type;
  292. }
  293. std::vector<ObjectID> defs() const override {
  294. return {objID_};
  295. }
  296. std::vector<ObjectID> uses() const override {
  297. return {};
  298. }
  299. };
  300. struct CreateHostObjectRecord final : public CreateObjectRecord {
  301. static constexpr RecordType type{RecordType::CreateHostObject};
  302. using CreateObjectRecord::CreateObjectRecord;
  303. RecordType getType() const override {
  304. return type;
  305. }
  306. };
  307. struct CreateHostFunctionRecord final : public CreateObjectRecord {
  308. static constexpr RecordType type{RecordType::CreateHostFunction};
  309. const std::string functionName_;
  310. const unsigned paramCount_;
  311. CreateHostFunctionRecord(
  312. TimeSinceStart time,
  313. ObjectID objID,
  314. std::string functionName,
  315. unsigned paramCount)
  316. : CreateObjectRecord(time, objID),
  317. functionName_(std::move(functionName)),
  318. paramCount_(paramCount) {}
  319. bool operator==(const Record &that) const override;
  320. void toJSONInternal(::hermes::JSONEmitter &json, const SynthTrace &trace)
  321. const override;
  322. RecordType getType() const override {
  323. return type;
  324. }
  325. };
  326. struct GetOrSetPropertyRecord : public Record {
  327. const ObjectID objID_;
  328. const std::string propName_;
  329. const TraceValue value_;
  330. explicit GetOrSetPropertyRecord(
  331. TimeSinceStart time,
  332. ObjectID objID,
  333. const std::string &propName,
  334. TraceValue value)
  335. : Record(time), objID_(objID), propName_(propName), value_(value) {}
  336. bool operator==(const Record &that) const final;
  337. std::vector<ObjectID> uses() const override {
  338. return {objID_};
  339. }
  340. void toJSONInternal(::hermes::JSONEmitter &json, const SynthTrace &trace)
  341. const override;
  342. };
  343. /// A GetPropertyRecord is an event where native code accesses the property
  344. /// of a JS object.
  345. struct GetPropertyRecord : public GetOrSetPropertyRecord {
  346. static constexpr RecordType type{RecordType::GetProperty};
  347. using GetOrSetPropertyRecord::GetOrSetPropertyRecord;
  348. RecordType getType() const override {
  349. return type;
  350. }
  351. std::vector<ObjectID> defs() const override {
  352. auto defs = GetOrSetPropertyRecord::defs();
  353. if (value_.isObject()) {
  354. defs.push_back(decodeObject(value_));
  355. }
  356. return defs;
  357. }
  358. };
  359. /// A SetPropertyRecord is an event where native code writes to the property
  360. /// of a JS object.
  361. struct SetPropertyRecord : public GetOrSetPropertyRecord {
  362. static constexpr RecordType type{RecordType::SetProperty};
  363. using GetOrSetPropertyRecord::GetOrSetPropertyRecord;
  364. RecordType getType() const override {
  365. return type;
  366. }
  367. std::vector<ObjectID> uses() const override {
  368. auto uses = GetOrSetPropertyRecord::uses();
  369. if (value_.isObject()) {
  370. uses.push_back(decodeObject(value_));
  371. }
  372. return uses;
  373. }
  374. };
  375. /// A HasPropertyRecord is an event where native code queries whether a
  376. /// property exists on an object. (We don't care about the result because
  377. /// it cannot influence the trace.)
  378. struct HasPropertyRecord final : public Record {
  379. static constexpr RecordType type{RecordType::HasProperty};
  380. const ObjectID objID_;
  381. const std::string propName_;
  382. explicit HasPropertyRecord(
  383. TimeSinceStart time,
  384. ObjectID objID,
  385. const std::string &propName)
  386. : Record(time), objID_(objID), propName_(propName) {}
  387. bool operator==(const Record &that) const final;
  388. void toJSONInternal(::hermes::JSONEmitter &json, const SynthTrace &trace)
  389. const override;
  390. RecordType getType() const override {
  391. return type;
  392. }
  393. std::vector<ObjectID> uses() const override {
  394. return {objID_};
  395. }
  396. };
  397. struct GetPropertyNamesRecord final : public Record {
  398. static constexpr RecordType type{RecordType::GetPropertyNames};
  399. const ObjectID objID_;
  400. // Since getPropertyNames always returns an array, this can be an object id
  401. // rather than a TraceValue.
  402. const ObjectID propNamesID_;
  403. explicit GetPropertyNamesRecord(
  404. TimeSinceStart time,
  405. ObjectID objID,
  406. ObjectID propNamesID)
  407. : Record(time), objID_(objID), propNamesID_(propNamesID) {}
  408. bool operator==(const Record &that) const final;
  409. void toJSONInternal(::hermes::JSONEmitter &json, const SynthTrace &trace)
  410. const override;
  411. RecordType getType() const override {
  412. return type;
  413. }
  414. std::vector<ObjectID> defs() const override {
  415. return {propNamesID_};
  416. }
  417. std::vector<ObjectID> uses() const override {
  418. return {objID_};
  419. }
  420. };
  421. /// A CreateArrayRecord is an event where a new array is created of a specific
  422. /// length.
  423. struct CreateArrayRecord final : public Record {
  424. static constexpr RecordType type{RecordType::CreateArray};
  425. const ObjectID objID_;
  426. const size_t length_;
  427. explicit CreateArrayRecord(
  428. TimeSinceStart time,
  429. ObjectID objID,
  430. size_t length)
  431. : Record(time), objID_(objID), length_(length) {}
  432. bool operator==(const Record &that) const final;
  433. void toJSONInternal(::hermes::JSONEmitter &json, const SynthTrace &trace)
  434. const override;
  435. RecordType getType() const override {
  436. return type;
  437. }
  438. std::vector<ObjectID> defs() const override {
  439. return {objID_};
  440. }
  441. };
  442. struct ArrayReadOrWriteRecord : public Record {
  443. const ObjectID objID_;
  444. const size_t index_;
  445. const TraceValue value_;
  446. explicit ArrayReadOrWriteRecord(
  447. TimeSinceStart time,
  448. ObjectID objID,
  449. size_t index,
  450. TraceValue value)
  451. : Record(time), objID_(objID), index_(index), value_(value) {}
  452. bool operator==(const Record &that) const final;
  453. void toJSONInternal(::hermes::JSONEmitter &json, const SynthTrace &trace)
  454. const override;
  455. std::vector<ObjectID> uses() const override {
  456. return {objID_};
  457. }
  458. };
  459. /// An ArrayReadRecord is an event where a value was read from an index
  460. /// of an array.
  461. /// It is modeled separately from GetProperty because it is more efficient to
  462. /// read from a numeric index on an array than a string.
  463. struct ArrayReadRecord final : public ArrayReadOrWriteRecord {
  464. static constexpr RecordType type{RecordType::ArrayRead};
  465. using ArrayReadOrWriteRecord::ArrayReadOrWriteRecord;
  466. RecordType getType() const override {
  467. return type;
  468. }
  469. std::vector<ObjectID> defs() const override {
  470. auto defs = ArrayReadOrWriteRecord::defs();
  471. if (value_.isObject()) {
  472. defs.push_back(decodeObject(value_));
  473. }
  474. return defs;
  475. }
  476. };
  477. /// An ArrayWriteRecord is an event where a value was written into an index
  478. /// of an array.
  479. struct ArrayWriteRecord final : public ArrayReadOrWriteRecord {
  480. static constexpr RecordType type{RecordType::ArrayWrite};
  481. using ArrayReadOrWriteRecord::ArrayReadOrWriteRecord;
  482. RecordType getType() const override {
  483. return type;
  484. }
  485. std::vector<ObjectID> uses() const override {
  486. auto uses = ArrayReadOrWriteRecord::uses();
  487. if (value_.isObject()) {
  488. uses.push_back(decodeObject(value_));
  489. }
  490. return uses;
  491. }
  492. };
  493. struct CallRecord : public Record {
  494. /// The functionID_ is the id of the function JS object that is called from
  495. /// JS.
  496. const ObjectID functionID_;
  497. const TraceValue thisArg_;
  498. /// The arguments given to a call (excluding the this parameter),
  499. /// already JSON stringified.
  500. const std::vector<TraceValue> args_;
  501. explicit CallRecord(
  502. TimeSinceStart time,
  503. ObjectID functionID,
  504. TraceValue thisArg,
  505. const std::vector<TraceValue> &args)
  506. : Record(time),
  507. functionID_(functionID),
  508. thisArg_(thisArg),
  509. args_(args) {}
  510. bool operator==(const Record &that) const final;
  511. void toJSONInternal(::hermes::JSONEmitter &json, const SynthTrace &trace)
  512. const override;
  513. std::vector<ObjectID> uses() const override {
  514. // The function is used regardless of direction.
  515. return {functionID_};
  516. }
  517. protected:
  518. std::vector<ObjectID> getArgObjects() const {
  519. std::vector<ObjectID> objs;
  520. if (thisArg_.isObject()) {
  521. objs.push_back(decodeObject(thisArg_));
  522. }
  523. for (const auto &arg : args_) {
  524. if (arg.isObject()) {
  525. objs.push_back(decodeObject(arg));
  526. }
  527. }
  528. return objs;
  529. }
  530. };
  531. /// A CallFromNativeRecord is an event where native code calls into a JS
  532. /// function.
  533. struct CallFromNativeRecord : public CallRecord {
  534. static constexpr RecordType type{RecordType::CallFromNative};
  535. using CallRecord::CallRecord;
  536. RecordType getType() const override {
  537. return type;
  538. }
  539. std::vector<ObjectID> uses() const override {
  540. auto uses = CallRecord::uses();
  541. auto objs = CallRecord::getArgObjects();
  542. uses.insert(uses.end(), objs.begin(), objs.end());
  543. return uses;
  544. }
  545. };
  546. /// A ConstructFromNativeRecord is the same as \c CallFromNativeRecord, except
  547. /// the function is called with the new operator.
  548. struct ConstructFromNativeRecord final : public CallFromNativeRecord {
  549. static constexpr RecordType type{RecordType::ConstructFromNative};
  550. using CallFromNativeRecord::CallFromNativeRecord;
  551. RecordType getType() const override {
  552. return type;
  553. }
  554. };
  555. /// A ReturnFromNativeRecord is an event where a native function returns to a
  556. /// JS caller.
  557. /// It pairs with \c CallToNativeRecord.
  558. struct ReturnFromNativeRecord final : public Record, public ReturnMixin {
  559. static constexpr RecordType type{RecordType::ReturnFromNative};
  560. ReturnFromNativeRecord(TimeSinceStart time, TraceValue retVal)
  561. : Record(time), ReturnMixin(retVal) {}
  562. RecordType getType() const override {
  563. return type;
  564. }
  565. std::vector<ObjectID> uses() const override {
  566. auto uses = Record::uses();
  567. if (retVal_.isObject()) {
  568. uses.push_back(decodeObject(retVal_));
  569. }
  570. return uses;
  571. }
  572. bool operator==(const Record &that) const final;
  573. void toJSONInternal(::hermes::JSONEmitter &json, const SynthTrace &trace)
  574. const override;
  575. };
  576. /// A ReturnToNativeRecord is an event where a JS function returns to a native
  577. /// caller.
  578. /// It pairs with \c CallFromNativeRecord.
  579. struct ReturnToNativeRecord final : public Record, public ReturnMixin {
  580. static constexpr RecordType type{RecordType::ReturnToNative};
  581. ReturnToNativeRecord(TimeSinceStart time, TraceValue retVal)
  582. : Record(time), ReturnMixin(retVal) {}
  583. RecordType getType() const override {
  584. return type;
  585. }
  586. std::vector<ObjectID> defs() const override {
  587. auto defs = Record::defs();
  588. if (retVal_.isObject()) {
  589. defs.push_back(decodeObject(retVal_));
  590. }
  591. return defs;
  592. }
  593. bool operator==(const Record &that) const final;
  594. void toJSONInternal(::hermes::JSONEmitter &json, const SynthTrace &trace)
  595. const override;
  596. };
  597. /// A CallToNativeRecord is an event where JS code calls into a natively
  598. /// defined function.
  599. struct CallToNativeRecord final : public CallRecord {
  600. static constexpr RecordType type{RecordType::CallToNative};
  601. using CallRecord::CallRecord;
  602. RecordType getType() const override {
  603. return type;
  604. }
  605. std::vector<ObjectID> defs() const override {
  606. auto defs = CallRecord::defs();
  607. auto objs = CallRecord::getArgObjects();
  608. defs.insert(defs.end(), objs.begin(), objs.end());
  609. return defs;
  610. }
  611. };
  612. struct GetOrSetPropertyNativeRecord : public Record {
  613. const ObjectID hostObjectID_;
  614. const std::string propName_;
  615. explicit GetOrSetPropertyNativeRecord(
  616. TimeSinceStart time,
  617. ObjectID hostObjectID,
  618. std::string propName)
  619. : Record(time), hostObjectID_(hostObjectID), propName_(propName) {}
  620. void toJSONInternal(::hermes::JSONEmitter &json, const SynthTrace &trace)
  621. const override;
  622. std::vector<ObjectID> uses() const override {
  623. return {hostObjectID_};
  624. }
  625. protected:
  626. bool operator==(const Record &that) const override;
  627. };
  628. /// A GetPropertyNativeRecord is an event where JS tries to access a property
  629. /// on a native object.
  630. /// This needs to be modeled as a call with no arguments, since native code
  631. /// can arbitrarily affect the JS heap during the accessor.
  632. struct GetPropertyNativeRecord final : public GetOrSetPropertyNativeRecord {
  633. static constexpr RecordType type{RecordType::GetPropertyNative};
  634. using GetOrSetPropertyNativeRecord::GetOrSetPropertyNativeRecord;
  635. RecordType getType() const override {
  636. return type;
  637. }
  638. bool operator==(const Record &that) const final;
  639. };
  640. struct GetPropertyNativeReturnRecord final : public Record,
  641. public ReturnMixin {
  642. static constexpr RecordType type{RecordType::GetPropertyNativeReturn};
  643. GetPropertyNativeReturnRecord(TimeSinceStart time, TraceValue retVal)
  644. : Record(time), ReturnMixin(retVal) {}
  645. RecordType getType() const override {
  646. return type;
  647. }
  648. std::vector<ObjectID> uses() const override {
  649. auto uses = Record::uses();
  650. if (retVal_.isObject()) {
  651. uses.push_back(decodeObject(retVal_));
  652. }
  653. return uses;
  654. }
  655. bool operator==(const Record &that) const final;
  656. protected:
  657. void toJSONInternal(::hermes::JSONEmitter &json, const SynthTrace &trace)
  658. const override;
  659. };
  660. /// A SetPropertyNativeRecord is an event where JS code writes to the property
  661. /// of a Native object.
  662. /// This needs to be modeled as a call with one argument, since native code
  663. /// can arbitrarily affect the JS heap during the accessor.
  664. struct SetPropertyNativeRecord final : public GetOrSetPropertyNativeRecord {
  665. static constexpr RecordType type{RecordType::SetPropertyNative};
  666. TraceValue value_;
  667. explicit SetPropertyNativeRecord(
  668. TimeSinceStart time,
  669. ObjectID hostObjectID,
  670. std::string propName,
  671. TraceValue value)
  672. : GetOrSetPropertyNativeRecord(time, hostObjectID, propName),
  673. value_(value) {}
  674. bool operator==(const Record &that) const final;
  675. void toJSONInternal(::hermes::JSONEmitter &json, const SynthTrace &trace)
  676. const override;
  677. RecordType getType() const override {
  678. return type;
  679. }
  680. std::vector<ObjectID> defs() const override {
  681. auto defs = GetOrSetPropertyNativeRecord::defs();
  682. if (value_.isObject()) {
  683. defs.push_back(decodeObject(value_));
  684. }
  685. return defs;
  686. }
  687. };
  688. /// A SetPropertyNativeReturnRecord needs to record no extra information
  689. struct SetPropertyNativeReturnRecord final : public Record {
  690. static constexpr RecordType type{RecordType::SetPropertyNativeReturn};
  691. using Record::Record;
  692. RecordType getType() const override {
  693. return type;
  694. }
  695. bool operator==(const Record &that) const final {
  696. // Since there are no fields to compare, any two will always be the same.
  697. return Record::operator==(that);
  698. }
  699. };
  700. /// A GetNativePropertyNamesRecord records an event where JS asked for a list
  701. /// of property names available on a host object. It records the object, and
  702. /// the returned list of property names.
  703. struct GetNativePropertyNamesRecord : public Record {
  704. static constexpr RecordType type{RecordType::GetNativePropertyNames};
  705. const ObjectID hostObjectID_;
  706. explicit GetNativePropertyNamesRecord(
  707. TimeSinceStart time,
  708. ObjectID hostObjectID)
  709. : Record(time), hostObjectID_(hostObjectID) {}
  710. RecordType getType() const override {
  711. return type;
  712. }
  713. void toJSONInternal(::hermes::JSONEmitter &json, const SynthTrace &trace)
  714. const override;
  715. std::vector<ObjectID> uses() const override {
  716. return {hostObjectID_};
  717. }
  718. bool operator==(const Record &that) const override;
  719. };
  720. /// A GetNativePropertyNamesReturnRecord records what property names were
  721. /// returned by the GetNativePropertyNames query.
  722. struct GetNativePropertyNamesReturnRecord final : public Record {
  723. static constexpr RecordType type{RecordType::GetNativePropertyNamesReturn};
  724. const std::vector<std::string> propNames_;
  725. explicit GetNativePropertyNamesReturnRecord(
  726. TimeSinceStart time,
  727. const std::vector<std::string> &propNames)
  728. : Record(time), propNames_(propNames) {}
  729. RecordType getType() const override {
  730. return type;
  731. }
  732. void toJSONInternal(::hermes::JSONEmitter &json, const SynthTrace &trace)
  733. const override;
  734. bool operator==(const Record &that) const override;
  735. };
  736. /// Completes writing of the trace to the trace stream. If writing
  737. /// to a file, disables further writing to the file, or accumulation
  738. /// of data.
  739. void flushAndDisable(const ::hermes::vm::MockedEnvironment &env);
  740. };
  741. llvm::raw_ostream &operator<<(
  742. llvm::raw_ostream &os,
  743. SynthTrace::RecordType type);
  744. std::istream &operator>>(std::istream &is, SynthTrace::RecordType &type);
  745. } // namespace tracing
  746. } // namespace hermes
  747. } // namespace facebook
  748. #endif // HERMESVM_API_TRACE
  749. #endif // HERMES_SYNTHTRACE_H