1SurfaceReplayer Documentation 2=================== 3 4[go/SurfaceReplayer](go/SurfaceReplayer) 5 6SurfaceReplayer is a playback mechanism that allows the replaying of traces recorded by 7[SurfaceInterceptor](go/SurfaceInterceptor) from SurfaceFlinger. It specifically replays 8 9* Creation and deletion of surfaces/displays 10* Alterations to the surfaces/displays called Transactions 11* Buffer Updates to surfaces 12* VSync events 13 14At their specified times to be as close to the original trace. 15 16Usage 17-------- 18 19###Creating a trace 20 21SurfaceInterceptor is the mechanism used to create traces. The device needs to be rooted in order to 22utilize it. To allow it to write to the device, run 23 24`setenforce 0` 25 26To start recording a trace, run 27 28`service call SurfaceFlinger 1020 i32 1` 29 30To stop recording, run 31 32`service call SurfaceFlinger 1020 i32 0` 33 34The default location for the trace is `/data/SurfaceTrace.dat` 35 36###Executable 37 38To replay a specific trace, execute 39 40`/data/local/tmp/surfacereplayer /absolute/path/to/trace` 41 42inside the android shell. This will replay the full trace and then exit. Running this command 43outside of the shell by prepending `adb shell` will not allow for manual control and will not turn 44off VSync injections if it interrupted in any way other than fully replaying the trace 45 46The replay will not fill surfaces with their contents during the capture. Rather they are given a 47random color which will be the same every time the trace is replayed. Surfaces modulate their color 48at buffer updates. 49 50**Options:** 51 52- -m pause the replayer at the start of the trace for manual replay 53- -t [Number of Threads] uses specified number of threads to queue up actions (default is 3) 54- -s [Timestamp] switches to manual replay at specified timestamp 55- -n Ignore timestamps and run through trace as fast as possible 56- -l Indefinitely loop the replayer 57- -h displays help menu 58 59**Manual Replay:** 60When replaying, if the user presses CTRL-C, the replay will stop and can be manually controlled 61by the user. Pressing CTRL-C again will exit the replayer. 62 63Manual replaying is similar to debugging in gdb. A prompt is presented and the user is able to 64input commands to choose how to proceed by hitting enter after inputting a command. Pressing enter 65without inputting a command repeats the previous command. 66 67- n - steps the replayer to the next VSync event 68- ni - steps the replayer to the next increment 69- c - continues normal replaying 70- c [milliseconds] - continue until specified number of milliseconds have passed 71- s [timestamp] - continue and stop at specified timestamp 72- l - list out timestamp of current increment 73- h - displays help menu 74 75###Shared Library 76 77To use the shared library include these shared libraries 78 79`libsurfacereplayer` 80`libprotobuf-cpp-full` 81`libutils` 82 83And the static library 84 85`libtrace_proto` 86 87Include the replayer header at the top of your file 88 89`#include <replayer/Replayer.h>` 90 91There are two constructors for the replayer 92 93`Replayer(std::string& filename, bool replayManually, int numThreads, bool wait, nsecs_t stopHere)` 94`Replayer(Trace& trace, ... ditto ...)` 95 96The first constructor takes in the filepath where the trace is located and loads in the trace 97object internally. 98- replayManually - **True**: if the replayer will immediately switch to manual replay at the start 99- numThreads - Number of worker threads the replayer will use. 100- wait - **False**: Replayer ignores waits in between increments 101- stopHere - Time stamp of where the replayer should run to then switch to manual replay 102 103The second constructor includes all of the same parameters but takes in a preloaded trace object. 104To use add 105 106`#include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h>` 107 108To your file 109 110After initializing the Replayer call 111 112 replayer.replay(); 113 114And the trace will start replaying. Once the trace is finished replaying, the function will return. 115The layers that are visible at the end of the trace will remain on screen until the program 116terminates. 117 118 119**If VSyncs are broken after running the replayer** that means `enableVSyncInjections(false)` was 120never executed. This can be fixed by executing 121 122`service call SurfaceFlinger 23 i32 0` 123 124in the android shell 125 126Code Breakdown 127------------- 128 129The Replayer is composed of 5 components. 130 131- The data format of the trace (Trace.proto) 132- The Replayer object (Replayer.cpp) 133- The synchronization mechanism to signal threads within the Replayer (Event.cpp) 134- The scheduler for buffer updates per surface (BufferQueueScheduler.cpp) 135- The Main executable (Main.cpp) 136 137### Traces 138 139Traces are represented as a protobuf message located in surfacereplayer/proto/src. 140 141**Traces** contain *repeated* **Increments** (events that have occurred in SurfaceFlinger). 142**Increments** contain the time stamp of when it occurred and a *oneof* which can be a 143 144 - Transaction 145 - SurfaceCreation 146 - SurfaceDeletion 147 - DisplayCreation 148 - DisplayDeleteion 149 - BufferUpdate 150 - VSyncEvent 151 - PowerModeUpdate 152 153**Transactions** contain whether the transaction was synchronous or animated and *repeated* 154**SurfaceChanges** and **DisplayChanges** 155 156- **SurfaceChanges** contain an id of the surface being manipulated and can be changes such as 157position, alpha, hidden, size, etc. 158- **DisplayChanges** contain the id of the display being manipulated and can be changes such as 159size, layer stack, projection, etc. 160 161**Surface/Display Creation** contain the id of the surface/display and the name of the 162surface/display 163 164**Surface/Display Deletion** contain the id of the surface/display to be deleted 165 166**Buffer Updates** contain the id of the surface who's buffer is being updated, the size of the 167buffer, and the frame number. 168 169**VSyncEvents** contain when the VSync event has occurred. 170 171**PowerModeUpdates** contain the id of the display being updated and what mode it is being 172changed to. 173 174To output the contents of a trace in a readable format, execute 175 176`**aprotoc** --decode=Trace \ 177-I=$ANDROID_BUILD_TOP/frameworks/native/cmds/surfacereplayer/proto/src \ 178$ANDROID_BUILD_TOP/frameworks/native/cmds/surfacereplayer/proto/src/trace.proto \ 179 < **YourTraceFile.dat** > **YourOutputName.txt**` 180 181 182###Replayer 183 184Fundamentally the replayer loads a trace and iterates through each increment, waiting the required 185amount of time until the increment should be executed, then executing the increment. The first 186increment in a trace does not start at 0, rather the replayer treats its time stamp as time 0 and 187goes from there. 188 189Increments from the trace are played asynchronously rather than one by one, being dispatched by 190the main thread, queued up in a thread pool and completed when the main thread deems they are 191ready to finish execution. 192 193When an increment is dispatched, it completes as much work as it can before it has to be 194synchronized (e.g. prebaking a buffer for a BufferUpdate). When it gets to a critical action 195(e.g. locking and pushing a buffer), it waits for the main thread to complete it using an Event 196object. The main thread holds a queue of these Event objects and completes the 197corresponding Event base on its time stamp. After completing an increment, the main thread will 198dispatch another increment and continue. 199 200The main thread's execution flow is outlined below 201 202 initReplay() //queue up the initial increments 203 while(!pendingIncrements.empty()) { //while increments remaining 204 event = pendingIncrement.pop(); 205 wait(event.time_stamp(); //waitUntil it is time to complete this increment 206 207 event.complete() //signal to let event finish 208 if(increments remaing()) { 209 dispatchEvent() //queue up another increment 210 } 211 } 212 213A worker thread's flow looks like so 214 215 //dispatched! 216 Execute non-time sensitive work here 217 ... 218 event.readyToExecute() //time sensitive point...waiting for Main Thread 219 ... 220 Finish execution 221 222 223### Event 224 225An Event is a simple synchronization mechanism used to facilitate communication between the main 226and worker threads. Every time an increment is dispatched, an Event object is also created. 227 228An Event can be in 4 different states: 229 230- **SettingUp** - The worker is in the process of completing all non-time sensitive work 231- **Waiting** - The worker is waiting on the main thread to signal it. 232- **Signaled** - The worker has just been signaled by the main thread 233- **Running** - The worker is running again and finishing the rest of its work. 234 235When the main thread wants to finish the execution of a worker, the worker can either still be 236**SettingUp**, in which the main thread will wait, or the worker will be **Waiting**, in which the 237main thread will **Signal** it to complete. The worker thread changes itself to the **Running** 238state once **Signaled**. This last step exists in order to communicate back to the main thread that 239the worker thread has actually started completing its execution, rather than being preempted right 240after signalling. Once this happens, the main thread schedules the next worker. This makes sure 241there is a constant amount of workers running at one time. 242 243This activity is encapsulated in the `readyToExecute()` and `complete()` functions called by the 244worker and main thread respectively. 245 246### BufferQueueScheduler 247 248During a **BuferUpdate**, the worker thread will wait until **Signaled** to unlock and post a 249buffer that has been prefilled during the **SettingUp** phase. However if there are two sequential 250**BufferUpdates** that act on the same surface, both threads will try to lock a buffer and fill it, 251which isn't possible and will cause a deadlock. The BufferQueueScheduler solves this problem by 252handling when **BufferUpdates** should be scheduled, making sure that they don't overlap. 253 254When a surface is created, a BufferQueueScheduler is also created along side it. Whenever a 255**BufferUpdate** is read, it schedules the event onto its own internal queue and then schedules one 256every time an Event is completed. 257 258### Main 259 260The main exectuable reads in the command line arguments. Creates the Replayer using those 261arguments. Executes `replay()` on the Replayer. If there are no errors while replaying it will exit 262gracefully, if there are then it will report the error and then exit. 263