* Copyright (c) 2025 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 "modules_snapshot_helper.h"
#include <zlib.h>
#include "ecmascript/base/error_helper.h"
#include "ecmascript/base/string_helper.h"
#include "ecmascript/js_runtime_options.h"
#include "ecmascript/js_thread.h"
#include "ecmascript/js_tagged_value-inl.h"
#include "ecmascript/log_wrapper.h"
#include "ecmascript/platform/file.h"
#include "ecmascript/platform/filesystem.h"
#include "ecmascript/platform/signal_manager.h"
#include "ecmascript/platform/time.h"
#include "ecmascript/serializer/serialize_data.h"
#include <csignal>
#include <cstddef>
#include <memory>
#include <mutex>
#include <securec.h>
#include <string>
#include <string_view>
#include <system_error>
#include <unistd.h>
#include <vector>
namespace panda::ecmascript {
bool ModulesSnapshotHelper::g_escaperDisabled_ = false;
int ModulesSnapshotHelper::g_featureState_ = static_cast<int>(SnapshotFeatureState::DEFAULT);
int ModulesSnapshotHelper::g_featureLoaded_ = static_cast<int>(SnapshotFeatureState::DEFAULT);
char ModulesSnapshotHelper::g_stateFilePathBuffer_[PATH_MAX] = {};
volatile bool ModulesSnapshotHelper::g_escaperTriggered_ {false};
ModulesSnapshotHelper::FileGuard::FileGuard(const CString &path, uint32_t tid)
: target_(path), temp_(path + "." + CString(std::to_string(tid)))
{
}
ModulesSnapshotHelper::FileGuard::~FileGuard()
{
if (!done_) {
Unlink(temp_.c_str());
}
}
bool ModulesSnapshotHelper::FileGuard::Done()
{
if (done_) {
return true;
}
auto err = Rename(temp_.c_str(), target_.c_str(), false);
if (err) {
LOG_ECMA(ERROR) << "Failed to rename to: '" << target_ << "', error: (" << err.value() << ")" << err.message();
return false;
}
done_ = true;
return true;
}
void ModulesSnapshotHelper::InitEscaper(EcmaVM *vm)
{
static std::once_flag flag{};
std::call_once(flag, [vm]() {
if ((g_featureState_ & static_cast<int>(SnapshotFeatureState::MODULE)) != 0 &&
(g_featureState_ & static_cast<int>(SnapshotFeatureState::PANDAFILE)) != 0) {
return;
}
vm->RegisterExtraJSCrashMessageCallback("modules snapshot escaper", []([[maybe_unused]] const EcmaVM* vm) {
if (DoNeedEscape()) {
TryDisableSnapshot(vm->GetJSThread());
}
return "";
});
constexpr const int SIGDUMP = 35;
std::vector registeredSignos{
#ifdef SIGTRAP
SIGTRAP,
#endif
#ifdef SIGABRT
SIGABRT,
#endif
#ifdef SIGBUS
SIGBUS,
#endif
#ifdef SIGFPE
SIGFPE,
#endif
#ifdef SIGSEGV
SIGSEGV,
#endif
#ifdef SIGSTKFLT
SIGSTKFLT,
#endif
SIGDUMP,
};
if (registeredSignos.empty()) {
return;
}
DECL_SIGCHAIN_ACTION(action);
if (!SignalManager::InitSigchainAction(action, registeredSignos, SigchainHandler)) {
return;
}
for (auto signo : registeredSignos) {
SignalManager::GetSignalManager(signo).AddSpecialHandler(action);
}
});
}
bool ModulesSnapshotHelper::SigchainHandler(int signo, void *info, void *ucontext)
{
if (!DoNeedEscape()) {
return false;
}
TryDisableSnapshot(signo);
return false;
}
void ModulesSnapshotHelper::MarkModuleSnapshotLoaded()
{
g_featureLoaded_ |= static_cast<int>(SnapshotFeatureState::MODULE);
}
void ModulesSnapshotHelper::MarkJSPandaFileSnapshotLoaded()
{
g_featureLoaded_ |= static_cast<int>(SnapshotFeatureState::PANDAFILE);
}
void ModulesSnapshotHelper::MarkSnapshotDisabledByOption()
{
g_featureState_ =
static_cast<int>(SnapshotFeatureState::PANDAFILE) | static_cast<int>(SnapshotFeatureState::MODULE);
}
bool ModulesSnapshotHelper::TryDisableSnapshot(int reason)
{
if (reason <= 0) {
return TryDisableSnapshot(DISABLE_REASON_UNKNOWN);
}
constexpr size_t SIG_MAX_DIGITS = 2;
char sigBuf[SIG_MAX_DIGITS]{};
auto written = IntToString(reason, sigBuf, SIG_MAX_DIGITS);
return TryDisableSnapshot(DISABLE_REASON_SIGNAL, std::string_view(sigBuf, written));
}
bool ModulesSnapshotHelper::TryDisableSnapshot(const std::string_view& reason, const std::string_view& extraInfo)
{
int64_t timestamp = GetCurrentTimestamp();
PosixFile stateFile(g_stateFilePathBuffer_, "w");
if (!stateFile.IsValid()) {
return false;
}
std::string_view disableWord = "";
if ((g_featureLoaded_ & static_cast<int>(SnapshotFeatureState::MODULE)) != 0 &&
(g_featureLoaded_ & static_cast<int>(SnapshotFeatureState::PANDAFILE)) != 0) {
disableWord = &STATE_WORD_MODULE_SNAPSHOT_DISABLED;
} else {
disableWord = &STATE_WORD_ALL_SNAPSHOT_DISABLED;
}
if (auto written = stateFile.Write(disableWord);
written > 0 && static_cast<size_t>(written) < disableWord.size()) {
return false;
}
stateFile.Write(":", 1);
stateFile.Write(reason);
stateFile.Write(extraInfo);
constexpr size_t TIMESTAMP_MAX_DIGITS = 20;
char timestampBuf[TIMESTAMP_MAX_DIGITS]{};
stateFile.Write(std::string_view(", timestamp: "));
auto written = IntToString(timestamp, timestampBuf, TIMESTAMP_MAX_DIGITS);
stateFile.Write(timestampBuf, written);
stateFile.Write(std::string_view("\n"));
return true;
}
void ModulesSnapshotHelper::TryDisableSnapshot(JSThread* thread)
{
if (!DoNeedEscape()) {
return;
}
if (!TryDisableSnapshot(DISABLE_REASON_UNCAUGHT_EXCEPTION)) {
return;
}
LOG_ECMA(WARN) << "js crash occurred, try to disable snapshot";
if (thread == nullptr || !thread->HasPendingException()) {
return;
}
JSHandle<JSTaggedValue> exceptionHandle(thread, thread->GetException());
if (!exceptionHandle->IsJSError()) {
return;
}
thread->ClearException();
std::string content {};
content += "name: ";
content += base::ErrorHelper::GetJSErrorInfo(thread, exceptionHandle, base::ErrorHelper::JSErrorProps::NAME);
content += "\n";
content += "message: ";
content += base::ErrorHelper::GetJSErrorInfo(thread, exceptionHandle, base::ErrorHelper::JSErrorProps::MESSAGE);
content += "\n";
content += base::ErrorHelper::GetJSErrorInfo(thread, exceptionHandle, base::ErrorHelper::JSErrorProps::STACK);
content += "\n";
PosixFile stateFile(g_stateFilePathBuffer_, "r+");
stateFile.Seek(0, PosixFile::SeekOrigin::END);
stateFile.Write(content);
LOG_ECMA(WARN) << "uncaught exception occurs, modues and (or) pandafile snapshot(s) will be disabled next time.\n"
<< content;
thread->SetException(exceptionHandle.GetTaggedValue());
}
void ModulesSnapshotHelper::TryDisableSnapshotOnANR()
{
if (!DoNeedEscape()) {
return;
}
TryDisableSnapshot(DISABLE_APPLICATION_NOT_RESPONDING);
}
void ModulesSnapshotHelper::DisableSnapshotEscaper() { g_escaperDisabled_ = true; }
void ModulesSnapshotHelper::RemoveSnapshotFiles(const CString &path)
{
if (FileExist(path.data())) {
DeleteFilesWithSuffix(path.data(), SNAPSHOT_FILE_SUFFIX.data());
}
}
void ModulesSnapshotHelper::RemoveFile(const CString& path)
{
if (FileExist(path.data())) {
Unlink(path.c_str());
}
}
void ModulesSnapshotHelper::HandleCorruptedFile(const CString& dir, const CString& endsWith)
{
if (!FileExist(dir.data())) {
return;
}
const std::string backupSuffix(BACKUP_FILE_SUFFIX);
auto err = Iterdir(dir.c_str(), [&dir, &endsWith, &backupSuffix](const std::string_view& name, FileType fileType) {
if (fileType != FileType::REGULAR) {
return;
}
if (name.size() < endsWith.size() || name.rfind(endsWith) != (name.size() - endsWith.size())) {
return;
}
std::string fullPath(dir);
fullPath += name;
if (FileExist(fullPath.c_str())) {
Rename(fullPath.c_str(), (fullPath + backupSuffix).c_str(), true);
}
});
if (err) {
LOG_ECMA(ERROR) << "HandleCorruptedFile failed, error: " << err.message();
}
}
bool ModulesSnapshotHelper::SetReadOnly(const std::string_view &path, std::string *errorMsg)
{
std::error_code ec{};
if (Chmod(path, FILE_MODE_READONLY, ec)) {
return true;
}
if (errorMsg != nullptr) {
*errorMsg = ec.message();
} else {
LOG_ECMA(WARN) << "Failed to chmod file '" << path << "' to read-only, error: " << ec.message();
}
return false;
}
bool ModulesSnapshotHelper::WriteFileHeader(FileMemMapWriter& writer, const SnapshotVersionInfo::UniquePtr& header)
{
if (!writer.WriteSingleData(header.get(), header->Size(), "SnapshotFileHeader")) {
return false;
}
return true;
}
std::string ModulesSnapshotHelper::ReadFileHeader(FileMemMapReader& reader,
const SnapshotVersionInfo::UniquePtr& header)
{
auto mHeader = SnapshotVersionInfo::FromBuffer(reader.GetReadPtr());
if (!reader.Step(mHeader->Size())) {
return "read snapshot file version info and description failed";
}
auto returnMismatch = [](const std::string_view& title, const std::string_view& read,
const std::string_view& current) {
std::string errMsg = std::string(title) + " mismatch, read: " + std::string(read);
errMsg += ", current: " + std::string(current);
return errMsg;
};
if (mHeader->appVersionCode_ != header->appVersionCode_) {
return returnMismatch("application version code", std::to_string(mHeader->appVersionCode_),
std::to_string(header->appVersionCode_));
}
auto mRomVersion = mHeader->GetRomVersion();
auto romVersion = header->GetRomVersion();
auto mDesc = mHeader->GetDescription();
auto desc = header->GetDescription();
if (romVersion != mRomVersion) {
return returnMismatch("ROM version", mRomVersion, romVersion);
}
if (desc != mDesc) {
return returnMismatch("snapshot file description", mDesc, desc);
}
return "";
}
bool ModulesSnapshotHelper::WriteDataInfo(FileMemMapWriter& writer, const std::unique_ptr<SerializeData>& data)
{
SerializeDataInfo info {
.dataIndex_ = data->dataIndex_,
.sizeLimit_ = data->sizeLimit_,
.bufferSize_ = data->bufferSize_,
.bufferCapacity_ = data->bufferCapacity_,
.regularSpaceSize_ = data->regularSpaceSize_,
.pinSpaceSize_ = data->pinSpaceSize_,
.oldSpaceSize_ = data->oldSpaceSize_,
.nonMovableSpaceSize_ = data->nonMovableSpaceSize_,
.machineCodeSpaceSize_ = data->machineCodeSpaceSize_,
.sharedOldSpaceSize_ = data->sharedOldSpaceSize_,
.sharedNonMovableSpaceSize_ = data->sharedNonMovableSpaceSize_,
.incompleteData_ = data->incompleteData_,
};
CString message("serializeDataInfo");
return writer.WriteSingleData(&info, sizeof(info), message);
}
bool ModulesSnapshotHelper::ReadDataInfo(FileMemMapReader& reader, const std::unique_ptr<SerializeData>& data)
{
auto info = reinterpret_cast<const SerializeDataInfo*>(reader.GetReadPtr());
if (!reader.Step(sizeof(SerializeDataInfo))) {
return false;
}
data->dataIndex_ = info->dataIndex_;
data->sizeLimit_ = info->sizeLimit_;
data->bufferSize_ = info->bufferSize_;
data->bufferCapacity_ = info->bufferCapacity_;
data->regularSpaceSize_ = info->regularSpaceSize_;
data->pinSpaceSize_ = info->pinSpaceSize_;
data->oldSpaceSize_ = info->oldSpaceSize_;
data->nonMovableSpaceSize_ = info->nonMovableSpaceSize_;
data->machineCodeSpaceSize_ = info->machineCodeSpaceSize_;
data->sharedOldSpaceSize_ = info->sharedOldSpaceSize_;
data->sharedNonMovableSpaceSize_ = info->sharedNonMovableSpaceSize_;
data->incompleteData_ = info->incompleteData_;
return true;
}
bool ModulesSnapshotHelper::WriteDataToFile(const std::unique_ptr<SerializeData>& data,
const CString& filePath, const SnapshotVersionInfo::UniquePtr& header,
const CString& logPrefix)
{
const CString logTag = logPrefix + "::WriteDataToFile";
LOG_ECMA(DEBUG) << logTag;
ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, logTag.c_str(), "");
const uint32_t appVersionCode = header->appVersionCode_;
constexpr const uint32_t checksumSize = sizeof(uint32_t);
auto calcTotalSize = [&data, &header]() {
size_t totalSize = header->Size();
totalSize += sizeof(ModulesSnapshotHelper::SerializeDataInfo);
if (g_isEnableCMCGC) {
totalSize += CMC_GC_REGION_SIZE * sizeof(size_t);
totalSize += data->regularRemainSizeVector_.size() * sizeof(size_t);
totalSize += data->pinRemainSizeVector_.size() * sizeof(size_t);
} else {
totalSize += SERIALIZE_SPACE_NUM * sizeof(size_t);
size_t totalVecBytes = 0;
for (const auto& vec : data->regionRemainSizeVectors_) {
totalVecBytes += vec.size() * sizeof(size_t);
}
totalSize += totalVecBytes;
}
totalSize += data->bufferSize_;
totalSize += checksumSize;
return totalSize;
};
const size_t totalSize = calcTotalSize();
if (FileExist(filePath.c_str())) {
LOG_ECMA(WARN) << logTag << " snapshot file already exist: " << filePath;
return false;
}
ModulesSnapshotHelper::FileGuard guard(filePath, JSThread::GetCurrentThreadId());
MemMap fileMapMem =
CreateFileMap(guard.GetTempPath().c_str(), totalSize, FILE_RDWR | FILE_CREAT | FILE_TRUNC, PAGE_PROT_READWRITE);
if (fileMapMem.GetOriginAddr() == nullptr) {
LOG_ECMA(ERROR) << logTag << " File mmap failed";
return false;
}
MemMapScope memMapScope(fileMapMem);
FileMemMapWriter writer(fileMapMem, logTag);
if (!ModulesSnapshotHelper::WriteFileHeader(writer, header)) {
return false;
}
if (!WriteDataInfo(writer, data)) {
return false;
}
if (g_isEnableCMCGC) {
size_t regularRemainSize = data->regularRemainSizeVector_.size();
if (!writer.WriteSingleData(®ularRemainSize, sizeof(regularRemainSize), "regularRemainSize")) {
return false;
}
size_t regularRemainLen = regularRemainSize * sizeof(size_t);
if (regularRemainLen > 0) {
if (!writer.WriteSingleData(data->regularRemainSizeVector_.data(), regularRemainLen,
"regularRemainSizeVector")) {
return false;
}
}
size_t pinRemainSize = data->pinRemainSizeVector_.size();
if (!writer.WriteSingleData(&pinRemainSize, sizeof(pinRemainSize), "pinRemainSize")) {
return false;
}
size_t pinRemainLen = pinRemainSize * sizeof(size_t);
if (pinRemainLen > 0) {
if (!writer.WriteSingleData(data->pinRemainSizeVector_.data(), pinRemainLen, "pinRemainSizeVector")) {
return false;
}
}
} else {
std::array<size_t, SERIALIZE_SPACE_NUM> vecSizes;
for (int i = 0; i < SERIALIZE_SPACE_NUM; ++i) {
vecSizes[i] = data->regionRemainSizeVectors_[i].size();
}
uint32_t vecSize = SERIALIZE_SPACE_NUM * sizeof(size_t);
if (!writer.WriteSingleData(vecSizes.data(), vecSize, "vecSizes")) {
return false;
}
for (const auto& vec : data->regionRemainSizeVectors_) {
if (!vec.empty()) {
uint32_t curVectorDataSize = vec.size() * sizeof(size_t);
if (!writer.WriteSingleData(vec.data(), curVectorDataSize, "vec")) {
return false;
}
}
}
}
if (data->bufferSize_ > 0) {
if (!data->buffer_) {
return false;
}
if (!writer.WriteSingleData(data->buffer_, data->bufferSize_, "buffer")) {
return false;
}
}
const uint32_t contentSize = fileMapMem.GetSize() - checksumSize;
const uint32_t checksum = adler32(0, static_cast<const Bytef*>(fileMapMem.GetOriginAddr()), contentSize);
if (!writer.WriteSingleData(&checksum, checksumSize, "checksum")) {
return false;
}
FileSync(fileMapMem, FILE_MS_SYNC);
memMapScope.Escape();
FileUnMap(fileMapMem);
if (guard.Done()) {
ModulesSnapshotHelper::SetReadOnly(filePath);
}
LOG_ECMA(INFO) << logTag << " success";
return true;
}
bool ModulesSnapshotHelper::ReadDataFromFile(const std::unique_ptr<SerializeData>& data, const CString& filePath,
const SnapshotVersionInfo::UniquePtr& header, const CString& logPrefix)
{
const CString logTag = logPrefix + "::ReadDataFromFile";
ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, logTag.c_str(), "");
if (!FileExist(filePath.c_str())) {
LOG_ECMA(WARN) << logTag << " data file does not exist: " << filePath;
return false;
}
MemMap fileMapMem = FileMap(filePath.c_str(), FILE_RDONLY, PAGE_PROT_READ);
if (fileMapMem.GetOriginAddr() == nullptr) {
LOG_ECMA(ERROR) << logTag << " File mmap failed";
return false;
}
#if defined(PANDA_TARGET_OHOS) && ENABLE_V70_OPTIMIZATION
if (madvise(fileMapMem.GetOriginAddr(), fileMapMem.GetSize(), MADV_WILLNEED) != 0) {
LOG_ECMA(INFO) << logTag << " madvise failed, errno: " << errno << ", " << strerror(errno);
}
#endif
LOG_ECMA(DEBUG) << logTag;
MemMapScope memMapScope(fileMapMem);
FileMemMapReader reader(fileMapMem, std::bind(RemoveFile, filePath), logTag);
constexpr const uint32_t checksumSize = sizeof(uint32_t);
const uint32_t contentSize = fileMapMem.GetSize() - checksumSize;
uint32_t readCheckSum = 0;
if (!reader.ReadFromOffset(&readCheckSum, checksumSize, contentSize, "checksum")) {
return false;
}
const uint32_t checksum = adler32(0, static_cast<const Bytef*>(fileMapMem.GetOriginAddr()), contentSize);
if (checksum != readCheckSum) {
LOG_ECMA(ERROR) << logTag << " checksum compare failed, compute: " << checksum << ", written: "
<< readCheckSum;
return false;
}
if (auto err = ReadFileHeader(reader, header); !err.empty()) {
LOG_ECMA(ERROR) << logTag << " file header check failed, error: " << err;
return false;
}
if (!ReadDataInfo(reader, data)) {
LOG_ECMA(ERROR) << logTag << " read serialize data info failed";
return false;
}
if (data->incompleteData_) {
LOG_ECMA(ERROR) << logTag << " has incompleteData: " << data->incompleteData_;
return false;
}
if (g_isEnableCMCGC) {
size_t regularRemainSizeVectorSize;
if (!reader.ReadSingleData(®ularRemainSizeVectorSize, sizeof(regularRemainSizeVectorSize),
"regularRemainSizeVectorSize")) {
return false;
}
size_t vecSize = regularRemainSizeVectorSize * sizeof(size_t);
if (vecSize > 0) {
data->regularRemainSizeVector_.resize(regularRemainSizeVectorSize);
if (!reader.ReadSingleData(data->regularRemainSizeVector_.data(), vecSize, "regularRemainSizeVector")) {
return false;
}
}
size_t pinRemainSizeVectorSize = 0;
if (!reader.ReadSingleData(&pinRemainSizeVectorSize, sizeof(pinRemainSizeVectorSize),
"pinRemainSizeVectorSize")) {
return false;
}
vecSize = pinRemainSizeVectorSize * sizeof(size_t);
if (vecSize > 0) {
data->pinRemainSizeVector_.resize(pinRemainSizeVectorSize);
if (!reader.ReadSingleData(data->pinRemainSizeVector_.data(), vecSize, "pinRemainSizeVector")) {
return false;
}
}
} else {
std::array<size_t, SERIALIZE_SPACE_NUM> vecSizes {};
uint32_t vecSize = SERIALIZE_SPACE_NUM * sizeof(size_t);
if (!reader.ReadSingleData(vecSizes.data(), vecSize, "vecSizes")) {
return false;
}
for (int i = 0; i < SERIALIZE_SPACE_NUM; ++i) {
auto& vec = data->regionRemainSizeVectors_[i];
const size_t curVectorSize = vecSizes[i];
const uint32_t curVectorDataSize = curVectorSize * sizeof(size_t);
if (curVectorSize > 0) {
vec.resize(curVectorSize);
if (!reader.ReadSingleData(vec.data(), curVectorDataSize, "vec")) {
return false;
}
} else {
vec.clear();
}
}
}
if (data->bufferSize_ > 0) {
data->buffer_ = static_cast<uint8_t*>(malloc(data->bufferSize_));
if (!data->buffer_) {
return false;
}
if (!reader.ReadSingleData(data->buffer_, data->bufferSize_, "buffer")) {
return false;
}
} else {
data->buffer_ = nullptr;
}
LOG_ECMA(INFO) << logTag << " success";
return true;
}
ModulesSnapshotHelper::SnapshotVersionInfo::UniquePtr ModulesSnapshotHelper::SnapshotVersionInfo::New(
uint32_t appVersionCode, const std::string_view& romVersion, const std::string_view& description)
{
size_t totalSize = Sizeof() + romVersion.size() + description.size() + TERMINATE_CHAR_COUNT;
UniquePtr result(static_cast<SnapshotVersionInfo*>(malloc(totalSize)), SnapshotVersionInfo::Deleter);
if (result == nullptr) {
return result;
}
auto header = new (result.get()) SnapshotVersionInfo {appVersionCode, romVersion.size(), description.size()};
char* buffer = header->buffer_;
if (romVersion.size() > 0 && memcpy_s(buffer, romVersion.size(), romVersion.data(), romVersion.size()) != 0) {
result.reset();
LOG_ECMA(ERROR) << "memcpy_s failed when copy rom version to snapshot file header";
return result;
}
buffer += romVersion.size();
*(buffer++) = '\0';
if (description.size() > 0 && memcpy_s(buffer, description.size(), description.data(), description.size())) {
result.reset();
LOG_ECMA(ERROR) << "memcpy_s failed when copy description to snapshot file header";
return result;
};
buffer += description.size();
*buffer = '\0';
return result;
}
bool ModulesSnapshotHelper::DoNeedEscape()
{
if (g_featureLoaded_ == static_cast<int>(SnapshotFeatureState::DEFAULT)) {
return false;
}
if (g_escaperDisabled_) {
return false;
}
if (g_escaperTriggered_) {
return false;
}
g_escaperTriggered_ = true;
return true;
}
size_t ModulesSnapshotHelper::IntToString(int64_t value, char *buf, size_t bufSize)
{
if (buf == nullptr || bufSize == 0) {
return 0;
}
if (value == 0) {
buf[0] = '0';
return 1;
}
bool negative = (value < 0);
uint64_t uVal = negative ? static_cast<uint64_t>(-value) : static_cast<uint64_t>(value);
constexpr const int OCTET_BASE = 10;
uint64_t temp = uVal;
size_t digits = 0;
while (temp != 0) {
digits++;
temp /= OCTET_BASE;
}
size_t needed = digits + (negative ? 1 : 0);
if (needed > bufSize) {
return 0;
}
char *p = buf + needed - 1;
temp = uVal;
while (temp != 0) {
*(p--) = '0' + static_cast<char>(temp % OCTET_BASE);
temp /= OCTET_BASE;
}
if (negative) {
*p = '-';
}
return needed;
}
int ModulesSnapshotHelper::GetDisabledFeature(const CString &path)
{
static std::once_flag flag;
std::string_view cachedPath(g_stateFilePathBuffer_);
if (!path.empty() && ((cachedPath.size() < MODULE_SNAPSHOT_STATE_FILE_NAME.size()) ||
(path != cachedPath.substr(0, cachedPath.size() - MODULE_SNAPSHOT_STATE_FILE_NAME.size())))) {
UpdateFromStateFile(path);
}
return g_featureState_;
}
void ModulesSnapshotHelper::UpdateFromStateFile(const CString &path)
{
constexpr static int disableAll =
static_cast<int>(SnapshotFeatureState::PANDAFILE) | static_cast<int>(SnapshotFeatureState::MODULE);
if (!FileExist(path.c_str())) {
g_featureState_ = disableAll;
return;
}
auto stateFilePath = base::ConcatToCString(path, MODULE_SNAPSHOT_STATE_FILE_NAME);
if (memset_s(g_stateFilePathBuffer_, PATH_MAX, 0, PATH_MAX) != EOK) {
LOG_ECMA(WARN) << "memset_s failed when copy state file path";
}
if (memcpy_s(g_stateFilePathBuffer_, PATH_MAX, stateFilePath.c_str(), stateFilePath.length()) != EOK) {
LOG_ECMA(WARN) << "memcpy_s failed when copy state file path";
}
if (g_escaperDisabled_ || !FileExist(stateFilePath.c_str())) {
return;
}
PosixFile stateFile(stateFilePath, "r");
char stateWord{};
if (stateFile.IsValid() && stateFile.Read(&stateWord, sizeof(stateWord)) <= 0) {
g_featureState_ = disableAll;
LOG_ECMA(WARN) << "module snapshot is disabled: invalid state file";
return;
}
stateFile.Seek(1, PosixFile::SeekOrigin::CURRENT);
std::string report = "unknown";
report.resize(stateFile.Size());
if (auto len = stateFile.Read(report); len > 0) {
report.resize((len));
}
size_t sumLen = report.find("\n");
auto summary = std::string_view(report.c_str(), sumLen != std::string::npos ? sumLen : report.size());
std::string_view detail;
if (sumLen != std::string::npos) {
detail = std::string_view(summary.end() + 1, report.size() - sumLen - 1);
}
switch (stateWord) {
case STATE_WORD_MODULE_SNAPSHOT_DISABLED:
g_featureState_ |= static_cast<int>(SnapshotFeatureState::MODULE);
LOG_ECMA(INFO) << "module snapshot is disabled: crash occurs. recent error: " << summary;
LOG_ECMA(DEBUG) << detail;
break;
case STATE_WORD_ALL_SNAPSHOT_DISABLED:
g_featureState_ = disableAll;
LOG_ECMA(INFO) << "module and pandafile snapshots are disabled: crash occurs. recent error: " << summary;
LOG_ECMA(DEBUG) << detail;
break;
default:
g_featureState_ = disableAll;
LOG_ECMA(WARN) << "module and pandafile snapshots are disabled: unexpected state word";
break;
}
}
SnapshotSaveTask::SnapshotSaveTask(int32_t id, std::unique_ptr<SerializeData> serializeData, const CString& path,
ModulesSnapshotHelper::SnapshotVersionInfo::UniquePtr header,
const CString& logPrefix)
: Task(id), serializeData_(std::move(serializeData)), path_(path), header_(std::move(header)), logPrefix_(logPrefix)
{
}
bool SnapshotSaveTask::Run([[maybe_unused]] uint32_t threadIndex)
{
ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "ModuleSnapshotTask", "");
ModulesSnapshotHelper::WriteDataToFile(serializeData_, path_, header_, "SnapshotSaveTask");
return true;
}
}