1 /*
2 * Copyright (c) 2023 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 "ecmascript/pgo_profiler/pgo_profiler_encoder.h"
16
17 #include "common_components/taskpool/taskpool.h"
18 #include "ecmascript/pgo_profiler/pgo_profiler_manager.h"
19 #include "ecmascript/pgo_profiler/pgo_trace.h"
20 #include "ecmascript/platform/file.h"
21 #include "ecmascript/platform/filesystem.h"
22 #include "ecmascript/platform/os.h"
23 #include "zlib.h"
24
25 namespace panda::ecmascript::pgo {
Save(const std::shared_ptr<PGOInfo> pgoInfo)26 bool PGOProfilerEncoder::Save(const std::shared_ptr<PGOInfo> pgoInfo)
27 {
28 return InternalSave(pgoInfo);
29 }
30
GetDirectoryPath(const std::string & path) const31 std::string PGOProfilerEncoder::GetDirectoryPath(const std::string& path) const
32 {
33 size_t lastSlash = path.find_last_of('/');
34 if (lastSlash == std::string::npos) {
35 return ".";
36 } else if (lastSlash == 0) {
37 return "/";
38 } else {
39 return path.substr(0, lastSlash);
40 }
41 }
42
WriteProfilerFile(const std::shared_ptr<PGOInfo> info,const std::string & tmpOutPath)43 bool PGOProfilerEncoder::WriteProfilerFile(const std::shared_ptr<PGOInfo> info, const std::string& tmpOutPath)
44 {
45 umask(S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); // ensure the permission of ap file is -rw-------
46 std::fstream fileStream(tmpOutPath.c_str(),
47 std::fstream::binary | std::fstream::out | std::fstream::in | std::fstream::trunc);
48 if (!fileStream.is_open()) {
49 LOG_PGO(ERROR) << "can't open the file path (" << tmpOutPath << ") errno: " << errno;
50 return false;
51 }
52 if (info->GetHeaderPtr() == nullptr) {
53 LOG_PGO(FATAL) << "[PGOProfilerEncoder::SaveAndRename] header_ is not initialized";
54 }
55 info->GetPandaFileInfos().ProcessToBinary(fileStream, info->GetHeaderPtr()->GetPandaInfoSection());
56 info->GetRecordDetailInfosPtr()->ProcessToBinary(fileStream, info->GetHeaderPtr());
57 PGOFileSectionInterface::ProcessSectionToBinary(
58 info->GetRecordDetailInfos(), fileStream, info->GetHeaderPtr(), *info->GetAbcFilePool().GetPool());
59 info->GetHeaderPtr()->SetFileSize(static_cast<uint32_t>(fileStream.tellp()));
60 info->GetHeaderPtr()->SetCompatibleAnVersion(AOTFileVersion::AN_VERSION);
61 info->GetHeaderPtr()->ProcessToBinary(fileStream);
62 if (info->GetHeaderPtr()->SupportFileConsistency()) {
63 AddChecksum(fileStream);
64 }
65 fileStream.close();
66 umask(0); // unset umask to avoid affecting other file permissions in the process
67 return fileStream.good();
68 }
69
ValidateAndRename(const std::string & tmpOutPath)70 bool PGOProfilerEncoder::ValidateAndRename(const std::string& tmpOutPath)
71 {
72 if (filesystem::FileSize(tmpOutPath.c_str()) > MAX_AP_FILE_SIZE) {
73 LOG_PGO(ERROR) << "ap file size is larger than " << MAX_AP_FILE_SIZE / CONVERT_FACTOR << "MB";
74 remove(tmpOutPath.c_str());
75 return false;
76 }
77 if (FileExist(path_.c_str()) && remove(path_.c_str())) {
78 LOG_PGO(ERROR) << "remove " << path_ << " failed, errno: " << errno;
79 return false;
80 }
81 if (rename(tmpOutPath.c_str(), path_.c_str())) {
82 LOG_PGO(ERROR) << "rename " << tmpOutPath << " to " << path_ << " failed, errno: " << errno;
83 return false;
84 }
85 return true;
86 }
87
SaveAndRename(const std::shared_ptr<PGOInfo> info)88 bool PGOProfilerEncoder::SaveAndRename(const std::shared_ptr<PGOInfo> info)
89 {
90 ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "PGOProfilerEncoder::SaveAndRename", "");
91 LOG_PGO(INFO) << "start save and rename ap file to " << path_;
92 ClockScope start;
93 std::string dirPath = GetDirectoryPath(path_);
94 if (!CheckDiskSpace(dirPath, MIN_DISK_SPACE)) {
95 LOG_PGO(ERROR) << "insufficient disk space, " << MIN_DISK_SPACE / CONVERT_FACTOR << "MB required, path: ["
96 << dirPath << "]";
97 return false;
98 }
99 static const char* tempSuffix = ".tmp";
100 auto tmpPath = path_ + "." + std::to_string(getpid()) + tempSuffix;
101 std::string tmpOutPath;
102 if (!RealPath(tmpPath, tmpOutPath, false)) {
103 LOG_PGO(ERROR) << "Fail to get realPath: " << tmpPath;
104 return false;
105 }
106 if (!WriteProfilerFile(info, tmpOutPath)) {
107 LOG_PGO(ERROR) << "failed to write or close file, errno: " << errno << ", " << strerror(errno);
108 remove(tmpOutPath.c_str());
109 return false;
110 }
111 if (!ValidateAndRename(tmpOutPath)) {
112 return false;
113 }
114 PGOProfilerManager::GetInstance()->RequestAot();
115 if (PGOTrace::GetInstance()->IsEnable()) {
116 PGOTrace::GetInstance()->SetSaveTime(start.TotalSpentTime());
117 PGOTrace::GetInstance()->Print();
118 }
119 LOG_PGO(INFO) << "successfully save and rename ap file to " << path_;
120 return true;
121 }
122
InternalSave(const std::shared_ptr<PGOInfo> rtInfo)123 bool PGOProfilerEncoder::InternalSave(const std::shared_ptr<PGOInfo> rtInfo)
124 {
125 ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "PGOProfilerEncoder::InternalSave", "");
126 if (rtInfo == nullptr) {
127 LOG_PGO(ERROR) << "pgo info is nullptr";
128 return false;
129 }
130 LOG_PGO(INFO) << (mode_ == MERGE ? "MERGE(1)" : "OVERWRITE(0)") << " pgo info";
131 if ((mode_ == MERGE) && FileExist(path_.c_str())) {
132 auto info = std::make_shared<PGOInfo>(rtInfo->GetHotnessThreshold());
133 PGOProfilerDecoder decoder(path_, rtInfo->GetHotnessThreshold());
134 info->MergeWithExistProfile(*rtInfo, decoder);
135 return SaveAndRename(info);
136 }
137 return SaveAndRename(rtInfo);
138 }
139
AddChecksum(std::fstream & fileStream)140 void PGOProfilerEncoder::AddChecksum(std::fstream& fileStream)
141 {
142 static constexpr uint32_t KILO_BYTES = 1024;
143 static constexpr uint32_t STEP_IN_KB = 256;
144 static constexpr uint32_t STEP_SIZE = STEP_IN_KB * KILO_BYTES;
145 uint32_t size = static_cast<uint32_t>(fileStream.seekp(0, std::fstream::end).tellp());
146 std::unique_ptr<std::vector<uint8_t>> buffer = std::make_unique<std::vector<uint8_t>>(STEP_SIZE);
147 // first, calculate the version field's checksum.
148 fileStream.seekg(PGOProfilerHeader::MAGIC_SIZE, std::fstream::beg)
149 .read(reinterpret_cast<char*>(buffer->data()), PGOProfilerHeader::VERSION_SIZE);
150 uint32_t checksum = adler32(0, reinterpret_cast<const Bytef*>(buffer->data()), PGOProfilerHeader::VERSION_SIZE);
151 // second, calculate the checksum for remaining content(exclude checksum field).
152 uint32_t remainingSize = size - PGOProfilerHeader::CHECKSUM_END_OFFSET;
153 fileStream.seekg(PGOProfilerHeader::CHECKSUM_END_OFFSET);
154 while (remainingSize > 0) {
155 uint32_t readSize = std::min(STEP_SIZE, remainingSize);
156 remainingSize = remainingSize - readSize;
157 fileStream.read(reinterpret_cast<char*>(buffer->data()), readSize);
158 checksum = adler32(checksum, reinterpret_cast<const Bytef*>(buffer->data()), readSize);
159 }
160 // third, write the checksum back to the checksum field in the output stream.
161 fileStream.seekp(PGOProfilerHeader::MAGIC_SIZE + PGOProfilerHeader::VERSION_SIZE, std::fstream::beg);
162 fileStream.write(reinterpret_cast<char*>(&checksum), sizeof(checksum));
163 }
164
GetApGenMode() const165 ApGenMode PGOProfilerEncoder::GetApGenMode() const
166 {
167 return mode_;
168 }
169
SetApGenMode(ApGenMode mode)170 void PGOProfilerEncoder::SetApGenMode(ApGenMode mode)
171 {
172 mode_ = mode;
173 }
174 } // namespace panda::ecmascript::pgo
175