xref: /aosp_15_r20/external/perfetto/src/traceconv/pprof_reader.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "src/traceconv/pprof_reader.h"
18 
19 #include <cinttypes>
20 #include <fstream>
21 
22 #include "perfetto/ext/base/file_utils.h"
23 
24 namespace perfetto::pprof {
25 
26 using namespace third_party::perftools::profiles::gen;
27 
PprofProfileReader(const std::string & path)28 PprofProfileReader::PprofProfileReader(const std::string& path) {
29   std::string pprof_contents;
30   base::ReadFile(path, &pprof_contents);
31   profile_.ParseFromString(pprof_contents);
32 }
33 
get_sample_count() const34 uint64_t PprofProfileReader::get_sample_count() const {
35   return static_cast<uint64_t>(profile_.sample_size());
36 }
37 
get_string_index(const std::string & str) const38 int64_t PprofProfileReader::get_string_index(const std::string& str) const {
39   const auto it = std::find(profile_.string_table().begin(),
40                             profile_.string_table().end(), str);
41 
42   if (it == profile_.string_table().end()) {
43     PERFETTO_FATAL("String %s not found in string table", str.c_str());
44   }
45 
46   return std::distance(profile_.string_table().begin(), it);
47 }
48 
get_string_by_index(const uint64_t string_index) const49 std::string PprofProfileReader::get_string_by_index(
50     const uint64_t string_index) const {
51   if (string_index >= profile_.string_table().size()) {
52     PERFETTO_FATAL("String %" PRIu64 " is out of range in string table",
53                    string_index);
54   }
55 
56   return profile_.string_table()[string_index];
57 }
58 
find_location_id(const std::string & function_name) const59 uint64_t PprofProfileReader::find_location_id(
60     const std::string& function_name) const {
61   const int64_t function_string_id = get_string_index(function_name);
62 
63   // Find a function based on function_name
64   uint64_t function_id = 0;
65   bool found_function_id = false;
66 
67   for (const auto& function : profile_.function()) {
68     if (function.name() == function_string_id) {
69       function_id = function.id();
70       found_function_id = true;
71     }
72   }
73 
74   if (!found_function_id) {
75     PERFETTO_FATAL("Function %s not found", function_name.c_str());
76   }
77 
78   // Find a location for the function
79   for (const auto& location : profile_.location()) {
80     for (const auto& line : location.line()) {
81       if (line.function_id() == function_id) {
82         return location.id();
83       }
84     }
85   }
86 
87   PERFETTO_FATAL("Location for function %s not found", function_name.c_str());
88 }
89 
find_location(const uint64_t location_id) const90 Location PprofProfileReader::find_location(const uint64_t location_id) const {
91   const auto it = std::find_if(
92       profile_.location().begin(), profile_.location().end(),
93       [location_id](const Location& loc) { return loc.id() == location_id; });
94 
95   if (it != profile_.location().end()) {
96     return *it;
97   }
98 
99   PERFETTO_FATAL("Location with id %" PRIu64 " not found", location_id);
100 }
101 
find_function(const uint64_t function_id) const102 Function PprofProfileReader::find_function(const uint64_t function_id) const {
103   const auto it = std::find_if(
104       profile_.function().begin(), profile_.function().end(),
105       [function_id](const Function& fun) { return fun.id() == function_id; });
106 
107   if (it != profile_.function().end()) {
108     return *it;
109   }
110 
111   PERFETTO_FATAL("Function with id %" PRIu64 " not found", function_id);
112 }
113 
get_sample_function_names(const Sample & sample) const114 std::vector<std::string> PprofProfileReader::get_sample_function_names(
115     const Sample& sample) const {
116   std::vector<std::string> function_names;
117   for (const auto location_id : sample.location_id()) {
118     const auto location = find_location(location_id);
119 
120     for (const auto& line : location.line()) {
121       Function function = find_function(line.function_id());
122       std::string function_name =
123           get_string_by_index(static_cast<uint64_t>(function.name()));
124       function_names.push_back(function_name);
125     }
126   }
127 
128   return function_names;
129 }
130 
get_samples(const std::string & last_function_name) const131 std::vector<Sample> PprofProfileReader::get_samples(
132     const std::string& last_function_name) const {
133   const uint64_t location_id = find_location_id(last_function_name);
134 
135   std::vector<Sample> samples;
136   for (const auto& sample : profile_.sample()) {
137     if (sample.location_id_size() == 0) {
138       continue;
139     }
140 
141     // Get the first location id from the iterator as they are stored inverted
142     const uint64_t last_location_id = sample.location_id()[0];
143 
144     if (last_location_id == location_id) {
145       samples.push_back(sample);
146     }
147   }
148 
149   return samples;
150 }
151 
get_sample_value_index(const std::string & value_name) const152 uint64_t PprofProfileReader::get_sample_value_index(
153     const std::string& value_name) const {
154   const int64_t value_name_string_index = get_string_index(value_name);
155 
156   const auto it =
157       std::find_if(profile_.sample_type().begin(), profile_.sample_type().end(),
158                    [value_name_string_index](const auto& sample_type) {
159                      return sample_type.type() == value_name_string_index;
160                    });
161 
162   if (it != profile_.sample_type().end()) {
163     return static_cast<uint64_t>(
164         std::distance(profile_.sample_type().begin(), it));
165   }
166 
167   PERFETTO_FATAL("Can't find value type with name \"%s\"", value_name.c_str());
168 }
169 
get_samples_value_sum(const std::string & last_function_name,const std::string & value_name) const170 int64_t PprofProfileReader::get_samples_value_sum(
171     const std::string& last_function_name,
172     const std::string& value_name) const {
173   int64_t total = 0;
174   const auto samples = get_samples(last_function_name);
175   const auto value_index = get_sample_value_index(value_name);
176   for (const auto& sample : samples) {
177     total += sample.value()[value_index];
178   }
179   return total;
180 }
181 }  // namespace perfetto::pprof
182