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
30using namespace PerfFileFormat;
31
32std::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
47RecordFileReader::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
52RecordFileReader::~RecordFileReader() {
53  if (record_fp_ != nullptr) {
54    Close();
55  }
56}
57
58bool 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
68bool 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
79bool 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
131bool 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
155bool 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
168bool 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
182bool 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
218std::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
287bool 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
295void 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
302size_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
310bool 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
331std::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
352std::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
375std::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
388bool 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
437bool RecordFileReader::ReadMetaInfoFeature(std::unordered_map<std::string, std::string>* info_map) {
438  std::vector<char> buf;
439  if (!ReadFeatureSection(FEAT_META_INFO, &buf)) {
440    return false;
441  }
442  const char* p = buf.data();
443  const char* end = buf.data() + buf.size();
444  while (p < end) {
445    const char* key = p;
446    const char* value = key + strlen(key) + 1;
447    CHECK(value < end);
448    (*info_map)[p] = value;
449    p = value + strlen(value) + 1;
450  }
451  return true;
452}
453
454void RecordFileReader::LoadBuildIdAndFileFeatures(ThreadTree& thread_tree) {
455  std::vector<BuildIdRecord> records = ReadBuildIdFeature();
456  std::vector<std::pair<std::string, BuildId>> build_ids;
457  for (auto& r : records) {
458    build_ids.push_back(std::make_pair(r.filename, r.build_id));
459  }
460  Dso::SetBuildIds(build_ids);
461
462  if (HasFeature(PerfFileFormat::FEAT_FILE)) {
463    std::string file_path;
464    uint32_t file_type;
465    uint64_t min_vaddr;
466    std::vector<Symbol> symbols;
467    size_t read_pos = 0;
468    while (ReadFileFeature(
469        read_pos, &file_path, &file_type, &min_vaddr, &symbols)) {
470      thread_tree.AddDsoInfo(file_path, file_type, min_vaddr, &symbols);
471    }
472  }
473}
474
475std::vector<std::unique_ptr<Record>> RecordFileReader::DataSection() {
476  std::vector<std::unique_ptr<Record>> records;
477  ReadDataSection([&](std::unique_ptr<Record> record) {
478    records.push_back(std::move(record));
479    return true;
480  });
481  return records;
482}
483