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 "ecmascript/pgo_profiler/pgo_profiler_encoder.h"
17 #include "ecmascript/pgo_profiler/pgo_profiler_manager.h"
18 #include "ecmascript/pgo_profiler/pgo_trace.h"
19 #include "ecmascript/platform/file.h"
20 #include "ecmascript/platform/os.h"
21
22 namespace panda::ecmascript::pgo {
23
Save(const std::shared_ptr<PGOInfo> pgoInfo)24 bool PGOProfilerEncoder::Save(const std::shared_ptr<PGOInfo> pgoInfo)
25 {
26 return InternalSave(pgoInfo);
27 }
28
SaveAndRename(const std::shared_ptr<PGOInfo> info,const SaveTask * task)29 bool PGOProfilerEncoder::SaveAndRename(const std::shared_ptr<PGOInfo> info, const SaveTask* task)
30 {
31 ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "PGOProfilerEncoder::SaveAndRename");
32 LOG_PGO(INFO) << "start save and rename ap file to " << path_;
33 ClockScope start;
34 umask(S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
35 static const char* tempSuffix = ".tmp";
36 auto tmpOutPath = path_ + "." + std::to_string(getpid()) + tempSuffix;
37 std::fstream fileStream(tmpOutPath.c_str(),
38 std::fstream::binary | std::fstream::out | std::fstream::in | std::fstream::trunc);
39 if (!fileStream.is_open()) {
40 LOG_PGO(ERROR) << "can't open the file path (" << tmpOutPath << ") errno: " << errno;
41 return false;
42 }
43 if (info->GetHeaderPtr() == nullptr) {
44 LOG_PGO(FATAL) << "[PGOProfilerEncoder::SaveAndRename] header_ is not initialized";
45 }
46 info->GetPandaFileInfos().ProcessToBinary(fileStream, info->GetHeaderPtr()->GetPandaInfoSection());
47 info->GetRecordDetailInfosPtr()->ProcessToBinary(task, fileStream, info->GetHeaderPtr());
48 PGOFileSectionInterface::ProcessSectionToBinary(
49 info->GetRecordDetailInfos(), fileStream, info->GetHeaderPtr(), *info->GetAbcFilePool().GetPool());
50 info->GetHeaderPtr()->SetFileSize(static_cast<uint32_t>(fileStream.tellp()));
51 info->GetHeaderPtr()->SetCompatibleAnVersion(AOTFileVersion::AN_VERSION);
52 info->GetHeaderPtr()->ProcessToBinary(fileStream);
53 if (info->GetHeaderPtr()->SupportFileConsistency()) {
54 AddChecksum(fileStream);
55 }
56 fileStream.close();
57 if (task && task->IsTerminate()) {
58 return false;
59 }
60 if (FileExist(path_.c_str()) && remove(path_.c_str())) {
61 LOG_PGO(ERROR) << "remove " << path_ << " failed, errno: " << errno;
62 return false;
63 }
64 if (rename(tmpOutPath.c_str(), path_.c_str())) {
65 LOG_PGO(ERROR) << "rename " << tmpOutPath << " to " << path_ << " failed, errno: " << errno;
66 return false;
67 }
68 PGOProfilerManager::GetInstance()->RequestAot();
69 if (PGOTrace::GetInstance()->IsEnable()) {
70 PGOTrace::GetInstance()->SetSaveTime(start.TotalSpentTime());
71 PGOTrace::GetInstance()->Print();
72 }
73 LOG_PGO(INFO) << "successfully save and rename ap file to " << path_;
74 return true;
75 }
76
InternalSave(const std::shared_ptr<PGOInfo> rtInfo,const SaveTask * task)77 bool PGOProfilerEncoder::InternalSave(const std::shared_ptr<PGOInfo> rtInfo, const SaveTask* task)
78 {
79 ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "PGOProfilerEncoder::InternalSave");
80 LOG_PGO(INFO) << (mode_ == MERGE ? "MERGE(1)" : "OVERWRITE(0)") << " pgo info";
81 if ((mode_ == MERGE) && FileExist(path_.c_str())) {
82 auto info = std::make_shared<PGOInfo>(rtInfo->GetHotnessThreshold());
83 PGOProfilerDecoder decoder(path_, rtInfo->GetHotnessThreshold());
84 info->MergeWithExistProfile(*rtInfo, decoder, task);
85 return SaveAndRename(info, task);
86 }
87 return SaveAndRename(rtInfo, task);
88 }
89
AddChecksum(std::fstream & fileStream)90 void PGOProfilerEncoder::AddChecksum(std::fstream& fileStream)
91 {
92 static constexpr uint32_t KILO_BYTES = 1024;
93 static constexpr uint32_t STEP_IN_KB = 256;
94 static constexpr uint32_t STEP_SIZE = STEP_IN_KB * KILO_BYTES;
95 uint32_t size = static_cast<uint32_t>(fileStream.seekp(0, std::fstream::end).tellp());
96 std::unique_ptr<std::vector<uint8_t>> buffer = std::make_unique<std::vector<uint8_t>>(STEP_SIZE);
97 // first, calculate the version field's checksum.
98 fileStream.seekg(PGOProfilerHeader::MAGIC_SIZE, std::fstream::beg)
99 .read(reinterpret_cast<char*>(buffer->data()), PGOProfilerHeader::VERSION_SIZE);
100 uint32_t checksum = adler32(0, reinterpret_cast<const Bytef*>(buffer->data()), PGOProfilerHeader::VERSION_SIZE);
101 // second, calculate the checksum for remaining content(exclude checksum field).
102 uint32_t remainingSize = size - PGOProfilerHeader::CHECKSUM_END_OFFSET;
103 fileStream.seekg(PGOProfilerHeader::CHECKSUM_END_OFFSET);
104 while (remainingSize > 0) {
105 uint32_t readSize = std::min(STEP_SIZE, remainingSize);
106 remainingSize = remainingSize - readSize;
107 fileStream.read(reinterpret_cast<char*>(buffer->data()), readSize);
108 checksum = adler32(checksum, reinterpret_cast<const Bytef*>(buffer->data()), readSize);
109 }
110 // third, write the checksum back to the checksum field in the output stream.
111 fileStream.seekp(PGOProfilerHeader::MAGIC_SIZE + PGOProfilerHeader::VERSION_SIZE, std::fstream::beg);
112 fileStream.write(reinterpret_cast<char*>(&checksum), sizeof(checksum));
113 }
114
TerminateSaveTask()115 void PGOProfilerEncoder::TerminateSaveTask()
116 {
117 Taskpool::GetCurrentTaskpool()->TerminateTask(GLOBAL_TASK_ID, TaskType::PGO_SAVE_TASK);
118 }
119
PostSaveTask(const std::string & path,ApGenMode mode,const std::shared_ptr<PGOInfo> pgoInfo)120 void PGOProfilerEncoder::PostSaveTask(const std::string& path,
121 ApGenMode mode,
122 const std::shared_ptr<PGOInfo> pgoInfo)
123 {
124 LOG_PGO(INFO) << "dispatch save task, path: " << path;
125 auto encoder = std::make_shared<PGOProfilerEncoder>(path, mode);
126 Taskpool::GetCurrentTaskpool()->PostTask(std::make_unique<SaveTask>(encoder, pgoInfo, GLOBAL_TASK_ID));
127 }
128
StartSaveTask(const std::shared_ptr<PGOInfo> info,const SaveTask * task)129 void PGOProfilerEncoder::StartSaveTask(const std::shared_ptr<PGOInfo> info, const SaveTask* task)
130 {
131 if (task == nullptr) {
132 return;
133 }
134 if (task->IsTerminate()) {
135 LOG_PGO(ERROR) << "save task is terminated";
136 return;
137 }
138 LockHolder lock(PGOProfilerManager::GetPGOInfoMutex());
139 InternalSave(info, task);
140 }
141
GetApGenMode() const142 ApGenMode PGOProfilerEncoder::GetApGenMode() const
143 {
144 return mode_;
145 }
146
SetApGenMode(ApGenMode mode)147 void PGOProfilerEncoder::SetApGenMode(ApGenMode mode)
148 {
149 mode_ = mode;
150 }
151 } // namespace panda::ecmascript::pgo
152