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 "perf_event.h" 27 #include "record.h" 28 #include "utils.h" 29 30 using namespace PerfFileFormat; 31 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 47 RecordFileReader::RecordFileReader(const std::string& filename, FILE* fp) 48 : filename_(filename), record_fp_(fp) { 49 } 50 51 RecordFileReader::~RecordFileReader() { 52 if (record_fp_ != nullptr) { 53 Close(); 54 } 55 } 56 57 bool RecordFileReader::Close() { 58 bool result = true; 59 if (fclose(record_fp_) != 0) { 60 PLOG(ERROR) << "failed to close record file '" << filename_ << "'"; 61 result = false; 62 } 63 record_fp_ = nullptr; 64 return result; 65 } 66 67 bool RecordFileReader::ReadHeader() { 68 if (fread(&header_, sizeof(header_), 1, record_fp_) != 1) { 69 PLOG(ERROR) << "failed to read file " << filename_; 70 return false; 71 } 72 return true; 73 } 74 75 bool RecordFileReader::ReadAttrSection() { 76 size_t attr_count = header_.attrs.size / header_.attr_size; 77 if (header_.attr_size != sizeof(FileAttr)) { 78 LOG(DEBUG) << "attr size (" << header_.attr_size << ") in " << filename_ 79 << " doesn't match expected size (" << sizeof(FileAttr) << ")"; 80 } 81 if (attr_count == 0) { 82 LOG(ERROR) << "no attr in file " << filename_; 83 return false; 84 } 85 if (fseek(record_fp_, header_.attrs.offset, SEEK_SET) != 0) { 86 PLOG(ERROR) << "failed to fseek()"; 87 return false; 88 } 89 for (size_t i = 0; i < attr_count; ++i) { 90 std::vector<char> buf(header_.attr_size); 91 if (fread(&buf[0], buf.size(), 1, record_fp_) != 1) { 92 PLOG(ERROR) << "failed to read " << filename_; 93 return false; 94 } 95 // The size of perf_event_attr is changing between different linux kernel versions. 96 // Make sure we copy correct data to memory. 97 FileAttr attr; 98 memset(&attr, 0, sizeof(attr)); 99 size_t section_desc_size = sizeof(attr.ids); 100 size_t perf_event_attr_size = header_.attr_size - section_desc_size; 101 memcpy(&attr.attr, &buf[0], std::min(sizeof(attr.attr), perf_event_attr_size)); 102 memcpy(&attr.ids, &buf[perf_event_attr_size], section_desc_size); 103 file_attrs_.push_back(attr); 104 } 105 return true; 106 } 107 108 bool RecordFileReader::ReadFeatureSectionDescriptors() { 109 std::vector<int> features; 110 for (size_t i = 0; i < sizeof(header_.features); ++i) { 111 for (size_t j = 0; j < 8; ++j) { 112 if (header_.features[i] & (1 << j)) { 113 features.push_back(i * 8 + j); 114 } 115 } 116 } 117 uint64_t feature_section_offset = header_.data.offset + header_.data.size; 118 if (fseek(record_fp_, feature_section_offset, SEEK_SET) != 0) { 119 PLOG(ERROR) << "failed to fseek()"; 120 return false; 121 } 122 for (const auto& id : features) { 123 SectionDesc desc; 124 if (fread(&desc, sizeof(desc), 1, record_fp_) != 1) { 125 PLOG(ERROR) << "failed to read " << filename_; 126 return false; 127 } 128 feature_section_descriptors_.emplace(id, desc); 129 } 130 return true; 131 } 132 133 bool RecordFileReader::ReadIdsForAttr(const FileAttr& attr, std::vector<uint64_t>* ids) { 134 size_t id_count = attr.ids.size / sizeof(uint64_t); 135 if (fseek(record_fp_, attr.ids.offset, SEEK_SET) != 0) { 136 PLOG(ERROR) << "failed to fseek()"; 137 return false; 138 } 139 ids->resize(id_count); 140 if (fread(ids->data(), attr.ids.size, 1, record_fp_) != 1) { 141 PLOG(ERROR) << "failed to read file " << filename_; 142 return false; 143 } 144 return true; 145 } 146 147 bool RecordFileReader::ReadDataSection(std::function<bool(std::unique_ptr<Record>)> callback, 148 bool sorted) { 149 if (fseek(record_fp_, header_.data.offset, SEEK_SET) != 0) { 150 PLOG(ERROR) << "failed to fseek()"; 151 return false; 152 } 153 RecordCache cache(file_attrs_[0].attr); 154 for (size_t nbytes_read = 0; nbytes_read < header_.data.size;) { 155 std::unique_ptr<Record> record = ReadRecordFromFile(file_attrs_[0].attr, record_fp_); 156 if (record == nullptr) { 157 return false; 158 } 159 nbytes_read += record->size(); 160 if (sorted) { 161 cache.Push(std::move(record)); 162 record = cache.Pop(); 163 if (record != nullptr) { 164 if (!callback(std::move(record))) { 165 return false; 166 } 167 } 168 } else { 169 if (!callback(std::move(record))) { 170 return false; 171 } 172 } 173 } 174 std::vector<std::unique_ptr<Record>> records = cache.PopAll(); 175 for (auto& record : records) { 176 if (!callback(std::move(record))) { 177 return false; 178 } 179 } 180 return true; 181 } 182 183 bool RecordFileReader::ReadFeatureSection(int feature, std::vector<char>* data) { 184 const std::map<int, SectionDesc>& section_map = FeatureSectionDescriptors(); 185 auto it = section_map.find(feature); 186 if (it == section_map.end()) { 187 return false; 188 } 189 SectionDesc section = it->second; 190 data->resize(section.size); 191 if (fseek(record_fp_, section.offset, SEEK_SET) != 0) { 192 PLOG(ERROR) << "failed to fseek()"; 193 return false; 194 } 195 if (fread(data->data(), data->size(), 1, record_fp_) != 1) { 196 PLOG(ERROR) << "failed to read " << filename_; 197 return false; 198 } 199 return true; 200 } 201 202 std::vector<std::string> RecordFileReader::ReadCmdlineFeature() { 203 std::vector<char> buf; 204 if (!ReadFeatureSection(FEAT_CMDLINE, &buf)) { 205 return std::vector<std::string>(); 206 } 207 const char* p = buf.data(); 208 const char* end = buf.data() + buf.size(); 209 std::vector<std::string> cmdline; 210 uint32_t arg_count; 211 MoveFromBinaryFormat(arg_count, p); 212 CHECK_LE(p, end); 213 for (size_t i = 0; i < arg_count; ++i) { 214 uint32_t len; 215 MoveFromBinaryFormat(len, p); 216 CHECK_LE(p + len, end); 217 cmdline.push_back(p); 218 p += len; 219 } 220 return cmdline; 221 } 222 223 std::vector<BuildIdRecord> RecordFileReader::ReadBuildIdFeature() { 224 std::vector<char> buf; 225 if (!ReadFeatureSection(FEAT_BUILD_ID, &buf)) { 226 return std::vector<BuildIdRecord>(); 227 } 228 const char* p = buf.data(); 229 const char* end = buf.data() + buf.size(); 230 std::vector<BuildIdRecord> result; 231 while (p < end) { 232 const perf_event_header* header = reinterpret_cast<const perf_event_header*>(p); 233 CHECK_LE(p + header->size, end); 234 BuildIdRecord record(header); 235 // Set type explicitly as the perf.data produced by perf doesn't set it. 236 record.header.type = PERF_RECORD_BUILD_ID; 237 result.push_back(record); 238 p += header->size; 239 } 240 return result; 241 } 242 243 std::string RecordFileReader::ReadFeatureString(int feature) { 244 std::vector<char> buf; 245 if (!ReadFeatureSection(feature, &buf)) { 246 return std::string(); 247 } 248 const char* p = buf.data(); 249 const char* end = buf.data() + buf.size(); 250 uint32_t len; 251 MoveFromBinaryFormat(len, p); 252 CHECK_LE(p + len, end); 253 return p; 254 } 255 256 std::vector<std::unique_ptr<Record>> RecordFileReader::DataSection() { 257 std::vector<std::unique_ptr<Record>> records; 258 ReadDataSection([&](std::unique_ptr<Record> record) { 259 records.push_back(std::move(record)); 260 return true; 261 }); 262 return records; 263 } 264