• 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 {defer} from '../base/deferred';
16import {assertExists, assertTrue} from '../base/logging';
17import * as init_trace_processor from '../gen/trace_processor';
18
19// The Initialize() call will allocate a buffer of REQ_BUF_SIZE bytes which
20// will be used to copy the input request data. This is to avoid passing the
21// input data on the stack, which has a limited (~1MB) size.
22// The buffer will be allocated by the C++ side and reachable at
23// HEAPU8[reqBufferAddr, +REQ_BUFFER_SIZE].
24const REQ_BUF_SIZE = 32 * 1024 * 1024;
25
26export interface WasmBridgeRequest {
27  id: number;
28  methodName: string;
29  data: Uint8Array;
30}
31
32export interface WasmBridgeResponse {
33  id: number;
34  data: Uint8Array;
35}
36
37export class WasmBridge {
38  // When this promise has resolved it is safe to call callWasm.
39  whenInitialized: Promise<void>;
40
41  private aborted: boolean;
42  private currentRequestResult: WasmBridgeResponse|null;
43  private connection: init_trace_processor.Module;
44  private reqBufferAddr = 0;
45  private lastStderr: string[] = [];
46
47  constructor(init: init_trace_processor.InitWasm) {
48    this.aborted = false;
49    this.currentRequestResult = null;
50
51    const deferredRuntimeInitialized = defer<void>();
52    this.connection = init({
53      locateFile: (s: string) => s,
54      print: (line: string) => console.log(line),
55      printErr: (line: string) => this.appendAndLogErr(line),
56      onRuntimeInitialized: () => deferredRuntimeInitialized.resolve(),
57    });
58    this.whenInitialized = deferredRuntimeInitialized.then(() => {
59      const fn = this.connection.addFunction(this.onReply.bind(this), 'vii');
60      this.reqBufferAddr = this.connection.ccall(
61          'Initialize',
62          /*return=*/ 'number',
63          /*args=*/['number', 'number'],
64          [fn, REQ_BUF_SIZE]);
65    });
66  }
67
68  callWasm(req: WasmBridgeRequest): WasmBridgeResponse {
69    if (this.aborted) {
70      throw new Error('Wasm module crashed');
71    }
72    assertTrue(req.data.length <= REQ_BUF_SIZE);
73    const endAddr = this.reqBufferAddr + req.data.length;
74    this.connection.HEAPU8.subarray(this.reqBufferAddr, endAddr).set(req.data);
75    try {
76      this.connection.ccall(
77          req.methodName,    // C method name.
78          'void',            // Return type.
79          ['number'],        // Arg types.
80          [req.data.length]  // Args.
81      );
82      const result = assertExists(this.currentRequestResult);
83      this.currentRequestResult = null;
84      result.id = req.id;
85      return result;
86    } catch (err) {
87      this.aborted = true;
88      let abortReason = `${err}`;
89      if (err instanceof Error) {
90        abortReason = `${err.name}: ${err.message}\n${err.stack}`;
91      }
92      abortReason += '\n\nstderr: \n' + this.lastStderr.join('\n');
93      throw new Error(abortReason);
94    }
95  }
96
97  // This is invoked from ccall in the same call stack as callWasm.
98  private onReply(heapPtr: number, size: number) {
99    const data = this.connection.HEAPU8.slice(heapPtr, heapPtr + size);
100    this.currentRequestResult = {
101      id: 0,  // Will be set by callWasm()'s epilogue.
102      data,
103    };
104  }
105
106  private appendAndLogErr(line: string) {
107    console.warn(line);
108    // Keep the last N lines in the |lastStderr| buffer.
109    this.lastStderr.push(line);
110    if (this.lastStderr.length > 512) {
111      this.lastStderr.shift();
112    }
113  }
114}
115