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