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