1 /*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <emscripten/emscripten.h>
18 #include <map>
19 #include <string>
20
21 #include "perfetto/base/logging.h"
22 #include "perfetto/trace_processor/trace_processor.h"
23 #include "src/trace_processor/rpc/rpc.h"
24
25 namespace perfetto {
26 namespace trace_processor {
27
28 using RequestID = uint32_t;
29
30 // Reply(): replies to a RPC method invocation.
31 // Called asynchronously (i.e. in a separate task) by the C++ code inside the
32 // trace processor to return data for a RPC method call.
33 // The function is generic and thankfully we need just one for all methods
34 // because the output is always a protobuf buffer.
35 using ReplyFunction = void (*)(const char* /*proto_reply_data*/,
36 uint32_t /*len*/);
37
38 namespace {
39 Rpc* g_trace_processor_rpc;
40 ReplyFunction g_reply;
41
42 // The buffer used to pass the request arguments. The caller (JS) decides how
43 // big this buffer should be in the Initialize() call.
44 uint8_t* g_req_buf;
45
46 } // namespace
47
48 // +---------------------------------------------------------------------------+
49 // | Exported functions called by the JS/TS running in the worker. |
50 // +---------------------------------------------------------------------------+
51 extern "C" {
52
53 // Returns the address of the allocated request buffer.
54 uint8_t* EMSCRIPTEN_KEEPALIVE Initialize(ReplyFunction, uint32_t);
Initialize(ReplyFunction reply_function,uint32_t req_buffer_size)55 uint8_t* Initialize(ReplyFunction reply_function, uint32_t req_buffer_size) {
56 g_trace_processor_rpc = new Rpc();
57 g_reply = reply_function;
58 g_req_buf = new uint8_t[req_buffer_size];
59 return g_req_buf;
60 }
61
62 // Ingests trace data.
63 void EMSCRIPTEN_KEEPALIVE trace_processor_parse(uint32_t);
trace_processor_parse(uint32_t size)64 void trace_processor_parse(uint32_t size) {
65 // TODO(primiano): Parse() makes a copy of the data, which is unfortunate.
66 // Ideally there should be a way to take the Blob coming from JS and move it.
67 // See https://github.com/WebAssembly/design/issues/1162.
68 auto status = g_trace_processor_rpc->Parse(g_req_buf, size);
69 if (status.ok()) {
70 g_reply("", 0);
71 } else {
72 PERFETTO_FATAL("Fatal failure while parsing the trace: %s",
73 status.c_message());
74 }
75 }
76
77 // We keep the same signature as other methods even though we don't take input
78 // arguments for simplicity.
79 void EMSCRIPTEN_KEEPALIVE trace_processor_notify_eof(uint32_t);
trace_processor_notify_eof(uint32_t)80 void trace_processor_notify_eof(uint32_t /* size, not used. */) {
81 g_trace_processor_rpc->NotifyEndOfFile();
82 g_reply("", 0);
83 }
84
85 void EMSCRIPTEN_KEEPALIVE trace_processor_raw_query(uint32_t);
trace_processor_raw_query(uint32_t size)86 void trace_processor_raw_query(uint32_t size) {
87 std::vector<uint8_t> res = g_trace_processor_rpc->RawQuery(g_req_buf, size);
88 g_reply(reinterpret_cast<const char*>(res.data()),
89 static_cast<uint32_t>(res.size()));
90 }
91
92 void EMSCRIPTEN_KEEPALIVE trace_processor_compute_metric(uint32_t);
trace_processor_compute_metric(uint32_t size)93 void trace_processor_compute_metric(uint32_t size) {
94 std::vector<uint8_t> res =
95 g_trace_processor_rpc->ComputeMetric(g_req_buf, size);
96 g_reply(reinterpret_cast<const char*>(res.data()),
97 static_cast<uint32_t>(res.size()));
98 }
99
100 void EMSCRIPTEN_KEEPALIVE trace_processor_get_metric_descriptors(uint32_t);
trace_processor_get_metric_descriptors(uint32_t size)101 void trace_processor_get_metric_descriptors(uint32_t size) {
102 std::vector<uint8_t> res =
103 g_trace_processor_rpc->GetMetricDescriptors(g_req_buf, size);
104 g_reply(reinterpret_cast<const char*>(res.data()),
105 static_cast<uint32_t>(res.size()));
106 }
107
108 void EMSCRIPTEN_KEEPALIVE trace_processor_enable_metatrace(uint32_t);
trace_processor_enable_metatrace(uint32_t)109 void trace_processor_enable_metatrace(uint32_t) {
110 g_trace_processor_rpc->EnableMetatrace();
111 g_reply("", 0);
112 }
113
114 void EMSCRIPTEN_KEEPALIVE trace_processor_disable_and_read_metatrace(uint32_t);
trace_processor_disable_and_read_metatrace(uint32_t)115 void trace_processor_disable_and_read_metatrace(uint32_t) {
116 std::vector<uint8_t> res = g_trace_processor_rpc->DisableAndReadMetatrace();
117 g_reply(reinterpret_cast<const char*>(res.data()),
118 static_cast<uint32_t>(res.size()));
119 }
120
121 } // extern "C"
122 } // namespace trace_processor
123 } // namespace perfetto
124
main(int,char **)125 int main(int, char**) {
126 // This is unused but is needed for the following series of reason:
127 // - We need the callMain() Emscripten JS helper function for traceconv (but
128 // not for trace_processor).
129 // - Newer versions of emscripten require that callMain is explicitly exported
130 // via EXTRA_EXPORTED_RUNTIME_METHODS = ['callMain'].
131 // - We have one set of EXTRA_EXPORTED_RUNTIME_METHODS for both
132 // trace_processor.wasm (which does not need a main) and traceconv (which
133 // does).
134 // - Without this main(), the Wasm bootstrap code will cause a JS error at
135 // runtime when trying to load trace_processor.js.
136 return 0;
137 }
138