1*6dbdd20aSAndroid Build Coastguard Worker# Batch Trace Processor 2*6dbdd20aSAndroid Build Coastguard WorkerThis document describes the overall design of Batch Trace Processor and 3*6dbdd20aSAndroid Build Coastguard Workeraids in integrating it into other systems. 4*6dbdd20aSAndroid Build Coastguard Worker 5*6dbdd20aSAndroid Build Coastguard Worker 6*6dbdd20aSAndroid Build Coastguard Worker 7*6dbdd20aSAndroid Build Coastguard Worker## Motivation 8*6dbdd20aSAndroid Build Coastguard WorkerThe Perfetto trace processor is the de-facto way to perform analysis on a 9*6dbdd20aSAndroid Build Coastguard Workersingle trace. Using the 10*6dbdd20aSAndroid Build Coastguard Worker[trace processor Python API](/docs/analysis/trace-processor#python-api), 11*6dbdd20aSAndroid Build Coastguard Workertraces can be queried interactively, plots made from those results etc. 12*6dbdd20aSAndroid Build Coastguard Worker 13*6dbdd20aSAndroid Build Coastguard WorkerWhile queries on a single trace are useful when debugging a specific problem 14*6dbdd20aSAndroid Build Coastguard Workerin that trace or in the very early stages of understanding a domain, it soon 15*6dbdd20aSAndroid Build Coastguard Workerbecomes limiting. One trace is unlikely to be representative 16*6dbdd20aSAndroid Build Coastguard Workerof the entire population and it's easy to overfit queries i.e. spend a 17*6dbdd20aSAndroid Build Coastguard Workerlot of effort on breaking down a problem in that trace while neglecting 18*6dbdd20aSAndroid Build Coastguard Workerother, more common issues in the population. 19*6dbdd20aSAndroid Build Coastguard Worker 20*6dbdd20aSAndroid Build Coastguard WorkerBecause of this, what we actually want is to be able to query many traces 21*6dbdd20aSAndroid Build Coastguard Worker(usually on the order of 250-10000+) and identify the patterns which show 22*6dbdd20aSAndroid Build Coastguard Workerup in a significant fraction of them. This ensures that time is being spent 23*6dbdd20aSAndroid Build Coastguard Workeron issues which are affecting user experience instead of just a random 24*6dbdd20aSAndroid Build Coastguard Workerproblem which happened to show up in the trace. 25*6dbdd20aSAndroid Build Coastguard Worker 26*6dbdd20aSAndroid Build Coastguard WorkerOne low-effort option for solving this problem is simply to ask people to use 27*6dbdd20aSAndroid Build Coastguard Workerutilities like [Executors](https://docs.python.org/3/library/concurrent.futures.html#executor-objects) 28*6dbdd20aSAndroid Build Coastguard Workerwith the Python API to load multiple traces and query them in parallel. 29*6dbdd20aSAndroid Build Coastguard WorkerUnfortunately, there are several downsides to this approach: 30*6dbdd20aSAndroid Build Coastguard Worker* Every user has to reinvent the wheel every time they want to query multiple 31*6dbdd20aSAndroid Build Coastguard Worker traces. Over time, there would likely be a proliferation of slightly modified 32*6dbdd20aSAndroid Build Coastguard Worker code which is copied from each place. 33*6dbdd20aSAndroid Build Coastguard Worker* While the basics of parallelising queries on multiple traces on a single 34*6dbdd20aSAndroid Build Coastguard Worker machine is straightforward, one day, we may want to shard trace processing 35*6dbdd20aSAndroid Build Coastguard Worker across multiple machines. Once this happens, the complexity of the code would 36*6dbdd20aSAndroid Build Coastguard Worker rise significantly to the point where a central implementation becomes a 37*6dbdd20aSAndroid Build Coastguard Worker necessity. Because of this, it's better to have the API first before engineers 38*6dbdd20aSAndroid Build Coastguard Worker start building their own custom solutions. 39*6dbdd20aSAndroid Build Coastguard Worker* A big aim for the Perfetto team these days is to make trace analysis more 40*6dbdd20aSAndroid Build Coastguard Worker accessible to reduce the number of places where we need to be in the loop. 41*6dbdd20aSAndroid Build Coastguard Worker Having a well supported API for an important usecase like bulk trace analysis 42*6dbdd20aSAndroid Build Coastguard Worker directly helps with this. 43*6dbdd20aSAndroid Build Coastguard Worker 44*6dbdd20aSAndroid Build Coastguard WorkerWhile we've discussed querying traces so far, the experience for loading traces 45*6dbdd20aSAndroid Build Coastguard Workerfrom different traces should be just as good. This has historically been a big 46*6dbdd20aSAndroid Build Coastguard Workerreason why the Python API has not gained as much adoption as we would have 47*6dbdd20aSAndroid Build Coastguard Workerliked. 48*6dbdd20aSAndroid Build Coastguard Worker 49*6dbdd20aSAndroid Build Coastguard WorkerEspecially internally in Google, we should not be relying on engineers 50*6dbdd20aSAndroid Build Coastguard Workerknowing where traces live on the network filesystem and the directory layout. 51*6dbdd20aSAndroid Build Coastguard WorkerInstead, they should be able to simply be able to specify the data source (i.e. 52*6dbdd20aSAndroid Build Coastguard Workerlab, testing population) and some parameters (e.g. build id, date, kernel 53*6dbdd20aSAndroid Build Coastguard Workerversion) that traces should match should match and traces meeting these criteria 54*6dbdd20aSAndroid Build Coastguard Workershould found and loaded. 55*6dbdd20aSAndroid Build Coastguard Worker 56*6dbdd20aSAndroid Build Coastguard WorkerPutting all this together, we want to build a library which can: 57*6dbdd20aSAndroid Build Coastguard Worker* Interactively query ~1000+ traces in O(s) (for simple queries) 58*6dbdd20aSAndroid Build Coastguard Worker* Expose full SQL expressiveness from trace processor 59*6dbdd20aSAndroid Build Coastguard Worker* Load traces from many sources with minimal ceremony. This should include 60*6dbdd20aSAndroid Build Coastguard Worker Google-internal sources: e.g. lab runs and internal testing populations 61*6dbdd20aSAndroid Build Coastguard Worker* Integrate with data analysis libraries for easy charting and visulazation 62*6dbdd20aSAndroid Build Coastguard Worker 63*6dbdd20aSAndroid Build Coastguard Worker## Design Highlights 64*6dbdd20aSAndroid Build Coastguard WorkerIn this section, we briefly discuss some of the most impactful design decisions 65*6dbdd20aSAndroid Build Coastguard Workertaken when building batch trace processor and the reasons behind them. 66*6dbdd20aSAndroid Build Coastguard Worker 67*6dbdd20aSAndroid Build Coastguard Worker### Language 68*6dbdd20aSAndroid Build Coastguard WorkerThe choice of langugage is pretty straightforward. Python is already the go-to 69*6dbdd20aSAndroid Build Coastguard Workerlangugage for data analysis in a wide variety of domains and our problem 70*6dbdd20aSAndroid Build Coastguard Workeris not unique enough to warrant making a different decision. Moreover, another 71*6dbdd20aSAndroid Build Coastguard Workerpoint in favour is the existence of the Python API for trace processor. This 72*6dbdd20aSAndroid Build Coastguard Workerfurther eases the implementation as we do not have to start from scratch. 73*6dbdd20aSAndroid Build Coastguard Worker 74*6dbdd20aSAndroid Build Coastguard WorkerThe main downside of choosing Python is performance but given that that all 75*6dbdd20aSAndroid Build Coastguard Workerthe data crunching happens in C++ inside TP, this is not a big factor. 76*6dbdd20aSAndroid Build Coastguard Worker 77*6dbdd20aSAndroid Build Coastguard Worker### Trace URIs and Resolvers 78*6dbdd20aSAndroid Build Coastguard Worker[Trace URIs](/docs/analysis/batch-trace-processor#trace-uris) 79*6dbdd20aSAndroid Build Coastguard Workerare an elegant solution to the problem of loading traces from a diverse range 80*6dbdd20aSAndroid Build Coastguard Workerof public and internal sources. As with web URIs, the idea with trace URIs is 81*6dbdd20aSAndroid Build Coastguard Workerto describe both the protocol (i.e. the source) from which traces should be 82*6dbdd20aSAndroid Build Coastguard Workerfetched and the arguments (i.e. query parameters) which the traces should match. 83*6dbdd20aSAndroid Build Coastguard Worker 84*6dbdd20aSAndroid Build Coastguard WorkerBatch trace processor should integrate tightly with trace URIs and their 85*6dbdd20aSAndroid Build Coastguard Workerresolvers. Users should be able to pass either just the URI (whcih is really 86*6dbdd20aSAndroid Build Coastguard Workerjust a string for maximum flexibility) or a resolver object which can yield a 87*6dbdd20aSAndroid Build Coastguard Workerlist of trace file paths. 88*6dbdd20aSAndroid Build Coastguard Worker 89*6dbdd20aSAndroid Build Coastguard WorkerTo handle URI strings, there should be some mecahinsm of "registering" resolvers 90*6dbdd20aSAndroid Build Coastguard Workerto make them eligible to resolve a certain "protocol". By default, we should 91*6dbdd20aSAndroid Build Coastguard Workerprovide a resolver to handle filesystem. We should ensure that the resolver 92*6dbdd20aSAndroid Build Coastguard Workerdesign is such that resolvers can be closed soruce while the rest of batch trace 93*6dbdd20aSAndroid Build Coastguard Workerprocessor is open. 94*6dbdd20aSAndroid Build Coastguard Worker 95*6dbdd20aSAndroid Build Coastguard WorkerAlong with the job of yielding a list of traces, resolvers should also be 96*6dbdd20aSAndroid Build Coastguard Workerresponsible for creating metadata for each trace these are different pieces 97*6dbdd20aSAndroid Build Coastguard Workerof information about the trace that the user might be interested in e.g. OS 98*6dbdd20aSAndroid Build Coastguard Workerversion, device name, collected date etc. The metadata can then be used when 99*6dbdd20aSAndroid Build Coastguard Worker"flattening" results across many traces as discussed below. 100*6dbdd20aSAndroid Build Coastguard Worker 101*6dbdd20aSAndroid Build Coastguard Worker### Persisting loaded traces 102*6dbdd20aSAndroid Build Coastguard WorkerOptimizing the loading of traces is critical for the O(s) query performance 103*6dbdd20aSAndroid Build Coastguard Workerwe want out of batch trace processor. Traces are often accessed 104*6dbdd20aSAndroid Build Coastguard Workerover the network meaning fetching their contents has a high latency. 105*6dbdd20aSAndroid Build Coastguard WorkerTraces also take at least a few seconds to parse, eating up the budget for 106*6dbdd20aSAndroid Build Coastguard WorkerO(s) before even getting the running time of queries. 107*6dbdd20aSAndroid Build Coastguard Worker 108*6dbdd20aSAndroid Build Coastguard WorkerTo address this issue, we take the decision to keep all traces fully loaded in 109*6dbdd20aSAndroid Build Coastguard Workermemory in trace processor instances. That way, instead of loading them on every 110*6dbdd20aSAndroid Build Coastguard Workerquery/set of queries, we can issue queries directly. 111*6dbdd20aSAndroid Build Coastguard Worker 112*6dbdd20aSAndroid Build Coastguard WorkerFor the moment, we restrict the loading and querying of traces to a 113*6dbdd20aSAndroid Build Coastguard Workersingle machine. While querying n traces is "embarassngly parallel" and shards 114*6dbdd20aSAndroid Build Coastguard Workerperfectly across multiple machines, introducing distributed systems to any 115*6dbdd20aSAndroid Build Coastguard Workersolution simply makes everything more complicated. The move to multiple 116*6dbdd20aSAndroid Build Coastguard Workermachines is explored further in the "Future plans" section. 117*6dbdd20aSAndroid Build Coastguard Worker 118*6dbdd20aSAndroid Build Coastguard Worker### Flattening query results 119*6dbdd20aSAndroid Build Coastguard WorkerThe naive way to return the result of querying n traces is a list 120*6dbdd20aSAndroid Build Coastguard Workerof n elements, with each element being result for a single trace. However, 121*6dbdd20aSAndroid Build Coastguard Workerafter performing several case-study performance investigations using BTP, it 122*6dbdd20aSAndroid Build Coastguard Workerbecame obvious that this obvious answer was not the most convienent for the end 123*6dbdd20aSAndroid Build Coastguard Workeruser. 124*6dbdd20aSAndroid Build Coastguard Worker 125*6dbdd20aSAndroid Build Coastguard WorkerInstead, a pattern which proved very useful was to "flatten" the results into 126*6dbdd20aSAndroid Build Coastguard Workera single table, containing the results from all the traces. However, 127*6dbdd20aSAndroid Build Coastguard Workersimply flattening causes us to lose the information about which trace a row 128*6dbdd20aSAndroid Build Coastguard Workeroriginated from. We can deal with this by allowing resolvers to silently add 129*6dbdd20aSAndroid Build Coastguard Workercolumns with the metadata for each trace. 130*6dbdd20aSAndroid Build Coastguard Worker 131*6dbdd20aSAndroid Build Coastguard Worker 132*6dbdd20aSAndroid Build Coastguard WorkerSo suppose we query three traces with: 133*6dbdd20aSAndroid Build Coastguard Worker 134*6dbdd20aSAndroid Build Coastguard Worker```SELECT ts, dur FROM slice``` 135*6dbdd20aSAndroid Build Coastguard Worker 136*6dbdd20aSAndroid Build Coastguard WorkerThen in the flattening operation might do something like this behind the scenes: 137*6dbdd20aSAndroid Build Coastguard Worker 138*6dbdd20aSAndroid Build Coastguard Worker 139*6dbdd20aSAndroid Build Coastguard Worker 140*6dbdd20aSAndroid Build Coastguard Worker## Integration points 141*6dbdd20aSAndroid Build Coastguard WorkerBatch trace processor needs to be both open source yet allow deep integration 142*6dbdd20aSAndroid Build Coastguard Workerwith Google internal tooling. Because of this, there are various integration 143*6dbdd20aSAndroid Build Coastguard Workerpoints built design to allow closed compoentns to be slotted in place of the 144*6dbdd20aSAndroid Build Coastguard Workerdefault, open source ones. 145*6dbdd20aSAndroid Build Coastguard Worker 146*6dbdd20aSAndroid Build Coastguard WorkerThe first point is the formalization of the idea "platform" code. Even since the 147*6dbdd20aSAndroid Build Coastguard Workerbegining of the Python API, there was always a need for code internally to be 148*6dbdd20aSAndroid Build Coastguard Workerrun slightly different to open source code. For example, Google internal Python 149*6dbdd20aSAndroid Build Coastguard Workerdistrubution does not use Pip, instead packaging dependencies into a single 150*6dbdd20aSAndroid Build Coastguard Workerbinary. The notion of a "platform" loosely existed to abstract this sort of 151*6dbdd20aSAndroid Build Coastguard Workerdifferences but this was very ad-hoc. As part of batch trace processor 152*6dbdd20aSAndroid Build Coastguard Workerimplementation, this has been retroactively formalized. 153*6dbdd20aSAndroid Build Coastguard Worker 154*6dbdd20aSAndroid Build Coastguard WorkerResolvers are another big point of pluggability. By allowing registration of 155*6dbdd20aSAndroid Build Coastguard Workera "protocol" for each internal trace source (e.g. lab, testing population), we 156*6dbdd20aSAndroid Build Coastguard Workerallow for trace loading to be neatly abstracted. 157*6dbdd20aSAndroid Build Coastguard Worker 158*6dbdd20aSAndroid Build Coastguard WorkerFinally, for batch trace processor specifically, we abstract the creation of 159*6dbdd20aSAndroid Build Coastguard Workerthread pools for loading traces and running queries. The parallelism and memory 160*6dbdd20aSAndroid Build Coastguard Workeravailable to programs internally is often does not 1:1 correspond with the 161*6dbdd20aSAndroid Build Coastguard Workeravailable CPUs/memory on the system: internal APIs need to be accessed to find 162*6dbdd20aSAndroid Build Coastguard Workerout this information. 163*6dbdd20aSAndroid Build Coastguard Worker 164*6dbdd20aSAndroid Build Coastguard Worker## Future plans 165*6dbdd20aSAndroid Build Coastguard WorkerOne common problem when running batch trace processor is that we are 166*6dbdd20aSAndroid Build Coastguard Workerconstrained by a single machine and so can only load O(1000) traces. 167*6dbdd20aSAndroid Build Coastguard WorkerFor rare problems, there might only be a handful of traces matching a given 168*6dbdd20aSAndroid Build Coastguard Workerpattern even in such a large sample. 169*6dbdd20aSAndroid Build Coastguard Worker 170*6dbdd20aSAndroid Build Coastguard WorkerA way around this would be to build a "no trace limit" mode. The idea here 171*6dbdd20aSAndroid Build Coastguard Workeris that you would develop queries like usual with batch trace processor 172*6dbdd20aSAndroid Build Coastguard Workeroperating on a O(1000) traces with O(s) performance. Once the queries are 173*6dbdd20aSAndroid Build Coastguard Workerrelatively finalized, we could then "switch" the mode of batch trace processor 174*6dbdd20aSAndroid Build Coastguard Workerto opeate closer to a "MapReduce" style pipeline which operates over O(10000)+ 175*6dbdd20aSAndroid Build Coastguard Workertraces loading O(n cpus) traces at any one time. 176*6dbdd20aSAndroid Build Coastguard Worker 177*6dbdd20aSAndroid Build Coastguard WorkerThis allows us to retain both the quick iteration speed while developing queries 178*6dbdd20aSAndroid Build Coastguard Workerwhile also allowing for large scale analysis without needing to move code 179*6dbdd20aSAndroid Build Coastguard Workerto a pipeline model. However, this approach does not really resolve the root 180*6dbdd20aSAndroid Build Coastguard Workercause of the problem which is that we are restricted to a single machine. 181*6dbdd20aSAndroid Build Coastguard Worker 182*6dbdd20aSAndroid Build Coastguard WorkerThe "ideal" solution here is to, as mentioned above, shard batch trace processor 183*6dbdd20aSAndroid Build Coastguard Workeracross >1 machine. When querying traces, each trace is entirely independent of 184*6dbdd20aSAndroid Build Coastguard Workerany other so paralleising across multiple machines yields very close to perfect 185*6dbdd20aSAndroid Build Coastguard Workergains in performance at little cost. 186*6dbdd20aSAndroid Build Coastguard Worker 187*6dbdd20aSAndroid Build Coastguard WorkerThis is would be however quite a complex undertaking. We would need to design 188*6dbdd20aSAndroid Build Coastguard Workerthe API in such a way that allows for pluggable integration with various compute 189*6dbdd20aSAndroid Build Coastguard Workerplatforms (e.g. GCP, Google internal, your custom infra). Even restricting to 190*6dbdd20aSAndroid Build Coastguard Workerjust Google infra and leaving others as open for contribution, internal infra's 191*6dbdd20aSAndroid Build Coastguard Workerideal workload does not match the approach of "have a bunch of machines tied to 192*6dbdd20aSAndroid Build Coastguard Workerone user waiting for their input". There would need to be significiant research 193*6dbdd20aSAndroid Build Coastguard Workerand design work before going here but it would likely be wortwhile. 194