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