xref: /aosp_15_r20/external/perfetto/src/traceconv/pprof_builder.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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