feat(PGO): Add profdump tool

1、profiler translate from binary to text
    2、profiler translate from text to binary
    3、PGO profiler code refactoring

link #I6E38H

Change-Id: Id6988af126d32937ecc8aaa4c617a638ac4795b4
Signed-off-by: yingguofeng@huawei.com <yingguofeng@huawei.com>
This commit is contained in:
yingguofeng@huawei.com 2023-02-09 21:47:15 +08:00
parent b2ae7019f4
commit 47134bfb20
39 changed files with 2197 additions and 980 deletions

View File

@ -27,7 +27,10 @@ group("ark_js_packages") {
]
if (is_clang && clang_version != "9.0.3" && current_cpu == "arm64" &&
is_ohos) {
deps += [ "ecmascript/compiler:ark_aot_compiler($build_root/toolchain/ohos:ohos_clang_arm64)" ]
deps += [
"ecmascript/compiler:ark_aot_compiler($build_root/toolchain/ohos:ohos_clang_arm64)",
"ecmascript/pgo_profiler/prof_dump:profdump($build_root/toolchain/ohos:ohos_clang_arm64)",
]
}
}
}
@ -36,6 +39,7 @@ group("ark_js_host_windows_tools_packages") {
deps = []
if (host_os != "mac" && target_os != "android") {
deps += [
"ecmascript/pgo_profiler/prof_dump:profdump($build_root/toolchain/mingw:mingw_x86_64)",
"//arkcompiler/ets_runtime/ecmascript/compiler:ark_aot_compiler($build_root/toolchain/mingw:mingw_x86_64)",
"//arkcompiler/ets_runtime/ecmascript/js_vm:ark_js_vm($build_root/toolchain/mingw:mingw_x86_64)",
]
@ -49,11 +53,13 @@ group("ark_js_host_mac_tools_packages") {
deps += [
"ecmascript/compiler:ark_aot_compiler($build_root/toolchain/mac:clang_arm64)",
"ecmascript/js_vm:ark_js_vm($build_root/toolchain/mac:clang_arm64)",
"ecmascript/pgo_profiler/prof_dump:profdump($build_root/toolchain/mac:clang_arm64)",
]
} else {
deps += [
"ecmascript/compiler:ark_aot_compiler($build_root/toolchain/mac:clang_x64)",
"ecmascript/js_vm:ark_js_vm($build_root/toolchain/mac:clang_x64)",
"ecmascript/pgo_profiler/prof_dump:profdump($build_root/toolchain/mac:clang_x64)",
]
}
}
@ -68,6 +74,7 @@ group("ark_js_host_linux_tools_packages") {
]
if (is_standard_system) {
deps += [
"ecmascript/pgo_profiler/prof_dump:profdump(${host_toolchain})",
"//arkcompiler/ets_runtime/ecmascript/compiler:ark_aot_compiler(${host_toolchain})",
"//arkcompiler/ets_runtime/ecmascript/compiler:ark_stub_compiler(${host_toolchain})",
]
@ -80,12 +87,12 @@ group("ark_js_unittest") {
deps = []
if (host_os != "mac") {
deps += [
"ecmascript/pgo_profiler/tests:unittest",
"//arkcompiler/ets_runtime/ecmascript/base/tests:unittest",
"//arkcompiler/ets_runtime/ecmascript/builtins/tests:unittest",
"//arkcompiler/ets_runtime/ecmascript/containers/tests:unittest",
"//arkcompiler/ets_runtime/ecmascript/debugger/tests:unittest",
"//arkcompiler/ets_runtime/ecmascript/dfx/hprof/tests:unittest",
"//arkcompiler/ets_runtime/ecmascript/dfx/pgo_profiler/tests:unittest",
"//arkcompiler/ets_runtime/ecmascript/ic/tests:unittest",
"//arkcompiler/ets_runtime/ecmascript/jobs/tests:unittest",
"//arkcompiler/ets_runtime/ecmascript/jspandafile/tests:unittest",
@ -115,11 +122,11 @@ group("ark_unittest") {
if (host_os != "mac") {
# js unittest
deps += [
"ecmascript/pgo_profiler/tests:host_unittest",
"//arkcompiler/ets_runtime/ecmascript/base/tests:host_unittest",
"//arkcompiler/ets_runtime/ecmascript/builtins/tests:host_unittest",
"//arkcompiler/ets_runtime/ecmascript/containers/tests:host_unittest",
"//arkcompiler/ets_runtime/ecmascript/dfx/hprof/tests:host_unittest",
"//arkcompiler/ets_runtime/ecmascript/dfx/pgo_profiler/tests:host_unittest",
"//arkcompiler/ets_runtime/ecmascript/ic/tests:host_unittest",
"//arkcompiler/ets_runtime/ecmascript/jobs/tests:host_unittest",
"//arkcompiler/ets_runtime/ecmascript/jspandafile/tests:host_unittest",
@ -513,8 +520,6 @@ ecma_source = [
"ecmascript/date_parse.cpp",
"ecmascript/deoptimizer/calleeReg.cpp",
"ecmascript/deoptimizer/deoptimizer.cpp",
"ecmascript/dfx/pgo_profiler/pgo_profiler_loader.cpp",
"ecmascript/dfx/pgo_profiler/pgo_profiler_manager.cpp",
"ecmascript/dfx/stackinfo/js_stackinfo.cpp",
"ecmascript/dfx/vmstat/caller_stat.cpp",
"ecmascript/dfx/vmstat/opt_code_profiler.cpp",
@ -652,6 +657,10 @@ ecma_source = [
"ecmascript/object_operator.cpp",
"ecmascript/patch/patch_loader.cpp",
"ecmascript/patch/quick_fix_manager.cpp",
"ecmascript/pgo_profiler/pgo_profiler.cpp",
"ecmascript/pgo_profiler/pgo_profiler_info.cpp",
"ecmascript/pgo_profiler/pgo_profiler_loader.cpp",
"ecmascript/pgo_profiler/pgo_profiler_saver.cpp",
"ecmascript/stackmap/ark_stackmap_builder.cpp",
"ecmascript/stackmap/ark_stackmap_parser.cpp",
"ecmascript/stackmap/llvm_stackmap_parser.cpp",

View File

@ -28,6 +28,29 @@ union Data {
R dst;
};
template <typename T>
inline T ReadBuffer(void **buffer)
{
T result = *(reinterpret_cast<T *>(*buffer));
*buffer = reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(*buffer) + result.offset_);
return result;
}
inline char *ReadBuffer(void **buffer)
{
auto result = reinterpret_cast<char *>(*buffer);
*buffer = reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(*buffer) + strlen(result) + 1);
return result;
}
template <typename T>
inline T *ReadBufferInSize(void **buffer)
{
T *result = reinterpret_cast<T *>(*buffer);
*buffer = reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(*buffer) + result->Size());
return result;
}
template <typename T>
inline constexpr uint32_t CountLeadingZeros(T value)
{

View File

@ -40,18 +40,36 @@ protected:
}
if (version_ > lastVersion) {
LOG_ECMA(ERROR) << "version error, expected version should be less or equal than "
<< ConvToStr(lastVersion) << ", but got " << GetVersion();
<< ConvToStr(lastVersion) << ", but got " << GetVersionInner();
return false;
}
LOG_ECMA(DEBUG) << "Magic:" << ConvToStr(magic_) << ", version:" << GetVersion();
LOG_ECMA(DEBUG) << "Magic:" << ConvToStr(magic_) << ", version:" << GetVersionInner();
return true;
}
std::string GetVersion() const
std::string GetVersionInner() const
{
return ConvToStr(version_);
}
bool SetVersionInner(std::string version)
{
std::vector<std::string> versionNumber = StringHelper::SplitString(version, ".");
if (versionNumber.size() != VERSION_SIZE) {
LOG_ECMA(ERROR) << "version: " << version << " format error";
return false;
}
for (uint32_t i = 0; i < VERSION_SIZE; i++) {
uint32_t result;
if (!StringHelper::StrToUInt32(versionNumber[i].c_str(), &result)) {
LOG_ECMA(ERROR) << "version: " << version << " format error";
return false;
}
version_[i] = static_cast<uint8_t>(result);
}
return true;
}
private:
template <size_t size>
std::string ConvToStr(std::array<uint8_t, size> array) const

View File

@ -390,6 +390,17 @@ public:
std::string subStr = str.substr(str.length() - suffix.length(), str.length());
return subStr == suffix;
}
static bool StrToUInt32(const char *content, uint32_t *result)
{
const int DEC = 10;
char *endPtr = nullptr;
*result = std::strtoul(content, &endPtr, DEC);
if (endPtr == content || *endPtr != '\0') {
return false;
}
return true;
}
};
} // namespace panda::ecmascript::base
#endif // ECMASCRIPT_BASE_STRING_HELP_H

View File

@ -110,11 +110,11 @@ int Main(const int argc, const char **argv)
entrypoint = runtimeOptions.GetEntryPoint();
}
PassManager passManager(vm, entrypoint, triple, optLevel, relocMode, &log, &logList, maxAotMethodSize,
isEnableTypeLowering, hotnessThreshold);
isEnableTypeLowering, profilerIn, hotnessThreshold);
for (const auto &fileName : pandaFileNames) {
auto extendedFilePath = panda::os::file::File::GetExtendedFilePath(fileName);
LOG_COMPILER(INFO) << "AOT compile: " << extendedFilePath;
if (passManager.Compile(extendedFilePath, generator, profilerIn) == false) {
if (passManager.Compile(extendedFilePath, generator) == false) {
ret = false;
continue;
}

View File

@ -16,8 +16,8 @@
#ifndef ECMASCRIPT_COMPILER_BYTECODE_INFO_COLLECTOR_H
#define ECMASCRIPT_COMPILER_BYTECODE_INFO_COLLECTOR_H
#include "ecmascript/dfx/pgo_profiler/pgo_profiler_loader.h"
#include "ecmascript/jspandafile/js_pandafile.h"
#include "ecmascript/pgo_profiler/pgo_profiler_loader.h"
#include "libpandafile/bytecode_instruction-inl.h"
namespace panda::ecmascript::kungfu {

View File

@ -18,21 +18,18 @@
namespace panda::ecmascript::kungfu {
void CompilationDriver::UpdatePGO()
{
if (!IsPGOLoaded()) {
return;
}
const auto &previousHotList = pfLoader_.GetProfile();
for (auto pgoIndex = previousHotList.begin(); pgoIndex != previousHotList.end(); pgoIndex++) {
const CString &recordName = pgoIndex->first;
std::unordered_set<EntityId> newMethodIds;
auto dfs = [this, &newMethodIds] (const CString &recordName,
const std::unordered_set<EntityId> &oldIds) -> std::unordered_set<EntityId> & {
newMethodIds.clear();
if (!jsPandaFile_->HasTSTypes(recordName)) {
continue;
return newMethodIds;
}
auto methodSet = pgoIndex->second;
std::unordered_set<EntityId> newMethodSet;
uint32_t mainMethodOffset = jsPandaFile_->GetMainMethodIndex(recordName);
SearchForCompilation(methodSet, newMethodSet, mainMethodOffset, false);
pfLoader_.UpdateProfile(recordName, newMethodSet);
}
SearchForCompilation(oldIds, newMethodIds, mainMethodOffset, false);
return newMethodIds;
};
pfLoader_.Update(dfs);
}
void CompilationDriver::InitializeCompileQueue()

View File

@ -43,10 +43,17 @@ public:
}
// update profile and update compile queue
std::unordered_set<EntityId> fullResolvedMethodSet;
auto dfs = [this, &fullResolvedMethodSet, resolvedMethod] (const CString &recordName,
[[maybe_unused]] const std::unordered_set<EntityId> &oldIds) -> std::unordered_set<EntityId> & {
fullResolvedMethodSet.clear();
std::unordered_set<EntityId> currentResolvedMethodSet {resolvedMethod};
uint32_t mainMethodOffset = jsPandaFile_->GetMainMethodIndex(recordName);
SearchForCompilation(currentResolvedMethodSet, fullResolvedMethodSet, mainMethodOffset, true);
pfLoader_.UpdateProfile(recordName, fullResolvedMethodSet);
return fullResolvedMethodSet;
};
pfLoader_.Update(recordName, dfs);
if (fullResolvedMethodSet.size() > 0) {
bytecodeInfo_.AddRecordName(recordName);
}
@ -107,7 +114,7 @@ private:
void InitializeCompileQueue();
void SearchForCompilation(std::unordered_set<EntityId> &methodSet, std::unordered_set<EntityId> &newMethodSet,
void SearchForCompilation(const std::unordered_set<EntityId> &methodSet, std::unordered_set<EntityId> &newMethodSet,
uint32_t mainMethodOffset, bool needUpdateCompile)
{
auto &methodList = bytecodeInfo_.GetMethodList();

View File

@ -26,7 +26,7 @@
namespace panda::ecmascript::kungfu {
bool PassManager::Compile(const std::string &fileName, AOTFileGenerator &generator, const std::string &profilerIn)
bool PassManager::Compile(const std::string &fileName, AOTFileGenerator &generator)
{
[[maybe_unused]] EcmaHandleScope handleScope(vm_->GetJSThread());
JSPandaFile *jsPandaFile = CreateAndVerifyJSPandaFile(fileName.c_str());
@ -35,7 +35,7 @@ bool PassManager::Compile(const std::string &fileName, AOTFileGenerator &generat
return false;
}
if (!profilerLoader_.LoadAndVerify(profilerIn, hotnessThreshold_, jsPandaFile->GetChecksum())) {
if (!profilerLoader_.LoadAndVerify(jsPandaFile->GetChecksum())) {
LOG_COMPILER(ERROR) << "Load and verify profiler failure";
return false;
}

View File

@ -18,8 +18,8 @@
#include "ecmascript/compiler/compiler_log.h"
#include "ecmascript/compiler/file_generators.h"
#include "ecmascript/dfx/pgo_profiler/pgo_profiler_loader.h"
#include "ecmascript/ecma_vm.h"
#include "ecmascript/pgo_profiler/pgo_profiler_loader.h"
namespace panda::ecmascript::kungfu {
class Bytecodes;
@ -102,15 +102,15 @@ class PassManager {
public:
PassManager(EcmaVM* vm, std::string entry, std::string &triple, size_t optLevel, size_t relocMode,
CompilerLog *log, AotMethodLogList *logList, size_t maxAotMethodSize, bool enableTypeLowering,
uint32_t hotnessThreshold)
const std::string &profIn, uint32_t hotnessThreshold)
: vm_(vm), entry_(entry), triple_(triple), optLevel_(optLevel), relocMode_(relocMode), log_(log),
logList_(logList), maxAotMethodSize_(maxAotMethodSize), enableTypeLowering_(enableTypeLowering),
enableTypeInfer_(enableTypeLowering || vm_->GetTSManager()->AssertTypes()),
hotnessThreshold_(hotnessThreshold) {};
profilerLoader_(profIn, hotnessThreshold) {};
PassManager() = default;
~PassManager() = default;
bool Compile(const std::string &fileName, AOTFileGenerator &generator, const std::string &profilerIn);
bool Compile(const std::string &fileName, AOTFileGenerator &generator);
private:
JSPandaFile *CreateAndVerifyJSPandaFile(const CString &fileName);
@ -137,7 +137,6 @@ private:
size_t maxAotMethodSize_ {0};
bool enableTypeLowering_ {true};
bool enableTypeInfer_ {true};
uint32_t hotnessThreshold_ {0};
PGOProfilerLoader profilerLoader_;
};
}

View File

@ -1,178 +0,0 @@
/*
* Copyright (c) 2022 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/dfx/pgo_profiler/pgo_profiler_loader.h"
#include <string>
#include "ecmascript/base/string_helper.h"
#include "ecmascript/dfx/pgo_profiler/pgo_profiler_manager.h"
#include "ecmascript/js_tagged_value.h"
#include "ecmascript/log_wrapper.h"
#include "ecmascript/method.h"
#include "ecmascript/platform/file.h"
namespace panda::ecmascript {
bool PGOProfilerLoader::Load(const std::string &inPath, uint32_t hotnessThreshold)
{
hotnessThreshold_ = hotnessThreshold;
isLoaded_ = false;
hotnessMethods_.clear();
if (inPath.empty()) {
return false;
}
std::string realPath;
if (!RealPath(inPath, realPath)) {
return false;
}
static const std::string endString = ".ap";
if (realPath.compare(realPath.length() - endString.length(), endString.length(), endString)) {
LOG_ECMA(ERROR) << "The file path( " << realPath << ") does not end with .ap";
return false;
}
LOG_ECMA(INFO) << "Load profiler from file:" << realPath;
fd_t fd = Open(realPath.c_str(), FILE_RDONLY);
if (UNLIKELY(fd == INVALID_FD)) {
LOG_ECMA(ERROR) << "open file failed";
return false;
}
int64_t fileSize = GetFileSizeByFd(fd);
if (fileSize == -1) {
Close(fd);
LOG_ECMA(ERROR) << "GetFileSize failed";
return false;
}
fd_t extra = INVALID_FD;
void *addr = FileMmap(fd, fileSize, 0, &extra);
if (addr == nullptr) {
Close(fd);
LOG_ECMA(ERROR) << "file mmap failed";
return false;
}
if (!ParseProfilerHeader(&addr)) {
FileUnMap(addr, fileSize, &extra);
Close(fd);
LOG_ECMA(ERROR) << "Parse profiler header failure";
return false;
}
if (!ParsePandaFileInfo(&addr)) {
FileUnMap(addr, fileSize, &extra);
Close(fd);
LOG_ECMA(ERROR) << "Parse profiler panda file info failure";
return false;
}
ParseProfiler(&addr);
FileUnMap(addr, fileSize, &extra);
Close(fd);
isLoaded_ = true;
return true;
}
bool PGOProfilerLoader::Verify(uint32_t checksum)
{
isVerifySuccess_ = false;
if (!isLoaded_) {
return false;
}
for (auto info : pandaFileProfilerInfos_) {
if (checksum == info.GetChecksum()) {
isVerifySuccess_ = true;
return true;
}
}
LOG_ECMA(ERROR) << "Verify profiler failure";
return false;
}
bool PGOProfilerLoader::LoadAndVerify(const std::string &inPath, uint32_t hotnessThreshold, uint32_t checksum)
{
// When the file name is empty, Enter full compiler mode.
if (inPath.empty()) {
return true;
}
if (Load(inPath, hotnessThreshold) && Verify(checksum)) {
return true;
}
return false;
}
bool PGOProfilerLoader::ParseProfilerHeader(void **buffer)
{
memcpy_s(&header_, sizeof(PGOProfilerHeader), *buffer, sizeof(PGOProfilerHeader));
*buffer = ToVoidPtr(ToUintPtr(*buffer) + sizeof(PGOProfilerHeader));
return header_.Verify();
}
bool PGOProfilerLoader::ParsePandaFileInfo(void **buffer)
{
uint32_t size = *(reinterpret_cast<uint32_t *>(*buffer));
*buffer = ToVoidPtr(ToUintPtr(*buffer) + sizeof(uint32_t));
pandaFileProfilerInfos_.resize(size);
for (uint32_t i = 0; i < size; i++) {
pandaFileProfilerInfos_.emplace_back(*(reinterpret_cast<PandaFileProfilerInfo *>(*buffer)));
*buffer = ToVoidPtr(ToUintPtr(*buffer) + sizeof(PandaFileProfilerInfo));
}
LOG_ECMA(DEBUG) << "Profiler panda file count:" << size;
return true;
}
void PGOProfilerLoader::ParseProfiler(void **buffer)
{
uint32_t recordNameCount = *(reinterpret_cast<uint32_t *>(*buffer));
*buffer = ToVoidPtr(ToUintPtr(*buffer) + sizeof(uint32_t));
for (uint32_t i = 0; i < recordNameCount; i++) {
auto recordName = ConvertToString(reinterpret_cast<char *>(*buffer));
*buffer = ToVoidPtr(ToUintPtr(*buffer) + recordName.size() + 1);
std::unordered_set<EntityId> methodIds;
uint32_t methodCount = *(reinterpret_cast<uint32_t *>(*buffer));
*buffer = ToVoidPtr(ToUintPtr(*buffer) + sizeof(uint32_t));
for (uint32_t j = 0; j < methodCount; j++) {
MethodProfilerInfo *info = reinterpret_cast<MethodProfilerInfo *>(*buffer);
if (info->GetCount() >= hotnessThreshold_) {
methodIds.emplace(info->GetMethodId());
LOG_ECMA(DEBUG) << "Method:" << info->GetMethodId() << "/" << info->GetCount()
<< "/" << std::to_string(static_cast<int>(info->GetSampleMode()))
<< "/" << info->GetMethodName() << "/" << info->GetMethodLength();
}
*buffer = ToVoidPtr(ToUintPtr(*buffer) + info->Size());
}
if (!methodIds.empty()) {
hotnessMethods_.emplace(recordName, methodIds);
}
}
}
bool PGOProfilerLoader::Match(const CString &recordName, EntityId methodId)
{
if (!isLoaded_) {
return true;
}
if (!isVerifySuccess_) {
return false;
}
auto hotnessMethodSet = hotnessMethods_.find(recordName);
if (hotnessMethodSet == hotnessMethods_.end()) {
return false;
}
return hotnessMethodSet->second.find(methodId) != hotnessMethodSet->second.end();
}
} // namespace panda::ecmascript

View File

@ -1,75 +0,0 @@
/*
* Copyright (c) 2022 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.
*/
#ifndef ECMASCRIPT_DFX_PGO_PROFILE_LOADER_H
#define ECMASCRIPT_DFX_PGO_PROFILE_LOADER_H
#include <unordered_map>
#include <unordered_set>
#include "ecmascript/dfx/pgo_profiler/pgo_profiler_manager.h"
#include "ecmascript/ecma_macros.h"
#include "ecmascript/jspandafile/method_literal.h"
namespace panda::ecmascript {
class PGOProfilerLoader {
public:
PGOProfilerLoader() = default;
virtual ~PGOProfilerLoader() = default;
NO_COPY_SEMANTIC(PGOProfilerLoader);
NO_MOVE_SEMANTIC(PGOProfilerLoader);
bool PUBLIC_API Match(const CString &recordName, EntityId methodId);
bool PUBLIC_API Load(const std::string &inPath, uint32_t hotnessThreshold);
bool PUBLIC_API Verify(uint32_t checksum);
bool PUBLIC_API LoadAndVerify(const std::string &inPath, uint32_t hotnessThreshold, uint32_t checksum);
const std::unordered_map<CString, std::unordered_set<EntityId>> &GetProfile() const
{
return hotnessMethods_;
}
void UpdateProfile(const CString &recordName, std::unordered_set<EntityId> &pgoMethods)
{
if (hotnessMethods_.find(recordName) != hotnessMethods_.end()) {
hotnessMethods_[recordName].insert(pgoMethods.begin(), pgoMethods.end());
} else {
hotnessMethods_.emplace(recordName, pgoMethods);
}
}
bool IsLoaded() const
{
return isLoaded_;
}
private:
static constexpr int HEADER_INFO_COUNT = 2;
static constexpr int MAGIC_ID_INDEX = 0;
static constexpr int VERSION_ID_INDEX = 1;
bool ParseProfilerHeader(void **buffer);
bool ParsePandaFileInfo(void **buffer);
void ParseProfiler(void **buffer);
bool isLoaded_ {false};
bool isVerifySuccess_ {true};
uint32_t hotnessThreshold_ {0};
PGOProfilerHeader header_;
std::vector<PandaFileProfilerInfo> pandaFileProfilerInfos_;
std::unordered_map<CString, std::unordered_set<EntityId>> hotnessMethods_;
};
} // namespace panda::ecmascript
#endif // ECMASCRIPT_DFX_PGO_PROFILE_LOADER_H

View File

@ -1,272 +0,0 @@
/*
* Copyright (c) 2022 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/dfx/pgo_profiler/pgo_profiler_manager.h"
#include <ios>
#include <sstream>
#include <string>
#include "ecmascript/js_function.h"
#include "ecmascript/jspandafile/js_pandafile.h"
#include "ecmascript/log_wrapper.h"
#include "ecmascript/js_tagged_value-inl.h"
#include "ecmascript/mem/c_string.h"
#include "ecmascript/platform/file.h"
namespace panda::ecmascript {
static const std::string PROFILE_FILE_NAME = "/modules.ap";
PGOProfiler::~PGOProfiler()
{
isEnable_ = false;
profilerMap_.clear();
}
void PGOProfiler::Sample(JSTaggedType value, SampleMode mode)
{
if (!isEnable_) {
return;
}
DISALLOW_GARBAGE_COLLECTION;
JSTaggedValue jsValue(value);
if (jsValue.IsJSFunction() && JSFunction::Cast(jsValue)->GetMethod().IsMethod()) {
auto jsMethod = Method::Cast(JSFunction::Cast(jsValue)->GetMethod());
JSTaggedValue recordNameValue = JSFunction::Cast(jsValue)->GetRecordName();
if (recordNameValue.IsHole()) {
return;
}
CString recordName = ConvertToString(recordNameValue);
auto iter = profilerMap_.find(recordName);
if (iter != profilerMap_.end()) {
auto methodCountMap = iter->second;
auto result = methodCountMap->find(jsMethod->GetMethodId());
if (result != methodCountMap->end()) {
auto info = result->second;
info->IncreaseCount();
info->SetSampleMode(mode);
} else {
size_t len = strlen(jsMethod->GetMethodName());
void *infoAddr = chunk_.Allocate(MethodProfilerInfo::Size(len));
auto info = new (infoAddr) MethodProfilerInfo(jsMethod->GetMethodId(), 1, mode, len);
info->SetMethodName(jsMethod->GetMethodName(), len);
methodCountMap->emplace(jsMethod->GetMethodId(), info);
methodCount_++;
}
} else {
ChunkUnorderedMap<EntityId, MethodProfilerInfo *> *methodsCountMap =
chunk_.New<ChunkUnorderedMap<EntityId, MethodProfilerInfo *>>(&chunk_);
size_t len = strlen(jsMethod->GetMethodName());
void *infoAddr = chunk_.Allocate(MethodProfilerInfo::Size(len));
auto info = new (infoAddr) MethodProfilerInfo(jsMethod->GetMethodId(), 1, mode, len);
info->SetMethodName(jsMethod->GetMethodName(), len);
methodsCountMap->emplace(jsMethod->GetMethodId(), info);
profilerMap_.emplace(recordName, methodsCountMap);
methodCount_++;
}
// Merged every 10 methods
if (methodCount_ >= MERGED_EVERY_COUNT) {
LOG_ECMA(INFO) << "Sample: post task to save profiler";
PGOProfilerManager::GetInstance()->TerminateSaveTask();
PGOProfilerManager::GetInstance()->Merge(this);
PGOProfilerManager::GetInstance()->PostSaveTask();
methodCount_ = 0;
}
}
}
void PGOProfilerManager::Initialize(uint32_t hotnessThreshold, const std::string &outDir)
{
hotnessThreshold_ = hotnessThreshold;
outDir_ = outDir;
}
void PGOProfilerManager::Destroy()
{
if (!isInitialized_) {
return;
}
// SaveTask is already finished
SaveProfiler();
globalProfilerMap_->clear();
chunk_.reset();
nativeAreaAllocator_.reset();
isInitialized_ = false;
}
bool PGOProfilerManager::InitializeData()
{
os::memory::LockHolder lock(mutex_);
if (!isInitialized_) {
if (!RealPath(outDir_, realOutPath_, false)) {
return false;
}
realOutPath_ += PROFILE_FILE_NAME;
LOG_ECMA(INFO) << "Save profiler to file:" << realOutPath_;
nativeAreaAllocator_ = std::make_unique<NativeAreaAllocator>();
chunk_ = std::make_unique<Chunk>(nativeAreaAllocator_.get());
globalProfilerMap_ = chunk_->
New<ChunkUnorderedMap<CString, ChunkUnorderedMap<EntityId, MethodProfilerInfo *> *>>(chunk_.get());
pandaFileProfilerInfos_ = chunk_->New<ChunkVector<PandaFileProfilerInfo *>>(chunk_.get());
isInitialized_ = true;
}
return true;
}
void PGOProfilerManager::SamplePandaFileInfo(uint32_t checksum)
{
if (!isInitialized_) {
return;
}
for (auto info : *pandaFileProfilerInfos_) {
if (info->GetChecksum() == checksum) {
return;
}
}
auto info = chunk_->New<PandaFileProfilerInfo>(checksum);
pandaFileProfilerInfos_->emplace_back(info);
}
void PGOProfilerManager::Merge(PGOProfiler *profiler)
{
if (!isInitialized_) {
return;
}
os::memory::LockHolder lock(mutex_);
for (auto iter = profiler->profilerMap_.begin(); iter != profiler->profilerMap_.end(); iter++) {
auto recordName = iter->first;
auto methodCountMap = iter->second;
auto globalMethodCountIter = globalProfilerMap_->find(recordName);
ChunkUnorderedMap<EntityId, MethodProfilerInfo *> *globalMethodCountMap = nullptr;
if (globalMethodCountIter == globalProfilerMap_->end()) {
globalMethodCountMap = chunk_->New<ChunkUnorderedMap<EntityId, MethodProfilerInfo *>>(chunk_.get());
globalProfilerMap_->emplace(recordName, globalMethodCountMap);
} else {
globalMethodCountMap = globalMethodCountIter->second;
}
for (auto countIter = methodCountMap->begin(); countIter != methodCountMap->end(); countIter++) {
auto methodId = countIter->first;
auto &localInfo = countIter->second;
auto result = globalMethodCountMap->find(methodId);
if (result != globalMethodCountMap->end()) {
auto &info = result->second;
info->Merge(localInfo);
} else {
size_t len = strlen(localInfo->GetMethodName());
void *infoAddr = chunk_->Allocate(MethodProfilerInfo::Size(len));
auto info = new (infoAddr) MethodProfilerInfo(methodId, localInfo->GetCount(),
localInfo->GetSampleMode(), len);
info->SetMethodName(localInfo->GetMethodName(), len);
globalMethodCountMap->emplace(methodId, info);
}
localInfo->ClearCount();
}
}
}
void PGOProfilerManager::SaveProfiler(SaveTask *task)
{
std::ofstream fileStream(realOutPath_.c_str());
if (!fileStream.is_open()) {
LOG_ECMA(ERROR) << "The file path(" << realOutPath_ << ") open failure!";
return;
}
ProcessProfileHeader(fileStream);
ProcessPandaFileInfo(fileStream);
ProcessProfile(fileStream, task);
fileStream.close();
}
void PGOProfilerManager::ProcessProfileHeader(std::ofstream &fileStream)
{
fileStream.write(reinterpret_cast<char *>(&header_), sizeof(PGOProfilerHeader));
}
void PGOProfilerManager::ProcessPandaFileInfo(std::ofstream &fileStream)
{
uint32_t size = pandaFileProfilerInfos_->size();
fileStream.write(reinterpret_cast<char *>(&size), sizeof(uint32_t));
for (auto info : *pandaFileProfilerInfos_) {
fileStream.write(reinterpret_cast<char *>(info), sizeof(PandaFileProfilerInfo));
}
}
void PGOProfilerManager::ProcessProfile(std::ofstream &fileStream, SaveTask *task)
{
uint32_t recordCount = 0;
std::stringstream stream;
for (auto iter = globalProfilerMap_->begin(); iter != globalProfilerMap_->end(); iter++) {
uint32_t methodCount = 0;
std::stringstream methodStream;
auto methodCountMap = iter->second;
for (auto countIter = methodCountMap->begin(); countIter != methodCountMap->end(); countIter++) {
LOG_ECMA(DEBUG) << "Method:" << countIter->first << "/" << countIter->second->GetCount()
<< "/" << std::to_string(static_cast<int>(countIter->second->GetSampleMode()))
<< "/" << countIter->second->GetMethodName() << "/" << countIter->second->GetMethodLength();
if (task && task->IsTerminate()) {
LOG_ECMA(INFO) << "ProcessProfile: task is already terminate";
return;
}
if (countIter->second->GetCount() < hotnessThreshold_ &&
countIter->second->GetSampleMode() == SampleMode::CALL_MODE) {
continue;
}
methodStream.write(reinterpret_cast<char *>(countIter->second), countIter->second->Size());
methodCount++;
}
if (methodCount > 0) {
stream << iter->first;
stream << '\0';
stream.write(reinterpret_cast<char *>(&methodCount), sizeof(uint32_t));
stream << methodStream.rdbuf();
recordCount++;
}
}
fileStream.write(reinterpret_cast<char *>(&recordCount), sizeof(uint32_t));
fileStream << stream.rdbuf();
}
void PGOProfilerManager::TerminateSaveTask()
{
if (!isInitialized_) {
return;
}
Taskpool::GetCurrentTaskpool()->TerminateTask(GLOBAL_TASK_ID, TaskType::PGO_SAVE_TASK);
}
void PGOProfilerManager::PostSaveTask()
{
if (!isInitialized_) {
return;
}
Taskpool::GetCurrentTaskpool()->PostTask(std::make_unique<SaveTask>(GLOBAL_TASK_ID));
}
void PGOProfilerManager::StartSaveTask(SaveTask *task)
{
if (task == nullptr) {
return;
}
if (task->IsTerminate()) {
LOG_ECMA(ERROR) << "StartSaveTask: task is already terminate";
return;
}
os::memory::LockHolder lock(mutex_);
SaveProfiler(task);
}
} // namespace panda::ecmascript

View File

@ -1,274 +0,0 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef ECMASCRIPT_DFX_PGO_PROFILER_MANAGER_H
#define ECMASCRIPT_DFX_PGO_PROFILER_MANAGER_H
#include <algorithm>
#include <cstring>
#include <memory>
#include "ecmascript/base/file_header.h"
#include "ecmascript/ecma_macros.h"
#include "ecmascript/ecma_vm.h"
#include "ecmascript/log_wrapper.h"
#include "ecmascript/mem/chunk_containers.h"
#include "ecmascript/mem/native_area_allocator.h"
#include "ecmascript/taskpool/task.h"
namespace panda::ecmascript {
enum class SampleMode : uint8_t {
HOTNESS_MODE,
CALL_MODE,
};
/*
* Support statistics of JS Function call heat. Save the method ID whose calls are less than MIN_COUNT.
*
* The saving format is as follows:
* "recordName1:[methodId/count/mode/name,methodId/count/mode/name......]"
* "recordName2:[methodId/count/mode/name,methodId/count/mode/name,methodId/count/mode/name......]"
* "......"
* */
class MethodProfilerInfo {
public:
static constexpr size_t ALIGN_SIZE = 4;
MethodProfilerInfo(EntityId id, uint32_t count, SampleMode mode, uint16_t length)
: id_(id), count_(count), mode_(mode), methodLength_(length) {}
static int32_t Size(uint32_t length)
{
return sizeof(MethodProfilerInfo) + AlignUp(length, ALIGN_SIZE);
}
int32_t Size()
{
return sizeof(MethodProfilerInfo) + AlignUp(methodLength_, ALIGN_SIZE);
}
void IncreaseCount()
{
count_++;
}
void ClearCount()
{
count_ = 0;
}
void Merge(const MethodProfilerInfo *info)
{
count_ += info->GetCount();
methodLength_ = info->GetMethodLength();
SetSampleMode(info->GetSampleMode());
SetMethodName(info->GetMethodName(), info->GetMethodLength());
}
EntityId GetMethodId() const
{
return id_;
}
uint32_t GetCount() const
{
return count_;
}
uint16_t GetMethodLength() const
{
return methodLength_;
}
void SetMethodName(const char *methodName, size_t len)
{
if (memcpy_s(&methodName_, methodLength_, methodName, len) != EOK) {
LOG_ECMA(ERROR) << "SetMethodName memcpy_s failed" << methodName << ", len = " << len;
}
*(&methodName_ + len) = '\0';
}
const char *GetMethodName() const
{
return &methodName_;
}
void SetSampleMode(SampleMode mode)
{
if (mode_ == SampleMode::HOTNESS_MODE) {
return;
}
mode_ = mode;
}
SampleMode GetSampleMode() const
{
return mode_;
}
NO_COPY_SEMANTIC(MethodProfilerInfo);
NO_MOVE_SEMANTIC(MethodProfilerInfo);
private:
EntityId id_;
uint32_t count_ {0};
SampleMode mode_ {SampleMode::CALL_MODE};
uint16_t methodLength_ {0};
char methodName_;
};
class PGOProfilerHeader : public base::FileHeader {
public:
static constexpr std::array<uint8_t, VERSION_SIZE> LAST_VERSION = {0, 0, 0, 1};
PGOProfilerHeader() : base::FileHeader(LAST_VERSION) {}
bool Verify()
{
return VerifyInner(LAST_VERSION);
}
};
class PandaFileProfilerInfo {
public:
PandaFileProfilerInfo() = default;
PandaFileProfilerInfo(uint32_t checksum) : checksum_(checksum) {}
uint32_t GetChecksum()
{
return checksum_;
}
private:
uint32_t checksum_;
};
class PGOProfiler {
public:
NO_COPY_SEMANTIC(PGOProfiler);
NO_MOVE_SEMANTIC(PGOProfiler);
void Sample(JSTaggedType value, SampleMode mode = SampleMode::CALL_MODE);
private:
PGOProfiler(EcmaVM *vm, bool isEnable)
: isEnable_(isEnable), chunk_(vm->GetNativeAreaAllocator()), profilerMap_(&chunk_) {};
virtual ~PGOProfiler();
void Reset(bool isEnable)
{
isEnable_ = isEnable;
profilerMap_.clear();
methodCount_ = 0;
}
static constexpr uint32_t MERGED_EVERY_COUNT = 10;
bool isEnable_ {false};
Chunk chunk_;
ChunkUnorderedMap<CString, ChunkUnorderedMap<EntityId, MethodProfilerInfo *> *> profilerMap_;
uint32_t methodCount_ {0};
friend class PGOProfilerManager;
};
class PGOProfilerManager {
public:
static PGOProfilerManager *GetInstance()
{
static PGOProfilerManager instance;
return &instance;
}
PGOProfilerManager() = default;
~PGOProfilerManager() = default;
NO_COPY_SEMANTIC(PGOProfilerManager);
NO_MOVE_SEMANTIC(PGOProfilerManager);
void Initialize(uint32_t hotnessThreshold, const std::string &outDir);
bool InitializeData();
void Destroy();
// Factory
PGOProfiler *Build(EcmaVM *vm, bool isEnable)
{
if (isEnable) {
isEnable = InitializeData();
}
return new PGOProfiler(vm, isEnable);
}
void Reset(PGOProfiler *profiler, bool isEnable)
{
if (profiler) {
profiler->Reset(isEnable);
}
if (isEnable) {
InitializeData();
}
}
void Destroy(PGOProfiler *profiler)
{
if (profiler != nullptr) {
Merge(profiler);
delete profiler;
}
}
void SamplePandaFileInfo(uint32_t checksum);
void Merge(PGOProfiler *profile);
void TerminateSaveTask();
void PostSaveTask();
private:
class SaveTask : public Task {
public:
explicit SaveTask(int32_t id) : Task(id) {};
virtual ~SaveTask() override = default;
bool Run([[maybe_unused]] uint32_t threadIndex) override
{
PGOProfilerManager::GetInstance()->StartSaveTask(this);
return true;
}
TaskType GetTaskType() override
{
return TaskType::PGO_SAVE_TASK;
}
NO_COPY_SEMANTIC(SaveTask);
NO_MOVE_SEMANTIC(SaveTask);
};
void StartSaveTask(SaveTask *task);
void SaveProfiler(SaveTask *task = nullptr);
void ProcessProfileHeader(std::ofstream &fileStream);
void ProcessPandaFileInfo(std::ofstream &fileStream);
void ProcessProfile(std::ofstream &fileStream, SaveTask *task);
bool isInitialized_ {false};
uint32_t hotnessThreshold_ {2};
std::string outDir_;
std::string realOutPath_;
std::unique_ptr<NativeAreaAllocator> nativeAreaAllocator_;
std::unique_ptr<Chunk> chunk_;
PGOProfilerHeader header_;
ChunkUnorderedMap<CString, ChunkUnorderedMap<EntityId, MethodProfilerInfo *> *> *globalProfilerMap_;
ChunkVector<PandaFileProfilerInfo *> *pandaFileProfilerInfos_;
os::memory::Mutex mutex_;
};
} // namespace panda::ecmascript
#endif // ECMASCRIPT_DFX_PGO_PROFILER_MANAGER_H

View File

@ -33,7 +33,6 @@
#include "ecmascript/compiler/common_stubs.h"
#include "ecmascript/compiler/interpreter_stub.h"
#include "ecmascript/compiler/rt_call_signature.h"
#include "ecmascript/dfx/pgo_profiler/pgo_profiler_manager.h"
#if defined(ECMASCRIPT_SUPPORT_CPUPROFILER)
#include "ecmascript/dfx/cpu_profiler/cpu_profiler.h"
#endif
@ -72,6 +71,7 @@
#include "ecmascript/module/js_module_manager.h"
#include "ecmascript/object_factory.h"
#include "ecmascript/patch/quick_fix_manager.h"
#include "ecmascript/pgo_profiler/pgo_profiler_manager.h"
#include "ecmascript/taskpool/taskpool.h"
#include "ecmascript/regexp/regexp_parser_cache.h"
#include "ecmascript/runtime_call_id.h"
@ -85,14 +85,6 @@
#include "ecmascript/ts_types/ts_manager.h"
#include "ecmascript/require/js_cjs_module_cache.h"
#include "ecmascript/require/js_require_manager.h"
#ifdef PANDA_TARGET_WINDOWS
#ifdef ERROR
#undef ERROR
#endif
#ifdef GetObject
#undef GetObject
#endif
#endif
namespace panda::ecmascript {
using PathHelper = base::PathHelper;

View File

@ -16,10 +16,6 @@
#include "ecmascript/js_thread.h"
#include "ecmascript/log_wrapper.h"
#include "ecmascript/platform/file.h"
#ifdef VOID
#undef VOID
#endif
#if defined(ENABLE_EXCEPTION_BACKTRACE)
#include "ecmascript/platform/backtrace.h"
#endif

View File

@ -17,9 +17,9 @@
#include "ecmascript/aot_file_manager.h"
#include "ecmascript/base/path_helper.h"
#include "ecmascript/dfx/pgo_profiler/pgo_profiler_manager.h"
#include "ecmascript/jspandafile/program_object.h"
#include "ecmascript/js_file_path.h"
#include "ecmascript/pgo_profiler/pgo_profiler_manager.h"
namespace panda::ecmascript {
static const size_t MALLOC_SIZE_LIMIT = 2147483648; // Max internal memory used by the VM declared in options

View File

@ -27,7 +27,6 @@
#if defined(ECMASCRIPT_SUPPORT_CPUPROFILER)
#include "ecmascript/dfx/cpu_profiler/cpu_profiler.h"
#endif
#include "ecmascript/dfx/pgo_profiler/pgo_profiler_manager.h"
#include "ecmascript/debugger/js_debugger_manager.h"
#include "ecmascript/ecma_global_storage.h"
#include "ecmascript/ecma_runtime_call_info.h"
@ -73,6 +72,7 @@
#include "ecmascript/module/js_module_source_text.h"
#include "ecmascript/object_factory.h"
#include "ecmascript/patch/quick_fix_manager.h"
#include "ecmascript/pgo_profiler/pgo_profiler_manager.h"
#include "ecmascript/platform/file.h"
#include "ecmascript/tagged_array.h"
#include "ecmascript/regexp/regexp_parser.h"
@ -751,7 +751,7 @@ void JSNApi::DestroyMemMapAllocator()
void JSNApi::InitializePGOProfiler(const ecmascript::JSRuntimeOptions &options)
{
ecmascript::PGOProfilerManager::GetInstance()->Initialize(
options.GetPGOHotnessThreshold(), options.GetPGOProfilerPath());
options.GetPGOProfilerPath(), options.GetPGOHotnessThreshold());
}
void JSNApi::DestroyPGOProfiler()

View File

@ -0,0 +1,48 @@
/*
* 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.h"
#include "ecmascript/js_function.h"
#include "ecmascript/pgo_profiler/pgo_profiler_manager.h"
namespace panda::ecmascript {
void PGOProfiler::Sample(JSTaggedType value, SampleMode mode)
{
if (!isEnable_) {
return;
}
DISALLOW_GARBAGE_COLLECTION;
JSTaggedValue jsValue(value);
if (jsValue.IsJSFunction() && JSFunction::Cast(jsValue)->GetMethod().IsMethod()) {
auto jsMethod = Method::Cast(JSFunction::Cast(jsValue)->GetMethod());
JSTaggedValue recordNameValue = JSFunction::Cast(jsValue)->GetRecordName();
if (recordNameValue.IsHole()) {
return;
}
CString recordName = ConvertToString(recordNameValue);
if (recordInfos_->AddMethod(recordName, jsMethod->GetMethodId(), jsMethod->GetMethodName(), mode)) {
methodCount_++;
}
// Merged every 10 methods
if (methodCount_ >= MERGED_EVERY_COUNT) {
LOG_ECMA(DEBUG) << "Sample: post task to save profiler";
PGOProfilerManager::GetInstance()->Merge(this);
PGOProfilerManager::GetInstance()->AsynSave();
methodCount_ = 0;
}
}
}
} // namespace panda::ecmascript

View File

@ -0,0 +1,65 @@
/*
* 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.
*/
#ifndef ECMASCRIPT_PGO_PROFILER_H
#define ECMASCRIPT_PGO_PROFILER_H
#include <memory>
#include "ecmascript/ecma_vm.h"
#include "ecmascript/pgo_profiler/pgo_profiler_info.h"
namespace panda::ecmascript {
class PGOProfiler {
public:
NO_COPY_SEMANTIC(PGOProfiler);
NO_MOVE_SEMANTIC(PGOProfiler);
void Sample(JSTaggedType value, SampleMode mode = SampleMode::CALL_MODE);
private:
static constexpr uint32_t MERGED_EVERY_COUNT = 10;
PGOProfiler([[maybe_unused]] EcmaVM *vm, bool isEnable) : isEnable_(isEnable)
{
if (isEnable_) {
recordInfos_ = std::make_unique<PGORecordDetailInfos>(0);
}
};
virtual ~PGOProfiler()
{
Reset(false);
}
void Reset(bool isEnable)
{
isEnable_ = isEnable;
methodCount_ = 0;
if (recordInfos_) {
recordInfos_->Clear();
} else {
if (isEnable_) {
recordInfos_ = std::make_unique<PGORecordDetailInfos>(0);
}
}
}
bool isEnable_ {false};
uint32_t methodCount_ {0};
std::unique_ptr<PGORecordDetailInfos> recordInfos_;
friend class PGOProfilerManager;
};
} // namespace panda::ecmascript
#endif // ECMASCRIPT_PGO_PROFILER_H

View File

@ -0,0 +1,471 @@
/*
* 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_info.h"
#include "ecmascript/base/bit_helper.h"
#include "ecmascript/pgo_profiler/pgo_profiler_saver.h"
namespace panda::ecmascript {
static const std::string ELEMENT_SEPARATOR = "/";
static const std::string BLOCK_SEPARATOR = ",";
static const std::string BLOCK_START = ":";
static const std::string ARRAY_START = "[";
static const std::string ARRAY_END = "]";
static const std::string NEW_LINE = "\n";
static const std::string SPACE = " ";
static const std::string BLOCK_AND_ARRAY_START = BLOCK_START + SPACE + ARRAY_START + SPACE;
static const std::string VERSION_HEADER = "Profiler Version" + BLOCK_START + SPACE;
static const std::string PANDA_FILE_INFO_HEADER = "Panda file sumcheck list" + BLOCK_AND_ARRAY_START;
bool PGOProfilerHeader::ParseFromBinary(void *buffer, PGOProfilerHeader **header)
{
auto in = reinterpret_cast<PGOProfilerHeader *>(buffer);
if (in->Verify()) {
size_t desSize = in->Size();
if (desSize > LastSize()) {
LOG_ECMA(ERROR) << "header size error, expected size is less than " << LastSize()
<< ", but got " << desSize;
return false;
}
Build(header, desSize);
if (memcpy_s(*header, desSize, in, in->Size()) != EOK) {
UNREACHABLE();
}
return true;
}
return false;
}
void PGOProfilerHeader::ProcessToBinary(std::ofstream &fileStream) const
{
fileStream.seekp(0);
fileStream.write(reinterpret_cast<const char *>(this), Size());
}
bool PGOProfilerHeader::ParseFromText(std::ifstream &stream)
{
std::string header;
if (std::getline(stream, header)) {
if (header.empty()) {
return false;
}
auto index = header.find(BLOCK_START);
if (index == std::string::npos) {
return false;
}
auto version = header.substr(index + 1);
if (!SetVersionInner(version)) {
return false;
}
if (!Verify()) {
return false;
}
return true;
}
return false;
}
bool PGOProfilerHeader::ProcessToText(std::ofstream &stream) const
{
if (!Verify()) {
return false;
}
stream << VERSION_HEADER << GetVersionInner() << NEW_LINE;
return true;
}
void PGOPandaFileInfos::ParseFromBinary(void *buffer, SectionInfo *const info)
{
void *addr = reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(buffer) + info->offset_);
for (uint32_t i = 0; i < info->number_; i++) {
pandaFileInfos_.emplace(*base::ReadBufferInSize<PandaFileInfo>(&addr));
}
LOG_ECMA(DEBUG) << "Profiler panda file count:" << info->number_;
}
void PGOPandaFileInfos::ProcessToBinary(std::ofstream &fileStream, SectionInfo *info) const
{
fileStream.seekp(info->offset_);
info->number_ = pandaFileInfos_.size();
for (auto localInfo : pandaFileInfos_) {
fileStream.write(reinterpret_cast<char *>(&localInfo), localInfo.Size());
}
info->size_ = static_cast<uint32_t>(fileStream.tellp()) - info->offset_;
}
bool PGOPandaFileInfos::ParseFromText(std::ifstream &stream)
{
std::string pandaFileInfo;
while (std::getline(stream, pandaFileInfo)) {
if (pandaFileInfo.empty()) {
continue;
}
size_t start = pandaFileInfo.find_first_of(ARRAY_START);
size_t end = pandaFileInfo.find_last_of(ARRAY_END);
if (start == std::string::npos || end == std::string::npos || start > end) {
return false;
}
auto content = pandaFileInfo.substr(start + 1, end - (start + 1) - 1);
std::vector<std::string> infos = base::StringHelper::SplitString(content, BLOCK_SEPARATOR);
for (auto checksum : infos) {
uint32_t result;
if (!base::StringHelper::StrToUInt32(checksum.c_str(), &result)) {
LOG_ECMA(ERROR) << "checksum: " << checksum << " parse failed";
return false;
}
Sample(result);
}
return true;
}
return true;
}
void PGOPandaFileInfos::ProcessToText(std::ofstream &stream) const
{
std::string pandaFileInfo = NEW_LINE + PANDA_FILE_INFO_HEADER;
bool isFirst = true;
for (auto &info : pandaFileInfos_) {
if (!isFirst) {
pandaFileInfo += BLOCK_SEPARATOR + SPACE;
} else {
isFirst = false;
}
pandaFileInfo += std::to_string(info.GetChecksum());
}
pandaFileInfo += (SPACE + ARRAY_END + NEW_LINE);
stream << pandaFileInfo;
}
bool PGOPandaFileInfos::CheckSum(uint32_t checksum) const
{
if (pandaFileInfos_.find(checksum) == pandaFileInfos_.end()) {
LOG_ECMA(ERROR) << "Checksum verification failed. Please ensure that the .abc and .ap match.";
return false;
}
return true;
}
void PGOMethodInfo::ProcessToText(std::string &text) const
{
text += std::to_string(GetMethodId().GetOffset());
text += ELEMENT_SEPARATOR;
text += std::to_string(GetCount());
text += ELEMENT_SEPARATOR;
text += GetSampleModeToString();
text += ELEMENT_SEPARATOR;
text += GetMethodName();
}
std::vector<std::string> PGOMethodInfo::ParseFromText(const std::string &infoString)
{
std::vector<std::string> infoStrings = base::StringHelper::SplitString(infoString, ELEMENT_SEPARATOR);
return infoStrings;
}
bool PGOMethodInfoMap::AddMethod(Chunk *chunk, EntityId methodId, const CString &methodName, SampleMode mode)
{
auto result = methodInfos_.find(methodId);
if (result != methodInfos_.end()) {
auto info = result->second;
info->IncreaseCount();
info->SetSampleMode(mode);
return false;
} else {
size_t strlen = methodName.size();
size_t size = PGOMethodInfo::Size(strlen);
void *infoAddr = chunk->Allocate(size);
auto info = new (infoAddr) PGOMethodInfo(methodId, 1, mode, methodName.c_str());
methodInfos_.emplace(methodId, info);
return true;
}
}
void PGOMethodInfoMap::Merge(Chunk *chunk, PGOMethodInfoMap *methodInfos)
{
for (auto iter = methodInfos->methodInfos_.begin(); iter != methodInfos->methodInfos_.end(); iter++) {
auto methodId = iter->first;
auto fromMethodInfo = iter->second;
auto result = methodInfos_.find(methodId);
if (result != methodInfos_.end()) {
auto toMethodInfo = result->second;
toMethodInfo->Merge(fromMethodInfo);
} else {
size_t len = strlen(fromMethodInfo->GetMethodName());
size_t size = PGOMethodInfo::Size(len);
void *infoAddr = chunk->Allocate(size);
auto newMethodInfo = new (infoAddr) PGOMethodInfo(methodId, fromMethodInfo->GetCount(),
fromMethodInfo->GetSampleMode(), fromMethodInfo->GetMethodName());
methodInfos_.emplace(methodId, newMethodInfo);
}
fromMethodInfo->ClearCount();
}
}
bool PGOMethodInfoMap::ParseFromBinary(uint32_t threshold, void **buffer)
{
SectionInfo secInfo = base::ReadBuffer<SectionInfo>(buffer);
for (uint32_t j = 0; j < secInfo.number_; j++) {
PGOMethodInfo *info = base::ReadBufferInSize<PGOMethodInfo>(buffer);
if (!info->IsFilter(threshold)) {
methodInfos_.emplace(info->GetMethodId(), info);
LOG_ECMA(DEBUG) << "Method:" << info->GetMethodId() << ELEMENT_SEPARATOR << info->GetCount()
<< ELEMENT_SEPARATOR << std::to_string(static_cast<int>(info->GetSampleMode()))
<< ELEMENT_SEPARATOR << info->GetMethodName();
}
}
return !methodInfos_.empty();
}
bool PGOMethodInfoMap::ProcessToBinary(uint32_t threshold, const CString &recordName,
const SaveTask *task, std::ofstream &stream) const
{
SectionInfo secInfo;
std::stringstream methodStream;
for (auto iter = methodInfos_.begin(); iter != methodInfos_.end(); iter++) {
LOG_ECMA(DEBUG) << "Method:" << iter->first << ELEMENT_SEPARATOR << iter->second->GetCount()
<< ELEMENT_SEPARATOR << std::to_string(static_cast<int>(iter->second->GetSampleMode()))
<< ELEMENT_SEPARATOR << iter->second->GetMethodName();
if (task && task->IsTerminate()) {
LOG_ECMA(INFO) << "ProcessProfile: task is already terminate";
return false;
}
auto curMethodInfo = iter->second;
if (curMethodInfo->IsFilter(threshold)) {
continue;
}
methodStream.write(reinterpret_cast<char *>(curMethodInfo), curMethodInfo->Size());
secInfo.number_++;
}
if (secInfo.number_ > 0) {
secInfo.offset_ = sizeof(SectionInfo);
secInfo.size_ = methodStream.tellg();
stream << recordName << '\0';
stream.write(reinterpret_cast<char *>(&secInfo), sizeof(SectionInfo));
stream << methodStream.rdbuf();
return true;
}
return false;
}
bool PGOMethodInfoMap::ParseFromText(Chunk *chunk, uint32_t threshold, const std::vector<std::string> &content)
{
for (auto infoString : content) {
std::vector<std::string> infoStrings = PGOMethodInfo::ParseFromText(infoString);
if (infoStrings.size() < PGOMethodInfo::METHOD_INFO_COUNT) {
LOG_ECMA(ERROR) << "method info:" << infoString << " format error";
return false;
}
uint32_t count;
if (!base::StringHelper::StrToUInt32(infoStrings[PGOMethodInfo::METHOD_COUNT_INDEX].c_str(), &count)) {
LOG_ECMA(ERROR) << "count: " << infoStrings[PGOMethodInfo::METHOD_COUNT_INDEX] << " parse failed";
return false;
}
SampleMode mode;
if (!PGOMethodInfo::GetSampleMode(infoStrings[PGOMethodInfo::METHOD_MODE_INDEX], mode)) {
LOG_ECMA(ERROR) << "mode: " << infoStrings[PGOMethodInfo::METHOD_MODE_INDEX] << " parse failed";
return false;
}
if (count < threshold && mode == SampleMode::CALL_MODE) {
return true;
}
uint32_t methodId;
if (!base::StringHelper::StrToUInt32(infoStrings[PGOMethodInfo::METHOD_ID_INDEX].c_str(), &methodId)) {
LOG_ECMA(ERROR) << "method id: " << infoStrings[PGOMethodInfo::METHOD_ID_INDEX] << " parse failed" ;
return false;
}
std::string methodName = infoStrings[PGOMethodInfo::METHOD_NAME_INDEX];
size_t len = methodName.size();
void *infoAddr = chunk->Allocate(PGOMethodInfo::Size(len));
auto info = new (infoAddr) PGOMethodInfo(EntityId(methodId), count, mode, methodName.c_str());
methodInfos_.emplace(methodId, info);
}
return true;
}
void PGOMethodInfoMap::ProcessToText(uint32_t threshold, const CString &recordName, std::ofstream &stream) const
{
std::string profilerString;
bool isFirst = true;
for (auto methodInfoIter : methodInfos_) {
auto methodInfo = methodInfoIter.second;
if (methodInfo->IsFilter(threshold)) {
continue;
}
if (isFirst) {
profilerString += NEW_LINE;
profilerString += recordName;
profilerString += BLOCK_AND_ARRAY_START;
isFirst = false;
} else {
profilerString += BLOCK_SEPARATOR + SPACE;
}
methodInfo->ProcessToText(profilerString);
}
if (!isFirst) {
profilerString += (SPACE + ARRAY_END + NEW_LINE);
stream << profilerString;
}
}
bool PGOMethodIdSet::ParseFromBinary(uint32_t threshold, void **buffer)
{
SectionInfo secInfo = base::ReadBuffer<SectionInfo>(buffer);
for (uint32_t j = 0; j < secInfo.number_; j++) {
PGOMethodInfo *info = base::ReadBufferInSize<PGOMethodInfo>(buffer);
if (!info->IsFilter(threshold)) {
methodIdSet_.emplace(info->GetMethodId());
LOG_ECMA(DEBUG) << "Method:" << info->GetMethodId() << ELEMENT_SEPARATOR << info->GetCount()
<< ELEMENT_SEPARATOR << std::to_string(static_cast<int>(info->GetSampleMode()))
<< ELEMENT_SEPARATOR << info->GetMethodName();
}
}
return methodIdSet_.size() != 0;
}
bool PGORecordDetailInfos::AddMethod(const CString &recordName, EntityId methodId,
const CString &methodName, SampleMode mode)
{
auto iter = recordInfos_.find(recordName.c_str());
PGOMethodInfoMap *curMethodInfos = nullptr;
if (iter != recordInfos_.end()) {
curMethodInfos = iter->second;
} else {
curMethodInfos = nativeAreaAllocator_.New<PGOMethodInfoMap>();
recordInfos_.emplace(recordName.c_str(), curMethodInfos);
}
return curMethodInfos->AddMethod(chunk_.get(), methodId, methodName, mode);
}
void PGORecordDetailInfos::Merge(const PGORecordDetailInfos &recordInfos)
{
for (auto iter = recordInfos.recordInfos_.begin(); iter != recordInfos.recordInfos_.end(); iter++) {
auto recordName = iter->first;
auto fromMethodInfos = iter->second;
auto recordInfosIter = recordInfos_.find(recordName);
PGOMethodInfoMap *toMethodInfos = nullptr;
if (recordInfosIter == recordInfos_.end()) {
toMethodInfos = nativeAreaAllocator_.New<PGOMethodInfoMap>();
recordInfos_.emplace(recordName, toMethodInfos);
} else {
toMethodInfos = recordInfosIter->second;
}
toMethodInfos->Merge(chunk_.get(), fromMethodInfos);
}
}
void PGORecordDetailInfos::ParseFromBinary(void *buffer, SectionInfo *const info)
{
void *addr = reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(buffer) + info->offset_);
for (uint32_t i = 0; i < info->number_; i++) {
auto recordName = base::ReadBuffer(&addr);
PGOMethodInfoMap *methodInfos = nativeAreaAllocator_.New<PGOMethodInfoMap>();
if (methodInfos->ParseFromBinary(hotnessThreshold_, &addr)) {
recordInfos_.emplace(recordName, methodInfos);
}
}
}
void PGORecordDetailInfos::ProcessToBinary(const SaveTask *task, std::ofstream &fileStream, SectionInfo *info) const
{
info->number_ = 0;
info->offset_ = static_cast<uint32_t>(fileStream.tellp());
for (auto iter = recordInfos_.begin(); iter != recordInfos_.end(); iter++) {
auto recordName = iter->first;
auto curMethodInfos = iter->second;
if (curMethodInfos->ProcessToBinary(hotnessThreshold_, recordName, task, fileStream)) {
info->number_++;
}
}
info->size_ = static_cast<uint32_t>(fileStream.tellp()) - info->offset_;
}
bool PGORecordDetailInfos::ParseFromText(std::ifstream &stream)
{
std::string details;
while (std::getline(stream, details)) {
if (details.empty()) {
continue;
}
size_t blockIndex = details.find_first_of(BLOCK_START);
if (blockIndex == std::string::npos) {
return false;
}
CString recordName = ConvertToString(details.substr(0, blockIndex));
size_t start = details.find_first_of(ARRAY_START);
size_t end = details.find_last_of(ARRAY_END);
if (start == std::string::npos || end == std::string::npos || start > end) {
return false;
}
auto content = details.substr(start + 1, end - (start + 1) - 1);
std::vector<std::string> infoStrings = base::StringHelper::SplitString(content, BLOCK_SEPARATOR);
if (infoStrings.size() <= 0) {
continue;
}
auto methodInfosIter = recordInfos_.find(recordName.c_str());
PGOMethodInfoMap *methodInfos = nullptr;
if (methodInfosIter == recordInfos_.end()) {
methodInfos = nativeAreaAllocator_.New<PGOMethodInfoMap>();
recordInfos_.emplace(recordName.c_str(), methodInfos);
} else {
methodInfos = methodInfosIter->second;
}
if (!methodInfos->ParseFromText(chunk_.get(), hotnessThreshold_, infoStrings)) {
return false;
}
}
return true;
}
void PGORecordDetailInfos::ProcessToText(std::ofstream &stream) const
{
for (auto iter = recordInfos_.begin(); iter != recordInfos_.end(); iter++) {
auto recordName = iter->first;
auto methodInfos = iter->second;
methodInfos->ProcessToText(hotnessThreshold_, recordName, stream);
}
}
bool PGORecordSimpleInfos::Match(const CString &recordName, EntityId methodId)
{
auto methodIdsIter = methodIds_.find(recordName);
if (methodIdsIter == methodIds_.end()) {
return false;
}
return methodIdsIter->second->Match(methodId);
}
void PGORecordSimpleInfos::ParseFromBinary(void *buffer, SectionInfo *const info)
{
void *addr = reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(buffer) + info->offset_);
for (uint32_t i = 0; i < info->number_; i++) {
auto recordName = base::ReadBuffer(&addr);
PGOMethodIdSet *methodIds = nativeAreaAllocator_.New<PGOMethodIdSet>();
if (methodIds->ParseFromBinary(hotnessThreshold_, &addr)) {
methodIds_.emplace(recordName, methodIds);
}
}
}
} // namespace panda::ecmascript

View File

@ -0,0 +1,485 @@
/*
* 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.
*/
#ifndef ECMASCRIPT_PGO_PROFILER_INFO_H
#define ECMASCRIPT_PGO_PROFILER_INFO_H
#include <memory>
#include <sstream>
#include <string.h>
#include "ecmascript/base/file_header.h"
#include "ecmascript/jspandafile/method_literal.h"
#include "ecmascript/mem/c_containers.h"
#include "ecmascript/mem/native_area_allocator.h"
namespace panda::ecmascript {
class SaveTask;
enum class SampleMode : uint8_t {
HOTNESS_MODE,
CALL_MODE,
};
struct SectionInfo {
uint32_t offset_ {0};
// reserve
uint32_t size_ {0};
uint32_t number_ {0};
};
static constexpr size_t ALIGN_SIZE = 4;
/**
* |----PGOProfilerHeader
* |--------MAGIC
* |--------VERSION
* |--------SECTION_NUMBER
* |--------PANDA_FILE_INFO_SECTION_INFO
* |------------offset
* |------------size (reserve)
* |------------number
* |--------RECORD_INFO_SECTION_INFO
* |------------offset
* |------------size (reserve)
* |------------number
* |----PGOPandaFileInfos
* |--------SIZE
* |--------CHECK_SUM
* |--------...
* |----PGORecordDetailInfos
* |--------PGOMethodInfoMap
* |------------PGOMethodInfo
* |----------------size
* |----------------id
* |----------------count
* |----------------mode
* |----------------methodName
* |----------------...
* |----PGORecordSimpleInfos
* |--------PGOMethodIdSet
* |------------id
* |------------...
*/
class PGOProfilerHeader : public base::FileHeader {
public:
static constexpr std::array<uint8_t, VERSION_SIZE> LAST_VERSION = {0, 0, 0, 1};
static constexpr size_t SECTION_SIZE = 2;
static constexpr size_t PANDA_FILE_SECTION_INDEX = 0;
static constexpr size_t RECORD_INFO_SECTION_INDEX = 1;
PGOProfilerHeader() : base::FileHeader(LAST_VERSION), sectionNumber_(SECTION_SIZE)
{
GetPandaInfoSection()->offset_ = Size();
}
static size_t LastSize()
{
return sizeof(PGOProfilerHeader) + (SECTION_SIZE - 1) * sizeof(SectionInfo);
}
size_t Size() const
{
return sizeof(PGOProfilerHeader) + (sectionNumber_ - 1) * sizeof(SectionInfo);
}
bool Verify() const
{
return VerifyInner(LAST_VERSION);
}
static void Build(PGOProfilerHeader **header, size_t size)
{
*header = reinterpret_cast<PGOProfilerHeader *>(malloc(size));
new (*header) PGOProfilerHeader();
}
static void Destroy(PGOProfilerHeader **header)
{
if (*header != nullptr) {
free(*header);
*header = nullptr;
}
}
// Copy Header.
static bool ParseFromBinary(void *buffer, PGOProfilerHeader **header);
void ProcessToBinary(std::ofstream &fileStream) const;
bool ParseFromText(std::ifstream &stream);
bool ProcessToText(std::ofstream &stream) const;
SectionInfo *GetPandaInfoSection() const
{
return GetSectionInfo(PANDA_FILE_SECTION_INDEX);
}
SectionInfo *GetRecordInfoSection() const
{
return GetSectionInfo(RECORD_INFO_SECTION_INDEX);
}
NO_COPY_SEMANTIC(PGOProfilerHeader);
NO_MOVE_SEMANTIC(PGOProfilerHeader);
private:
SectionInfo *GetSectionInfo(size_t index) const
{
if (index >= SECTION_SIZE) {
return nullptr;
}
return const_cast<SectionInfo *>(&sectionInfos_) + index;
}
uint32_t sectionNumber_ {SECTION_SIZE};
SectionInfo sectionInfos_;
};
class PGOPandaFileInfos {
public:
void Sample(uint32_t checksum)
{
pandaFileInfos_.insert(checksum);
}
void Clear()
{
pandaFileInfos_.clear();
}
void ParseFromBinary(void *buffer, SectionInfo *const info);
void ProcessToBinary(std::ofstream &fileStream, SectionInfo *info) const;
void ProcessToText(std::ofstream &stream) const;
bool ParseFromText(std::ifstream &stream);
bool CheckSum(uint32_t checksum) const;
private:
class PandaFileInfo {
public:
PandaFileInfo() = default;
PandaFileInfo(uint32_t checksum) : size_(LastSize()), checksum_(checksum) {}
static size_t LastSize()
{
return sizeof(PandaFileInfo);
}
size_t Size()
{
return static_cast<size_t>(size_);
}
bool operator<(const PandaFileInfo &right) const
{
return checksum_ < right.checksum_;
}
uint32_t GetChecksum() const
{
return checksum_;
}
private:
// Support extended fields
uint32_t size_;
uint32_t checksum_;
};
std::set<PandaFileInfo> pandaFileInfos_;
};
class PGOMethodInfo {
public:
static constexpr int METHOD_INFO_COUNT = 4;
static constexpr int METHOD_ID_INDEX = 0;
static constexpr int METHOD_COUNT_INDEX = 1;
static constexpr int METHOD_MODE_INDEX = 2;
static constexpr int METHOD_NAME_INDEX = 3;
PGOMethodInfo(EntityId id) : id_(id) {}
PGOMethodInfo(EntityId id, uint32_t count, SampleMode mode, const char *methodName)
: id_(id), count_(count), mode_(mode)
{
size_t len = strlen(methodName);
size_ = Size(len);
if (len > 0 && memcpy_s(&methodName_, len, methodName, len) != EOK) {
LOG_ECMA(ERROR) << "SetMethodName memcpy_s failed" << methodName << ", len = " << len;
UNREACHABLE();
}
*(&methodName_ + len) = '\0';
}
static int32_t Size(uint32_t length)
{
return sizeof(PGOMethodInfo) + AlignUp(length, ALIGN_SIZE);
}
int32_t Size() const
{
return size_;
}
static bool GetSampleMode(std::string content, SampleMode &mode)
{
if (content == "HOTNESS_MODE") {
mode = SampleMode::HOTNESS_MODE;
} else if (content == "CALL_MODE") {
mode = SampleMode::CALL_MODE;
} else {
return false;
}
return true;
}
void IncreaseCount()
{
count_++;
}
void ClearCount()
{
count_ = 0;
}
void Merge(const PGOMethodInfo *info)
{
if (!(id_ == info->GetMethodId())) {
LOG_ECMA(ERROR) << "The method id must same for merging";
return;
}
count_ += info->GetCount();
SetSampleMode(info->GetSampleMode());
}
EntityId GetMethodId() const
{
return id_;
}
uint32_t GetCount() const
{
return count_;
}
const char *GetMethodName() const
{
return &methodName_;
}
void SetSampleMode(SampleMode mode)
{
if (mode_ == SampleMode::HOTNESS_MODE) {
return;
}
mode_ = mode;
}
SampleMode GetSampleMode() const
{
return mode_;
}
std::string GetSampleModeToString() const
{
std::string result;
switch (mode_) {
case SampleMode::HOTNESS_MODE:
result = "HOTNESS_MODE";
break;
case SampleMode::CALL_MODE:
result = "CALL_MODE";
break;
default:
LOG_ECMA(ERROR) << "mode error";
}
return result;
}
bool IsFilter(uint32_t threshold) const
{
if (count_ < threshold && mode_ == SampleMode::CALL_MODE) {
return true;
}
return false;
}
void ParseFromBinary(void **buffer);
void ProcessToBinary(std::ofstream &fileStream) const;
static std::vector<std::string> ParseFromText(const std::string &infoString);
void ProcessToText(std::string &text) const;
NO_COPY_SEMANTIC(PGOMethodInfo);
NO_MOVE_SEMANTIC(PGOMethodInfo);
private:
uint32_t size_ {0};
EntityId id_;
uint32_t count_ {0};
SampleMode mode_ {SampleMode::CALL_MODE};
char methodName_;
};
class PGOMethodInfoMap {
public:
PGOMethodInfoMap() = default;
void Clear()
{
// PGOMethodInfo release by chunk
methodInfos_.clear();
}
bool AddMethod(Chunk *chunk, EntityId methodId, const CString &methodName, SampleMode mode);
void Merge(Chunk *chunk, PGOMethodInfoMap *methodInfos);
bool ParseFromBinary(uint32_t threshold, void **buffer);
bool ProcessToBinary(uint32_t threshold, const CString &recordName, const SaveTask *task,
std::ofstream &fileStream) const;
bool ParseFromText(Chunk *chunk, uint32_t threshold, const std::vector<std::string> &content);
void ProcessToText(uint32_t threshold, const CString &recordName, std::ofstream &stream) const;
NO_COPY_SEMANTIC(PGOMethodInfoMap);
NO_MOVE_SEMANTIC(PGOMethodInfoMap);
private:
CMap<EntityId, PGOMethodInfo *> methodInfos_;
};
class PGOMethodIdSet {
public:
PGOMethodIdSet() = default;
bool Match(EntityId methodId)
{
return methodIdSet_.find(methodId) != methodIdSet_.end();
}
template <typename Callback>
bool Update(const CString &recordName, Callback callback)
{
std::unordered_set<EntityId> newIds = callback(recordName, methodIdSet_);
if (!newIds.empty()) {
methodIdSet_.insert(newIds.begin(), newIds.end());
return true;
}
return false;
}
bool ParseFromBinary(uint32_t threshold, void **buffer);
NO_COPY_SEMANTIC(PGOMethodIdSet);
NO_MOVE_SEMANTIC(PGOMethodIdSet);
private:
std::unordered_set<EntityId> methodIdSet_;
};
class PGORecordDetailInfos {
public:
explicit PGORecordDetailInfos(uint32_t hotnessThreshold) : hotnessThreshold_(hotnessThreshold)
{
chunk_ = std::make_unique<Chunk>(&nativeAreaAllocator_);
};
~PGORecordDetailInfos()
{
Clear();
}
void Clear()
{
for (auto iter : recordInfos_) {
iter.second->Clear();
nativeAreaAllocator_.Delete(iter.second);
}
recordInfos_.clear();
chunk_ = std::make_unique<Chunk>(&nativeAreaAllocator_);
}
// If it is a new method, return true.
bool AddMethod(const CString &recordName, EntityId methodId, const CString &methodName, SampleMode mode);
void Merge(const PGORecordDetailInfos &recordInfos);
void ParseFromBinary(void *buffer, SectionInfo *const info);
void ProcessToBinary(const SaveTask *task, std::ofstream &fileStream, SectionInfo *info) const;
bool ParseFromText(std::ifstream &stream);
void ProcessToText(std::ofstream &stream) const;
NO_COPY_SEMANTIC(PGORecordDetailInfos);
NO_MOVE_SEMANTIC(PGORecordDetailInfos);
private:
uint32_t hotnessThreshold_ {2};
NativeAreaAllocator nativeAreaAllocator_;
std::unique_ptr<Chunk> chunk_;
CMap<CString, PGOMethodInfoMap *> recordInfos_;
};
class PGORecordSimpleInfos {
public:
explicit PGORecordSimpleInfos(uint32_t threshold) : hotnessThreshold_(threshold) {}
~PGORecordSimpleInfos()
{
Clear();
}
void Clear()
{
for (auto iter : methodIds_) {
nativeAreaAllocator_.Delete(iter.second);
}
methodIds_.clear();
}
bool Match(const CString &recordName, EntityId methodId);
template <typename Callback>
void Update(Callback callback)
{
for (auto iter = methodIds_.begin(); iter != methodIds_.end(); iter++) {
auto recordName = iter->first;
auto methodIds = iter->second;
methodIds->Update(recordName, callback);
}
}
template <typename Callback>
void Update(const CString &recordName, Callback callback)
{
auto iter = methodIds_.find(recordName);
if (iter != methodIds_.end()) {
iter->second->Update(recordName, callback);
} else {
PGOMethodIdSet *methodIds = nativeAreaAllocator_.New<PGOMethodIdSet>();
if (methodIds->Update(recordName, callback)) {
methodIds_.emplace(recordName, methodIds);
} else {
nativeAreaAllocator_.Delete(methodIds);
}
}
}
void ParseFromBinary(void *buffer, SectionInfo *const info);
NO_COPY_SEMANTIC(PGORecordSimpleInfos);
NO_MOVE_SEMANTIC(PGORecordSimpleInfos);
private:
uint32_t hotnessThreshold_ {2};
NativeAreaAllocator nativeAreaAllocator_;
CUnorderedMap<CString, PGOMethodIdSet *> methodIds_;
};
} // namespace panda::ecmascript
#endif // ECMASCRIPT_PGO_PROFILER_INFO_H

View File

@ -0,0 +1,177 @@
/*
* Copyright (c) 2022 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_loader.h"
#include "ecmascript/log_wrapper.h"
#include "ecmascript/pgo_profiler/pgo_profiler_info.h"
#include "ecmascript/platform/file.h"
namespace panda::ecmascript {
bool PGOProfilerLoader::Load()
{
if (isLoaded_) {
Clear();
}
if (!LoadAPBinaryFile()) {
return false;
}
void *addr = fileMapAddr_.GetOriginAddr();
if (!PGOProfilerHeader::ParseFromBinary(addr, &header_)) {
UnLoadAPBinaryFile();
LOG_ECMA(ERROR) << "Parse profiler header failed";
return false;
}
pandaFileInfos_.ParseFromBinary(addr, header_->GetPandaInfoSection());
if (!recordSimpleInfos_) {
recordSimpleInfos_ = std::make_unique<PGORecordSimpleInfos>(hotnessThreshold_);
}
recordSimpleInfos_->ParseFromBinary(addr, header_->GetRecordInfoSection());
UnLoadAPBinaryFile();
isLoaded_ = true;
return true;
}
bool PGOProfilerLoader::Verify(uint32_t checksum)
{
if (!isLoaded_) {
return false;
}
isVerifySuccess_ = pandaFileInfos_.CheckSum(checksum);
return isVerifySuccess_;
}
bool PGOProfilerLoader::LoadAndVerify(uint32_t checksum)
{
// The file does not exist. Enter full compiler mode.
if (inPath_.empty()) {
LOG_ECMA(INFO) << "When the file is empty. Enter full compiler mode.";
Clear();
return true;
}
if (Load() && Verify(checksum)) {
return true;
}
return false;
}
bool PGOProfilerLoader::LoadFull()
{
if (isLoaded_) {
Clear();
}
if (!LoadAPBinaryFile()) {
return false;
}
void *addr = fileMapAddr_.GetOriginAddr();
if (!PGOProfilerHeader::ParseFromBinary(addr, &header_)) {
UnLoadAPBinaryFile();
LOG_ECMA(ERROR) << "Parse profiler header failed";
return false;
}
pandaFileInfos_.ParseFromBinary(addr, header_->GetPandaInfoSection());
if (!recordDetailInfos_) {
recordDetailInfos_ = std::make_unique<PGORecordDetailInfos>(hotnessThreshold_);
}
recordDetailInfos_->ParseFromBinary(addr, header_->GetRecordInfoSection());
isLoaded_ = true;
return true;
}
bool PGOProfilerLoader::SaveAPTextFile(const std::string &outPath)
{
if (!isLoaded_) {
return false;
}
std::string realOutPath;
if (!RealPath(outPath, realOutPath, false)) {
return false;
}
std::ofstream fileStream(realOutPath.c_str());
if (!fileStream.is_open()) {
LOG_ECMA(ERROR) << "The file path(" << realOutPath << ") open failure!";
return false;
}
if (!header_->ProcessToText(fileStream)) {
return false;
}
pandaFileInfos_.ProcessToText(fileStream);
recordDetailInfos_->ProcessToText(fileStream);
return true;
}
bool PGOProfilerLoader::LoadAPBinaryFile()
{
std::string realPath;
if (!RealPath(inPath_, realPath)) {
return false;
}
static const std::string endString = ".ap";
if (realPath.compare(realPath.length() - endString.length(), endString.length(), endString)) {
LOG_ECMA(ERROR) << "The file path( " << realPath << ") does not end with .ap";
return false;
}
LOG_ECMA(INFO) << "Load profiler from file:" << realPath;
fileMapAddr_ = FileMap(realPath.c_str(), FILE_RDONLY, PAGE_PROT_READ);
if (fileMapAddr_.GetOriginAddr() == nullptr) {
LOG_ECMA(ERROR) << "File mmap failed";
return false;
}
return true;
}
void PGOProfilerLoader::UnLoadAPBinaryFile()
{
if (fileMapAddr_.GetOriginAddr() != nullptr && fileMapAddr_.GetSize() > 0) {
FileUnMap(fileMapAddr_);
fileMapAddr_.Reset();
}
}
void PGOProfilerLoader::Clear()
{
if (isLoaded_) {
UnLoadAPBinaryFile();
isVerifySuccess_ = true;
hotnessThreshold_ = 0;
PGOProfilerHeader::Destroy(&header_);
pandaFileInfos_.Clear();
if (recordDetailInfos_) {
recordDetailInfos_->Clear();
}
if (recordSimpleInfos_) {
recordSimpleInfos_->Clear();
}
isLoaded_ = false;
}
}
bool PGOProfilerLoader::Match(const CString &recordName, EntityId methodId)
{
if (!isLoaded_) {
return true;
}
if (!isVerifySuccess_) {
return false;
}
return recordSimpleInfos_->Match(recordName, methodId);
}
} // namespace panda::ecmascript

View File

@ -0,0 +1,83 @@
/*
* Copyright (c) 2022 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.
*/
#ifndef ECMASCRIPT_PGO_PROFILE_LOADER_H
#define ECMASCRIPT_PGO_PROFILE_LOADER_H
#include "ecmascript/pgo_profiler/pgo_profiler_info.h"
#include "ecmascript/platform/map.h"
namespace panda::ecmascript {
class PGOProfilerLoader {
public:
PGOProfilerLoader() = default;
PGOProfilerLoader(const std::string &inPath, uint32_t hotnessThreshold)
: inPath_(inPath), hotnessThreshold_(hotnessThreshold) {}
virtual ~PGOProfilerLoader() = default;
NO_COPY_SEMANTIC(PGOProfilerLoader);
NO_MOVE_SEMANTIC(PGOProfilerLoader);
bool PUBLIC_API Match(const CString &recordName, EntityId methodId);
bool PUBLIC_API LoadAndVerify(uint32_t checksum);
bool PUBLIC_API LoadFull();
void PUBLIC_API Clear();
bool PUBLIC_API SaveAPTextFile(const std::string &outPath);
template <typename Callback>
void Update(Callback callback)
{
if (!isLoaded_ || !isVerifySuccess_) {
return;
}
recordSimpleInfos_->Update(callback);
}
template <typename Callback>
void Update(const CString &recordName, Callback callback)
{
if (!isLoaded_ || !isVerifySuccess_) {
return;
}
recordSimpleInfos_->Update(recordName, callback);
}
bool IsLoaded() const
{
return isLoaded_;
}
private:
bool Load();
bool Verify(uint32_t checksum);
bool LoadAPBinaryFile();
void UnLoadAPBinaryFile();
bool isLoaded_ {false};
bool isVerifySuccess_ {false};
std::string inPath_;
uint32_t hotnessThreshold_ {0};
PGOProfilerHeader *header_ {nullptr};
PGOPandaFileInfos pandaFileInfos_;
std::unique_ptr<PGORecordDetailInfos> recordDetailInfos_;
std::unique_ptr<PGORecordSimpleInfos> recordSimpleInfos_;
MemMap fileMapAddr_;
};
} // namespace panda::ecmascript
#endif // ECMASCRIPT_PGO_PROFILE_LOADER_H

View File

@ -0,0 +1,139 @@
/*
* 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.
*/
#ifndef ECMASCRIPT_PGO_PROFILER_MANAGER_H
#define ECMASCRIPT_PGO_PROFILER_MANAGER_H
#include <memory>
#include "ecmascript/pgo_profiler/pgo_profiler.h"
#include "ecmascript/pgo_profiler/pgo_profiler_loader.h"
#include "ecmascript/pgo_profiler/pgo_profiler_saver.h"
namespace panda::ecmascript {
class PGOProfilerManager {
public:
static PGOProfilerManager *GetInstance()
{
static PGOProfilerManager instance;
return &instance;
}
PGOProfilerManager() = default;
~PGOProfilerManager() = default;
NO_COPY_SEMANTIC(PGOProfilerManager);
NO_MOVE_SEMANTIC(PGOProfilerManager);
void Initialize(const std::string &outDir, uint32_t hotnessThreshold)
{
saver_ = std::make_unique<PGOProfilerSaver>(outDir, hotnessThreshold);
}
void Destroy()
{
saver_->Save();
saver_->Destroy();
saver_.reset();
}
// Factory
PGOProfiler *Build(EcmaVM *vm, bool isEnable)
{
if (isEnable) {
isEnable = InitializeData();
}
return new PGOProfiler(vm, isEnable);
}
void Destroy(PGOProfiler *profiler)
{
if (profiler != nullptr) {
Merge(profiler);
delete profiler;
}
}
void Reset(PGOProfiler *profiler, bool isEnable)
{
if (isEnable) {
isEnable = InitializeData();
}
if (profiler) {
profiler->Reset(isEnable);
}
}
void SamplePandaFileInfo(uint32_t checksum)
{
if (saver_) {
saver_->SamplePandaFileInfo(checksum);
}
}
void Merge(PGOProfiler *profiler)
{
if (saver_) {
saver_->TerminateSaveTask();
saver_->Merge(*profiler->recordInfos_);
}
}
void AsynSave()
{
if (saver_) {
saver_->PostSaveTask();
}
}
bool PUBLIC_API TextToBinary(const std::string &inPath, const std::string &outPath, uint32_t hotnessThreshold)
{
PGOProfilerSaver saver(outPath, hotnessThreshold);
if (!saver.InitializeData()) {
LOG_ECMA(ERROR) << "PGO Profiler saver initialized failed";
return false;
}
bool ret = saver.LoadAPTextFile(inPath);
if (ret) {
saver.Save();
}
saver.Destroy();
return ret;
}
bool PUBLIC_API BinaryToText(const std::string &inPath, const std::string &outPath, uint32_t hotnessThreshold)
{
PGOProfilerLoader loader(inPath, hotnessThreshold);
if (!loader.LoadFull()) {
return false;
}
bool ret = loader.SaveAPTextFile(outPath);
loader.Clear();
return ret;
}
private:
bool InitializeData()
{
if (!saver_) {
return false;
}
return saver_->InitializeData();
}
std::unique_ptr<PGOProfilerSaver> saver_;
};
} // namespace panda::ecmascript
#endif // ECMASCRIPT_PGO_PROFILER_MANAGER_H

View File

@ -0,0 +1,149 @@
/*
* 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_saver.h"
#include "ecmascript/platform/file.h"
namespace panda::ecmascript {
static const std::string PROFILE_FILE_NAME = "/modules.ap";
void PGOProfilerSaver::Destroy()
{
if (!isInitialized_) {
return;
}
PGOProfilerHeader::Destroy(&header_);
pandaFileInfos_.reset();
globalRecordInfos_->Clear();
globalRecordInfos_.reset();
isInitialized_ = false;
}
bool PGOProfilerSaver::InitializeData()
{
if (!isInitialized_) {
if (!RealPath(outDir_, realOutPath_, false)) {
return false;
}
realOutPath_ += PROFILE_FILE_NAME;
LOG_ECMA(INFO) << "Save profiler to file:" << realOutPath_;
PGOProfilerHeader::Build(&header_, PGOProfilerHeader::LastSize());
pandaFileInfos_ = std::make_unique<PGOPandaFileInfos>();
globalRecordInfos_ = std::make_unique<PGORecordDetailInfos>(hotnessThreshold_);
isInitialized_ = true;
}
return true;
}
void PGOProfilerSaver::SamplePandaFileInfo(uint32_t checksum)
{
if (!isInitialized_) {
return;
}
pandaFileInfos_->Sample(checksum);
}
void PGOProfilerSaver::Merge(const PGORecordDetailInfos &recordInfos)
{
if (!isInitialized_) {
return;
}
os::memory::LockHolder lock(mutex_);
globalRecordInfos_->Merge(recordInfos);
}
void PGOProfilerSaver::Save()
{
if (!isInitialized_) {
return;
}
os::memory::LockHolder lock(mutex_);
SaveProfiler();
}
void PGOProfilerSaver::SaveProfiler(const SaveTask *task)
{
std::ofstream fileStream(realOutPath_.c_str());
if (!fileStream.is_open()) {
LOG_ECMA(ERROR) << "The file path(" << realOutPath_ << ") open failure!";
return;
}
pandaFileInfos_->ProcessToBinary(fileStream, header_->GetPandaInfoSection());
globalRecordInfos_->ProcessToBinary(task, fileStream, header_->GetRecordInfoSection());
header_->ProcessToBinary(fileStream);
fileStream.close();
}
void PGOProfilerSaver::TerminateSaveTask()
{
if (!isInitialized_) {
return;
}
Taskpool::GetCurrentTaskpool()->TerminateTask(GLOBAL_TASK_ID, TaskType::PGO_SAVE_TASK);
}
void PGOProfilerSaver::PostSaveTask()
{
if (!isInitialized_) {
return;
}
Taskpool::GetCurrentTaskpool()->PostTask(std::make_unique<SaveTask>(this, GLOBAL_TASK_ID));
}
void PGOProfilerSaver::StartSaveTask(const SaveTask *task)
{
if (task == nullptr) {
return;
}
if (task->IsTerminate()) {
LOG_ECMA(ERROR) << "StartSaveTask: task is already terminate";
return;
}
os::memory::LockHolder lock(mutex_);
SaveProfiler(task);
}
bool PGOProfilerSaver::LoadAPTextFile(const std::string &inPath)
{
if (!isInitialized_) {
return false;
}
std::string realPath;
if (!RealPath(inPath, realPath)) {
return false;
}
std::ifstream fileStream(realPath.c_str());
if (!fileStream.is_open()) {
LOG_ECMA(ERROR) << "The file path(" << realOutPath_ << ") open failure!";
return false;
}
if (!header_->ParseFromText(fileStream)) {
LOG_ECMA(ERROR) << "header format error";
return false;
}
if (!pandaFileInfos_->ParseFromText(fileStream)) {
LOG_ECMA(ERROR) << "panda file info format error";
return false;
}
if (!globalRecordInfos_->ParseFromText(fileStream)) {
LOG_ECMA(ERROR) << "record info format error";
return false;
}
return true;
}
} // namespace panda::ecmascript

View File

@ -0,0 +1,81 @@
/*
* 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.
*/
#ifndef ECMASCRIPT_PGO_PROFILER_SAVER_H
#define ECMASCRIPT_PGO_PROFILER_SAVER_H
#include "ecmascript/pgo_profiler/pgo_profiler_info.h"
#include "macros.h"
namespace panda::ecmascript {
class PGOProfilerSaver {
public:
PGOProfilerSaver(const std::string &outDir, uint32_t hotnessThreshold)
: outDir_(outDir), hotnessThreshold_(hotnessThreshold) {}
NO_COPY_SEMANTIC(PGOProfilerSaver);
NO_MOVE_SEMANTIC(PGOProfilerSaver);
bool PUBLIC_API InitializeData();
void PUBLIC_API Destroy();
void SamplePandaFileInfo(uint32_t checksum);
void Merge(const PGORecordDetailInfos &recordInfos);
void TerminateSaveTask();
void PostSaveTask();
void PUBLIC_API Save();
bool PUBLIC_API LoadAPTextFile(const std::string &inPath);
private:
void StartSaveTask(const SaveTask *task);
void SaveProfiler(const SaveTask *task = nullptr);
bool isInitialized_ {false};
std::string outDir_;
uint32_t hotnessThreshold_ {2};
std::string realOutPath_;
PGOProfilerHeader *header_ {nullptr};
std::unique_ptr<PGOPandaFileInfos> pandaFileInfos_;
std::unique_ptr<PGORecordDetailInfos> globalRecordInfos_;
os::memory::Mutex mutex_;
friend SaveTask;
};
class SaveTask : public Task {
public:
explicit SaveTask(PGOProfilerSaver *saver, int32_t id) : Task(id), saver_(saver) {};
virtual ~SaveTask() override = default;
bool Run([[maybe_unused]] uint32_t threadIndex) override
{
saver_->StartSaveTask(this);
return true;
}
TaskType GetTaskType() const override
{
return TaskType::PGO_SAVE_TASK;
}
NO_COPY_SEMANTIC(SaveTask);
NO_MOVE_SEMANTIC(SaveTask);
private:
PGOProfilerSaver *saver_;
};
} // namespace panda::ecmascript
#endif // ECMASCRIPT_PGO_PROFILER_SAVER_H

View File

@ -0,0 +1,36 @@
# 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.
import("//arkcompiler/ets_runtime/js_runtime_config.gni")
source_set("prof_dump_set") {
sources = [ "main.cpp" ]
public_configs = [
"$js_root:ark_jsruntime_common_config",
"$js_root:ark_jsruntime_public_config",
]
}
ohos_executable("profdump") {
deps = [
":prof_dump_set",
"$ark_root/libpandabase:libarkbase_static",
"$js_root:libark_jsruntime",
]
install_enable = true
part_name = "ets_runtime"
subsystem_name = "arkcompiler"
}

View File

@ -0,0 +1,171 @@
/*
* 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 <getopt.h>
#include "ecmascript/base/string_helper.h"
#include "ecmascript/ecma_macros.h"
#include "ecmascript/log_wrapper.h"
#include "ecmascript/pgo_profiler/pgo_profiler_manager.h"
namespace panda::ecmascript {
static const std::string VERSION = "0.0.0.1";
static const int MIN_PARAM_COUNT = 3;
class Option {
public:
enum class Mode : uint8_t {
VERSION_QUERY,
TO_BINARY,
TO_TEXT,
};
std::string GetProfInPath() const
{
return profInPath_;
}
std::string GetProfOutPath() const
{
return profOutPath_;
}
uint32_t GetHotnessThreshold() const
{
return hotnessThreshold_;
}
Mode GetMode() const
{
return mode_;
}
bool ParseCommand(const int argc, const char **argv)
{
if (argc <= 1) {
return false;
}
const struct option longOptions[] = {
{"text", required_argument, nullptr, 't'},
{"binary", required_argument, nullptr, 'b'},
{"hotness-threshold", required_argument, nullptr, 's'},
{"help", no_argument, nullptr, 'h'},
{"version", no_argument, nullptr, 'v'},
{nullptr, 0, nullptr, 0},
};
const char *optstr = "tbs:hv";
int opt;
while ((opt = getopt_long_only(argc, const_cast<char **>(argv), optstr, longOptions, nullptr)) != -1) {
switch (opt) {
case 't':
mode_ = Mode::TO_TEXT;
break;
case 'b':
mode_ = Mode::TO_BINARY;
break;
case 's':
if (!base::StringHelper::StrToUInt32(optarg, &hotnessThreshold_)) {
LOG_NO_TAG(ERROR) << "hotness-threshold parse failure";
return false;
}
break;
case 'h':
return false;
case 'v':
mode_ = Mode::VERSION_QUERY;
return true;
default:
LOG_NO_TAG(ERROR) << "Invalid option";
return -1;
}
}
if (optind != argc - MIN_PARAM_COUNT + 1) {
return false;
}
profInPath_ = argv[optind];
profOutPath_ = argv[optind + 1];
return true;
}
std::string GetHelper() const
{
const std::string PROF_DUMP_HELP_HEAD_MSG =
"Usage: profdump... SOURCE... DEST... [OPTIONS]\n"
"\n"
"optional arguments:\n";
const std::string PROF_DUMP_HELP_OPTION_MSG =
"-t, --text binary to text.\n"
"-b, --binary text to binary.\n"
"-s, --hotness-threshold set minimum number of calls to filter method. default: 2\n"
"-h, --help display this help and exit\n"
"-v, --version output version information and exit\n";
return PROF_DUMP_HELP_HEAD_MSG + PROF_DUMP_HELP_OPTION_MSG;
}
const std::string GetVersion() const
{
return VERSION;
}
private:
Mode mode_ { Mode::TO_TEXT };
uint32_t hotnessThreshold_ { 2 };
std::string profInPath_;
std::string profOutPath_;
};
int Main(const int argc, const char **argv)
{
Option option;
if (!option.ParseCommand(argc, argv)) {
LOG_NO_TAG(ERROR) << option.GetHelper();
return -1;
}
switch (option.GetMode()) {
case Option::Mode::VERSION_QUERY:
LOG_NO_TAG(ERROR) << "Ver: " << VERSION;
break;
case Option::Mode::TO_TEXT: {
if (PGOProfilerManager::GetInstance()->BinaryToText(option.GetProfInPath(),
option.GetProfOutPath(), option.GetHotnessThreshold())) {
LOG_NO_TAG(ERROR) << "profiler dump to text success!";
} else {
LOG_NO_TAG(ERROR) << "profiler dump to text failed!";
}
break;
}
case Option::Mode::TO_BINARY: {
if (PGOProfilerManager::GetInstance()->TextToBinary(option.GetProfInPath(),
option.GetProfOutPath(), option.GetHotnessThreshold())) {
LOG_NO_TAG(ERROR) << "profiler dump to binary success!";
} else {
LOG_NO_TAG(ERROR) << "profiler dump to binary failed!";
}
break;
}
default:
break;
}
return 0;
}
} // namespace panda::ecmascript
int main(int argc, const char **argv)
{
return panda::ecmascript::Main(argc, argv);
}

View File

@ -15,12 +15,13 @@
#include "gtest/gtest.h"
#include "ecmascript/dfx/pgo_profiler/pgo_profiler_loader.h"
#include "ecmascript/dfx/pgo_profiler/pgo_profiler_manager.h"
#include "ecmascript/ecma_vm.h"
#include "ecmascript/jspandafile/method_literal.h"
#include "ecmascript/js_thread.h"
#include "ecmascript/napi/include/jsnapi.h"
#include "ecmascript/pgo_profiler/pgo_profiler_info.h"
#include "ecmascript/pgo_profiler/pgo_profiler_loader.h"
#include "ecmascript/pgo_profiler/pgo_profiler_manager.h"
#include "ecmascript/tests/test_helper.h"
using namespace panda;
@ -62,12 +63,13 @@ HWTEST_F_L0(PGOProfilerTest, Sample)
vm_->GetPGOProfiler()->Sample(func.GetTaggedType());
JSNApi::DestroyJSVM(vm_);
// Loader
PGOProfilerLoader loader;
ASSERT_TRUE(loader.LoadAndVerify("ark-profiler/modules.ap", 2, checksum));
PGOProfilerLoader loader("ark-profiler/modules.ap", 2);
CString expectRecordName = "test";
#if defined(SUPPORT_ENABLE_ASM_INTERP)
ASSERT_TRUE(loader.LoadAndVerify(checksum));
ASSERT_TRUE(!loader.Match(expectRecordName, EntityId(61)));
#else
ASSERT_TRUE(!loader.LoadAndVerify(checksum));
ASSERT_TRUE(loader.Match(expectRecordName, EntityId(61)));
#endif
unlink("ark-profiler/modules.ap");
@ -108,14 +110,15 @@ HWTEST_F_L0(PGOProfilerTest, Sample1)
JSNApi::DestroyJSVM(vm_);
// Loader
PGOProfilerLoader loader;
ASSERT_TRUE(loader.LoadAndVerify("ark-profiler1/modules.ap", 2, checksum));
PGOProfilerLoader loader("ark-profiler1/modules.ap", 2);
CString expectRecordName = "test";
#if defined(SUPPORT_ENABLE_ASM_INTERP)
ASSERT_TRUE(loader.LoadAndVerify(checksum));
ASSERT_TRUE(loader.Match(expectRecordName, EntityId(70)));
ASSERT_TRUE(loader.Match(expectRecordName, EntityId(80)));
#if defined(SUPPORT_ENABLE_ASM_INTERP)
ASSERT_TRUE(!loader.Match(expectRecordName, EntityId(75)));
#else
ASSERT_TRUE(!loader.LoadAndVerify(checksum));
ASSERT_TRUE(loader.Match(expectRecordName, EntityId(75)));
#endif
unlink("ark-profiler1/modules.ap");
@ -150,13 +153,14 @@ HWTEST_F_L0(PGOProfilerTest, Sample2)
JSNApi::DestroyJSVM(vm_);
// Loader
PGOProfilerLoader loader;
ASSERT_TRUE(loader.LoadAndVerify("ark-profiler2/modules.ap", 2, checksum));
PGOProfilerLoader loader("ark-profiler2/modules.ap", 2);
CString expectRecordName = "test";
CString expectRecordName1 = "test1";
#if defined(SUPPORT_ENABLE_ASM_INTERP)
ASSERT_TRUE(loader.LoadAndVerify(checksum));
ASSERT_TRUE(!loader.Match(expectRecordName, EntityId(61)));
#else
ASSERT_TRUE(!loader.LoadAndVerify(checksum));
ASSERT_TRUE(loader.Match(expectRecordName, EntityId(61)));
#endif
ASSERT_TRUE(loader.Match(expectRecordName1, EntityId(62)));
@ -171,6 +175,8 @@ HWTEST_F_L0(PGOProfilerTest, DisEnableSample)
option.SetEnableProfile(false);
option.SetProfileDir("ark-profiler3/");
vm_ = JSNApi::CreateJSVM(option);
uint32_t checksum = 304293;
PGOProfilerManager::GetInstance()->SamplePandaFileInfo(checksum);
ASSERT_TRUE(vm_ != nullptr) << "Cannot create Runtime";
MethodLiteral *methodLiteral = new MethodLiteral(EntityId(61));
@ -182,9 +188,9 @@ HWTEST_F_L0(PGOProfilerTest, DisEnableSample)
JSNApi::DestroyJSVM(vm_);
// Loader
PGOProfilerLoader loader;
PGOProfilerLoader loader("ark-profiler3/modules.ap", 2);
// path is empty()
ASSERT_TRUE(!loader.Load("ark-profiler3/modules.ap", 2));
ASSERT_TRUE(!loader.LoadAndVerify(checksum));
CString expectRecordName = "test";
ASSERT_TRUE(loader.Match(expectRecordName, EntityId(61)));
rmdir("ark-profiler3/");
@ -223,6 +229,8 @@ HWTEST_F_L0(PGOProfilerTest, PGOProfilerManagerSample)
currentPath[PATH_MAX + 1] = '\0';
option.SetProfileDir(currentPath);
vm_ = JSNApi::CreateJSVM(option);
uint32_t checksum = 304293;
PGOProfilerManager::GetInstance()->SamplePandaFileInfo(checksum);
ASSERT_TRUE(vm_ != nullptr) << "Cannot create Runtime";
JSHandle<JSArray> array = vm_->GetFactory()->NewJSArray();
@ -236,12 +244,13 @@ HWTEST_F_L0(PGOProfilerTest, PGOProfilerManagerSample)
vm_->GetPGOProfiler()->Sample(func.GetTaggedType());
JSNApi::DestroyJSVM(vm_);
PGOProfilerLoader loader;
PGOProfilerLoader loader("", 2);
// path is empty()
ASSERT_TRUE(!loader.Load("", 2));
ASSERT_TRUE(loader.LoadAndVerify(checksum));
// path size greater than PATH_MAX
char path[PATH_MAX + 1] = {'0'};
loader.Load(path, 4);
PGOProfilerLoader loader1(path, 4);
ASSERT_TRUE(!loader1.LoadAndVerify(checksum));
}
HWTEST_F_L0(PGOProfilerTest, PGOProfilerDoubleVM)
@ -252,8 +261,11 @@ HWTEST_F_L0(PGOProfilerTest, PGOProfilerDoubleVM)
// outDir is empty
option.SetProfileDir("ark-profiler5/");
vm_ = JSNApi::CreateJSVM(option);
uint32_t checksum = 304293;
PGOProfilerManager::GetInstance()->SamplePandaFileInfo(checksum);
ASSERT_TRUE(vm_ != nullptr) << "Cannot create Runtime";
auto vm2 = JSNApi::CreateJSVM(option);
PGOProfilerManager::GetInstance()->SamplePandaFileInfo(checksum);
ASSERT_TRUE(vm2 != nullptr) << "Cannot create Runtime";
MethodLiteral *methodLiteral = new MethodLiteral(EntityId(70));
@ -277,17 +289,19 @@ HWTEST_F_L0(PGOProfilerTest, PGOProfilerDoubleVM)
JSNApi::DestroyJSVM(vm2);
JSNApi::DestroyJSVM(vm_);
PGOProfilerLoader loader;
PGOProfilerLoader loader("ark-profiler5/profiler", 2);
mkdir("ark-profiler5/profiler", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
ASSERT_TRUE(!loader.Load("ark-profiler5/profiler", 2));
ASSERT_TRUE(!loader.LoadAndVerify(checksum));
CString expectRecordName = "test";
ASSERT_TRUE(loader.Match(expectRecordName, EntityId(75)));
loader.Load("ark-profiler5/modules.ap", 2);
PGOProfilerLoader loader1("ark-profiler5/modules.ap", 2);
#if defined(SUPPORT_ENABLE_ASM_INTERP)
ASSERT_TRUE(!loader.Match(expectRecordName, EntityId(75)));
ASSERT_TRUE(loader1.LoadAndVerify(checksum));
ASSERT_TRUE(!loader1.Match(expectRecordName, EntityId(75)));
#else
ASSERT_TRUE(loader.Match(expectRecordName, EntityId(75)));
ASSERT_TRUE(!loader1.LoadAndVerify(checksum));
ASSERT_TRUE(loader1.Match(expectRecordName, EntityId(75)));
#endif
unlink("ark-profiler5/modules.ap");
@ -295,23 +309,6 @@ HWTEST_F_L0(PGOProfilerTest, PGOProfilerDoubleVM)
rmdir("ark-profiler5/");
}
HWTEST_F_L0(PGOProfilerTest, DoubleRecordNameFormat)
{
mkdir("ark-profiler7/", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
std::ofstream file("ark-profiler7/modules.ap");
std::string result = "recordName:[123/223/hello]\n";
file.write(result.c_str(), result.size());
result = "recordName:[1232/3/hello]\n";
file.write(result.c_str(), result.size());
file.close();
PGOProfilerLoader loader;
loader.Load("ark-profiler7/modules.ap", 2);
unlink("ark-profiler7/modules.ap");
rmdir("ark-profiler7");
}
HWTEST_F_L0(PGOProfilerTest, PGOProfilerLoaderNoHotMethod)
{
mkdir("ark-profiler8/", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
@ -330,12 +327,13 @@ HWTEST_F_L0(PGOProfilerTest, PGOProfilerLoaderNoHotMethod)
vm_->GetPGOProfiler()->Sample(func.GetTaggedType());
JSNApi::DestroyJSVM(vm_);
PGOProfilerLoader loader;
ASSERT_TRUE(loader.LoadAndVerify("ark-profiler8/modules.ap", 2, checksum));
PGOProfilerLoader loader("ark-profiler8/modules.ap", 2);
CString expectRecordName = "test";
#if defined(SUPPORT_ENABLE_ASM_INTERP)
ASSERT_TRUE(loader.LoadAndVerify(checksum));
ASSERT_TRUE(!loader.Match(expectRecordName, EntityId(61)));
#else
ASSERT_TRUE(!loader.LoadAndVerify(checksum));
ASSERT_TRUE(loader.Match(expectRecordName, EntityId(61)));
#endif
@ -353,11 +351,6 @@ HWTEST_F_L0(PGOProfilerTest, PGOProfilerPostTask)
uint32_t checksum = 304293;
PGOProfilerManager::GetInstance()->SamplePandaFileInfo(checksum);
for (int i = 0; i < 5; i++) {
PGOProfilerManager::GetInstance()->PostSaveTask();
PGOProfilerManager::GetInstance()->TerminateSaveTask();
}
JSHandle<JSTaggedValue> recordName(vm_->GetFactory()->NewFromStdString("test"));
for (int i = 61; i < 91; i++) {
MethodLiteral *methodLiteral = new MethodLiteral(EntityId(i));
@ -372,8 +365,12 @@ HWTEST_F_L0(PGOProfilerTest, PGOProfilerPostTask)
JSNApi::DestroyJSVM(vm_);
PGOProfilerLoader loader;
ASSERT_TRUE(loader.LoadAndVerify("ark-profiler9/modules.ap", 2, checksum));
PGOProfilerLoader loader("ark-profiler9/modules.ap", 2);
#if defined(SUPPORT_ENABLE_ASM_INTERP)
ASSERT_TRUE(loader.LoadAndVerify(checksum));
#else
ASSERT_TRUE(!loader.LoadAndVerify(checksum));
#endif
CString expectRecordName = "test";
for (int i = 61; i < 91; i++) {
if (i % 3 == 0) {
@ -390,4 +387,55 @@ HWTEST_F_L0(PGOProfilerTest, PGOProfilerPostTask)
unlink("ark-profiler9/modules.ap");
rmdir("ark-profiler9/");
}
HWTEST_F_L0(PGOProfilerTest, BinaryToText)
{
mkdir("ark-profiler7/", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
std::ofstream file("ark-profiler7/modules.ap");
PGOProfilerHeader *header = nullptr;
PGOProfilerHeader::Build(&header, PGOProfilerHeader::LastSize());
std::unique_ptr<PGOPandaFileInfos> pandaFileInfos = std::make_unique<PGOPandaFileInfos>();
std::unique_ptr<PGORecordDetailInfos> recordInfos = std::make_unique<PGORecordDetailInfos>(2);
pandaFileInfos->Sample(0x34556738);
ASSERT_TRUE(recordInfos->AddMethod("test", EntityId(23), "test", SampleMode::CALL_MODE));
ASSERT_FALSE(recordInfos->AddMethod("test", EntityId(23), "test", SampleMode::CALL_MODE));
ASSERT_FALSE(recordInfos->AddMethod("test", EntityId(23), "test", SampleMode::CALL_MODE));
pandaFileInfos->ProcessToBinary(file, header->GetPandaInfoSection());
recordInfos->ProcessToBinary(nullptr, file, header->GetRecordInfoSection());
header->ProcessToBinary(file);
file.close();
ASSERT_TRUE(PGOProfilerManager::GetInstance()->BinaryToText(
"ark-profiler7/modules.ap", "ark-profiler7/modules.text", 2));
unlink("ark-profiler7/modules.ap");
unlink("ark-profiler7/modules.text");
rmdir("ark-profiler7");
}
HWTEST_F_L0(PGOProfilerTest, TextToBinary)
{
mkdir("ark-profiler10/", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
std::ofstream file("ark-profiler10/modules.text");
std::string result = "Profiler Version: 0.0.0.1\n";
file.write(result.c_str(), result.size());
result = "\nPanda file sumcheck list: [ 413775942 ]\n";
file.write(result.c_str(), result.size());
result = "\nrecordName: [ 1232/3/CALL_MODE/hello, 234/100/HOTNESS_MODE/h#ello1 ]\n";
file.write(result.c_str(), result.size());
file.close();
ASSERT_TRUE(PGOProfilerManager::GetInstance()->TextToBinary("ark-profiler10/modules.text", "ark-profiler10/", 2));
PGOProfilerLoader loader("ark-profiler10/modules.ap", 2);
ASSERT_TRUE(loader.LoadAndVerify(413775942));
unlink("ark-profiler10/modules.ap");
unlink("ark-profiler10/modules.text");
rmdir("ark-profiler10");
}
} // namespace panda::test

View File

@ -36,6 +36,18 @@ using fd_t = HANDLE;
#define FILE_RDONLY GENERIC_READ
#define FILE_WRONLY GENERIC_WRITE
#define FILE_RDWR (GENERIC_READ | GENERIC_WRITE)
#ifdef ERROR
#undef ERROR
#endif
#ifdef VOID
#undef VOID
#endif
#ifdef CONST
#undef CONST
#endif
#else
using fd_t = int;
#define INVALID_FD (-1)
@ -50,13 +62,11 @@ using fd_t = int;
std::string GetFileDelimiter();
bool RealPath(const std::string &path, std::string &realPath, bool readOnly = true);
int64_t GetFileSizeByFd(fd_t fd);
fd_t Open(const char *file, int flag);
void DPrintf(fd_t fd, const std::string &buffer);
void Close(fd_t fd);
void FSync(fd_t fd);
void *FileMmap(fd_t fd, uint64_t size, uint64_t offset, fd_t *extra);
int FileUnMap(void *addr, uint64_t size, fd_t *extra);
MemMap FileMap(const char *fileName, int flag, int prot, int64_t offset = 0);
int FileUnMap(MemMap addr);
JSHandle<EcmaString> ResolveFilenameFromNative(JSThread *thread, JSTaggedValue dirname,
JSTaggedValue request);
} // namespace panda::ecmascript

View File

@ -23,11 +23,18 @@
namespace panda::ecmascript {
class MemMap {
public:
MemMap() : mem_(nullptr), size_(0) {}
MemMap() : originAddr_(nullptr), mem_(nullptr), size_(0) {}
MemMap(void *mem, size_t size) : originAddr_(mem), mem_(mem), size_(size) {};
MemMap(void *originAddr, void *mem, size_t size) : originAddr_(originAddr), mem_(mem), size_(size) {};
~MemMap() = default;
void Reset()
{
originAddr_ = nullptr;
mem_ = nullptr;
size_ = 0;
}
inline void *GetMem() const
{
return mem_;

View File

@ -44,18 +44,13 @@ bool RealPath(const std::string &path, std::string &realPath, bool readOnly)
realPath = path;
return true;
}
LOG_ECMA(ERROR) << "File path" << path << " realpath failure";
LOG_ECMA(ERROR) << "File path:" << path << " realpath failure";
return false;
}
realPath = std::string(buffer);
return true;
}
fd_t Open(const char *file, int flag)
{
return open(file, flag);
}
void DPrintf(fd_t fd, const std::string &buffer)
{
int ret = dprintf(fd, "%s", buffer.c_str());
@ -77,23 +72,29 @@ void Close(fd_t fd)
close(fd);
}
int64_t GetFileSizeByFd(fd_t fd)
MemMap FileMap(const char *fileName, int flag, int prot, int64_t offset)
{
return lseek(fd, 0, SEEK_END);
}
void *FileMmap(fd_t fd, uint64_t size, uint64_t offset, [[maybe_unused]] fd_t *extra)
{
void *addr = mmap(nullptr, size, PAGE_PROT_READWRITE, MAP_PRIVATE, fd, offset);
if (reinterpret_cast<intptr_t>(addr) == -1) {
LOG_ECMA(FATAL) << "mmap failed with error code:" << errno;
fd_t fd = open(fileName, flag);
if (fd == INVALID_FD) {
LOG_ECMA(ERROR) << fileName << " file open failed";
return MemMap();
}
return addr;
size_t size = lseek(fd, 0, SEEK_END);
if (size <= 0) {
close(fd);
LOG_ECMA(ERROR) << fileName << " file is empty";
return MemMap();
}
void *addr = mmap(nullptr, size, prot, MAP_PRIVATE, fd, offset);
close(fd);
return MemMap(addr, size);
}
int FileUnMap(void *addr, uint64_t size, [[maybe_unused]] fd_t *extra)
int FileUnMap(MemMap addr)
{
return munmap(addr, size);
return munmap(addr.GetOriginAddr(), addr.GetSize());
}
JSHandle<EcmaString> ResolveFilenameFromNative(JSThread *thread, JSTaggedValue dirname,

View File

@ -15,9 +15,11 @@
#include "ecmascript/platform/file.h"
#include <windef.h>
#include <winbase.h>
#include <winnt.h>
#include <climits>
#include <fileapi.h>
#include <shlwapi.h>
#ifdef ERROR
#undef ERROR
@ -59,13 +61,6 @@ bool RealPath(const std::string &path, std::string &realPath, [[maybe_unused]] b
return true;
}
// use CreateFile instead of _open to work with CreateFileMapping
fd_t Open(const char *file, int flag)
{
fd_t fd = CreateFile(file, flag, 0, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
return fd;
}
void DPrintf(fd_t fd, const std::string &buffer)
{
LOG_ECMA(DEBUG) << "Unsupport dprintf fd(" << fd << ") in windows, buffer:" << buffer;
@ -81,39 +76,47 @@ void Close(fd_t fd)
CloseHandle(fd);
}
int64_t GetFileSizeByFd(fd_t fd)
MemMap FileMap(const char *fileName, int flag, int prot, int64_t offset)
{
LARGE_INTEGER size;
if (!GetFileSizeEx(fd, &size)) {
LOG_ECMA(ERROR) << "GetFileSize failed with error code:" << GetLastError();
return -1;
fd_t fd = CreateFile(fileName, flag, 0, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
if (fd == INVALID_FD) {
LOG_ECMA(ERROR) << fileName << " file open failed";
return MemMap();
}
return size.QuadPart;
}
void *FileMmap(fd_t fd, uint64_t size, uint64_t offset, fd_t *extra)
{
// 32: high 32 bits
*extra = CreateFileMapping(fd, NULL, PAGE_PROT_READ, size >> 32, size & 0xffffffff, nullptr);
if (*extra == nullptr) {
LOG_ECMA(ERROR) << "CreateFileMapping failed with error code:" << GetLastError();
return nullptr;
LARGE_INTEGER fileSize;
if (!GetFileSizeEx(fd, &fileSize)) {
CloseHandle(fd);
LOG_ECMA(ERROR) << "GetFileSize failed with error code:" << GetLastError();
return MemMap();
}
auto size = fileSize.QuadPart;
if (size <= 0) {
CloseHandle(fd);
LOG_ECMA(ERROR) << fileName << " file is empty";
return MemMap();
}
// 32: high 32 bits
void *addr = MapViewOfFile(*extra, FILE_MAP_READ, offset >> 32, offset & 0xffffffff, size);
fd_t extra = CreateFileMapping(fd, NULL, prot, size >> 32, size & 0xffffffff, nullptr);
if (extra == nullptr) {
CloseHandle(fd);
LOG_ECMA(ERROR) << "CreateFileMapping failed with error code:" << GetLastError();
return MemMap();
}
int accessor = (prot == PAGE_PROT_READ) ? FILE_MAP_READ : FILE_MAP_WRITE;
void *addr = MapViewOfFile(extra, accessor, offset >> 32, offset & 0xffffffff, size);
CloseHandle(extra);
CloseHandle(fd);
if (addr == nullptr) {
LOG_ECMA(ERROR) << "MapViewOfFile failed with error code:" << GetLastError();
CloseHandle(*extra);
}
return addr;
return MemMap(addr, size);
}
int FileUnMap(void *addr, [[maybe_unused]] uint64_t size, fd_t *extra)
int FileUnMap(MemMap addr)
{
if (UnmapViewOfFile(addr) == 0) {
return FILE_FAILED;
}
if (CloseHandle(*extra) == 0) {
if (UnmapViewOfFile(addr.GetOriginAddr()) == 0) {
return FILE_FAILED;
}
return FILE_SUCCESS;

View File

@ -139,35 +139,21 @@ const JSPandaFile *Snapshot::Deserialize(SnapshotType type, const CString &snaps
LOG_FULL(FATAL) << "snapshot file path error";
UNREACHABLE();
}
fd_t fd = Open(realPath.c_str(), FILE_RDONLY); // NOLINT(cppcoreguidelines-pro-type-vararg)
if (UNLIKELY(fd == INVALID_FD)) {
LOG_FULL(FATAL) << "open file failed";
UNREACHABLE();
}
int64_t fileSize = GetFileSizeByFd(fd);
if (fileSize == -1) {
Close(fd);
LOG_FULL(FATAL) << "GetFileSize failed";
UNREACHABLE();
}
SnapshotProcessor processor(vm_, snapshotFile);
if (isBuiltins) {
processor.SetBuiltinsDeserializeStart();
}
fd_t extra = INVALID_FD;
void *addr = FileMmap(fd, fileSize, 0, &extra);
if (addr == nullptr) {
Close(fd);
MemMap fileMap = FileMap(realPath.c_str(), FILE_RDONLY, PAGE_PROT_READWRITE);
if (fileMap.GetOriginAddr() == nullptr) {
LOG_FULL(FATAL) << "file mmap failed";
UNREACHABLE();
}
auto readFile = ToUintPtr(addr);
auto readFile = ToUintPtr(fileMap.GetOriginAddr());
auto hdr = *ToNativePtr<const SnapShotHeader>(readFile);
if (!hdr.Verify()) {
FileUnMap(addr, fileSize, &extra);
Close(fd);
FileUnMap(fileMap);
LOG_FULL(FATAL) << "file verify failed";
UNREACHABLE();
}
@ -179,15 +165,14 @@ const JSPandaFile *Snapshot::Deserialize(SnapshotType type, const CString &snaps
uintptr_t stringEnd = stringBegin + hdr.stringSize;
processor.DeserializeString(stringBegin, stringEnd);
FileUnMap(addr, hdr.pandaFileBegin, &extra);
FileUnMap(MemMap(fileMap.GetOriginAddr(), hdr.pandaFileBegin));
const JSPandaFile *jsPandaFile = nullptr;
if (static_cast<uint32_t>(fileSize) > hdr.pandaFileBegin) {
if (static_cast<uint32_t>(fileMap.GetSize()) > hdr.pandaFileBegin) {
uintptr_t pandaFileMem = readFile + hdr.pandaFileBegin;
auto pf = panda_file::File::OpenFromMemory(os::mem::ConstBytePtr(ToNativePtr<std::byte>(pandaFileMem),
static_cast<uint32_t>(fileSize) - hdr.pandaFileBegin, os::mem::MmapDeleter));
static_cast<uint32_t>(fileMap.GetSize()) - hdr.pandaFileBegin, os::mem::MmapDeleter));
jsPandaFile = JSPandaFileManager::GetInstance()->NewJSPandaFile(pf.release(), "");
}
Close(fd);
// relocate object field
processor.Relocate(type, jsPandaFile, hdr.rootObjectSize);
LOG_COMPILER(INFO) << "loaded ai file: " << snapshotFile.c_str();

View File

@ -23,7 +23,6 @@
#include "ecmascript/compiler/ecma_opcode_des.h"
#include "ecmascript/compiler/rt_call_signature.h"
#include "ecmascript/deoptimizer/deoptimizer.h"
#include "ecmascript/dfx/pgo_profiler/pgo_profiler_manager.h"
#include "ecmascript/dfx/vmstat/opt_code_profiler.h"
#include "ecmascript/ecma_macros.h"
#include "ecmascript/ecma_vm.h"
@ -46,6 +45,7 @@
#include "ecmascript/mem/space-inl.h"
#include "ecmascript/message_string.h"
#include "ecmascript/object_factory.h"
#include "ecmascript/pgo_profiler/pgo_profiler.h"
#include "ecmascript/tagged_dictionary.h"
#include "ecmascript/tagged_node.h"
#include "ecmascript/ts_types/ts_manager.h"

View File

@ -37,12 +37,12 @@ public:
NO_COPY_SEMANTIC(Task);
NO_MOVE_SEMANTIC(Task);
virtual TaskType GetTaskType()
virtual TaskType GetTaskType() const
{
return TaskType::ALL;
}
int32_t GetId()
int32_t GetId() const
{
return id_;
}
@ -52,7 +52,7 @@ public:
terminate_ = true;
}
bool IsTerminate()
bool IsTerminate() const
{
return terminate_;
}

View File

@ -67,10 +67,15 @@
panda::ecmascript::JSRuntimeOptions::*;
panda::ecmascript::Taskpool::*;
panda::ecmascript::JSThread::*;
panda::ecmascript::Chunk::*;
panda::ecmascript::PGOProfilerLoader::*;
panda::ecmascript::PGOProfilerSaver::*;
panda::ecmascript::NativeAreaAllocator::*;
panda::os::thread::*;
panda::panda_file::File::MAGIC;
panda::os::unix::memory::*;
panda::ecmascript::JSHClass::*;
};
local:
*;