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