xref: /aosp_15_r20/external/perfetto/src/trace_processor/importers/gecko/gecko_trace_tokenizer.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1 /*
2  * Copyright (C) 2024 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/importers/gecko/gecko_trace_tokenizer.h"
18 
19 #include <json/value.h>
20 #include <cstddef>
21 #include <cstdint>
22 #include <optional>
23 #include <string>
24 #include <string_view>
25 #include <vector>
26 
27 #include "perfetto/base/status.h"
28 #include "perfetto/ext/base/flat_hash_map.h"
29 #include "perfetto/ext/base/string_view.h"
30 #include "perfetto/trace_processor/trace_blob_view.h"
31 #include "protos/perfetto/trace/clock_snapshot.pbzero.h"
32 #include "src/trace_processor/importers/common/mapping_tracker.h"
33 #include "src/trace_processor/importers/common/stack_profile_tracker.h"
34 #include "src/trace_processor/importers/common/virtual_memory_mapping.h"
35 #include "src/trace_processor/importers/gecko/gecko_event.h"
36 #include "src/trace_processor/importers/json/json_utils.h"
37 #include "src/trace_processor/sorter/trace_sorter.h"
38 #include "src/trace_processor/storage/trace_storage.h"
39 #include "src/trace_processor/types/trace_processor_context.h"
40 #include "src/trace_processor/util/status_macros.h"
41 
42 namespace perfetto::trace_processor::gecko_importer {
43 namespace {
44 
45 struct Callsite {
46   CallsiteId id;
47   uint32_t depth;
48 };
49 
50 }  // namespace
51 
GeckoTraceTokenizer(TraceProcessorContext * ctx)52 GeckoTraceTokenizer::GeckoTraceTokenizer(TraceProcessorContext* ctx)
53     : context_(ctx) {}
54 GeckoTraceTokenizer::~GeckoTraceTokenizer() = default;
55 
Parse(TraceBlobView blob)56 base::Status GeckoTraceTokenizer::Parse(TraceBlobView blob) {
57   pending_json_.append(reinterpret_cast<const char*>(blob.data()), blob.size());
58   return base::OkStatus();
59 }
60 
NotifyEndOfFile()61 base::Status GeckoTraceTokenizer::NotifyEndOfFile() {
62   std::optional<Json::Value> opt_value =
63       json::ParseJsonString(base::StringView(pending_json_));
64   if (!opt_value) {
65     return base::ErrStatus(
66         "Syntactic error while Gecko trace; please use an external JSON tool "
67         "(e.g. jq) to understand the source of the error.");
68   }
69   context_->clock_tracker->SetTraceTimeClock(
70       protos::pbzero::ClockSnapshot::Clock::MONOTONIC);
71 
72   DummyMemoryMapping* dummy_mapping = nullptr;
73   base::FlatHashMap<std::string, DummyMemoryMapping*> mappings;
74 
75   const Json::Value& value = *opt_value;
76   std::vector<FrameId> frame_ids;
77   std::vector<Callsite> callsites;
78   for (const auto& t : value["threads"]) {
79     // The trace uses per-thread indices, we reuse the vector for perf reasons
80     // to prevent reallocs on every thread.
81     frame_ids.clear();
82     callsites.clear();
83 
84     const auto& strings = t["stringTable"];
85     const auto& frames = t["frameTable"];
86     const auto& frames_schema = frames["schema"];
87     uint32_t location_idx = frames_schema["location"].asUInt();
88     for (const auto& frame : frames["data"]) {
89       base::StringView name = strings[frame[location_idx].asUInt()].asCString();
90 
91       constexpr std::string_view kMappingStart = " (in ";
92       size_t mapping_meta_start = name.find(
93           base::StringView(kMappingStart.data(), kMappingStart.size()));
94       if (mapping_meta_start == base::StringView::npos &&
95           name.data()[name.size() - 1] == ')') {
96         if (!dummy_mapping) {
97           dummy_mapping =
98               &context_->mapping_tracker->CreateDummyMapping("gecko");
99         }
100         frame_ids.push_back(
101             dummy_mapping->InternDummyFrame(name, base::StringView()));
102         continue;
103       }
104 
105       DummyMemoryMapping* mapping;
106       size_t mapping_start = mapping_meta_start + kMappingStart.size();
107       size_t mapping_end = name.find(')', mapping_start);
108       std::string mapping_name =
109           name.substr(mapping_start, mapping_end - mapping_start).ToStdString();
110       if (auto* mapping_ptr = mappings.Find(mapping_name); mapping_ptr) {
111         mapping = *mapping_ptr;
112       } else {
113         mapping = &context_->mapping_tracker->CreateDummyMapping(mapping_name);
114         mappings.Insert(mapping_name, mapping);
115       }
116       frame_ids.push_back(mapping->InternDummyFrame(
117           name.substr(0, mapping_meta_start), base::StringView()));
118     }
119 
120     const auto& stacks = t["stackTable"];
121     const auto& stacks_schema = stacks["schema"];
122     uint32_t prefix_index = stacks_schema["prefix"].asUInt();
123     uint32_t frame_index = stacks_schema["frame"].asUInt();
124     for (const auto& frame : stacks["data"]) {
125       const auto& prefix = frame[prefix_index];
126       std::optional<CallsiteId> prefix_id;
127       uint32_t depth = 0;
128       if (!prefix.isNull()) {
129         const auto& c = callsites[prefix.asUInt()];
130         prefix_id = c.id;
131         depth = c.depth + 1;
132       }
133       CallsiteId cid = context_->stack_profile_tracker->InternCallsite(
134           prefix_id, frame_ids[frame[frame_index].asUInt()], depth);
135       callsites.push_back({cid, depth});
136     }
137 
138     const auto& samples = t["samples"];
139     const auto& samples_schema = samples["schema"];
140     uint32_t stack_index = samples_schema["stack"].asUInt();
141     uint32_t time_index = samples_schema["time"].asUInt();
142     bool added_metadata = false;
143     for (const auto& sample : samples["data"]) {
144       uint32_t stack_idx = sample[stack_index].asUInt();
145       auto ts =
146           static_cast<int64_t>(sample[time_index].asDouble() * 1000 * 1000);
147       if (!added_metadata) {
148         context_->sorter->PushGeckoEvent(
149             ts, GeckoEvent{GeckoEvent::ThreadMetadata{
150                     t["tid"].asUInt(), t["pid"].asUInt(),
151                     context_->storage->InternString(t["name"].asCString())}});
152         added_metadata = true;
153       }
154       ASSIGN_OR_RETURN(
155           int64_t converted,
156           context_->clock_tracker->ToTraceTime(
157               protos::pbzero::ClockSnapshot::Clock::MONOTONIC, ts));
158       context_->sorter->PushGeckoEvent(
159           converted, GeckoEvent{GeckoEvent::StackSample{
160                          t["tid"].asUInt(), callsites[stack_idx].id}});
161     }
162   }
163   return base::OkStatus();
164 }
165 
166 }  // namespace perfetto::trace_processor::gecko_importer
167