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