xref: /aosp_15_r20/external/perfetto/docs/design-docs/batch-trace-processor.md (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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![BTP Overview](/docs/images/perfetto-btp-overview.svg)
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![BTP Flattening](/docs/images/perfetto-btp-flattening.svg)
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