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/importers/proto/stack_profile_sequence_state.h"
18
19 #include <cstdint>
20 #include <optional>
21 #include <utility>
22 #include <vector>
23
24 #include "perfetto/base/logging.h"
25 #include "perfetto/ext/base/string_utils.h"
26 #include "perfetto/ext/base/string_view.h"
27 #include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
28 #include "protos/perfetto/trace/profiling/profile_common.pbzero.h"
29 #include "src/trace_processor/importers/common/address_range.h"
30 #include "src/trace_processor/importers/common/mapping_tracker.h"
31 #include "src/trace_processor/importers/common/process_tracker.h"
32 #include "src/trace_processor/importers/common/stack_profile_tracker.h"
33 #include "src/trace_processor/importers/proto/packet_sequence_state_generation.h"
34 #include "src/trace_processor/importers/proto/profile_packet_utils.h"
35 #include "src/trace_processor/storage/stats.h"
36 #include "src/trace_processor/storage/trace_storage.h"
37 #include "src/trace_processor/types/trace_processor_context.h"
38 #include "src/trace_processor/util/build_id.h"
39
40 namespace perfetto {
41 namespace trace_processor {
42 namespace {
ToStringView(protozero::ConstBytes bytes)43 base::StringView ToStringView(protozero::ConstBytes bytes) {
44 return base::StringView(reinterpret_cast<const char*>(bytes.data),
45 bytes.size);
46 }
47
48 // Determine wether this is the magical kernel mapping created in
49 // `perfetto::::profiling::Unwinder::SymbolizeKernelCallchain`
IsMagicalKernelMapping(const CreateMappingParams & params)50 bool IsMagicalKernelMapping(const CreateMappingParams& params) {
51 return params.memory_range.start() == 0 &&
52 params.memory_range.length() == 0 && params.exact_offset == 0 &&
53 !params.build_id.has_value() && (params.name == "/kernel");
54 }
55
56 } // namespace
57
StackProfileSequenceState(TraceProcessorContext * context)58 StackProfileSequenceState::StackProfileSequenceState(
59 TraceProcessorContext* context)
60 : context_(context) {}
61
62 StackProfileSequenceState::~StackProfileSequenceState() = default;
63
FindOrInsertMapping(uint64_t iid)64 VirtualMemoryMapping* StackProfileSequenceState::FindOrInsertMapping(
65 uint64_t iid) {
66 if (pid_and_tid_valid()) {
67 return FindOrInsertMappingImpl(
68 context_->process_tracker->GetOrCreateProcess(
69 static_cast<uint32_t>(pid())),
70 iid);
71 }
72
73 return FindOrInsertMappingImpl(std::nullopt, iid);
74 }
75
FindOrInsertMappingImpl(std::optional<UniquePid> upid,uint64_t iid)76 VirtualMemoryMapping* StackProfileSequenceState::FindOrInsertMappingImpl(
77 std::optional<UniquePid> upid,
78 uint64_t iid) {
79 if (auto ptr = cached_mappings_.Find({upid, iid}); ptr) {
80 return *ptr;
81 }
82 auto* decoder =
83 LookupInternedMessage<protos::pbzero::InternedData::kMappingsFieldNumber,
84 protos::pbzero::Mapping>(iid);
85 if (!decoder) {
86 context_->storage->IncrementStats(stats::stackprofile_invalid_mapping_id);
87 return nullptr;
88 }
89
90 std::vector<base::StringView> path_components;
91 for (auto it = decoder->path_string_ids(); it; ++it) {
92 std::optional<base::StringView> str = LookupInternedMappingPath(*it);
93 if (!str) {
94 // For backward compatibility reasons we do not return an error but
95 // instead stop adding path components.
96 break;
97 }
98 path_components.push_back(*str);
99 }
100
101 CreateMappingParams params;
102 std::optional<base::StringView> build_id =
103 LookupInternedBuildId(decoder->build_id());
104 if (!build_id) {
105 return nullptr;
106 }
107 if (!build_id->empty()) {
108 params.build_id = BuildId::FromRaw(*build_id);
109 }
110
111 params.memory_range = AddressRange(decoder->start(), decoder->end());
112 params.exact_offset = decoder->exact_offset();
113 params.start_offset = decoder->start_offset();
114 params.load_bias = decoder->load_bias();
115 params.name = ProfilePacketUtils::MakeMappingName(path_components);
116
117 VirtualMemoryMapping* mapping;
118
119 if (IsMagicalKernelMapping(params)) {
120 mapping = &context_->mapping_tracker->CreateKernelMemoryMapping(
121 std::move(params));
122 // A lot of tests to not set a proper mapping range
123 // Dummy mappings can also be emitted (e.g. for errors during unwinding)
124 } else if (params.memory_range.empty()) {
125 mapping =
126 &context_->mapping_tracker->InternMemoryMapping(std::move(params));
127 } else if (upid.has_value()) {
128 mapping = &context_->mapping_tracker->CreateUserMemoryMapping(
129 *upid, std::move(params));
130 } else {
131 mapping =
132 &context_->mapping_tracker->InternMemoryMapping(std::move(params));
133 }
134
135 cached_mappings_.Insert({upid, iid}, mapping);
136 return mapping;
137 }
138
139 std::optional<base::StringView>
LookupInternedBuildId(uint64_t iid)140 StackProfileSequenceState::LookupInternedBuildId(uint64_t iid) {
141 // This should really be an error (value not set) or at the very least return
142 // a null string, but for backward compatibility use an empty string instead.
143 if (iid == 0) {
144 return "";
145 }
146 auto* decoder =
147 LookupInternedMessage<protos::pbzero::InternedData::kBuildIdsFieldNumber,
148 protos::pbzero::InternedString>(iid);
149 if (!decoder) {
150 context_->storage->IncrementStats(stats::stackprofile_invalid_string_id);
151 return std::nullopt;
152 }
153
154 return ToStringView(decoder->str());
155 }
156
157 std::optional<base::StringView>
LookupInternedMappingPath(uint64_t iid)158 StackProfileSequenceState::LookupInternedMappingPath(uint64_t iid) {
159 auto* decoder = LookupInternedMessage<
160 protos::pbzero::InternedData::kMappingPathsFieldNumber,
161 protos::pbzero::InternedString>(iid);
162 if (!decoder) {
163 context_->storage->IncrementStats(stats::stackprofile_invalid_string_id);
164 return std::nullopt;
165 }
166
167 return ToStringView(decoder->str());
168 }
169
FindOrInsertCallstack(UniquePid upid,uint64_t iid)170 std::optional<CallsiteId> StackProfileSequenceState::FindOrInsertCallstack(
171 UniquePid upid,
172 uint64_t iid) {
173 if (CallsiteId* id = cached_callstacks_.Find({upid, iid}); id) {
174 return *id;
175 }
176 auto* decoder = LookupInternedMessage<
177 protos::pbzero::InternedData::kCallstacksFieldNumber,
178 protos::pbzero::Callstack>(iid);
179 if (!decoder) {
180 context_->storage->IncrementStats(stats::stackprofile_invalid_callstack_id);
181 return std::nullopt;
182 }
183
184 std::optional<CallsiteId> parent_callsite_id;
185 uint32_t depth = 0;
186 for (auto it = decoder->frame_ids(); it; ++it) {
187 std::optional<FrameId> frame_id = FindOrInsertFrame(upid, *it);
188 if (!frame_id) {
189 return std::nullopt;
190 }
191 parent_callsite_id = context_->stack_profile_tracker->InternCallsite(
192 parent_callsite_id, *frame_id, depth);
193 ++depth;
194 }
195
196 if (!parent_callsite_id) {
197 context_->storage->IncrementStats(stats::stackprofile_empty_callstack);
198 return std::nullopt;
199 }
200
201 cached_callstacks_.Insert({upid, iid}, *parent_callsite_id);
202
203 return parent_callsite_id;
204 }
205
FindOrInsertFrame(UniquePid upid,uint64_t iid)206 std::optional<FrameId> StackProfileSequenceState::FindOrInsertFrame(
207 UniquePid upid,
208 uint64_t iid) {
209 if (FrameId* id = cached_frames_.Find({upid, iid}); id) {
210 return *id;
211 }
212 auto* decoder =
213 LookupInternedMessage<protos::pbzero::InternedData::kFramesFieldNumber,
214 protos::pbzero::Frame>(iid);
215 if (!decoder) {
216 context_->storage->IncrementStats(stats::stackprofile_invalid_frame_id);
217 return std::nullopt;
218 }
219
220 VirtualMemoryMapping* mapping =
221 FindOrInsertMappingImpl(upid, decoder->mapping_id());
222 if (!mapping) {
223 return std::nullopt;
224 }
225
226 base::StringView function_name;
227 if (decoder->function_name_id() != 0) {
228 std::optional<base::StringView> func =
229 LookupInternedFunctionName(decoder->function_name_id());
230 if (!func) {
231 return std::nullopt;
232 }
233 function_name = *func;
234 }
235
236 FrameId frame_id = mapping->InternFrame(decoder->rel_pc(), function_name);
237 if (!mapping->is_jitted()) {
238 cached_frames_.Insert({upid, iid}, frame_id);
239 }
240
241 return frame_id;
242 }
243
244 std::optional<base::StringView>
LookupInternedFunctionName(uint64_t iid)245 StackProfileSequenceState::LookupInternedFunctionName(uint64_t iid) {
246 // This should really be an error (value not set) or at the very least return
247 // a null string, but for backward compatibility use an empty string instead.
248 if (iid == 0) {
249 return "";
250 }
251 auto* decoder = LookupInternedMessage<
252 protos::pbzero::InternedData::kFunctionNamesFieldNumber,
253 protos::pbzero::InternedString>(iid);
254 if (!decoder) {
255 context_->storage->IncrementStats(stats::stackprofile_invalid_string_id);
256 return std::nullopt;
257 }
258
259 return ToStringView(decoder->str());
260 }
261
262 } // namespace trace_processor
263 } // namespace perfetto
264