1
2 #include "perf_data_converter.h"
3
4 #include <algorithm>
5 #include <limits>
6 #include <map>
7 #include <memory>
8 #include <set>
9 #include <unordered_map>
10
11 #include <android-base/logging.h>
12 #include <android-base/macros.h>
13 #include <android-base/strings.h>
14 #include <perf_data_utils.h>
15 #include <perf_parser.h>
16 #include <perf_protobuf_io.h>
17
18 #include "perfprofd_record.pb.h"
19 #include "perf_data.pb.h"
20
21 #include "map_utils.h"
22 #include "quipper_helper.h"
23 #include "symbolizer.h"
24
25 using std::map;
26
27 namespace android {
28 namespace perfprofd {
29
30 namespace {
31
AddSymbolInfo(PerfprofdRecord * record,::quipper::PerfParser & perf_parser,::perfprofd::Symbolizer * symbolizer)32 void AddSymbolInfo(PerfprofdRecord* record,
33 ::quipper::PerfParser& perf_parser,
34 ::perfprofd::Symbolizer* symbolizer) {
35 std::unordered_set<std::string> filenames_w_build_id;
36 for (auto& perf_build_id : record->perf_data().build_ids()) {
37 filenames_w_build_id.insert(perf_build_id.filename());
38 }
39
40 std::unordered_set<std::string> files_wo_build_id;
41 {
42 quipper::MmapEventIterator it(record->perf_data());
43 for (; it != it.end(); ++it) {
44 const ::quipper::PerfDataProto_MMapEvent* mmap_event = &it->mmap_event();
45 if (!mmap_event->has_filename() || !mmap_event->has_start() || !mmap_event->has_len()) {
46 // Don't care.
47 continue;
48 }
49 if (filenames_w_build_id.count(mmap_event->filename()) == 0) {
50 files_wo_build_id.insert(mmap_event->filename());
51 }
52 }
53 }
54 if (files_wo_build_id.empty()) {
55 return;
56 }
57
58 struct Dso {
59 uint64_t min_vaddr;
60 RangeMap<std::string, uint64_t> symbols;
61 explicit Dso(uint64_t min_vaddr_in) : min_vaddr(min_vaddr_in) {
62 }
63 };
64 std::unordered_map<std::string, Dso> files;
65
66 auto it = record->perf_data().events().begin();
67 auto end = record->perf_data().events().end();
68 auto parsed_it = perf_parser.parsed_events().begin();
69 auto parsed_end = perf_parser.parsed_events().end();
70 for (; it != end; ++it, ++parsed_it) {
71 CHECK(parsed_it != parsed_end);
72 if (!it->has_sample_event()) {
73 continue;
74 }
75
76 const ::quipper::PerfDataProto_SampleEvent& sample_event = it->sample_event();
77
78 if (android::base::kEnableDChecks) {
79 // Check that the parsed_event and sample_event are consistent.
80 CHECK_EQ(parsed_it->callchain.size(), sample_event.callchain_size());
81 }
82
83 auto check_address = [&](const std::string& dso_name, uint64_t offset) {
84 if (files_wo_build_id.count(dso_name) == 0) {
85 return;
86 }
87
88 // OK, that's a hit in the mmap segment (w/o build id).
89
90 Dso* dso_data;
91 {
92 auto dso_it = files.find(dso_name);
93 constexpr uint64_t kNoMinAddr = std::numeric_limits<uint64_t>::max();
94 if (dso_it == files.end()) {
95 uint64_t min_vaddr;
96 bool has_min_vaddr = symbolizer->GetMinExecutableVAddr(dso_name, &min_vaddr);
97 if (!has_min_vaddr) {
98 min_vaddr = kNoMinAddr;
99 }
100 auto it = files.emplace(dso_name, Dso(min_vaddr));
101 dso_data = &it.first->second;
102 } else {
103 dso_data = &dso_it->second;
104 }
105 if (dso_data->min_vaddr == kNoMinAddr) {
106 return;
107 }
108 }
109
110 // TODO: Is min_vaddr necessary here?
111 const uint64_t file_addr = offset;
112
113 std::string symbol = symbolizer->Decode(dso_name, file_addr);
114 if (symbol.empty()) {
115 return;
116 }
117
118 dso_data->symbols.Insert(symbol, file_addr);
119 };
120 if (sample_event.has_ip() && parsed_it->dso_and_offset.dso_info_ != nullptr) {
121 check_address(parsed_it->dso_and_offset.dso_info_->name, parsed_it->dso_and_offset.offset_);
122 }
123 if (sample_event.callchain_size() > 0) {
124 for (auto& callchain_data: parsed_it->callchain) {
125 if (callchain_data.dso_info_ == nullptr) {
126 continue;
127 }
128 check_address(callchain_data.dso_info_->name, callchain_data.offset_);
129 }
130 }
131 }
132
133 if (!files.empty()) {
134 // We have extra symbol info, create proto messages now.
135 for (auto& file_data : files) {
136 const std::string& filename = file_data.first;
137 const Dso& dso = file_data.second;
138 if (dso.symbols.empty()) {
139 continue;
140 }
141
142 PerfprofdRecord_SymbolInfo* symbol_info = record->add_symbol_info();
143 symbol_info->set_filename(filename);
144 symbol_info->set_filename_md5_prefix(::quipper::Md5Prefix(filename));
145 symbol_info->set_min_vaddr(dso.min_vaddr);
146 for (auto& aggr_sym : dso.symbols) {
147 PerfprofdRecord_SymbolInfo_Symbol* symbol = symbol_info->add_symbols();
148 symbol->set_addr(*aggr_sym.second.offsets.begin());
149 symbol->set_size(*aggr_sym.second.offsets.rbegin() - *aggr_sym.second.offsets.begin() + 1);
150 symbol->set_name(aggr_sym.second.symbol);
151 symbol->set_name_md5_prefix(::quipper::Md5Prefix(aggr_sym.second.symbol));
152 }
153 }
154 }
155 }
156
157 } // namespace
158
159 PerfprofdRecord*
RawPerfDataToAndroidPerfProfile(const string & perf_file,::perfprofd::Symbolizer * symbolizer)160 RawPerfDataToAndroidPerfProfile(const string &perf_file,
161 ::perfprofd::Symbolizer* symbolizer) {
162 std::unique_ptr<PerfprofdRecord> ret(new PerfprofdRecord());
163 ret->set_id(0); // TODO.
164
165 ::quipper::PerfParserOptions options = {};
166 options.do_remap = true;
167 options.discard_unused_events = true;
168 options.read_missing_buildids = true;
169
170 ::quipper::PerfDataProto* perf_data = ret->mutable_perf_data();
171
172 ::quipper::PerfReader reader;
173 if (!reader.ReadFile(perf_file)) return nullptr;
174
175 ::quipper::PerfParser parser(&reader, options);
176 if (!parser.ParseRawEvents()) return nullptr;
177
178 if (!reader.Serialize(perf_data)) return nullptr;
179
180 // Append parser stats to protobuf.
181 ::quipper::PerfSerializer::SerializeParserStats(parser.stats(), perf_data);
182
183 // TODO: Symbolization.
184 if (symbolizer != nullptr) {
185 AddSymbolInfo(ret.get(), parser, symbolizer);
186 }
187
188 return ret.release();
189 }
190
191 } // namespace perfprofd
192 } // namespace android
193