• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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