1*6dbdd20aSAndroid Build Coastguard Worker /*
2*6dbdd20aSAndroid Build Coastguard Worker * Copyright (C) 2019 The Android Open Source Project
3*6dbdd20aSAndroid Build Coastguard Worker *
4*6dbdd20aSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*6dbdd20aSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*6dbdd20aSAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*6dbdd20aSAndroid Build Coastguard Worker *
8*6dbdd20aSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*6dbdd20aSAndroid Build Coastguard Worker *
10*6dbdd20aSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*6dbdd20aSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*6dbdd20aSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*6dbdd20aSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*6dbdd20aSAndroid Build Coastguard Worker * limitations under the License.
15*6dbdd20aSAndroid Build Coastguard Worker */
16*6dbdd20aSAndroid Build Coastguard Worker
17*6dbdd20aSAndroid Build Coastguard Worker #include "perfetto/profiling/pprof_builder.h"
18*6dbdd20aSAndroid Build Coastguard Worker
19*6dbdd20aSAndroid Build Coastguard Worker #include "perfetto/base/build_config.h"
20*6dbdd20aSAndroid Build Coastguard Worker
21*6dbdd20aSAndroid Build Coastguard Worker #if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
22*6dbdd20aSAndroid Build Coastguard Worker #include <cxxabi.h>
23*6dbdd20aSAndroid Build Coastguard Worker #endif
24*6dbdd20aSAndroid Build Coastguard Worker
25*6dbdd20aSAndroid Build Coastguard Worker #include <algorithm>
26*6dbdd20aSAndroid Build Coastguard Worker #include <cinttypes>
27*6dbdd20aSAndroid Build Coastguard Worker #include <map>
28*6dbdd20aSAndroid Build Coastguard Worker #include <set>
29*6dbdd20aSAndroid Build Coastguard Worker #include <unordered_map>
30*6dbdd20aSAndroid Build Coastguard Worker #include <vector>
31*6dbdd20aSAndroid Build Coastguard Worker
32*6dbdd20aSAndroid Build Coastguard Worker #include "perfetto/base/logging.h"
33*6dbdd20aSAndroid Build Coastguard Worker #include "perfetto/ext/base/hash.h"
34*6dbdd20aSAndroid Build Coastguard Worker #include "perfetto/ext/base/string_utils.h"
35*6dbdd20aSAndroid Build Coastguard Worker #include "perfetto/ext/base/utils.h"
36*6dbdd20aSAndroid Build Coastguard Worker #include "perfetto/protozero/packed_repeated_fields.h"
37*6dbdd20aSAndroid Build Coastguard Worker #include "perfetto/protozero/scattered_heap_buffer.h"
38*6dbdd20aSAndroid Build Coastguard Worker #include "perfetto/trace_processor/trace_processor.h"
39*6dbdd20aSAndroid Build Coastguard Worker #include "src/trace_processor/containers/string_pool.h"
40*6dbdd20aSAndroid Build Coastguard Worker #include "src/traceconv/utils.h"
41*6dbdd20aSAndroid Build Coastguard Worker
42*6dbdd20aSAndroid Build Coastguard Worker #include "protos/third_party/pprof/profile.pbzero.h"
43*6dbdd20aSAndroid Build Coastguard Worker
44*6dbdd20aSAndroid Build Coastguard Worker // Quick hint on navigating the file:
45*6dbdd20aSAndroid Build Coastguard Worker // Conversions for both perf and heap profiles start with |TraceToPprof|.
46*6dbdd20aSAndroid Build Coastguard Worker // Non-shared logic is in the |heap_profile| and |perf_profile| namespaces.
47*6dbdd20aSAndroid Build Coastguard Worker //
48*6dbdd20aSAndroid Build Coastguard Worker // To build one or more profiles, first the callstack information is queried
49*6dbdd20aSAndroid Build Coastguard Worker // from the SQL tables, and converted into an in-memory representation by
50*6dbdd20aSAndroid Build Coastguard Worker // |PreprocessLocations|. Then an instance of |GProfileBuilder| is used to
51*6dbdd20aSAndroid Build Coastguard Worker // accumulate samples for that profile, and emit all additional information as a
52*6dbdd20aSAndroid Build Coastguard Worker // serialized proto. Only the entities referenced by that particular
53*6dbdd20aSAndroid Build Coastguard Worker // |GProfileBuilder| instance are emitted.
54*6dbdd20aSAndroid Build Coastguard Worker //
55*6dbdd20aSAndroid Build Coastguard Worker // See protos/third_party/pprof/profile.proto for the meaning of terms like
56*6dbdd20aSAndroid Build Coastguard Worker // function/location/line.
57*6dbdd20aSAndroid Build Coastguard Worker
58*6dbdd20aSAndroid Build Coastguard Worker namespace {
59*6dbdd20aSAndroid Build Coastguard Worker using StringId = ::perfetto::trace_processor::StringPool::Id;
60*6dbdd20aSAndroid Build Coastguard Worker
61*6dbdd20aSAndroid Build Coastguard Worker // In-memory representation of a Profile.Function.
62*6dbdd20aSAndroid Build Coastguard Worker struct Function {
63*6dbdd20aSAndroid Build Coastguard Worker StringId name_id = StringId::Null();
64*6dbdd20aSAndroid Build Coastguard Worker StringId system_name_id = StringId::Null();
65*6dbdd20aSAndroid Build Coastguard Worker StringId filename_id = StringId::Null();
66*6dbdd20aSAndroid Build Coastguard Worker
Function__anon3e98f22b0111::Function67*6dbdd20aSAndroid Build Coastguard Worker Function(StringId n, StringId s, StringId f)
68*6dbdd20aSAndroid Build Coastguard Worker : name_id(n), system_name_id(s), filename_id(f) {}
69*6dbdd20aSAndroid Build Coastguard Worker
operator ==__anon3e98f22b0111::Function70*6dbdd20aSAndroid Build Coastguard Worker bool operator==(const Function& other) const {
71*6dbdd20aSAndroid Build Coastguard Worker return std::tie(name_id, system_name_id, filename_id) ==
72*6dbdd20aSAndroid Build Coastguard Worker std::tie(other.name_id, other.system_name_id, other.filename_id);
73*6dbdd20aSAndroid Build Coastguard Worker }
74*6dbdd20aSAndroid Build Coastguard Worker };
75*6dbdd20aSAndroid Build Coastguard Worker
76*6dbdd20aSAndroid Build Coastguard Worker // In-memory representation of a Profile.Line.
77*6dbdd20aSAndroid Build Coastguard Worker struct Line {
78*6dbdd20aSAndroid Build Coastguard Worker int64_t function_id = 0; // LocationTracker's interned Function id
79*6dbdd20aSAndroid Build Coastguard Worker int64_t line_no = 0;
80*6dbdd20aSAndroid Build Coastguard Worker
Line__anon3e98f22b0111::Line81*6dbdd20aSAndroid Build Coastguard Worker Line(int64_t func, int64_t line) : function_id(func), line_no(line) {}
82*6dbdd20aSAndroid Build Coastguard Worker
operator ==__anon3e98f22b0111::Line83*6dbdd20aSAndroid Build Coastguard Worker bool operator==(const Line& other) const {
84*6dbdd20aSAndroid Build Coastguard Worker return function_id == other.function_id && line_no == other.line_no;
85*6dbdd20aSAndroid Build Coastguard Worker }
86*6dbdd20aSAndroid Build Coastguard Worker };
87*6dbdd20aSAndroid Build Coastguard Worker
88*6dbdd20aSAndroid Build Coastguard Worker // In-memory representation of a Profile.Location.
89*6dbdd20aSAndroid Build Coastguard Worker struct Location {
90*6dbdd20aSAndroid Build Coastguard Worker int64_t mapping_id = 0; // sqlite row id
91*6dbdd20aSAndroid Build Coastguard Worker // Common case: location references a single function.
92*6dbdd20aSAndroid Build Coastguard Worker int64_t single_function_id = 0; // interned Function id
93*6dbdd20aSAndroid Build Coastguard Worker // Alternatively: multiple inlined functions, recovered via offline
94*6dbdd20aSAndroid Build Coastguard Worker // symbolisation. Leaf-first ordering.
95*6dbdd20aSAndroid Build Coastguard Worker std::vector<Line> inlined_functions;
96*6dbdd20aSAndroid Build Coastguard Worker
Location__anon3e98f22b0111::Location97*6dbdd20aSAndroid Build Coastguard Worker Location(int64_t map, int64_t func, std::vector<Line> inlines)
98*6dbdd20aSAndroid Build Coastguard Worker : mapping_id(map),
99*6dbdd20aSAndroid Build Coastguard Worker single_function_id(func),
100*6dbdd20aSAndroid Build Coastguard Worker inlined_functions(std::move(inlines)) {}
101*6dbdd20aSAndroid Build Coastguard Worker
operator ==__anon3e98f22b0111::Location102*6dbdd20aSAndroid Build Coastguard Worker bool operator==(const Location& other) const {
103*6dbdd20aSAndroid Build Coastguard Worker return std::tie(mapping_id, single_function_id, inlined_functions) ==
104*6dbdd20aSAndroid Build Coastguard Worker std::tie(other.mapping_id, other.single_function_id,
105*6dbdd20aSAndroid Build Coastguard Worker other.inlined_functions);
106*6dbdd20aSAndroid Build Coastguard Worker }
107*6dbdd20aSAndroid Build Coastguard Worker };
108*6dbdd20aSAndroid Build Coastguard Worker } // namespace
109*6dbdd20aSAndroid Build Coastguard Worker
110*6dbdd20aSAndroid Build Coastguard Worker template <>
111*6dbdd20aSAndroid Build Coastguard Worker struct std::hash<Function> {
operator ()std::hash112*6dbdd20aSAndroid Build Coastguard Worker size_t operator()(const Function& loc) const {
113*6dbdd20aSAndroid Build Coastguard Worker perfetto::base::Hasher hasher;
114*6dbdd20aSAndroid Build Coastguard Worker hasher.Update(loc.name_id.raw_id());
115*6dbdd20aSAndroid Build Coastguard Worker hasher.Update(loc.system_name_id.raw_id());
116*6dbdd20aSAndroid Build Coastguard Worker hasher.Update(loc.filename_id.raw_id());
117*6dbdd20aSAndroid Build Coastguard Worker return static_cast<size_t>(hasher.digest());
118*6dbdd20aSAndroid Build Coastguard Worker }
119*6dbdd20aSAndroid Build Coastguard Worker };
120*6dbdd20aSAndroid Build Coastguard Worker
121*6dbdd20aSAndroid Build Coastguard Worker template <>
122*6dbdd20aSAndroid Build Coastguard Worker struct std::hash<Location> {
operator ()std::hash123*6dbdd20aSAndroid Build Coastguard Worker size_t operator()(const Location& loc) const {
124*6dbdd20aSAndroid Build Coastguard Worker perfetto::base::Hasher hasher;
125*6dbdd20aSAndroid Build Coastguard Worker hasher.Update(loc.mapping_id);
126*6dbdd20aSAndroid Build Coastguard Worker hasher.Update(loc.single_function_id);
127*6dbdd20aSAndroid Build Coastguard Worker for (auto line : loc.inlined_functions) {
128*6dbdd20aSAndroid Build Coastguard Worker hasher.Update(line.function_id);
129*6dbdd20aSAndroid Build Coastguard Worker hasher.Update(line.line_no);
130*6dbdd20aSAndroid Build Coastguard Worker }
131*6dbdd20aSAndroid Build Coastguard Worker return static_cast<size_t>(hasher.digest());
132*6dbdd20aSAndroid Build Coastguard Worker }
133*6dbdd20aSAndroid Build Coastguard Worker };
134*6dbdd20aSAndroid Build Coastguard Worker
135*6dbdd20aSAndroid Build Coastguard Worker namespace perfetto {
136*6dbdd20aSAndroid Build Coastguard Worker namespace trace_to_text {
137*6dbdd20aSAndroid Build Coastguard Worker namespace {
138*6dbdd20aSAndroid Build Coastguard Worker
139*6dbdd20aSAndroid Build Coastguard Worker using ::perfetto::trace_processor::Iterator;
140*6dbdd20aSAndroid Build Coastguard Worker
ToPprofId(int64_t id)141*6dbdd20aSAndroid Build Coastguard Worker uint64_t ToPprofId(int64_t id) {
142*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_DCHECK(id >= 0);
143*6dbdd20aSAndroid Build Coastguard Worker return static_cast<uint64_t>(id) + 1;
144*6dbdd20aSAndroid Build Coastguard Worker }
145*6dbdd20aSAndroid Build Coastguard Worker
AsCsvString(std::vector<uint64_t> vals)146*6dbdd20aSAndroid Build Coastguard Worker std::string AsCsvString(std::vector<uint64_t> vals) {
147*6dbdd20aSAndroid Build Coastguard Worker std::string ret;
148*6dbdd20aSAndroid Build Coastguard Worker for (size_t i = 0; i < vals.size(); i++) {
149*6dbdd20aSAndroid Build Coastguard Worker if (i != 0) {
150*6dbdd20aSAndroid Build Coastguard Worker ret += ",";
151*6dbdd20aSAndroid Build Coastguard Worker }
152*6dbdd20aSAndroid Build Coastguard Worker ret += std::to_string(vals[i]);
153*6dbdd20aSAndroid Build Coastguard Worker }
154*6dbdd20aSAndroid Build Coastguard Worker return ret;
155*6dbdd20aSAndroid Build Coastguard Worker }
156*6dbdd20aSAndroid Build Coastguard Worker
GetStatsEntry(trace_processor::TraceProcessor * tp,const std::string & name,std::optional<uint64_t> idx=std::nullopt)157*6dbdd20aSAndroid Build Coastguard Worker std::optional<int64_t> GetStatsEntry(
158*6dbdd20aSAndroid Build Coastguard Worker trace_processor::TraceProcessor* tp,
159*6dbdd20aSAndroid Build Coastguard Worker const std::string& name,
160*6dbdd20aSAndroid Build Coastguard Worker std::optional<uint64_t> idx = std::nullopt) {
161*6dbdd20aSAndroid Build Coastguard Worker std::string query = "select value from stats where name == '" + name + "'";
162*6dbdd20aSAndroid Build Coastguard Worker if (idx.has_value())
163*6dbdd20aSAndroid Build Coastguard Worker query += " and idx == " + std::to_string(idx.value());
164*6dbdd20aSAndroid Build Coastguard Worker
165*6dbdd20aSAndroid Build Coastguard Worker auto it = tp->ExecuteQuery(query);
166*6dbdd20aSAndroid Build Coastguard Worker if (!it.Next()) {
167*6dbdd20aSAndroid Build Coastguard Worker if (!it.Status().ok()) {
168*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_DFATAL_OR_ELOG("Invalid iterator: %s",
169*6dbdd20aSAndroid Build Coastguard Worker it.Status().message().c_str());
170*6dbdd20aSAndroid Build Coastguard Worker return std::nullopt;
171*6dbdd20aSAndroid Build Coastguard Worker }
172*6dbdd20aSAndroid Build Coastguard Worker // some stats are not present unless non-zero
173*6dbdd20aSAndroid Build Coastguard Worker return std::make_optional(0);
174*6dbdd20aSAndroid Build Coastguard Worker }
175*6dbdd20aSAndroid Build Coastguard Worker return std::make_optional(it.Get(0).AsLong());
176*6dbdd20aSAndroid Build Coastguard Worker }
177*6dbdd20aSAndroid Build Coastguard Worker
178*6dbdd20aSAndroid Build Coastguard Worker // Interns Locations, Lines, and Functions. Interning is done by the entity's
179*6dbdd20aSAndroid Build Coastguard Worker // contents, and has no relation to the row ids in the SQL tables.
180*6dbdd20aSAndroid Build Coastguard Worker // Contains all data for the trace, so can be reused when emitting multiple
181*6dbdd20aSAndroid Build Coastguard Worker // profiles.
182*6dbdd20aSAndroid Build Coastguard Worker //
183*6dbdd20aSAndroid Build Coastguard Worker // TODO(rsavitski): consider moving mappings into here as well. For now, they're
184*6dbdd20aSAndroid Build Coastguard Worker // still emitted in a single scan during profile building. Mappings should be
185*6dbdd20aSAndroid Build Coastguard Worker // unique-enough already in the SQL tables, with only incremental state clearing
186*6dbdd20aSAndroid Build Coastguard Worker // duplicating entries.
187*6dbdd20aSAndroid Build Coastguard Worker class LocationTracker {
188*6dbdd20aSAndroid Build Coastguard Worker public:
InternLocation(Location loc)189*6dbdd20aSAndroid Build Coastguard Worker int64_t InternLocation(Location loc) {
190*6dbdd20aSAndroid Build Coastguard Worker auto it = locations_.find(loc);
191*6dbdd20aSAndroid Build Coastguard Worker if (it == locations_.end()) {
192*6dbdd20aSAndroid Build Coastguard Worker bool inserted = false;
193*6dbdd20aSAndroid Build Coastguard Worker std::tie(it, inserted) = locations_.emplace(
194*6dbdd20aSAndroid Build Coastguard Worker std::move(loc), static_cast<int64_t>(locations_.size()));
195*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_DCHECK(inserted);
196*6dbdd20aSAndroid Build Coastguard Worker }
197*6dbdd20aSAndroid Build Coastguard Worker return it->second;
198*6dbdd20aSAndroid Build Coastguard Worker }
199*6dbdd20aSAndroid Build Coastguard Worker
InternFunction(Function func)200*6dbdd20aSAndroid Build Coastguard Worker int64_t InternFunction(Function func) {
201*6dbdd20aSAndroid Build Coastguard Worker auto it = functions_.find(func);
202*6dbdd20aSAndroid Build Coastguard Worker if (it == functions_.end()) {
203*6dbdd20aSAndroid Build Coastguard Worker bool inserted = false;
204*6dbdd20aSAndroid Build Coastguard Worker std::tie(it, inserted) =
205*6dbdd20aSAndroid Build Coastguard Worker functions_.emplace(func, static_cast<int64_t>(functions_.size()));
206*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_DCHECK(inserted);
207*6dbdd20aSAndroid Build Coastguard Worker }
208*6dbdd20aSAndroid Build Coastguard Worker return it->second;
209*6dbdd20aSAndroid Build Coastguard Worker }
210*6dbdd20aSAndroid Build Coastguard Worker
IsCallsiteProcessed(int64_t callstack_id) const211*6dbdd20aSAndroid Build Coastguard Worker bool IsCallsiteProcessed(int64_t callstack_id) const {
212*6dbdd20aSAndroid Build Coastguard Worker return callsite_to_locations_.find(callstack_id) !=
213*6dbdd20aSAndroid Build Coastguard Worker callsite_to_locations_.end();
214*6dbdd20aSAndroid Build Coastguard Worker }
215*6dbdd20aSAndroid Build Coastguard Worker
MaybeSetCallsiteLocations(int64_t callstack_id,const std::vector<int64_t> & locs)216*6dbdd20aSAndroid Build Coastguard Worker void MaybeSetCallsiteLocations(int64_t callstack_id,
217*6dbdd20aSAndroid Build Coastguard Worker const std::vector<int64_t>& locs) {
218*6dbdd20aSAndroid Build Coastguard Worker // nop if already set
219*6dbdd20aSAndroid Build Coastguard Worker callsite_to_locations_.emplace(callstack_id, locs);
220*6dbdd20aSAndroid Build Coastguard Worker }
221*6dbdd20aSAndroid Build Coastguard Worker
LocationsForCallstack(int64_t callstack_id) const222*6dbdd20aSAndroid Build Coastguard Worker const std::vector<int64_t>& LocationsForCallstack(
223*6dbdd20aSAndroid Build Coastguard Worker int64_t callstack_id) const {
224*6dbdd20aSAndroid Build Coastguard Worker auto it = callsite_to_locations_.find(callstack_id);
225*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_CHECK(callstack_id >= 0 && it != callsite_to_locations_.end());
226*6dbdd20aSAndroid Build Coastguard Worker return it->second;
227*6dbdd20aSAndroid Build Coastguard Worker }
228*6dbdd20aSAndroid Build Coastguard Worker
AllLocations() const229*6dbdd20aSAndroid Build Coastguard Worker const std::unordered_map<Location, int64_t>& AllLocations() const {
230*6dbdd20aSAndroid Build Coastguard Worker return locations_;
231*6dbdd20aSAndroid Build Coastguard Worker }
AllFunctions() const232*6dbdd20aSAndroid Build Coastguard Worker const std::unordered_map<Function, int64_t>& AllFunctions() const {
233*6dbdd20aSAndroid Build Coastguard Worker return functions_;
234*6dbdd20aSAndroid Build Coastguard Worker }
235*6dbdd20aSAndroid Build Coastguard Worker
236*6dbdd20aSAndroid Build Coastguard Worker private:
237*6dbdd20aSAndroid Build Coastguard Worker // Root-first location ids for a given callsite id.
238*6dbdd20aSAndroid Build Coastguard Worker std::unordered_map<int64_t, std::vector<int64_t>> callsite_to_locations_;
239*6dbdd20aSAndroid Build Coastguard Worker std::unordered_map<Location, int64_t> locations_;
240*6dbdd20aSAndroid Build Coastguard Worker std::unordered_map<Function, int64_t> functions_;
241*6dbdd20aSAndroid Build Coastguard Worker };
242*6dbdd20aSAndroid Build Coastguard Worker
243*6dbdd20aSAndroid Build Coastguard Worker struct PreprocessedInline {
244*6dbdd20aSAndroid Build Coastguard Worker // |name_id| is already demangled
245*6dbdd20aSAndroid Build Coastguard Worker StringId name_id = StringId::Null();
246*6dbdd20aSAndroid Build Coastguard Worker StringId filename_id = StringId::Null();
247*6dbdd20aSAndroid Build Coastguard Worker int64_t line_no = 0;
248*6dbdd20aSAndroid Build Coastguard Worker
PreprocessedInlineperfetto::trace_to_text::__anon3e98f22b0211::PreprocessedInline249*6dbdd20aSAndroid Build Coastguard Worker PreprocessedInline(StringId s, StringId f, int64_t line)
250*6dbdd20aSAndroid Build Coastguard Worker : name_id(s), filename_id(f), line_no(line) {}
251*6dbdd20aSAndroid Build Coastguard Worker };
252*6dbdd20aSAndroid Build Coastguard Worker
253*6dbdd20aSAndroid Build Coastguard Worker std::unordered_map<int64_t, std::vector<PreprocessedInline>>
PreprocessInliningInfo(trace_processor::TraceProcessor * tp,trace_processor::StringPool * interner)254*6dbdd20aSAndroid Build Coastguard Worker PreprocessInliningInfo(trace_processor::TraceProcessor* tp,
255*6dbdd20aSAndroid Build Coastguard Worker trace_processor::StringPool* interner) {
256*6dbdd20aSAndroid Build Coastguard Worker std::unordered_map<int64_t, std::vector<PreprocessedInline>> inlines;
257*6dbdd20aSAndroid Build Coastguard Worker
258*6dbdd20aSAndroid Build Coastguard Worker // Most-inlined function (leaf) has the lowest id within a symbol set. Query
259*6dbdd20aSAndroid Build Coastguard Worker // such that the per-set line vectors are built up leaf-first.
260*6dbdd20aSAndroid Build Coastguard Worker Iterator it = tp->ExecuteQuery(
261*6dbdd20aSAndroid Build Coastguard Worker "select symbol_set_id, name, source_file, line_number from "
262*6dbdd20aSAndroid Build Coastguard Worker "stack_profile_symbol order by symbol_set_id asc, id asc;");
263*6dbdd20aSAndroid Build Coastguard Worker while (it.Next()) {
264*6dbdd20aSAndroid Build Coastguard Worker int64_t symbol_set_id = it.Get(0).AsLong();
265*6dbdd20aSAndroid Build Coastguard Worker auto func_sysname = it.Get(1).is_null() ? "" : it.Get(1).AsString();
266*6dbdd20aSAndroid Build Coastguard Worker auto filename = it.Get(2).is_null() ? "" : it.Get(2).AsString();
267*6dbdd20aSAndroid Build Coastguard Worker int64_t line_no = it.Get(3).is_null() ? 0 : it.Get(3).AsLong();
268*6dbdd20aSAndroid Build Coastguard Worker
269*6dbdd20aSAndroid Build Coastguard Worker inlines[symbol_set_id].emplace_back(interner->InternString(func_sysname),
270*6dbdd20aSAndroid Build Coastguard Worker interner->InternString(filename),
271*6dbdd20aSAndroid Build Coastguard Worker line_no);
272*6dbdd20aSAndroid Build Coastguard Worker }
273*6dbdd20aSAndroid Build Coastguard Worker
274*6dbdd20aSAndroid Build Coastguard Worker if (!it.Status().ok()) {
275*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_DFATAL_OR_ELOG("Invalid iterator: %s",
276*6dbdd20aSAndroid Build Coastguard Worker it.Status().message().c_str());
277*6dbdd20aSAndroid Build Coastguard Worker return {};
278*6dbdd20aSAndroid Build Coastguard Worker }
279*6dbdd20aSAndroid Build Coastguard Worker return inlines;
280*6dbdd20aSAndroid Build Coastguard Worker }
281*6dbdd20aSAndroid Build Coastguard Worker
282*6dbdd20aSAndroid Build Coastguard Worker // Extracts and interns the unique frames and locations (as defined by the proto
283*6dbdd20aSAndroid Build Coastguard Worker // format) from the callstack SQL tables.
284*6dbdd20aSAndroid Build Coastguard Worker //
285*6dbdd20aSAndroid Build Coastguard Worker // Approach:
286*6dbdd20aSAndroid Build Coastguard Worker // * for each callstack (callsite ids of the leaves):
287*6dbdd20aSAndroid Build Coastguard Worker // * use experimental_annotated_callstack to build the full list of
288*6dbdd20aSAndroid Build Coastguard Worker // constituent frames
289*6dbdd20aSAndroid Build Coastguard Worker // * for each frame (root to leaf):
290*6dbdd20aSAndroid Build Coastguard Worker // * intern the location and function(s)
291*6dbdd20aSAndroid Build Coastguard Worker // * remember the mapping from callsite_id to the callstack so far (from
292*6dbdd20aSAndroid Build Coastguard Worker // the root and including the frame being considered)
293*6dbdd20aSAndroid Build Coastguard Worker //
294*6dbdd20aSAndroid Build Coastguard Worker // Optionally mixes in the annotations as a frame name suffix (since there's no
295*6dbdd20aSAndroid Build Coastguard Worker // good way to attach extra info to locations in the proto format). This relies
296*6dbdd20aSAndroid Build Coastguard Worker // on the annotations (produced by experimental_annotated_callstack) to be
297*6dbdd20aSAndroid Build Coastguard Worker // stable for a given callsite (equivalently: dependent only on their parents).
PreprocessLocations(trace_processor::TraceProcessor * tp,trace_processor::StringPool * interner,bool annotate_frames)298*6dbdd20aSAndroid Build Coastguard Worker LocationTracker PreprocessLocations(trace_processor::TraceProcessor* tp,
299*6dbdd20aSAndroid Build Coastguard Worker trace_processor::StringPool* interner,
300*6dbdd20aSAndroid Build Coastguard Worker bool annotate_frames) {
301*6dbdd20aSAndroid Build Coastguard Worker LocationTracker tracker;
302*6dbdd20aSAndroid Build Coastguard Worker
303*6dbdd20aSAndroid Build Coastguard Worker // Keyed by symbol_set_id, discarded once this function converts the inlines
304*6dbdd20aSAndroid Build Coastguard Worker // into Line and Function entries.
305*6dbdd20aSAndroid Build Coastguard Worker std::unordered_map<int64_t, std::vector<PreprocessedInline>> inlining_info =
306*6dbdd20aSAndroid Build Coastguard Worker PreprocessInliningInfo(tp, interner);
307*6dbdd20aSAndroid Build Coastguard Worker
308*6dbdd20aSAndroid Build Coastguard Worker // Higher callsite ids most likely correspond to the deepest stacks, so we'll
309*6dbdd20aSAndroid Build Coastguard Worker // fill more of the overall callsite->location map by visiting the callsited
310*6dbdd20aSAndroid Build Coastguard Worker // in decreasing id order. Since processing a callstack also fills in the data
311*6dbdd20aSAndroid Build Coastguard Worker // for all parent callsites.
312*6dbdd20aSAndroid Build Coastguard Worker Iterator cid_it = tp->ExecuteQuery(
313*6dbdd20aSAndroid Build Coastguard Worker "select id from stack_profile_callsite order by id desc;");
314*6dbdd20aSAndroid Build Coastguard Worker while (cid_it.Next()) {
315*6dbdd20aSAndroid Build Coastguard Worker int64_t query_cid = cid_it.Get(0).AsLong();
316*6dbdd20aSAndroid Build Coastguard Worker
317*6dbdd20aSAndroid Build Coastguard Worker // If the leaf has been processed, the rest of the stack is already known.
318*6dbdd20aSAndroid Build Coastguard Worker if (tracker.IsCallsiteProcessed(query_cid))
319*6dbdd20aSAndroid Build Coastguard Worker continue;
320*6dbdd20aSAndroid Build Coastguard Worker
321*6dbdd20aSAndroid Build Coastguard Worker std::string annotated_query =
322*6dbdd20aSAndroid Build Coastguard Worker "select sp.id, sp.annotation, spf.mapping, spf.name, "
323*6dbdd20aSAndroid Build Coastguard Worker "coalesce(spf.deobfuscated_name, demangle(spf.name), spf.name), "
324*6dbdd20aSAndroid Build Coastguard Worker "spf.symbol_set_id from "
325*6dbdd20aSAndroid Build Coastguard Worker "experimental_annotated_callstack(" +
326*6dbdd20aSAndroid Build Coastguard Worker std::to_string(query_cid) +
327*6dbdd20aSAndroid Build Coastguard Worker ") sp join stack_profile_frame spf on (sp.frame_id == spf.id) "
328*6dbdd20aSAndroid Build Coastguard Worker "order by depth asc";
329*6dbdd20aSAndroid Build Coastguard Worker Iterator c_it = tp->ExecuteQuery(annotated_query);
330*6dbdd20aSAndroid Build Coastguard Worker
331*6dbdd20aSAndroid Build Coastguard Worker std::vector<int64_t> callstack_loc_ids;
332*6dbdd20aSAndroid Build Coastguard Worker while (c_it.Next()) {
333*6dbdd20aSAndroid Build Coastguard Worker int64_t cid = c_it.Get(0).AsLong();
334*6dbdd20aSAndroid Build Coastguard Worker auto annotation = c_it.Get(1).is_null() ? "" : c_it.Get(1).AsString();
335*6dbdd20aSAndroid Build Coastguard Worker int64_t mapping_id = c_it.Get(2).AsLong();
336*6dbdd20aSAndroid Build Coastguard Worker auto func_sysname = c_it.Get(3).is_null() ? "" : c_it.Get(3).AsString();
337*6dbdd20aSAndroid Build Coastguard Worker auto func_name = c_it.Get(4).is_null() ? "" : c_it.Get(4).AsString();
338*6dbdd20aSAndroid Build Coastguard Worker std::optional<int64_t> symbol_set_id =
339*6dbdd20aSAndroid Build Coastguard Worker c_it.Get(5).is_null() ? std::nullopt
340*6dbdd20aSAndroid Build Coastguard Worker : std::make_optional(c_it.Get(5).AsLong());
341*6dbdd20aSAndroid Build Coastguard Worker
342*6dbdd20aSAndroid Build Coastguard Worker Location loc(mapping_id, /*single_function_id=*/-1, {});
343*6dbdd20aSAndroid Build Coastguard Worker
344*6dbdd20aSAndroid Build Coastguard Worker auto intern_function = [interner, &tracker, annotate_frames](
345*6dbdd20aSAndroid Build Coastguard Worker StringId func_sysname_id,
346*6dbdd20aSAndroid Build Coastguard Worker StringId original_func_name_id,
347*6dbdd20aSAndroid Build Coastguard Worker StringId filename_id,
348*6dbdd20aSAndroid Build Coastguard Worker const std::string& anno) {
349*6dbdd20aSAndroid Build Coastguard Worker std::string fname = interner->Get(original_func_name_id).ToStdString();
350*6dbdd20aSAndroid Build Coastguard Worker if (annotate_frames && !anno.empty() && !fname.empty())
351*6dbdd20aSAndroid Build Coastguard Worker fname = fname + " [" + anno + "]";
352*6dbdd20aSAndroid Build Coastguard Worker StringId func_name_id = interner->InternString(base::StringView(fname));
353*6dbdd20aSAndroid Build Coastguard Worker Function func(func_name_id, func_sysname_id, filename_id);
354*6dbdd20aSAndroid Build Coastguard Worker return tracker.InternFunction(func);
355*6dbdd20aSAndroid Build Coastguard Worker };
356*6dbdd20aSAndroid Build Coastguard Worker
357*6dbdd20aSAndroid Build Coastguard Worker // Inlining information available
358*6dbdd20aSAndroid Build Coastguard Worker if (symbol_set_id.has_value()) {
359*6dbdd20aSAndroid Build Coastguard Worker auto it = inlining_info.find(*symbol_set_id);
360*6dbdd20aSAndroid Build Coastguard Worker if (it == inlining_info.end()) {
361*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_DFATAL_OR_ELOG(
362*6dbdd20aSAndroid Build Coastguard Worker "Failed to find stack_profile_symbol entry for symbol_set_id "
363*6dbdd20aSAndroid Build Coastguard Worker "%" PRIi64 "",
364*6dbdd20aSAndroid Build Coastguard Worker *symbol_set_id);
365*6dbdd20aSAndroid Build Coastguard Worker return {};
366*6dbdd20aSAndroid Build Coastguard Worker }
367*6dbdd20aSAndroid Build Coastguard Worker
368*6dbdd20aSAndroid Build Coastguard Worker // N inlined functions
369*6dbdd20aSAndroid Build Coastguard Worker // The symbolised packets currently assume pre-demangled data (as that's
370*6dbdd20aSAndroid Build Coastguard Worker // the default of llvm-symbolizer), so we don't have a system name for
371*6dbdd20aSAndroid Build Coastguard Worker // each deinlined frame. Set the human-readable name for both fields. We
372*6dbdd20aSAndroid Build Coastguard Worker // can change this, but there's no demand for accurate system names in
373*6dbdd20aSAndroid Build Coastguard Worker // pprofs.
374*6dbdd20aSAndroid Build Coastguard Worker for (const auto& line : it->second) {
375*6dbdd20aSAndroid Build Coastguard Worker int64_t func_id = intern_function(line.name_id, line.name_id,
376*6dbdd20aSAndroid Build Coastguard Worker line.filename_id, annotation);
377*6dbdd20aSAndroid Build Coastguard Worker
378*6dbdd20aSAndroid Build Coastguard Worker loc.inlined_functions.emplace_back(func_id, line.line_no);
379*6dbdd20aSAndroid Build Coastguard Worker }
380*6dbdd20aSAndroid Build Coastguard Worker } else {
381*6dbdd20aSAndroid Build Coastguard Worker // Otherwise - single function
382*6dbdd20aSAndroid Build Coastguard Worker int64_t func_id =
383*6dbdd20aSAndroid Build Coastguard Worker intern_function(interner->InternString(func_sysname),
384*6dbdd20aSAndroid Build Coastguard Worker interner->InternString(func_name),
385*6dbdd20aSAndroid Build Coastguard Worker /*filename_id=*/StringId::Null(), annotation);
386*6dbdd20aSAndroid Build Coastguard Worker loc.single_function_id = func_id;
387*6dbdd20aSAndroid Build Coastguard Worker }
388*6dbdd20aSAndroid Build Coastguard Worker
389*6dbdd20aSAndroid Build Coastguard Worker int64_t loc_id = tracker.InternLocation(std::move(loc));
390*6dbdd20aSAndroid Build Coastguard Worker
391*6dbdd20aSAndroid Build Coastguard Worker // Update the tracker with the locations so far (for example, at depth 2,
392*6dbdd20aSAndroid Build Coastguard Worker // we'll have 3 root-most locations in |callstack_loc_ids|).
393*6dbdd20aSAndroid Build Coastguard Worker callstack_loc_ids.push_back(loc_id);
394*6dbdd20aSAndroid Build Coastguard Worker tracker.MaybeSetCallsiteLocations(cid, callstack_loc_ids);
395*6dbdd20aSAndroid Build Coastguard Worker }
396*6dbdd20aSAndroid Build Coastguard Worker
397*6dbdd20aSAndroid Build Coastguard Worker if (!c_it.Status().ok()) {
398*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_DFATAL_OR_ELOG("Invalid iterator: %s",
399*6dbdd20aSAndroid Build Coastguard Worker c_it.Status().message().c_str());
400*6dbdd20aSAndroid Build Coastguard Worker return {};
401*6dbdd20aSAndroid Build Coastguard Worker }
402*6dbdd20aSAndroid Build Coastguard Worker }
403*6dbdd20aSAndroid Build Coastguard Worker
404*6dbdd20aSAndroid Build Coastguard Worker if (!cid_it.Status().ok()) {
405*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_DFATAL_OR_ELOG("Invalid iterator: %s",
406*6dbdd20aSAndroid Build Coastguard Worker cid_it.Status().message().c_str());
407*6dbdd20aSAndroid Build Coastguard Worker return {};
408*6dbdd20aSAndroid Build Coastguard Worker }
409*6dbdd20aSAndroid Build Coastguard Worker
410*6dbdd20aSAndroid Build Coastguard Worker return tracker;
411*6dbdd20aSAndroid Build Coastguard Worker }
412*6dbdd20aSAndroid Build Coastguard Worker
413*6dbdd20aSAndroid Build Coastguard Worker // Builds the |perftools.profiles.Profile| proto.
414*6dbdd20aSAndroid Build Coastguard Worker class GProfileBuilder {
415*6dbdd20aSAndroid Build Coastguard Worker public:
GProfileBuilder(const LocationTracker & locations,trace_processor::StringPool * interner)416*6dbdd20aSAndroid Build Coastguard Worker GProfileBuilder(const LocationTracker& locations,
417*6dbdd20aSAndroid Build Coastguard Worker trace_processor::StringPool* interner)
418*6dbdd20aSAndroid Build Coastguard Worker : locations_(locations), interner_(interner) {
419*6dbdd20aSAndroid Build Coastguard Worker // The pprof format requires the first entry in the string table to be the
420*6dbdd20aSAndroid Build Coastguard Worker // empty string.
421*6dbdd20aSAndroid Build Coastguard Worker int64_t empty_id = ToStringTableId(StringId::Null());
422*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_CHECK(empty_id == 0);
423*6dbdd20aSAndroid Build Coastguard Worker }
424*6dbdd20aSAndroid Build Coastguard Worker
WriteSampleTypes(const std::vector<std::pair<std::string,std::string>> & sample_types)425*6dbdd20aSAndroid Build Coastguard Worker void WriteSampleTypes(
426*6dbdd20aSAndroid Build Coastguard Worker const std::vector<std::pair<std::string, std::string>>& sample_types) {
427*6dbdd20aSAndroid Build Coastguard Worker for (const auto& st : sample_types) {
428*6dbdd20aSAndroid Build Coastguard Worker auto* sample_type = result_->add_sample_type();
429*6dbdd20aSAndroid Build Coastguard Worker sample_type->set_type(
430*6dbdd20aSAndroid Build Coastguard Worker ToStringTableId(interner_->InternString(base::StringView(st.first))));
431*6dbdd20aSAndroid Build Coastguard Worker sample_type->set_unit(ToStringTableId(
432*6dbdd20aSAndroid Build Coastguard Worker interner_->InternString(base::StringView(st.second))));
433*6dbdd20aSAndroid Build Coastguard Worker }
434*6dbdd20aSAndroid Build Coastguard Worker }
435*6dbdd20aSAndroid Build Coastguard Worker
AddSample(const protozero::PackedVarInt & values,int64_t callstack_id)436*6dbdd20aSAndroid Build Coastguard Worker bool AddSample(const protozero::PackedVarInt& values, int64_t callstack_id) {
437*6dbdd20aSAndroid Build Coastguard Worker const auto& location_ids = locations_.LocationsForCallstack(callstack_id);
438*6dbdd20aSAndroid Build Coastguard Worker if (location_ids.empty()) {
439*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_DFATAL_OR_ELOG(
440*6dbdd20aSAndroid Build Coastguard Worker "Failed to find frames for callstack id %" PRIi64 "", callstack_id);
441*6dbdd20aSAndroid Build Coastguard Worker return false;
442*6dbdd20aSAndroid Build Coastguard Worker }
443*6dbdd20aSAndroid Build Coastguard Worker
444*6dbdd20aSAndroid Build Coastguard Worker // LocationTracker stores location lists root-first, but the pprof format
445*6dbdd20aSAndroid Build Coastguard Worker // requires leaf-first.
446*6dbdd20aSAndroid Build Coastguard Worker protozero::PackedVarInt packed_locs;
447*6dbdd20aSAndroid Build Coastguard Worker for (auto it = location_ids.rbegin(); it != location_ids.rend(); ++it)
448*6dbdd20aSAndroid Build Coastguard Worker packed_locs.Append(ToPprofId(*it));
449*6dbdd20aSAndroid Build Coastguard Worker
450*6dbdd20aSAndroid Build Coastguard Worker auto* gsample = result_->add_sample();
451*6dbdd20aSAndroid Build Coastguard Worker gsample->set_value(values);
452*6dbdd20aSAndroid Build Coastguard Worker gsample->set_location_id(packed_locs);
453*6dbdd20aSAndroid Build Coastguard Worker
454*6dbdd20aSAndroid Build Coastguard Worker // Remember the locations s.t. we only serialize the referenced ones.
455*6dbdd20aSAndroid Build Coastguard Worker seen_locations_.insert(location_ids.cbegin(), location_ids.cend());
456*6dbdd20aSAndroid Build Coastguard Worker return true;
457*6dbdd20aSAndroid Build Coastguard Worker }
458*6dbdd20aSAndroid Build Coastguard Worker
CompleteProfile(trace_processor::TraceProcessor * tp,bool write_mappings=true)459*6dbdd20aSAndroid Build Coastguard Worker std::string CompleteProfile(trace_processor::TraceProcessor* tp,
460*6dbdd20aSAndroid Build Coastguard Worker bool write_mappings = true) {
461*6dbdd20aSAndroid Build Coastguard Worker std::set<int64_t> seen_mappings;
462*6dbdd20aSAndroid Build Coastguard Worker std::set<int64_t> seen_functions;
463*6dbdd20aSAndroid Build Coastguard Worker
464*6dbdd20aSAndroid Build Coastguard Worker if (!WriteLocations(&seen_mappings, &seen_functions))
465*6dbdd20aSAndroid Build Coastguard Worker return {};
466*6dbdd20aSAndroid Build Coastguard Worker if (!WriteFunctions(seen_functions))
467*6dbdd20aSAndroid Build Coastguard Worker return {};
468*6dbdd20aSAndroid Build Coastguard Worker if (write_mappings && !WriteMappings(tp, seen_mappings))
469*6dbdd20aSAndroid Build Coastguard Worker return {};
470*6dbdd20aSAndroid Build Coastguard Worker
471*6dbdd20aSAndroid Build Coastguard Worker WriteStringTable();
472*6dbdd20aSAndroid Build Coastguard Worker return result_.SerializeAsString();
473*6dbdd20aSAndroid Build Coastguard Worker }
474*6dbdd20aSAndroid Build Coastguard Worker
475*6dbdd20aSAndroid Build Coastguard Worker private:
476*6dbdd20aSAndroid Build Coastguard Worker // Serializes the Profile.Location entries referenced by this profile.
WriteLocations(std::set<int64_t> * seen_mappings,std::set<int64_t> * seen_functions)477*6dbdd20aSAndroid Build Coastguard Worker bool WriteLocations(std::set<int64_t>* seen_mappings,
478*6dbdd20aSAndroid Build Coastguard Worker std::set<int64_t>* seen_functions) {
479*6dbdd20aSAndroid Build Coastguard Worker const std::unordered_map<Location, int64_t>& locations =
480*6dbdd20aSAndroid Build Coastguard Worker locations_.AllLocations();
481*6dbdd20aSAndroid Build Coastguard Worker
482*6dbdd20aSAndroid Build Coastguard Worker size_t written_locations = 0;
483*6dbdd20aSAndroid Build Coastguard Worker for (const auto& loc_and_id : locations) {
484*6dbdd20aSAndroid Build Coastguard Worker const auto& loc = loc_and_id.first;
485*6dbdd20aSAndroid Build Coastguard Worker int64_t id = loc_and_id.second;
486*6dbdd20aSAndroid Build Coastguard Worker
487*6dbdd20aSAndroid Build Coastguard Worker if (seen_locations_.find(id) == seen_locations_.end())
488*6dbdd20aSAndroid Build Coastguard Worker continue;
489*6dbdd20aSAndroid Build Coastguard Worker
490*6dbdd20aSAndroid Build Coastguard Worker written_locations += 1;
491*6dbdd20aSAndroid Build Coastguard Worker seen_mappings->emplace(loc.mapping_id);
492*6dbdd20aSAndroid Build Coastguard Worker
493*6dbdd20aSAndroid Build Coastguard Worker auto* glocation = result_->add_location();
494*6dbdd20aSAndroid Build Coastguard Worker glocation->set_id(ToPprofId(id));
495*6dbdd20aSAndroid Build Coastguard Worker glocation->set_mapping_id(ToPprofId(loc.mapping_id));
496*6dbdd20aSAndroid Build Coastguard Worker
497*6dbdd20aSAndroid Build Coastguard Worker if (!loc.inlined_functions.empty()) {
498*6dbdd20aSAndroid Build Coastguard Worker for (const auto& line : loc.inlined_functions) {
499*6dbdd20aSAndroid Build Coastguard Worker seen_functions->insert(line.function_id);
500*6dbdd20aSAndroid Build Coastguard Worker
501*6dbdd20aSAndroid Build Coastguard Worker auto* gline = glocation->add_line();
502*6dbdd20aSAndroid Build Coastguard Worker gline->set_function_id(ToPprofId(line.function_id));
503*6dbdd20aSAndroid Build Coastguard Worker gline->set_line(line.line_no);
504*6dbdd20aSAndroid Build Coastguard Worker }
505*6dbdd20aSAndroid Build Coastguard Worker } else {
506*6dbdd20aSAndroid Build Coastguard Worker seen_functions->insert(loc.single_function_id);
507*6dbdd20aSAndroid Build Coastguard Worker
508*6dbdd20aSAndroid Build Coastguard Worker glocation->add_line()->set_function_id(
509*6dbdd20aSAndroid Build Coastguard Worker ToPprofId(loc.single_function_id));
510*6dbdd20aSAndroid Build Coastguard Worker }
511*6dbdd20aSAndroid Build Coastguard Worker }
512*6dbdd20aSAndroid Build Coastguard Worker
513*6dbdd20aSAndroid Build Coastguard Worker if (written_locations != seen_locations_.size()) {
514*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_DFATAL_OR_ELOG(
515*6dbdd20aSAndroid Build Coastguard Worker "Found only %zu/%zu locations during serialization.",
516*6dbdd20aSAndroid Build Coastguard Worker written_locations, seen_locations_.size());
517*6dbdd20aSAndroid Build Coastguard Worker return false;
518*6dbdd20aSAndroid Build Coastguard Worker }
519*6dbdd20aSAndroid Build Coastguard Worker return true;
520*6dbdd20aSAndroid Build Coastguard Worker }
521*6dbdd20aSAndroid Build Coastguard Worker
522*6dbdd20aSAndroid Build Coastguard Worker // Serializes the Profile.Function entries referenced by this profile.
WriteFunctions(const std::set<int64_t> & seen_functions)523*6dbdd20aSAndroid Build Coastguard Worker bool WriteFunctions(const std::set<int64_t>& seen_functions) {
524*6dbdd20aSAndroid Build Coastguard Worker const std::unordered_map<Function, int64_t>& functions =
525*6dbdd20aSAndroid Build Coastguard Worker locations_.AllFunctions();
526*6dbdd20aSAndroid Build Coastguard Worker
527*6dbdd20aSAndroid Build Coastguard Worker size_t written_functions = 0;
528*6dbdd20aSAndroid Build Coastguard Worker for (const auto& func_and_id : functions) {
529*6dbdd20aSAndroid Build Coastguard Worker const auto& func = func_and_id.first;
530*6dbdd20aSAndroid Build Coastguard Worker int64_t id = func_and_id.second;
531*6dbdd20aSAndroid Build Coastguard Worker
532*6dbdd20aSAndroid Build Coastguard Worker if (seen_functions.find(id) == seen_functions.end())
533*6dbdd20aSAndroid Build Coastguard Worker continue;
534*6dbdd20aSAndroid Build Coastguard Worker
535*6dbdd20aSAndroid Build Coastguard Worker written_functions += 1;
536*6dbdd20aSAndroid Build Coastguard Worker
537*6dbdd20aSAndroid Build Coastguard Worker auto* gfunction = result_->add_function();
538*6dbdd20aSAndroid Build Coastguard Worker gfunction->set_id(ToPprofId(id));
539*6dbdd20aSAndroid Build Coastguard Worker gfunction->set_name(ToStringTableId(func.name_id));
540*6dbdd20aSAndroid Build Coastguard Worker gfunction->set_system_name(ToStringTableId(func.system_name_id));
541*6dbdd20aSAndroid Build Coastguard Worker if (!func.filename_id.is_null())
542*6dbdd20aSAndroid Build Coastguard Worker gfunction->set_filename(ToStringTableId(func.filename_id));
543*6dbdd20aSAndroid Build Coastguard Worker }
544*6dbdd20aSAndroid Build Coastguard Worker
545*6dbdd20aSAndroid Build Coastguard Worker if (written_functions != seen_functions.size()) {
546*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_DFATAL_OR_ELOG(
547*6dbdd20aSAndroid Build Coastguard Worker "Found only %zu/%zu functions during serialization.",
548*6dbdd20aSAndroid Build Coastguard Worker written_functions, seen_functions.size());
549*6dbdd20aSAndroid Build Coastguard Worker return false;
550*6dbdd20aSAndroid Build Coastguard Worker }
551*6dbdd20aSAndroid Build Coastguard Worker return true;
552*6dbdd20aSAndroid Build Coastguard Worker }
553*6dbdd20aSAndroid Build Coastguard Worker
554*6dbdd20aSAndroid Build Coastguard Worker // Serializes the Profile.Mapping entries referenced by this profile.
WriteMappings(trace_processor::TraceProcessor * tp,const std::set<int64_t> & seen_mappings)555*6dbdd20aSAndroid Build Coastguard Worker bool WriteMappings(trace_processor::TraceProcessor* tp,
556*6dbdd20aSAndroid Build Coastguard Worker const std::set<int64_t>& seen_mappings) {
557*6dbdd20aSAndroid Build Coastguard Worker Iterator mapping_it = tp->ExecuteQuery(
558*6dbdd20aSAndroid Build Coastguard Worker "SELECT id, exact_offset, start, end, name, build_id "
559*6dbdd20aSAndroid Build Coastguard Worker "FROM stack_profile_mapping;");
560*6dbdd20aSAndroid Build Coastguard Worker size_t mappings_no = 0;
561*6dbdd20aSAndroid Build Coastguard Worker while (mapping_it.Next()) {
562*6dbdd20aSAndroid Build Coastguard Worker int64_t id = mapping_it.Get(0).AsLong();
563*6dbdd20aSAndroid Build Coastguard Worker if (seen_mappings.find(id) == seen_mappings.end())
564*6dbdd20aSAndroid Build Coastguard Worker continue;
565*6dbdd20aSAndroid Build Coastguard Worker ++mappings_no;
566*6dbdd20aSAndroid Build Coastguard Worker auto interned_filename = ToStringTableId(
567*6dbdd20aSAndroid Build Coastguard Worker interner_->InternString(mapping_it.Get(4).AsString()));
568*6dbdd20aSAndroid Build Coastguard Worker auto interned_build_id = ToStringTableId(
569*6dbdd20aSAndroid Build Coastguard Worker interner_->InternString(mapping_it.Get(5).AsString()));
570*6dbdd20aSAndroid Build Coastguard Worker auto* gmapping = result_->add_mapping();
571*6dbdd20aSAndroid Build Coastguard Worker gmapping->set_id(ToPprofId(id));
572*6dbdd20aSAndroid Build Coastguard Worker gmapping->set_file_offset(
573*6dbdd20aSAndroid Build Coastguard Worker static_cast<uint64_t>(mapping_it.Get(1).AsLong()));
574*6dbdd20aSAndroid Build Coastguard Worker gmapping->set_memory_start(
575*6dbdd20aSAndroid Build Coastguard Worker static_cast<uint64_t>(mapping_it.Get(2).AsLong()));
576*6dbdd20aSAndroid Build Coastguard Worker gmapping->set_memory_limit(
577*6dbdd20aSAndroid Build Coastguard Worker static_cast<uint64_t>(mapping_it.Get(3).AsLong()));
578*6dbdd20aSAndroid Build Coastguard Worker gmapping->set_filename(interned_filename);
579*6dbdd20aSAndroid Build Coastguard Worker gmapping->set_build_id(interned_build_id);
580*6dbdd20aSAndroid Build Coastguard Worker }
581*6dbdd20aSAndroid Build Coastguard Worker if (!mapping_it.Status().ok()) {
582*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_DFATAL_OR_ELOG("Invalid mapping iterator: %s",
583*6dbdd20aSAndroid Build Coastguard Worker mapping_it.Status().message().c_str());
584*6dbdd20aSAndroid Build Coastguard Worker return false;
585*6dbdd20aSAndroid Build Coastguard Worker }
586*6dbdd20aSAndroid Build Coastguard Worker if (mappings_no != seen_mappings.size()) {
587*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_DFATAL_OR_ELOG("Missing mappings.");
588*6dbdd20aSAndroid Build Coastguard Worker return false;
589*6dbdd20aSAndroid Build Coastguard Worker }
590*6dbdd20aSAndroid Build Coastguard Worker return true;
591*6dbdd20aSAndroid Build Coastguard Worker }
592*6dbdd20aSAndroid Build Coastguard Worker
WriteStringTable()593*6dbdd20aSAndroid Build Coastguard Worker void WriteStringTable() {
594*6dbdd20aSAndroid Build Coastguard Worker for (StringId id : string_table_) {
595*6dbdd20aSAndroid Build Coastguard Worker trace_processor::NullTermStringView s = interner_->Get(id);
596*6dbdd20aSAndroid Build Coastguard Worker result_->add_string_table(s.data(), s.size());
597*6dbdd20aSAndroid Build Coastguard Worker }
598*6dbdd20aSAndroid Build Coastguard Worker }
599*6dbdd20aSAndroid Build Coastguard Worker
ToStringTableId(StringId interned_id)600*6dbdd20aSAndroid Build Coastguard Worker int64_t ToStringTableId(StringId interned_id) {
601*6dbdd20aSAndroid Build Coastguard Worker auto it = interning_remapper_.find(interned_id);
602*6dbdd20aSAndroid Build Coastguard Worker if (it == interning_remapper_.end()) {
603*6dbdd20aSAndroid Build Coastguard Worker int64_t table_id = static_cast<int64_t>(string_table_.size());
604*6dbdd20aSAndroid Build Coastguard Worker string_table_.push_back(interned_id);
605*6dbdd20aSAndroid Build Coastguard Worker bool inserted = false;
606*6dbdd20aSAndroid Build Coastguard Worker std::tie(it, inserted) =
607*6dbdd20aSAndroid Build Coastguard Worker interning_remapper_.emplace(interned_id, table_id);
608*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_DCHECK(inserted);
609*6dbdd20aSAndroid Build Coastguard Worker }
610*6dbdd20aSAndroid Build Coastguard Worker return it->second;
611*6dbdd20aSAndroid Build Coastguard Worker }
612*6dbdd20aSAndroid Build Coastguard Worker
613*6dbdd20aSAndroid Build Coastguard Worker // Contains all locations, lines, functions (in memory):
614*6dbdd20aSAndroid Build Coastguard Worker const LocationTracker& locations_;
615*6dbdd20aSAndroid Build Coastguard Worker
616*6dbdd20aSAndroid Build Coastguard Worker // String interner, strings referenced by LocationTracker are already
617*6dbdd20aSAndroid Build Coastguard Worker // interned. The new internings will come from mappings, and sample types.
618*6dbdd20aSAndroid Build Coastguard Worker trace_processor::StringPool* interner_;
619*6dbdd20aSAndroid Build Coastguard Worker
620*6dbdd20aSAndroid Build Coastguard Worker // The profile format uses the repeated string_table field's index as an
621*6dbdd20aSAndroid Build Coastguard Worker // implicit id, so these structures remap the interned strings into sequential
622*6dbdd20aSAndroid Build Coastguard Worker // ids. Only the strings referenced by this GProfileBuilder instance will be
623*6dbdd20aSAndroid Build Coastguard Worker // added to the table.
624*6dbdd20aSAndroid Build Coastguard Worker std::unordered_map<StringId, int64_t> interning_remapper_;
625*6dbdd20aSAndroid Build Coastguard Worker std::vector<StringId> string_table_;
626*6dbdd20aSAndroid Build Coastguard Worker
627*6dbdd20aSAndroid Build Coastguard Worker // Profile proto being serialized.
628*6dbdd20aSAndroid Build Coastguard Worker protozero::HeapBuffered<third_party::perftools::profiles::pbzero::Profile>
629*6dbdd20aSAndroid Build Coastguard Worker result_;
630*6dbdd20aSAndroid Build Coastguard Worker
631*6dbdd20aSAndroid Build Coastguard Worker // Set of locations referenced by the added samples.
632*6dbdd20aSAndroid Build Coastguard Worker std::set<int64_t> seen_locations_;
633*6dbdd20aSAndroid Build Coastguard Worker };
634*6dbdd20aSAndroid Build Coastguard Worker
635*6dbdd20aSAndroid Build Coastguard Worker namespace heap_profile {
636*6dbdd20aSAndroid Build Coastguard Worker struct View {
637*6dbdd20aSAndroid Build Coastguard Worker const char* type;
638*6dbdd20aSAndroid Build Coastguard Worker const char* unit;
639*6dbdd20aSAndroid Build Coastguard Worker const char* aggregator;
640*6dbdd20aSAndroid Build Coastguard Worker const char* filter;
641*6dbdd20aSAndroid Build Coastguard Worker };
642*6dbdd20aSAndroid Build Coastguard Worker
643*6dbdd20aSAndroid Build Coastguard Worker const View kMallocViews[] = {
644*6dbdd20aSAndroid Build Coastguard Worker {"Total malloc count", "count", "sum(count)", "size >= 0"},
645*6dbdd20aSAndroid Build Coastguard Worker {"Total malloc size", "bytes", "SUM(size)", "size >= 0"},
646*6dbdd20aSAndroid Build Coastguard Worker {"Unreleased malloc count", "count", "SUM(count)", nullptr},
647*6dbdd20aSAndroid Build Coastguard Worker {"Unreleased malloc size", "bytes", "SUM(size)", nullptr}};
648*6dbdd20aSAndroid Build Coastguard Worker
649*6dbdd20aSAndroid Build Coastguard Worker const View kGenericViews[] = {
650*6dbdd20aSAndroid Build Coastguard Worker {"Total count", "count", "sum(count)", "size >= 0"},
651*6dbdd20aSAndroid Build Coastguard Worker {"Total size", "bytes", "SUM(size)", "size >= 0"},
652*6dbdd20aSAndroid Build Coastguard Worker {"Unreleased count", "count", "SUM(count)", nullptr},
653*6dbdd20aSAndroid Build Coastguard Worker {"Unreleased size", "bytes", "SUM(size)", nullptr}};
654*6dbdd20aSAndroid Build Coastguard Worker
655*6dbdd20aSAndroid Build Coastguard Worker const View kJavaSamplesViews[] = {
656*6dbdd20aSAndroid Build Coastguard Worker {"Total allocation count", "count", "SUM(count)", nullptr},
657*6dbdd20aSAndroid Build Coastguard Worker {"Total allocation size", "bytes", "SUM(size)", nullptr}};
658*6dbdd20aSAndroid Build Coastguard Worker
VerifyPIDStats(trace_processor::TraceProcessor * tp,uint64_t pid)659*6dbdd20aSAndroid Build Coastguard Worker static bool VerifyPIDStats(trace_processor::TraceProcessor* tp, uint64_t pid) {
660*6dbdd20aSAndroid Build Coastguard Worker bool success = true;
661*6dbdd20aSAndroid Build Coastguard Worker std::optional<int64_t> stat =
662*6dbdd20aSAndroid Build Coastguard Worker GetStatsEntry(tp, "heapprofd_buffer_corrupted", std::make_optional(pid));
663*6dbdd20aSAndroid Build Coastguard Worker if (!stat.has_value()) {
664*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_DFATAL_OR_ELOG("Failed to get heapprofd_buffer_corrupted stat");
665*6dbdd20aSAndroid Build Coastguard Worker } else if (stat.value() > 0) {
666*6dbdd20aSAndroid Build Coastguard Worker success = false;
667*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_ELOG("WARNING: The profile for %" PRIu64
668*6dbdd20aSAndroid Build Coastguard Worker " ended early due to a buffer corruption."
669*6dbdd20aSAndroid Build Coastguard Worker " THIS IS ALWAYS A BUG IN HEAPPROFD OR"
670*6dbdd20aSAndroid Build Coastguard Worker " CLIENT MEMORY CORRUPTION.",
671*6dbdd20aSAndroid Build Coastguard Worker pid);
672*6dbdd20aSAndroid Build Coastguard Worker }
673*6dbdd20aSAndroid Build Coastguard Worker stat = GetStatsEntry(tp, "heapprofd_buffer_overran", std::make_optional(pid));
674*6dbdd20aSAndroid Build Coastguard Worker if (!stat.has_value()) {
675*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_DFATAL_OR_ELOG("Failed to get heapprofd_buffer_overran stat");
676*6dbdd20aSAndroid Build Coastguard Worker } else if (stat.value() > 0) {
677*6dbdd20aSAndroid Build Coastguard Worker success = false;
678*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_ELOG("WARNING: The profile for %" PRIu64
679*6dbdd20aSAndroid Build Coastguard Worker " ended early due to a buffer overrun.",
680*6dbdd20aSAndroid Build Coastguard Worker pid);
681*6dbdd20aSAndroid Build Coastguard Worker }
682*6dbdd20aSAndroid Build Coastguard Worker
683*6dbdd20aSAndroid Build Coastguard Worker stat = GetStatsEntry(tp, "heapprofd_rejected_concurrent", pid);
684*6dbdd20aSAndroid Build Coastguard Worker if (!stat.has_value()) {
685*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_DFATAL_OR_ELOG("Failed to get heapprofd_rejected_concurrent stat");
686*6dbdd20aSAndroid Build Coastguard Worker } else if (stat.value() > 0) {
687*6dbdd20aSAndroid Build Coastguard Worker success = false;
688*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_ELOG("WARNING: The profile for %" PRIu64
689*6dbdd20aSAndroid Build Coastguard Worker " was rejected due to a concurrent profile.",
690*6dbdd20aSAndroid Build Coastguard Worker pid);
691*6dbdd20aSAndroid Build Coastguard Worker }
692*6dbdd20aSAndroid Build Coastguard Worker return success;
693*6dbdd20aSAndroid Build Coastguard Worker }
694*6dbdd20aSAndroid Build Coastguard Worker
BuildViewIterators(trace_processor::TraceProcessor * tp,uint64_t upid,uint64_t ts,const char * heap_name,const std::vector<View> & views)695*6dbdd20aSAndroid Build Coastguard Worker static std::vector<Iterator> BuildViewIterators(
696*6dbdd20aSAndroid Build Coastguard Worker trace_processor::TraceProcessor* tp,
697*6dbdd20aSAndroid Build Coastguard Worker uint64_t upid,
698*6dbdd20aSAndroid Build Coastguard Worker uint64_t ts,
699*6dbdd20aSAndroid Build Coastguard Worker const char* heap_name,
700*6dbdd20aSAndroid Build Coastguard Worker const std::vector<View>& views) {
701*6dbdd20aSAndroid Build Coastguard Worker std::vector<Iterator> view_its;
702*6dbdd20aSAndroid Build Coastguard Worker for (const View& v : views) {
703*6dbdd20aSAndroid Build Coastguard Worker std::string query = "SELECT hpa.callsite_id ";
704*6dbdd20aSAndroid Build Coastguard Worker query +=
705*6dbdd20aSAndroid Build Coastguard Worker ", " + std::string(v.aggregator) + " FROM heap_profile_allocation hpa ";
706*6dbdd20aSAndroid Build Coastguard Worker // TODO(fmayer): Figure out where negative callsite_id comes from.
707*6dbdd20aSAndroid Build Coastguard Worker query += "WHERE hpa.callsite_id >= 0 ";
708*6dbdd20aSAndroid Build Coastguard Worker query += "AND hpa.upid = " + std::to_string(upid) + " ";
709*6dbdd20aSAndroid Build Coastguard Worker query += "AND hpa.ts <= " + std::to_string(ts) + " ";
710*6dbdd20aSAndroid Build Coastguard Worker query += "AND hpa.heap_name = '" + std::string(heap_name) + "' ";
711*6dbdd20aSAndroid Build Coastguard Worker if (v.filter)
712*6dbdd20aSAndroid Build Coastguard Worker query += "AND " + std::string(v.filter) + " ";
713*6dbdd20aSAndroid Build Coastguard Worker query += "GROUP BY hpa.callsite_id;";
714*6dbdd20aSAndroid Build Coastguard Worker view_its.emplace_back(tp->ExecuteQuery(query));
715*6dbdd20aSAndroid Build Coastguard Worker }
716*6dbdd20aSAndroid Build Coastguard Worker return view_its;
717*6dbdd20aSAndroid Build Coastguard Worker }
718*6dbdd20aSAndroid Build Coastguard Worker
WriteAllocations(GProfileBuilder * builder,std::vector<Iterator> * view_its)719*6dbdd20aSAndroid Build Coastguard Worker static bool WriteAllocations(GProfileBuilder* builder,
720*6dbdd20aSAndroid Build Coastguard Worker std::vector<Iterator>* view_its) {
721*6dbdd20aSAndroid Build Coastguard Worker for (;;) {
722*6dbdd20aSAndroid Build Coastguard Worker bool all_next = true;
723*6dbdd20aSAndroid Build Coastguard Worker bool any_next = false;
724*6dbdd20aSAndroid Build Coastguard Worker for (size_t i = 0; i < view_its->size(); ++i) {
725*6dbdd20aSAndroid Build Coastguard Worker Iterator& it = (*view_its)[i];
726*6dbdd20aSAndroid Build Coastguard Worker bool next = it.Next();
727*6dbdd20aSAndroid Build Coastguard Worker if (!it.Status().ok()) {
728*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_DFATAL_OR_ELOG("Invalid view iterator: %s",
729*6dbdd20aSAndroid Build Coastguard Worker it.Status().message().c_str());
730*6dbdd20aSAndroid Build Coastguard Worker return false;
731*6dbdd20aSAndroid Build Coastguard Worker }
732*6dbdd20aSAndroid Build Coastguard Worker all_next = all_next && next;
733*6dbdd20aSAndroid Build Coastguard Worker any_next = any_next || next;
734*6dbdd20aSAndroid Build Coastguard Worker }
735*6dbdd20aSAndroid Build Coastguard Worker
736*6dbdd20aSAndroid Build Coastguard Worker if (!all_next) {
737*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_CHECK(!any_next);
738*6dbdd20aSAndroid Build Coastguard Worker break;
739*6dbdd20aSAndroid Build Coastguard Worker }
740*6dbdd20aSAndroid Build Coastguard Worker
741*6dbdd20aSAndroid Build Coastguard Worker protozero::PackedVarInt sample_values;
742*6dbdd20aSAndroid Build Coastguard Worker int64_t callstack_id = -1;
743*6dbdd20aSAndroid Build Coastguard Worker for (size_t i = 0; i < view_its->size(); ++i) {
744*6dbdd20aSAndroid Build Coastguard Worker if (i == 0) {
745*6dbdd20aSAndroid Build Coastguard Worker callstack_id = (*view_its)[i].Get(0).AsLong();
746*6dbdd20aSAndroid Build Coastguard Worker } else if (callstack_id != (*view_its)[i].Get(0).AsLong()) {
747*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_DFATAL_OR_ELOG("Wrong callstack.");
748*6dbdd20aSAndroid Build Coastguard Worker return false;
749*6dbdd20aSAndroid Build Coastguard Worker }
750*6dbdd20aSAndroid Build Coastguard Worker sample_values.Append((*view_its)[i].Get(1).AsLong());
751*6dbdd20aSAndroid Build Coastguard Worker }
752*6dbdd20aSAndroid Build Coastguard Worker
753*6dbdd20aSAndroid Build Coastguard Worker if (!builder->AddSample(sample_values, callstack_id))
754*6dbdd20aSAndroid Build Coastguard Worker return false;
755*6dbdd20aSAndroid Build Coastguard Worker }
756*6dbdd20aSAndroid Build Coastguard Worker return true;
757*6dbdd20aSAndroid Build Coastguard Worker }
758*6dbdd20aSAndroid Build Coastguard Worker
TraceToHeapPprof(trace_processor::TraceProcessor * tp,std::vector<SerializedProfile> * output,bool annotate_frames,uint64_t target_pid,const std::vector<uint64_t> & target_timestamps)759*6dbdd20aSAndroid Build Coastguard Worker static bool TraceToHeapPprof(trace_processor::TraceProcessor* tp,
760*6dbdd20aSAndroid Build Coastguard Worker std::vector<SerializedProfile>* output,
761*6dbdd20aSAndroid Build Coastguard Worker bool annotate_frames,
762*6dbdd20aSAndroid Build Coastguard Worker uint64_t target_pid,
763*6dbdd20aSAndroid Build Coastguard Worker const std::vector<uint64_t>& target_timestamps) {
764*6dbdd20aSAndroid Build Coastguard Worker trace_processor::StringPool interner;
765*6dbdd20aSAndroid Build Coastguard Worker LocationTracker locations =
766*6dbdd20aSAndroid Build Coastguard Worker PreprocessLocations(tp, &interner, annotate_frames);
767*6dbdd20aSAndroid Build Coastguard Worker
768*6dbdd20aSAndroid Build Coastguard Worker bool any_fail = false;
769*6dbdd20aSAndroid Build Coastguard Worker Iterator it = tp->ExecuteQuery(
770*6dbdd20aSAndroid Build Coastguard Worker "select distinct hpa.upid, hpa.ts, p.pid, hpa.heap_name "
771*6dbdd20aSAndroid Build Coastguard Worker "from heap_profile_allocation hpa, "
772*6dbdd20aSAndroid Build Coastguard Worker "process p where p.upid = hpa.upid;");
773*6dbdd20aSAndroid Build Coastguard Worker while (it.Next()) {
774*6dbdd20aSAndroid Build Coastguard Worker GProfileBuilder builder(locations, &interner);
775*6dbdd20aSAndroid Build Coastguard Worker uint64_t upid = static_cast<uint64_t>(it.Get(0).AsLong());
776*6dbdd20aSAndroid Build Coastguard Worker uint64_t ts = static_cast<uint64_t>(it.Get(1).AsLong());
777*6dbdd20aSAndroid Build Coastguard Worker uint64_t profile_pid = static_cast<uint64_t>(it.Get(2).AsLong());
778*6dbdd20aSAndroid Build Coastguard Worker const char* heap_name = it.Get(3).AsString();
779*6dbdd20aSAndroid Build Coastguard Worker if ((target_pid > 0 && profile_pid != target_pid) ||
780*6dbdd20aSAndroid Build Coastguard Worker (!target_timestamps.empty() &&
781*6dbdd20aSAndroid Build Coastguard Worker std::find(target_timestamps.begin(), target_timestamps.end(), ts) ==
782*6dbdd20aSAndroid Build Coastguard Worker target_timestamps.end())) {
783*6dbdd20aSAndroid Build Coastguard Worker continue;
784*6dbdd20aSAndroid Build Coastguard Worker }
785*6dbdd20aSAndroid Build Coastguard Worker
786*6dbdd20aSAndroid Build Coastguard Worker if (!VerifyPIDStats(tp, profile_pid))
787*6dbdd20aSAndroid Build Coastguard Worker any_fail = true;
788*6dbdd20aSAndroid Build Coastguard Worker
789*6dbdd20aSAndroid Build Coastguard Worker std::vector<View> views;
790*6dbdd20aSAndroid Build Coastguard Worker if (base::StringView(heap_name) == "libc.malloc") {
791*6dbdd20aSAndroid Build Coastguard Worker views.assign(std::begin(kMallocViews), std::end(kMallocViews));
792*6dbdd20aSAndroid Build Coastguard Worker } else if (base::StringView(heap_name) == "com.android.art") {
793*6dbdd20aSAndroid Build Coastguard Worker views.assign(std::begin(kJavaSamplesViews), std::end(kJavaSamplesViews));
794*6dbdd20aSAndroid Build Coastguard Worker } else {
795*6dbdd20aSAndroid Build Coastguard Worker views.assign(std::begin(kGenericViews), std::end(kGenericViews));
796*6dbdd20aSAndroid Build Coastguard Worker }
797*6dbdd20aSAndroid Build Coastguard Worker
798*6dbdd20aSAndroid Build Coastguard Worker std::vector<std::pair<std::string, std::string>> sample_types;
799*6dbdd20aSAndroid Build Coastguard Worker for (const View& view : views) {
800*6dbdd20aSAndroid Build Coastguard Worker sample_types.emplace_back(view.type, view.unit);
801*6dbdd20aSAndroid Build Coastguard Worker }
802*6dbdd20aSAndroid Build Coastguard Worker builder.WriteSampleTypes(sample_types);
803*6dbdd20aSAndroid Build Coastguard Worker
804*6dbdd20aSAndroid Build Coastguard Worker std::vector<Iterator> view_its =
805*6dbdd20aSAndroid Build Coastguard Worker BuildViewIterators(tp, upid, ts, heap_name, views);
806*6dbdd20aSAndroid Build Coastguard Worker std::string profile_proto;
807*6dbdd20aSAndroid Build Coastguard Worker if (WriteAllocations(&builder, &view_its)) {
808*6dbdd20aSAndroid Build Coastguard Worker profile_proto = builder.CompleteProfile(tp);
809*6dbdd20aSAndroid Build Coastguard Worker }
810*6dbdd20aSAndroid Build Coastguard Worker output->emplace_back(
811*6dbdd20aSAndroid Build Coastguard Worker SerializedProfile{ProfileType::kHeapProfile, profile_pid,
812*6dbdd20aSAndroid Build Coastguard Worker std::move(profile_proto), heap_name});
813*6dbdd20aSAndroid Build Coastguard Worker }
814*6dbdd20aSAndroid Build Coastguard Worker
815*6dbdd20aSAndroid Build Coastguard Worker if (!it.Status().ok()) {
816*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_DFATAL_OR_ELOG("Invalid iterator: %s",
817*6dbdd20aSAndroid Build Coastguard Worker it.Status().message().c_str());
818*6dbdd20aSAndroid Build Coastguard Worker return false;
819*6dbdd20aSAndroid Build Coastguard Worker }
820*6dbdd20aSAndroid Build Coastguard Worker if (any_fail) {
821*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_ELOG(
822*6dbdd20aSAndroid Build Coastguard Worker "One or more of your profiles had an issue. Please consult "
823*6dbdd20aSAndroid Build Coastguard Worker "https://perfetto.dev/docs/data-sources/"
824*6dbdd20aSAndroid Build Coastguard Worker "native-heap-profiler#troubleshooting");
825*6dbdd20aSAndroid Build Coastguard Worker }
826*6dbdd20aSAndroid Build Coastguard Worker return true;
827*6dbdd20aSAndroid Build Coastguard Worker }
828*6dbdd20aSAndroid Build Coastguard Worker } // namespace heap_profile
829*6dbdd20aSAndroid Build Coastguard Worker
830*6dbdd20aSAndroid Build Coastguard Worker namespace java_heap_profile {
831*6dbdd20aSAndroid Build Coastguard Worker struct View {
832*6dbdd20aSAndroid Build Coastguard Worker const char* type;
833*6dbdd20aSAndroid Build Coastguard Worker const char* unit;
834*6dbdd20aSAndroid Build Coastguard Worker const char* query;
835*6dbdd20aSAndroid Build Coastguard Worker };
836*6dbdd20aSAndroid Build Coastguard Worker
837*6dbdd20aSAndroid Build Coastguard Worker constexpr View kJavaAllocationViews[] = {
838*6dbdd20aSAndroid Build Coastguard Worker {"Total allocation count", "count", "count"},
839*6dbdd20aSAndroid Build Coastguard Worker {"Total allocation size", "bytes", "size"}};
840*6dbdd20aSAndroid Build Coastguard Worker
CreateHeapDumpFlameGraphQuery(const std::string & columns,const uint64_t upid,const uint64_t ts)841*6dbdd20aSAndroid Build Coastguard Worker std::string CreateHeapDumpFlameGraphQuery(const std::string& columns,
842*6dbdd20aSAndroid Build Coastguard Worker const uint64_t upid,
843*6dbdd20aSAndroid Build Coastguard Worker const uint64_t ts) {
844*6dbdd20aSAndroid Build Coastguard Worker std::string query = "SELECT " + columns + " ";
845*6dbdd20aSAndroid Build Coastguard Worker query += "FROM experimental_flamegraph(";
846*6dbdd20aSAndroid Build Coastguard Worker
847*6dbdd20aSAndroid Build Coastguard Worker const std::vector<std::string> query_params = {
848*6dbdd20aSAndroid Build Coastguard Worker // The type of the profile from which the flamegraph is being generated
849*6dbdd20aSAndroid Build Coastguard Worker // Always 'graph' for Java heap graphs.
850*6dbdd20aSAndroid Build Coastguard Worker "'graph'",
851*6dbdd20aSAndroid Build Coastguard Worker // Heapdump timestamp
852*6dbdd20aSAndroid Build Coastguard Worker std::to_string(ts),
853*6dbdd20aSAndroid Build Coastguard Worker // Timestamp constraints: not relevant and always null for Java heap
854*6dbdd20aSAndroid Build Coastguard Worker // graphs.
855*6dbdd20aSAndroid Build Coastguard Worker "NULL",
856*6dbdd20aSAndroid Build Coastguard Worker // The upid of the heap graph sample
857*6dbdd20aSAndroid Build Coastguard Worker std::to_string(upid),
858*6dbdd20aSAndroid Build Coastguard Worker // The upid group: not relevant and always null for Java heap graphs
859*6dbdd20aSAndroid Build Coastguard Worker "NULL",
860*6dbdd20aSAndroid Build Coastguard Worker // A regex for focusing on a particular node in the heapgraph
861*6dbdd20aSAndroid Build Coastguard Worker "NULL"};
862*6dbdd20aSAndroid Build Coastguard Worker
863*6dbdd20aSAndroid Build Coastguard Worker query += base::Join(query_params, ", ");
864*6dbdd20aSAndroid Build Coastguard Worker query += ")";
865*6dbdd20aSAndroid Build Coastguard Worker
866*6dbdd20aSAndroid Build Coastguard Worker return query;
867*6dbdd20aSAndroid Build Coastguard Worker }
868*6dbdd20aSAndroid Build Coastguard Worker
WriteAllocations(GProfileBuilder * builder,const std::unordered_map<int64_t,std::vector<int64_t>> & view_values)869*6dbdd20aSAndroid Build Coastguard Worker bool WriteAllocations(
870*6dbdd20aSAndroid Build Coastguard Worker GProfileBuilder* builder,
871*6dbdd20aSAndroid Build Coastguard Worker const std::unordered_map<int64_t, std::vector<int64_t>>& view_values) {
872*6dbdd20aSAndroid Build Coastguard Worker for (const auto& [id, values] : view_values) {
873*6dbdd20aSAndroid Build Coastguard Worker protozero::PackedVarInt sample_values;
874*6dbdd20aSAndroid Build Coastguard Worker for (const int64_t value : values) {
875*6dbdd20aSAndroid Build Coastguard Worker sample_values.Append(value);
876*6dbdd20aSAndroid Build Coastguard Worker }
877*6dbdd20aSAndroid Build Coastguard Worker if (!builder->AddSample(sample_values, id)) {
878*6dbdd20aSAndroid Build Coastguard Worker return false;
879*6dbdd20aSAndroid Build Coastguard Worker }
880*6dbdd20aSAndroid Build Coastguard Worker }
881*6dbdd20aSAndroid Build Coastguard Worker return true;
882*6dbdd20aSAndroid Build Coastguard Worker }
883*6dbdd20aSAndroid Build Coastguard Worker
884*6dbdd20aSAndroid Build Coastguard Worker // Extracts and interns the unique locations from the heap dump SQL tables.
885*6dbdd20aSAndroid Build Coastguard Worker //
886*6dbdd20aSAndroid Build Coastguard Worker // It uses experimental_flamegraph table to get normalized representation of
887*6dbdd20aSAndroid Build Coastguard Worker // the heap graph as a tree, which always takes the shortest path to the root.
888*6dbdd20aSAndroid Build Coastguard Worker //
889*6dbdd20aSAndroid Build Coastguard Worker // Approach:
890*6dbdd20aSAndroid Build Coastguard Worker // * First we iterate over all heap dump flamegraph rows and create a map
891*6dbdd20aSAndroid Build Coastguard Worker // of flamegraph item id -> flamegraph item parent_id, each flamechart
892*6dbdd20aSAndroid Build Coastguard Worker // item is converted to a Location where we populate Function name using
893*6dbdd20aSAndroid Build Coastguard Worker // the name of the class (as opposed to using actual call function as
894*6dbdd20aSAndroid Build Coastguard Worker // allocation call stack is not available for java heap dumps).
895*6dbdd20aSAndroid Build Coastguard Worker // Also populate view_values straightaway here to not iterate over the data
896*6dbdd20aSAndroid Build Coastguard Worker // again in the future.
897*6dbdd20aSAndroid Build Coastguard Worker // * For each location we iterate over all its parents until we find
898*6dbdd20aSAndroid Build Coastguard Worker // the root and use this list of locations as a 'callstack' (which is
899*6dbdd20aSAndroid Build Coastguard Worker // actually list of class names)
PreprocessLocationsForJavaHeap(trace_processor::TraceProcessor * tp,trace_processor::StringPool * interner,const std::vector<View> & views,std::unordered_map<int64_t,std::vector<int64_t>> & view_values_out,uint64_t upid,uint64_t ts)900*6dbdd20aSAndroid Build Coastguard Worker LocationTracker PreprocessLocationsForJavaHeap(
901*6dbdd20aSAndroid Build Coastguard Worker trace_processor::TraceProcessor* tp,
902*6dbdd20aSAndroid Build Coastguard Worker trace_processor::StringPool* interner,
903*6dbdd20aSAndroid Build Coastguard Worker const std::vector<View>& views,
904*6dbdd20aSAndroid Build Coastguard Worker std::unordered_map<int64_t, std::vector<int64_t>>& view_values_out,
905*6dbdd20aSAndroid Build Coastguard Worker uint64_t upid,
906*6dbdd20aSAndroid Build Coastguard Worker uint64_t ts) {
907*6dbdd20aSAndroid Build Coastguard Worker LocationTracker tracker;
908*6dbdd20aSAndroid Build Coastguard Worker
909*6dbdd20aSAndroid Build Coastguard Worker std::string columns;
910*6dbdd20aSAndroid Build Coastguard Worker for (const auto& view : views) {
911*6dbdd20aSAndroid Build Coastguard Worker columns += std::string(view.query) + ", ";
912*6dbdd20aSAndroid Build Coastguard Worker }
913*6dbdd20aSAndroid Build Coastguard Worker
914*6dbdd20aSAndroid Build Coastguard Worker const auto data_columns_count = static_cast<uint32_t>(views.size());
915*6dbdd20aSAndroid Build Coastguard Worker columns += "id, parent_id, name";
916*6dbdd20aSAndroid Build Coastguard Worker
917*6dbdd20aSAndroid Build Coastguard Worker const std::string query = CreateHeapDumpFlameGraphQuery(columns, upid, ts);
918*6dbdd20aSAndroid Build Coastguard Worker Iterator it = tp->ExecuteQuery(query);
919*6dbdd20aSAndroid Build Coastguard Worker
920*6dbdd20aSAndroid Build Coastguard Worker // flamegraph id -> flamegraph parent_id
921*6dbdd20aSAndroid Build Coastguard Worker std::unordered_map<int64_t, int64_t> parents;
922*6dbdd20aSAndroid Build Coastguard Worker // flamegraph id -> interned location id
923*6dbdd20aSAndroid Build Coastguard Worker std::unordered_map<int64_t, int64_t> interned_ids;
924*6dbdd20aSAndroid Build Coastguard Worker
925*6dbdd20aSAndroid Build Coastguard Worker // Create locations
926*6dbdd20aSAndroid Build Coastguard Worker while (it.Next()) {
927*6dbdd20aSAndroid Build Coastguard Worker const int64_t id = it.Get(data_columns_count).AsLong();
928*6dbdd20aSAndroid Build Coastguard Worker
929*6dbdd20aSAndroid Build Coastguard Worker const int64_t parent_id = it.Get(data_columns_count + 1).is_null()
930*6dbdd20aSAndroid Build Coastguard Worker ? -1
931*6dbdd20aSAndroid Build Coastguard Worker : it.Get(data_columns_count + 1).AsLong();
932*6dbdd20aSAndroid Build Coastguard Worker
933*6dbdd20aSAndroid Build Coastguard Worker auto name = it.Get(data_columns_count + 2).is_null()
934*6dbdd20aSAndroid Build Coastguard Worker ? ""
935*6dbdd20aSAndroid Build Coastguard Worker : it.Get(data_columns_count + 2).AsString();
936*6dbdd20aSAndroid Build Coastguard Worker
937*6dbdd20aSAndroid Build Coastguard Worker parents.emplace(id, parent_id);
938*6dbdd20aSAndroid Build Coastguard Worker
939*6dbdd20aSAndroid Build Coastguard Worker StringId func_name_id = interner->InternString(name);
940*6dbdd20aSAndroid Build Coastguard Worker Function func(func_name_id, StringId::Null(), StringId::Null());
941*6dbdd20aSAndroid Build Coastguard Worker auto interned_function_id = tracker.InternFunction(func);
942*6dbdd20aSAndroid Build Coastguard Worker
943*6dbdd20aSAndroid Build Coastguard Worker Location loc(/*map=*/0, /*func=*/interned_function_id, /*inlines=*/{});
944*6dbdd20aSAndroid Build Coastguard Worker auto interned_location_id = tracker.InternLocation(std::move(loc));
945*6dbdd20aSAndroid Build Coastguard Worker
946*6dbdd20aSAndroid Build Coastguard Worker interned_ids.emplace(id, interned_location_id);
947*6dbdd20aSAndroid Build Coastguard Worker
948*6dbdd20aSAndroid Build Coastguard Worker std::vector<int64_t> view_values_vector;
949*6dbdd20aSAndroid Build Coastguard Worker for (uint32_t i = 0; i < views.size(); ++i) {
950*6dbdd20aSAndroid Build Coastguard Worker view_values_vector.push_back(it.Get(i).AsLong());
951*6dbdd20aSAndroid Build Coastguard Worker }
952*6dbdd20aSAndroid Build Coastguard Worker
953*6dbdd20aSAndroid Build Coastguard Worker view_values_out.emplace(id, view_values_vector);
954*6dbdd20aSAndroid Build Coastguard Worker }
955*6dbdd20aSAndroid Build Coastguard Worker
956*6dbdd20aSAndroid Build Coastguard Worker if (!it.Status().ok()) {
957*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_DFATAL_OR_ELOG("Invalid iterator: %s",
958*6dbdd20aSAndroid Build Coastguard Worker it.Status().message().c_str());
959*6dbdd20aSAndroid Build Coastguard Worker return {};
960*6dbdd20aSAndroid Build Coastguard Worker }
961*6dbdd20aSAndroid Build Coastguard Worker
962*6dbdd20aSAndroid Build Coastguard Worker // Iterate over all known locations again and build root-first paths
963*6dbdd20aSAndroid Build Coastguard Worker // for every location
964*6dbdd20aSAndroid Build Coastguard Worker for (auto& parent : parents) {
965*6dbdd20aSAndroid Build Coastguard Worker std::vector<int64_t> path;
966*6dbdd20aSAndroid Build Coastguard Worker
967*6dbdd20aSAndroid Build Coastguard Worker int64_t current_parent_id = parent.first;
968*6dbdd20aSAndroid Build Coastguard Worker while (current_parent_id != -1) {
969*6dbdd20aSAndroid Build Coastguard Worker auto id_it = interned_ids.find(current_parent_id);
970*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_CHECK(id_it != interned_ids.end());
971*6dbdd20aSAndroid Build Coastguard Worker
972*6dbdd20aSAndroid Build Coastguard Worker auto parent_location_id = id_it->second;
973*6dbdd20aSAndroid Build Coastguard Worker path.push_back(parent_location_id);
974*6dbdd20aSAndroid Build Coastguard Worker
975*6dbdd20aSAndroid Build Coastguard Worker // Find parent of the parent
976*6dbdd20aSAndroid Build Coastguard Worker auto parent_id_it = parents.find(current_parent_id);
977*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_CHECK(parent_id_it != parents.end());
978*6dbdd20aSAndroid Build Coastguard Worker
979*6dbdd20aSAndroid Build Coastguard Worker current_parent_id = parent_id_it->second;
980*6dbdd20aSAndroid Build Coastguard Worker }
981*6dbdd20aSAndroid Build Coastguard Worker
982*6dbdd20aSAndroid Build Coastguard Worker // Reverse to make it root-first list
983*6dbdd20aSAndroid Build Coastguard Worker std::reverse(path.begin(), path.end());
984*6dbdd20aSAndroid Build Coastguard Worker
985*6dbdd20aSAndroid Build Coastguard Worker tracker.MaybeSetCallsiteLocations(parent.first, path);
986*6dbdd20aSAndroid Build Coastguard Worker }
987*6dbdd20aSAndroid Build Coastguard Worker
988*6dbdd20aSAndroid Build Coastguard Worker return tracker;
989*6dbdd20aSAndroid Build Coastguard Worker }
990*6dbdd20aSAndroid Build Coastguard Worker
TraceToHeapPprof(trace_processor::TraceProcessor * tp,std::vector<SerializedProfile> * output,uint64_t target_pid,const std::vector<uint64_t> & target_timestamps)991*6dbdd20aSAndroid Build Coastguard Worker bool TraceToHeapPprof(trace_processor::TraceProcessor* tp,
992*6dbdd20aSAndroid Build Coastguard Worker std::vector<SerializedProfile>* output,
993*6dbdd20aSAndroid Build Coastguard Worker uint64_t target_pid,
994*6dbdd20aSAndroid Build Coastguard Worker const std::vector<uint64_t>& target_timestamps) {
995*6dbdd20aSAndroid Build Coastguard Worker trace_processor::StringPool interner;
996*6dbdd20aSAndroid Build Coastguard Worker
997*6dbdd20aSAndroid Build Coastguard Worker // Find all heap graphs available in the trace and iterate over them
998*6dbdd20aSAndroid Build Coastguard Worker Iterator it = tp->ExecuteQuery(
999*6dbdd20aSAndroid Build Coastguard Worker "select distinct hgo.graph_sample_ts, hgo.upid, p.pid from "
1000*6dbdd20aSAndroid Build Coastguard Worker "heap_graph_object hgo join process p using (upid)");
1001*6dbdd20aSAndroid Build Coastguard Worker
1002*6dbdd20aSAndroid Build Coastguard Worker while (it.Next()) {
1003*6dbdd20aSAndroid Build Coastguard Worker uint64_t ts = static_cast<uint64_t>(it.Get(0).AsLong());
1004*6dbdd20aSAndroid Build Coastguard Worker uint64_t upid = static_cast<uint64_t>(it.Get(1).AsLong());
1005*6dbdd20aSAndroid Build Coastguard Worker uint64_t profile_pid = static_cast<uint64_t>(it.Get(2).AsLong());
1006*6dbdd20aSAndroid Build Coastguard Worker
1007*6dbdd20aSAndroid Build Coastguard Worker if ((target_pid > 0 && profile_pid != target_pid) ||
1008*6dbdd20aSAndroid Build Coastguard Worker (!target_timestamps.empty() &&
1009*6dbdd20aSAndroid Build Coastguard Worker std::find(target_timestamps.begin(), target_timestamps.end(), ts) ==
1010*6dbdd20aSAndroid Build Coastguard Worker target_timestamps.end())) {
1011*6dbdd20aSAndroid Build Coastguard Worker continue;
1012*6dbdd20aSAndroid Build Coastguard Worker }
1013*6dbdd20aSAndroid Build Coastguard Worker
1014*6dbdd20aSAndroid Build Coastguard Worker // flamegraph id -> view values
1015*6dbdd20aSAndroid Build Coastguard Worker std::unordered_map<int64_t, std::vector<int64_t>> view_values;
1016*6dbdd20aSAndroid Build Coastguard Worker
1017*6dbdd20aSAndroid Build Coastguard Worker std::vector<View> views;
1018*6dbdd20aSAndroid Build Coastguard Worker views.assign(std::begin(kJavaAllocationViews),
1019*6dbdd20aSAndroid Build Coastguard Worker std::end(kJavaAllocationViews));
1020*6dbdd20aSAndroid Build Coastguard Worker
1021*6dbdd20aSAndroid Build Coastguard Worker LocationTracker locations = PreprocessLocationsForJavaHeap(
1022*6dbdd20aSAndroid Build Coastguard Worker tp, &interner, views, view_values, upid, ts);
1023*6dbdd20aSAndroid Build Coastguard Worker
1024*6dbdd20aSAndroid Build Coastguard Worker GProfileBuilder builder(locations, &interner);
1025*6dbdd20aSAndroid Build Coastguard Worker
1026*6dbdd20aSAndroid Build Coastguard Worker std::vector<std::pair<std::string, std::string>> sample_types;
1027*6dbdd20aSAndroid Build Coastguard Worker for (const auto& view : views) {
1028*6dbdd20aSAndroid Build Coastguard Worker sample_types.emplace_back(view.type, view.unit);
1029*6dbdd20aSAndroid Build Coastguard Worker }
1030*6dbdd20aSAndroid Build Coastguard Worker builder.WriteSampleTypes(sample_types);
1031*6dbdd20aSAndroid Build Coastguard Worker
1032*6dbdd20aSAndroid Build Coastguard Worker std::string profile_proto;
1033*6dbdd20aSAndroid Build Coastguard Worker if (WriteAllocations(&builder, view_values)) {
1034*6dbdd20aSAndroid Build Coastguard Worker profile_proto = builder.CompleteProfile(tp, /*write_mappings=*/false);
1035*6dbdd20aSAndroid Build Coastguard Worker }
1036*6dbdd20aSAndroid Build Coastguard Worker
1037*6dbdd20aSAndroid Build Coastguard Worker output->emplace_back(SerializedProfile{ProfileType::kJavaHeapProfile,
1038*6dbdd20aSAndroid Build Coastguard Worker profile_pid,
1039*6dbdd20aSAndroid Build Coastguard Worker std::move(profile_proto), ""});
1040*6dbdd20aSAndroid Build Coastguard Worker }
1041*6dbdd20aSAndroid Build Coastguard Worker
1042*6dbdd20aSAndroid Build Coastguard Worker if (!it.Status().ok()) {
1043*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_DFATAL_OR_ELOG("Invalid iterator: %s",
1044*6dbdd20aSAndroid Build Coastguard Worker it.Status().message().c_str());
1045*6dbdd20aSAndroid Build Coastguard Worker return false;
1046*6dbdd20aSAndroid Build Coastguard Worker }
1047*6dbdd20aSAndroid Build Coastguard Worker
1048*6dbdd20aSAndroid Build Coastguard Worker return true;
1049*6dbdd20aSAndroid Build Coastguard Worker }
1050*6dbdd20aSAndroid Build Coastguard Worker } // namespace java_heap_profile
1051*6dbdd20aSAndroid Build Coastguard Worker
1052*6dbdd20aSAndroid Build Coastguard Worker namespace perf_profile {
1053*6dbdd20aSAndroid Build Coastguard Worker struct ProcessInfo {
1054*6dbdd20aSAndroid Build Coastguard Worker uint64_t pid;
1055*6dbdd20aSAndroid Build Coastguard Worker std::vector<uint64_t> utids;
1056*6dbdd20aSAndroid Build Coastguard Worker };
1057*6dbdd20aSAndroid Build Coastguard Worker
1058*6dbdd20aSAndroid Build Coastguard Worker // Returns a map of upid -> {pid, utids[]} for sampled processes.
GetProcessMap(trace_processor::TraceProcessor * tp)1059*6dbdd20aSAndroid Build Coastguard Worker static std::map<uint64_t, ProcessInfo> GetProcessMap(
1060*6dbdd20aSAndroid Build Coastguard Worker trace_processor::TraceProcessor* tp) {
1061*6dbdd20aSAndroid Build Coastguard Worker Iterator it = tp->ExecuteQuery(
1062*6dbdd20aSAndroid Build Coastguard Worker "select distinct process.upid, process.pid, thread.utid from perf_sample "
1063*6dbdd20aSAndroid Build Coastguard Worker "join thread using (utid) join process using (upid) where callsite_id is "
1064*6dbdd20aSAndroid Build Coastguard Worker "not null order by process.upid asc");
1065*6dbdd20aSAndroid Build Coastguard Worker std::map<uint64_t, ProcessInfo> process_map;
1066*6dbdd20aSAndroid Build Coastguard Worker while (it.Next()) {
1067*6dbdd20aSAndroid Build Coastguard Worker uint64_t upid = static_cast<uint64_t>(it.Get(0).AsLong());
1068*6dbdd20aSAndroid Build Coastguard Worker uint64_t pid = static_cast<uint64_t>(it.Get(1).AsLong());
1069*6dbdd20aSAndroid Build Coastguard Worker uint64_t utid = static_cast<uint64_t>(it.Get(2).AsLong());
1070*6dbdd20aSAndroid Build Coastguard Worker process_map[upid].pid = pid;
1071*6dbdd20aSAndroid Build Coastguard Worker process_map[upid].utids.push_back(utid);
1072*6dbdd20aSAndroid Build Coastguard Worker }
1073*6dbdd20aSAndroid Build Coastguard Worker if (!it.Status().ok()) {
1074*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_DFATAL_OR_ELOG("Invalid iterator: %s",
1075*6dbdd20aSAndroid Build Coastguard Worker it.Status().message().c_str());
1076*6dbdd20aSAndroid Build Coastguard Worker return {};
1077*6dbdd20aSAndroid Build Coastguard Worker }
1078*6dbdd20aSAndroid Build Coastguard Worker return process_map;
1079*6dbdd20aSAndroid Build Coastguard Worker }
1080*6dbdd20aSAndroid Build Coastguard Worker
LogTracePerfEventIssues(trace_processor::TraceProcessor * tp)1081*6dbdd20aSAndroid Build Coastguard Worker static void LogTracePerfEventIssues(trace_processor::TraceProcessor* tp) {
1082*6dbdd20aSAndroid Build Coastguard Worker std::optional<int64_t> stat = GetStatsEntry(tp, "perf_samples_skipped");
1083*6dbdd20aSAndroid Build Coastguard Worker if (!stat.has_value()) {
1084*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_DFATAL_OR_ELOG("Failed to look up perf_samples_skipped stat");
1085*6dbdd20aSAndroid Build Coastguard Worker } else if (stat.value() > 0) {
1086*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_ELOG(
1087*6dbdd20aSAndroid Build Coastguard Worker "Warning: the trace recorded %" PRIi64
1088*6dbdd20aSAndroid Build Coastguard Worker " skipped samples, which otherwise matched the tracing config. This "
1089*6dbdd20aSAndroid Build Coastguard Worker "would cause a process to be completely absent from the trace, but "
1090*6dbdd20aSAndroid Build Coastguard Worker "does *not* imply data loss in any of the output profiles.",
1091*6dbdd20aSAndroid Build Coastguard Worker stat.value());
1092*6dbdd20aSAndroid Build Coastguard Worker }
1093*6dbdd20aSAndroid Build Coastguard Worker
1094*6dbdd20aSAndroid Build Coastguard Worker stat = GetStatsEntry(tp, "perf_samples_skipped_dataloss");
1095*6dbdd20aSAndroid Build Coastguard Worker if (!stat.has_value()) {
1096*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_DFATAL_OR_ELOG(
1097*6dbdd20aSAndroid Build Coastguard Worker "Failed to look up perf_samples_skipped_dataloss stat");
1098*6dbdd20aSAndroid Build Coastguard Worker } else if (stat.value() > 0) {
1099*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_ELOG("DATA LOSS: the trace recorded %" PRIi64
1100*6dbdd20aSAndroid Build Coastguard Worker " lost perf samples (within traced_perf). This means that "
1101*6dbdd20aSAndroid Build Coastguard Worker "the trace is missing information, but it is not known "
1102*6dbdd20aSAndroid Build Coastguard Worker "which profile that affected.",
1103*6dbdd20aSAndroid Build Coastguard Worker stat.value());
1104*6dbdd20aSAndroid Build Coastguard Worker }
1105*6dbdd20aSAndroid Build Coastguard Worker
1106*6dbdd20aSAndroid Build Coastguard Worker // Check if any per-cpu ringbuffers encountered dataloss (as recorded by the
1107*6dbdd20aSAndroid Build Coastguard Worker // kernel).
1108*6dbdd20aSAndroid Build Coastguard Worker Iterator it = tp->ExecuteQuery(
1109*6dbdd20aSAndroid Build Coastguard Worker "select idx, value from stats where name == 'perf_cpu_lost_records' and "
1110*6dbdd20aSAndroid Build Coastguard Worker "value > 0 order by idx asc");
1111*6dbdd20aSAndroid Build Coastguard Worker while (it.Next()) {
1112*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_ELOG(
1113*6dbdd20aSAndroid Build Coastguard Worker "DATA LOSS: during the trace, the per-cpu kernel ring buffer for cpu "
1114*6dbdd20aSAndroid Build Coastguard Worker "%" PRIi64 " recorded %" PRIi64
1115*6dbdd20aSAndroid Build Coastguard Worker " lost samples. This means that the trace is missing information, "
1116*6dbdd20aSAndroid Build Coastguard Worker "but it is not known which profile that affected.",
1117*6dbdd20aSAndroid Build Coastguard Worker static_cast<int64_t>(it.Get(0).AsLong()),
1118*6dbdd20aSAndroid Build Coastguard Worker static_cast<int64_t>(it.Get(1).AsLong()));
1119*6dbdd20aSAndroid Build Coastguard Worker }
1120*6dbdd20aSAndroid Build Coastguard Worker if (!it.Status().ok()) {
1121*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_DFATAL_OR_ELOG("Invalid iterator: %s",
1122*6dbdd20aSAndroid Build Coastguard Worker it.Status().message().c_str());
1123*6dbdd20aSAndroid Build Coastguard Worker }
1124*6dbdd20aSAndroid Build Coastguard Worker }
1125*6dbdd20aSAndroid Build Coastguard Worker
1126*6dbdd20aSAndroid Build Coastguard Worker // TODO(rsavitski): decide whether errors in |AddSample| should result in an
1127*6dbdd20aSAndroid Build Coastguard Worker // empty profile (and/or whether they should make the overall conversion
1128*6dbdd20aSAndroid Build Coastguard Worker // unsuccessful). Furthermore, clarify the return value's semantics for both
1129*6dbdd20aSAndroid Build Coastguard Worker // perf and heap profiles.
TraceToPerfPprof(trace_processor::TraceProcessor * tp,std::vector<SerializedProfile> * output,bool annotate_frames,uint64_t target_pid)1130*6dbdd20aSAndroid Build Coastguard Worker static bool TraceToPerfPprof(trace_processor::TraceProcessor* tp,
1131*6dbdd20aSAndroid Build Coastguard Worker std::vector<SerializedProfile>* output,
1132*6dbdd20aSAndroid Build Coastguard Worker bool annotate_frames,
1133*6dbdd20aSAndroid Build Coastguard Worker uint64_t target_pid) {
1134*6dbdd20aSAndroid Build Coastguard Worker trace_processor::StringPool interner;
1135*6dbdd20aSAndroid Build Coastguard Worker LocationTracker locations =
1136*6dbdd20aSAndroid Build Coastguard Worker PreprocessLocations(tp, &interner, annotate_frames);
1137*6dbdd20aSAndroid Build Coastguard Worker
1138*6dbdd20aSAndroid Build Coastguard Worker LogTracePerfEventIssues(tp);
1139*6dbdd20aSAndroid Build Coastguard Worker
1140*6dbdd20aSAndroid Build Coastguard Worker // Aggregate samples by upid when building profiles.
1141*6dbdd20aSAndroid Build Coastguard Worker std::map<uint64_t, ProcessInfo> process_map = GetProcessMap(tp);
1142*6dbdd20aSAndroid Build Coastguard Worker for (const auto& p : process_map) {
1143*6dbdd20aSAndroid Build Coastguard Worker const ProcessInfo& process = p.second;
1144*6dbdd20aSAndroid Build Coastguard Worker
1145*6dbdd20aSAndroid Build Coastguard Worker if (target_pid != 0 && process.pid != target_pid)
1146*6dbdd20aSAndroid Build Coastguard Worker continue;
1147*6dbdd20aSAndroid Build Coastguard Worker
1148*6dbdd20aSAndroid Build Coastguard Worker GProfileBuilder builder(locations, &interner);
1149*6dbdd20aSAndroid Build Coastguard Worker builder.WriteSampleTypes({{"samples", "count"}});
1150*6dbdd20aSAndroid Build Coastguard Worker
1151*6dbdd20aSAndroid Build Coastguard Worker std::string query = "select callsite_id from perf_sample where utid in (" +
1152*6dbdd20aSAndroid Build Coastguard Worker AsCsvString(process.utids) +
1153*6dbdd20aSAndroid Build Coastguard Worker ") and callsite_id is not null order by ts asc;";
1154*6dbdd20aSAndroid Build Coastguard Worker
1155*6dbdd20aSAndroid Build Coastguard Worker protozero::PackedVarInt single_count_value;
1156*6dbdd20aSAndroid Build Coastguard Worker single_count_value.Append(1);
1157*6dbdd20aSAndroid Build Coastguard Worker
1158*6dbdd20aSAndroid Build Coastguard Worker Iterator it = tp->ExecuteQuery(query);
1159*6dbdd20aSAndroid Build Coastguard Worker while (it.Next()) {
1160*6dbdd20aSAndroid Build Coastguard Worker int64_t callsite_id = static_cast<int64_t>(it.Get(0).AsLong());
1161*6dbdd20aSAndroid Build Coastguard Worker builder.AddSample(single_count_value, callsite_id);
1162*6dbdd20aSAndroid Build Coastguard Worker }
1163*6dbdd20aSAndroid Build Coastguard Worker if (!it.Status().ok()) {
1164*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_DFATAL_OR_ELOG("Failed to iterate over samples: %s",
1165*6dbdd20aSAndroid Build Coastguard Worker it.Status().c_message());
1166*6dbdd20aSAndroid Build Coastguard Worker return false;
1167*6dbdd20aSAndroid Build Coastguard Worker }
1168*6dbdd20aSAndroid Build Coastguard Worker
1169*6dbdd20aSAndroid Build Coastguard Worker std::string profile_proto = builder.CompleteProfile(tp);
1170*6dbdd20aSAndroid Build Coastguard Worker output->emplace_back(SerializedProfile{
1171*6dbdd20aSAndroid Build Coastguard Worker ProfileType::kPerfProfile, process.pid, std::move(profile_proto), ""});
1172*6dbdd20aSAndroid Build Coastguard Worker }
1173*6dbdd20aSAndroid Build Coastguard Worker return true;
1174*6dbdd20aSAndroid Build Coastguard Worker }
1175*6dbdd20aSAndroid Build Coastguard Worker } // namespace perf_profile
1176*6dbdd20aSAndroid Build Coastguard Worker } // namespace
1177*6dbdd20aSAndroid Build Coastguard Worker
TraceToPprof(trace_processor::TraceProcessor * tp,std::vector<SerializedProfile> * output,ConversionMode mode,uint64_t flags,uint64_t pid,const std::vector<uint64_t> & timestamps)1178*6dbdd20aSAndroid Build Coastguard Worker bool TraceToPprof(trace_processor::TraceProcessor* tp,
1179*6dbdd20aSAndroid Build Coastguard Worker std::vector<SerializedProfile>* output,
1180*6dbdd20aSAndroid Build Coastguard Worker ConversionMode mode,
1181*6dbdd20aSAndroid Build Coastguard Worker uint64_t flags,
1182*6dbdd20aSAndroid Build Coastguard Worker uint64_t pid,
1183*6dbdd20aSAndroid Build Coastguard Worker const std::vector<uint64_t>& timestamps) {
1184*6dbdd20aSAndroid Build Coastguard Worker bool annotate_frames =
1185*6dbdd20aSAndroid Build Coastguard Worker flags & static_cast<uint64_t>(ConversionFlags::kAnnotateFrames);
1186*6dbdd20aSAndroid Build Coastguard Worker switch (mode) {
1187*6dbdd20aSAndroid Build Coastguard Worker case (ConversionMode::kHeapProfile):
1188*6dbdd20aSAndroid Build Coastguard Worker return heap_profile::TraceToHeapPprof(tp, output, annotate_frames, pid,
1189*6dbdd20aSAndroid Build Coastguard Worker timestamps);
1190*6dbdd20aSAndroid Build Coastguard Worker case (ConversionMode::kPerfProfile):
1191*6dbdd20aSAndroid Build Coastguard Worker return perf_profile::TraceToPerfPprof(tp, output, annotate_frames, pid);
1192*6dbdd20aSAndroid Build Coastguard Worker case (ConversionMode::kJavaHeapProfile):
1193*6dbdd20aSAndroid Build Coastguard Worker return java_heap_profile::TraceToHeapPprof(tp, output, pid, timestamps);
1194*6dbdd20aSAndroid Build Coastguard Worker }
1195*6dbdd20aSAndroid Build Coastguard Worker PERFETTO_FATAL("unknown conversion option"); // for gcc
1196*6dbdd20aSAndroid Build Coastguard Worker }
1197*6dbdd20aSAndroid Build Coastguard Worker
1198*6dbdd20aSAndroid Build Coastguard Worker } // namespace trace_to_text
1199*6dbdd20aSAndroid Build Coastguard Worker } // namespace perfetto
1200