• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "perfetto/profiling/pprof_builder.h"
18 
19 #include <cxxabi.h>
20 #include <inttypes.h>
21 
22 #include <algorithm>
23 #include <map>
24 #include <set>
25 #include <utility>
26 #include <vector>
27 
28 #include "tools/trace_to_text/utils.h"
29 
30 #include "perfetto/base/logging.h"
31 #include "perfetto/base/time.h"
32 #include "perfetto/ext/base/string_utils.h"
33 #include "perfetto/ext/base/utils.h"
34 #include "perfetto/protozero/packed_repeated_fields.h"
35 #include "perfetto/protozero/scattered_heap_buffer.h"
36 #include "perfetto/trace_processor/trace_processor.h"
37 
38 #include "src/profiling/symbolizer/symbolize_database.h"
39 #include "src/profiling/symbolizer/symbolizer.h"
40 
41 #include "protos/perfetto/trace/trace.pbzero.h"
42 #include "protos/perfetto/trace/trace_packet.pbzero.h"
43 #include "protos/third_party/pprof/profile.pbzero.h"
44 
45 namespace perfetto {
46 namespace trace_to_text {
47 
48 namespace {
49 
50 using ::protozero::proto_utils::kMessageLengthFieldSize;
51 using ::protozero::proto_utils::MakeTagLengthDelimited;
52 using ::protozero::proto_utils::WriteVarInt;
53 
54 struct View {
55   const char* type;
56   const char* unit;
57   const char* aggregator;
58   const char* filter;
59 };
60 
MaybeDemangle(std::string * name)61 void MaybeDemangle(std::string* name) {
62   int ignored;
63   char* data = abi::__cxa_demangle(name->c_str(), nullptr, nullptr, &ignored);
64   if (data) {
65     *name = data;
66     free(data);
67   }
68 }
69 
70 const View kSpaceView{"space", "bytes", "SUM(size)", nullptr};
71 const View kAllocSpaceView{"alloc_space", "bytes", "SUM(size)", "size > 0"};
72 const View kAllocObjectsView{"alloc_objects", "count", "sum(count)",
73                              "size > 0"};
74 const View kObjectsView{"objects", "count", "SUM(count)", nullptr};
75 
76 const View kViews[] = {kAllocObjectsView, kObjectsView, kAllocSpaceView,
77                        kSpaceView};
78 
79 using Iterator = trace_processor::TraceProcessor::Iterator;
80 
81 constexpr const char* kQueryProfiles =
82     "select distinct hpa.upid, hpa.ts, p.pid from heap_profile_allocation hpa, "
83     "process p where p.upid = hpa.upid;";
84 
GetStatsInt(trace_processor::TraceProcessor * tp,const std::string & name,uint64_t pid)85 int64_t GetStatsInt(trace_processor::TraceProcessor* tp,
86                     const std::string& name,
87                     uint64_t pid) {
88   auto it = tp->ExecuteQuery("SELECT value from stats where name = '" + name +
89                              "' AND idx = " + std::to_string(pid));
90   if (!it.Next()) {
91     if (!it.Status().ok()) {
92       PERFETTO_DFATAL_OR_ELOG("Invalid iterator: %s",
93                               it.Status().message().c_str());
94       return -1;
95     }
96     // TODO(fmayer): Remove this case once we always get an entry in the stats
97     // table.
98     return 0;
99   }
100   return it.Get(0).AsLong();
101 }
102 
VerifyPIDStats(trace_processor::TraceProcessor * tp,uint64_t pid)103 bool VerifyPIDStats(trace_processor::TraceProcessor* tp, uint64_t pid) {
104   bool success = true;
105   int64_t stat = GetStatsInt(tp, "heapprofd_buffer_corrupted", pid);
106   if (stat == -1) {
107     PERFETTO_DFATAL_OR_ELOG("Failed to get heapprofd_buffer_corrupted stat");
108   } else if (stat > 0) {
109     success = false;
110     PERFETTO_ELOG("WARNING: The profile for %" PRIu64
111                   " ended early due to a buffer corruption."
112                   " THIS IS ALWAYS A BUG IN HEAPPROFD OR"
113                   " CLIENT MEMORY CORRUPTION.",
114                   pid);
115   }
116   stat = GetStatsInt(tp, "heapprofd_buffer_overran", pid);
117   if (stat == -1) {
118     PERFETTO_DFATAL_OR_ELOG("Failed to get heapprofd_buffer_overran stat");
119   } else if (stat > 0) {
120     success = false;
121     PERFETTO_ELOG("WARNING: The profile for %" PRIu64
122                   " ended early due to a buffer overrun.",
123                   pid);
124   }
125 
126   stat = GetStatsInt(tp, "heapprofd_rejected_concurrent", pid);
127   if (stat == -1) {
128     PERFETTO_DFATAL_OR_ELOG("Failed to get heapprofd_rejected_concurrent stat");
129   } else if (stat > 0) {
130     success = false;
131     PERFETTO_ELOG("WARNING: The profile for %" PRIu64
132                   " was rejected due to a concurrent profile.",
133                   pid);
134   }
135   return success;
136 }
137 
138 struct Callsite {
139   int64_t id;
140   int64_t frame_id;
141 };
142 
143 // Return map from callsite_id to list of frame_ids that make up the callstack.
GetCallsiteToFrames(trace_processor::TraceProcessor * tp)144 std::vector<std::vector<int64_t>> GetCallsiteToFrames(
145     trace_processor::TraceProcessor* tp) {
146   Iterator count_it =
147       tp->ExecuteQuery("select count(*) from stack_profile_callsite;");
148   if (!count_it.Next()) {
149     PERFETTO_DFATAL_OR_ELOG("Failed to get number of callsites: %s",
150                             count_it.Status().message().c_str());
151     return {};
152   }
153   int64_t count = count_it.Get(0).AsLong();
154 
155   Iterator it = tp->ExecuteQuery(
156       "select id, parent_id, frame_id from stack_profile_callsite order by "
157       "depth;");
158   std::vector<std::vector<int64_t>> result(static_cast<size_t>(count));
159   while (it.Next()) {
160     int64_t id = it.Get(0).AsLong();
161     int64_t frame_id = it.Get(2).AsLong();
162     std::vector<int64_t>& path = result[static_cast<size_t>(id)];
163     path.push_back(frame_id);
164 
165     auto parent_id_value = it.Get(1);
166     if (!parent_id_value.is_null()) {
167       const std::vector<int64_t>& parent_path =
168           result[static_cast<size_t>(parent_id_value.AsLong())];
169       path.insert(path.end(), parent_path.begin(), parent_path.end());
170     }
171   }
172 
173   if (!it.Status().ok()) {
174     PERFETTO_DFATAL_OR_ELOG("Invalid iterator: %s",
175                             it.Status().message().c_str());
176     return {};
177   }
178   return result;
179 }
180 
181 struct Line {
182   int64_t symbol_id;
183   uint32_t line_number;
184 };
185 
GetSymbolSetIdToLines(trace_processor::TraceProcessor * tp)186 std::map<int64_t, std::vector<Line>> GetSymbolSetIdToLines(
187     trace_processor::TraceProcessor* tp) {
188   std::map<int64_t, std::vector<Line>> result;
189   Iterator it = tp->ExecuteQuery(
190       "SELECT symbol_set_id, id, line_number FROM stack_profile_symbol;");
191   while (it.Next()) {
192     int64_t symbol_set_id = it.Get(0).AsLong();
193     int64_t id = it.Get(1).AsLong();
194     int64_t line_number = it.Get(2).AsLong();
195     result[symbol_set_id].emplace_back(
196         Line{id, static_cast<uint32_t>(line_number)});
197   }
198 
199   if (!it.Status().ok()) {
200     PERFETTO_DFATAL_OR_ELOG("Invalid iterator: %s",
201                             it.Status().message().c_str());
202     return {};
203   }
204   return result;
205 }
206 
207 class GProfileBuilder {
208  public:
GProfileBuilder(const std::vector<std::vector<int64_t>> & callsite_to_frames,const std::map<int64_t,std::vector<Line>> & symbol_set_id_to_lines,int64_t max_symbol_id)209   GProfileBuilder(
210       const std::vector<std::vector<int64_t>>& callsite_to_frames,
211       const std::map<int64_t, std::vector<Line>>& symbol_set_id_to_lines,
212       int64_t max_symbol_id)
213       : callsite_to_frames_(callsite_to_frames),
214         symbol_set_id_to_lines_(symbol_set_id_to_lines),
215         max_symbol_id_(max_symbol_id) {
216     // The pprof format expects the first entry in the string table to be the
217     // empty string.
218     int64_t empty_id = Intern("");
219     PERFETTO_CHECK(empty_id == 0);
220   }
221 
BuildViewIterators(trace_processor::TraceProcessor * tp,uint64_t upid,uint64_t ts)222   std::vector<Iterator> BuildViewIterators(trace_processor::TraceProcessor* tp,
223                                            uint64_t upid,
224                                            uint64_t ts) {
225     std::vector<Iterator> view_its;
226     for (size_t i = 0; i < base::ArraySize(kViews); ++i) {
227       const View& v = kViews[i];
228       std::string query = "SELECT hpa.callsite_id ";
229       query += ", " + std::string(v.aggregator) +
230                " FROM heap_profile_allocation hpa ";
231       // TODO(fmayer): Figure out where negative callsite_id comes from.
232       query += "WHERE hpa.callsite_id >= 0 ";
233       query += "AND hpa.upid = " + std::to_string(upid) + " ";
234       query += "AND hpa.ts <= " + std::to_string(ts) + " ";
235       if (v.filter)
236         query += "AND " + std::string(v.filter) + " ";
237       query += "GROUP BY hpa.callsite_id;";
238       view_its.emplace_back(tp->ExecuteQuery(query));
239     }
240     return view_its;
241   }
242 
WriteAllocations(std::vector<Iterator> * view_its,std::set<int64_t> * seen_frames)243   bool WriteAllocations(std::vector<Iterator>* view_its,
244                         std::set<int64_t>* seen_frames) {
245     for (;;) {
246       bool all_next = true;
247       bool any_next = false;
248       for (size_t i = 0; i < base::ArraySize(kViews); ++i) {
249         Iterator& it = (*view_its)[i];
250         bool next = it.Next();
251         if (!it.Status().ok()) {
252           PERFETTO_DFATAL_OR_ELOG("Invalid view iterator: %s",
253                                   it.Status().message().c_str());
254           return false;
255         }
256         all_next = all_next && next;
257         any_next = any_next || next;
258       }
259 
260       if (!all_next) {
261         PERFETTO_DCHECK(!any_next);
262         break;
263       }
264 
265       auto* gsample = result_->add_sample();
266       protozero::PackedVarInt sample_values;
267       for (size_t i = 0; i < base::ArraySize(kViews); ++i) {
268         int64_t callstack_id = (*view_its)[i].Get(0).AsLong();
269         if (i == 0) {
270           auto frames = FramesForCallstack(callstack_id);
271           if (frames.empty())
272             return false;
273           protozero::PackedVarInt location_ids;
274           for (int64_t frame : frames)
275             location_ids.Append(ToPprofId(frame));
276           gsample->set_location_id(location_ids);
277           seen_frames->insert(frames.cbegin(), frames.cend());
278         } else {
279           if (callstack_id != (*view_its)[i].Get(0).AsLong()) {
280             PERFETTO_DFATAL_OR_ELOG("Wrong callstack.");
281             return false;
282           }
283         }
284         sample_values.Append((*view_its)[i].Get(1).AsLong());
285       }
286       gsample->set_value(sample_values);
287     }
288     return true;
289   }
290 
WriteMappings(trace_processor::TraceProcessor * tp,const std::set<int64_t> seen_mappings)291   bool WriteMappings(trace_processor::TraceProcessor* tp,
292                      const std::set<int64_t> seen_mappings) {
293     Iterator mapping_it = tp->ExecuteQuery(
294         "SELECT id, exact_offset, start, end, name "
295         "FROM stack_profile_mapping;");
296     size_t mappings_no = 0;
297     while (mapping_it.Next()) {
298       int64_t id = mapping_it.Get(0).AsLong();
299       if (seen_mappings.find(id) == seen_mappings.end())
300         continue;
301       ++mappings_no;
302       auto interned_filename = Intern(mapping_it.Get(4).AsString());
303       auto* gmapping = result_->add_mapping();
304       gmapping->set_id(ToPprofId(id));
305       // Do not set the build_id here to avoid downstream services
306       // trying to symbolize (e.g. b/141735056)
307       gmapping->set_file_offset(
308           static_cast<uint64_t>(mapping_it.Get(1).AsLong()));
309       gmapping->set_memory_start(
310           static_cast<uint64_t>(mapping_it.Get(2).AsLong()));
311       gmapping->set_memory_limit(
312           static_cast<uint64_t>(mapping_it.Get(3).AsLong()));
313       gmapping->set_filename(interned_filename);
314     }
315     if (!mapping_it.Status().ok()) {
316       PERFETTO_DFATAL_OR_ELOG("Invalid mapping iterator: %s",
317                               mapping_it.Status().message().c_str());
318       return false;
319     }
320     if (mappings_no != seen_mappings.size()) {
321       PERFETTO_DFATAL_OR_ELOG("Missing mappings.");
322       return false;
323     }
324     return true;
325   }
326 
WriteSymbols(trace_processor::TraceProcessor * tp,const std::set<int64_t> & seen_symbol_ids)327   bool WriteSymbols(trace_processor::TraceProcessor* tp,
328                     const std::set<int64_t>& seen_symbol_ids) {
329     Iterator symbol_it = tp->ExecuteQuery(
330         "SELECT id, name, source_file FROM stack_profile_symbol");
331     size_t symbols_no = 0;
332     while (symbol_it.Next()) {
333       int64_t id = symbol_it.Get(0).AsLong();
334       if (seen_symbol_ids.find(id) == seen_symbol_ids.end())
335         continue;
336       ++symbols_no;
337       const std::string& name = symbol_it.Get(1).AsString();
338       std::string demangled_name = name;
339       MaybeDemangle(&demangled_name);
340 
341       auto interned_demangled_name = Intern(demangled_name);
342       auto interned_system_name = Intern(name);
343       auto interned_filename = Intern(symbol_it.Get(2).AsString());
344       auto* gfunction = result_->add_function();
345       gfunction->set_id(ToPprofId(id));
346       gfunction->set_name(interned_demangled_name);
347       gfunction->set_system_name(interned_system_name);
348       gfunction->set_filename(interned_filename);
349     }
350 
351     if (!symbol_it.Status().ok()) {
352       PERFETTO_DFATAL_OR_ELOG("Invalid iterator: %s",
353                               symbol_it.Status().message().c_str());
354       return false;
355     }
356 
357     if (symbols_no != seen_symbol_ids.size()) {
358       PERFETTO_DFATAL_OR_ELOG("Missing symbols.");
359       return false;
360     }
361     return true;
362   }
363 
WriteFrames(trace_processor::TraceProcessor * tp,const std::set<int64_t> & seen_frames,std::set<int64_t> * seen_mappings,std::set<int64_t> * seen_symbol_ids)364   bool WriteFrames(trace_processor::TraceProcessor* tp,
365                    const std::set<int64_t>& seen_frames,
366                    std::set<int64_t>* seen_mappings,
367                    std::set<int64_t>* seen_symbol_ids) {
368     Iterator frame_it = tp->ExecuteQuery(
369         "SELECT spf.id, spf.name, spf.mapping, spf.rel_pc, spf.symbol_set_id "
370         "FROM stack_profile_frame spf;");
371     size_t frames_no = 0;
372     while (frame_it.Next()) {
373       int64_t frame_id = frame_it.Get(0).AsLong();
374       if (seen_frames.find(frame_id) == seen_frames.end())
375         continue;
376       frames_no++;
377       std::string frame_name = frame_it.Get(1).AsString();
378       int64_t mapping_id = frame_it.Get(2).AsLong();
379       int64_t rel_pc = frame_it.Get(3).AsLong();
380       base::Optional<int64_t> symbol_set_id;
381       if (!frame_it.Get(4).is_null())
382         symbol_set_id = frame_it.Get(4).AsLong();
383 
384       seen_mappings->emplace(mapping_id);
385       auto* glocation = result_->add_location();
386       glocation->set_id(ToPprofId(frame_id));
387       glocation->set_mapping_id(ToPprofId(mapping_id));
388       // TODO(fmayer): Convert to abspc.
389       // relpc + (mapping.start - (mapping.exact_offset -
390       //                           mapping.start_offset)).
391       glocation->set_address(static_cast<uint64_t>(rel_pc));
392       if (symbol_set_id) {
393         for (const Line& line : LineForSymbolSetId(*symbol_set_id)) {
394           seen_symbol_ids->emplace(line.symbol_id);
395           auto* gline = glocation->add_line();
396           gline->set_line(line.line_number);
397           gline->set_function_id(ToPprofId(line.symbol_id));
398         }
399       } else {
400         int64_t synthesized_symbol_id = ++max_symbol_id_;
401         std::string demangled_name = frame_name;
402         MaybeDemangle(&demangled_name);
403 
404         auto* gline = glocation->add_line();
405         gline->set_line(0);
406         gline->set_function_id(ToPprofId(synthesized_symbol_id));
407 
408         auto interned_demangled_name = Intern(demangled_name);
409         auto interned_system_name = Intern(frame_name);
410         auto* gfunction = result_->add_function();
411         gfunction->set_id(ToPprofId(synthesized_symbol_id));
412         gfunction->set_name(interned_demangled_name);
413         gfunction->set_system_name(interned_system_name);
414       }
415     }
416 
417     if (!frame_it.Status().ok()) {
418       PERFETTO_DFATAL_OR_ELOG("Invalid iterator: %s",
419                               frame_it.Status().message().c_str());
420       return false;
421     }
422     if (frames_no != seen_frames.size()) {
423       PERFETTO_DFATAL_OR_ELOG("Missing frames.");
424       return false;
425     }
426     return true;
427   }
428 
ToPprofId(int64_t id)429   uint64_t ToPprofId(int64_t id) {
430     PERFETTO_DCHECK(id >= 0);
431     return static_cast<uint64_t>(id) + 1;
432   }
433 
WriteSampleTypes()434   void WriteSampleTypes() {
435     for (size_t i = 0; i < base::ArraySize(kViews); ++i) {
436       Intern(kViews[i].type);
437       Intern(kViews[i].unit);
438     }
439 
440     for (size_t i = 0; i < base::ArraySize(kViews); ++i) {
441       auto* sample_type = result_->add_sample_type();
442       sample_type->set_type(Intern(kViews[i].type));
443       sample_type->set_unit(Intern(kViews[i].unit));
444     }
445   }
446 
GenerateGProfile(trace_processor::TraceProcessor * tp,uint64_t upid,uint64_t ts)447   std::string GenerateGProfile(trace_processor::TraceProcessor* tp,
448                                uint64_t upid,
449                                uint64_t ts) {
450     std::set<int64_t> seen_frames;
451     std::set<int64_t> seen_mappings;
452     std::set<int64_t> seen_symbol_ids;
453 
454     std::vector<Iterator> view_its = BuildViewIterators(tp, upid, ts);
455 
456     WriteSampleTypes();
457     if (!WriteAllocations(&view_its, &seen_frames))
458       return {};
459     if (!WriteFrames(tp, seen_frames, &seen_mappings, &seen_symbol_ids))
460       return {};
461     if (!WriteMappings(tp, seen_mappings))
462       return {};
463     if (!WriteSymbols(tp, seen_symbol_ids))
464       return {};
465     return result_.SerializeAsString();
466   }
467 
FramesForCallstack(int64_t callstack_id)468   const std::vector<int64_t>& FramesForCallstack(int64_t callstack_id) {
469     size_t callsite_idx = static_cast<size_t>(callstack_id);
470     PERFETTO_CHECK(callstack_id >= 0 &&
471                    callsite_idx < callsite_to_frames_.size());
472     return callsite_to_frames_[callsite_idx];
473   }
474 
LineForSymbolSetId(int64_t symbol_set_id)475   const std::vector<Line>& LineForSymbolSetId(int64_t symbol_set_id) {
476     auto it = symbol_set_id_to_lines_.find(symbol_set_id);
477     if (it == symbol_set_id_to_lines_.end())
478       return empty_line_vector_;
479     return it->second;
480   }
481 
Intern(const std::string & s)482   int64_t Intern(const std::string& s) {
483     auto it = string_table_.find(s);
484     if (it == string_table_.end()) {
485       std::tie(it, std::ignore) =
486           string_table_.emplace(s, string_table_.size());
487       result_->add_string_table(s);
488     }
489     return it->second;
490   }
491 
492  private:
493   protozero::HeapBuffered<third_party::perftools::profiles::pbzero::Profile>
494       result_;
495   std::map<std::string, int64_t> string_table_;
496   const std::vector<std::vector<int64_t>>& callsite_to_frames_;
497   const std::map<int64_t, std::vector<Line>>& symbol_set_id_to_lines_;
498   const std::vector<Line> empty_line_vector_;
499   int64_t max_symbol_id_;
500 };
501 
502 }  // namespace
503 
TraceToPprof(std::istream * input,std::vector<SerializedProfile> * output,profiling::Symbolizer * symbolizer,uint64_t pid,const std::vector<uint64_t> & timestamps)504 bool TraceToPprof(std::istream* input,
505                   std::vector<SerializedProfile>* output,
506                   profiling::Symbolizer* symbolizer,
507                   uint64_t pid,
508                   const std::vector<uint64_t>& timestamps) {
509   trace_processor::Config config;
510   std::unique_ptr<trace_processor::TraceProcessor> tp =
511       trace_processor::TraceProcessor::CreateInstance(config);
512 
513   if (!ReadTrace(tp.get(), input))
514     return false;
515 
516   tp->NotifyEndOfFile();
517   return TraceToPprof(tp.get(), output, symbolizer, pid, timestamps);
518 }
519 
TraceToPprof(trace_processor::TraceProcessor * tp,std::vector<SerializedProfile> * output,profiling::Symbolizer * symbolizer,uint64_t pid,const std::vector<uint64_t> & timestamps)520 bool TraceToPprof(trace_processor::TraceProcessor* tp,
521                   std::vector<SerializedProfile>* output,
522                   profiling::Symbolizer* symbolizer,
523                   uint64_t pid,
524                   const std::vector<uint64_t>& timestamps) {
525   if (symbolizer) {
526     profiling::SymbolizeDatabase(
527         tp, symbolizer, [tp](const std::string& trace_proto) {
528           std::unique_ptr<uint8_t[]> buf(new uint8_t[trace_proto.size()]);
529           memcpy(buf.get(), trace_proto.data(), trace_proto.size());
530           auto status = tp->Parse(std::move(buf), trace_proto.size());
531           if (!status.ok()) {
532             PERFETTO_DFATAL_OR_ELOG("Failed to parse: %s",
533                                     status.message().c_str());
534             return;
535           }
536         });
537   }
538 
539   tp->NotifyEndOfFile();
540   auto max_symbol_id_it =
541       tp->ExecuteQuery("SELECT MAX(id) from stack_profile_symbol");
542   if (!max_symbol_id_it.Next()) {
543     PERFETTO_DFATAL_OR_ELOG("Failed to get max symbol set id: %s",
544                             max_symbol_id_it.Status().message().c_str());
545     return false;
546   }
547 
548   int64_t max_symbol_id = max_symbol_id_it.Get(0).AsLong();
549   const auto callsite_to_frames = GetCallsiteToFrames(tp);
550   const auto symbol_set_id_to_lines = GetSymbolSetIdToLines(tp);
551 
552   bool any_fail = false;
553   Iterator it = tp->ExecuteQuery(kQueryProfiles);
554   while (it.Next()) {
555     GProfileBuilder builder(callsite_to_frames, symbol_set_id_to_lines,
556                             max_symbol_id);
557     uint64_t upid = static_cast<uint64_t>(it.Get(0).AsLong());
558     uint64_t ts = static_cast<uint64_t>(it.Get(1).AsLong());
559     uint64_t profile_pid = static_cast<uint64_t>(it.Get(2).AsLong());
560     if ((pid > 0 && profile_pid != pid) ||
561         (!timestamps.empty() && std::find(timestamps.begin(), timestamps.end(),
562                                           ts) == timestamps.end())) {
563       continue;
564     }
565 
566     if (!VerifyPIDStats(tp, pid))
567       any_fail = true;
568 
569     std::string pid_query = "select pid from process where upid = ";
570     pid_query += std::to_string(upid) + ";";
571     Iterator pid_it = tp->ExecuteQuery(pid_query);
572     PERFETTO_CHECK(pid_it.Next());
573 
574     std::string profile_proto = builder.GenerateGProfile(tp, upid, ts);
575     output->emplace_back(SerializedProfile{
576         static_cast<uint64_t>(pid_it.Get(0).AsLong()), profile_proto});
577   }
578   if (any_fail) {
579     PERFETTO_ELOG(
580         "One or more of your profiles had an issue. Please consult "
581         "https://docs.perfetto.dev/#/heapprofd?id=troubleshooting.");
582   }
583   if (!it.Status().ok()) {
584     PERFETTO_DFATAL_OR_ELOG("Invalid iterator: %s",
585                             it.Status().message().c_str());
586     return false;
587   }
588   return true;
589 }
590 
TraceToPprof(std::istream * input,std::vector<SerializedProfile> * output,uint64_t pid,const std::vector<uint64_t> & timestamps)591 bool TraceToPprof(std::istream* input,
592                   std::vector<SerializedProfile>* output,
593                   uint64_t pid,
594                   const std::vector<uint64_t>& timestamps) {
595   return TraceToPprof(input, output, nullptr, pid, timestamps);
596 }
597 
598 }  // namespace trace_to_text
599 }  // namespace perfetto
600