• 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 
16 #include <cerrno>
17 #include <cstdio>
18 #include <fstream>
19 #include <memory>
20 #include <string>
21 
22 #include "ecmascript/log_wrapper.h"
23 #include "ecmascript/pgo_profiler/pgo_profiler_decoder.h"
24 #include "ecmascript/pgo_profiler/pgo_profiler_encoder.h"
25 
26 #include "ecmascript/platform/file.h"
27 #include "zlib.h"
28 
29 namespace panda::ecmascript {
30 static const std::string PROFILE_FILE_NAME = "/modules.ap";
Destroy()31 void PGOProfilerEncoder::Destroy()
32 {
33     if (!isInitialized_) {
34         return;
35     }
36     PGOProfilerHeader::Destroy(&header_);
37     pandaFileInfos_.reset();
38     globalRecordInfos_->Clear();
39     globalRecordInfos_.reset();
40     isInitialized_ = false;
41 }
42 
InitializeData()43 bool PGOProfilerEncoder::InitializeData()
44 {
45     if (!isInitialized_) {
46         if (!RealPath(outDir_, realOutPath_, false)) {
47             return false;
48         }
49 
50         static const std::string endString = ".ap";
51         if (realOutPath_.compare(realOutPath_.length() - endString.length(), endString.length(), endString)) {
52             realOutPath_ += PROFILE_FILE_NAME;
53         }
54         LOG_ECMA(INFO) << "Save profiler to file:" << realOutPath_;
55         PGOProfilerHeader::Build(&header_, PGOProfilerHeader::LastSize());
56         pandaFileInfos_ = std::make_unique<PGOPandaFileInfos>();
57         globalRecordInfos_ = std::make_shared<PGORecordDetailInfos>(hotnessThreshold_);
58         isInitialized_ = true;
59     }
60     return true;
61 }
62 
SamplePandaFileInfo(uint32_t checksum)63 void PGOProfilerEncoder::SamplePandaFileInfo(uint32_t checksum)
64 {
65     if (!isInitialized_) {
66         return;
67     }
68     pandaFileInfos_->Sample(checksum);
69 }
70 
Merge(const PGORecordDetailInfos & recordInfos)71 void PGOProfilerEncoder::Merge(const PGORecordDetailInfos &recordInfos)
72 {
73     if (!isInitialized_) {
74         return;
75     }
76     os::memory::LockHolder lock(mutex_);
77     globalRecordInfos_->Merge(recordInfos);
78 }
79 
Merge(const PGOPandaFileInfos & pandaFileInfos)80 void PGOProfilerEncoder::Merge(const PGOPandaFileInfos &pandaFileInfos)
81 {
82     return pandaFileInfos_->Merge(pandaFileInfos);
83 }
84 
VerifyPandaFileMatched(const PGOPandaFileInfos & pandaFileInfos,const std::string & base,const std::string & incoming) const85 bool PGOProfilerEncoder::VerifyPandaFileMatched(const PGOPandaFileInfos &pandaFileInfos, const std::string &base,
86                                                 const std::string &incoming) const
87 {
88     return pandaFileInfos_->VerifyChecksum(pandaFileInfos, base, incoming);
89 }
90 
Save()91 bool PGOProfilerEncoder::Save()
92 {
93     if (!isInitialized_) {
94         return false;
95     }
96     os::memory::LockHolder lock(mutex_);
97     return InternalSave();
98 }
99 
MergeWithExistProfile(PGOProfilerEncoder & encoder,PGOProfilerDecoder & decoder,const SaveTask * task)100 void PGOProfilerEncoder::MergeWithExistProfile(PGOProfilerEncoder &encoder, PGOProfilerDecoder &decoder,
101                                                const SaveTask *task)
102 {
103     if (!decoder.LoadFull()) {
104         LOG_ECMA(ERROR) << "Fail to load ap: " << realOutPath_;
105     } else {
106         Merge(decoder.GetPandaFileInfos());
107         globalRecordInfos_ = decoder.GetRecordDetailInfosPtr();
108     }
109     if (task && task->IsTerminate()) {
110         LOG_ECMA(DEBUG) << "ProcessProfile: task is already terminate";
111         return;
112     }
113     Merge(*encoder.pandaFileInfos_);
114     if (task && task->IsTerminate()) {
115         LOG_ECMA(DEBUG) << "ProcessProfile: task is already terminate";
116         return;
117     }
118     Merge(*encoder.globalRecordInfos_);
119 }
120 
SaveAndRename(const SaveTask * task)121 bool PGOProfilerEncoder::SaveAndRename(const SaveTask *task)
122 {
123     static const char *tempSuffix = ".tmp";
124     auto tmpOutPath = realOutPath_ + "." + std::to_string(getpid()) + "." + tempSuffix;
125     std::fstream fileStream(tmpOutPath.c_str(),
126                             std::fstream::binary | std::fstream::out | std::fstream::in | std::fstream::trunc);
127     if (!fileStream.is_open()) {
128         LOG_ECMA(ERROR) << "The file path(" << tmpOutPath << ") open failure! errno: " << errno;
129         return false;
130     }
131     pandaFileInfos_->ProcessToBinary(fileStream, header_->GetPandaInfoSection());
132     globalRecordInfos_->ProcessToBinary(task, fileStream, header_);
133     header_->ProcessToBinary(fileStream);
134     if (header_->SupportFileConsistency()) {
135         AddChecksum(fileStream);
136     }
137     fileStream.close();
138     if (task && task->IsTerminate()) {
139         LOG_ECMA(DEBUG) << "ProcessProfile: task is already terminate";
140         return false;
141     }
142     if (FileExist(realOutPath_.c_str()) && remove(realOutPath_.c_str())) {
143         LOG_ECMA(ERROR) << "Remove " << realOutPath_ << " failure!, errno: " << errno;
144         return false;
145     }
146     if (rename(tmpOutPath.c_str(), realOutPath_.c_str())) {
147         LOG_ECMA(ERROR) << "Rename " << tmpOutPath << " --> " << realOutPath_ << " failure!, errno: " << errno;
148         return false;
149     }
150     return true;
151 }
152 
InternalSave(const SaveTask * task)153 bool PGOProfilerEncoder::InternalSave(const SaveTask *task)
154 {
155     ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "PGOProfilerEncoder::InternalSave");
156     if (!isInitialized_) {
157         return false;
158     }
159     if ((mode_ == MERGE) && FileExist(realOutPath_.c_str())) {
160         PGOProfilerEncoder encoder(realOutPath_, hotnessThreshold_, mode_);
161         encoder.InitializeData();
162         PGOProfilerDecoder decoder(realOutPath_, hotnessThreshold_);
163         encoder.MergeWithExistProfile(*this, decoder, task);
164         return encoder.SaveAndRename(task);
165     }
166     return SaveAndRename(task);
167 }
168 
AddChecksum(std::fstream & fileStream)169 void PGOProfilerEncoder::AddChecksum(std::fstream &fileStream)
170 {
171     static constexpr uint32_t KILO_BYTES = 1024;
172     static constexpr uint32_t STEP_IN_KB = 256;
173     static constexpr uint32_t STEP_SIZE = STEP_IN_KB * KILO_BYTES;
174     uint32_t size = static_cast<uint32_t>(fileStream.seekp(0, std::fstream::end).tellp());
175     std::unique_ptr<std::vector<uint8_t>> buffer = std::make_unique<std::vector<uint8_t>>(STEP_SIZE);
176     // first, calculate the version field's checksum.
177     fileStream.seekg(PGOProfilerHeader::MAGIC_SIZE, std::fstream::beg)
178         .read(reinterpret_cast<char *>(buffer->data()), PGOProfilerHeader::VERSION_SIZE);
179     uint32_t checksum = adler32(0, reinterpret_cast<const Bytef *>(buffer->data()), PGOProfilerHeader::VERSION_SIZE);
180     // second, calculate the checksum for remaining content(exclude checksum field).
181     uint32_t remainingSize = size - PGOProfilerHeader::CHECKSUM_END_OFFSET;
182     fileStream.seekg(PGOProfilerHeader::CHECKSUM_END_OFFSET);
183     while (remainingSize > 0) {
184         uint32_t readSize = std::min(STEP_SIZE, remainingSize);
185         remainingSize = remainingSize - readSize;
186         fileStream.read(reinterpret_cast<char *>(buffer->data()), readSize);
187         checksum = adler32(checksum, reinterpret_cast<const Bytef *>(buffer->data()), readSize);
188     }
189     // third, write the checksum back to the checksum field in the output stream.
190     fileStream.seekp(PGOProfilerHeader::MAGIC_SIZE + PGOProfilerHeader::VERSION_SIZE, std::fstream::beg);
191     fileStream.write(reinterpret_cast<char *>(&checksum), sizeof(checksum));
192 }
193 
TerminateSaveTask()194 void PGOProfilerEncoder::TerminateSaveTask()
195 {
196     if (!isInitialized_) {
197         return;
198     }
199     Taskpool::GetCurrentTaskpool()->TerminateTask(GLOBAL_TASK_ID, TaskType::PGO_SAVE_TASK);
200 }
201 
PostSaveTask()202 void PGOProfilerEncoder::PostSaveTask()
203 {
204     if (!isInitialized_) {
205         return;
206     }
207     Taskpool::GetCurrentTaskpool()->PostTask(std::make_unique<SaveTask>(this, GLOBAL_TASK_ID));
208 }
209 
StartSaveTask(const SaveTask * task)210 void PGOProfilerEncoder::StartSaveTask(const SaveTask *task)
211 {
212     if (task == nullptr) {
213         return;
214     }
215     if (task->IsTerminate()) {
216         LOG_ECMA(ERROR) << "StartSaveTask: task is already terminate";
217         return;
218     }
219     os::memory::LockHolder lock(mutex_);
220     InternalSave(task);
221 }
222 
LoadAPTextFile(const std::string & inPath)223 bool PGOProfilerEncoder::LoadAPTextFile(const std::string &inPath)
224 {
225     if (!isInitialized_) {
226         return false;
227     }
228     std::string realPath;
229     if (!RealPath(inPath, realPath)) {
230         return false;
231     }
232 
233     std::ifstream fileStream(realPath.c_str());
234     if (!fileStream.is_open()) {
235         LOG_ECMA(ERROR) << "The file path(" << realOutPath_ << ") open failure!";
236         return false;
237     }
238 
239     if (!header_->ParseFromText(fileStream)) {
240         LOG_ECMA(ERROR) << "header format error";
241         return false;
242     }
243     if (!pandaFileInfos_->ParseFromText(fileStream)) {
244         LOG_ECMA(ERROR) << "panda file info format error";
245         return false;
246     }
247     if (!globalRecordInfos_->ParseFromText(fileStream)) {
248         LOG_ECMA(ERROR) << "record info format error";
249         return false;
250     }
251 
252     return true;
253 }
254 } // namespace panda::ecmascript
255