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