1 /*
2 * Copyright (C) 2019 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 "src/trace_processor/rpc/rpc.h"
18
19 #include <cinttypes>
20 #include <cstdint>
21 #include <cstdio>
22 #include <cstring>
23 #include <memory>
24 #include <string>
25 #include <utility>
26 #include <vector>
27
28 #include "perfetto/base/logging.h"
29 #include "perfetto/base/status.h"
30 #include "perfetto/base/time.h"
31 #include "perfetto/ext/base/version.h"
32 #include "perfetto/ext/protozero/proto_ring_buffer.h"
33 #include "perfetto/ext/trace_processor/rpc/query_result_serializer.h"
34 #include "perfetto/protozero/field.h"
35 #include "perfetto/protozero/proto_utils.h"
36 #include "perfetto/protozero/scattered_heap_buffer.h"
37 #include "perfetto/trace_processor/basic_types.h"
38 #include "perfetto/trace_processor/metatrace_config.h"
39 #include "perfetto/trace_processor/trace_processor.h"
40 #include "src/trace_processor/tp_metatrace.h"
41 #include "src/trace_processor/util/status_macros.h"
42
43 #include "protos/perfetto/trace_processor/metatrace_categories.pbzero.h"
44 #include "protos/perfetto/trace_processor/trace_processor.pbzero.h"
45
46 namespace perfetto::trace_processor {
47
48 namespace {
49 // Writes a "Loading trace ..." update every N bytes.
50 constexpr size_t kProgressUpdateBytes = 50ul * 1000 * 1000;
51 using TraceProcessorRpcStream = protos::pbzero::TraceProcessorRpcStream;
52 using RpcProto = protos::pbzero::TraceProcessorRpc;
53
54 // Most RPC messages are either very small or a query results.
55 // QueryResultSerializer splits rows into batches of approximately 128KB. Try
56 // avoid extra heap allocations for the nominal case.
57 constexpr auto kSliceSize =
58 QueryResultSerializer::kDefaultBatchSplitThreshold + 4096;
59
60 // Holds a trace_processor::TraceProcessorRpc pbzero message. Avoids extra
61 // copies by doing direct scattered calls from the fragmented heap buffer onto
62 // the RpcResponseFunction (the receiver is expected to deal with arbitrary
63 // fragmentation anyways). It also takes care of prefixing each message with
64 // the proto preamble and varint size.
65 class Response {
66 public:
67 Response(int64_t seq, int method);
68 Response(const Response&) = delete;
69 Response& operator=(const Response&) = delete;
operator ->()70 RpcProto* operator->() { return msg_; }
71 void Send(Rpc::RpcResponseFunction);
72
73 private:
74 RpcProto* msg_ = nullptr;
75
76 // The reason why we use TraceProcessorRpcStream as root message is because
77 // the RPC wire protocol expects each message to be prefixed with a proto
78 // preamble and varint size. This happens to be the same serialization of a
79 // repeated field (this is really the same trick we use between
80 // Trace and TracePacket in trace.proto)
81 protozero::HeapBuffered<TraceProcessorRpcStream> buf_;
82 };
83
Response(int64_t seq,int method)84 Response::Response(int64_t seq, int method) : buf_(kSliceSize, kSliceSize) {
85 msg_ = buf_->add_msg();
86 msg_->set_seq(seq);
87 msg_->set_response(static_cast<RpcProto::TraceProcessorMethod>(method));
88 }
89
Send(Rpc::RpcResponseFunction send_fn)90 void Response::Send(Rpc::RpcResponseFunction send_fn) {
91 buf_->Finalize();
92 for (const auto& slice : buf_.GetSlices()) {
93 auto range = slice.GetUsedRange();
94 send_fn(range.begin, static_cast<uint32_t>(range.size()));
95 }
96 }
97
98 } // namespace
99
Rpc(std::unique_ptr<TraceProcessor> preloaded_instance)100 Rpc::Rpc(std::unique_ptr<TraceProcessor> preloaded_instance)
101 : trace_processor_(std::move(preloaded_instance)) {
102 if (!trace_processor_)
103 ResetTraceProcessorInternal(Config());
104 }
105
Rpc()106 Rpc::Rpc() : Rpc(nullptr) {}
107 Rpc::~Rpc() = default;
108
ResetTraceProcessorInternal(const Config & config)109 void Rpc::ResetTraceProcessorInternal(const Config& config) {
110 trace_processor_config_ = config;
111 trace_processor_ = TraceProcessor::CreateInstance(config);
112 bytes_parsed_ = bytes_last_progress_ = 0;
113 t_parse_started_ = base::GetWallTimeNs().count();
114 // Deliberately not resetting the RPC channel state (rxbuf_, {tx,rx}_seq_id_).
115 // This is invoked from the same client to clear the current trace state
116 // before loading a new one. The IPC channel is orthogonal to that and the
117 // message numbering continues regardless of the reset.
118 }
119
OnRpcRequest(const void * data,size_t len)120 void Rpc::OnRpcRequest(const void* data, size_t len) {
121 rxbuf_.Append(data, len);
122 for (;;) {
123 auto msg = rxbuf_.ReadMessage();
124 if (!msg.valid()) {
125 if (msg.fatal_framing_error) {
126 protozero::HeapBuffered<TraceProcessorRpcStream> err_msg;
127 err_msg->add_msg()->set_fatal_error("RPC framing error");
128 auto err = err_msg.SerializeAsArray();
129 rpc_response_fn_(err.data(), static_cast<uint32_t>(err.size()));
130 rpc_response_fn_(nullptr, 0); // Disconnect.
131 }
132 break;
133 }
134 ParseRpcRequest(msg.start, msg.len);
135 }
136 }
137
138 namespace {
139
140 using ProtoEnum = protos::pbzero::MetatraceCategories;
MetatraceCategoriesToPublicEnum(ProtoEnum categories)141 TraceProcessor::MetatraceCategories MetatraceCategoriesToPublicEnum(
142 ProtoEnum categories) {
143 TraceProcessor::MetatraceCategories result =
144 TraceProcessor::MetatraceCategories::NONE;
145 if (categories & ProtoEnum::QUERY_TIMELINE) {
146 result = static_cast<TraceProcessor::MetatraceCategories>(
147 result | TraceProcessor::MetatraceCategories::QUERY_TIMELINE);
148 }
149 if (categories & ProtoEnum::QUERY_DETAILED) {
150 result = static_cast<TraceProcessor::MetatraceCategories>(
151 result | TraceProcessor::MetatraceCategories::QUERY_DETAILED);
152 }
153 if (categories & ProtoEnum::FUNCTION_CALL) {
154 result = static_cast<TraceProcessor::MetatraceCategories>(
155 result | TraceProcessor::MetatraceCategories::FUNCTION_CALL);
156 }
157 if (categories & ProtoEnum::DB) {
158 result = static_cast<TraceProcessor::MetatraceCategories>(
159 result | TraceProcessor::MetatraceCategories::DB);
160 }
161 if (categories & ProtoEnum::API_TIMELINE) {
162 result = static_cast<TraceProcessor::MetatraceCategories>(
163 result | TraceProcessor::MetatraceCategories::API_TIMELINE);
164 }
165 return result;
166 }
167
168 } // namespace
169
170 // [data, len] here is a tokenized TraceProcessorRpc proto message, without the
171 // size header.
ParseRpcRequest(const uint8_t * data,size_t len)172 void Rpc::ParseRpcRequest(const uint8_t* data, size_t len) {
173 RpcProto::Decoder req(data, len);
174
175 // We allow restarting the sequence from 0. This happens when refreshing the
176 // browser while using the external trace_processor_shell --httpd.
177 if (req.seq() != 0 && rx_seq_id_ != 0 && req.seq() != rx_seq_id_ + 1) {
178 char err_str[255];
179 // "(ERR:rpc_seq)" is intercepted by error_dialog.ts in the UI.
180 snprintf(err_str, sizeof(err_str),
181 "RPC request out of order. Expected %" PRId64 ", got %" PRId64
182 " (ERR:rpc_seq)",
183 rx_seq_id_ + 1, req.seq());
184 PERFETTO_ELOG("%s", err_str);
185 protozero::HeapBuffered<TraceProcessorRpcStream> err_msg;
186 err_msg->add_msg()->set_fatal_error(err_str);
187 auto err = err_msg.SerializeAsArray();
188 rpc_response_fn_(err.data(), static_cast<uint32_t>(err.size()));
189 rpc_response_fn_(nullptr, 0); // Disconnect.
190 return;
191 }
192 rx_seq_id_ = req.seq();
193
194 // The static cast is to prevent that the compiler breaks future proofness.
195 const int req_type = static_cast<int>(req.request());
196 static const char kErrFieldNotSet[] = "RPC error: request field not set";
197 switch (req_type) {
198 case RpcProto::TPM_APPEND_TRACE_DATA: {
199 Response resp(tx_seq_id_++, req_type);
200 auto* result = resp->set_append_result();
201 if (!req.has_append_trace_data()) {
202 result->set_error(kErrFieldNotSet);
203 } else {
204 protozero::ConstBytes byte_range = req.append_trace_data();
205 base::Status res = Parse(byte_range.data, byte_range.size);
206 if (!res.ok()) {
207 result->set_error(res.message());
208 }
209 }
210 resp.Send(rpc_response_fn_);
211 break;
212 }
213 case RpcProto::TPM_FINALIZE_TRACE_DATA: {
214 Response resp(tx_seq_id_++, req_type);
215 auto* result = resp->set_finalize_data_result();
216 base::Status res = NotifyEndOfFile();
217 if (!res.ok()) {
218 result->set_error(res.message());
219 }
220 resp.Send(rpc_response_fn_);
221 break;
222 }
223 case RpcProto::TPM_QUERY_STREAMING: {
224 if (!req.has_query_args()) {
225 Response resp(tx_seq_id_++, req_type);
226 auto* result = resp->set_query_result();
227 result->set_error(kErrFieldNotSet);
228 resp.Send(rpc_response_fn_);
229 } else {
230 protozero::ConstBytes args = req.query_args();
231 protos::pbzero::QueryArgs::Decoder query(args.data, args.size);
232 std::string sql = query.sql_query().ToStdString();
233
234 PERFETTO_TP_TRACE(metatrace::Category::API_TIMELINE, "RPC_QUERY",
235 [&](metatrace::Record* r) {
236 r->AddArg("SQL", sql);
237 if (query.has_tag()) {
238 r->AddArg("tag", query.tag());
239 }
240 });
241
242 auto it = trace_processor_->ExecuteQuery(sql);
243 QueryResultSerializer serializer(std::move(it));
244 for (bool has_more = true; has_more;) {
245 const auto seq_id = tx_seq_id_++;
246 Response resp(seq_id, req_type);
247 has_more = serializer.Serialize(resp->set_query_result());
248 const uint32_t resp_size = resp->Finalize();
249 if (resp_size < protozero::proto_utils::kMaxMessageLength) {
250 // This is the nominal case.
251 resp.Send(rpc_response_fn_);
252 continue;
253 }
254 // In rare cases a query can end up with a batch which is too big.
255 // Normally batches are automatically split before hitting the limit,
256 // but one can come up with a query where a single cell is > 256MB.
257 // If this happens, just bail out gracefully rather than creating an
258 // unparsable proto which will cause a RPC framing error.
259 // If we hit this, we have to discard `resp` because it's
260 // unavoidably broken (due to have overflown the 4-bytes size) and
261 // can't be parsed. Instead create a new response with the error.
262 Response err_resp(seq_id, req_type);
263 auto* qres = err_resp->set_query_result();
264 qres->add_batch()->set_is_last_batch(true);
265 qres->set_error(
266 "The query ended up with a response that is too big (" +
267 std::to_string(resp_size) +
268 " bytes). This usually happens when a single row is >= 256 MiB. "
269 "See also WRITE_FILE for dealing with large rows.");
270 err_resp.Send(rpc_response_fn_);
271 break;
272 }
273 }
274 break;
275 }
276 case RpcProto::TPM_COMPUTE_METRIC: {
277 Response resp(tx_seq_id_++, req_type);
278 auto* result = resp->set_metric_result();
279 if (!req.has_compute_metric_args()) {
280 result->set_error(kErrFieldNotSet);
281 } else {
282 protozero::ConstBytes args = req.compute_metric_args();
283 ComputeMetricInternal(args.data, args.size, result);
284 }
285 resp.Send(rpc_response_fn_);
286 break;
287 }
288 case RpcProto::TPM_GET_METRIC_DESCRIPTORS: {
289 Response resp(tx_seq_id_++, req_type);
290 auto descriptor_set = trace_processor_->GetMetricDescriptors();
291 auto* result = resp->set_metric_descriptors();
292 result->AppendRawProtoBytes(descriptor_set.data(), descriptor_set.size());
293 resp.Send(rpc_response_fn_);
294 break;
295 }
296 case RpcProto::TPM_RESTORE_INITIAL_TABLES: {
297 trace_processor_->RestoreInitialTables();
298 Response resp(tx_seq_id_++, req_type);
299 resp.Send(rpc_response_fn_);
300 break;
301 }
302 case RpcProto::TPM_ENABLE_METATRACE: {
303 using protos::pbzero::MetatraceCategories;
304 protozero::ConstBytes args = req.enable_metatrace_args();
305 EnableMetatrace(args.data, args.size);
306
307 Response resp(tx_seq_id_++, req_type);
308 resp.Send(rpc_response_fn_);
309 break;
310 }
311 case RpcProto::TPM_DISABLE_AND_READ_METATRACE: {
312 Response resp(tx_seq_id_++, req_type);
313 DisableAndReadMetatraceInternal(resp->set_metatrace());
314 resp.Send(rpc_response_fn_);
315 break;
316 }
317 case RpcProto::TPM_GET_STATUS: {
318 Response resp(tx_seq_id_++, req_type);
319 std::vector<uint8_t> status = GetStatus();
320 resp->set_status()->AppendRawProtoBytes(status.data(), status.size());
321 resp.Send(rpc_response_fn_);
322 break;
323 }
324 case RpcProto::TPM_RESET_TRACE_PROCESSOR: {
325 Response resp(tx_seq_id_++, req_type);
326 protozero::ConstBytes args = req.reset_trace_processor_args();
327 ResetTraceProcessor(args.data, args.size);
328 resp.Send(rpc_response_fn_);
329 break;
330 }
331 case RpcProto::TPM_REGISTER_SQL_PACKAGE: {
332 Response resp(tx_seq_id_++, req_type);
333 base::Status status = RegisterSqlPackage(req.register_sql_package_args());
334 auto* res = resp->set_register_sql_package_result();
335 if (!status.ok()) {
336 res->set_error(status.message());
337 }
338 resp.Send(rpc_response_fn_);
339 break;
340 }
341 default: {
342 // This can legitimately happen if the client is newer. We reply with a
343 // generic "unkown request" response, so the client can do feature
344 // detection
345 PERFETTO_DLOG("[RPC] Uknown request type (%d), size=%zu", req_type, len);
346 Response resp(tx_seq_id_++, req_type);
347 resp->set_invalid_request(
348 static_cast<RpcProto::TraceProcessorMethod>(req_type));
349 resp.Send(rpc_response_fn_);
350 break;
351 }
352 } // switch(req_type)
353 }
354
Parse(const uint8_t * data,size_t len)355 base::Status Rpc::Parse(const uint8_t* data, size_t len) {
356 PERFETTO_TP_TRACE(
357 metatrace::Category::API_TIMELINE, "RPC_PARSE",
358 [&](metatrace::Record* r) { r->AddArg("length", std::to_string(len)); });
359 if (eof_) {
360 // Reset the trace processor state if another trace has been previously
361 // loaded. Use the same TraceProcessor Config.
362 ResetTraceProcessorInternal(trace_processor_config_);
363 }
364
365 eof_ = false;
366 bytes_parsed_ += len;
367 MaybePrintProgress();
368
369 if (len == 0)
370 return base::OkStatus();
371
372 // TraceProcessor needs take ownership of the memory chunk.
373 std::unique_ptr<uint8_t[]> data_copy(new uint8_t[len]);
374 memcpy(data_copy.get(), data, len);
375 return trace_processor_->Parse(std::move(data_copy), len);
376 }
377
NotifyEndOfFile()378 base::Status Rpc::NotifyEndOfFile() {
379 PERFETTO_TP_TRACE(metatrace::Category::API_TIMELINE,
380 "RPC_NOTIFY_END_OF_FILE");
381
382 eof_ = true;
383 RETURN_IF_ERROR(trace_processor_->NotifyEndOfFile());
384 MaybePrintProgress();
385 return base::OkStatus();
386 }
387
ResetTraceProcessor(const uint8_t * args,size_t len)388 void Rpc::ResetTraceProcessor(const uint8_t* args, size_t len) {
389 protos::pbzero::ResetTraceProcessorArgs::Decoder reset_trace_processor_args(
390 args, len);
391 Config config;
392 if (reset_trace_processor_args.has_drop_track_event_data_before()) {
393 config.drop_track_event_data_before =
394 reset_trace_processor_args.drop_track_event_data_before() ==
395 protos::pbzero::ResetTraceProcessorArgs::
396 TRACK_EVENT_RANGE_OF_INTEREST
397 ? DropTrackEventDataBefore::kTrackEventRangeOfInterest
398 : DropTrackEventDataBefore::kNoDrop;
399 }
400 if (reset_trace_processor_args.has_ingest_ftrace_in_raw_table()) {
401 config.ingest_ftrace_in_raw_table =
402 reset_trace_processor_args.ingest_ftrace_in_raw_table();
403 }
404 if (reset_trace_processor_args.has_analyze_trace_proto_content()) {
405 config.analyze_trace_proto_content =
406 reset_trace_processor_args.analyze_trace_proto_content();
407 }
408 if (reset_trace_processor_args.has_ftrace_drop_until_all_cpus_valid()) {
409 config.soft_drop_ftrace_data_before =
410 reset_trace_processor_args.ftrace_drop_until_all_cpus_valid()
411 ? SoftDropFtraceDataBefore::kAllPerCpuBuffersValid
412 : SoftDropFtraceDataBefore::kNoDrop;
413 }
414 using Args = protos::pbzero::ResetTraceProcessorArgs;
415 switch (reset_trace_processor_args.parsing_mode()) {
416 case Args::ParsingMode::DEFAULT:
417 config.parsing_mode = ParsingMode::kDefault;
418 break;
419 case Args::ParsingMode::TOKENIZE_ONLY:
420 config.parsing_mode = ParsingMode::kTokenizeOnly;
421 break;
422 case Args::ParsingMode::TOKENIZE_AND_SORT:
423 config.parsing_mode = ParsingMode::kTokenizeAndSort;
424 break;
425 }
426 ResetTraceProcessorInternal(config);
427 }
428
RegisterSqlPackage(protozero::ConstBytes bytes)429 base::Status Rpc::RegisterSqlPackage(protozero::ConstBytes bytes) {
430 protos::pbzero::RegisterSqlPackageArgs::Decoder args(bytes);
431 SqlPackage package;
432 package.name = args.package_name().ToStdString();
433 package.allow_override = args.allow_override();
434 for (auto it = args.modules(); it; ++it) {
435 protos::pbzero::RegisterSqlPackageArgs::Module::Decoder m(*it);
436 package.modules.emplace_back(m.name().ToStdString(), m.sql().ToStdString());
437 }
438 return trace_processor_->RegisterSqlPackage(package);
439 }
440
MaybePrintProgress()441 void Rpc::MaybePrintProgress() {
442 if (eof_ || bytes_parsed_ - bytes_last_progress_ > kProgressUpdateBytes) {
443 bytes_last_progress_ = bytes_parsed_;
444 auto t_load_s =
445 static_cast<double>(base::GetWallTimeNs().count() - t_parse_started_) /
446 1e9;
447 fprintf(stderr, "\rLoading trace %.2f MB (%.1f MB/s)%s",
448 static_cast<double>(bytes_parsed_) / 1e6,
449 static_cast<double>(bytes_parsed_) / 1e6 / t_load_s,
450 (eof_ ? "\n" : ""));
451 fflush(stderr);
452 }
453 }
454
Query(const uint8_t * args,size_t len,const QueryResultBatchCallback & result_callback)455 void Rpc::Query(const uint8_t* args,
456 size_t len,
457 const QueryResultBatchCallback& result_callback) {
458 protos::pbzero::QueryArgs::Decoder query(args, len);
459 std::string sql = query.sql_query().ToStdString();
460 PERFETTO_TP_TRACE(metatrace::Category::API_TIMELINE, "RPC_QUERY",
461 [&](metatrace::Record* r) {
462 r->AddArg("SQL", sql);
463 if (query.has_tag()) {
464 r->AddArg("tag", query.tag());
465 }
466 });
467
468 auto it = trace_processor_->ExecuteQuery(sql);
469
470 QueryResultSerializer serializer(std::move(it));
471
472 std::vector<uint8_t> res;
473 for (bool has_more = true; has_more;) {
474 has_more = serializer.Serialize(&res);
475 result_callback(res.data(), res.size(), has_more);
476 res.clear();
477 }
478 }
479
RestoreInitialTables()480 void Rpc::RestoreInitialTables() {
481 trace_processor_->RestoreInitialTables();
482 }
483
ComputeMetric(const uint8_t * args,size_t len)484 std::vector<uint8_t> Rpc::ComputeMetric(const uint8_t* args, size_t len) {
485 protozero::HeapBuffered<protos::pbzero::ComputeMetricResult> result;
486 ComputeMetricInternal(args, len, result.get());
487 return result.SerializeAsArray();
488 }
489
ComputeMetricInternal(const uint8_t * data,size_t len,protos::pbzero::ComputeMetricResult * result)490 void Rpc::ComputeMetricInternal(const uint8_t* data,
491 size_t len,
492 protos::pbzero::ComputeMetricResult* result) {
493 protos::pbzero::ComputeMetricArgs::Decoder args(data, len);
494 std::vector<std::string> metric_names;
495 for (auto it = args.metric_names(); it; ++it) {
496 metric_names.emplace_back(it->as_std_string());
497 }
498
499 PERFETTO_TP_TRACE(metatrace::Category::API_TIMELINE, "RPC_COMPUTE_METRIC",
500 [&](metatrace::Record* r) {
501 for (const auto& metric : metric_names) {
502 r->AddArg("Metric", metric);
503 r->AddArg("Format", std::to_string(args.format()));
504 }
505 });
506
507 PERFETTO_DLOG("[RPC] ComputeMetrics(%zu, %s), format=%d", metric_names.size(),
508 metric_names.empty() ? "" : metric_names.front().c_str(),
509 args.format());
510 switch (args.format()) {
511 case protos::pbzero::ComputeMetricArgs::BINARY_PROTOBUF: {
512 std::vector<uint8_t> metrics_proto;
513 base::Status status =
514 trace_processor_->ComputeMetric(metric_names, &metrics_proto);
515 if (status.ok()) {
516 result->set_metrics(metrics_proto.data(), metrics_proto.size());
517 } else {
518 result->set_error(status.message());
519 }
520 break;
521 }
522 case protos::pbzero::ComputeMetricArgs::TEXTPROTO: {
523 std::string metrics_string;
524 base::Status status = trace_processor_->ComputeMetricText(
525 metric_names, TraceProcessor::MetricResultFormat::kProtoText,
526 &metrics_string);
527 if (status.ok()) {
528 result->set_metrics_as_prototext(metrics_string);
529 } else {
530 result->set_error(status.message());
531 }
532 break;
533 }
534 case protos::pbzero::ComputeMetricArgs::JSON: {
535 std::string metrics_string;
536 base::Status status = trace_processor_->ComputeMetricText(
537 metric_names, TraceProcessor::MetricResultFormat::kJson,
538 &metrics_string);
539 if (status.ok()) {
540 result->set_metrics_as_json(metrics_string);
541 } else {
542 result->set_error(status.message());
543 }
544 break;
545 }
546 }
547 }
548
EnableMetatrace(const uint8_t * data,size_t len)549 void Rpc::EnableMetatrace(const uint8_t* data, size_t len) {
550 using protos::pbzero::MetatraceCategories;
551 TraceProcessor::MetatraceConfig config;
552 protos::pbzero::EnableMetatraceArgs::Decoder args(data, len);
553 config.categories = MetatraceCategoriesToPublicEnum(
554 static_cast<MetatraceCategories>(args.categories()));
555 trace_processor_->EnableMetatrace(config);
556 }
557
DisableAndReadMetatrace()558 std::vector<uint8_t> Rpc::DisableAndReadMetatrace() {
559 protozero::HeapBuffered<protos::pbzero::DisableAndReadMetatraceResult> result;
560 DisableAndReadMetatraceInternal(result.get());
561 return result.SerializeAsArray();
562 }
563
DisableAndReadMetatraceInternal(protos::pbzero::DisableAndReadMetatraceResult * result)564 void Rpc::DisableAndReadMetatraceInternal(
565 protos::pbzero::DisableAndReadMetatraceResult* result) {
566 std::vector<uint8_t> trace_proto;
567 base::Status status = trace_processor_->DisableAndReadMetatrace(&trace_proto);
568 if (status.ok()) {
569 result->set_metatrace(trace_proto.data(), trace_proto.size());
570 } else {
571 result->set_error(status.message());
572 }
573 }
574
GetStatus()575 std::vector<uint8_t> Rpc::GetStatus() {
576 protozero::HeapBuffered<protos::pbzero::StatusResult> status;
577 status->set_loaded_trace_name(trace_processor_->GetCurrentTraceName());
578 status->set_human_readable_version(base::GetVersionString());
579 if (const char* version_code = base::GetVersionCode(); version_code) {
580 status->set_version_code(version_code);
581 }
582 status->set_api_version(protos::pbzero::TRACE_PROCESSOR_CURRENT_API_VERSION);
583 return status.SerializeAsArray();
584 }
585
586 } // namespace perfetto::trace_processor
587