• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (C) 2018 The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15import m from 'mithril';
16
17import {assertExists} from '../base/logging';
18import {Actions} from '../common/actions';
19import {VERSION} from '../gen/perfetto_version';
20import {StatusResult, TraceProcessorApiVersion} from '../protos';
21import {HttpRpcEngine} from '../trace_processor/http_rpc_engine';
22import {showModal} from '../widgets/modal';
23import {Router} from './router';
24
25import {globals} from './globals';
26import {publishHttpRpcState} from './publish';
27
28const CURRENT_API_VERSION =
29  TraceProcessorApiVersion.TRACE_PROCESSOR_CURRENT_API_VERSION;
30
31function getPromptMessage(tpStatus: StatusResult): string {
32  return `Trace Processor Native Accelerator detected on ${HttpRpcEngine.hostAndPort} with:
33${tpStatus.loadedTraceName}
34
35YES, use loaded trace:
36Will load from the current state of Trace Processor. If you did run
37trace_processor_shell --httpd file.pftrace this is likely what you want.
38
39YES, but reset state:
40Use this if you want to open another trace but still use the
41accelerator. This is the equivalent of killing and restarting
42trace_processor_shell --httpd.
43
44NO, Use builtin WASM:
45Will not use the accelerator in this tab.
46
47Using the native accelerator has some minor caveats:
48- Only one tab can be using the accelerator.
49- Sharing, downloading and conversion-to-legacy aren't supported.
50`;
51}
52
53function getIncompatibleRpcMessage(tpStatus: StatusResult): string {
54  return `The Trace Processor instance on ${HttpRpcEngine.hostAndPort} is too old.
55
56This UI requires TraceProcessor features that are not present in the
57Trace Processor native accelerator you are currently running.
58If you continue, this is almost surely going to cause UI failures.
59
60Please update your local Trace Processor binary:
61
62curl -LO https://get.perfetto.dev/trace_processor
63chmod +x ./trace_processor
64./trace_processor --httpd
65
66UI version code: ${VERSION}
67UI RPC API: ${CURRENT_API_VERSION}
68
69Trace processor version: ${tpStatus.humanReadableVersion}
70Trace processor version code: ${tpStatus.versionCode}
71Trace processor RPC API: ${tpStatus.apiVersion}
72`;
73}
74
75function getVersionMismatchMessage(tpStatus: StatusResult): string {
76  return `The trace processor instance on ${HttpRpcEngine.hostAndPort} is a different build from the UI.
77
78This may cause problems. Where possible it is better to use the matched version of the UI.
79You can do this by clicking the button below.
80
81UI version code: ${VERSION}
82UI RPC API: ${CURRENT_API_VERSION}
83
84Trace processor version: ${tpStatus.humanReadableVersion}
85Trace processor version code: ${tpStatus.versionCode}
86Trace processor RPC API: ${tpStatus.apiVersion}
87`;
88}
89
90// The flow is fairly complicated:
91// +-----------------------------------+
92// |        User loads the UI          |
93// +-----------------+-----------------+
94//                   |
95// +-----------------+-----------------+
96// |   Is trace_processor present at   |
97// |   HttpRpcEngine.hostAndPort?      |
98// +--------------------------+--------+
99//    |No                     |Yes
100//    |        +--------------+-------------------------------+
101//    |        |  Does version code of UI and TP match?       |
102//    |        +--------------+----------------------------+--+
103//    |                       |No                          |Yes
104//    |                       |                            |
105//    |                       |                            |
106//    |         +-------------+-------------+              |
107//    |         |Is a build of the UI at the|              |
108//    |         |TP version code existant   |              |
109//    |         |and reachable?             |              |
110//    |         +---+----------------+------+              |
111//    |             | No             | Yes                 |
112//    |             |                |                     |
113//    |             |       +--------+-------+             |
114//    |             |       |Dialog: Mismatch|             |
115//    |             |       |Load matched UI +-------------------------------+
116//    |             |       |Continue        +-+           |                 |
117//    |             |       +----------------+ |           |                 |
118//    |             |                          |           |                 |
119//    |      +------+--------------------------+----+      |                 |
120//    |      |TP RPC version >= UI RPC version      |      |                 |
121//    |      +----+-------------------+-------------+      |                 |
122//    |           | No                |Yes                 |                 |
123//    |      +----+--------------+    |                    |                 |
124//    |      |Dialog: Bad RPC    |    |                    |                 |
125//    |  +---+Use built-in WASM  |    |                    |                 |
126//    |  |   |Continue anyway    +----|                    |                 |
127//    |  |   +-------------------+    |        +-----------+-----------+     |
128//    |  |                            +--------+TP has preloaded trace?|     |
129//    |  |                                     +-+---------------+-----+     |
130//    |  |                                       |No             |Yes        |
131//    |  |                                       |  +---------------------+  |
132//    |  |                                       |  | Dialog: Preloaded?  |  |
133//    |  |                                       |  + YES, use loaded trace  |
134//    |  |                                 +--------| YES, but reset state|  |
135//    |  |  +---------------------------------------| NO, Use builtin Wasm|  |
136//    |  |  |                              |     |  +---------------------+  |
137//    |  |  |                              |     |                           |
138//    |  |  |                           Reset TP |                           |
139//    |  |  |                              |     |                           |
140//    |  |  |                              |     |                           |
141//  Show the UI                         Show the UI                  Link to
142//  (WASM mode)                         (RPC mode)                   matched UI
143
144// There are three options in the end:
145// - Show the UI (WASM mode)
146// - Show the UI (RPC mode)
147// - Redirect to a matched version of the UI
148
149// Try to connect to the external Trace Processor HTTP RPC accelerator (if
150// available, often it isn't). If connected it will populate the
151// |httpRpcState| in the frontend local state. In turn that will show the UI
152// chip in the sidebar. trace_controller.ts will repeat this check before
153// trying to load a new trace. We do this ahead of time just to have a
154// consistent UX (i.e. so that the user can tell if the RPC is working without
155// having to open a trace).
156export async function CheckHttpRpcConnection(): Promise<void> {
157  const state = await HttpRpcEngine.checkConnection();
158  publishHttpRpcState(state);
159  if (!state.connected) {
160    // No RPC = exit immediately to the WASM UI.
161    return;
162  }
163  const tpStatus = assertExists(state.status);
164
165  function forceWasm() {
166    globals.dispatch(Actions.setNewEngineMode({mode: 'FORCE_BUILTIN_WASM'}));
167  }
168
169  // Check short version:
170  if (tpStatus.versionCode !== '' && tpStatus.versionCode !== VERSION) {
171    const url = await Router.isVersionAvailable(tpStatus.versionCode);
172    if (url !== undefined) {
173      // If matched UI available show a dialog asking the user to
174      // switch.
175      const result = await showDialogVersionMismatch(tpStatus, url);
176      switch (result) {
177        case MismatchedVersionDialog.Dismissed:
178        case MismatchedVersionDialog.UseMatchingUi:
179          Router.navigateToVersion(tpStatus.versionCode);
180          return;
181        case MismatchedVersionDialog.UseMismatchedRpc:
182          break;
183        case MismatchedVersionDialog.UseWasm:
184          forceWasm();
185          return;
186        default:
187          const x: never = result;
188          throw new Error(`Unsupported result ${x}`);
189      }
190    }
191  }
192
193  // Check the RPC version:
194  if (tpStatus.apiVersion < CURRENT_API_VERSION) {
195    const result = await showDialogIncompatibleRPC(tpStatus);
196    switch (result) {
197      case IncompatibleRpcDialogResult.Dismissed:
198      case IncompatibleRpcDialogResult.UseWasm:
199        forceWasm();
200        return;
201      case IncompatibleRpcDialogResult.UseIncompatibleRpc:
202        break;
203      default:
204        const x: never = result;
205        throw new Error(`Unsupported result ${x}`);
206    }
207  }
208
209  // Check if pre-loaded:
210  if (tpStatus.loadedTraceName) {
211    // If a trace is already loaded in the trace processor (e.g., the user
212    // launched trace_processor_shell -D trace_file.pftrace), prompt the user to
213    // initialize the UI with the already-loaded trace.
214    const result = await showDialogToUsePreloadedTrace(tpStatus);
215    switch (result) {
216      case PreloadedDialogResult.Dismissed:
217      case PreloadedDialogResult.UseRpcWithPreloadedTrace:
218        globals.dispatch(Actions.openTraceFromHttpRpc({}));
219        return;
220      case PreloadedDialogResult.UseRpc:
221        // Resetting state is the default.
222        return;
223      case PreloadedDialogResult.UseWasm:
224        forceWasm();
225        return;
226      default:
227        const x: never = result;
228        throw new Error(`Unsupported result ${x}`);
229    }
230  }
231}
232
233enum MismatchedVersionDialog {
234  UseMatchingUi = 'useMatchingUi',
235  UseWasm = 'useWasm',
236  UseMismatchedRpc = 'useMismatchedRpc',
237  Dismissed = 'dismissed',
238}
239
240async function showDialogVersionMismatch(
241  tpStatus: StatusResult,
242  url: string,
243): Promise<MismatchedVersionDialog> {
244  let result = MismatchedVersionDialog.Dismissed;
245  await showModal({
246    title: 'Version mismatch',
247    content: m('.modal-pre', getVersionMismatchMessage(tpStatus)),
248    buttons: [
249      {
250        primary: true,
251        text: `Open ${url}`,
252        action: () => {
253          result = MismatchedVersionDialog.UseMatchingUi;
254        },
255      },
256      {
257        text: 'Use builtin Wasm',
258        action: () => {
259          result = MismatchedVersionDialog.UseWasm;
260        },
261      },
262      {
263        text: 'Use mismatched version regardless (might crash)',
264        action: () => {
265          result = MismatchedVersionDialog.UseMismatchedRpc;
266        },
267      },
268    ],
269  });
270  return result;
271}
272
273enum IncompatibleRpcDialogResult {
274  UseWasm = 'useWasm',
275  UseIncompatibleRpc = 'useIncompatibleRpc',
276  Dismissed = 'dismissed',
277}
278
279async function showDialogIncompatibleRPC(
280  tpStatus: StatusResult,
281): Promise<IncompatibleRpcDialogResult> {
282  let result = IncompatibleRpcDialogResult.Dismissed;
283  await showModal({
284    title: 'Incompatible RPC version',
285    content: m('.modal-pre', getIncompatibleRpcMessage(tpStatus)),
286    buttons: [
287      {
288        text: 'Use builtin Wasm',
289        primary: true,
290        action: () => {
291          result = IncompatibleRpcDialogResult.UseWasm;
292        },
293      },
294      {
295        text: 'Use old version regardless (will crash)',
296        action: () => {
297          result = IncompatibleRpcDialogResult.UseIncompatibleRpc;
298        },
299      },
300    ],
301  });
302  return result;
303}
304
305enum PreloadedDialogResult {
306  UseRpcWithPreloadedTrace = 'useRpcWithPreloadedTrace',
307  UseRpc = 'useRpc',
308  UseWasm = 'useWasm',
309  Dismissed = 'dismissed',
310}
311
312async function showDialogToUsePreloadedTrace(
313  tpStatus: StatusResult,
314): Promise<PreloadedDialogResult> {
315  let result = PreloadedDialogResult.Dismissed;
316  await showModal({
317    title: 'Use trace processor native acceleration?',
318    content: m('.modal-pre', getPromptMessage(tpStatus)),
319    buttons: [
320      {
321        text: 'YES, use loaded trace',
322        primary: true,
323        action: () => {
324          result = PreloadedDialogResult.UseRpcWithPreloadedTrace;
325        },
326      },
327      {
328        text: 'YES, but reset state',
329        action: () => {
330          result = PreloadedDialogResult.UseRpc;
331        },
332      },
333      {
334        text: 'NO, Use builtin WASM',
335        action: () => {
336          result = PreloadedDialogResult.UseWasm;
337        },
338      },
339    ],
340  });
341  return result;
342}
343