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#include <sys/stat.h> 22 23#include "ecmascript/log_wrapper.h" 24#include "ecmascript/mem/c_string.h" 25#include "ecmascript/ohos/enable_aot_list_helper.h" 26#include "ecmascript/pgo_profiler/ap_file/pgo_file_info.h" 27#include "ecmascript/pgo_profiler/pgo_profiler_decoder.h" 28#include "ecmascript/pgo_profiler/pgo_profiler_encoder.h" 29#include "ecmascript/pgo_profiler/pgo_profiler_manager.h" 30#include "ecmascript/pgo_profiler/pgo_trace.h" 31#include "ecmascript/pgo_profiler/pgo_utils.h" 32#include "ecmascript/platform/file.h" 33#include "ecmascript/platform/mutex.h" 34#include "ecmascript/platform/os.h" 35 36namespace panda::ecmascript::pgo { 37void PGOProfilerEncoder::Destroy() 38{ 39 LockHolder lock(mutex_); 40 { 41 WriteLockHolder rwLock(rwLock_); 42 pandaFileInfos_->Clear(); 43 abcFilePool_->Clear(); 44 } 45 if (!isProfilingInitialized_) { 46 return; 47 } 48 PGOProfilerHeader::Destroy(&header_); 49 globalRecordInfos_->Clear(); 50 globalRecordInfos_.reset(); 51 isProfilingInitialized_ = false; 52} 53 54bool PGOProfilerEncoder::ResetOutPathByModuleName(const std::string &moduleName) 55{ 56 LockHolder lock(mutex_); 57 // only first assign takes effect 58 if (!moduleName_.empty() || moduleName.empty()) { 59 return false; 60 } 61 moduleName_ = moduleName; 62 return ResetOutPath(ApNameUtils::GetRuntimeApName(moduleName_)); 63} 64 65bool PGOProfilerEncoder::ResetOutPath(const std::string &profileFileName) 66{ 67 if (!RealPath(outDir_, realOutPath_, false)) { 68 return false; 69 } 70 71 auto suffixLength = ApNameUtils::AP_SUFFIX.length(); 72 if (realOutPath_.compare(realOutPath_.length() - suffixLength, suffixLength, ApNameUtils::AP_SUFFIX)) { 73 realOutPath_ += "/" + profileFileName; 74 } 75 76 SetSecurityLabel(realOutPath_); 77 78 LOG_ECMA(INFO) << "Save profiler to file:" << realOutPath_; 79 return true; 80} 81 82bool PGOProfilerEncoder::InitializeData() 83{ 84 LockHolder lock(mutex_); 85 if (!isProfilingInitialized_) { 86 if (!ResetOutPath(ApNameUtils::DEFAULT_AP_NAME)) { 87 return false; 88 } 89 PGOProfilerHeader::Build(&header_, PGOProfilerHeader::LastSize()); 90 globalRecordInfos_ = std::make_shared<PGORecordDetailInfos>(hotnessThreshold_); 91 isProfilingInitialized_ = true; 92 } 93 return true; 94} 95 96void PGOProfilerEncoder::SamplePandaFileInfo(uint32_t checksum, const CString &abcName) 97{ 98 WriteLockHolder lock(rwLock_); 99 pandaFileInfos_->Sample(checksum); 100 ApEntityId entryId(0); 101 abcFilePool_->TryAdd(abcName, entryId); 102} 103 104bool PGOProfilerEncoder::GetPandaFileId(const CString &abcName, ApEntityId &entryId) 105{ 106 ReadLockHolder lock(rwLock_); 107 return abcFilePool_->GetEntryId(abcName, entryId); 108} 109 110bool PGOProfilerEncoder::GetPandaFileDesc(ApEntityId abcId, CString &desc) 111{ 112 if (!isProfilingInitialized_) { 113 return false; 114 } 115 ReadLockHolder lock(rwLock_); 116 const auto *entry = abcFilePool_->GetEntry(abcId); 117 if (entry == nullptr) { 118 return false; 119 } 120 desc = entry->GetData(); 121 return true; 122} 123 124void PGOProfilerEncoder::Merge(const PGORecordDetailInfos &recordInfos) 125{ 126 if (!isProfilingInitialized_) { 127 return; 128 } 129 LockHolder lock(mutex_); 130 globalRecordInfos_->Merge(recordInfos); 131} 132 133void PGOProfilerEncoder::Merge(const PGOPandaFileInfos &pandaFileInfos) 134{ 135 WriteLockHolder lock(rwLock_); 136 return pandaFileInfos_->Merge(pandaFileInfos); 137} 138 139void PGOProfilerEncoder::Merge(const PGOProfilerEncoder &encoder) 140{ 141 Merge(*encoder.pandaFileInfos_); 142 Merge(*encoder.globalRecordInfos_); 143} 144 145bool PGOProfilerEncoder::VerifyPandaFileMatched(const PGOPandaFileInfos &pandaFileInfos, const std::string &base, 146 const std::string &incoming) const 147{ 148 return pandaFileInfos_->VerifyChecksum(pandaFileInfos, base, incoming); 149} 150 151bool PGOProfilerEncoder::Save() 152{ 153 if (!isProfilingInitialized_) { 154 return false; 155 } 156 LockHolder lock(mutex_); 157 return InternalSave(); 158} 159 160void PGOProfilerEncoder::MergeWithExistProfile(PGOProfilerEncoder &runtimeEncoder, PGOProfilerDecoder &decoder, 161 const SaveTask *task) 162{ 163 // inherit some info from runtime encoder 164 ASSERT(header_ != nullptr); 165 ASSERT(runtimeEncoder.header_ != nullptr); 166 header_->SetVersion(runtimeEncoder.header_->GetVersion()); 167 bundleName_ = runtimeEncoder.bundleName_; 168 moduleName_ = runtimeEncoder.moduleName_; 169 170 // copy abcFilePool from runtime to temp merger. 171 ASSERT(abcFilePool_->GetPool()->Empty()); 172 { 173 WriteLockHolder lock(rwLock_); 174 abcFilePool_->Copy(runtimeEncoder.abcFilePool_); 175 } 176 if (!decoder.LoadFull(abcFilePool_)) { 177 LOG_ECMA(ERROR) << "Fail to load ap: " << realOutPath_; 178 } else { 179 Merge(decoder.GetPandaFileInfos()); 180 globalRecordInfos_ = decoder.GetRecordDetailInfosPtr(); 181 } 182 if (task && task->IsTerminate()) { 183 LOG_ECMA(DEBUG) << "ProcessProfile: task is already terminate"; 184 return; 185 } 186 Merge(*runtimeEncoder.pandaFileInfos_); 187 if (task && task->IsTerminate()) { 188 LOG_ECMA(DEBUG) << "ProcessProfile: task is already terminate"; 189 return; 190 } 191 Merge(*runtimeEncoder.globalRecordInfos_); 192} 193 194bool PGOProfilerEncoder::SaveAndRename(const SaveTask *task) 195{ 196 ClockScope start; 197 umask(S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); 198 static const char *tempSuffix = ".tmp"; 199 auto tmpOutPath = realOutPath_ + "." + std::to_string(getpid()) + tempSuffix; 200 std::fstream fileStream(tmpOutPath.c_str(), 201 std::fstream::binary | std::fstream::out | std::fstream::in | std::fstream::trunc); 202 if (!fileStream.is_open()) { 203 LOG_ECMA(ERROR) << "The file path(" << tmpOutPath << ") open failure! errno: " << errno; 204 return false; 205 } 206 if (header_ == nullptr) { 207 LOG_ECMA(FATAL) << "PGOProfilerEncoder::SaveAndRename:header_ is nullptr"; 208 } 209 pandaFileInfos_->ProcessToBinary(fileStream, header_->GetPandaInfoSection()); 210 globalRecordInfos_->ProcessToBinary(task, fileStream, header_); 211 { 212 ReadLockHolder lock(rwLock_); 213 PGOFileSectionInterface::ProcessSectionToBinary(*globalRecordInfos_, fileStream, header_, 214 *abcFilePool_->GetPool()); 215 } 216 header_->SetFileSize(static_cast<uint32_t>(fileStream.tellp())); 217 header_->SetCompatibleAnVersion(AOTFileVersion::AN_VERSION); 218 header_->ProcessToBinary(fileStream); 219 if (header_->SupportFileConsistency()) { 220 AddChecksum(fileStream); 221 } 222 fileStream.close(); 223 if (task && task->IsTerminate()) { 224 LOG_ECMA(DEBUG) << "ProcessProfile: task is already terminate"; 225 return false; 226 } 227 if (FileExist(realOutPath_.c_str()) && remove(realOutPath_.c_str())) { 228 LOG_ECMA(ERROR) << "Remove " << realOutPath_ << " failure!, errno: " << errno; 229 return false; 230 } 231 if (rename(tmpOutPath.c_str(), realOutPath_.c_str())) { 232 LOG_ECMA(ERROR) << "Rename " << tmpOutPath << " --> " << realOutPath_ << " failure!, errno: " << errno; 233 return false; 234 } 235 RequestAot(); 236 if (PGOTrace::GetInstance()->IsEnable()) { 237 PGOTrace::GetInstance()->SetSaveTime(start.TotalSpentTime()); 238 PGOTrace::GetInstance()->Print(); 239 } 240 return true; 241} 242 243void PGOProfilerEncoder::RequestAot() 244{ 245 if (bundleName_.empty() || moduleName_.empty()) { 246 return; 247 } 248 249 LOG_ECMA(INFO) << "Request local aot, bundle: " << bundleName_ << ", module: " << moduleName_; 250 if (!PGOProfilerManager::GetInstance()->RequestAot(bundleName_, moduleName_, RequestAotMode::RE_COMPILE_ON_IDLE)) { 251 LOG_ECMA(ERROR) << "Request aot failed, bundle: " << bundleName_ << ", module: " << moduleName_; 252 } 253} 254 255bool PGOProfilerEncoder::InternalSave(const SaveTask *task) 256{ 257 ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "PGOProfilerEncoder::InternalSave"); 258 if (!isProfilingInitialized_) { 259 return false; 260 } 261 if ((mode_ == MERGE) && FileExist(realOutPath_.c_str())) { 262 PGOProfilerEncoder encoder(realOutPath_, hotnessThreshold_, mode_); 263 encoder.InitializeData(); 264 PGOProfilerDecoder decoder(realOutPath_, hotnessThreshold_); 265 { 266 ClockScope start; 267 encoder.MergeWithExistProfile(*this, decoder, task); 268 if (PGOTrace::GetInstance()->IsEnable()) { 269 PGOTrace::GetInstance()->SetMergeWithExistProfileTime(start.TotalSpentTime()); 270 } 271 } 272 auto saveAndRenameResult = encoder.SaveAndRename(task); 273 encoder.Destroy(); 274 return saveAndRenameResult; 275 } 276 return SaveAndRename(task); 277} 278 279void PGOProfilerEncoder::AddChecksum(std::fstream &fileStream) 280{ 281 static constexpr uint32_t KILO_BYTES = 1024; 282 static constexpr uint32_t STEP_IN_KB = 256; 283 static constexpr uint32_t STEP_SIZE = STEP_IN_KB * KILO_BYTES; 284 uint32_t size = static_cast<uint32_t>(fileStream.seekp(0, std::fstream::end).tellp()); 285 std::unique_ptr<std::vector<uint8_t>> buffer = std::make_unique<std::vector<uint8_t>>(STEP_SIZE); 286 // first, calculate the version field's checksum. 287 fileStream.seekg(PGOProfilerHeader::MAGIC_SIZE, std::fstream::beg) 288 .read(reinterpret_cast<char *>(buffer->data()), PGOProfilerHeader::VERSION_SIZE); 289 uint32_t checksum = adler32(0, reinterpret_cast<const Bytef *>(buffer->data()), PGOProfilerHeader::VERSION_SIZE); 290 // second, calculate the checksum for remaining content(exclude checksum field). 291 uint32_t remainingSize = size - PGOProfilerHeader::CHECKSUM_END_OFFSET; 292 fileStream.seekg(PGOProfilerHeader::CHECKSUM_END_OFFSET); 293 while (remainingSize > 0) { 294 uint32_t readSize = std::min(STEP_SIZE, remainingSize); 295 remainingSize = remainingSize - readSize; 296 fileStream.read(reinterpret_cast<char *>(buffer->data()), readSize); 297 checksum = adler32(checksum, reinterpret_cast<const Bytef *>(buffer->data()), readSize); 298 } 299 // third, write the checksum back to the checksum field in the output stream. 300 fileStream.seekp(PGOProfilerHeader::MAGIC_SIZE + PGOProfilerHeader::VERSION_SIZE, std::fstream::beg); 301 fileStream.write(reinterpret_cast<char *>(&checksum), sizeof(checksum)); 302} 303 304void PGOProfilerEncoder::TerminateSaveTask() 305{ 306 if (!isProfilingInitialized_) { 307 return; 308 } 309 Taskpool::GetCurrentTaskpool()->TerminateTask(GLOBAL_TASK_ID, TaskType::PGO_SAVE_TASK); 310} 311 312void PGOProfilerEncoder::PostSaveTask() 313{ 314 if (!isProfilingInitialized_) { 315 return; 316 } 317 Taskpool::GetCurrentTaskpool()->PostTask(std::make_unique<SaveTask>(this, GLOBAL_TASK_ID)); 318} 319 320void PGOProfilerEncoder::PostResetOutPathTask(const std::string &moduleName) 321{ 322 if (moduleName.empty()) { 323 LOG_ECMA(ERROR) << "PostSetModuleNameTask: moduleName is empty."; 324 return; 325 } 326 // only post moduleName once 327 bool hasPost = false; 328 if (!hasPostModuleName_.compare_exchange_strong(hasPost, true)) { 329 return; 330 } 331 Taskpool::GetCurrentTaskpool()->PostTask(std::make_unique<ResetOutPathTask>(this, moduleName, GLOBAL_TASK_ID)); 332} 333 334void PGOProfilerEncoder::StartSaveTask(const SaveTask *task) 335{ 336 if (task == nullptr) { 337 return; 338 } 339 if (task->IsTerminate()) { 340 LOG_ECMA(ERROR) << "StartSaveTask: task is already terminate"; 341 return; 342 } 343 LockHolder lock(mutex_); 344 InternalSave(task); 345} 346 347bool PGOProfilerEncoder::LoadAPTextFile(const std::string &inPath) 348{ 349 if (!isProfilingInitialized_) { 350 return false; 351 } 352 std::string realPath; 353 if (!RealPath(inPath, realPath)) { 354 return false; 355 } 356 357 std::ifstream fileStream(realPath.c_str()); 358 if (!fileStream.is_open()) { 359 LOG_ECMA(ERROR) << "The file path(" << realOutPath_ << ") open failure!"; 360 return false; 361 } 362 363 if (!header_->ParseFromText(fileStream)) { 364 LOG_ECMA(ERROR) << "header format error"; 365 return false; 366 } 367 if (!pandaFileInfos_->ParseFromText(fileStream)) { 368 LOG_ECMA(ERROR) << "panda file info format error"; 369 return false; 370 } 371 if (!globalRecordInfos_->ParseFromText(fileStream)) { 372 LOG_ECMA(ERROR) << "record info format error"; 373 return false; 374 } 375 376 return true; 377} 378} // namespace panda::ecmascript::pgo 379