* Copyright (c) 2023 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ecmascript/pgo_profiler/pgo_profiler_encoder.h"
#include "common_components/taskpool/taskpool.h"
#include "ecmascript/pgo_profiler/pgo_profiler_manager.h"
#include "ecmascript/pgo_profiler/pgo_trace.h"
#include "ecmascript/platform/file.h"
#include "ecmascript/platform/filesystem.h"
#include "ecmascript/platform/os.h"
#include "zlib.h"
namespace panda::ecmascript::pgo {
bool PGOProfilerEncoder::Save(const std::shared_ptr<PGOInfo> pgoInfo)
{
return InternalSave(pgoInfo);
}
std::string PGOProfilerEncoder::GetDirectoryPath(const std::string& path) const
{
size_t lastSlash = path.find_last_of('/');
if (lastSlash == std::string::npos) {
return ".";
} else if (lastSlash == 0) {
return "/";
} else {
return path.substr(0, lastSlash);
}
}
bool PGOProfilerEncoder::WriteProfilerFile(const std::shared_ptr<PGOInfo> info, const std::string& tmpOutPath)
{
umask(S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
std::fstream fileStream(tmpOutPath.c_str(),
std::fstream::binary | std::fstream::out | std::fstream::in | std::fstream::trunc);
if (!fileStream.is_open()) {
LOG_PGO(ERROR) << "can't open the file path (" << tmpOutPath << ") errno: " << errno;
return false;
}
if (info->GetHeaderPtr() == nullptr) {
LOG_PGO(FATAL) << "[PGOProfilerEncoder::SaveAndRename] header_ is not initialized";
}
info->GetPandaFileInfos().ProcessToBinary(fileStream, info->GetHeaderPtr()->GetPandaInfoSection());
info->GetRecordDetailInfosPtr()->ProcessToBinary(fileStream, info->GetHeaderPtr());
PGOFileSectionInterface::ProcessSectionToBinary(
info->GetRecordDetailInfos(), fileStream, info->GetHeaderPtr(), *info->GetAbcFilePool().GetPool());
info->GetHeaderPtr()->SetFileSize(static_cast<uint32_t>(fileStream.tellp()));
info->GetHeaderPtr()->SetCompatibleAnVersion(AOTFileVersion::AN_VERSION);
info->GetHeaderPtr()->ProcessToBinary(fileStream);
if (info->GetHeaderPtr()->SupportFileConsistency()) {
AddChecksum(fileStream);
}
fileStream.close();
umask(0);
return fileStream.good();
}
bool PGOProfilerEncoder::ValidateAndRename(const std::string& tmpOutPath)
{
if (filesystem::FileSize(tmpOutPath.c_str()) > MAX_AP_FILE_SIZE) {
LOG_PGO(ERROR) << "ap file size is larger than " << MAX_AP_FILE_SIZE / CONVERT_FACTOR << "MB";
remove(tmpOutPath.c_str());
return false;
}
if (FileExist(path_.c_str()) && remove(path_.c_str())) {
LOG_PGO(ERROR) << "remove " << path_ << " failed, errno: " << errno;
return false;
}
if (rename(tmpOutPath.c_str(), path_.c_str())) {
LOG_PGO(ERROR) << "rename " << tmpOutPath << " to " << path_ << " failed, errno: " << errno;
return false;
}
return true;
}
bool PGOProfilerEncoder::SaveAndRename(const std::shared_ptr<PGOInfo> info)
{
ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "PGOProfilerEncoder::SaveAndRename", "");
LOG_PGO(INFO) << "start save and rename ap file to " << path_;
ClockScope start;
std::string dirPath = GetDirectoryPath(path_);
if (!CheckDiskSpace(dirPath, MIN_DISK_SPACE)) {
LOG_PGO(ERROR) << "insufficient disk space, " << MIN_DISK_SPACE / CONVERT_FACTOR << "MB required, path: ["
<< dirPath << "]";
return false;
}
static const char* tempSuffix = ".tmp";
auto tmpPath = path_ + "." + std::to_string(getpid()) + tempSuffix;
std::string tmpOutPath;
if (!RealPath(tmpPath, tmpOutPath, false)) {
LOG_PGO(ERROR) << "Fail to get realPath: " << tmpPath;
return false;
}
if (!WriteProfilerFile(info, tmpOutPath)) {
LOG_PGO(ERROR) << "failed to write or close file, errno: " << errno << ", " << strerror(errno);
remove(tmpOutPath.c_str());
return false;
}
if (!ValidateAndRename(tmpOutPath)) {
return false;
}
PGOProfilerManager::GetInstance()->RequestAot();
if (PGOTrace::GetInstance()->IsEnable()) {
PGOTrace::GetInstance()->SetSaveTime(start.TotalSpentTime());
PGOTrace::GetInstance()->Print();
}
LOG_PGO(INFO) << "successfully save and rename ap file to " << path_;
return true;
}
bool PGOProfilerEncoder::InternalSave(const std::shared_ptr<PGOInfo> rtInfo)
{
ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "PGOProfilerEncoder::InternalSave", "");
if (rtInfo == nullptr) {
LOG_PGO(ERROR) << "pgo info is nullptr";
return false;
}
LOG_PGO(INFO) << (mode_ == MERGE ? "MERGE(1)" : "OVERWRITE(0)") << " pgo info";
if ((mode_ == MERGE) && FileExist(path_.c_str())) {
auto info = std::make_shared<PGOInfo>(rtInfo->GetHotnessThreshold());
PGOProfilerDecoder decoder(path_, rtInfo->GetHotnessThreshold());
info->MergeWithExistProfile(*rtInfo, decoder);
return SaveAndRename(info);
}
return SaveAndRename(rtInfo);
}
void PGOProfilerEncoder::AddChecksum(std::fstream& fileStream)
{
static constexpr uint32_t KILO_BYTES = 1024;
static constexpr uint32_t STEP_IN_KB = 256;
static constexpr uint32_t STEP_SIZE = STEP_IN_KB * KILO_BYTES;
uint32_t size = static_cast<uint32_t>(fileStream.seekp(0, std::fstream::end).tellp());
std::unique_ptr<std::vector<uint8_t>> buffer = std::make_unique<std::vector<uint8_t>>(STEP_SIZE);
fileStream.seekg(PGOProfilerHeader::MAGIC_SIZE, std::fstream::beg)
.read(reinterpret_cast<char*>(buffer->data()), PGOProfilerHeader::VERSION_SIZE);
uint32_t checksum = adler32(0, reinterpret_cast<const Bytef*>(buffer->data()), PGOProfilerHeader::VERSION_SIZE);
uint32_t remainingSize = size - PGOProfilerHeader::CHECKSUM_END_OFFSET;
fileStream.seekg(PGOProfilerHeader::CHECKSUM_END_OFFSET);
while (remainingSize > 0) {
uint32_t readSize = std::min(STEP_SIZE, remainingSize);
remainingSize = remainingSize - readSize;
fileStream.read(reinterpret_cast<char*>(buffer->data()), readSize);
checksum = adler32(checksum, reinterpret_cast<const Bytef*>(buffer->data()), readSize);
}
fileStream.seekp(PGOProfilerHeader::MAGIC_SIZE + PGOProfilerHeader::VERSION_SIZE, std::fstream::beg);
fileStream.write(reinterpret_cast<char*>(&checksum), sizeof(checksum));
}
ApGenMode PGOProfilerEncoder::GetApGenMode() const
{
return mode_;
}
void PGOProfilerEncoder::SetApGenMode(ApGenMode mode)
{
mode_ = mode;
}
}