mirror of
https://gitee.com/openharmony/arkcompiler_ets_runtime
synced 2024-10-06 23:54:03 +00:00
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:
parent
b2ae7019f4
commit
47134bfb20
19
BUILD.gn
19
BUILD.gn
@ -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",
|
||||
|
@ -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)
|
||||
{
|
||||
@ -142,4 +165,4 @@ inline constexpr uint32_t BitNumbers()
|
||||
return sizeof(T) * BIT_NUMBER_OF_CHAR;
|
||||
}
|
||||
} // panda::ecmascript::base
|
||||
#endif
|
||||
#endif
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
if (!jsPandaFile_->HasTSTypes(recordName)) {
|
||||
continue;
|
||||
}
|
||||
auto methodSet = pgoIndex->second;
|
||||
std::unordered_set<EntityId> newMethodSet;
|
||||
uint32_t mainMethodOffset = jsPandaFile_->GetMainMethodIndex(recordName);
|
||||
SearchForCompilation(methodSet, newMethodSet, mainMethodOffset, false);
|
||||
pfLoader_.UpdateProfile(recordName, newMethodSet);
|
||||
}
|
||||
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)) {
|
||||
return newMethodIds;
|
||||
}
|
||||
uint32_t mainMethodOffset = jsPandaFile_->GetMainMethodIndex(recordName);
|
||||
SearchForCompilation(oldIds, newMethodIds, mainMethodOffset, false);
|
||||
return newMethodIds;
|
||||
};
|
||||
pfLoader_.Update(dfs);
|
||||
}
|
||||
|
||||
void CompilationDriver::InitializeCompileQueue()
|
||||
@ -52,4 +49,4 @@ bool CompilationDriver::FilterMethod(const CString &recordName, const MethodLite
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} // namespace panda::ecmascript::kungfu
|
||||
} // namespace panda::ecmascript::kungfu
|
||||
|
@ -43,10 +43,17 @@ public:
|
||||
}
|
||||
// update profile and update compile queue
|
||||
std::unordered_set<EntityId> fullResolvedMethodSet;
|
||||
std::unordered_set<EntityId> currentResolvedMethodSet {resolvedMethod};
|
||||
uint32_t mainMethodOffset = jsPandaFile_->GetMainMethodIndex(recordName);
|
||||
SearchForCompilation(currentResolvedMethodSet, fullResolvedMethodSet, mainMethodOffset, true);
|
||||
pfLoader_.UpdateProfile(recordName, 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);
|
||||
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();
|
||||
@ -173,4 +180,4 @@ private:
|
||||
std::queue<uint32_t> compileQueue_ {};
|
||||
};
|
||||
} // namespace panda::ecmascript::kungfu
|
||||
#endif // ECMASCRIPT_COMPILER_COMPILATION_DRIVER_H
|
||||
#endif // ECMASCRIPT_COMPILER_COMPILATION_DRIVER_H
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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_;
|
||||
};
|
||||
}
|
||||
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
48
ecmascript/pgo_profiler/pgo_profiler.cpp
Normal file
48
ecmascript/pgo_profiler/pgo_profiler.cpp
Normal 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
|
65
ecmascript/pgo_profiler/pgo_profiler.h
Normal file
65
ecmascript/pgo_profiler/pgo_profiler.h
Normal 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
|
471
ecmascript/pgo_profiler/pgo_profiler_info.cpp
Normal file
471
ecmascript/pgo_profiler/pgo_profiler_info.cpp
Normal 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
|
485
ecmascript/pgo_profiler/pgo_profiler_info.h
Normal file
485
ecmascript/pgo_profiler/pgo_profiler_info.h
Normal 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 *>(§ionInfos_) + 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
|
177
ecmascript/pgo_profiler/pgo_profiler_loader.cpp
Normal file
177
ecmascript/pgo_profiler/pgo_profiler_loader.cpp
Normal 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
|
83
ecmascript/pgo_profiler/pgo_profiler_loader.h
Normal file
83
ecmascript/pgo_profiler/pgo_profiler_loader.h
Normal 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
|
139
ecmascript/pgo_profiler/pgo_profiler_manager.h
Normal file
139
ecmascript/pgo_profiler/pgo_profiler_manager.h
Normal 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
|
149
ecmascript/pgo_profiler/pgo_profiler_saver.cpp
Normal file
149
ecmascript/pgo_profiler/pgo_profiler_saver.cpp
Normal 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
|
81
ecmascript/pgo_profiler/pgo_profiler_saver.h
Normal file
81
ecmascript/pgo_profiler/pgo_profiler_saver.h
Normal 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
|
36
ecmascript/pgo_profiler/prof_dump/BUILD.gn
Normal file
36
ecmascript/pgo_profiler/prof_dump/BUILD.gn
Normal 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"
|
||||
}
|
171
ecmascript/pgo_profiler/prof_dump/main.cpp
Normal file
171
ecmascript/pgo_profiler/prof_dump/main.cpp
Normal 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);
|
||||
}
|
@ -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
|
@ -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
|
||||
|
@ -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_;
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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"
|
||||
|
@ -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_;
|
||||
}
|
||||
|
@ -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:
|
||||
*;
|
||||
|
Loading…
Reference in New Issue
Block a user