/src/mozilla-central/mfbt/RecordReplay.cpp
Line | Count | Source (jump to first uncovered line) |
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/. */ |
6 | | |
7 | | #include "RecordReplay.h" |
8 | | |
9 | | #include "js/GCAnnotations.h" |
10 | | #include "mozilla/Atomics.h" |
11 | | #include "mozilla/Casting.h" |
12 | | |
13 | | #include <stdlib.h> |
14 | | |
15 | | // Recording and replaying is only enabled on Mac nightlies. |
16 | | #if defined(XP_MACOSX) && defined(NIGHTLY_BUILD) |
17 | | #define ENABLE_RECORD_REPLAY |
18 | | #endif |
19 | | |
20 | | #ifdef ENABLE_RECORD_REPLAY |
21 | | #include <dlfcn.h> |
22 | | #endif |
23 | | |
24 | | namespace mozilla { |
25 | | namespace recordreplay { |
26 | | |
27 | | #define FOR_EACH_INTERFACE(Macro) \ |
28 | 0 | Macro(InternalAreThreadEventsPassedThrough, bool, (), ()) \ |
29 | 0 | Macro(InternalAreThreadEventsDisallowed, bool, (), ()) \ |
30 | 0 | Macro(InternalRecordReplayValue, size_t, (size_t aValue), (aValue)) \ |
31 | 0 | Macro(InternalHasDivergedFromRecording, bool, (), ()) \ |
32 | 0 | Macro(InternalGeneratePLDHashTableCallbacks, const PLDHashTableOps*, \ |
33 | 0 | (const PLDHashTableOps* aOps), (aOps)) \ |
34 | 0 | Macro(InternalUnwrapPLDHashTableCallbacks, const PLDHashTableOps*, \ |
35 | 0 | (const PLDHashTableOps* aOps), (aOps)) \ |
36 | 0 | Macro(InternalThingIndex, size_t, (void* aThing), (aThing)) \ |
37 | 0 | Macro(InternalVirtualThingName, const char*, (void* aThing), (aThing)) \ |
38 | 0 | Macro(ExecutionProgressCounter, ProgressCounter*, (), ()) \ |
39 | 0 | Macro(NewTimeWarpTarget, ProgressCounter, (), ()) \ |
40 | 0 | Macro(IsInternalScript, bool, (const char* aURL), (aURL)) \ |
41 | 0 | Macro(DefineRecordReplayControlObject, bool, (JSContext* aCx, JSObject* aObj), (aCx, aObj)) |
42 | | |
43 | | #define FOR_EACH_INTERFACE_VOID(Macro) \ |
44 | 0 | Macro(InternalBeginOrderedAtomicAccess, (), ()) \ |
45 | 0 | Macro(InternalEndOrderedAtomicAccess, (), ()) \ |
46 | 0 | Macro(InternalBeginPassThroughThreadEvents, (), ()) \ |
47 | 0 | Macro(InternalEndPassThroughThreadEvents, (), ()) \ |
48 | 0 | Macro(InternalBeginDisallowThreadEvents, (), ()) \ |
49 | 0 | Macro(InternalEndDisallowThreadEvents, (), ()) \ |
50 | 0 | Macro(InternalRecordReplayBytes, \ |
51 | 0 | (void* aData, size_t aSize), (aData, aSize)) \ |
52 | 0 | Macro(NotifyUnrecordedWait, \ |
53 | 0 | (const std::function<void()>& aCallback), (aCallback)) \ |
54 | 0 | Macro(MaybeWaitForCheckpointSave, (), ()) \ |
55 | 0 | Macro(InternalInvalidateRecording, (const char* aWhy), (aWhy)) \ |
56 | 0 | Macro(InternalDestroyPLDHashTableCallbacks, \ |
57 | 0 | (const PLDHashTableOps* aOps), (aOps)) \ |
58 | 0 | Macro(InternalMovePLDHashTableContents, \ |
59 | 0 | (const PLDHashTableOps* aFirstOps, const PLDHashTableOps* aSecondOps), \ |
60 | 0 | (aFirstOps, aSecondOps)) \ |
61 | 0 | Macro(SetWeakPointerJSRoot, \ |
62 | 0 | (const void* aPtr, JSObject* aJSObj), (aPtr, aJSObj)) \ |
63 | 0 | Macro(RegisterTrigger, \ |
64 | 0 | (void* aObj, const std::function<void()>& aCallback), \ |
65 | 0 | (aObj, aCallback)) \ |
66 | 0 | Macro(UnregisterTrigger, \ |
67 | 0 | (void* aObj), (aObj)) \ |
68 | 0 | Macro(ActivateTrigger, (void* aObj), (aObj)) \ |
69 | 0 | Macro(ExecuteTriggers, (), ()) \ |
70 | 0 | Macro(InternalRecordReplayAssert, (const char* aFormat, va_list aArgs), (aFormat, aArgs)) \ |
71 | 0 | Macro(InternalRecordReplayAssertBytes, \ |
72 | 0 | (const void* aData, size_t aSize), (aData, aSize)) \ |
73 | 0 | Macro(InternalRegisterThing, (void* aThing), (aThing)) \ |
74 | 0 | Macro(InternalUnregisterThing, (void* aThing), (aThing)) \ |
75 | 0 | Macro(InternalRecordReplayDirective, (long aDirective), (aDirective)) \ |
76 | 0 | Macro(BeginContentParse, \ |
77 | 0 | (const void* aToken, const char* aURL, const char* aContentType), \ |
78 | 0 | (aToken, aURL, aContentType)) \ |
79 | 0 | Macro(AddContentParseData, \ |
80 | 0 | (const void* aToken, const char16_t* aBuffer, size_t aLength), \ |
81 | 0 | (aToken, aBuffer, aLength)) \ |
82 | 0 | Macro(EndContentParse, (const void* aToken), (aToken)) |
83 | | |
84 | | #define DECLARE_SYMBOL(aName, aReturnType, aFormals, _) \ |
85 | | static aReturnType (*gPtr ##aName) aFormals; |
86 | | #define DECLARE_SYMBOL_VOID(aName, aFormals, _) DECLARE_SYMBOL(aName, void, aFormals, _) |
87 | | |
88 | | FOR_EACH_INTERFACE(DECLARE_SYMBOL) |
89 | | FOR_EACH_INTERFACE_VOID(DECLARE_SYMBOL_VOID) |
90 | | |
91 | | #undef DECLARE_SYMBOL |
92 | | #undef DECLARE_SYMBOL_VOID |
93 | | |
94 | | static void* |
95 | | LoadSymbol(const char* aName) |
96 | 0 | { |
97 | | #ifdef ENABLE_RECORD_REPLAY |
98 | | void* rv = dlsym(RTLD_DEFAULT, aName); |
99 | | MOZ_RELEASE_ASSERT(rv); |
100 | | return rv; |
101 | | #else |
102 | | return nullptr; |
103 | 0 | #endif |
104 | 0 | } |
105 | | |
106 | | void |
107 | | Initialize(int aArgc, char* aArgv[]) |
108 | 0 | { |
109 | 0 | // Only initialize if the right command line option was specified. |
110 | 0 | bool found = false; |
111 | 0 | for (int i = 0; i < aArgc; i++) { |
112 | 0 | if (!strcmp(aArgv[i], gProcessKindOption)) { |
113 | 0 | found = true; |
114 | 0 | break; |
115 | 0 | } |
116 | 0 | } |
117 | 0 | if (!found) { |
118 | 0 | return; |
119 | 0 | } |
120 | 0 | |
121 | 0 | void (*initialize)(int, char**); |
122 | 0 | BitwiseCast(LoadSymbol("RecordReplayInterface_Initialize"), &initialize); |
123 | 0 | if (!initialize) { |
124 | 0 | return; |
125 | 0 | } |
126 | 0 | |
127 | 0 | #define INIT_SYMBOL(aName, _1, _2, _3) \ |
128 | 0 | BitwiseCast(LoadSymbol("RecordReplayInterface_" #aName), &gPtr ##aName); |
129 | 0 | #define INIT_SYMBOL_VOID(aName, _2, _3) INIT_SYMBOL(aName, void, _2, _3) |
130 | 0 |
|
131 | 0 | FOR_EACH_INTERFACE(INIT_SYMBOL) |
132 | 0 | FOR_EACH_INTERFACE_VOID(INIT_SYMBOL_VOID) |
133 | 0 |
|
134 | 0 | #undef INIT_SYMBOL |
135 | 0 | #undef INIT_SYMBOL_VOID |
136 | 0 |
|
137 | 0 | initialize(aArgc, aArgv); |
138 | 0 | } |
139 | | |
140 | | // Record/replay API functions can't GC, but we can't use |
141 | | // JS::AutoSuppressGCAnalysis here due to linking issues. |
142 | | struct AutoSuppressGCAnalysis |
143 | | { |
144 | 0 | AutoSuppressGCAnalysis() {} |
145 | 0 | ~AutoSuppressGCAnalysis() { |
146 | | #ifdef DEBUG |
147 | | // Need nontrivial destructor. |
148 | | static Atomic<int, SequentiallyConsistent, Behavior::DontPreserve> dummy; |
149 | | dummy++; |
150 | | #endif |
151 | | } |
152 | | } JS_HAZ_GC_SUPPRESSED; |
153 | | |
154 | | #define DEFINE_WRAPPER(aName, aReturnType, aFormals, aActuals) \ |
155 | | aReturnType aName aFormals \ |
156 | 0 | { \ |
157 | 0 | AutoSuppressGCAnalysis suppress; \ |
158 | 0 | MOZ_ASSERT(IsRecordingOrReplaying() || IsMiddleman()); \ |
159 | 0 | return gPtr ##aName aActuals; \ |
160 | 0 | } Unexecuted instantiation: mozilla::recordreplay::InternalAreThreadEventsPassedThrough() Unexecuted instantiation: mozilla::recordreplay::InternalAreThreadEventsDisallowed() Unexecuted instantiation: mozilla::recordreplay::InternalRecordReplayValue(unsigned long) Unexecuted instantiation: mozilla::recordreplay::InternalHasDivergedFromRecording() Unexecuted instantiation: mozilla::recordreplay::InternalGeneratePLDHashTableCallbacks(PLDHashTableOps const*) Unexecuted instantiation: mozilla::recordreplay::InternalUnwrapPLDHashTableCallbacks(PLDHashTableOps const*) Unexecuted instantiation: mozilla::recordreplay::InternalThingIndex(void*) Unexecuted instantiation: mozilla::recordreplay::InternalVirtualThingName(void*) Unexecuted instantiation: mozilla::recordreplay::ExecutionProgressCounter() Unexecuted instantiation: mozilla::recordreplay::NewTimeWarpTarget() Unexecuted instantiation: mozilla::recordreplay::IsInternalScript(char const*) Unexecuted instantiation: mozilla::recordreplay::DefineRecordReplayControlObject(JSContext*, JSObject*) |
161 | | |
162 | | #define DEFINE_WRAPPER_VOID(aName, aFormals, aActuals) \ |
163 | | void aName aFormals \ |
164 | 0 | { \ |
165 | 0 | AutoSuppressGCAnalysis suppress; \ |
166 | 0 | MOZ_ASSERT(IsRecordingOrReplaying() || IsMiddleman()); \ |
167 | 0 | gPtr ##aName aActuals; \ |
168 | 0 | } Unexecuted instantiation: mozilla::recordreplay::InternalBeginOrderedAtomicAccess() Unexecuted instantiation: mozilla::recordreplay::InternalEndOrderedAtomicAccess() Unexecuted instantiation: mozilla::recordreplay::InternalBeginPassThroughThreadEvents() Unexecuted instantiation: mozilla::recordreplay::InternalEndPassThroughThreadEvents() Unexecuted instantiation: mozilla::recordreplay::InternalBeginDisallowThreadEvents() Unexecuted instantiation: mozilla::recordreplay::InternalEndDisallowThreadEvents() Unexecuted instantiation: mozilla::recordreplay::InternalRecordReplayBytes(void*, unsigned long) Unexecuted instantiation: mozilla::recordreplay::NotifyUnrecordedWait(std::__1::function<void ()> const&) Unexecuted instantiation: mozilla::recordreplay::MaybeWaitForCheckpointSave() Unexecuted instantiation: mozilla::recordreplay::InternalInvalidateRecording(char const*) Unexecuted instantiation: mozilla::recordreplay::InternalDestroyPLDHashTableCallbacks(PLDHashTableOps const*) Unexecuted instantiation: mozilla::recordreplay::InternalMovePLDHashTableContents(PLDHashTableOps const*, PLDHashTableOps const*) Unexecuted instantiation: mozilla::recordreplay::SetWeakPointerJSRoot(void const*, JSObject*) Unexecuted instantiation: mozilla::recordreplay::RegisterTrigger(void*, std::__1::function<void ()> const&) Unexecuted instantiation: mozilla::recordreplay::UnregisterTrigger(void*) Unexecuted instantiation: mozilla::recordreplay::ActivateTrigger(void*) Unexecuted instantiation: mozilla::recordreplay::ExecuteTriggers() Unexecuted instantiation: mozilla::recordreplay::InternalRecordReplayAssert(char const*, __va_list_tag*) Unexecuted instantiation: mozilla::recordreplay::InternalRecordReplayAssertBytes(void const*, unsigned long) Unexecuted instantiation: mozilla::recordreplay::InternalRegisterThing(void*) Unexecuted instantiation: mozilla::recordreplay::InternalUnregisterThing(void*) Unexecuted instantiation: mozilla::recordreplay::InternalRecordReplayDirective(long) Unexecuted instantiation: mozilla::recordreplay::BeginContentParse(void const*, char const*, char const*) Unexecuted instantiation: mozilla::recordreplay::AddContentParseData(void const*, char16_t const*, unsigned long) Unexecuted instantiation: mozilla::recordreplay::EndContentParse(void const*) |
169 | | |
170 | | FOR_EACH_INTERFACE(DEFINE_WRAPPER) |
171 | | FOR_EACH_INTERFACE_VOID(DEFINE_WRAPPER_VOID) |
172 | | |
173 | | #undef DEFINE_WRAPPER |
174 | | #undef DEFINE_WRAPPER_VOID |
175 | | |
176 | | #ifdef ENABLE_RECORD_REPLAY |
177 | | |
178 | | bool gIsRecordingOrReplaying; |
179 | | bool gIsRecording; |
180 | | bool gIsReplaying; |
181 | | bool gIsMiddleman; |
182 | | |
183 | | #endif // ENABLE_RECORD_REPLAY |
184 | | |
185 | | #undef ENABLE_RECORD_REPLAY |
186 | | |
187 | | } // recordreplay |
188 | | } // mozilla |