xref: /aosp_15_r20/external/perfetto/docs/visualization/deep-linking-to-perfetto-ui.md (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1# Deep linking to the Perfetto UI
2
3This document describes how to open traces hosted on external servers with the
4Perfetto UI. This can help integrating the Perfetto UI with custom dashboards
5and implement _'Open with Perfetto UI'_-like features.
6
7## Using window.open and postMessage
8
9The supported way of doing this is to _inject_ the trace as an ArrayBuffer
10via `window.open('https://ui.perfetto.dev')` and `postMessage()`.
11In order to do this you need some minimal JavaScript code running on some
12hosting infrastructure you control which can access the trace file. In most
13cases this is some dashboard which you want to deep-link to the Perfetto UI.
14
15#### Open ui.perfetto.dev via window.open
16
17The source dashboard, the one that knows how to locate a trace and deal with
18ACL checking / oauth authentication and the like, creates a new tab by doing
19
20```js
21var handle = window.open('https://ui.perfetto.dev');
22```
23
24The window handle allows bidirectional communication using `postMessage()`
25between the source dashboard and the Perfetto UI.
26
27#### Wait for the UI to be ready via PING/PONG
28
29Wait for the UI to be ready. The `window.open()` message channel is not
30buffered. If you send a message before the opened page has registered an
31`onmessage` listener the messagge will be dropped on the floor.
32In order to avoid this race, you can use a very basic PING/PONG protocol: keep
33sending a 'PING' message until the opened window replies with a 'PONG'.
34When this happens, that is the signal that the Perfetto UI is ready to open
35traces.
36
37#### Post a message the following JavaScript object
38
39```js
40  {
41    'perfetto': {
42      buffer: ArrayBuffer;
43      title: string;
44      fileName?: string;  // Optional
45      url?: string;       // Optional
46    }
47  }
48```
49
50`buffer` is the ArrayBuffer with the actual trace file content. This is
51typically something that you obtain by doing a `fetch()` on your backend
52storage.
53
54`title` is the human friendly trace title that will be shown in the
55sidebar. This can help people to disambiguate traces from several tabs.
56
57`fileName` will be used if the user clicks on "Download". A generic name will
58be used if omitted.
59
60`url` is used if the user clicks on the "Share" link in the sidebar. This should
61print to a URL owned by you that would cause your dashboard to re-open the
62current trace, by re-kicking-off the window.open() process herein described.
63If omitted traces won't be shareable.
64
65### Code samples
66
67See [this example caller](https://bl.ocks.org/chromy/170c11ce30d9084957d7f3aa065e89f8),
68for which the code is in
69[this GitHub gist](https://gist.github.com/chromy/170c11ce30d9084957d7f3aa065e89f8).
70
71Googlers: take a look at the
72[existing examples in the internal codesearch](http://go/perfetto-ui-deeplink-cs)
73
74### Common pitfalls
75
76Many browsers sometimes block window.open() requests prompting the user to allow
77popups for the site. This usually happens if:
78
79- The window.open() is NOT initiated by a user gesture.
80- Too much time is passed from the user gesture to the window.open()
81
82If the trace file is big enough, the fetch() might take long time and pass the
83user gesture threshold. This can be detected by observing that the window.open()
84returned `null`. When this happens the best option is to show another clickable
85element and bind the fetched trace ArrayBuffer to the new onclick handler, like
86the code in the example above does.
87
88Some browser can have a variable time threshold for the user gesture timeout
89which depends on the website engagement score (how much the user has visited
90the page that does the window.open() before). It's quite common when testing
91this code to see a popup blocker the first time the new feature is used and
92then not see it again.
93
94This scheme will not work from a `file://` based URL.
95This is due to browser security context for `file://` URLs.
96
97The source website must not be served with the
98`Cross-Origin-Opener-Policy: same-origin` header.
99For example see
100[this issue](https://github.com/google/perfetto/issues/525#issuecomment-1625055986).
101
102### Where does the posted trace go?
103
104The Perfetto UI is client-only and doesn't require any server-side interaction.
105Traces pushed via postMessage() are kept only in the browser memory/cache and
106are not sent to any server.
107
108## Why can't I just pass a URL?
109
110_"Why you don't let me just pass a URL to the Perfetto UI (e.g. ui.perfetto.dev?url=...) and you deal with all this?"_
111
112The answer to this is manifold and boils down to security.
113
114#### Cross origin requests blocking
115
116If ui.perfetto.dev had to do a `fetch('https://yourwebsite.com/trace')` that
117would be a cross-origin request. Browsers disallow by default cross-origin
118fetch requests.
119In order for this to work, the web server that hosts yourwebsite.com would have
120to expose a custom HTTP response header
121 (`Access-Control-Allow-Origin: https://ui.perfetto.dev`) to allow the fetch.
122In most cases customizing the HTTP response headers is outside of dashboard's
123owners control.
124
125You can learn more about CORS at
126https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
127
128#### Content Security Policy
129
130Perfetto UI uses a strict Content Security Policy which disallows foreign
131fetches and subresources, as a security mitigation about common attacks.
132Even assuming that CORS headers are properly set and your trace files are
133publicly accessible, fetching the trace from the Perfetto UI would require
134allow-listing your origin in our CSP policy. This is not scalable.
135
136You can learn more about CSP at
137https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
138
139#### Dealing with OAuth2 or other authentication mechanisms
140
141Even ignoring CORS, the Perfetto UI would have to deal with OAuth2 or other
142authentication mechanisms to fetch the trace file. Even if all the dashboards
143out there used OAuth2, that would still mean that Perfetto UI would have to know
144about all the possible OAuth2 scopes, one for each dashboard. This is not
145scalable.
146
147## Opening the trace at a specific event or time
148
149Using the fragment query string allows for more control over the UI after
150the trace opens. For example this URL:
151
152```
153https://ui.perfetto.dev/#!/?visStart=261191575272856&visEnd=261191675272856
154```
155
156Will open the pushed trace at 261191575272856ns (~261192s) and the
157viewing window will be 261191675272856ns -261191575272856ns = 100ms wide.
158
159**Selecting a slice on load**:
160
161You can pass the following parameters: `ts`, `dur`, `pid`, `tid`.
162The UI will query the slice table and find a slice that matches the parameters
163passed. If a slice is found it's highlighted.
164You don't have to provide all the parameters.
165Usually `ts` and `dur` suffice to uniquely identifying a slice.
166
167We deliberately do NOT support linking by slice id. This is because slice IDs
168are not stable across perfetto versions. Instead you can link a slice by passing
169the exact start and duration (`ts` and `dur`), as you see them by issuing a
170query `SELECT ts, dur FROM slices WHERE id=...`.
171
172**Zooming into a region of the trace on load**:
173
174Pass `visStart`, `visEnd`. These values are the raw values in `ns` as seen in
175the sql tables.
176
177**Issuing a query on load**:
178
179Pass the query in the `query` parameter.
180
181
182Try the following examples:
183- [visStart & visEnd](https://ui.perfetto.dev/#!/?url=https%3A%2F%2Fstorage.googleapis.com%2Fperfetto-misc%2Fexample_android_trace_15s&visStart=261191575272856&visEnd=261191675272856)
184- [ts & dur](https://ui.perfetto.dev/#!/?url=https%3A%2F%2Fstorage.googleapis.com%2Fperfetto-misc%2Fexample_android_trace_15s&ts=261192482777530&dur=1667500)
185- [query](https://ui.perfetto.dev/#!/?url=https%3A%2F%2Fstorage.googleapis.com%2Fperfetto-misc%2Fexample_android_trace_15s&query=select%20'Hello%2C%20world!'%20as%20msg)
186
187You must take care to correctly escape strings where needed.
188
189## Source links
190
191The source code that deals with the postMessage() in the Perfetto UI is
192[`post_message_handler.ts`](/ui/src/frontend/post_message_handler.ts).
193