1 /*
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #include "perf_file_writer.h"
16
17 #include <cinttypes>
18 #include <cstdlib>
19 #include <unistd.h>
20
21 #include "utilities.h"
22 using namespace std::chrono;
23 namespace OHOS {
24 namespace Developtools {
25 namespace HiPerf {
~PerfFileWriter()26 PerfFileWriter::~PerfFileWriter()
27 {
28 // if file was not closed properly, remove it before exit
29 if (fp_ != nullptr) {
30 fclose(fp_);
31 fp_ = nullptr;
32 if (remove(fileName_.c_str()) != 0) {
33 HLOGE("fail to remove file(%s).", fileName_.c_str());
34 }
35 }
36 }
37
Open(const std::string & fileName,bool compressData)38 bool PerfFileWriter::Open(const std::string &fileName, bool compressData)
39 {
40 // check file existence, if exist, remove it
41 if (access(fileName.c_str(), F_OK) == 0) {
42 // file exists
43 if (remove(fileName.c_str()) != 0) {
44 char errInfo[ERRINFOLEN] = { 0 };
45 strerror_r(errno, errInfo, ERRINFOLEN);
46 printf("can't remove exist file(%s). %d:%s\n", fileName.c_str(), errno,
47 errInfo);
48 return false;
49 }
50 }
51 std::string resolvedPath = CanonicalizeSpecPath(fileName.c_str());
52 fp_ = fopen(resolvedPath.c_str(), "web+");
53 if (fp_ == nullptr) {
54 char errInfo[ERRINFOLEN] = { 0 };
55 strerror_r(errno, errInfo, ERRINFOLEN);
56 printf("can't create file(%s). %d:%s\n", fileName.c_str(), errno, errInfo);
57 return false;
58 }
59
60 fileName_ = fileName;
61 compressData_ = compressData;
62 attrSection_.offset = 0;
63 attrSection_.size = 0;
64 dataSection_ = attrSection_;
65 header_.size = sizeof(header_);
66 fileBuffer_.resize(WRITER_BUFFER_SIZE);
67 if (setvbuf(fp_, fileBuffer_.data(), _IOFBF, WRITER_BUFFER_SIZE) != 0) {
68 HLOGD("setvbuf failed");
69 }
70
71 return true;
72 }
73
Close()74 bool PerfFileWriter::Close()
75 {
76 HLOG_ASSERT(fp_ != nullptr);
77 bool rc = true;
78
79 if (!WriteHeader()) {
80 rc = false;
81 }
82 if (!WriteFeatureData()) {
83 rc = false;
84 }
85
86 if (fclose(fp_) != 0) {
87 HLOGD("fail to close fp ");
88 rc = false;
89 }
90 fp_ = nullptr;
91
92 // compress and rename .perf.data to gzip file
93 if (compressData_) {
94 std::string gzName = fileName_ + ".gz";
95 if (CompressFile(fileName_, gzName)) {
96 if (remove(fileName_.c_str()) != 0) {
97 char errInfo[ERRINFOLEN] = { 0 };
98 strerror_r(errno, errInfo, ERRINFOLEN);
99 printf("can't remove file(%s). %d:%s\n",
100 fileName_.c_str(), errno, errInfo);
101 }
102 if (rename(gzName.c_str(), fileName_.c_str()) != 0) {
103 char errInfo[ERRINFOLEN] = { 0 };
104 strerror_r(errno, errInfo, ERRINFOLEN);
105 printf("can't rename file(%s) to (%s). %d:%s\n", gzName.c_str(), fileName_.c_str(),
106 errno, errInfo);
107 }
108 } else {
109 char errInfo[ERRINFOLEN] = { 0 };
110 strerror_r(errno, errInfo, ERRINFOLEN);
111 printf("failed to compress file(%s). %d:%s\n", fileName_.c_str(), errno,
112 errInfo);
113 }
114 }
115
116 return rc;
117 }
118
WriteRecord(const PerfEventRecord & record)119 bool PerfFileWriter::WriteRecord(const PerfEventRecord &record)
120 {
121 if (!isWritingRecord) {
122 HLOGV("need write <attrs> first");
123 return false;
124 }
125
126 HLOGV("write '%s'", record.GetName().c_str());
127
128 if (record.GetSize() > RECORD_SIZE_LIMIT) {
129 HLOGD("%s record size exceed limit", record.GetName().c_str());
130 return false;
131 }
132 // signal 7 (SIGBUS), code 1 (BUS_ADRALN), fault addr 0xb64eb195
133 static std::vector<u8> buf(RECORD_SIZE_LIMIT);
134
135 if (!record.GetBinary(buf)) {
136 return false;
137 }
138
139 if (!Write(buf.data(), record.GetSize())) {
140 return false;
141 }
142 dataSection_.size += record.GetSize();
143
144 ++recordCount_;
145
146 return true;
147 }
148
ReadDataSection(ProcessRecordCB & callback)149 bool PerfFileWriter::ReadDataSection(ProcessRecordCB &callback)
150 {
151 HLOG_ASSERT(fp_ != nullptr);
152 if (fseek(fp_, dataSection_.offset, SEEK_SET) != 0) {
153 HLOGE("fseek() failed");
154 return false;
155 }
156 HLOGD("dataSection_ at offset %" PRIu64 " + %" PRIu64 "", dataSection_.offset,
157 dataSection_.size);
158
159 return ReadRecords(callback);
160 }
161
ReadRecords(ProcessRecordCB & callback)162 bool PerfFileWriter::ReadRecords(ProcessRecordCB &callback)
163 {
164 // record size can not exceed 64K
165 HIPERF_BUF_ALIGN uint8_t buf[RECORD_SIZE_LIMIT];
166 // diff with reader
167 uint64_t remainingSize = dataSection_.size;
168 size_t recordNumber = 0;
169 while (remainingSize > 0) {
170 if (remainingSize < sizeof(perf_event_header)) {
171 HLOGW("not enough sizeof(perf_event_header).");
172 return false;
173 } else if (Read(buf, sizeof(perf_event_header)) <= 0) {
174 HLOGW("read perf_event_header failed.");
175 return false;
176 } else {
177 perf_event_header *header = reinterpret_cast<perf_event_header *>(buf);
178 HLOG_ASSERT(header->size < sizeof(buf));
179 if (remainingSize >= header->size) {
180 size_t headerSize = sizeof(perf_event_header);
181 if (Read(buf + headerSize, header->size - headerSize)) {
182 uint8_t *data = buf;
183 // the record is allowed from a cache memory, does not free memory after use
184 auto record = GetPerfSampleFromCacheMain(static_cast<perf_event_type>(header->type),
185 data, defaultEventAttr_);
186 // skip unknown record
187 if (record == nullptr) {
188 return true;
189 }
190 remainingSize -= header->size;
191 // call callback to process, do not destroy the record
192 callback(std::move(record));
193 recordNumber++;
194 }
195 } else {
196 HLOGW("not enough header->size.");
197 }
198 }
199 }
200 HLOGD("read back %zu records", recordNumber);
201 return true;
202 }
203
Read(void * buf,size_t len)204 bool PerfFileWriter::Read(void *buf, size_t len)
205 {
206 HLOG_ASSERT(buf != nullptr);
207 HLOG_ASSERT(fp_ != nullptr);
208 HLOG_ASSERT(len > 0);
209
210 if (fread(buf, len, 1, fp_) != 1) {
211 HLOGD("failed to read file");
212 return false;
213 }
214 return true;
215 }
216
GetDataSize() const217 uint64_t PerfFileWriter::GetDataSize() const
218 {
219 return dataSection_.size;
220 }
221
GetRecordCount() const222 uint PerfFileWriter::GetRecordCount() const
223 {
224 return recordCount_;
225 }
226
GetFilePos(uint64_t & pos) const227 bool PerfFileWriter::GetFilePos(uint64_t &pos) const
228 {
229 off_t offset = ftello(fp_);
230 if (offset == -1) {
231 HLOGD("RecordFileWriter ftello fail");
232 return false;
233 }
234 pos = static_cast<uint64_t>(offset);
235 return true;
236 }
237
Write(const void * buf,size_t len)238 bool PerfFileWriter::Write(const void *buf, size_t len)
239 {
240 #ifdef HIPERF_DEBUG_TIME
241 const auto startTime = steady_clock::now();
242 #endif
243 if (len != 0u && fwrite(buf, len, 1, fp_) != 1) {
244 HLOGD("PerfFileWriter fwrite fail ");
245 return false;
246 }
247 #ifdef HIPERF_DEBUG_TIME
248 writeTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
249 #endif
250 return true;
251 }
252
WriteAttrAndId(const std::vector<AttrWithId> & attrIds)253 bool PerfFileWriter::WriteAttrAndId(const std::vector<AttrWithId> &attrIds)
254 {
255 if (attrIds.empty()) {
256 return false;
257 }
258
259 // Skip file header part.
260 if (fp_ == nullptr) {
261 return false;
262 } else if (fseek(fp_, header_.size, SEEK_SET) == -1) {
263 return false;
264 }
265
266 // Write id section.
267 uint64_t idSectionOffset;
268 if (!GetFilePos(idSectionOffset)) {
269 return false;
270 }
271
272 HLOGD("attrIds %zu", attrIds.size());
273 for (auto &attrId : attrIds) {
274 HLOGD(" attrIds ids %zu", attrId.ids.size());
275 if (!Write(attrId.ids.data(), attrId.ids.size() * sizeof(uint64_t))) {
276 return false;
277 }
278 }
279
280 // Write attr section.
281 uint64_t attrSectionOffset;
282 if (!GetFilePos(attrSectionOffset)) {
283 return false;
284 }
285 for (auto &attrId : attrIds) {
286 perf_file_attr fileAttr;
287 fileAttr.attr = attrId.attr;
288 fileAttr.ids.offset = idSectionOffset;
289 fileAttr.ids.size = attrId.ids.size() * sizeof(uint64_t);
290 idSectionOffset += fileAttr.ids.size;
291
292 if (!Write(&fileAttr, sizeof(fileAttr))) {
293 return false;
294 }
295 }
296
297 uint64_t dataSectionOffset;
298 if (!GetFilePos(dataSectionOffset)) {
299 return false;
300 }
301
302 attrSection_.offset = attrSectionOffset;
303 attrSection_.size = dataSectionOffset - attrSectionOffset;
304 dataSection_.offset = dataSectionOffset;
305
306 defaultEventAttr_ = attrIds[0].attr;
307
308 isWritingRecord = true;
309
310 return true;
311 }
312
LeftLessRight(const std::unique_ptr<PerfFileSection> & l,const std::unique_ptr<PerfFileSection> & r)313 static bool LeftLessRight(const std::unique_ptr<PerfFileSection> &l,
314 const std::unique_ptr<PerfFileSection> &r)
315 {
316 if (l == nullptr || r == nullptr) {
317 return false;
318 }
319 return l->featureId_ < r->featureId_;
320 }
321 // to write perf_file_header to file
322 // can only be called after all attr and records has been written.
WriteHeader()323 bool PerfFileWriter::WriteHeader()
324 {
325 header_.attrSize = sizeof(perf_file_attr);
326 header_.attrs = attrSection_;
327 header_.data = dataSection_;
328
329 // ignore event_types
330 header_.eventTypes.size = 0;
331 header_.eventTypes.offset = 0;
332
333 // save header
334 if (fseek(fp_, 0, SEEK_SET) == -1) {
335 HLOGD("fseek return error ");
336 return false;
337 }
338 if (!Write(&header_, sizeof(header_))) {
339 return false;
340 }
341 return true;
342 }
343
WriteFeatureData()344 bool PerfFileWriter::WriteFeatureData()
345 {
346 long featureOffset = 0;
347 if (fseek(fp_, 0, SEEK_END) != 0) {
348 HLOGD("fseek SEEK_END return error ");
349 return false;
350 }
351 if ((featureOffset = ftell(fp_)) == -1) {
352 HLOGD("ftell return error ");
353 return false;
354 }
355
356 for (size_t i = 0; i < sizeof(header_.features); i++) {
357 if (header_.features[i] != 0) {
358 HLOGV(" features['%zu'] '0x%x'", i, header_.features[i]);
359 }
360 }
361
362 unsigned long contentOffset = featureOffset + featureSections_.size() * sizeof(perf_file_section);
363 HLOGV("features start at file '0x%lx' content at '0x%lx'", featureOffset, contentOffset);
364
365 // reorder
366 std::sort(featureSections_.begin(), featureSections_.end(), LeftLessRight);
367 // save features head
368 int i = 0;
369 for (auto &featureSection : featureSections_) {
370 featureSection->header.offset = contentOffset;
371 featureSection->header.size = featureSection->GetSize();
372 contentOffset += featureSection->header.size;
373 HLOGV("save features[%d] head offset '0x%" PRIx64 "' size '0x%" PRIx64 "'", i,
374 featureSection->header.offset, featureSection->header.size);
375 i++;
376
377 // write features heads
378 if (!Write(&featureSection->header, sizeof(featureSection->header))) {
379 return false;
380 }
381 }
382 long offset = ftell(fp_);
383 if (offset < 0) {
384 return false;
385 }
386 HLOGV("features data at file '0x%lx'", offset);
387
388 i = 0;
389 for (auto &featureSection : featureSections_) {
390 std::vector<char> buf(featureSection->header.size);
391 featureSection->GetBinary(&buf[0], featureSection->header.size);
392 HLOGV("save features[%d] content size '0x%" PRIx64 "'", i, featureSection->header.size);
393 i++;
394
395 // write features content
396 if (!Write(&buf[0], featureSection->header.size)) {
397 HLOGE("write failed %" PRIu64 ".", featureSection->header.size);
398 return false;
399 }
400 }
401
402 return true;
403 }
404
AddNrCpusFeature(FEATURE feature,uint32_t nrCpusAvailable,uint32_t nrCpusOnline)405 bool PerfFileWriter::AddNrCpusFeature(FEATURE feature, uint32_t nrCpusAvailable,
406 uint32_t nrCpusOnline)
407 {
408 HLOGV("add feature '%s': nrCpusAvailable %u, nrCpusOnline %u",
409 PerfFileSection::GetFeatureName(FEATURE::NRCPUS).c_str(), nrCpusAvailable, nrCpusOnline);
410 featureSections_.emplace_back(
411 std::make_unique<PerfFileSectionNrCpus>(feature, nrCpusAvailable, nrCpusOnline));
412
413 // update header feature bits
414 header_.features[static_cast<int>(FEATURE::NRCPUS) / BITS_IN_BYTE] |=
415 1 << (static_cast<int>(FEATURE::NRCPUS) % BITS_IN_BYTE); // bit
416 return true;
417 }
418
AddEventDescFeature(FEATURE feature,const std::vector<AttrWithId> & eventDesces)419 bool PerfFileWriter::AddEventDescFeature(FEATURE feature,
420 const std::vector<AttrWithId> &eventDesces)
421 {
422 HLOGV("add feature '%s' %zu", PerfFileSection::GetFeatureName(FEATURE::EVENT_DESC).c_str(),
423 eventDesces.size());
424 featureSections_.emplace_back(std::make_unique<PerfFileSectionEventDesc>(feature, eventDesces));
425
426 header_.features[static_cast<int>(FEATURE::EVENT_DESC) / BITS_IN_BYTE] |=
427 1 << (static_cast<int>(FEATURE::EVENT_DESC) % BITS_IN_BYTE); // bit
428 return true;
429 }
430
AddStringFeature(FEATURE feature,std::string string)431 bool PerfFileWriter::AddStringFeature(FEATURE feature, std::string string)
432 {
433 HLOGV("add feature '%s' string '%s'", PerfFileSection::GetFeatureName(feature).c_str(),
434 string.c_str());
435 featureSections_.emplace_back(std::make_unique<PerfFileSectionString>(feature, string));
436
437 // update header feature bits
438 header_.features[static_cast<int>(feature) / BITS_IN_BYTE] |= 1 << (static_cast<int>(feature) % BITS_IN_BYTE);
439 return true;
440 }
441
AddBoolFeature(FEATURE feature)442 bool PerfFileWriter::AddBoolFeature(FEATURE feature)
443 {
444 // same as u64, just use 1 as value
445 return AddU64Feature(feature, 1u);
446 }
447
AddU64Feature(FEATURE feature,uint64_t v)448 bool PerfFileWriter::AddU64Feature(FEATURE feature, uint64_t v)
449 {
450 HLOGV("add feature '%s' uint64_t '%" PRIu64 "'",
451 PerfFileSection::GetFeatureName(feature).c_str(), v);
452 featureSections_.emplace_back(std::make_unique<PerfFileSectionU64>(feature, v));
453
454 // update header feature bits
455 header_.features[static_cast<int>(feature) / BITS_IN_BYTE] |= 1 << (static_cast<int>(feature) % BITS_IN_BYTE);
456 return true;
457 }
458
AddUniStackTableFeature(const ProcessStackMap * table)459 bool PerfFileWriter::AddUniStackTableFeature(const ProcessStackMap *table)
460 {
461 const FEATURE feature = FEATURE::HIPERF_FILES_UNISTACK_TABLE;
462 featureSections_.emplace_back(
463 std::make_unique<PerfFileSectionUniStackTable>(feature, table));
464 header_.features[static_cast<int>(feature) / BITS_IN_BYTE] |= 1 << (static_cast<int>(feature) % BITS_IN_BYTE);
465 return true;
466 }
467
AddSymbolsFeature(const std::vector<std::unique_ptr<SymbolsFile>> & symbolsFiles)468 bool PerfFileWriter::AddSymbolsFeature(
469 const std::vector<std::unique_ptr<SymbolsFile>> &symbolsFiles)
470 {
471 const FEATURE feature = FEATURE::HIPERF_FILES_SYMBOL;
472 HLOGV("add feature symbolsFiles %zu", symbolsFiles.size());
473 std::vector<SymbolFileStruct> symbolFileStructs {};
474 for (auto &symbolsFile : symbolsFiles) {
475 if (symbolsFile->SymbolsLoaded()) {
476 auto &symbolsFileStruct = symbolFileStructs.emplace_back();
477 symbolsFile->ExportSymbolToFileFormat(symbolsFileStruct);
478 }
479 }
480 featureSections_.emplace_back(
481 std::make_unique<PerfFileSectionSymbolsFiles>(feature, symbolFileStructs));
482 // update header feature bits
483 header_.features[static_cast<int>(feature) / BITS_IN_BYTE] |= 1 << (static_cast<int>(feature) % BITS_IN_BYTE);
484 return true;
485 }
486 } // namespace HiPerf
487 } // namespace Developtools
488 } // namespace OHOS
489