!4381 Support Merge Ap for profdump tool and aot compiler

Merge pull request !4381 from hzzhouzebin/MergeAp
This commit is contained in:
openharmony_ci 2023-07-10 08:08:43 +00:00 committed by Gitee
commit f8240a77bb
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
14 changed files with 430 additions and 84 deletions

View File

@ -742,6 +742,7 @@ ecma_source = [
"ecmascript/pgo_profiler/pgo_profiler_encoder.cpp",
"ecmascript/pgo_profiler/pgo_profiler_info.cpp",
"ecmascript/pgo_profiler/pgo_profiler_layout.cpp",
"ecmascript/pgo_profiler/pgo_profiler_manager.cpp",
"ecmascript/stackmap/ark_stackmap_builder.cpp",
"ecmascript/stackmap/ark_stackmap_parser.cpp",
"ecmascript/stackmap/llvm_stackmap_parser.cpp",

View File

@ -81,13 +81,18 @@ public:
return version_ >= expectVersion;
}
VersionType GetVersion() const
{
return version_;
}
protected:
explicit FileHeaderBase(const VersionType &lastVersion) : magic_(MAGIC), version_(lastVersion) {}
static bool VerifyVersion(const char *fileDesc, const VersionType &currVersion, const VersionType &lastVersion,
bool strictMatch)
{
bool matched = strictMatch ? currVersion == lastVersion : currVersion <= lastVersion;
bool matched = strictMatch ? (currVersion == lastVersion) : (currVersion <= lastVersion);
if (!matched) {
LOG_HOST_TOOL_ERROR << fileDesc << " version error, expected version should be "
<< (strictMatch ? "equal to " : "less or equal than ") << ConvToStr(lastVersion)

View File

@ -19,6 +19,7 @@
#include "ecmascript/ecma_handle_scope.h"
#include "ecmascript/jspandafile/js_pandafile_manager.h"
#include "ecmascript/jspandafile/panda_file_translator.h"
#include "ecmascript/pgo_profiler/pgo_profiler_manager.h"
#include "ecmascript/snapshot/mem/snapshot.h"
#include "ecmascript/ts_types/ts_manager.h"
@ -39,7 +40,7 @@ bool PassManager::Compile(JSPandaFile *jsPandaFile, const std::string &fileName,
return false;
}
if (!profilerDecoder_.LoadAndVerify(jsPandaFile->GetChecksum())) {
if (!PGOProfilerManager::MergeApFiles(jsPandaFile->GetChecksum(), profilerDecoder_)) {
LOG_COMPILER(ERROR) << "Load and verify profiler failure";
return false;
}

View File

@ -21,6 +21,7 @@
#include "ecmascript/compiler/file_generators.h"
#include "ecmascript/ecma_vm.h"
#include "ecmascript/pgo_profiler/pgo_profiler_decoder.h"
#include "ecmascript/pgo_profiler/pgo_profiler_manager.h"
#include "ecmascript/ts_types/ts_manager.h"
namespace panda::ecmascript::kungfu {
@ -166,7 +167,7 @@ private: \
class PassManager {
public:
PassManager(EcmaVM* vm, std::string entry, std::string &triple, size_t optLevel, size_t relocMode,
PassManager(EcmaVM* vm, std::string &entry, std::string &triple, size_t optLevel, size_t relocMode,
CompilerLog *log, AotMethodLogList *logList, size_t maxAotMethodSize, size_t maxMethodsInModule,
const std::string &profIn, uint32_t hotnessThreshold, PassOptions *passOptions)
: vm_(vm), entry_(entry), triple_(triple), optLevel_(optLevel), relocMode_(relocMode), log_(log),

View File

@ -15,6 +15,7 @@
#include "ecmascript/pgo_profiler/pgo_profiler_decoder.h"
#include "ecmascript/base/file_header.h"
#include "ecmascript/jspandafile/method_literal.h"
#include "ecmascript/log_wrapper.h"
#include "ecmascript/pgo_profiler/pgo_profiler_info.h"
@ -80,7 +81,8 @@ bool PGOProfilerDecoder::LoadFull()
if (isLoaded_) {
Clear();
}
if (!LoadAPBinaryFile()) {
// profiler dump tools may write data to memory when merge ap files.
if (!LoadAPBinaryFile(PAGE_PROT_READWRITE)) {
return false;
}
void *addr = fileMapAddr_.GetOriginAddr();
@ -123,7 +125,7 @@ bool PGOProfilerDecoder::SaveAPTextFile(const std::string &outPath)
return true;
}
bool PGOProfilerDecoder::LoadAPBinaryFile()
bool PGOProfilerDecoder::LoadAPBinaryFile(int prot)
{
std::string realPath;
if (!RealPath(inPath_, realPath)) {
@ -136,7 +138,7 @@ bool PGOProfilerDecoder::LoadAPBinaryFile()
return false;
}
LOG_ECMA(INFO) << "Load profiler from file:" << realPath;
fileMapAddr_ = FileMap(realPath.c_str(), FILE_RDONLY, PAGE_PROT_READ);
fileMapAddr_ = FileMap(realPath.c_str(), FILE_RDONLY, prot);
if (fileMapAddr_.GetOriginAddr() == nullptr) {
LOG_ECMA(ERROR) << "File mmap failed";
return false;
@ -197,4 +199,34 @@ void PGOProfilerDecoder::GetMismatchResult(uint32_t &totalMethodCount, uint32_t
}
return recordSimpleInfos_->GetMismatchResult(totalMethodCount, mismatchMethodCount, mismatchMethodSet);
}
bool PGOProfilerDecoder::InitMergeData()
{
ASSERT(!isLoaded_);
if (!recordSimpleInfos_) {
recordSimpleInfos_ = std::make_unique<PGORecordSimpleInfos>(hotnessThreshold_);
}
if (!header_) {
// For merge scene, we only care about the ap capability which is in the version field.
PGOProfilerHeader::Build(&header_, sizeof(PGOProfilerHeader));
memset_s(header_, sizeof(PGOProfilerHeader), 0, sizeof(PGOProfilerHeader));
}
isLoaded_ = true;
isVerifySuccess_ = true;
return true;
}
void PGOProfilerDecoder::Merge(const PGOProfilerDecoder &decoder)
{
if (!isLoaded_ || !isVerifySuccess_) {
return;
}
// For merge scene, we chose the highest version from input ap files
if (!(header_->CompatibleVerify(decoder.header_->GetVersion()))) {
// For merge scene, we only care about the ap capability which is in the version field.
memcpy_s(header_, sizeof(base::FileHeaderBase), decoder.header_, sizeof(base::FileHeaderBase));
}
pandaFileInfos_.Merge(decoder.GetPandaFileInfos());
recordSimpleInfos_->Merge(decoder.GetRecordSimpleInfos());
}
} // namespace panda::ecmascript

View File

@ -43,6 +43,20 @@ public:
bool PUBLIC_API SaveAPTextFile(const std::string &outPath);
void Merge(const PGOProfilerDecoder &decoder);
bool InitMergeData();
const std::string& GetInPath() const
{
return inPath_;
}
uint32_t GetHotnessThreshold() const
{
return hotnessThreshold_;
}
template <typename Callback>
void Update(Callback callback)
{
@ -102,11 +116,26 @@ public:
return isLoaded_;
}
PGORecordDetailInfos &GetRecordDetailInfos() const
{
return *recordDetailInfos_;
}
PGORecordSimpleInfos &GetRecordSimpleInfos() const
{
return *recordSimpleInfos_;
}
const PGOPandaFileInfos &GetPandaFileInfos() const
{
return pandaFileInfos_;
}
private:
bool Load();
bool Verify(uint32_t checksum);
bool LoadAPBinaryFile();
bool LoadAPBinaryFile(int prot = PAGE_PROT_READ);
void UnLoadAPBinaryFile();
bool isLoaded_ {false};

View File

@ -75,6 +75,17 @@ void PGOProfilerEncoder::Merge(const PGORecordDetailInfos &recordInfos)
globalRecordInfos_->Merge(recordInfos);
}
void PGOProfilerEncoder::Merge(const PGOPandaFileInfos &pandaFileInfos)
{
return pandaFileInfos_->Merge(pandaFileInfos);
}
bool PGOProfilerEncoder::VerifyPandaFileMatched(const PGOPandaFileInfos &pandaFileInfos, const std::string &base,
const std::string &incoming) const
{
return pandaFileInfos_->VerifyChecksum(pandaFileInfos, base, incoming);
}
bool PGOProfilerEncoder::Save()
{
if (!isInitialized_) {

View File

@ -41,6 +41,9 @@ public:
void SamplePandaFileInfo(uint32_t checksum);
void Merge(const PGORecordDetailInfos &recordInfos);
void Merge(const PGOPandaFileInfos &pandaFileInfos);
bool VerifyPandaFileMatched(const PGOPandaFileInfos &pandaFileInfos, const std::string &base,
const std::string &incoming) const;
void TerminateSaveTask();
void PostSaveTask();

View File

@ -15,6 +15,7 @@
#include "ecmascript/pgo_profiler/pgo_profiler_info.h"
#include <cstdint>
#include <fstream>
#include <iomanip>
#include "ecmascript/base/bit_helper.h"
@ -180,7 +181,7 @@ 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));
fileInfos_.emplace(*base::ReadBufferInSize<FileInfo>(&addr));
}
LOG_ECMA(DEBUG) << "Profiler panda file count:" << info->number_;
}
@ -188,13 +189,34 @@ void PGOPandaFileInfos::ParseFromBinary(void *buffer, SectionInfo *const info)
void PGOPandaFileInfos::ProcessToBinary(std::fstream &fileStream, SectionInfo *info) const
{
fileStream.seekp(info->offset_);
info->number_ = pandaFileInfos_.size();
for (auto localInfo : pandaFileInfos_) {
info->number_ = fileInfos_.size();
for (auto localInfo : fileInfos_) {
fileStream.write(reinterpret_cast<char *>(&localInfo), localInfo.Size());
}
info->size_ = static_cast<uint32_t>(fileStream.tellp()) - info->offset_;
}
void PGOPandaFileInfos::Merge(const PGOPandaFileInfos &pandaFileInfos)
{
for (const auto &info : pandaFileInfos.fileInfos_) {
fileInfos_.emplace(info.GetChecksum());
}
}
bool PGOPandaFileInfos::VerifyChecksum(const PGOPandaFileInfos &pandaFileInfos, const std::string &base,
const std::string &incoming) const
{
std::set<FileInfo> unionChecksum;
set_union(fileInfos_.begin(), fileInfos_.end(), pandaFileInfos.fileInfos_.begin(), pandaFileInfos.fileInfos_.end(),
inserter(unionChecksum, unionChecksum.begin()));
if (!fileInfos_.empty() && unionChecksum.empty()) {
LOG_ECMA(ERROR) << "First AP file(" << base << ") and the incoming file(" << incoming
<< ") do not come from the same abc file, skip merge the incoming file.";
return false;
}
return true;
}
bool PGOPandaFileInfos::ParseFromText(std::ifstream &stream)
{
std::string pandaFileInfo;
@ -227,7 +249,7 @@ void PGOPandaFileInfos::ProcessToText(std::ofstream &stream) const
{
std::string pandaFileInfo = NEW_LINE + PANDA_FILE_INFO_HEADER;
bool isFirst = true;
for (auto &info : pandaFileInfos_) {
for (auto &info : fileInfos_) {
if (!isFirst) {
pandaFileInfo += BLOCK_SEPARATOR + SPACE;
} else {
@ -242,7 +264,7 @@ void PGOPandaFileInfos::ProcessToText(std::ofstream &stream) const
bool PGOPandaFileInfos::Checksum(uint32_t checksum) const
{
if (pandaFileInfos_.find(checksum) == pandaFileInfos_.end()) {
if (fileInfos_.find(checksum) == fileInfos_.end()) {
LOG_ECMA(ERROR) << "Checksum verification failed. Please ensure that the .abc and .ap match.";
return false;
}
@ -879,13 +901,14 @@ bool PGOMethodIdSet::ParseFromBinary(uint32_t threshold, void **buffer, PGOProfi
}
continue;
}
methodInfoMap_.emplace(info->GetMethodName(), info->GetMethodId());
auto methodIter = methodInfoMap_.find(info->GetMethodName());
ASSERT(methodIter != methodInfoMap_.end());
auto &methodInfo = methodIter->second;
uint32_t checksum = 0;
if (header->SupportMethodChecksum()) {
methodInfo.GetConsistencyInfo().SetChecksum(base::ReadBuffer<uint32_t>(buffer, sizeof(uint32_t)));
checksum = base::ReadBuffer<uint32_t>(buffer, sizeof(uint32_t));
}
auto ret = methodInfoMap_.emplace(info->GetMethodName(), chunk_);
ASSERT(ret.second);
auto methodNameSetIter = ret.first;
auto &methodInfo = methodNameSetIter->second.GetOrCreateMethodInfo(checksum, 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();
@ -902,15 +925,39 @@ void PGOMethodIdSet::GetMismatchResult(const CString &recordName, uint32_t &tota
std::set<std::pair<std::string, CString>> &mismatchMethodSet) const
{
totalMethodCount += methodInfoMap_.size();
for (const auto &method : methodInfoMap_) {
if (!method.second.IsMatch()) {
auto info = std::make_pair(method.first, recordName);
mismatchMethodSet.emplace(info);
mismatchMethodCount++;
for (const auto &methodNameSet : methodInfoMap_) {
if (methodNameSet.second.IsMatch()) {
continue;
}
auto info = std::make_pair(methodNameSet.first, recordName);
mismatchMethodSet.emplace(info);
mismatchMethodCount++;
}
}
void PGOMethodIdSet::Merge(const PGOMethodIdSet &from)
{
for (const auto &methodNameSet : from.methodInfoMap_) {
auto iter = methodInfoMap_.find(methodNameSet.first);
if (iter == methodInfoMap_.end()) {
auto ret = methodInfoMap_.emplace(methodNameSet.first, chunk_);
ASSERT(ret.second);
iter = ret.first;
}
const_cast<PGOMethodNameSet &>(iter->second).Merge(methodNameSet.second);
}
}
void PGODecodeMethodInfo::Merge(const PGODecodeMethodInfo &from)
{
ASSERT(methodId_.IsValid() && from.methodId_.IsValid());
if (!(methodId_ == from.methodId_)) {
LOG_ECMA(ERROR) << "MethodId not match. " << methodId_ << " vs " << from.methodId_;
return;
}
pgoMethodTypeSet_.Merge(&from.pgoMethodTypeSet_);
}
PGOMethodInfoMap *PGORecordDetailInfos::GetMethodInfoMap(const CString &recordName)
{
auto iter = recordInfos_.find(recordName.c_str());
@ -1083,8 +1130,8 @@ bool PGORecordDetailInfos::ProcessToBinaryForLayout(
NativeAreaAllocator *allocator, const SaveTask *task, std::fstream &stream) const
{
SectionInfo secInfo;
std::stringstream layoutDescStream;
auto layoutBeginPosition = stream.tellp();
stream.seekp(sizeof(SectionInfo), std::ofstream::cur);
for (const auto &typeInfo : moduleLayoutDescInfos_) {
if (task && task->IsTerminate()) {
LOG_ECMA(DEBUG) << "ProcessProfile: task is already terminate";
@ -1099,17 +1146,16 @@ bool PGORecordDetailInfos::ProcessToBinaryForLayout(
void *addr = allocator->Allocate(size);
auto descInfos = new (addr) PGOHClassLayoutDescInner(size, classType, superType);
descInfos->Merge(typeInfo);
layoutDescStream.write(reinterpret_cast<char *>(descInfos), size);
stream.write(reinterpret_cast<char *>(descInfos), size);
allocator->Delete(addr);
secInfo.number_++;
}
secInfo.offset_ = sizeof(SectionInfo);
secInfo.size_ = static_cast<uint32_t>(layoutDescStream.tellg());
stream.write(reinterpret_cast<char *>(&secInfo), sizeof(SectionInfo));
if (secInfo.number_ > 0) {
stream << layoutDescStream.rdbuf();
}
secInfo.size_ = static_cast<uint32_t>(stream.tellp()) - layoutBeginPosition - sizeof(SectionInfo);
stream.seekp(layoutBeginPosition, std::ofstream::beg)
.write(reinterpret_cast<char *>(&secInfo), sizeof(SectionInfo))
.seekp(0, std::ofstream::end);
return true;
}
@ -1207,6 +1253,29 @@ void PGORecordSimpleInfos::ParseFromBinary(void *buffer, PGOProfilerHeader *cons
}
}
void PGORecordSimpleInfos::Merge(const PGORecordSimpleInfos &simpleInfos)
{
for (const auto &method : simpleInfos.methodIds_) {
auto result = methodIds_.find(method.first);
if (result == methodIds_.end()) {
PGOMethodIdSet *methodIds = nativeAreaAllocator_.New<PGOMethodIdSet>(chunk_.get());
auto ret = methodIds_.emplace(method.first, methodIds);
ASSERT(ret.second);
result = ret.first;
}
const_cast<PGOMethodIdSet &>(*result->second).Merge(*method.second);
}
// Merge global layout desc infos to global method info map
for (const auto &moduleLayoutDescInfo : simpleInfos.moduleLayoutDescInfos_) {
auto result = moduleLayoutDescInfos_.find(moduleLayoutDescInfo);
if (result == moduleLayoutDescInfos_.end()) {
moduleLayoutDescInfos_.emplace(moduleLayoutDescInfo);
} else {
const_cast<PGOHClassLayoutDesc &>(*result).Merge(moduleLayoutDescInfo);
}
}
}
bool PGORecordSimpleInfos::ParseFromBinaryForLayout(void **buffer)
{
SectionInfo secInfo = base::ReadBuffer<SectionInfo>(buffer);

View File

@ -259,16 +259,19 @@ class PGOPandaFileInfos {
public:
void Sample(uint32_t checksum)
{
pandaFileInfos_.insert(checksum);
fileInfos_.emplace(checksum);
}
void Clear()
{
pandaFileInfos_.clear();
fileInfos_.clear();
}
void ParseFromBinary(void *buffer, SectionInfo *const info);
void ProcessToBinary(std::fstream &fileStream, SectionInfo *info) const;
void Merge(const PGOPandaFileInfos &pandaFileInfos);
bool VerifyChecksum(const PGOPandaFileInfos &pandaFileInfos, const std::string &base,
const std::string &incoming) const;
void ProcessToText(std::ofstream &stream) const;
bool ParseFromText(std::ifstream &stream);
@ -276,22 +279,22 @@ public:
bool Checksum(uint32_t checksum) const;
private:
class PandaFileInfo {
class FileInfo {
public:
PandaFileInfo() = default;
PandaFileInfo(uint32_t checksum) : size_(LastSize()), checksum_(checksum) {}
FileInfo() = default;
FileInfo(uint32_t checksum) : size_(LastSize()), checksum_(checksum) {}
static size_t LastSize()
{
return sizeof(PandaFileInfo);
return sizeof(FileInfo);
}
size_t Size()
size_t Size() const
{
return static_cast<size_t>(size_);
}
bool operator<(const PandaFileInfo &right) const
bool operator<(const FileInfo &right) const
{
return checksum_ < right.checksum_;
}
@ -307,7 +310,7 @@ private:
uint32_t checksum_;
};
std::set<PandaFileInfo> pandaFileInfos_;
std::set<FileInfo> fileInfos_;
};
class PGOMethodInfo {
@ -658,22 +661,6 @@ class PGODecodeMethodInfo {
public:
explicit PGODecodeMethodInfo(PGOMethodId id) : methodId_(id) {}
class ConsistencyInfo {
public:
void SetChecksum(uint32_t checksum)
{
checksum_ = checksum;
}
uint32_t GetChecksum() const
{
return checksum_;
}
private:
uint32_t checksum_ {0};
};
PGOMethodId GetMethodId() const
{
return methodId_;
@ -684,26 +671,11 @@ public:
return pgoMethodTypeSet_;
}
ConsistencyInfo &GetConsistencyInfo()
{
return consistencyInfo_;
}
void SetMatch()
{
methodNameMatch_ = true;
}
bool IsMatch() const
{
return methodNameMatch_;
}
void Merge(const PGODecodeMethodInfo &from);
private:
PGOMethodId methodId_ {0};
bool methodNameMatch_ {false};
PGOMethodTypeSet pgoMethodTypeSet_ {};
ConsistencyInfo consistencyInfo_ {};
};
class PGOHClassLayoutDescInner {
@ -830,6 +802,11 @@ public:
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;
const CMap<PGOMethodId, PGOMethodInfo *> &GetMethodInfos() const
{
return methodInfos_;
}
NO_COPY_SEMANTIC(PGOMethodInfoMap);
NO_MOVE_SEMANTIC(PGOMethodInfoMap);
@ -844,12 +821,15 @@ private:
class PGOMethodIdSet {
public:
explicit PGOMethodIdSet(Chunk* chunk): methodInfoMap_(chunk) {};
explicit PGOMethodIdSet(Chunk* chunk): chunk_(chunk), methodInfoMap_(chunk) {};
~PGOMethodIdSet() = default;
void Clear()
{
candidateSet_.clear();
for (auto &methodNameSet : methodInfoMap_) {
methodNameSet.second.Clear();
}
methodInfoMap_.clear();
}
@ -872,9 +852,10 @@ public:
template <typename Callback>
void GetTypeInfo(const char *methodName, Callback callback)
{
// for no function checksum in ap file
auto iter = methodInfoMap_.find(methodName);
if (iter != methodInfoMap_.end()) {
iter->second.GetPGOMethodTypeSet().GetTypeInfo(callback);
if ((iter != methodInfoMap_.end()) && (iter->second.GetFirstMethodInfo() != nullptr)) {
iter->second.GetFirstMethodInfo()->GetPGOMethodTypeSet().GetTypeInfo(callback);
}
}
@ -882,15 +863,10 @@ public:
void GetTypeInfo(const char *methodName, uint32_t checksum, Callback callback)
{
auto iter = methodInfoMap_.find(methodName);
if (iter == methodInfoMap_.end()) {
return;
if ((iter != methodInfoMap_.end()) && (iter->second.GetMethodInfo(checksum) != nullptr)) {
return iter->second.GetMethodInfo(checksum)->GetPGOMethodTypeSet().GetTypeInfo(callback);
}
auto &methodInfo = iter->second;
if (methodInfo.GetConsistencyInfo().GetChecksum() != checksum) {
LOG_ECMA(DEBUG) << "Method checksum mismatched, name: " << methodName;
return;
}
return methodInfo.GetPGOMethodTypeSet().GetTypeInfo(callback);
LOG_ECMA(DEBUG) << "Method checksum mismatched, name: " << methodName;
}
void MatchAndMarkMethod(const char *methodName, EntityId methodId)
@ -909,12 +885,80 @@ public:
void GetMismatchResult(const CString &recordName, uint32_t &totalMethodCount, uint32_t &mismatchMethodCount,
std::set<std::pair<std::string, CString>> &mismatchMethodSet) const;
void Merge(const PGOMethodIdSet &from);
class PGOMethodNameSet {
public:
explicit PGOMethodNameSet(Chunk* chunk): methodMap_(chunk) {};
void SetMatch()
{
methodNameMatch_ = true;
}
bool IsMatch() const
{
return methodNameMatch_;
}
PGODecodeMethodInfo& GetOrCreateMethodInfo(uint32_t checksum, PGOMethodId methodId)
{
auto methodIter = methodMap_.find(checksum);
if (methodIter == methodMap_.end()) {
auto ret = methodMap_.emplace(checksum, methodId);
ASSERT(ret.second);
methodIter = ret.first;
}
return methodIter->second;
}
void Merge(const PGOMethodNameSet &from)
{
for (const auto &method : from.methodMap_) {
uint32_t checksum = method.first;
auto methodInfo = methodMap_.find(checksum);
if (methodInfo == methodMap_.end()) {
auto ret = methodMap_.emplace(checksum, method.second.GetMethodId());
ASSERT(ret.second);
methodInfo = ret.first;
}
methodInfo->second.Merge(method.second);
}
}
PGODecodeMethodInfo *GetFirstMethodInfo()
{
if (methodMap_.empty()) {
return nullptr;
}
return &(methodMap_.begin()->second);
}
PGODecodeMethodInfo *GetMethodInfo(uint32_t checksum)
{
auto methodInfo = methodMap_.find(checksum);
if (methodInfo == methodMap_.end()) {
return nullptr;
}
return &(methodInfo->second);
}
void Clear()
{
methodMap_.clear();
}
private:
bool methodNameMatch_ {false};
ChunkUnorderedMap<uint32_t, PGODecodeMethodInfo> methodMap_;
};
NO_COPY_SEMANTIC(PGOMethodIdSet);
NO_MOVE_SEMANTIC(PGOMethodIdSet);
private:
Chunk* chunk_;
std::unordered_set<EntityId> candidateSet_; // methodId in abc file, DO NOT for pgo internal use
ChunkUnorderedMap<CString, PGODecodeMethodInfo> methodInfoMap_;
ChunkUnorderedMap<CString, PGOMethodNameSet> methodInfoMap_;
};
class PGORecordDetailInfos {
@ -954,6 +998,11 @@ public:
bool ParseFromText(std::ifstream &stream);
void ProcessToText(std::ofstream &stream) const;
const CMap<CString, PGOMethodInfoMap *> &GetRecordInfos() const
{
return recordInfos_;
}
NO_COPY_SEMANTIC(PGORecordDetailInfos);
NO_MOVE_SEMANTIC(PGORecordDetailInfos);
@ -983,7 +1032,7 @@ public:
void Clear()
{
for (const auto& iter : methodIds_) {
for (const auto &iter : methodIds_) {
iter.second->Clear();
nativeAreaAllocator_.Delete(iter.second);
}
@ -1066,6 +1115,8 @@ public:
void ParseFromBinary(void *buffer, PGOProfilerHeader *const header);
void Merge(const PGORecordSimpleInfos &simpleInfos);
NO_COPY_SEMANTIC(PGORecordSimpleInfos);
NO_MOVE_SEMANTIC(PGORecordSimpleInfos);

View File

@ -0,0 +1,94 @@
/*
* 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_manager.h"
#include "ecmascript/log_wrapper.h"
#include "ecmascript/platform/file.h"
namespace panda::ecmascript {
bool PGOProfilerManager::MergeApFiles(const std::string &inFiles, const std::string &outPath, uint32_t hotnessThreshold)
{
arg_list_t pandaFileNames = base::StringHelper::SplitString(inFiles, GetFileDelimiter());
PGOProfilerEncoder merger(outPath, hotnessThreshold);
if (!merger.InitializeData()) {
LOG_ECMA(ERROR) << "PGO Profiler encoder initialized failed. outPath: " << outPath
<< " ,hotnessThreshold: " << hotnessThreshold;
return false;
}
bool isFirstFile = true;
std::string firstApFileName;
for (const auto &fileName : pandaFileNames) {
if (!base::StringHelper::EndsWith(fileName, ".ap")) {
LOG_ECMA(ERROR) << "The file path(" << fileName << ") does not end with .ap";
continue;
}
PGOProfilerDecoder decoder(fileName, hotnessThreshold);
if (!decoder.LoadFull()) {
LOG_ECMA(ERROR) << "Fail to load file path(" << fileName << "), skip it.";
continue;
}
if (isFirstFile) {
firstApFileName = fileName;
} else {
if (!merger.VerifyPandaFileMatched(decoder.GetPandaFileInfos(), firstApFileName, fileName)) {
continue;
}
}
merger.Merge(decoder.GetRecordDetailInfos());
merger.Merge(decoder.GetPandaFileInfos());
isFirstFile = false;
}
if (isFirstFile) {
LOG_ECMA(ERROR) << "No input file processed. Input files: " << inFiles;
return false;
}
merger.Save();
return true;
}
bool PGOProfilerManager::MergeApFiles(uint32_t checksum, PGOProfilerDecoder &merger)
{
uint32_t hotnessThreshold = merger.GetHotnessThreshold();
std::string inFiles(merger.GetInPath());
arg_list_t pandaFileNames = base::StringHelper::SplitString(inFiles, GetFileDelimiter());
if (pandaFileNames.empty()) {
return true;
}
merger.InitMergeData();
bool isFirstFile = true;
std::string firstApFileName;
for (const auto &fileName : pandaFileNames) {
PGOProfilerDecoder decoder(fileName, hotnessThreshold);
if (!decoder.LoadAndVerify(checksum)) {
LOG_ECMA(ERROR) << "Load and verify file(" << fileName << ") failed, skip it.";
continue;
}
if (isFirstFile) {
firstApFileName = fileName;
} else {
if (!merger.GetPandaFileInfos().VerifyChecksum(decoder.GetPandaFileInfos(), firstApFileName, fileName)) {
continue;
}
}
merger.Merge(decoder);
isFirstFile = false;
}
if (isFirstFile) {
LOG_ECMA(ERROR) << "No input file processed. Input files: " << inFiles;
return false;
}
return true;
}
} // namespace panda::ecmascript

View File

@ -131,6 +131,9 @@ public:
return ret;
}
static bool MergeApFiles(const std::string &inFiles, const std::string &outPath, uint32_t hotnessThreshold);
static bool MergeApFiles(uint32_t checksum, PGOProfilerDecoder &merger);
private:
bool InitializeData()
{

View File

@ -19,6 +19,7 @@
#include "ecmascript/ecma_macros.h"
#include "ecmascript/log_wrapper.h"
#include "ecmascript/pgo_profiler/pgo_profiler_manager.h"
#include "ecmascript/platform/file.h"
namespace panda::ecmascript {
static const std::string VERSION = "0.0.0.1";
@ -30,6 +31,7 @@ public:
VERSION_QUERY,
TO_BINARY,
TO_TEXT,
MERGE,
};
std::string GetProfInPath() const
@ -62,6 +64,7 @@ public:
{"text", required_argument, nullptr, 't'},
{"binary", required_argument, nullptr, 'b'},
{"hotness-threshold", required_argument, nullptr, 's'},
{"merge", no_argument, nullptr, 'm'},
{"help", no_argument, nullptr, 'h'},
{"version", no_argument, nullptr, 'v'},
{nullptr, 0, nullptr, 0},
@ -83,6 +86,9 @@ public:
return false;
}
break;
case 'm':
mode_ = Mode::MERGE;
break;
case 'h':
return false;
case 'v':
@ -112,6 +118,7 @@ public:
"-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"
"-m, --merge merge multi binary ap files into one.\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;
@ -158,6 +165,13 @@ int Main(const int argc, const char **argv)
}
break;
}
case Option::Mode::MERGE: {
if (PGOProfilerManager::MergeApFiles(option.GetProfInPath(), option.GetProfOutPath(),
option.GetHotnessThreshold())) {
LOG_NO_TAG(ERROR) << "profiler merge success!";
}
break;
}
default:
break;
}

View File

@ -922,4 +922,36 @@ HWTEST_F_L0(PGOProfilerTest, FileConsistencyCheck)
unlink("ark-profiler17/modules.ap");
rmdir("ark-profiler17/");
}
#if defined(SUPPORT_ENABLE_ASM_INTERP)
HWTEST_F_L0(PGOProfilerTest, MergeApSelfTwice)
{
mkdir("ark-profiler18/", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
const char *targetRecordName = "op_type_test";
std::shared_ptr<JSPandaFile> jsPandaFile = ExecuteAndLoadJSPandaFile("ark-profiler18/", targetRecordName);
ASSERT_NE(jsPandaFile, nullptr);
// Loader
PGOProfilerDecoder decoder("ark-profiler18/modules_merge.ap", 1);
PGOProfilerDecoder decoderSingle("ark-profiler18/modules.ap", 1);
ASSERT_TRUE(PGOProfilerManager::MergeApFiles("ark-profiler18/modules.ap:ark-profiler18/modules.ap",
"ark-profiler18/modules_merge.ap", 1));
ASSERT_TRUE(decoder.LoadFull());
ASSERT_TRUE(decoderSingle.LoadFull());
auto doubleCount =
decoder.GetRecordDetailInfos().GetRecordInfos().begin()->second->GetMethodInfos().begin()->second->GetCount();
auto singleCount = decoderSingle.GetRecordDetailInfos()
.GetRecordInfos()
.begin()
->second->GetMethodInfos()
.begin()
->second->GetCount();
ASSERT_EQ(doubleCount, singleCount + singleCount);
unlink("ark-profiler18/modules.ap");
unlink("ark-profiler18/modules_merge.ap");
rmdir("ark-profiler18/");
}
#endif
} // namespace panda::test