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 <sys/mman.h> 22#include <unistd.h> 23#include <set> 24#include <string> 25#include <unordered_map> 26#include <vector> 27 28#include <android-base/file.h> 29#include <android-base/logging.h> 30 31#include "event_attr.h" 32#include "perf_event.h" 33#include "record.h" 34#include "utils.h" 35 36using namespace PerfFileFormat; 37 38std::unique_ptr<RecordFileWriter> RecordFileWriter::CreateInstance(const std::string& filename) { 39 // Remove old perf.data to avoid file ownership problems. 40 std::string err; 41 if (!android::base::RemoveFileIfExists(filename, &err)) { 42 LOG(ERROR) << "failed to remove file " << filename << ": " << err; 43 return nullptr; 44 } 45 FILE* fp = fopen(filename.c_str(), "web+"); 46 if (fp == nullptr) { 47 PLOG(ERROR) << "failed to open record file '" << filename << "'"; 48 return nullptr; 49 } 50 51 return std::unique_ptr<RecordFileWriter>(new RecordFileWriter(filename, fp)); 52} 53 54RecordFileWriter::RecordFileWriter(const std::string& filename, FILE* fp) 55 : filename_(filename), 56 record_fp_(fp), 57 attr_section_offset_(0), 58 attr_section_size_(0), 59 data_section_offset_(0), 60 data_section_size_(0), 61 feature_section_offset_(0), 62 feature_count_(0) { 63} 64 65RecordFileWriter::~RecordFileWriter() { 66 if (record_fp_ != nullptr) { 67 Close(); 68 } 69} 70 71bool RecordFileWriter::WriteAttrSection(const std::vector<EventAttrWithId>& attr_ids) { 72 if (attr_ids.empty()) { 73 return false; 74 } 75 76 // Skip file header part. 77 if (fseek(record_fp_, sizeof(FileHeader), SEEK_SET) == -1) { 78 return false; 79 } 80 81 // Write id section. 82 uint64_t id_section_offset; 83 if (!GetFilePos(&id_section_offset)) { 84 return false; 85 } 86 for (auto& attr_id : attr_ids) { 87 if (!Write(attr_id.ids.data(), attr_id.ids.size() * sizeof(uint64_t))) { 88 return false; 89 } 90 } 91 92 // Write attr section. 93 uint64_t attr_section_offset; 94 if (!GetFilePos(&attr_section_offset)) { 95 return false; 96 } 97 for (auto& attr_id : attr_ids) { 98 FileAttr file_attr; 99 file_attr.attr = *attr_id.attr; 100 file_attr.ids.offset = id_section_offset; 101 file_attr.ids.size = attr_id.ids.size() * sizeof(uint64_t); 102 id_section_offset += file_attr.ids.size; 103 if (!Write(&file_attr, sizeof(file_attr))) { 104 return false; 105 } 106 } 107 108 uint64_t data_section_offset; 109 if (!GetFilePos(&data_section_offset)) { 110 return false; 111 } 112 113 attr_section_offset_ = attr_section_offset; 114 attr_section_size_ = data_section_offset - attr_section_offset; 115 data_section_offset_ = data_section_offset; 116 117 // Save event_attr for use when reading records. 118 event_attr_ = *attr_ids[0].attr; 119 return true; 120} 121 122bool RecordFileWriter::WriteRecord(const Record& record) { 123 // linux-tools-perf only accepts records with size <= 65535 bytes. To make 124 // perf.data generated by simpleperf be able to be parsed by linux-tools-perf, 125 // Split simpleperf custom records which are > 65535 into a bunch of 126 // RECORD_SPLIT records, followed by a RECORD_SPLIT_END record. 127 constexpr uint32_t RECORD_SIZE_LIMIT = 65535; 128 if (record.size() <= RECORD_SIZE_LIMIT) { 129 WriteData(record.Binary(), record.size()); 130 return true; 131 } 132 CHECK_GT(record.type(), SIMPLE_PERF_RECORD_TYPE_START); 133 const char* p = record.Binary(); 134 uint32_t left_bytes = static_cast<uint32_t>(record.size()); 135 RecordHeader header; 136 header.type = SIMPLE_PERF_RECORD_SPLIT; 137 char header_buf[Record::header_size()]; 138 char* header_p; 139 while (left_bytes > 0) { 140 uint32_t bytes_to_write = std::min(RECORD_SIZE_LIMIT - Record::header_size(), left_bytes); 141 header.size = bytes_to_write + Record::header_size(); 142 header_p = header_buf; 143 header.MoveToBinaryFormat(header_p); 144 if (!WriteData(header_buf, Record::header_size())) { 145 return false; 146 } 147 if (!WriteData(p, bytes_to_write)) { 148 return false; 149 } 150 p += bytes_to_write; 151 left_bytes -= bytes_to_write; 152 } 153 header.type = SIMPLE_PERF_RECORD_SPLIT_END; 154 header.size = Record::header_size(); 155 header_p = header_buf; 156 header.MoveToBinaryFormat(header_p); 157 return WriteData(header_buf, Record::header_size()); 158} 159 160bool RecordFileWriter::WriteData(const void* buf, size_t len) { 161 if (!Write(buf, len)) { 162 return false; 163 } 164 data_section_size_ += len; 165 return true; 166} 167 168bool RecordFileWriter::Write(const void* buf, size_t len) { 169 if (fwrite(buf, len, 1, record_fp_) != 1) { 170 PLOG(ERROR) << "failed to write to record file '" << filename_ << "'"; 171 return false; 172 } 173 return true; 174} 175 176bool RecordFileWriter::Read(void* buf, size_t len) { 177 if (len != 0u && fread(buf, len, 1, record_fp_) != 1) { 178 PLOG(ERROR) << "failed to read record file '" << filename_ << "'"; 179 return false; 180 } 181 return true; 182} 183 184bool RecordFileWriter::ReadDataSection(const std::function<void(const Record*)>& callback) { 185 if (fseek(record_fp_, data_section_offset_, SEEK_SET) == -1) { 186 PLOG(ERROR) << "fseek() failed"; 187 return false; 188 } 189 std::vector<char> record_buf(512); 190 uint64_t read_pos = 0; 191 while (read_pos < data_section_size_) { 192 if (!Read(record_buf.data(), Record::header_size())) { 193 return false; 194 } 195 RecordHeader header(record_buf.data()); 196 if (record_buf.size() < header.size) { 197 record_buf.resize(header.size); 198 } 199 if (!Read(record_buf.data() + Record::header_size(), header.size - Record::header_size())) { 200 return false; 201 } 202 read_pos += header.size; 203 std::unique_ptr<Record> r = ReadRecordFromBuffer(event_attr_, header.type, record_buf.data()); 204 callback(r.get()); 205 } 206 return true; 207} 208 209bool RecordFileWriter::GetFilePos(uint64_t* file_pos) { 210 off_t offset = ftello(record_fp_); 211 if (offset == -1) { 212 PLOG(ERROR) << "ftello() failed"; 213 return false; 214 } 215 *file_pos = static_cast<uint64_t>(offset); 216 return true; 217} 218 219bool RecordFileWriter::BeginWriteFeatures(size_t feature_count) { 220 feature_section_offset_ = data_section_offset_ + data_section_size_; 221 feature_count_ = feature_count; 222 uint64_t feature_header_size = feature_count * sizeof(SectionDesc); 223 224 // Reserve enough space in the record file for the feature header. 225 std::vector<unsigned char> zero_data(feature_header_size); 226 if (fseek(record_fp_, feature_section_offset_, SEEK_SET) == -1) { 227 PLOG(ERROR) << "fseek() failed"; 228 return false; 229 } 230 return Write(zero_data.data(), zero_data.size()); 231} 232 233bool RecordFileWriter::WriteBuildIdFeature(const std::vector<BuildIdRecord>& build_id_records) { 234 if (!WriteFeatureBegin(FEAT_BUILD_ID)) { 235 return false; 236 } 237 for (auto& record : build_id_records) { 238 if (!Write(record.Binary(), record.size())) { 239 return false; 240 } 241 } 242 return WriteFeatureEnd(FEAT_BUILD_ID); 243} 244 245bool RecordFileWriter::WriteStringWithLength(const std::string& s) { 246 uint32_t len = static_cast<uint32_t>(Align(s.size() + 1, 64)); 247 if (!Write(&len, sizeof(len))) { 248 return false; 249 } 250 if (!Write(&s[0], s.size() + 1)) { 251 return false; 252 } 253 size_t pad_size = Align(s.size() + 1, 64) - s.size() - 1; 254 if (pad_size > 0u) { 255 char align_buf[pad_size]; 256 memset(align_buf, '\0', pad_size); 257 if (!Write(align_buf, pad_size)) { 258 return false; 259 } 260 } 261 return true; 262} 263 264bool RecordFileWriter::WriteFeatureString(int feature, const std::string& s) { 265 if (!WriteFeatureBegin(feature)) { 266 return false; 267 } 268 if (!WriteStringWithLength(s)) { 269 return false; 270 } 271 return WriteFeatureEnd(feature); 272} 273 274bool RecordFileWriter::WriteCmdlineFeature(const std::vector<std::string>& cmdline) { 275 if (!WriteFeatureBegin(FEAT_CMDLINE)) { 276 return false; 277 } 278 uint32_t arg_count = cmdline.size(); 279 if (!Write(&arg_count, sizeof(arg_count))) { 280 return false; 281 } 282 for (auto& arg : cmdline) { 283 if (!WriteStringWithLength(arg)) { 284 return false; 285 } 286 } 287 return WriteFeatureEnd(FEAT_CMDLINE); 288} 289 290bool RecordFileWriter::WriteBranchStackFeature() { 291 if (!WriteFeatureBegin(FEAT_BRANCH_STACK)) { 292 return false; 293 } 294 return WriteFeatureEnd(FEAT_BRANCH_STACK); 295} 296 297bool RecordFileWriter::WriteFileFeature(const std::string& file_path, 298 uint32_t file_type, 299 uint64_t min_vaddr, 300 const std::vector<const Symbol*>& symbols) { 301 uint32_t size = file_path.size() + 1 + sizeof(uint32_t) * 2 + 302 sizeof(uint64_t) + symbols.size() * (sizeof(uint64_t) + sizeof(uint32_t)); 303 for (const auto& symbol : symbols) { 304 size += strlen(symbol->Name()) + 1; 305 } 306 std::vector<char> buf(sizeof(uint32_t) + size); 307 char* p = buf.data(); 308 MoveToBinaryFormat(size, p); 309 MoveToBinaryFormat(file_path.c_str(), file_path.size() + 1, p); 310 MoveToBinaryFormat(file_type, p); 311 MoveToBinaryFormat(min_vaddr, p); 312 uint32_t symbol_count = static_cast<uint32_t>(symbols.size()); 313 MoveToBinaryFormat(symbol_count, p); 314 for (const auto& symbol : symbols) { 315 MoveToBinaryFormat(symbol->addr, p); 316 uint32_t len = symbol->len; 317 MoveToBinaryFormat(len, p); 318 MoveToBinaryFormat(symbol->Name(), strlen(symbol->Name()) + 1, p); 319 } 320 CHECK_EQ(buf.size(), static_cast<size_t>(p - buf.data())); 321 322 if (!WriteFeatureBegin(FEAT_FILE)) { 323 return false; 324 } 325 if (!Write(buf.data(), buf.size())) { 326 return false; 327 } 328 return WriteFeatureEnd(FEAT_FILE); 329} 330 331bool RecordFileWriter::WriteMetaInfoFeature( 332 const std::unordered_map<std::string, std::string>& info_map) { 333 uint32_t size = 0u; 334 for (auto& pair : info_map) { 335 size += pair.first.size() + 1; 336 size += pair.second.size() + 1; 337 } 338 std::vector<char> buf(size); 339 char* p = buf.data(); 340 for (auto& pair : info_map) { 341 MoveToBinaryFormat(pair.first.c_str(), pair.first.size() + 1, p); 342 MoveToBinaryFormat(pair.second.c_str(), pair.second.size() + 1, p); 343 } 344 if (!WriteFeatureBegin(FEAT_META_INFO)) { 345 return false; 346 } 347 if (!Write(buf.data(), buf.size())) { 348 return false; 349 } 350 return WriteFeatureEnd(FEAT_META_INFO); 351} 352 353bool RecordFileWriter::WriteFeatureBegin(int feature) { 354 auto it = features_.find(feature); 355 if (it == features_.end()) { 356 CHECK_LT(features_.size(), feature_count_); 357 auto& sec = features_[feature]; 358 if (!GetFilePos(&sec.offset)) { 359 return false; 360 } 361 sec.size = 0; 362 } 363 return true; 364} 365 366bool RecordFileWriter::WriteFeatureEnd(int feature) { 367 auto it = features_.find(feature); 368 if (it == features_.end()) { 369 return false; 370 } 371 uint64_t offset; 372 if (!GetFilePos(&offset)) { 373 return false; 374 } 375 it->second.size = offset - it->second.offset; 376 return true; 377} 378 379bool RecordFileWriter::EndWriteFeatures() { 380 // Used features (features_.size()) should be <= allocated feature space. 381 CHECK_LE(features_.size(), feature_count_); 382 if (fseek(record_fp_, feature_section_offset_, SEEK_SET) == -1) { 383 PLOG(ERROR) << "fseek() failed"; 384 return false; 385 } 386 for (const auto& pair : features_) { 387 if (!Write(&pair.second, sizeof(SectionDesc))) { 388 return false; 389 } 390 } 391 return true; 392} 393 394bool RecordFileWriter::WriteFileHeader() { 395 FileHeader header; 396 memset(&header, 0, sizeof(header)); 397 memcpy(header.magic, PERF_MAGIC, sizeof(header.magic)); 398 header.header_size = sizeof(header); 399 header.attr_size = sizeof(FileAttr); 400 header.attrs.offset = attr_section_offset_; 401 header.attrs.size = attr_section_size_; 402 header.data.offset = data_section_offset_; 403 header.data.size = data_section_size_; 404 for (const auto& pair : features_) { 405 int i = pair.first / 8; 406 int j = pair.first % 8; 407 header.features[i] |= (1 << j); 408 } 409 410 if (fseek(record_fp_, 0, SEEK_SET) == -1) { 411 return false; 412 } 413 if (!Write(&header, sizeof(header))) { 414 return false; 415 } 416 return true; 417} 418 419bool RecordFileWriter::Close() { 420 CHECK(record_fp_ != nullptr); 421 bool result = true; 422 423 // Write file header. We gather enough information to write file header only after 424 // writing data section and feature section. 425 if (!WriteFileHeader()) { 426 result = false; 427 } 428 429 if (fclose(record_fp_) != 0) { 430 PLOG(ERROR) << "failed to close record file '" << filename_ << "'"; 431 result = false; 432 } 433 record_fp_ = nullptr; 434 return result; 435} 436