aboutsummaryrefslogtreecommitdiff
path: root/src/trace_processor/rpc/wasm_bridge.cc
blob: 79e5dae7808e7dffbbcdd186172e7c2f58292502 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <emscripten/emscripten.h>
#include <map>
#include <string>

#include "perfetto/base/logging.h"
#include "perfetto/trace_processor/trace_processor.h"
#include "src/trace_processor/rpc/rpc.h"

namespace perfetto {
namespace trace_processor {

using RequestID = uint32_t;

// Reply(): replies to a RPC method invocation.
// Called asynchronously (i.e. in a separate task) by the C++ code inside the
// trace processor to return data for a RPC method call.
// The function is generic and thankfully we need just one for all methods
// because the output is always a protobuf buffer.
using ReplyFunction = void (*)(const char* /*proto_reply_data*/,
                               uint32_t /*len*/);

namespace {
Rpc* g_trace_processor_rpc;
ReplyFunction g_reply;

// The buffer used to pass the request arguments. The caller (JS) decides how
// big this buffer should be in the Initialize() call.
uint8_t* g_req_buf;

}  // namespace

// +---------------------------------------------------------------------------+
// | Exported functions called by the JS/TS running in the worker.             |
// +---------------------------------------------------------------------------+
extern "C" {

// Returns the address of the allocated request buffer.
uint8_t* EMSCRIPTEN_KEEPALIVE Initialize(ReplyFunction, uint32_t);
uint8_t* Initialize(ReplyFunction reply_function, uint32_t req_buffer_size) {
  g_trace_processor_rpc = new Rpc();
  g_reply = reply_function;
  g_req_buf = new uint8_t[req_buffer_size];
  return g_req_buf;
}

// Ingests trace data.
void EMSCRIPTEN_KEEPALIVE trace_processor_parse(uint32_t);
void trace_processor_parse(uint32_t size) {
  // TODO(primiano): Parse() makes a copy of the data, which is unfortunate.
  // Ideally there should be a way to take the Blob coming from JS and move it.
  // See https://github.com/WebAssembly/design/issues/1162.
  auto status = g_trace_processor_rpc->Parse(g_req_buf, size);
  if (status.ok()) {
    g_reply("", 0);
  } else {
    PERFETTO_FATAL("Fatal failure while parsing the trace: %s",
                   status.c_message());
  }
}

// We keep the same signature as other methods even though we don't take input
// arguments for simplicity.
void EMSCRIPTEN_KEEPALIVE trace_processor_notify_eof(uint32_t);
void trace_processor_notify_eof(uint32_t /* size, not used. */) {
  g_trace_processor_rpc->NotifyEndOfFile();
  g_reply("", 0);
}

void EMSCRIPTEN_KEEPALIVE trace_processor_raw_query(uint32_t);
void trace_processor_raw_query(uint32_t size) {
  std::vector<uint8_t> res = g_trace_processor_rpc->RawQuery(g_req_buf, size);
  g_reply(reinterpret_cast<const char*>(res.data()),
          static_cast<uint32_t>(res.size()));
}

void EMSCRIPTEN_KEEPALIVE trace_processor_compute_metric(uint32_t);
void trace_processor_compute_metric(uint32_t size) {
  std::vector<uint8_t> res =
      g_trace_processor_rpc->ComputeMetric(g_req_buf, size);
  g_reply(reinterpret_cast<const char*>(res.data()),
          static_cast<uint32_t>(res.size()));
}

void EMSCRIPTEN_KEEPALIVE trace_processor_get_metric_descriptors(uint32_t);
void trace_processor_get_metric_descriptors(uint32_t size) {
  std::vector<uint8_t> res =
      g_trace_processor_rpc->GetMetricDescriptors(g_req_buf, size);
  g_reply(reinterpret_cast<const char*>(res.data()),
          static_cast<uint32_t>(res.size()));
}

void EMSCRIPTEN_KEEPALIVE trace_processor_enable_metatrace(uint32_t);
void trace_processor_enable_metatrace(uint32_t) {
  g_trace_processor_rpc->EnableMetatrace();
  g_reply("", 0);
}

void EMSCRIPTEN_KEEPALIVE trace_processor_disable_and_read_metatrace(uint32_t);
void trace_processor_disable_and_read_metatrace(uint32_t) {
  std::vector<uint8_t> res = g_trace_processor_rpc->DisableAndReadMetatrace();
  g_reply(reinterpret_cast<const char*>(res.data()),
          static_cast<uint32_t>(res.size()));
}

}  // extern "C"
}  // namespace trace_processor
}  // namespace perfetto

int main(int, char**) {
  // This is unused but is needed for the following series of reason:
  // - We need the callMain() Emscripten JS helper function for traceconv (but
  //   not for trace_processor).
  // - Newer versions of emscripten require that callMain is explicitly exported
  //   via EXTRA_EXPORTED_RUNTIME_METHODS = ['callMain'].
  // - We have one set of EXTRA_EXPORTED_RUNTIME_METHODS for both
  //   trace_processor.wasm (which does not need a main) and traceconv (which
  //   does).
  // - Without this main(), the Wasm bootstrap code will cause a JS error at
  //   runtime when trying to load trace_processor.js.
  return 0;
}