OSDN Git Service

Implement simpleperf record/dumprecord subcommands.
[android-x86/system-extras.git] / simpleperf / record_file.cpp
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
25 #include <base/logging.h>
26
27 #include "event_fd.h"
28 #include "perf_event.h"
29 #include "record.h"
30 #include "utils.h"
31
32 using namespace PerfFileFormat;
33
34 std::unique_ptr<RecordFileWriter> RecordFileWriter::CreateInstance(
35     const std::string& filename, const perf_event_attr& event_attr,
36     const std::vector<std::unique_ptr<EventFd>>& event_fds) {
37   FILE* fp = fopen(filename.c_str(), "web+");
38   if (fp == nullptr) {
39     PLOG(ERROR) << "failed to open record file '" << filename << "'";
40     return nullptr;
41   }
42
43   auto writer = std::unique_ptr<RecordFileWriter>(new RecordFileWriter(filename, fp));
44   if (!writer->WriteAttrSection(event_attr, event_fds)) {
45     return nullptr;
46   }
47   return writer;
48 }
49
50 RecordFileWriter::RecordFileWriter(const std::string& filename, FILE* fp)
51     : filename_(filename), record_fp_(fp), data_section_offset_(0), data_section_size_(0) {
52 }
53
54 RecordFileWriter::~RecordFileWriter() {
55   if (record_fp_ != nullptr) {
56     Close();
57   }
58 }
59
60 bool RecordFileWriter::WriteAttrSection(const perf_event_attr& event_attr,
61                                         const std::vector<std::unique_ptr<EventFd>>& event_fds) {
62   // Skip file header part.
63   if (fseek(record_fp_, sizeof(FileHeader), SEEK_SET) == -1) {
64     return false;
65   }
66
67   // Write id section.
68   std::vector<uint64_t> ids;
69   for (auto& event_fd : event_fds) {
70     ids.push_back(event_fd->Id());
71   }
72   long id_section_offset = ftell(record_fp_);
73   if (id_section_offset == -1) {
74     return false;
75   }
76   if (!Write(ids.data(), ids.size() * sizeof(uint64_t))) {
77     return false;
78   }
79
80   // Write attr section.
81   FileAttr attr;
82   attr.attr = event_attr;
83   attr.ids.offset = id_section_offset;
84   attr.ids.size = ids.size() * sizeof(uint64_t);
85
86   long attr_section_offset = ftell(record_fp_);
87   if (attr_section_offset == -1) {
88     return false;
89   }
90   if (!Write(&attr, sizeof(attr))) {
91     return false;
92   }
93
94   long data_section_offset = ftell(record_fp_);
95   if (data_section_offset == -1) {
96     return false;
97   }
98
99   attr_section_offset_ = attr_section_offset;
100   attr_section_size_ = sizeof(attr);
101   data_section_offset_ = data_section_offset;
102
103   // Save event_attr for use when reading records.
104   event_attr_ = event_attr;
105   return true;
106 }
107
108 bool RecordFileWriter::WriteData(const void* buf, size_t len) {
109   if (!Write(buf, len)) {
110     return false;
111   }
112   data_section_size_ += len;
113   return true;
114 }
115
116 bool RecordFileWriter::Write(const void* buf, size_t len) {
117   if (fwrite(buf, len, 1, record_fp_) != 1) {
118     PLOG(ERROR) << "failed to write to record file '" << filename_ << "'";
119     return false;
120   }
121   return true;
122 }
123
124 bool RecordFileWriter::WriteFileHeader() {
125   FileHeader header;
126   memset(&header, 0, sizeof(header));
127   memcpy(header.magic, PERF_MAGIC, sizeof(header.magic));
128   header.header_size = sizeof(header);
129   header.attr_size = sizeof(FileAttr);
130   header.attrs.offset = attr_section_offset_;
131   header.attrs.size = attr_section_size_;
132   header.data.offset = data_section_offset_;
133   header.data.size = data_section_size_;
134   for (auto& feature : features_) {
135     int i = feature / 8;
136     int j = feature % 8;
137     header.features[i] |= (1 << j);
138   }
139
140   if (fseek(record_fp_, 0, SEEK_SET) == -1) {
141     return false;
142   }
143   if (!Write(&header, sizeof(header))) {
144     return false;
145   }
146   return true;
147 }
148
149 bool RecordFileWriter::Close() {
150   CHECK(record_fp_ != nullptr);
151   bool result = true;
152
153   // Write file header. We gather enough information to write file header only after
154   // writing data section and feature section.
155   if (!WriteFileHeader()) {
156     result = false;
157   }
158
159   if (fclose(record_fp_) != 0) {
160     PLOG(ERROR) << "failed to close record file '" << filename_ << "'";
161     result = false;
162   }
163   record_fp_ = nullptr;
164   return result;
165 }
166
167 std::unique_ptr<RecordFileReader> RecordFileReader::CreateInstance(const std::string& filename) {
168   int fd = open(filename.c_str(), O_RDONLY | O_CLOEXEC);
169   if (fd == -1) {
170     PLOG(ERROR) << "failed to open record file '" << filename << "'";
171     return nullptr;
172   }
173   auto reader = std::unique_ptr<RecordFileReader>(new RecordFileReader(filename, fd));
174   if (!reader->MmapFile()) {
175     return nullptr;
176   }
177   return reader;
178 }
179
180 RecordFileReader::RecordFileReader(const std::string& filename, int fd)
181     : filename_(filename), record_fd_(fd), mmap_addr_(nullptr), mmap_len_(0) {
182 }
183
184 RecordFileReader::~RecordFileReader() {
185   if (record_fd_ != -1) {
186     Close();
187   }
188 }
189
190 bool RecordFileReader::Close() {
191   bool result = true;
192   if (munmap(const_cast<char*>(mmap_addr_), mmap_len_) == -1) {
193     PLOG(ERROR) << "failed to munmap() record file '" << filename_ << "'";
194     result = false;
195   }
196   if (close(record_fd_) == -1) {
197     PLOG(ERROR) << "failed to close record file '" << filename_ << "'";
198     result = false;
199   }
200   record_fd_ = -1;
201   return result;
202 }
203
204 bool RecordFileReader::MmapFile() {
205   off64_t file_size = lseek64(record_fd_, 0, SEEK_END);
206   if (file_size == -1) {
207     return false;
208   }
209   size_t mmap_len = file_size;
210   void* mmap_addr = mmap(nullptr, mmap_len, PROT_READ, MAP_SHARED, record_fd_, 0);
211   if (mmap_addr == MAP_FAILED) {
212     PLOG(ERROR) << "failed to mmap() record file '" << filename_ << "'";
213     return false;
214   }
215
216   mmap_addr_ = reinterpret_cast<const char*>(mmap_addr);
217   mmap_len_ = mmap_len;
218   return true;
219 }
220
221 const FileHeader* RecordFileReader::FileHeader() {
222   return reinterpret_cast<const struct FileHeader*>(mmap_addr_);
223 }
224
225 std::vector<const FileAttr*> RecordFileReader::AttrSection() {
226   std::vector<const FileAttr*> result;
227   const struct FileHeader* header = FileHeader();
228   size_t attr_count = header->attrs.size / header->attr_size;
229   const FileAttr* attr = reinterpret_cast<const FileAttr*>(mmap_addr_ + header->attrs.offset);
230   for (size_t i = 0; i < attr_count; ++i) {
231     result.push_back(attr++);
232   }
233   return result;
234 }
235
236 std::vector<uint64_t> RecordFileReader::IdsForAttr(const FileAttr* attr) {
237   std::vector<uint64_t> result;
238   size_t id_count = attr->ids.size / sizeof(uint64_t);
239   const uint64_t* id = reinterpret_cast<const uint64_t*>(mmap_addr_ + attr->ids.offset);
240   for (size_t i = 0; i < id_count; ++i) {
241     result.push_back(*id++);
242   }
243   return result;
244 }
245
246 std::vector<std::unique_ptr<const Record>> RecordFileReader::DataSection() {
247   std::vector<std::unique_ptr<const Record>> result;
248   const struct FileHeader* header = FileHeader();
249   auto file_attrs = AttrSection();
250   CHECK(file_attrs.size() > 0);
251   perf_event_attr attr = file_attrs[0]->attr;
252
253   const char* end = mmap_addr_ + header->data.offset + header->data.size;
254   const char* p = mmap_addr_ + header->data.offset;
255   while (p < end) {
256     const perf_event_header* header = reinterpret_cast<const perf_event_header*>(p);
257     if (p + header->size <= end) {
258       result.push_back(std::move(ReadRecordFromBuffer(attr, header)));
259     }
260     p += header->size;
261   }
262   return result;
263 }