diff options
Diffstat (limited to 'simpleperf/record_file_reader.cpp')
-rw-r--r-- | simpleperf/record_file_reader.cpp | 439 |
1 files changed, 258 insertions, 181 deletions
diff --git a/simpleperf/record_file_reader.cpp b/simpleperf/record_file_reader.cpp index 5bfc5ac7..1b1bab7e 100644 --- a/simpleperf/record_file_reader.cpp +++ b/simpleperf/record_file_reader.cpp @@ -18,7 +18,9 @@ #include <fcntl.h> #include <string.h> + #include <set> +#include <string_view> #include <vector> #include <android-base/logging.h> @@ -58,6 +60,7 @@ static const std::map<int, std::string> feature_name_map = { {FEAT_DEBUG_UNWIND, "debug_unwind"}, {FEAT_DEBUG_UNWIND_FILE, "debug_unwind_file"}, {FEAT_FILE2, "file2"}, + {FEAT_ETM_BRANCH_LIST, "etm_branch_list"}, }; std::string GetFeatureName(int feature_id) { @@ -133,18 +136,26 @@ bool RecordFileReader::ReadHeader() { return true; } -bool RecordFileReader::CheckSectionDesc(const SectionDesc& desc, uint64_t min_offset) { +bool RecordFileReader::CheckSectionDesc(const SectionDesc& desc, uint64_t min_offset, + uint64_t alignment) { uint64_t desc_end; if (desc.offset < min_offset || __builtin_add_overflow(desc.offset, desc.size, &desc_end) || desc_end > file_size_) { return false; } + if (desc.size % alignment != 0) { + return false; + } return true; } bool RecordFileReader::ReadAttrSection() { size_t attr_count = header_.attrs.size / header_.attr_size; if (header_.attr_size != sizeof(FileAttr)) { + if (header_.attr_size <= sizeof(SectionDesc)) { + LOG(ERROR) << "invalid attr section in " << filename_; + return false; + } LOG(DEBUG) << "attr size (" << header_.attr_size << ") in " << filename_ << " doesn't match expected size (" << sizeof(FileAttr) << ")"; } @@ -156,42 +167,42 @@ bool RecordFileReader::ReadAttrSection() { PLOG(ERROR) << "fseek() failed"; return false; } + event_attrs_.resize(attr_count); + std::vector<SectionDesc> id_sections(attr_count); + size_t attr_size_in_file = header_.attr_size - sizeof(SectionDesc); for (size_t i = 0; i < attr_count; ++i) { std::vector<char> buf(header_.attr_size); if (!Read(buf.data(), buf.size())) { return false; } - // The size of perf_event_attr is changing between different linux kernel versions. - // Make sure we copy correct data to memory. - FileAttr attr; - memset(&attr, 0, sizeof(attr)); - size_t section_desc_size = sizeof(attr.ids); - size_t perf_event_attr_size = header_.attr_size - section_desc_size; - memcpy(&attr.attr, &buf[0], std::min(sizeof(attr.attr), perf_event_attr_size)); - memcpy(&attr.ids, &buf[perf_event_attr_size], section_desc_size); - if (!CheckSectionDesc(attr.ids, 0)) { + // The struct perf_event_attr is defined in a Linux header file. It can be extended in newer + // kernel versions with more fields and a bigger size. To disable these extensions, set their + // values to zero. So to copy perf_event_attr from file to memory safely, ensure the copy + // doesn't overflow the file or memory, and set the values of any extra fields in memory to + // zero. + if (attr_size_in_file >= sizeof(perf_event_attr)) { + memcpy(&event_attrs_[i].attr, &buf[0], sizeof(perf_event_attr)); + } else { + memset(&event_attrs_[i].attr, 0, sizeof(perf_event_attr)); + memcpy(&event_attrs_[i].attr, &buf[0], attr_size_in_file); + } + memcpy(&id_sections[i], &buf[attr_size_in_file], sizeof(SectionDesc)); + if (!CheckSectionDesc(id_sections[i], 0, sizeof(uint64_t))) { LOG(ERROR) << "invalid attr section in " << filename_; return false; } - file_attrs_.push_back(attr); } - if (file_attrs_.size() > 1) { - std::vector<perf_event_attr> attrs; - for (const auto& file_attr : file_attrs_) { - attrs.push_back(file_attr.attr); - } - if (!GetCommonEventIdPositionsForAttrs(attrs, &event_id_pos_in_sample_records_, + if (event_attrs_.size() > 1) { + if (!GetCommonEventIdPositionsForAttrs(event_attrs_, &event_id_pos_in_sample_records_, &event_id_reverse_pos_in_non_sample_records_)) { return false; } } - for (size_t i = 0; i < file_attrs_.size(); ++i) { - std::vector<uint64_t> ids; - if (!ReadIdsForAttr(file_attrs_[i], &ids)) { + for (size_t i = 0; i < attr_count; ++i) { + if (!ReadIdSection(id_sections[i], &event_attrs_[i].ids)) { return false; } - event_ids_for_file_attrs_.push_back(ids); - for (auto id : ids) { + for (auto id : event_attrs_[i].ids) { event_id_to_attr_map_[id] = i; } } @@ -227,14 +238,14 @@ bool RecordFileReader::ReadFeatureSectionDescriptors() { return true; } -bool RecordFileReader::ReadIdsForAttr(const FileAttr& attr, std::vector<uint64_t>* ids) { - size_t id_count = attr.ids.size / sizeof(uint64_t); - if (fseek(record_fp_, attr.ids.offset, SEEK_SET) != 0) { +bool RecordFileReader::ReadIdSection(const SectionDesc& section, std::vector<uint64_t>* ids) { + size_t id_count = section.size / sizeof(uint64_t); + if (fseek(record_fp_, section.offset, SEEK_SET) != 0) { PLOG(ERROR) << "fseek() failed"; return false; } ids->resize(id_count); - if (!Read(ids->data(), attr.ids.size)) { + if (!Read(ids->data(), section.size)) { return false; } return true; @@ -289,36 +300,37 @@ bool RecordFileReader::ReadRecord(std::unique_ptr<Record>& record) { std::unique_ptr<Record> RecordFileReader::ReadRecord() { char header_buf[Record::header_size()]; - if (!Read(header_buf, Record::header_size())) { + RecordHeader header; + if (!Read(header_buf, Record::header_size()) || !header.Parse(header_buf)) { return nullptr; } - RecordHeader header(header_buf); std::unique_ptr<char[]> p; if (header.type == SIMPLE_PERF_RECORD_SPLIT) { // Read until meeting a RECORD_SPLIT_END record. std::vector<char> buf; - size_t cur_size = 0; - char header_buf[Record::header_size()]; while (header.type == SIMPLE_PERF_RECORD_SPLIT) { - size_t bytes_to_read = header.size - Record::header_size(); - buf.resize(cur_size + bytes_to_read); - if (!Read(&buf[cur_size], bytes_to_read)) { + size_t add_size = header.size - Record::header_size(); + size_t old_size = buf.size(); + buf.resize(old_size + add_size); + if (!Read(&buf[old_size], add_size)) { return nullptr; } - cur_size += bytes_to_read; read_record_size_ += header.size; - if (!Read(header_buf, Record::header_size())) { + if (!Read(header_buf, Record::header_size()) || !header.Parse(header_buf)) { return nullptr; } - header = RecordHeader(header_buf); } if (header.type != SIMPLE_PERF_RECORD_SPLIT_END) { LOG(ERROR) << "SPLIT records are not followed by a SPLIT_END record."; return nullptr; } read_record_size_ += header.size; - header = RecordHeader(buf.data()); - p.reset(new char[header.size]); + if (buf.size() < Record::header_size() || !header.Parse(buf.data()) || + header.size != buf.size()) { + LOG(ERROR) << "invalid record merged from SPLIT records"; + return nullptr; + } + p.reset(new char[buf.size()]); memcpy(p.get(), buf.data(), buf.size()); } else { p.reset(new char[header.size]); @@ -331,8 +343,8 @@ std::unique_ptr<Record> RecordFileReader::ReadRecord() { read_record_size_ += header.size; } - const perf_event_attr* attr = &file_attrs_[0].attr; - if (file_attrs_.size() > 1 && header.type < PERF_RECORD_USER_DEFINED_TYPE_START) { + const perf_event_attr* attr = &event_attrs_[0].attr; + if (event_attrs_.size() > 1 && header.type < PERF_RECORD_USER_DEFINED_TYPE_START) { bool has_event_id = false; uint64_t event_id; if (header.type == PERF_RECORD_SAMPLE) { @@ -350,7 +362,7 @@ std::unique_ptr<Record> RecordFileReader::ReadRecord() { if (has_event_id) { auto it = event_id_to_attr_map_.find(event_id); if (it != event_id_to_attr_map_.end()) { - attr = &file_attrs_[it->second].attr; + attr = &event_attrs_[it->second].attr; } } } @@ -390,8 +402,9 @@ bool RecordFileReader::ReadAtOffset(uint64_t offset, void* buf, size_t len) { void RecordFileReader::ProcessEventIdRecord(const EventIdRecord& r) { for (size_t i = 0; i < r.count; ++i) { - event_ids_for_file_attrs_[r.data[i].attr_id].push_back(r.data[i].event_id); - event_id_to_attr_map_[r.data[i].event_id] = r.data[i].attr_id; + const auto& data = r.data[i]; + event_attrs_[data.attr_id].ids.push_back(data.event_id); + event_id_to_attr_map_[data.event_id] = data.attr_id; } } @@ -440,22 +453,25 @@ bool RecordFileReader::ReadFeatureSection(int feature, std::string* data) { std::vector<std::string> RecordFileReader::ReadCmdlineFeature() { std::vector<char> buf; if (!ReadFeatureSection(FEAT_CMDLINE, &buf)) { - return std::vector<std::string>(); + return {}; } - const char* p = buf.data(); - const char* end = buf.data() + buf.size(); + BinaryReader reader(buf.data(), buf.size()); std::vector<std::string> cmdline; - uint32_t arg_count; - MoveFromBinaryFormat(arg_count, p); - CHECK_LE(p, end); - for (size_t i = 0; i < arg_count; ++i) { - uint32_t len; - MoveFromBinaryFormat(len, p); - CHECK_LE(p + len, end); - cmdline.push_back(p); - p += len; + + uint32_t arg_count = 0; + reader.Read(arg_count); + for (size_t i = 0; i < arg_count && !reader.error; ++i) { + uint32_t aligned_len; + reader.Read(aligned_len); + cmdline.emplace_back(reader.ReadString()); + uint32_t len = cmdline.back().size() + 1; + if (aligned_len != Align(len, 64)) { + reader.error = true; + break; + } + reader.Move(aligned_len - len); } - return cmdline; + return reader.error ? std::vector<std::string>() : cmdline; } std::vector<BuildIdRecord> RecordFileReader::ReadBuildIdFeature() { @@ -466,16 +482,16 @@ std::vector<BuildIdRecord> RecordFileReader::ReadBuildIdFeature() { const char* p = buf.data(); const char* end = buf.data() + buf.size(); std::vector<BuildIdRecord> result; - while (p < end) { + while (p + sizeof(perf_event_header) < end) { auto header = reinterpret_cast<const perf_event_header*>(p); - if (p + header->size > end) { + if ((header->size <= sizeof(perf_event_header)) || (header->size > end - p)) { return {}; } std::unique_ptr<char[]> binary(new char[header->size]); memcpy(binary.get(), p, header->size); p += header->size; BuildIdRecord record; - if (!record.Parse(file_attrs_[0].attr, binary.get(), binary.get() + header->size)) { + if (!record.Parse(event_attrs_[0].attr, binary.get(), binary.get() + header->size)) { return {}; } binary.release(); @@ -492,12 +508,11 @@ std::string RecordFileReader::ReadFeatureString(int feature) { if (!ReadFeatureSection(feature, &buf)) { return std::string(); } - const char* p = buf.data(); - const char* end = buf.data() + buf.size(); - uint32_t len; - MoveFromBinaryFormat(len, p); - CHECK_LE(p + len, end); - return p; + BinaryReader reader(buf.data(), buf.size()); + uint32_t len = 0; + reader.Read(len); + std::string s = reader.ReadString(); + return reader.error ? "" : s; } std::vector<uint64_t> RecordFileReader::ReadAuxTraceFeature() { @@ -505,144 +520,163 @@ std::vector<uint64_t> RecordFileReader::ReadAuxTraceFeature() { if (!ReadFeatureSection(FEAT_AUXTRACE, &buf)) { return {}; } - std::vector<uint64_t> auxtrace_offset; - const char* p = buf.data(); - const char* end = buf.data() + buf.size(); - if (buf.size() / sizeof(uint64_t) % 2 == 1) { + BinaryReader reader(buf.data(), buf.size()); + if (reader.LeftSize() % sizeof(uint64_t) != 0) { + return {}; + } + if (reader.LeftSize() / sizeof(uint64_t) % 2 == 1) { // Recording files generated by linux perf contain an extra uint64 field. Skip it here. - p += sizeof(uint64_t); + reader.Move(sizeof(uint64_t)); } - while (p < end) { + + std::vector<uint64_t> auxtrace_offset; + while (!reader.error && reader.LeftSize() > 0u) { uint64_t offset; uint64_t size; - MoveFromBinaryFormat(offset, p); + reader.Read(offset); + reader.Read(size); auxtrace_offset.push_back(offset); - MoveFromBinaryFormat(size, p); - CHECK_EQ(size, AuxTraceRecord::Size()); + if (size != AuxTraceRecord::Size()) { + reader.error = true; + } } - return auxtrace_offset; + return reader.error ? std::vector<uint64_t>() : auxtrace_offset; } -bool RecordFileReader::ReadFileFeature(size_t& read_pos, FileFeature* file) { - file->Clear(); - if (HasFeature(FEAT_FILE)) { - return ReadFileV1Feature(read_pos, file); - } - if (HasFeature(FEAT_FILE2)) { - return ReadFileV2Feature(read_pos, file); - } - return false; -} +bool RecordFileReader::ReadFileFeature(uint64_t& read_pos, FileFeature& file, bool& error) { + file.Clear(); + error = false; -bool RecordFileReader::ReadFileV1Feature(size_t& read_pos, FileFeature* file) { - auto it = feature_section_descriptors_.find(FEAT_FILE); - if (it == feature_section_descriptors_.end()) { + bool use_v1 = false; + PerfFileFormat::SectionDesc desc; + if (auto it = feature_section_descriptors_.find(FEAT_FILE); + it != feature_section_descriptors_.end()) { + use_v1 = true; + desc = it->second; + } else if (auto it = feature_section_descriptors_.find(FEAT_FILE2); + it != feature_section_descriptors_.end()) { + desc = it->second; + } else { return false; } - if (read_pos >= it->second.size) { + + if (read_pos >= desc.size) { return false; } if (read_pos == 0) { - if (fseek(record_fp_, it->second.offset, SEEK_SET) != 0) { + if (fseek(record_fp_, desc.offset, SEEK_SET) != 0) { PLOG(ERROR) << "fseek() failed"; + error = true; return false; } } - uint32_t size; - if (!Read(&size, 4)) { + + bool result = false; + if (use_v1) { + result = ReadFileV1Feature(read_pos, desc.size - read_pos, file); + } else { + result = ReadFileV2Feature(read_pos, desc.size - read_pos, file); + } + if (!result) { + LOG(ERROR) << "failed to read file feature section"; + error = true; + } + return result; +} + +bool RecordFileReader::ReadFileV1Feature(uint64_t& read_pos, uint64_t max_size, FileFeature& file) { + uint32_t size = 0; + if (max_size < 4 || !Read(&size, 4) || max_size - 4 < size) { return false; } + read_pos += 4; std::vector<char> buf(size); if (!Read(buf.data(), size)) { return false; } - read_pos += 4 + size; - const char* p = buf.data(); - file->path = p; - p += file->path.size() + 1; - uint32_t file_type; - MoveFromBinaryFormat(file_type, p); + read_pos += size; + BinaryReader reader(buf.data(), buf.size()); + file.path = reader.ReadString(); + uint32_t file_type = 0; + reader.Read(file_type); if (file_type > DSO_UNKNOWN_FILE) { - LOG(ERROR) << "unknown file type for " << file->path + LOG(ERROR) << "unknown file type for " << file.path << " in file feature section: " << file_type; return false; } - file->type = static_cast<DsoType>(file_type); - MoveFromBinaryFormat(file->min_vaddr, p); - uint32_t symbol_count; - MoveFromBinaryFormat(symbol_count, p); - file->symbols.reserve(symbol_count); - for (uint32_t i = 0; i < symbol_count; ++i) { - uint64_t start_vaddr; - uint32_t len; - MoveFromBinaryFormat(start_vaddr, p); - MoveFromBinaryFormat(len, p); - std::string name = p; - p += name.size() + 1; - file->symbols.emplace_back(name, start_vaddr, len); - } - if (file->type == DSO_DEX_FILE) { - uint32_t offset_count; - MoveFromBinaryFormat(offset_count, p); - file->dex_file_offsets.resize(offset_count); - MoveFromBinaryFormat(file->dex_file_offsets.data(), offset_count, p); - } - file->file_offset_of_min_vaddr = std::numeric_limits<uint64_t>::max(); - if ((file->type == DSO_ELF_FILE || file->type == DSO_KERNEL_MODULE) && - static_cast<size_t>(p - buf.data()) < size) { - MoveFromBinaryFormat(file->file_offset_of_min_vaddr, p); - } - CHECK_EQ(size, static_cast<size_t>(p - buf.data())) - << "file " << file->path << ", type " << file->type; - return true; -} - -bool RecordFileReader::ReadFileV2Feature(size_t& read_pos, FileFeature* file) { - auto it = feature_section_descriptors_.find(FEAT_FILE2); - if (it == feature_section_descriptors_.end()) { - return false; - } - if (read_pos >= it->second.size) { + file.type = static_cast<DsoType>(file_type); + reader.Read(file.min_vaddr); + uint32_t symbol_count = 0; + reader.Read(symbol_count); + if (symbol_count > size) { return false; } - if (read_pos == 0) { - if (fseek(record_fp_, it->second.offset, SEEK_SET) != 0) { - PLOG(ERROR) << "fseek() failed"; + file.symbols.reserve(symbol_count); + while (symbol_count-- > 0) { + uint64_t start_vaddr = 0; + uint32_t len = 0; + reader.Read(start_vaddr); + reader.Read(len); + std::string name = reader.ReadString(); + file.symbols.emplace_back(name, start_vaddr, len); + } + if (file.type == DSO_DEX_FILE) { + uint32_t offset_count = 0; + reader.Read(offset_count); + if (offset_count > size) { return false; } + file.dex_file_offsets.resize(offset_count); + reader.Read(file.dex_file_offsets.data(), offset_count); } + file.file_offset_of_min_vaddr = std::numeric_limits<uint64_t>::max(); + if ((file.type == DSO_ELF_FILE || file.type == DSO_KERNEL_MODULE) && !reader.error && + reader.LeftSize() > 0) { + reader.Read(file.file_offset_of_min_vaddr); + } + return !reader.error && reader.LeftSize() == 0; +} + +bool RecordFileReader::ReadFileV2Feature(uint64_t& read_pos, uint64_t max_size, FileFeature& file) { uint32_t size; - if (!Read(&size, 4)) { + if (max_size < 4 || !Read(&size, 4) || max_size - 4 < size) { return false; } - read_pos += 4 + size; + read_pos += 4; std::string s(size, '\0'); if (!Read(s.data(), size)) { return false; } + read_pos += size; proto::FileFeature proto_file; if (!proto_file.ParseFromString(s)) { return false; } - file->path = proto_file.path(); - file->type = static_cast<DsoType>(proto_file.type()); - file->min_vaddr = proto_file.min_vaddr(); - file->symbols.reserve(proto_file.symbol_size()); + file.path = proto_file.path(); + file.type = static_cast<DsoType>(proto_file.type()); + file.min_vaddr = proto_file.min_vaddr(); + file.symbols.reserve(proto_file.symbol_size()); for (size_t i = 0; i < proto_file.symbol_size(); i++) { const auto& proto_symbol = proto_file.symbol(i); - file->symbols.emplace_back(proto_symbol.name(), proto_symbol.vaddr(), proto_symbol.len()); + file.symbols.emplace_back(proto_symbol.name(), proto_symbol.vaddr(), proto_symbol.len()); } - if (file->type == DSO_DEX_FILE) { - CHECK(proto_file.has_dex_file()); + if (file.type == DSO_DEX_FILE) { + if (!proto_file.has_dex_file()) { + return false; + } const auto& dex_file_offsets = proto_file.dex_file().dex_file_offset(); - file->dex_file_offsets.insert(file->dex_file_offsets.end(), dex_file_offsets.begin(), - dex_file_offsets.end()); - } else if (file->type == DSO_ELF_FILE) { - CHECK(proto_file.has_elf_file()); - file->file_offset_of_min_vaddr = proto_file.elf_file().file_offset_of_min_vaddr(); - } else if (file->type == DSO_KERNEL_MODULE) { - CHECK(proto_file.has_kernel_module()); - file->file_offset_of_min_vaddr = proto_file.kernel_module().memory_offset_of_min_vaddr(); + file.dex_file_offsets.insert(file.dex_file_offsets.end(), dex_file_offsets.begin(), + dex_file_offsets.end()); + } else if (file.type == DSO_ELF_FILE) { + if (!proto_file.has_elf_file()) { + return false; + } + file.file_offset_of_min_vaddr = proto_file.elf_file().file_offset_of_min_vaddr(); + } else if (file.type == DSO_KERNEL_MODULE) { + if (!proto_file.has_kernel_module()) { + return false; + } + file.file_offset_of_min_vaddr = proto_file.kernel_module().memory_offset_of_min_vaddr(); } return true; } @@ -653,14 +687,24 @@ bool RecordFileReader::ReadMetaInfoFeature() { if (!ReadFeatureSection(FEAT_META_INFO, &buf)) { return false; } - const char* p = buf.data(); - const char* end = buf.data() + buf.size(); - while (p < end) { - const char* key = p; - const char* value = key + strlen(key) + 1; - CHECK(value < end); - meta_info_[p] = value; - p = value + strlen(value) + 1; + std::string_view s(buf.data(), buf.size()); + size_t key_start = 0; + while (key_start < s.size()) { + // Parse a C-string for key. + size_t key_end = s.find('\0', key_start); + if (key_end == key_start || key_end == s.npos) { + LOG(ERROR) << "invalid meta info in " << filename_; + return false; + } + // Parse a C-string for value. + size_t value_start = key_end + 1; + size_t value_end = s.find('\0', value_start); + if (value_end == value_start || value_end == s.npos) { + LOG(ERROR) << "invalid meta info in " << filename_; + return false; + } + meta_info_[&s[key_start]] = &s[value_start]; + key_start = value_end + 1; } } return true; @@ -691,7 +735,7 @@ std::optional<DebugUnwindFeature> RecordFileReader::ReadDebugUnwindFeature() { return std::nullopt; } -void RecordFileReader::LoadBuildIdAndFileFeatures(ThreadTree& thread_tree) { +bool RecordFileReader::LoadBuildIdAndFileFeatures(ThreadTree& thread_tree) { std::vector<BuildIdRecord> records = ReadBuildIdFeature(); std::vector<std::pair<std::string, BuildId>> build_ids; for (auto& r : records) { @@ -699,22 +743,34 @@ void RecordFileReader::LoadBuildIdAndFileFeatures(ThreadTree& thread_tree) { } Dso::SetBuildIds(build_ids); - if (HasFeature(PerfFileFormat::FEAT_FILE) || HasFeature(PerfFileFormat::FEAT_FILE2)) { - FileFeature file_feature; - size_t read_pos = 0; - while (ReadFileFeature(read_pos, &file_feature)) { - thread_tree.AddDsoInfo(file_feature); + FileFeature file_feature; + uint64_t read_pos = 0; + bool error = false; + while (ReadFileFeature(read_pos, file_feature, error)) { + if (!thread_tree.AddDsoInfo(file_feature)) { + return false; } } + return !error; } -bool RecordFileReader::ReadAuxData(uint32_t cpu, uint64_t aux_offset, void* buf, size_t size) { +bool RecordFileReader::ReadAuxData(uint32_t cpu, uint64_t aux_offset, size_t size, + std::vector<uint8_t>& buf, bool& error) { + error = false; long saved_pos = ftell(record_fp_); if (saved_pos == -1) { PLOG(ERROR) << "ftell() failed"; + error = true; + return false; + } + OverflowResult aux_end = SafeAdd(aux_offset, size); + if (aux_end.overflow) { + LOG(ERROR) << "aux_end overflow"; + error = true; return false; } if (aux_data_location_.empty() && !BuildAuxDataLocation()) { + error = true; return false; } AuxDataLocation* location = nullptr; @@ -726,21 +782,27 @@ bool RecordFileReader::ReadAuxData(uint32_t cpu, uint64_t aux_offset, void* buf, auto location_it = std::upper_bound(it->second.begin(), it->second.end(), aux_offset, comp); if (location_it != it->second.begin()) { --location_it; - if (location_it->aux_offset + location_it->aux_size >= aux_offset + size) { + if (location_it->aux_offset + location_it->aux_size >= aux_end.value) { location = &*location_it; } } } if (location == nullptr) { - LOG(ERROR) << "failed to find file offset of aux data: cpu " << cpu << ", aux_offset " - << aux_offset << ", size " << size; + // ETM data can be dropped when recording if the userspace buffer is full. This isn't an error. + LOG(INFO) << "aux data is missing: cpu " << cpu << ", aux_offset " << aux_offset << ", size " + << size << ". Probably the data is lost when recording."; return false; } - if (!ReadAtOffset(aux_offset - location->aux_offset + location->file_offset, buf, size)) { + if (buf.size() < size) { + buf.resize(size); + } + if (!ReadAtOffset(aux_offset - location->aux_offset + location->file_offset, buf.data(), size)) { + error = true; return false; } if (fseek(record_fp_, saved_pos, SEEK_SET) != 0) { PLOG(ERROR) << "fseek() failed"; + error = true; return false; } return true; @@ -748,21 +810,36 @@ bool RecordFileReader::ReadAuxData(uint32_t cpu, uint64_t aux_offset, void* buf, bool RecordFileReader::BuildAuxDataLocation() { std::vector<uint64_t> auxtrace_offset = ReadAuxTraceFeature(); - if (auxtrace_offset.empty()) { - LOG(ERROR) << "failed to read auxtrace feature section"; - return false; - } std::unique_ptr<char[]> buf(new char[AuxTraceRecord::Size()]); for (auto offset : auxtrace_offset) { if (!ReadAtOffset(offset, buf.get(), AuxTraceRecord::Size())) { return false; } AuxTraceRecord auxtrace; - if (!auxtrace.Parse(file_attrs_[0].attr, buf.get(), buf.get() + AuxTraceRecord::Size())) { + if (!auxtrace.Parse(event_attrs_[0].attr, buf.get(), buf.get() + AuxTraceRecord::Size())) { return false; } - aux_data_location_[auxtrace.data->cpu].emplace_back( - auxtrace.data->offset, auxtrace.data->aux_size, offset + auxtrace.size()); + AuxDataLocation location(auxtrace.data->offset, auxtrace.data->aux_size, + offset + auxtrace.size()); + OverflowResult aux_end = SafeAdd(location.aux_offset, location.aux_size); + OverflowResult file_end = SafeAdd(location.file_offset, location.aux_size); + if (aux_end.overflow || file_end.overflow || file_end.value > file_size_) { + LOG(ERROR) << "invalid auxtrace feature section"; + return false; + } + auto location_it = aux_data_location_.find(auxtrace.data->cpu); + if (location_it != aux_data_location_.end()) { + const AuxDataLocation& prev_location = location_it->second.back(); + uint64_t prev_aux_end = prev_location.aux_offset + prev_location.aux_size; + // The AuxTraceRecords should be sorted by aux_offset for each cpu. + if (prev_aux_end > location.aux_offset) { + LOG(ERROR) << "invalid auxtrace feature section"; + return false; + } + location_it->second.emplace_back(location); + } else { + aux_data_location_[auxtrace.data->cpu].emplace_back(location); + } } return true; } |