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 <vector>
25
26 #include <base/logging.h>
27
28 #include "event_fd.h"
29 #include "perf_event.h"
30 #include "record.h"
31 #include "utils.h"
32
33 using namespace PerfFileFormat;
34
CreateInstance(const std::string & filename,const perf_event_attr & event_attr,const std::vector<std::unique_ptr<EventFd>> & event_fds)35 std::unique_ptr<RecordFileWriter> RecordFileWriter::CreateInstance(
36 const std::string& filename, const perf_event_attr& event_attr,
37 const std::vector<std::unique_ptr<EventFd>>& event_fds) {
38 FILE* fp = fopen(filename.c_str(), "web+");
39 if (fp == nullptr) {
40 PLOG(ERROR) << "failed to open record file '" << filename << "'";
41 return nullptr;
42 }
43
44 auto writer = std::unique_ptr<RecordFileWriter>(new RecordFileWriter(filename, fp));
45 if (!writer->WriteAttrSection(event_attr, event_fds)) {
46 return nullptr;
47 }
48 return writer;
49 }
50
RecordFileWriter(const std::string & filename,FILE * fp)51 RecordFileWriter::RecordFileWriter(const std::string& filename, FILE* fp)
52 : filename_(filename),
53 record_fp_(fp),
54 attr_section_offset_(0),
55 attr_section_size_(0),
56 data_section_offset_(0),
57 data_section_size_(0),
58 feature_count_(0),
59 current_feature_index_(0) {
60 }
61
~RecordFileWriter()62 RecordFileWriter::~RecordFileWriter() {
63 if (record_fp_ != nullptr) {
64 Close();
65 }
66 }
67
WriteAttrSection(const perf_event_attr & event_attr,const std::vector<std::unique_ptr<EventFd>> & event_fds)68 bool RecordFileWriter::WriteAttrSection(const perf_event_attr& event_attr,
69 const std::vector<std::unique_ptr<EventFd>>& event_fds) {
70 // Skip file header part.
71 if (fseek(record_fp_, sizeof(FileHeader), SEEK_SET) == -1) {
72 return false;
73 }
74
75 // Write id section.
76 std::vector<uint64_t> ids;
77 for (auto& event_fd : event_fds) {
78 ids.push_back(event_fd->Id());
79 }
80 long id_section_offset = ftell(record_fp_);
81 if (id_section_offset == -1) {
82 return false;
83 }
84 if (!Write(ids.data(), ids.size() * sizeof(uint64_t))) {
85 return false;
86 }
87
88 // Write attr section.
89 FileAttr attr;
90 attr.attr = event_attr;
91 attr.ids.offset = id_section_offset;
92 attr.ids.size = ids.size() * sizeof(uint64_t);
93
94 long attr_section_offset = ftell(record_fp_);
95 if (attr_section_offset == -1) {
96 return false;
97 }
98 if (!Write(&attr, sizeof(attr))) {
99 return false;
100 }
101
102 long data_section_offset = ftell(record_fp_);
103 if (data_section_offset == -1) {
104 return false;
105 }
106
107 attr_section_offset_ = attr_section_offset;
108 attr_section_size_ = sizeof(attr);
109 data_section_offset_ = data_section_offset;
110
111 // Save event_attr for use when reading records.
112 event_attr_ = event_attr;
113 return true;
114 }
115
WriteData(const void * buf,size_t len)116 bool RecordFileWriter::WriteData(const void* buf, size_t len) {
117 if (!Write(buf, len)) {
118 return false;
119 }
120 data_section_size_ += len;
121 return true;
122 }
123
Write(const void * buf,size_t len)124 bool RecordFileWriter::Write(const void* buf, size_t len) {
125 if (fwrite(buf, len, 1, record_fp_) != 1) {
126 PLOG(ERROR) << "failed to write to record file '" << filename_ << "'";
127 return false;
128 }
129 return true;
130 }
131
GetHitModulesInBuffer(const char * p,const char * end,std::vector<std::string> * hit_kernel_modules,std::vector<std::string> * hit_user_files)132 void RecordFileWriter::GetHitModulesInBuffer(const char* p, const char* end,
133 std::vector<std::string>* hit_kernel_modules,
134 std::vector<std::string>* hit_user_files) {
135 std::vector<std::unique_ptr<const Record>> kernel_mmaps;
136 std::vector<std::unique_ptr<const Record>> user_mmaps;
137 std::set<std::string> hit_kernel_set;
138 std::set<std::string> hit_user_set;
139
140 while (p < end) {
141 auto header = reinterpret_cast<const perf_event_header*>(p);
142 CHECK_LE(p + header->size, end);
143 p += header->size;
144 std::unique_ptr<const Record> record = ReadRecordFromBuffer(event_attr_, header);
145 CHECK(record != nullptr);
146 if (record->header.type == PERF_RECORD_MMAP) {
147 if (record->header.misc & PERF_RECORD_MISC_KERNEL) {
148 kernel_mmaps.push_back(std::move(record));
149 } else {
150 user_mmaps.push_back(std::move(record));
151 }
152 } else if (record->header.type == PERF_RECORD_SAMPLE) {
153 auto& r = *static_cast<const SampleRecord*>(record.get());
154 if (!(r.sample_type & PERF_SAMPLE_IP) || !(r.sample_type & PERF_SAMPLE_TID)) {
155 continue;
156 }
157 uint32_t pid = r.tid_data.pid;
158 uint64_t ip = r.ip_data.ip;
159 if (r.header.misc & PERF_RECORD_MISC_KERNEL) {
160 // Loop from back to front, because new MmapRecords are inserted at the end of the mmaps,
161 // and we want to match the newest one.
162 for (auto it = kernel_mmaps.rbegin(); it != kernel_mmaps.rend(); ++it) {
163 auto& m_record = *reinterpret_cast<const MmapRecord*>(it->get());
164 if (ip >= m_record.data.addr && ip < m_record.data.addr + m_record.data.len) {
165 hit_kernel_set.insert(m_record.filename);
166 break;
167 }
168 }
169 } else {
170 for (auto it = user_mmaps.rbegin(); it != user_mmaps.rend(); ++it) {
171 auto& m_record = *reinterpret_cast<const MmapRecord*>(it->get());
172 if (pid == m_record.data.pid && ip >= m_record.data.addr &&
173 ip < m_record.data.addr + m_record.data.len) {
174 hit_user_set.insert(m_record.filename);
175 break;
176 }
177 }
178 }
179 }
180 }
181 hit_kernel_modules->clear();
182 hit_kernel_modules->insert(hit_kernel_modules->begin(), hit_kernel_set.begin(),
183 hit_kernel_set.end());
184 hit_user_files->clear();
185 hit_user_files->insert(hit_user_files->begin(), hit_user_set.begin(), hit_user_set.end());
186 }
187
GetHitModules(std::vector<std::string> * hit_kernel_modules,std::vector<std::string> * hit_user_files)188 bool RecordFileWriter::GetHitModules(std::vector<std::string>* hit_kernel_modules,
189 std::vector<std::string>* hit_user_files) {
190 if (fflush(record_fp_) != 0) {
191 PLOG(ERROR) << "fflush() failed";
192 return false;
193 }
194 if (fseek(record_fp_, 0, SEEK_END) == -1) {
195 PLOG(ERROR) << "fseek() failed";
196 return false;
197 }
198 long file_size = ftell(record_fp_);
199 if (file_size == -1) {
200 PLOG(ERROR) << "ftell() failed";
201 return false;
202 }
203 size_t mmap_len = file_size;
204 void* mmap_addr = mmap(nullptr, mmap_len, PROT_READ, MAP_SHARED, fileno(record_fp_), 0);
205 if (mmap_addr == MAP_FAILED) {
206 PLOG(ERROR) << "mmap() failed";
207 return false;
208 }
209 const char* data_section_p = reinterpret_cast<const char*>(mmap_addr) + data_section_offset_;
210 const char* data_section_end = data_section_p + data_section_size_;
211 GetHitModulesInBuffer(data_section_p, data_section_end, hit_kernel_modules, hit_user_files);
212
213 if (munmap(mmap_addr, mmap_len) == -1) {
214 PLOG(ERROR) << "munmap() failed";
215 return false;
216 }
217 return true;
218 }
219
WriteFeatureHeader(size_t feature_count)220 bool RecordFileWriter::WriteFeatureHeader(size_t feature_count) {
221 feature_count_ = feature_count;
222 current_feature_index_ = 0;
223 uint64_t feature_header_size = feature_count * sizeof(SectionDesc);
224
225 // Reserve enough space in the record file for the feature header.
226 std::vector<unsigned char> zero_data(feature_header_size);
227 if (fseek(record_fp_, data_section_offset_ + data_section_size_, SEEK_SET) == -1) {
228 PLOG(ERROR) << "fseek() failed";
229 return false;
230 }
231 return Write(zero_data.data(), zero_data.size());
232 }
233
WriteBuildIdFeature(const std::vector<BuildIdRecord> & build_id_records)234 bool RecordFileWriter::WriteBuildIdFeature(const std::vector<BuildIdRecord>& build_id_records) {
235 if (current_feature_index_ >= feature_count_) {
236 return false;
237 }
238 // Always write features at the end of the file.
239 if (fseek(record_fp_, 0, SEEK_END) == -1) {
240 PLOG(ERROR) << "fseek() failed";
241 return false;
242 }
243 long section_start = ftell(record_fp_);
244 if (section_start == -1) {
245 PLOG(ERROR) << "ftell() failed";
246 return false;
247 }
248 for (auto& record : build_id_records) {
249 std::vector<char> data = record.BinaryFormat();
250 if (!Write(data.data(), data.size())) {
251 return false;
252 }
253 }
254 long section_end = ftell(record_fp_);
255 if (section_end == -1) {
256 return false;
257 }
258
259 // Write feature section descriptor for build_id feature.
260 SectionDesc desc;
261 desc.offset = section_start;
262 desc.size = section_end - section_start;
263 uint64_t feature_offset = data_section_offset_ + data_section_size_;
264 if (fseek(record_fp_, feature_offset + current_feature_index_ * sizeof(SectionDesc), SEEK_SET) ==
265 -1) {
266 PLOG(ERROR) << "fseek() failed";
267 return false;
268 }
269 if (fwrite(&desc, sizeof(SectionDesc), 1, record_fp_) != 1) {
270 PLOG(ERROR) << "fwrite() failed";
271 return false;
272 }
273 ++current_feature_index_;
274 features_.push_back(FEAT_BUILD_ID);
275 return true;
276 }
277
WriteFileHeader()278 bool RecordFileWriter::WriteFileHeader() {
279 FileHeader header;
280 memset(&header, 0, sizeof(header));
281 memcpy(header.magic, PERF_MAGIC, sizeof(header.magic));
282 header.header_size = sizeof(header);
283 header.attr_size = sizeof(FileAttr);
284 header.attrs.offset = attr_section_offset_;
285 header.attrs.size = attr_section_size_;
286 header.data.offset = data_section_offset_;
287 header.data.size = data_section_size_;
288 for (auto& feature : features_) {
289 int i = feature / 8;
290 int j = feature % 8;
291 header.features[i] |= (1 << j);
292 }
293
294 if (fseek(record_fp_, 0, SEEK_SET) == -1) {
295 return false;
296 }
297 if (!Write(&header, sizeof(header))) {
298 return false;
299 }
300 return true;
301 }
302
Close()303 bool RecordFileWriter::Close() {
304 CHECK(record_fp_ != nullptr);
305 bool result = true;
306
307 // Write file header. We gather enough information to write file header only after
308 // writing data section and feature section.
309 if (!WriteFileHeader()) {
310 result = false;
311 }
312
313 if (fclose(record_fp_) != 0) {
314 PLOG(ERROR) << "failed to close record file '" << filename_ << "'";
315 result = false;
316 }
317 record_fp_ = nullptr;
318 return result;
319 }
320
CreateInstance(const std::string & filename)321 std::unique_ptr<RecordFileReader> RecordFileReader::CreateInstance(const std::string& filename) {
322 int fd = open(filename.c_str(), O_RDONLY | O_CLOEXEC);
323 if (fd == -1) {
324 PLOG(ERROR) << "failed to open record file '" << filename << "'";
325 return nullptr;
326 }
327 auto reader = std::unique_ptr<RecordFileReader>(new RecordFileReader(filename, fd));
328 if (!reader->MmapFile()) {
329 return nullptr;
330 }
331 return reader;
332 }
333
RecordFileReader(const std::string & filename,int fd)334 RecordFileReader::RecordFileReader(const std::string& filename, int fd)
335 : filename_(filename), record_fd_(fd), mmap_addr_(nullptr), mmap_len_(0) {
336 }
337
~RecordFileReader()338 RecordFileReader::~RecordFileReader() {
339 if (record_fd_ != -1) {
340 Close();
341 }
342 }
343
Close()344 bool RecordFileReader::Close() {
345 bool result = true;
346 if (munmap(const_cast<char*>(mmap_addr_), mmap_len_) == -1) {
347 PLOG(ERROR) << "failed to munmap() record file '" << filename_ << "'";
348 result = false;
349 }
350 if (close(record_fd_) == -1) {
351 PLOG(ERROR) << "failed to close record file '" << filename_ << "'";
352 result = false;
353 }
354 record_fd_ = -1;
355 return result;
356 }
357
MmapFile()358 bool RecordFileReader::MmapFile() {
359 off64_t file_size = lseek64(record_fd_, 0, SEEK_END);
360 if (file_size == -1) {
361 return false;
362 }
363 size_t mmap_len = file_size;
364 void* mmap_addr = mmap(nullptr, mmap_len, PROT_READ, MAP_SHARED, record_fd_, 0);
365 if (mmap_addr == MAP_FAILED) {
366 PLOG(ERROR) << "failed to mmap() record file '" << filename_ << "'";
367 return false;
368 }
369
370 mmap_addr_ = reinterpret_cast<const char*>(mmap_addr);
371 mmap_len_ = mmap_len;
372 return true;
373 }
374
FileHeader()375 const FileHeader* RecordFileReader::FileHeader() {
376 return reinterpret_cast<const struct FileHeader*>(mmap_addr_);
377 }
378
AttrSection()379 std::vector<const FileAttr*> RecordFileReader::AttrSection() {
380 std::vector<const FileAttr*> result;
381 const struct FileHeader* header = FileHeader();
382 size_t attr_count = header->attrs.size / header->attr_size;
383 const FileAttr* attr = reinterpret_cast<const FileAttr*>(mmap_addr_ + header->attrs.offset);
384 for (size_t i = 0; i < attr_count; ++i) {
385 result.push_back(attr++);
386 }
387 return result;
388 }
389
IdsForAttr(const FileAttr * attr)390 std::vector<uint64_t> RecordFileReader::IdsForAttr(const FileAttr* attr) {
391 std::vector<uint64_t> result;
392 size_t id_count = attr->ids.size / sizeof(uint64_t);
393 const uint64_t* id = reinterpret_cast<const uint64_t*>(mmap_addr_ + attr->ids.offset);
394 for (size_t i = 0; i < id_count; ++i) {
395 result.push_back(*id++);
396 }
397 return result;
398 }
399
DataSection()400 std::vector<std::unique_ptr<const Record>> RecordFileReader::DataSection() {
401 std::vector<std::unique_ptr<const Record>> result;
402 const struct FileHeader* header = FileHeader();
403 auto file_attrs = AttrSection();
404 CHECK(file_attrs.size() > 0);
405 perf_event_attr attr = file_attrs[0]->attr;
406
407 const char* end = mmap_addr_ + header->data.offset + header->data.size;
408 const char* p = mmap_addr_ + header->data.offset;
409 while (p < end) {
410 const perf_event_header* header = reinterpret_cast<const perf_event_header*>(p);
411 if (p + header->size <= end) {
412 result.push_back(std::move(ReadRecordFromBuffer(attr, header)));
413 }
414 p += header->size;
415 }
416 return result;
417 }
418
FeatureSectionDescriptors()419 std::vector<SectionDesc> RecordFileReader::FeatureSectionDescriptors() {
420 std::vector<SectionDesc> result;
421 const struct FileHeader* header = FileHeader();
422 size_t feature_count = 0;
423 for (size_t i = 0; i < sizeof(header->features); ++i) {
424 for (size_t j = 0; j < 8; ++j) {
425 if (header->features[i] & (1 << j)) {
426 ++feature_count;
427 }
428 }
429 }
430 uint64_t feature_section_offset = header->data.offset + header->data.size;
431 const SectionDesc* p = reinterpret_cast<const SectionDesc*>(mmap_addr_ + feature_section_offset);
432 for (size_t i = 0; i < feature_count; ++i) {
433 result.push_back(*p++);
434 }
435 return result;
436 }
437