Driver API
This document describes the API exposed by the Record Replay Driver. The driver is a dynamically linked library which can be loaded by applications which want to record their behavior and upload those recordings for inspection using the Record Replay Protocol.
In order to use the driver, the target application first loads the driver into memory via dlopen
and use dlsym
to look up the various methods described below. The target first calls RecordReplayAttach
, and finishes the recording with RecordReplayFinishRecording
. Between calls to these methods, the other methods below can be used to interact with the driver.
void RecordReplayAttach(const char* dispatch, const char* buildId)
Attach the driver to the current process and begin recording its behavior. dispatch
is the address of the protocol web socket server to upload the recording to (e.g. wss://dispatch.replay.io), and buildId
is a string that uniquely identifies the platform and version of the target that is running. This must be called very early in process startup, before other interaction with the underlying platform. dispatch
may be null, in which case the recording will be saved to disk if RecordReplaySaveRecording
is called.
void RecordReplaySetApiKey(const char* apiKey)
Set the API key to use when creating the recording. apiKey
is the non-null API key that will be sent to the server when uploading the recording and ensuring that you have permission to make recordings. If RecordReplayAttach
is called with a null dispatch
, the recording is not uploaded and this function has no effect.
void RecordReplaySaveRecording(const char* dir)
Save information about the recording to the given directory. A file recordings.log
will be created if needed and lines of JSON events will be appended that include information about the recording's progress. If no dispatch address was supplied when calling RecordReplayAttach
, the recording's contents will be saved to a file in this directory. The directory may be null, in which case the default directory will be used: $RECORD_REPLAY_DIRECTORY
if specified, otherwise $HOME/.replay
void RecordReplayAddMetadata(const char* metadata)
Add a recordings.log
event with the given metadata, which must be null or a valid JSON value.
void RecordReplayProfileExecution(const char* path)
Profile execution in the current process and write it to path
.
void RecordReplayAddProfilerEvent(const char* event, const char* json)
If profiling is active, add an event to the profile with some optional JSON data.
void RecordReplayLabelExecutableCode(const void* aCode, size_t aSize, const char* aKind)
Label a block of executable code with the given kind. Used while profiling.
void RecordReplayFinishRecording()
Finish the recording. After this is called, no further behavior will be recorded and other API methods cannot be used. This will block until the recording has been fully uploaded to the dispatch server specified by RecordReplayAttach
(or an error occurs).
void RecordReplayRecordCommandLineArguments(int* pargc, char*** pargv)
When recording, add the specified command line arguments to the recording. When replaying, the specified arguments will be updated to the arguments that were originally used when recording.
uintptr_t RecordReplayValue(const char* why, uintptr_t value)
Record a scalar value, returning the same value (when recording) or the original recorded value (when replaying). The driver's builtin recording functionality ensures that nearly all values (though not pointers) will be the same when replaying as they were when recording, so this method does not need to be used frequently. It should be used in cases where the driver cannot replay values reliably.
void RecordReplayBytes(const char* why, void* buf, size_t size)
Record a range of memory, overwriting it with the recorded contents when replaying. As for RecordReplayValue
, this should only be used when the driver cannot replay values reliably.
void RecordReplayPrint(const char* format, va_list args)
Print to stderr without interacting with the recording. This is mainly useful when debugging.
void RecordReplayDiagnostic(const char* format, va_list args)
Add a diagnostic message that doesn't interact with the recording and will be printed if the recording process crashes.
void RecordReplayAssert(const char* format, va_list args)
Check that a string is the same when replaying as when recording. This is mainly useful when debugging.
void RecordReplayAssertBytes(const char* why, const void* ptr, size_t nbytes)
Check that a range of memory has the same contents when replaying as when recording. This is mainly useful when debugging.
void RecordReplayRegisterPointer(void* ptr)
Associate a pointer with an identifier that will have the same value when replaying as when recording. The resulting identifier can be used in RecordReplayAssert
calls without having spurious mismatches when replaying due to pointer values differing.
size_t RecordReplayPointerId(void* ptr)
Get the identifier associated with ptr
by a previous call to RegisterPointer
. The identifier will be greater than zero. If there was no such previous call, zero will be returned.
void* RecordReplayIdPointer(size_t id)
Get the pointer associated with a given id
by a previous call to RegisterPointer
.
void RecordReplayUnregisterPointer(void* ptr)
Remove any identifier associated with ptr
by a previous call to RegisterPointer
.
uint64_t* RecordReplayProgressCounter()
Get the process wide progress counter used to measure execution progress within the target. This is incremented by the target at certain points, which must be the same when recording vs. replaying. The progress counter must only be updated on the process main thread.
void RecordReplaySetProgressCallback(void (*)(uint64_t))
Set a callback which the driver can invoke with a progress value. When that progress value is reached, RecordReplayProgressReached
must be called.
void RecordReplayProgressReached()
Called when reaching the progress value supplied via the callback passed to RecordReplaySetProgressCallback
.
void RecordReplayEnableProgressCheckpoints()
Call to automatically create checkpoints every so often as the progress counter advances. This can only be used if a progress callback has been set. If set, the progress callback may be invoked both when recording and replaying; otherwise the callback will only ever be invoked while replaying.
void RecordReplayBeginPassThroughEvents()
Begin a section of code where calls into the system on the current thread will happen without being included in the recording. This can be used reentrantly.
void RecordReplayEndPassThroughEvents()
Finish executing a section of code started by RecordReplayBeginPassThroughEvents
.
bool RecordReplayAreEventsPassedThrough()
Return whether the current thread is inside a section started by RecordReplayBeginPassThroughEvents
.
void RecordReplayBeginDisallowEvents()
Begin a section of code where calls into the system that require recording should not occur at all. This can be used reentrantly.
void RecordReplayEndDisallowEvents()
Finish executing a section of code started by RecordReplayBeginDisallowEvents
.
bool RecordReplayAreEventsDisallowed()
Return whether the current thread is inside a section started by RecordReplayBeginDisallowEvents
.
bool RecordReplayBeginCallbackRegion()
Begin a section of code where callbacks into the application from other libraries might occur.
bool RecordReplayEndCallbackRegion()
Finish executing a section of code started by RecordReplayBeginCallbackRegion
.
bool RecordReplayHasDivergedFromRecording()
Returns whether the current process is replaying and the application's behavior might not match up with what occurred while recording (for example, while an expression string is being evaluated).
bool RecordReplayIsUnhandledDivergenceAllowed()
Return whether execution is allowed to interact with the system in a way that could trigger an unhandled divergence. This returns true except during certain operations while diverged from the recording.
void RecordReplayNewCheckpoint()
Notify the driver that this might be a good place to create a checkpoint. This should happen regularly, ideally several times per second.
bool RecordReplayIsReplaying()
Returns whether the current process is replaying instead of recording.
size_t RecordReplayCreateOrderedLock(const char* name)
Returns a non-zero identifier for an ordered lock, which will be acquired and released in the same order between different threads when replaying as they did when recording.
void RecordReplayOrderedLock(size_t lock)
Acquire an ordered lock. This is not reentrant: a thread should not acquire an ordered lock multiple times at once.
void RecordReplayOrderedUnlock(size_t lock)
Release an ordered lock.
void RecordReplayAddOrderedPthreadMutex(const char* name, pthread_mutex_t* mutex)
Mark a native pthreads mutex as ordered. Locks and unlocks of this mutex will happen in the same order when replaying as they did when recording.
size_t RecordReplayPaintStart()
Notify the driver that the application's current graphics are going to be painted. Must be called on the main thread. Returns an ID which should be passed to RecordReplayPaintFinished
when the paint finishes.
void RecordReplayPaintFinished(size_t id)
Notify the driver that a paint which started earlier has finished generating the new graphics. May be called on any thread.
void RecordReplayOnPaint()
Notify the driver that the application's current graphics might have changed due to painting. Must be called on the main thread. This is equivalent to calling RecordReplayPaintStart
and RecordReplayPaintFinished
together.
void RecordReplaySetPaintCallback(char* (*callback)(const char* mimeType, int jpegQuality))
Set a callback which the driver can use to fetch the application's current graphics. The callback will be passed the desired Graphics.MimeType
MIME type of the graphics data, and a quality value from 1-100 for JPEG encodings. The returned value is the base64 encoded graphics data, which the driver will release using the callback specified in SetFreeCallback
.
void RecordReplayOnNewSource(const char* id, const char* kind, const char* url)
Notify the driver that a source which might execute has been created. id
is the Debugger.SourceId
for the source, and kind
is the Debugger.SourceKind
for the source. url
is any URL associated with the source, and may be null.
void RecordReplayOnNetworkRequest(const char* id, const char* kind, size_t bookmark)
Notify the driver that a request has been created.
id
is the unique identifier for the request.
kind
is a descriptor of the type of request being made. For now is expected to be "http" but may be TCP or others in the future.
bookmark
is the bookmark to use for jumping to the location that triggered the request.
void RecordReplayOnNetworkRequestEvent(const char* id)
Notify the driver that there is information about a request pending available via Target.getCurrentNetworkRequestEvent()
.
void RecordReplayOnNetworkStreamStart(const char* id, const char* kind, const char* parentId)
Notify the driver that a network stream has been created.
id
is the unique identifier for the stream.
kind
is one of "request-data" or "response-data" to indicate what type of stream this is.
parentId
is the ID of the request that the stream is associated with.
void RecordReplayOnNetworkStreamData(const char* id, size_t offset, size_t length, size_t bookmark)
Notify the driver that network stream data is available. While this method is being called, the driver may send a
Target.getCurrentNetworkStreamData
command to get data from the stream.
id
is the unique identifier for the stream.
offset
is the offset of the data within the stream.
length
is the length of this piece of data.
bookmark
is the ID of a bookmark associated with this data appearing, or 0 if there is none.
void RecordReplayOnNetworkStreamEnd(const char* id, size_t length)
Notify the driver that a network stream data is done.
id
is the unique identifier for the stream.
length
is the length of this piece of data.
const char* RecordReplayGetRecordingId()
Get the ID for the recording being created.
char* RecordReplayGetUnusableRecordingReason()
If the recording is unusable and new data cannot be added to the remote side, this returns a string describing why. The recording can become unusable due to a call to RecordReplayInvalidateRecording
or due to an internal error such as a connection problem. The returned string may be freed using RecordReplayFree
.
void RecordReplayInvalidateRecording(const char* format, ...)
Tells the driver that some event has occurred that cannot be replayed consistently (such as a stack overflow exception being thrown), and more data should not be added to the recording.
void RecordReplayProcessRecording()
Begin processing the recording in the backend as soon as it is created, per Recording.processRecording
.
void RecordReplaySetCommandCallback(const char* command, char* (*callback)(const char* params))
Set a callback for a protocol command which the driver can call to get information about the target's state. The callback will be invoked with the parameters for the command, encoded as a JSON string, and must return the command's result, also encoded as a JSON string, or null on an error. The returned string will be released using the callback specified in SetFreeCallback
.
void RecordReplaySetDefaultCommandCallback(char* (*callback)(const char* command, const char* params))
Set a callback which will be invoked for any command which RecordReplaySetCommandCallback
did not define a specific callback for. The callback is the same as for RecordReplaySetCommandCallback
, except that a parameter with the command name will also be passed to the callback.
void RecordReplaySetClearPauseDataCallback(void (*callback)());
Set a callback which should clear out the identifiers associated with objects, frames, etc. in Pause
commands.
void RecordReplaySetChangeInstrumentCallback(void (*callback)(bool wantInstrumentation))
Set a callback which will be invoked when the driver changes whether it is interested in RecordReplayOnInstrument
calls. If the flag passed to the callback is false, these calls do not need to happen. The initial value of the wantInstrumentation
flag in the process is false.
void RecordReplayOnInstrument(const char* kind, const char* functionId, size_t offset)
Notify the driver that some activity in one of the target's sources has occurred, such as pushing/popping a stack frame or reaching a breakpoint site. The instrumentation format, including the parameters to this function, are not currently specified. These notifications must occur at the same times when replaying as when recording, but they do not need to happen at all if the driver does not want instrumentation, see RecordReplaySetChangeInstrumentCallback
.
void RecordReplayOnExceptionUnwind()
Notify the driver that an exception is being unwound which could pop any number of stack frames without RecordReplayOnInstrument
notifications. While this method is being called, the driver may send a Pause.getExceptionValue
command to get the exception value being thrown.
void RecordReplayOnDebuggerStatement()
Notify the driver that a JavaScript debugger
statement has just executed.
void RecordReplayOnEvent(const char* event, bool before)
Notify the driver that an event that might have an associated handler is executing. event
is the kind of event, and before
indicates whether handlers are about to execute vs. have just executed. This must be called both before and after the handlers for the event have run.
void RecordReplayOnMouseEvent(const char* kind, size_t clientX, size_t clientY)
Notify the driver that a mouse event has occurred that clients might want to know about. Parameters are the same as for the Recording.MouseEvent
type.
void RecordReplayOnKeyEvent(const char* kind, const char* key)
Notify the driver that a keyboard event has occurred that clients might want to know about. Parameters are the same as for the Recording.KeyboardEvent
type.
void RecordReplayOnNavigationEvent(const char* kind, const char* url)
Notify the driver that the top-level window has navigated, which clients might want to know about. Callers may pass "nullptr" if no specific navigation kind is known.
void RecordReplayOnAnnotation(const char* kind, const char* contents)
Annotate the current point of execution with some data that can be sent to clients inspecting the recording. kind
describes the kind of data being annotated, and contents
encodes the annotated data.
size_t RecordReplayNewBookmark()
Get a non-zero identifier for the current execution point in the recording.
void RecordReplayOnConsoleMessage(size_t bookmark)
Notify the driver that a console message was just created by the target. bookmark
is an identifier created by RecordReplayNewBookmark
which indicates which point to associate the console message with, or zero to associate the console message with the current point. While this method is being called, the driver may send a Target.getCurrentMessageContents
command to get additional data about the message.
size_t RecordReplayElapsedTimeMs()
Returns the elapsed time since recording started, in milliseconds.
void RecordReplaySetCrashReasonCallback(const char* (*callback)())
Set a callback which will be invoked after the process crashes, and returns any reason why the target application itself has crashed.
void RecordReplaySetCrashNote(const char* note)
Set a message to print if the process crashes.
void RecordReplaySetCrashLogFile(const char* file)
Set a file where information about crashes will be written.
void RecordReplaySetFreeCallback(void (*callback)(void*))
Specify a callback for use when freeing pointers returned by various APIs. If not specified, free
will be used to free pointers.
void* RecordReplayJSONCreateNumber(double d)
Create a handle to a JSON number.
void* RecordReplayJSONCreateString(const char* str)
Create a handle to a JSON string.
void* RecordReplayJSONCreateObject(size_t numProperties, const char** properties, void** values)
Create a handle to a JSON object with the given property names and JSON handle values.
void* RecordReplayJSONCreateArray(size_t numElements, void** elements)
Create a handle to a JSON array with the given JSON handle elements.
char* RecordReplayJSONToString(void* value)
Convert a JSON handle to a UTF-8 string, which must be deallocated with RecordReplayFree
.
void RecordReplayJSONFree(void* value)
Release a handle to a JSON value created by an earlier call.
void RecordReplayFree(void* value)
Free a pointer returned by a previous call to the driver.
void RecordReplayNotifyActivity()
Notify the driver that the current thread is doing something. This should be called regularly during long running computations on non-main threads, and allows the driver to suspend the thread's execution if needed.
void NewStableHashTable(const void* aTable, bool (*aKeyEqualsEntry)(const void* aKey, const void* aEntry, void* aPrivate), void* aPrivate);
Notify the driver about a new hash table which can be used in other StableHashTable APIs to generate consistent hash codes when recording vs. replaying.
void MoveStableHashTable(const void* aTableSrc, const void* aTableDst)
Change the pointer used for an existing stable hash table.
void DeleteStableHashTable(const void* aTable)
Delete all information about a stable hash table.
uint32_t LookupStableHashCode(const void* aTable, const void* aKey, uint32_t aUnstableHashCode, bool* aFoundMatch)
Given a key being looked up and its unstable hash code, compute the stable hash code to use for the lookup. If specified, aFoundMatch is set to indicate whether a match with an existing table entry was found.
void StableHashTableAddEntryForLastLookup(const void* aTable, const void* aEntry)
Adds an entry to the table for the key used in the table's last LookupStableHashCode call. The last lookup must not have found a match.
void StableHashTableMoveEntry(const void* aTable, const void* aEntrySrc, const void* aEntryDst)
Changes the pointer used for an existing entry in a stable hash table.
void StableHashTableDeleteEntry(const void* aTable, const void* aEntry)
Remove an entry from a stable hash table.