OSDN Git Service

Merge "ext4_utils: copy mke2fs.conf to /etc" into oc-dr1-dev am: 7f60c1a8af
[android-x86/system-extras.git] / simpleperf / record_file_writer.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 #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
36 using namespace PerfFileFormat;
37
38 std::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
54 RecordFileWriter::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
65 RecordFileWriter::~RecordFileWriter() {
66   if (record_fp_ != nullptr) {
67     Close();
68   }
69 }
70
71 bool 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
122 bool 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
160 bool 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
168 bool 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
176 bool 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
184 bool 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
209 bool 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
219 bool 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
233 bool 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
245 bool 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
264 bool 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
274 bool 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
290 bool RecordFileWriter::WriteBranchStackFeature() {
291   if (!WriteFeatureBegin(FEAT_BRANCH_STACK)) {
292     return false;
293   }
294   return WriteFeatureEnd(FEAT_BRANCH_STACK);
295 }
296
297 bool 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
331 bool 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
353 bool 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
366 bool 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
379 bool 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
394 bool 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
419 bool 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 }