Bug 1568151 - Replace `target.getInspector()` by `target.getFront("inspector")`....
[gecko.git] / mfbt / RecordReplay.h
blobd1f64ffdedc1370e8997a140ad7245df75f94c69
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /* Public API for Web Replay. */
9 #ifndef mozilla_RecordReplay_h
10 #define mozilla_RecordReplay_h
12 #include "mozilla/Attributes.h"
13 #include "mozilla/GuardObjects.h"
14 #include "mozilla/TemplateLib.h"
15 #include "mozilla/Types.h"
16 #include "mozilla/Utf8.h"
18 #include <functional>
19 #include <stdarg.h>
21 struct PLDHashTableOps;
22 struct JSContext;
23 class JSObject;
25 namespace mozilla {
26 namespace recordreplay {
28 // Record/Replay Overview.
30 // Firefox content processes can be specified to record or replay their
31 // behavior. Whether a process is recording or replaying is initialized at the
32 // start of the main() routine, and is afterward invariant for the process.
34 // Recording and replaying works by controlling non-determinism in the browser:
35 // non-deterministic behaviors are initially recorded, then later replayed
36 // exactly to force the browser to behave deterministically. Two types of
37 // non-deterministic behaviors are captured: intra-thread and inter-thread.
38 // Intra-thread non-deterministic behaviors are non-deterministic even in the
39 // absence of actions by other threads, and inter-thread non-deterministic
40 // behaviors are those affected by interleaving execution with other threads.
42 // Intra-thread non-determinism is recorded and replayed as a stream of events
43 // for each thread. Most events originate from calls to system library
44 // functions (for i/o and such); the record/replay system handles these
45 // internally by redirecting these library functions so that code can be
46 // injected and the event recorded/replayed. Events can also be manually
47 // performed using the RecordReplayValue and RecordReplayBytes APIs below.
49 // Inter-thread non-determinism is recorded and replayed by keeping track of
50 // the order in which threads acquire locks or perform atomic accesses. If the
51 // program is data race free, then reproducing the order of these operations
52 // will give an interleaving that is functionally (if not exactly) the same
53 // as during the recording. As for intra-thread non-determinism, system library
54 // redirections are used to capture most inter-thread non-determinism, but the
55 // {Begin,End}OrderedAtomicAccess APIs below can be used to add new ordering
56 // constraints.
58 // Some behaviors can differ between recording and replay. Mainly, pointer
59 // values can differ, and JS GCs can occur at different points (a more complete
60 // list is at the URL below). Some of the APIs below are used to accommodate
61 // these behaviors and keep the replaying process on track.
63 // A third process type, middleman processes, are normal content processes
64 // which facilitate communication with recording and replaying processes,
65 // managing the graphics data they generate, and running devtools code that
66 // interacts with them.
68 // This file contains the main public API for places where mozilla code needs
69 // to interact with the record/replay system. There are a few additional public
70 // APIs in toolkit/recordreplay/ipc, for the IPC performed by
71 // recording/replaying processes and middleman processes.
73 // A more complete description of Web Replay can be found at this URL:
74 // https://developer.mozilla.org/en-US/docs/WebReplay
76 ///////////////////////////////////////////////////////////////////////////////
77 // Public API
78 ///////////////////////////////////////////////////////////////////////////////
80 // Recording and replaying is only enabled on Mac nightlies.
81 #if defined(XP_MACOSX) && defined(NIGHTLY_BUILD)
83 extern MFBT_DATA bool gIsRecordingOrReplaying;
84 extern MFBT_DATA bool gIsRecording;
85 extern MFBT_DATA bool gIsReplaying;
86 extern MFBT_DATA bool gIsMiddleman;
88 // Get the kind of recording/replaying process this is, if any.
89 static inline bool IsRecordingOrReplaying() { return gIsRecordingOrReplaying; }
90 static inline bool IsRecording() { return gIsRecording; }
91 static inline bool IsReplaying() { return gIsReplaying; }
92 static inline bool IsMiddleman() { return gIsMiddleman; }
94 #else // XP_MACOSX && NIGHTLY_BUILD
96 // On unsupported platforms, getting the kind of process is a no-op.
97 static inline bool IsRecordingOrReplaying() { return false; }
98 static inline bool IsRecording() { return false; }
99 static inline bool IsReplaying() { return false; }
100 static inline bool IsMiddleman() { return false; }
102 #endif // XP_MACOSX && NIGHTLY_BUILD
104 // Mark a region which occurs atomically wrt the recording. No two threads can
105 // be in an atomic region at once, and the order in which atomic sections are
106 // executed by the various threads for the same aValue will be the same in the
107 // replay as in the recording. These calls have no effect when not recording or
108 // replaying.
109 static inline void BeginOrderedAtomicAccess(const void* aValue);
110 static inline void EndOrderedAtomicAccess();
112 // RAII class for an atomic access.
113 struct MOZ_RAII AutoOrderedAtomicAccess {
114 explicit AutoOrderedAtomicAccess(const void* aValue) {
115 BeginOrderedAtomicAccess(aValue);
117 ~AutoOrderedAtomicAccess() { EndOrderedAtomicAccess(); }
120 // Mark a region where thread events are passed through the record/replay
121 // system. While recording, no information from system calls or other events
122 // will be recorded for the thread. While replaying, system calls and other
123 // events are performed normally.
124 static inline void BeginPassThroughThreadEvents();
125 static inline void EndPassThroughThreadEvents();
127 // Whether events in this thread are passed through.
128 static inline bool AreThreadEventsPassedThrough();
130 // RAII class for regions where thread events are passed through.
131 struct MOZ_RAII AutoPassThroughThreadEvents {
132 AutoPassThroughThreadEvents() { BeginPassThroughThreadEvents(); }
133 ~AutoPassThroughThreadEvents() { EndPassThroughThreadEvents(); }
136 // As for AutoPassThroughThreadEvents, but may be used when events are already
137 // passed through.
138 struct MOZ_RAII AutoEnsurePassThroughThreadEvents {
139 AutoEnsurePassThroughThreadEvents()
140 : mPassedThrough(AreThreadEventsPassedThrough()) {
141 if (!mPassedThrough) BeginPassThroughThreadEvents();
144 ~AutoEnsurePassThroughThreadEvents() {
145 if (!mPassedThrough) EndPassThroughThreadEvents();
148 private:
149 bool mPassedThrough;
152 // Mark a region where thread events are not allowed to occur. The process will
153 // crash immediately if an event does happen.
154 static inline void BeginDisallowThreadEvents();
155 static inline void EndDisallowThreadEvents();
157 // Whether events in this thread are disallowed.
158 static inline bool AreThreadEventsDisallowed();
160 // RAII class for a region where thread events are disallowed.
161 struct MOZ_RAII AutoDisallowThreadEvents {
162 AutoDisallowThreadEvents() { BeginDisallowThreadEvents(); }
163 ~AutoDisallowThreadEvents() { EndDisallowThreadEvents(); }
166 // Record or replay a value in the current thread's event stream.
167 static inline size_t RecordReplayValue(size_t aValue);
169 // Record or replay the contents of a range of memory in the current thread's
170 // event stream.
171 static inline void RecordReplayBytes(void* aData, size_t aSize);
173 // During recording or replay, mark the recording as unusable. There are some
174 // behaviors that can't be reliably recorded or replayed. For more information,
175 // see 'Unrecordable Executions' in the URL above.
176 static inline void InvalidateRecording(const char* aWhy);
178 // API for ensuring deterministic recording and replaying of PLDHashTables.
179 // This allows PLDHashTables to behave deterministically by generating a custom
180 // set of operations for each table and requiring no other instrumentation.
181 // (PLHashTables have a similar mechanism, though it is not exposed here.)
182 static inline const PLDHashTableOps* GeneratePLDHashTableCallbacks(
183 const PLDHashTableOps* aOps);
184 static inline const PLDHashTableOps* UnwrapPLDHashTableCallbacks(
185 const PLDHashTableOps* aOps);
186 static inline void DestroyPLDHashTableCallbacks(const PLDHashTableOps* aOps);
187 static inline void MovePLDHashTableContents(const PLDHashTableOps* aFirstOps,
188 const PLDHashTableOps* aSecondOps);
190 // Associate an arbitrary pointer with a JS object root while replaying. This
191 // is useful for replaying the behavior of weak pointers.
192 MFBT_API void SetWeakPointerJSRoot(const void* aPtr, JSObject* aJSObj);
194 // API for ensuring that a function executes at a consistent point when
195 // recording or replaying. This is primarily needed for finalizers and other
196 // activity during a GC that can perform recorded events (because GCs can
197 // occur at different times and behave differently between recording and
198 // replay, thread events are disallowed during a GC). Triggers can be
199 // registered at a point where thread events are allowed, then activated at
200 // a point where thread events are not allowed. When recording, the trigger's
201 // callback will execute at the next point when ExecuteTriggers is called on
202 // the thread which originally registered the trigger (typically at the top of
203 // the thread's event loop), and when replaying the callback will execute at
204 // the same point, even if it was never activated.
206 // Below is an example of how this API can be used.
208 // // This structure's lifetime is managed by the GC.
209 // struct GarbageCollectedHolder {
210 // GarbageCollectedHolder() {
211 // RegisterTrigger(this, [=]() { this->DestroyContents(); });
212 // }
213 // ~GarbageCollectedHolder() {
214 // UnregisterTrigger(this);
215 // }
217 // void Finalize() {
218 // // During finalization, thread events are disallowed.
219 // if (IsRecordingOrReplaying()) {
220 // ActivateTrigger(this);
221 // } else {
222 // DestroyContents();
223 // }
224 // }
226 // // This is free to release resources held by the system, communicate with
227 // // other threads or processes, and so forth. When replaying, this may
228 // // be called before the GC has actually collected this object, but since
229 // // the GC will have already collected this object at this point in the
230 // // recording, this object will never be accessed again.
231 // void DestroyContents();
232 // };
233 MFBT_API void RegisterTrigger(void* aObj,
234 const std::function<void()>& aCallback);
235 MFBT_API void UnregisterTrigger(void* aObj);
236 MFBT_API void ActivateTrigger(void* aObj);
237 MFBT_API void ExecuteTriggers();
239 // Some devtools operations which execute in a replaying process can cause code
240 // to run which did not run while recording. For example, the JS debugger can
241 // run arbitrary JS while paused at a breakpoint, by doing an eval(). In such
242 // cases we say that execution has diverged from the recording, and if recorded
243 // events are encountered the associated devtools operation fails. This API can
244 // be used to test for such cases and avoid causing the operation to fail.
245 static inline bool HasDivergedFromRecording();
247 // API for debugging inconsistent behavior between recording and replay.
248 // By calling Assert or AssertBytes a thread event will be inserted and any
249 // inconsistent execution order of events will be detected (as for normal
250 // thread events) and reported to the console.
252 // RegisterThing/UnregisterThing associate arbitrary pointers with indexes that
253 // will be consistent between recording/replaying and can be used in assertion
254 // strings.
255 static inline void RecordReplayAssert(const char* aFormat, ...);
256 static inline void RecordReplayAssertBytes(const void* aData, size_t aSize);
257 static inline void RegisterThing(void* aThing);
258 static inline void UnregisterThing(void* aThing);
259 static inline size_t ThingIndex(void* aThing);
261 // Helper for record/replay asserts, try to determine a name for a C++ object
262 // with virtual methods based on its vtable.
263 static inline const char* VirtualThingName(void* aThing);
265 // Enum which describes whether to preserve behavior between recording and
266 // replay sessions.
267 enum class Behavior { DontPreserve, Preserve };
269 // Determine whether this is a recording/replaying or middleman process, and
270 // initialize record/replay state if so.
271 MFBT_API void Initialize(int aArgc, char* aArgv[]);
273 // Kinds of recording/replaying processes that can be spawned.
274 enum class ProcessKind {
275 Recording,
276 Replaying,
277 MiddlemanRecording,
278 MiddlemanReplaying
281 // Command line option for specifying the record/replay kind of a process.
282 static const char gProcessKindOption[] = "-recordReplayKind";
284 // Command line option for specifying the recording file to use.
285 static const char gRecordingFileOption[] = "-recordReplayFile";
287 ///////////////////////////////////////////////////////////////////////////////
288 // JS interface
289 ///////////////////////////////////////////////////////////////////////////////
291 // Get the counter used to keep track of how much progress JS execution has
292 // made while running on the main thread. Progress must advance whenever a JS
293 // function is entered or loop entry point is reached, so that no script
294 // location may be hit twice while the progress counter is the same. See
295 // JSControl.h for more.
296 typedef uint64_t ProgressCounter;
297 MFBT_API ProgressCounter* ExecutionProgressCounter();
299 static inline void AdvanceExecutionProgressCounter() {
300 ++*ExecutionProgressCounter();
303 // Get an identifier for the current execution point which can be used to warp
304 // here later.
305 MFBT_API ProgressCounter NewTimeWarpTarget();
307 // Return whether a script should update the progress counter when it runs.
308 MFBT_API bool ShouldUpdateProgressCounter(const char* aURL);
310 // Define a RecordReplayControl object on the specified global object, with
311 // methods specialized to the current recording/replaying or middleman process
312 // kind.
313 MFBT_API bool DefineRecordReplayControlObject(JSContext* aCx, JSObject* aObj);
315 // Notify the infrastructure that some URL which contains JavaScript or CSS is
316 // being parsed. This is used to provide the complete contents of the URL to
317 // devtools code when it is inspecting the state of this process; that devtools
318 // code can't simply fetch the URL itself since it may have been changed since
319 // the recording was made or may no longer exist. The token for a parse may not
320 // be used in other parses until after EndContentParse() is called.
321 MFBT_API void BeginContentParse(const void* aToken, const char* aURL,
322 const char* aContentType);
324 // Add some UTF-8 parse data to an existing content parse.
325 MFBT_API void AddContentParseData8(const void* aToken,
326 const Utf8Unit* aUtf8Buffer, size_t aLength);
328 // Add some UTF-16 parse data to an existing content parse.
329 MFBT_API void AddContentParseData16(const void* aToken, const char16_t* aBuffer,
330 size_t aLength);
332 // Mark a content parse as having completed.
333 MFBT_API void EndContentParse(const void* aToken);
335 // Perform an entire content parse of UTF-8 data.
336 static inline void NoteContentParse(const void* aToken, const char* aURL,
337 const char* aContentType,
338 const Utf8Unit* aUtf8Buffer,
339 size_t aLength) {
340 BeginContentParse(aToken, aURL, aContentType);
341 AddContentParseData8(aToken, aUtf8Buffer, aLength);
342 EndContentParse(aToken);
345 // Perform an entire content parse of UTF-16 data.
346 static inline void NoteContentParse(const void* aToken, const char* aURL,
347 const char* aContentType,
348 const char16_t* aBuffer, size_t aLength) {
349 BeginContentParse(aToken, aURL, aContentType);
350 AddContentParseData16(aToken, aBuffer, aLength);
351 EndContentParse(aToken);
354 ///////////////////////////////////////////////////////////////////////////////
355 // API inline function implementation
356 ///////////////////////////////////////////////////////////////////////////////
358 // Define inline wrappers on builds where recording/replaying is enabled.
359 #if defined(XP_MACOSX) && defined(NIGHTLY_BUILD)
361 # define MOZ_MAKE_RECORD_REPLAY_WRAPPER_VOID(aName, aFormals, aActuals) \
362 MFBT_API void Internal##aName aFormals; \
363 static inline void aName aFormals { \
364 if (IsRecordingOrReplaying()) { \
365 Internal##aName aActuals; \
369 # define MOZ_MAKE_RECORD_REPLAY_WRAPPER(aName, aReturnType, aDefaultValue, \
370 aFormals, aActuals) \
371 MFBT_API aReturnType Internal##aName aFormals; \
372 static inline aReturnType aName aFormals { \
373 if (IsRecordingOrReplaying()) { \
374 return Internal##aName aActuals; \
376 return aDefaultValue; \
379 // Define inline wrappers on other builds. Avoiding references to the out of
380 // line method avoids link errors when e.g. using Atomic<> but not linking
381 // against MFBT.
382 #else
384 # define MOZ_MAKE_RECORD_REPLAY_WRAPPER_VOID(aName, aFormals, aActuals) \
385 static inline void aName aFormals {}
387 # define MOZ_MAKE_RECORD_REPLAY_WRAPPER(aName, aReturnType, aDefaultValue, \
388 aFormals, aActuals) \
389 static inline aReturnType aName aFormals { return aDefaultValue; }
391 #endif
393 MOZ_MAKE_RECORD_REPLAY_WRAPPER_VOID(BeginOrderedAtomicAccess,
394 (const void* aValue), (aValue))
395 MOZ_MAKE_RECORD_REPLAY_WRAPPER_VOID(EndOrderedAtomicAccess, (), ())
396 MOZ_MAKE_RECORD_REPLAY_WRAPPER_VOID(BeginPassThroughThreadEvents, (), ())
397 MOZ_MAKE_RECORD_REPLAY_WRAPPER_VOID(EndPassThroughThreadEvents, (), ())
398 MOZ_MAKE_RECORD_REPLAY_WRAPPER(AreThreadEventsPassedThrough, bool, false, (),
400 MOZ_MAKE_RECORD_REPLAY_WRAPPER_VOID(BeginDisallowThreadEvents, (), ())
401 MOZ_MAKE_RECORD_REPLAY_WRAPPER_VOID(EndDisallowThreadEvents, (), ())
402 MOZ_MAKE_RECORD_REPLAY_WRAPPER(AreThreadEventsDisallowed, bool, false, (), ())
403 MOZ_MAKE_RECORD_REPLAY_WRAPPER(RecordReplayValue, size_t, aValue,
404 (size_t aValue), (aValue))
405 MOZ_MAKE_RECORD_REPLAY_WRAPPER_VOID(RecordReplayBytes,
406 (void* aData, size_t aSize), (aData, aSize))
407 MOZ_MAKE_RECORD_REPLAY_WRAPPER(HasDivergedFromRecording, bool, false, (), ())
408 MOZ_MAKE_RECORD_REPLAY_WRAPPER(GeneratePLDHashTableCallbacks,
409 const PLDHashTableOps*, aOps,
410 (const PLDHashTableOps* aOps), (aOps))
411 MOZ_MAKE_RECORD_REPLAY_WRAPPER(UnwrapPLDHashTableCallbacks,
412 const PLDHashTableOps*, aOps,
413 (const PLDHashTableOps* aOps), (aOps))
414 MOZ_MAKE_RECORD_REPLAY_WRAPPER_VOID(DestroyPLDHashTableCallbacks,
415 (const PLDHashTableOps* aOps), (aOps))
416 MOZ_MAKE_RECORD_REPLAY_WRAPPER_VOID(MovePLDHashTableContents,
417 (const PLDHashTableOps* aFirstOps,
418 const PLDHashTableOps* aSecondOps),
419 (aFirstOps, aSecondOps))
420 MOZ_MAKE_RECORD_REPLAY_WRAPPER_VOID(InvalidateRecording, (const char* aWhy),
421 (aWhy))
422 MOZ_MAKE_RECORD_REPLAY_WRAPPER_VOID(
423 RegisterWeakPointer,
424 (const void* aPtr, const std::function<void(bool)>& aCallback),
425 (aPtr, aCallback))
426 MOZ_MAKE_RECORD_REPLAY_WRAPPER_VOID(UnregisterWeakPointer, (const void* aPtr),
427 (aPtr))
428 MOZ_MAKE_RECORD_REPLAY_WRAPPER_VOID(WeakPointerAccess,
429 (const void* aPtr, bool aSuccess),
430 (aPtr, aSuccess))
431 MOZ_MAKE_RECORD_REPLAY_WRAPPER_VOID(RecordReplayAssertBytes,
432 (const void* aData, size_t aSize),
433 (aData, aSize))
434 MOZ_MAKE_RECORD_REPLAY_WRAPPER_VOID(RegisterThing, (void* aThing), (aThing))
435 MOZ_MAKE_RECORD_REPLAY_WRAPPER_VOID(UnregisterThing, (void* aThing), (aThing))
436 MOZ_MAKE_RECORD_REPLAY_WRAPPER(ThingIndex, size_t, 0, (void* aThing), (aThing))
437 MOZ_MAKE_RECORD_REPLAY_WRAPPER(VirtualThingName, const char*, nullptr,
438 (void* aThing), (aThing))
440 #undef MOZ_MAKE_RECORD_REPLAY_WRAPPER_VOID
441 #undef MOZ_MAKERECORDREPLAYWRAPPER
443 MFBT_API void InternalRecordReplayAssert(const char* aFormat, va_list aArgs);
445 static inline void RecordReplayAssert(const char* aFormat, ...) {
446 if (IsRecordingOrReplaying()) {
447 va_list ap;
448 va_start(ap, aFormat);
449 InternalRecordReplayAssert(aFormat, ap);
450 va_end(ap);
454 } // namespace recordreplay
455 } // namespace mozilla
457 #endif /* mozilla_RecordReplay_h */