blob: 649cd22dd617f178a7c9df236572e5296fbbde83 [file] [log] [blame]
Primiano Tucci94c47f02019-12-05 03:13:11 +00001/*
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
Carlos Caballero Grolimund8470d8f2023-04-05 15:37:11 +000017#include <algorithm>
18#include <vector>
19
Hector Dearman7fead1c2019-06-25 00:47:38 +010020#include "perfetto/ext/base/file_utils.h"
Carlos Caballero Grolimund8470d8f2023-04-05 15:37:11 +000021#include "perfetto/ext/base/flat_hash_map.h"
Hector Dearman7fead1c2019-06-25 00:47:38 +010022#include "perfetto/ext/base/scoped_file.h"
Carlos Caballero Grolimund8470d8f2023-04-05 15:37:11 +000023#include "perfetto/protozero/field.h"
24#include "perfetto/protozero/packed_repeated_fields.h"
25#include "perfetto/protozero/proto_decoder.h"
26#include "perfetto/protozero/proto_utils.h"
27#include "perfetto/protozero/scattered_heap_buffer.h"
28#include "src/trace_processor/importers/proto/trace.descriptor.h"
29#include "src/trace_processor/util/proto_profiler.h"
30
31#include "protos/third_party/pprof/profile.pbzero.h"
Hector Dearman7fead1c2019-06-25 00:47:38 +010032
33namespace perfetto {
34namespace protoprofile {
35namespace {
36
Carlos Caballero Grolimund8470d8f2023-04-05 15:37:11 +000037class PprofProfileComputer {
38 public:
39 std::string Compute(const uint8_t* ptr,
40 size_t size,
41 const std::string& message_type,
42 trace_processor::DescriptorPool* pool);
Hector Dearman7fead1c2019-06-25 00:47:38 +010043
Carlos Caballero Grolimund8470d8f2023-04-05 15:37:11 +000044 private:
45 int InternString(const std::string& str);
46 int InternLocation(const std::string& str);
Hector Dearman7fead1c2019-06-25 00:47:38 +010047
Carlos Caballero Grolimund8470d8f2023-04-05 15:37:11 +000048 // Interned strings:
49 std::vector<std::string> strings_;
50 base::FlatHashMap<std::string, int> string_to_id_;
51
52 // Interned 'locations', each location is a single frame of the stack.
53 base::FlatHashMap<std::string, int> locations_;
54};
55
56int PprofProfileComputer::InternString(const std::string& s) {
57 auto val = string_to_id_.Find(s);
58 if (val) {
59 return *val;
60 }
61 strings_.push_back(s);
62 int id = static_cast<int>(strings_.size() - 1);
63 string_to_id_[s] = id;
64 return id;
65}
66
67int PprofProfileComputer::InternLocation(const std::string& s) {
68 auto val = locations_.Find(s);
69 if (val) {
70 return *val;
71 }
72 int id = static_cast<int>(locations_.size()) + 1;
73 locations_[s] = id;
74 return id;
75}
76
77std::string PprofProfileComputer::Compute(
78 const uint8_t* ptr,
79 size_t size,
80 const std::string& message_type,
81 trace_processor::DescriptorPool* pool) {
82 PERFETTO_CHECK(InternString("") == 0);
83
84 trace_processor::util::SizeProfileComputer computer(pool, message_type);
85 computer.Reset(ptr, size);
86
87 using PathToSamplesMap = std::unordered_map<
88 trace_processor::util::SizeProfileComputer::FieldPath,
89 std::vector<size_t>,
90 trace_processor::util::SizeProfileComputer::FieldPathHasher>;
91 PathToSamplesMap field_path_to_samples;
92 for (auto sample = computer.GetNext(); sample; sample = computer.GetNext()) {
93 field_path_to_samples[computer.GetPath()].push_back(*sample);
94 }
95
96 protozero::HeapBuffered<third_party::perftools::profiles::pbzero::Profile>
97 profile;
98
99 auto* sample_type = profile->add_sample_type();
100 sample_type->set_type(InternString("protos"));
101 sample_type->set_unit(InternString("count"));
102
103 sample_type = profile->add_sample_type();
104 sample_type->set_type(InternString("max_size"));
105 sample_type->set_unit(InternString("bytes"));
106
107 sample_type = profile->add_sample_type();
108 sample_type->set_type(InternString("min_size"));
109 sample_type->set_unit(InternString("bytes"));
110
111 sample_type = profile->add_sample_type();
112 sample_type->set_type(InternString("median"));
113 sample_type->set_unit(InternString("bytes"));
114
115 sample_type = profile->add_sample_type();
116 sample_type->set_type(InternString("total_size"));
117 sample_type->set_unit(InternString("bytes"));
118
119 // For each unique field path we've seen write out the stats:
120 for (auto& entry : field_path_to_samples) {
121 std::vector<std::string> field_path;
122 for (const auto& field : entry.first) {
123 if (field.has_field_name())
124 field_path.push_back(field.field_name());
125 field_path.push_back(field.type_name());
126 }
127 std::vector<size_t>& samples = entry.second;
128
129 protozero::PackedVarInt location_ids;
130 auto* sample = profile->add_sample();
131 for (auto loc_it = field_path.rbegin(); loc_it != field_path.rend();
132 ++loc_it) {
133 location_ids.Append(InternLocation(*loc_it));
134 }
135 sample->set_location_id(location_ids);
136
137 std::sort(samples.begin(), samples.end());
138 size_t count = samples.size();
139 size_t total_size = 0;
140 size_t max_size = samples[count - 1];
141 size_t min_size = samples[0];
142 size_t median_size = samples[count / 2];
143 for (size_t i = 0; i < count; ++i)
144 total_size += samples[i];
145 // These have to be in the same order as the sample types above:
146 protozero::PackedVarInt values;
147 values.Append(static_cast<int64_t>(count));
148 values.Append(static_cast<int64_t>(max_size));
149 values.Append(static_cast<int64_t>(min_size));
150 values.Append(static_cast<int64_t>(median_size));
151 values.Append(static_cast<int64_t>(total_size));
152 sample->set_value(values);
153 }
154
155 // The proto profile has a two step mapping where samples are associated with
156 // locations which in turn are associated to functions. We don't currently
157 // distinguish them so we make a 1:1 mapping between the locations and the
158 // functions:
159 for (auto it = locations_.GetIterator(); it; ++it) {
160 auto* location = profile->add_location();
161 location->set_id(static_cast<uint64_t>(it.value()));
162
163 auto* line = location->add_line();
164 line->set_function_id(static_cast<uint64_t>(it.value()));
165
166 auto* function = profile->add_function();
167 function->set_id(static_cast<uint64_t>(it.value()));
168 function->set_name(InternString(it.key()));
169 }
170 // Finally the string table. We intern more strings above, so this has to be
171 // last.
172 for (size_t i = 0; i < strings_.size(); i++) {
173 profile->add_string_table(strings_[i]);
174 }
175 return profile.SerializeAsString();
176}
Hector Dearman7fead1c2019-06-25 00:47:38 +0100177
Hector Dearman7fead1c2019-06-25 00:47:38 +0100178int PrintUsage(int, const char** argv) {
179 fprintf(stderr, "Usage: %s INPUT_PATH OUTPUT_PATH\n", argv[0]);
180 return 1;
181}
182
183int Main(int argc, const char** argv) {
184 if (argc != 3)
185 return PrintUsage(argc, argv);
186
187 const char* input_path = argv[1];
188 const char* output_path = argv[2];
189
Carlos Caballero Grolimund8470d8f2023-04-05 15:37:11 +0000190 base::ScopedFile proto_fd = base::OpenFile(input_path, O_RDONLY);
191 if (!proto_fd) {
Hector Dearman7fead1c2019-06-25 00:47:38 +0100192 PERFETTO_ELOG("Could not open input path (%s)", input_path);
193 return 1;
194 }
195
Carlos Caballero Grolimund8470d8f2023-04-05 15:37:11 +0000196 std::string s;
197 base::ReadFileDescriptor(proto_fd.get(), &s);
Hector Dearman7fead1c2019-06-25 00:47:38 +0100198
Carlos Caballero Grolimund8470d8f2023-04-05 15:37:11 +0000199 trace_processor::DescriptorPool pool;
200 base::Status status = pool.AddFromFileDescriptorSet(kTraceDescriptor.data(),
201 kTraceDescriptor.size());
202 if (!status.ok()) {
203 PERFETTO_ELOG("Could not add Trace proto descriptor: %s",
204 status.c_message());
205 return 1;
206 }
207
208 const uint8_t* start = reinterpret_cast<const uint8_t*>(s.data());
209 size_t size = s.size();
Hector Dearman7fead1c2019-06-25 00:47:38 +0100210
Hector Dearman7fead1c2019-06-25 00:47:38 +0100211 base::ScopedFile output_fd =
212 base::OpenFile(output_path, O_WRONLY | O_TRUNC | O_CREAT, 0600);
213 if (!output_fd) {
214 PERFETTO_ELOG("Could not open output path (%s)", output_path);
215 return 1;
216 }
Carlos Caballero Grolimund8470d8f2023-04-05 15:37:11 +0000217 PprofProfileComputer computer;
218 std::string out =
219 computer.Compute(start, size, ".perfetto.protos.Trace", &pool);
220 base::WriteAll(output_fd.get(), out.data(), out.size());
Hector Dearman7fead1c2019-06-25 00:47:38 +0100221 base::FlushFile(output_fd.get());
222
Hector Dearman7fead1c2019-06-25 00:47:38 +0100223 return 0;
224}
225
226} // namespace
227} // namespace protoprofile
228} // namespace perfetto
229
230int main(int argc, const char** argv) {
231 return perfetto::protoprofile::Main(argc, argv);
232}