1 /*
2 * Copyright (C) 2015 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 "record_file.h"
18
19 #include <fcntl.h>
20 #include <string.h>
21 #include <set>
22 #include <vector>
23
24 #include <android-base/logging.h>
25
26 #include "event_attr.h"
27 #include "record.h"
28 #include "utils.h"
29
30 using namespace PerfFileFormat;
31
32 namespace PerfFileFormat {
33
34 static const std::map<int, std::string> feature_name_map = {
35 {FEAT_TRACING_DATA, "tracing_data"},
36 {FEAT_BUILD_ID, "build_id"},
37 {FEAT_HOSTNAME, "hostname"},
38 {FEAT_OSRELEASE, "osrelease"},
39 {FEAT_VERSION, "version"},
40 {FEAT_ARCH, "arch"},
41 {FEAT_NRCPUS, "nrcpus"},
42 {FEAT_CPUDESC, "cpudesc"},
43 {FEAT_CPUID, "cpuid"},
44 {FEAT_TOTAL_MEM, "total_mem"},
45 {FEAT_CMDLINE, "cmdline"},
46 {FEAT_EVENT_DESC, "event_desc"},
47 {FEAT_CPU_TOPOLOGY, "cpu_topology"},
48 {FEAT_NUMA_TOPOLOGY, "numa_topology"},
49 {FEAT_BRANCH_STACK, "branch_stack"},
50 {FEAT_PMU_MAPPINGS, "pmu_mappings"},
51 {FEAT_GROUP_DESC, "group_desc"},
52 {FEAT_FILE, "file"},
53 {FEAT_META_INFO, "meta_info"},
54 };
55
GetFeatureName(int feature_id)56 std::string GetFeatureName(int feature_id) {
57 auto it = feature_name_map.find(feature_id);
58 return it == feature_name_map.end() ? "" : it->second;
59 }
60
GetFeatureId(const std::string & feature_name)61 int GetFeatureId(const std::string& feature_name) {
62 for (auto& pair : feature_name_map) {
63 if (pair.second == feature_name) {
64 return pair.first;
65 }
66 }
67 return -1;
68 }
69
70 } // namespace PerfFileFormat
71
CreateInstance(const std::string & filename)72 std::unique_ptr<RecordFileReader> RecordFileReader::CreateInstance(const std::string& filename) {
73 std::string mode = std::string("rb") + CLOSE_ON_EXEC_MODE;
74 FILE* fp = fopen(filename.c_str(), mode.c_str());
75 if (fp == nullptr) {
76 PLOG(ERROR) << "failed to open record file '" << filename << "'";
77 return nullptr;
78 }
79 auto reader = std::unique_ptr<RecordFileReader>(new RecordFileReader(filename, fp));
80 if (!reader->ReadHeader() || !reader->ReadAttrSection() ||
81 !reader->ReadFeatureSectionDescriptors()) {
82 return nullptr;
83 }
84 return reader;
85 }
86
RecordFileReader(const std::string & filename,FILE * fp)87 RecordFileReader::RecordFileReader(const std::string& filename, FILE* fp)
88 : filename_(filename), record_fp_(fp), event_id_pos_in_sample_records_(0),
89 event_id_reverse_pos_in_non_sample_records_(0), read_record_size_(0) {
90 }
91
~RecordFileReader()92 RecordFileReader::~RecordFileReader() {
93 if (record_fp_ != nullptr) {
94 Close();
95 }
96 }
97
Close()98 bool RecordFileReader::Close() {
99 bool result = true;
100 if (fclose(record_fp_) != 0) {
101 PLOG(ERROR) << "failed to close record file '" << filename_ << "'";
102 result = false;
103 }
104 record_fp_ = nullptr;
105 return result;
106 }
107
ReadHeader()108 bool RecordFileReader::ReadHeader() {
109 if (!Read(&header_, sizeof(header_))) {
110 return false;
111 }
112 if (memcmp(header_.magic, PERF_MAGIC, sizeof(header_.magic)) != 0) {
113 LOG(ERROR) << filename_ << " is not a valid profiling record file.";
114 return false;
115 }
116 return true;
117 }
118
ReadAttrSection()119 bool RecordFileReader::ReadAttrSection() {
120 size_t attr_count = header_.attrs.size / header_.attr_size;
121 if (header_.attr_size != sizeof(FileAttr)) {
122 LOG(DEBUG) << "attr size (" << header_.attr_size << ") in " << filename_
123 << " doesn't match expected size (" << sizeof(FileAttr) << ")";
124 }
125 if (attr_count == 0) {
126 LOG(ERROR) << "no attr in file " << filename_;
127 return false;
128 }
129 if (fseek(record_fp_, header_.attrs.offset, SEEK_SET) != 0) {
130 PLOG(ERROR) << "fseek() failed";
131 return false;
132 }
133 for (size_t i = 0; i < attr_count; ++i) {
134 std::vector<char> buf(header_.attr_size);
135 if (!Read(buf.data(), buf.size())) {
136 return false;
137 }
138 // The size of perf_event_attr is changing between different linux kernel versions.
139 // Make sure we copy correct data to memory.
140 FileAttr attr;
141 memset(&attr, 0, sizeof(attr));
142 size_t section_desc_size = sizeof(attr.ids);
143 size_t perf_event_attr_size = header_.attr_size - section_desc_size;
144 memcpy(&attr.attr, &buf[0], std::min(sizeof(attr.attr), perf_event_attr_size));
145 memcpy(&attr.ids, &buf[perf_event_attr_size], section_desc_size);
146 file_attrs_.push_back(attr);
147 }
148 if (file_attrs_.size() > 1) {
149 std::vector<perf_event_attr> attrs;
150 for (const auto& file_attr : file_attrs_) {
151 attrs.push_back(file_attr.attr);
152 }
153 if (!GetCommonEventIdPositionsForAttrs(attrs, &event_id_pos_in_sample_records_,
154 &event_id_reverse_pos_in_non_sample_records_)) {
155 return false;
156 }
157 }
158 for (size_t i = 0; i < file_attrs_.size(); ++i) {
159 std::vector<uint64_t> ids;
160 if (!ReadIdsForAttr(file_attrs_[i], &ids)) {
161 return false;
162 }
163 event_ids_for_file_attrs_.push_back(ids);
164 for (auto id : ids) {
165 event_id_to_attr_map_[id] = i;
166 }
167 }
168 return true;
169 }
170
ReadFeatureSectionDescriptors()171 bool RecordFileReader::ReadFeatureSectionDescriptors() {
172 std::vector<int> features;
173 for (size_t i = 0; i < sizeof(header_.features); ++i) {
174 for (size_t j = 0; j < 8; ++j) {
175 if (header_.features[i] & (1 << j)) {
176 features.push_back(i * 8 + j);
177 }
178 }
179 }
180 uint64_t feature_section_offset = header_.data.offset + header_.data.size;
181 if (fseek(record_fp_, feature_section_offset, SEEK_SET) != 0) {
182 PLOG(ERROR) << "fseek() failed";
183 return false;
184 }
185 for (const auto& id : features) {
186 SectionDesc desc;
187 if (!Read(&desc, sizeof(desc))) {
188 return false;
189 }
190 feature_section_descriptors_.emplace(id, desc);
191 }
192 return true;
193 }
194
ReadIdsForAttr(const FileAttr & attr,std::vector<uint64_t> * ids)195 bool RecordFileReader::ReadIdsForAttr(const FileAttr& attr, std::vector<uint64_t>* ids) {
196 size_t id_count = attr.ids.size / sizeof(uint64_t);
197 if (fseek(record_fp_, attr.ids.offset, SEEK_SET) != 0) {
198 PLOG(ERROR) << "fseek() failed";
199 return false;
200 }
201 ids->resize(id_count);
202 if (!Read(ids->data(), attr.ids.size)) {
203 return false;
204 }
205 return true;
206 }
207
ReadDataSection(const std::function<bool (std::unique_ptr<Record>)> & callback,bool sorted)208 bool RecordFileReader::ReadDataSection(
209 const std::function<bool(std::unique_ptr<Record>)>& callback, bool sorted) {
210 std::unique_ptr<Record> record;
211 while (ReadRecord(record, sorted)) {
212 if (record == nullptr) {
213 return true;
214 }
215 if (!callback(std::move(record))) {
216 return false;
217 }
218 }
219 return false;
220 }
221
ReadRecord(std::unique_ptr<Record> & record,bool sorted)222 bool RecordFileReader::ReadRecord(std::unique_ptr<Record>& record,
223 bool sorted) {
224 if (read_record_size_ == 0) {
225 if (fseek(record_fp_, header_.data.offset, SEEK_SET) != 0) {
226 PLOG(ERROR) << "fseek() failed";
227 return false;
228 }
229 bool has_timestamp = true;
230 for (const auto& attr : file_attrs_) {
231 if (!IsTimestampSupported(attr.attr)) {
232 has_timestamp = false;
233 break;
234 }
235 }
236 record_cache_.reset(new RecordCache(has_timestamp));
237 }
238 record = nullptr;
239 while (read_record_size_ < header_.data.size && record == nullptr) {
240 record = ReadRecord(&read_record_size_);
241 if (record == nullptr) {
242 return false;
243 }
244 if (record->type() == SIMPLE_PERF_RECORD_EVENT_ID) {
245 ProcessEventIdRecord(*static_cast<EventIdRecord*>(record.get()));
246 } else if (record->type() == PERF_RECORD_SAMPLE) {
247 SampleRecord* r = static_cast<SampleRecord*>(record.get());
248 // Although we have removed ip == 0 callchains when recording dwarf based callgraph,
249 // stack frame based callgraph can also generate ip == 0 callchains. Remove them here
250 // to avoid caller's effort.
251 if (r->sample_type & PERF_SAMPLE_CALLCHAIN) {
252 size_t i;
253 for (i = 0; i < r->callchain_data.ip_nr; ++i) {
254 if (r->callchain_data.ips[i] == 0) {
255 break;
256 }
257 }
258 r->callchain_data.ip_nr = i;
259 }
260 }
261 if (sorted) {
262 record_cache_->Push(std::move(record));
263 record = record_cache_->Pop();
264 }
265 }
266 if (record == nullptr) {
267 record = record_cache_->ForcedPop();
268 }
269 return true;
270 }
271
ReadRecord(uint64_t * nbytes_read)272 std::unique_ptr<Record> RecordFileReader::ReadRecord(uint64_t* nbytes_read) {
273 char header_buf[Record::header_size()];
274 if (!Read(header_buf, Record::header_size())) {
275 return nullptr;
276 }
277 RecordHeader header(header_buf);
278 std::unique_ptr<char[]> p;
279 if (header.type == SIMPLE_PERF_RECORD_SPLIT) {
280 // Read until meeting a RECORD_SPLIT_END record.
281 std::vector<char> buf;
282 size_t cur_size = 0;
283 char header_buf[Record::header_size()];
284 while (header.type == SIMPLE_PERF_RECORD_SPLIT) {
285 size_t bytes_to_read = header.size - Record::header_size();
286 buf.resize(cur_size + bytes_to_read);
287 if (!Read(&buf[cur_size], bytes_to_read)) {
288 return nullptr;
289 }
290 cur_size += bytes_to_read;
291 *nbytes_read += header.size;
292 if (!Read(header_buf, Record::header_size())) {
293 return nullptr;
294 }
295 header = RecordHeader(header_buf);
296 }
297 if (header.type != SIMPLE_PERF_RECORD_SPLIT_END) {
298 LOG(ERROR) << "SPLIT records are not followed by a SPLIT_END record.";
299 return nullptr;
300 }
301 *nbytes_read += header.size;
302 header = RecordHeader(buf.data());
303 p.reset(new char[header.size]);
304 memcpy(p.get(), buf.data(), buf.size());
305 } else {
306 p.reset(new char[header.size]);
307 memcpy(p.get(), header_buf, Record::header_size());
308 if (header.size > Record::header_size()) {
309 if (!Read(p.get() + Record::header_size(), header.size - Record::header_size())) {
310 return nullptr;
311 }
312 }
313 *nbytes_read += header.size;
314 }
315
316 const perf_event_attr* attr = &file_attrs_[0].attr;
317 if (file_attrs_.size() > 1 && header.type < PERF_RECORD_USER_DEFINED_TYPE_START) {
318 bool has_event_id = false;
319 uint64_t event_id;
320 if (header.type == PERF_RECORD_SAMPLE) {
321 if (header.size > event_id_pos_in_sample_records_ + sizeof(uint64_t)) {
322 has_event_id = true;
323 event_id = *reinterpret_cast<uint64_t*>(p.get() + event_id_pos_in_sample_records_);
324 }
325 } else {
326 if (header.size > event_id_reverse_pos_in_non_sample_records_) {
327 has_event_id = true;
328 event_id = *reinterpret_cast<uint64_t*>(p.get() + header.size - event_id_reverse_pos_in_non_sample_records_);
329 }
330 }
331 if (has_event_id) {
332 auto it = event_id_to_attr_map_.find(event_id);
333 if (it != event_id_to_attr_map_.end()) {
334 attr = &file_attrs_[it->second].attr;
335 }
336 }
337 }
338 return ReadRecordFromOwnedBuffer(*attr, header.type, p.release());
339 }
340
Read(void * buf,size_t len)341 bool RecordFileReader::Read(void* buf, size_t len) {
342 if (len != 0 && fread(buf, len, 1, record_fp_) != 1) {
343 PLOG(FATAL) << "failed to read file " << filename_;
344 return false;
345 }
346 return true;
347 }
348
ProcessEventIdRecord(const EventIdRecord & r)349 void RecordFileReader::ProcessEventIdRecord(const EventIdRecord& r) {
350 for (size_t i = 0; i < r.count; ++i) {
351 event_ids_for_file_attrs_[r.data[i].attr_id].push_back(r.data[i].event_id);
352 event_id_to_attr_map_[r.data[i].event_id] = r.data[i].attr_id;
353 }
354 }
355
GetAttrIndexOfRecord(const Record * record)356 size_t RecordFileReader::GetAttrIndexOfRecord(const Record* record) {
357 auto it = event_id_to_attr_map_.find(record->Id());
358 if (it != event_id_to_attr_map_.end()) {
359 return it->second;
360 }
361 return 0;
362 }
363
ReadFeatureSection(int feature,std::vector<char> * data)364 bool RecordFileReader::ReadFeatureSection(int feature, std::vector<char>* data) {
365 const std::map<int, SectionDesc>& section_map = FeatureSectionDescriptors();
366 auto it = section_map.find(feature);
367 if (it == section_map.end()) {
368 return false;
369 }
370 SectionDesc section = it->second;
371 data->resize(section.size);
372 if (section.size == 0) {
373 return true;
374 }
375 if (fseek(record_fp_, section.offset, SEEK_SET) != 0) {
376 PLOG(ERROR) << "fseek() failed";
377 return false;
378 }
379 if (!Read(data->data(), data->size())) {
380 return false;
381 }
382 return true;
383 }
384
ReadCmdlineFeature()385 std::vector<std::string> RecordFileReader::ReadCmdlineFeature() {
386 std::vector<char> buf;
387 if (!ReadFeatureSection(FEAT_CMDLINE, &buf)) {
388 return std::vector<std::string>();
389 }
390 const char* p = buf.data();
391 const char* end = buf.data() + buf.size();
392 std::vector<std::string> cmdline;
393 uint32_t arg_count;
394 MoveFromBinaryFormat(arg_count, p);
395 CHECK_LE(p, end);
396 for (size_t i = 0; i < arg_count; ++i) {
397 uint32_t len;
398 MoveFromBinaryFormat(len, p);
399 CHECK_LE(p + len, end);
400 cmdline.push_back(p);
401 p += len;
402 }
403 return cmdline;
404 }
405
ReadBuildIdFeature()406 std::vector<BuildIdRecord> RecordFileReader::ReadBuildIdFeature() {
407 std::vector<char> buf;
408 if (!ReadFeatureSection(FEAT_BUILD_ID, &buf)) {
409 return std::vector<BuildIdRecord>();
410 }
411 const char* p = buf.data();
412 const char* end = buf.data() + buf.size();
413 std::vector<BuildIdRecord> result;
414 while (p < end) {
415 auto header = reinterpret_cast<const perf_event_header*>(p);
416 CHECK_LE(p + header->size, end);
417 char* binary = new char[header->size];
418 memcpy(binary, p, header->size);
419 p += header->size;
420 BuildIdRecord record(binary);
421 record.OwnBinary();
422 // Set type explicitly as the perf.data produced by perf doesn't set it.
423 record.SetTypeAndMisc(PERF_RECORD_BUILD_ID, record.misc());
424 result.push_back(std::move(record));
425 }
426 return result;
427 }
428
ReadFeatureString(int feature)429 std::string RecordFileReader::ReadFeatureString(int feature) {
430 std::vector<char> buf;
431 if (!ReadFeatureSection(feature, &buf)) {
432 return std::string();
433 }
434 const char* p = buf.data();
435 const char* end = buf.data() + buf.size();
436 uint32_t len;
437 MoveFromBinaryFormat(len, p);
438 CHECK_LE(p + len, end);
439 return p;
440 }
441
ReadFileFeature(size_t & read_pos,std::string * file_path,uint32_t * file_type,uint64_t * min_vaddr,std::vector<Symbol> * symbols)442 bool RecordFileReader::ReadFileFeature(size_t& read_pos,
443 std::string* file_path,
444 uint32_t* file_type,
445 uint64_t* min_vaddr,
446 std::vector<Symbol>* symbols) {
447 auto it = feature_section_descriptors_.find(FEAT_FILE);
448 if (it == feature_section_descriptors_.end()) {
449 return false;
450 }
451 if (read_pos >= it->second.size) {
452 return false;
453 }
454 if (read_pos == 0) {
455 if (fseek(record_fp_, it->second.offset, SEEK_SET) != 0) {
456 PLOG(ERROR) << "fseek() failed";
457 return false;
458 }
459 }
460 uint32_t size;
461 if (!Read(&size, 4)) {
462 return false;
463 }
464 std::vector<char> buf(size);
465 if (!Read(buf.data(), size)) {
466 return false;
467 }
468 read_pos += 4 + size;
469 const char* p = buf.data();
470 *file_path = p;
471 p += file_path->size() + 1;
472 MoveFromBinaryFormat(*file_type, p);
473 MoveFromBinaryFormat(*min_vaddr, p);
474 uint32_t symbol_count;
475 MoveFromBinaryFormat(symbol_count, p);
476 symbols->clear();
477 symbols->reserve(symbol_count);
478 for (uint32_t i = 0; i < symbol_count; ++i) {
479 uint64_t start_vaddr;
480 uint32_t len;
481 MoveFromBinaryFormat(start_vaddr, p);
482 MoveFromBinaryFormat(len, p);
483 std::string name = p;
484 p += name.size() + 1;
485 symbols->emplace_back(name, start_vaddr, len);
486 }
487 CHECK_EQ(size, static_cast<size_t>(p - buf.data()));
488 return true;
489 }
490
ReadMetaInfoFeature(std::unordered_map<std::string,std::string> * info_map)491 bool RecordFileReader::ReadMetaInfoFeature(std::unordered_map<std::string, std::string>* info_map) {
492 std::vector<char> buf;
493 if (!ReadFeatureSection(FEAT_META_INFO, &buf)) {
494 return false;
495 }
496 const char* p = buf.data();
497 const char* end = buf.data() + buf.size();
498 while (p < end) {
499 const char* key = p;
500 const char* value = key + strlen(key) + 1;
501 CHECK(value < end);
502 (*info_map)[p] = value;
503 p = value + strlen(value) + 1;
504 }
505 return true;
506 }
507
LoadBuildIdAndFileFeatures(ThreadTree & thread_tree)508 void RecordFileReader::LoadBuildIdAndFileFeatures(ThreadTree& thread_tree) {
509 std::vector<BuildIdRecord> records = ReadBuildIdFeature();
510 std::vector<std::pair<std::string, BuildId>> build_ids;
511 for (auto& r : records) {
512 build_ids.push_back(std::make_pair(r.filename, r.build_id));
513 }
514 Dso::SetBuildIds(build_ids);
515
516 if (HasFeature(PerfFileFormat::FEAT_FILE)) {
517 std::string file_path;
518 uint32_t file_type;
519 uint64_t min_vaddr;
520 std::vector<Symbol> symbols;
521 size_t read_pos = 0;
522 while (ReadFileFeature(
523 read_pos, &file_path, &file_type, &min_vaddr, &symbols)) {
524 thread_tree.AddDsoInfo(file_path, file_type, min_vaddr, &symbols);
525 }
526 }
527 }
528
DataSection()529 std::vector<std::unique_ptr<Record>> RecordFileReader::DataSection() {
530 std::vector<std::unique_ptr<Record>> records;
531 ReadDataSection([&](std::unique_ptr<Record> record) {
532 records.push_back(std::move(record));
533 return true;
534 });
535 return records;
536 }
537