mirror of
https://github.com/openharmony/ark_runtime_core.git
synced 2026-07-19 21:23:32 -04:00
!95 Temporarily drop profile saver
Merge pull request !95 from 胡晓炜/OpenHarmony-3.1-Release
This commit is contained in:
@@ -138,8 +138,6 @@ ohos_static_library("libarkruntime_static") {
|
||||
"object_accessor.cpp",
|
||||
"object_header.cpp",
|
||||
"panda_vm.cpp",
|
||||
"profilesaver/profile_dump_info.cpp",
|
||||
"profilesaver/profile_saver.cpp",
|
||||
"runtime.cpp",
|
||||
"runtime_helpers.cpp",
|
||||
"signal_handler.cpp",
|
||||
|
||||
@@ -128,8 +128,6 @@ set(SOURCES
|
||||
object_accessor.cpp
|
||||
arch/asm_support.cpp
|
||||
signal_handler.cpp
|
||||
profilesaver/profile_saver.cpp
|
||||
profilesaver/profile_dump_info.cpp
|
||||
runtime_helpers.cpp
|
||||
handle_scope.cpp
|
||||
file_manager.cpp
|
||||
|
||||
@@ -38,7 +38,6 @@ class CFrameTest;
|
||||
class StackWalkerTest;
|
||||
class PandaRunner;
|
||||
class CompilerQueueTest;
|
||||
class ProfileSaverTest;
|
||||
class DynObjectsTest;
|
||||
class JSObjectTest;
|
||||
class JSFunctionTest;
|
||||
@@ -312,7 +311,6 @@ private:
|
||||
friend class panda::test::PandaRunner;
|
||||
friend class verifier::test::VerifierTest;
|
||||
friend class panda::Logger;
|
||||
friend class test::ProfileSaverTest;
|
||||
friend class panda::test::DynObjectsTest;
|
||||
friend class panda::test::JSObjectTest;
|
||||
friend class panda::test::JSFunctionTest;
|
||||
|
||||
@@ -1,575 +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.
|
||||
*/
|
||||
|
||||
#include "runtime/profilesaver/profile_dump_info.h"
|
||||
|
||||
#include <sys/file.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
#include <cerrno>
|
||||
#include <climits>
|
||||
#include <cstring>
|
||||
|
||||
#include "libpandabase/os/unix/failure_retry.h"
|
||||
#include "trace/trace.h"
|
||||
|
||||
#ifndef PATH_MAX
|
||||
#define PATH_MAX 1024
|
||||
#endif
|
||||
|
||||
namespace panda {
|
||||
static constexpr size_t K_BITS_PER_BYTE = 8;
|
||||
|
||||
static constexpr size_t K_LINE_HEADER_SIZE = 3 * sizeof(uint32_t) + sizeof(uint16_t);
|
||||
static constexpr size_t K_METHOD_BYTES = 4;
|
||||
static constexpr size_t K_CLASS_BYTES = 4;
|
||||
|
||||
const uint8_t ProfileDumpInfo::kProfileMagic[] = {'p', 'r', 'o', 'f', '\0'}; // NOLINT
|
||||
const uint8_t ProfileDumpInfo::kProfileVersion[] = {'0', '1', '\0'}; // NOLINT
|
||||
|
||||
static constexpr uint16_t K_MAX_FILE_KEY_LENGTH = PATH_MAX; // NOLINT
|
||||
|
||||
static bool WriteBuffer(int fd, const uint8_t *buffer, size_t byte_count)
|
||||
{ // NOLINT
|
||||
while (byte_count > 0) {
|
||||
int bytes_written = write(fd, buffer, byte_count); // real place to write
|
||||
if (bytes_written == -1) {
|
||||
return false;
|
||||
}
|
||||
byte_count -= bytes_written;
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||||
buffer += bytes_written;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void AddStringToBuffer(PandaVector<uint8_t> *buffer, const PandaString &value)
|
||||
{ // NOLINT
|
||||
buffer->insert(buffer->end(), value.begin(), value.end());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void AddUintToBuffer(PandaVector<uint8_t> *buffer, T value)
|
||||
{
|
||||
for (size_t i = 0; i < sizeof(T); i++) {
|
||||
buffer->push_back((value >> (i * K_BITS_PER_BYTE)) & 0xff); // NOLINT
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Tests for EOF by trying to read 1 byte from the descriptor.
|
||||
* Returns:
|
||||
* 0 if the descriptor is at the EOF,
|
||||
* -1 if there was an IO error
|
||||
* 1 if the descriptor has more content to read
|
||||
*/
|
||||
// NOLINTNEXTLINE(readability-identifier-naming)
|
||||
static int testEOF(int fd)
|
||||
{
|
||||
// NOLINTNEXTLINE(modernize-avoid-c-arrays)
|
||||
uint8_t buffer[1];
|
||||
return read(fd, buffer, 1);
|
||||
}
|
||||
|
||||
int64_t GetFileSizeBytes(const PandaString &filename)
|
||||
{
|
||||
struct stat stat_buf {
|
||||
};
|
||||
int rc = stat(filename.c_str(), &stat_buf);
|
||||
return rc == 0 ? stat_buf.st_size : -1;
|
||||
}
|
||||
|
||||
ProfileDumpInfo::ProfileLoadSatus ProfileDumpInfo::SerializerBuffer::FillFromFd(int fd, const PandaString &source,
|
||||
PandaString *error)
|
||||
{
|
||||
size_t byte_count = ptr_end_ - ptr_current_;
|
||||
uint8_t *buffer = ptr_current_;
|
||||
while (byte_count > 0) {
|
||||
int bytes_read = read(fd, buffer, byte_count);
|
||||
if (bytes_read == 0) { // NOLINT
|
||||
*error += "Profile EOF reached prematurely for " + source;
|
||||
return PROFILE_LOAD_BAD_DATA;
|
||||
} else if (bytes_read < 0) { // NOLINT
|
||||
*error += "Profile IO error for " + source + ConvertToString(os::Error(errno).ToString());
|
||||
return PROFILE_LOAD_IO_ERROR;
|
||||
}
|
||||
byte_count -= bytes_read;
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||||
buffer += bytes_read;
|
||||
}
|
||||
return PROFILE_LOAD_SUCCESS;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T ProfileDumpInfo::SerializerBuffer::ReadUintAndAdvance()
|
||||
{
|
||||
static_assert(std::is_unsigned<T>::value, "Type is not unsigned");
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||||
ASSERT(ptr_current_ + sizeof(T) <= ptr_end_);
|
||||
T value = 0;
|
||||
for (size_t i = 0; i < sizeof(T); i++) {
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||||
value += ptr_current_[i] << (i * K_BITS_PER_BYTE);
|
||||
}
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||||
ptr_current_ += sizeof(T);
|
||||
return value;
|
||||
}
|
||||
|
||||
bool ProfileDumpInfo::SerializerBuffer::CompareAndAdvance(const uint8_t *data, size_t data_size)
|
||||
{
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||||
if (ptr_current_ + data_size > ptr_end_) {
|
||||
return false;
|
||||
}
|
||||
if (memcmp(ptr_current_, data, data_size) == 0) {
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||||
ptr_current_ += data_size;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ProfileDumpInfo::MergeWith(const ProfileDumpInfo &other)
|
||||
{
|
||||
for (const auto &other_it : other.dump_info_) {
|
||||
auto info_it = dump_info_.find(other_it.first);
|
||||
if ((info_it != dump_info_.end()) && (info_it->second.checksum != other_it.second.checksum)) {
|
||||
LOG(INFO, RUNTIME) << "info_it->second.checksum" << info_it->second.checksum;
|
||||
LOG(INFO, RUNTIME) << "other_it->second.checksum" << other_it.second.checksum;
|
||||
LOG(INFO, RUNTIME) << "Checksum mismatch" << other_it.first;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
LOG(INFO, RUNTIME) << "All checksums match";
|
||||
|
||||
for (const auto &other_it : other.dump_info_) {
|
||||
const PandaString &other_profile_location = other_it.first;
|
||||
const ProfileLineData &other_profile_data = other_it.second;
|
||||
auto info_it = dump_info_.find(other_profile_location);
|
||||
if (info_it == dump_info_.end()) {
|
||||
auto ret =
|
||||
dump_info_.insert(std::make_pair(other_profile_location, ProfileLineData(other_profile_data.checksum)));
|
||||
ASSERT(ret.second);
|
||||
info_it = ret.first;
|
||||
}
|
||||
info_it->second.method_wrapper_set.insert(other_profile_data.method_wrapper_set.begin(),
|
||||
other_profile_data.method_wrapper_set.end());
|
||||
info_it->second.class_wrapper_set.insert(other_profile_data.class_wrapper_set.begin(),
|
||||
other_profile_data.class_wrapper_set.end());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ProfileDumpInfo::AddMethodsAndClasses(const PandaVector<ExtractedMethod> &methods,
|
||||
const PandaSet<ExtractedResolvedClasses> &resolved_classes)
|
||||
{
|
||||
for (const ExtractedMethod &method : methods) {
|
||||
if (!AddMethodWrapper(ConvertToString(method.panda_file_->GetFilename()),
|
||||
method.panda_file_->GetHeader()->checksum, MethodWrapper(method.file_id_.GetOffset()))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (const ExtractedResolvedClasses &class_resolved : resolved_classes) {
|
||||
if (!AddResolvedClasses(class_resolved)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t ProfileDumpInfo::GetNumberOfMethods() const
|
||||
{
|
||||
uint64_t total = 0;
|
||||
for (const auto &it : dump_info_) {
|
||||
total += it.second.method_wrapper_set.size();
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
uint64_t ProfileDumpInfo::GetNumberOfResolvedClasses() const
|
||||
{
|
||||
uint64_t total = 0;
|
||||
for (const auto &it : dump_info_) {
|
||||
total += it.second.class_wrapper_set.size();
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
bool ProfileDumpInfo::ContainsMethod(const ExtractedMethod &method_ref) const
|
||||
{
|
||||
auto info_it = dump_info_.find(ConvertToString(method_ref.panda_file_->GetFilename()));
|
||||
if (info_it != dump_info_.end()) {
|
||||
if (method_ref.panda_file_->GetHeader()->checksum != info_it->second.checksum) {
|
||||
return false;
|
||||
}
|
||||
const PandaSet<MethodWrapper> &methods = info_it->second.method_wrapper_set;
|
||||
return methods.find(MethodWrapper(method_ref.file_id_.GetOffset())) != methods.end();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ProfileDumpInfo::ContainsClass(const panda_file::File &pandafile, uint32_t class_def_idx) const
|
||||
{
|
||||
auto info_it = dump_info_.find(ConvertToString(pandafile.GetFilename()));
|
||||
if (info_it != dump_info_.end()) {
|
||||
if (pandafile.GetHeader()->checksum != info_it->second.checksum) {
|
||||
return false;
|
||||
}
|
||||
const PandaSet<ClassWrapper> &classes = info_it->second.class_wrapper_set;
|
||||
return classes.find(ClassWrapper(class_def_idx)) != classes.end();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ProfileDumpInfo::AddMethodWrapper(const PandaString &panda_file_location, uint32_t checksum,
|
||||
const ProfileDumpInfo::MethodWrapper &method_to_add)
|
||||
{
|
||||
ProfileLineData *const DATA = GetOrAddProfileLineData(panda_file_location, checksum);
|
||||
if (DATA == nullptr) {
|
||||
return false;
|
||||
}
|
||||
DATA->method_wrapper_set.insert(method_to_add);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ProfileDumpInfo::AddClassWrapper(const PandaString &panda_file_location, uint32_t checksum,
|
||||
const ProfileDumpInfo::ClassWrapper &class_to_add)
|
||||
{
|
||||
ProfileLineData *const DATA = GetOrAddProfileLineData(panda_file_location, checksum);
|
||||
if (DATA == nullptr) {
|
||||
return false;
|
||||
}
|
||||
DATA->class_wrapper_set.insert(class_to_add);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ProfileDumpInfo::AddResolvedClasses(const ExtractedResolvedClasses &classes)
|
||||
{ // NOLINT(readability-identifier-naming)
|
||||
const PandaString panda_file_location = classes.GetPandaFileLocation(); // NOLINT(readability-identifier-naming)
|
||||
const uint32_t checksum = classes.GetPandaFileChecksum(); // NOLINT(readability-identifier-naming)
|
||||
ProfileLineData *const DATA = GetOrAddProfileLineData(panda_file_location, checksum);
|
||||
if (DATA == nullptr) {
|
||||
return false;
|
||||
}
|
||||
for (auto const &i : classes.GetClasses()) {
|
||||
DATA->class_wrapper_set.insert(ClassWrapper(i));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
ProfileDumpInfo::ProfileLineData *ProfileDumpInfo::GetOrAddProfileLineData(const PandaString &panda_file_location,
|
||||
uint32_t checksum)
|
||||
{
|
||||
auto info_it = dump_info_.find(panda_file_location);
|
||||
if (info_it == dump_info_.end()) {
|
||||
auto ret = dump_info_.insert(std::make_pair(panda_file_location, ProfileLineData(checksum)));
|
||||
ASSERT(ret.second);
|
||||
info_it = ret.first;
|
||||
}
|
||||
if (info_it->second.checksum != checksum) {
|
||||
LOG(INFO, RUNTIME) << "Checksum mismatch" << panda_file_location;
|
||||
return nullptr;
|
||||
}
|
||||
return &(info_it->second);
|
||||
}
|
||||
|
||||
bool ProfileDumpInfo::Save(int fd)
|
||||
{
|
||||
ASSERT(fd >= 0);
|
||||
trace::ScopedTrace scoped_trace(__PRETTY_FUNCTION__);
|
||||
|
||||
static constexpr size_t K_MAX_BUFFER_SIZE = 8 * 1024;
|
||||
PandaVector<uint8_t> buffer; // each element 1 byte
|
||||
|
||||
WriteBuffer(fd, kProfileMagic, sizeof(kProfileMagic));
|
||||
WriteBuffer(fd, kProfileVersion, sizeof(kProfileVersion));
|
||||
AddUintToBuffer(&buffer, static_cast<uint32_t>(dump_info_.size()));
|
||||
|
||||
for (const auto &it : dump_info_) {
|
||||
if (buffer.size() > K_MAX_BUFFER_SIZE) {
|
||||
if (!WriteBuffer(fd, buffer.data(), buffer.size())) {
|
||||
return false;
|
||||
}
|
||||
buffer.clear();
|
||||
}
|
||||
const PandaString &file_location = it.first;
|
||||
const ProfileLineData &file_data = it.second;
|
||||
|
||||
if (file_location.size() >= K_MAX_FILE_KEY_LENGTH) {
|
||||
LOG(INFO, RUNTIME) << "PandaFileKey exceeds allocated limit";
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t required_capacity = buffer.size() + K_LINE_HEADER_SIZE + file_location.size() +
|
||||
K_METHOD_BYTES * file_data.method_wrapper_set.size() +
|
||||
K_CLASS_BYTES * file_data.class_wrapper_set.size();
|
||||
buffer.reserve(required_capacity);
|
||||
|
||||
ASSERT(file_location.size() <= std::numeric_limits<uint16_t>::max());
|
||||
ASSERT(file_data.method_wrapper_set.size() <= std::numeric_limits<uint32_t>::max());
|
||||
ASSERT(file_data.class_wrapper_set.size() <= std::numeric_limits<uint32_t>::max());
|
||||
|
||||
AddUintToBuffer(&buffer, static_cast<uint16_t>(file_location.size()));
|
||||
AddUintToBuffer(&buffer, static_cast<uint32_t>(file_data.method_wrapper_set.size()));
|
||||
AddUintToBuffer(&buffer, static_cast<uint32_t>(file_data.class_wrapper_set.size()));
|
||||
AddUintToBuffer(&buffer, file_data.checksum);
|
||||
AddStringToBuffer(&buffer, file_location);
|
||||
|
||||
if (UNLIKELY(file_data.empty())) {
|
||||
LOG(INFO, RUNTIME) << "EMPTY FILE DATA, WERIED!";
|
||||
}
|
||||
|
||||
for (auto method_it : file_data.method_wrapper_set) {
|
||||
AddUintToBuffer(&buffer, method_it.method_id_);
|
||||
}
|
||||
for (auto class_it : file_data.class_wrapper_set) {
|
||||
AddUintToBuffer(&buffer, class_it.class_id_);
|
||||
}
|
||||
ASSERT(required_capacity == buffer.size());
|
||||
}
|
||||
return WriteBuffer(fd, buffer.data(), buffer.size());
|
||||
}
|
||||
|
||||
bool ProfileDumpInfo::Load(int fd)
|
||||
{
|
||||
trace::ScopedTrace scoped_trace(__PRETTY_FUNCTION__);
|
||||
PandaString error;
|
||||
ProfileLoadSatus status = LoadInternal(fd, &error);
|
||||
|
||||
if (status == PROFILE_LOAD_SUCCESS) {
|
||||
return true;
|
||||
}
|
||||
LOG(INFO, RUNTIME) << "Error when reading profile " << error;
|
||||
return false;
|
||||
}
|
||||
|
||||
ProfileDumpInfo::ProfileLoadSatus ProfileDumpInfo::LoadInternal(int fd, PandaString *error)
|
||||
{
|
||||
ASSERT(fd >= 0);
|
||||
trace::ScopedTrace scoped_trace(__PRETTY_FUNCTION__);
|
||||
|
||||
struct stat stat_buffer {
|
||||
};
|
||||
if (fstat(fd, &stat_buffer) != 0) {
|
||||
return PROFILE_LOAD_IO_ERROR;
|
||||
}
|
||||
|
||||
if (stat_buffer.st_size == 0) {
|
||||
LOG(INFO, RUNTIME) << "empty file";
|
||||
return PROFILE_LOAD_EMPTYFILE;
|
||||
}
|
||||
|
||||
uint32_t number_of_lines;
|
||||
ProfileLoadSatus status = ReadProfileHeader(fd, &number_of_lines, error);
|
||||
if (status != PROFILE_LOAD_SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
LOG(INFO, RUNTIME) << "number of profile items = " << number_of_lines;
|
||||
|
||||
while (number_of_lines > 0) {
|
||||
ProfileLineHeader line_header;
|
||||
status = ReadProfileLineHeader(fd, &line_header, error);
|
||||
if (status != PROFILE_LOAD_SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = ReadProfileLine(fd, line_header, error);
|
||||
if (status != PROFILE_LOAD_SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
number_of_lines--;
|
||||
}
|
||||
|
||||
int result = testEOF(fd);
|
||||
if (result == 0) {
|
||||
return PROFILE_LOAD_SUCCESS;
|
||||
}
|
||||
|
||||
if (result < 0) {
|
||||
return PROFILE_LOAD_IO_ERROR;
|
||||
}
|
||||
|
||||
*error = "Unexpected content in the profile file";
|
||||
return PROFILE_LOAD_BAD_DATA;
|
||||
}
|
||||
|
||||
ProfileDumpInfo::ProfileLoadSatus ProfileDumpInfo::ReadProfileHeader(int fd, uint32_t *number_of_lines,
|
||||
PandaString *error)
|
||||
{
|
||||
const size_t K_MAGIC_VERSION_SIZE = sizeof(kProfileMagic) + sizeof(kProfileVersion) + sizeof(uint32_t);
|
||||
|
||||
SerializerBuffer safe_buffer(K_MAGIC_VERSION_SIZE);
|
||||
|
||||
ProfileLoadSatus status = safe_buffer.FillFromFd(fd, "ReadProfileHeader", error);
|
||||
if (status != PROFILE_LOAD_SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (!safe_buffer.CompareAndAdvance(kProfileMagic, sizeof(kProfileMagic))) {
|
||||
*error = "Profile missing magic";
|
||||
return PROFILE_LOAD_VERSION_MISMATCH;
|
||||
}
|
||||
if (!safe_buffer.CompareAndAdvance(kProfileVersion, sizeof(kProfileVersion))) {
|
||||
*error = "Profile version mismatch";
|
||||
return PROFILE_LOAD_VERSION_MISMATCH;
|
||||
}
|
||||
|
||||
*number_of_lines = safe_buffer.ReadUintAndAdvance<uint32_t>();
|
||||
return PROFILE_LOAD_SUCCESS;
|
||||
}
|
||||
|
||||
ProfileDumpInfo::ProfileLoadSatus ProfileDumpInfo::ReadProfileLineHeader(int fd, ProfileLineHeader *line_header,
|
||||
PandaString *error)
|
||||
{
|
||||
SerializerBuffer header_buffer(K_LINE_HEADER_SIZE);
|
||||
ProfileLoadSatus status = header_buffer.FillFromFd(fd, "ReadProfileLineHeader", error);
|
||||
if (status != PROFILE_LOAD_SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
|
||||
auto panda_location_size = header_buffer.ReadUintAndAdvance<uint16_t>(); // max chars in location, 4096 = 2 ^ 12
|
||||
line_header->method_set_size = header_buffer.ReadUintAndAdvance<uint32_t>();
|
||||
line_header->class_set_size = header_buffer.ReadUintAndAdvance<uint32_t>();
|
||||
line_header->checksum = header_buffer.ReadUintAndAdvance<uint32_t>();
|
||||
|
||||
if (panda_location_size == 0 || panda_location_size > K_MAX_FILE_KEY_LENGTH) {
|
||||
*error = "PandaFileKey has an invalid size: " + std::to_string(panda_location_size);
|
||||
return PROFILE_LOAD_BAD_DATA;
|
||||
}
|
||||
|
||||
SerializerBuffer location_buffer(panda_location_size);
|
||||
// Read the binary data: location string
|
||||
status = location_buffer.FillFromFd(fd, "ReadProfileLineHeader", error);
|
||||
if (status != PROFILE_LOAD_SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
line_header->panda_file_location.assign(reinterpret_cast<char *>(location_buffer.Get()), panda_location_size);
|
||||
return PROFILE_LOAD_SUCCESS;
|
||||
}
|
||||
|
||||
ProfileDumpInfo::ProfileLoadSatus ProfileDumpInfo::ReadProfileLine(int fd, const ProfileLineHeader &line_header,
|
||||
PandaString *error)
|
||||
{
|
||||
static constexpr uint32_t K_MAX_NUMBER_OF_ENTRIES_TO_READ = 8000; // ~8 kb
|
||||
uint32_t methods_left_to_read = line_header.method_set_size;
|
||||
uint32_t classes_left_to_read = line_header.class_set_size;
|
||||
|
||||
while ((methods_left_to_read > 0) || (classes_left_to_read > 0)) {
|
||||
uint32_t methods_to_read = std::min(K_MAX_NUMBER_OF_ENTRIES_TO_READ, methods_left_to_read);
|
||||
uint32_t max_classes_to_read = K_MAX_NUMBER_OF_ENTRIES_TO_READ - methods_to_read; // >=0
|
||||
uint32_t classes_to_read = std::min(max_classes_to_read, classes_left_to_read);
|
||||
|
||||
size_t line_size = K_METHOD_BYTES * methods_to_read + K_CLASS_BYTES * classes_to_read;
|
||||
SerializerBuffer line_buffer(line_size);
|
||||
|
||||
ProfileLoadSatus status = line_buffer.FillFromFd(fd, "ReadProfileLine", error);
|
||||
if (status != PROFILE_LOAD_SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
if (!ProcessLine(line_buffer, methods_to_read, classes_to_read, line_header.checksum,
|
||||
line_header.panda_file_location)) {
|
||||
*error = "Error when reading profile file line";
|
||||
return PROFILE_LOAD_BAD_DATA;
|
||||
}
|
||||
|
||||
methods_left_to_read -= methods_to_read;
|
||||
classes_left_to_read -= classes_to_read;
|
||||
}
|
||||
return PROFILE_LOAD_SUCCESS;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(google-runtime-references)
|
||||
bool ProfileDumpInfo::ProcessLine(SerializerBuffer &line_buffer, uint32_t method_set_size, uint32_t class_set_size,
|
||||
uint32_t checksum, const PandaString &panda_file_location)
|
||||
{
|
||||
for (uint32_t i = 0; i < method_set_size; i++) {
|
||||
// NB! Read the method info from buffer...
|
||||
auto method_idx = line_buffer.ReadUintAndAdvance<uint32_t>();
|
||||
if (!AddMethodWrapper(panda_file_location, checksum, MethodWrapper(method_idx))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < class_set_size; i++) {
|
||||
auto class_def_idx = line_buffer.ReadUintAndAdvance<uint32_t>();
|
||||
if (!AddClassWrapper(panda_file_location, checksum, ClassWrapper(class_def_idx))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ProfileDumpInfo::MergeAndSave(const PandaString &filename, uint64_t *bytes_written, bool force)
|
||||
{
|
||||
// NB! we using READWRITE mode to leave the creation job to framework layer.
|
||||
panda::os::unix::file::File myfile = panda::os::file::Open(filename, panda::os::file::Mode::READWRITE);
|
||||
if (!myfile.IsValid()) {
|
||||
LOG(ERROR, RUNTIME) << "Cannot open the profile file" << filename;
|
||||
return false;
|
||||
}
|
||||
panda::os::file::FileHolder fholder(myfile);
|
||||
int fd = myfile.GetFd();
|
||||
|
||||
LOG(INFO, RUNTIME) << " Step3.2: starting merging ***";
|
||||
PandaString error;
|
||||
ProfileDumpInfo file_dump_info;
|
||||
ProfileLoadSatus status = file_dump_info.LoadInternal(fd, &error);
|
||||
if (status == PROFILE_LOAD_SUCCESS || status == PROFILE_LOAD_EMPTYFILE) {
|
||||
if (MergeWith(file_dump_info)) {
|
||||
if (dump_info_ == file_dump_info.dump_info_) {
|
||||
if (bytes_written != nullptr) {
|
||||
*bytes_written = 0;
|
||||
}
|
||||
LOG(INFO, RUNTIME) << " No Saving as no change byte_written = 0";
|
||||
if (status != PROFILE_LOAD_EMPTYFILE) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOG(INFO, RUNTIME) << " No Saving as Could not merge previous profile data from file " << filename;
|
||||
if (!force) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else if (force && ((status == PROFILE_LOAD_VERSION_MISMATCH) || (status == PROFILE_LOAD_BAD_DATA))) {
|
||||
LOG(INFO, RUNTIME) << " Clearing bad or mismatch version profile data from file " << filename << ": " << error;
|
||||
} else {
|
||||
LOG(INFO, RUNTIME) << " No Saving as Could not load profile data from file " << filename << ": " << error;
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG(INFO, RUNTIME) << " Step3.3: starting Saving ***";
|
||||
LOG(INFO, RUNTIME) << " clear file data firstly";
|
||||
if (!myfile.ClearData()) {
|
||||
LOG(INFO, RUNTIME) << "Could not clear profile file: " << filename;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = Save(fd);
|
||||
if (result) {
|
||||
if (bytes_written != nullptr) {
|
||||
LOG(INFO, RUNTIME) << " Profile Saver Bingo! and bytes written = " << bytes_written;
|
||||
*bytes_written = GetFileSizeBytes(filename);
|
||||
}
|
||||
} else {
|
||||
LOG(ERROR, RUNTIME) << "Failed to save profile info to " << filename;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace panda
|
||||
@@ -1,289 +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 PANDA_RUNTIME_PROFILESAVER_PROFILE_DUMP_INFO_H_
|
||||
#define PANDA_RUNTIME_PROFILESAVER_PROFILE_DUMP_INFO_H_
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "libpandabase/utils/logger.h"
|
||||
#include "libpandafile/file.h"
|
||||
#include "runtime/include/mem/panda_containers.h"
|
||||
#include "runtime/include/mem/panda_smart_pointers.h"
|
||||
#include "runtime/include/mem/panda_string.h"
|
||||
|
||||
// NB! suppose that, panda file can always provide such profile file format data!
|
||||
// NB! we use serializer saving way which means the profile file is just binary file!
|
||||
// profile header
|
||||
// magic
|
||||
// version
|
||||
// checksum?(ommit)
|
||||
// #lines
|
||||
// Line1:
|
||||
// profileline header
|
||||
// file location
|
||||
// #method
|
||||
// #class
|
||||
// checksum
|
||||
// methods index/id(#method)
|
||||
// class index/id(#class)
|
||||
// LineN:
|
||||
// ...
|
||||
|
||||
namespace panda {
|
||||
|
||||
/*
|
||||
* Any newly added information, we have to change the following info naturely, especially
|
||||
* ExtractedResolvedClasses
|
||||
*/
|
||||
struct ExtractedMethod {
|
||||
ExtractedMethod(const panda_file::File *file, panda_file::File::EntityId file_id)
|
||||
: panda_file_(file), file_id_(file_id)
|
||||
{
|
||||
}
|
||||
const panda_file::File *panda_file_; // NOLINT(misc-non-private-member-variables-in-classes)
|
||||
panda_file::File::EntityId file_id_; // NOLINT(misc-non-private-member-variables-in-classes)
|
||||
};
|
||||
|
||||
struct ExtractedResolvedClasses {
|
||||
public:
|
||||
// NOLINTNEXTLINE(modernize-pass-by-value)
|
||||
ExtractedResolvedClasses(const PandaString &location, uint32_t checksum)
|
||||
: panda_file_location_(location), panda_file_checksum_(checksum)
|
||||
{
|
||||
}
|
||||
|
||||
int Compare(const ExtractedResolvedClasses &other) const
|
||||
{
|
||||
if (panda_file_checksum_ != other.panda_file_checksum_) {
|
||||
return static_cast<int>(panda_file_checksum_ - other.panda_file_checksum_);
|
||||
}
|
||||
return panda_file_location_.compare(other.panda_file_location_);
|
||||
}
|
||||
|
||||
template <class InputIt>
|
||||
void AddClasses(InputIt begin, InputIt end) const
|
||||
{
|
||||
classes_.insert(begin, end);
|
||||
}
|
||||
|
||||
void AddClass(uint32_t classindex) const
|
||||
{
|
||||
classes_.insert(classindex);
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(readability-const-return-type)
|
||||
const PandaString GetPandaFileLocation() const
|
||||
{
|
||||
return panda_file_location_;
|
||||
}
|
||||
|
||||
uint32_t GetPandaFileChecksum() const
|
||||
{
|
||||
return panda_file_checksum_;
|
||||
}
|
||||
|
||||
const PandaUnorderedSet<uint32_t> &GetClasses() const
|
||||
{
|
||||
return classes_;
|
||||
}
|
||||
|
||||
private:
|
||||
const PandaString panda_file_location_; // NOLINT(readability-identifier-naming)
|
||||
const uint32_t panda_file_checksum_; // NOLINT(readability-identifier-naming)
|
||||
// Array of resolved class def indexes. we leave this as extention
|
||||
mutable PandaUnorderedSet<uint32_t> classes_;
|
||||
};
|
||||
|
||||
// we define this for PandaSet find() function
|
||||
inline bool operator<(const ExtractedResolvedClasses &a, const ExtractedResolvedClasses &b)
|
||||
{
|
||||
return a.Compare(b) < 0;
|
||||
}
|
||||
|
||||
class ProfileDumpInfo {
|
||||
public:
|
||||
// Content of profile header
|
||||
static const uint8_t kProfileMagic[]; // NOLINT(modernize-avoid-c-arrays, readability-identifier-naming)
|
||||
static const uint8_t kProfileVersion[]; // NOLINT(modernize-avoid-c-arrays, readability-identifier-naming)
|
||||
|
||||
/*
|
||||
* Saves the profile data to the given file descriptor.
|
||||
*/
|
||||
bool Save(int fd);
|
||||
|
||||
/*
|
||||
* Loads profile information from the given file descriptor.
|
||||
*/
|
||||
bool Load(int fd);
|
||||
|
||||
/*
|
||||
* Merge the data from another ProfileDumpInfo into the current object.
|
||||
*/
|
||||
bool MergeWith(const ProfileDumpInfo &other);
|
||||
|
||||
/*
|
||||
* Add the given methods and classes to the current profile object
|
||||
*/
|
||||
bool AddMethodsAndClasses(const PandaVector<ExtractedMethod> &methods,
|
||||
const PandaSet<ExtractedResolvedClasses> &resolved_classes);
|
||||
|
||||
/*
|
||||
* Loads and merges profile information from the given file into the current cache
|
||||
* object and tries to save it back to disk.
|
||||
*
|
||||
* If `force` is true then the save will be forced regardless of bad data or mismatched version.
|
||||
*/
|
||||
bool MergeAndSave(const PandaString &filename, uint64_t *bytes_written, bool force);
|
||||
|
||||
/*
|
||||
* Returns the number of methods that were profiled.
|
||||
*/
|
||||
uint64_t GetNumberOfMethods() const;
|
||||
|
||||
/*
|
||||
* Returns the number of resolved classes that were profiled.
|
||||
*/
|
||||
uint64_t GetNumberOfResolvedClasses() const;
|
||||
|
||||
/*
|
||||
* Returns true if the method reference is present in the profiling info.
|
||||
*/
|
||||
bool ContainsMethod(const ExtractedMethod &method_ref) const;
|
||||
|
||||
/*
|
||||
* Returns true if the class is present in the profiling info.
|
||||
*/
|
||||
bool ContainsClass(const panda_file::File &pandafile, uint32_t class_def_idx) const;
|
||||
|
||||
private:
|
||||
enum ProfileLoadSatus {
|
||||
PROFILE_LOAD_IO_ERROR,
|
||||
PROFILE_LOAD_VERSION_MISMATCH,
|
||||
PROFILE_LOAD_BAD_DATA,
|
||||
PROFILE_LOAD_EMPTYFILE,
|
||||
PROFILE_LOAD_SUCCESS
|
||||
};
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
|
||||
struct ProfileLineHeader {
|
||||
PandaString panda_file_location;
|
||||
uint32_t method_set_size;
|
||||
uint32_t class_set_size;
|
||||
uint32_t checksum;
|
||||
};
|
||||
|
||||
// A helper structure to make sure we don't read past our buffers in the loops.
|
||||
struct SerializerBuffer {
|
||||
public:
|
||||
explicit SerializerBuffer(size_t size)
|
||||
{
|
||||
// NOLINTNEXTLINE(modernize-avoid-c-arrays)
|
||||
storage_ = MakePandaUnique<uint8_t[]>(size);
|
||||
ptr_current_ = storage_.get();
|
||||
ptr_end_ = ptr_current_ + size; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic
|
||||
}
|
||||
|
||||
ProfileLoadSatus FillFromFd(int fd, const PandaString &source, PandaString *error);
|
||||
|
||||
template <typename T>
|
||||
T ReadUintAndAdvance();
|
||||
|
||||
bool CompareAndAdvance(const uint8_t *data, size_t data_size);
|
||||
|
||||
uint8_t *Get()
|
||||
{
|
||||
return storage_.get();
|
||||
}
|
||||
|
||||
private:
|
||||
PandaUniquePtr<uint8_t[]> storage_; // NOLINT(modernize-avoid-c-arrays)
|
||||
uint8_t *ptr_current_;
|
||||
uint8_t *ptr_end_;
|
||||
};
|
||||
|
||||
struct MethodWrapper {
|
||||
explicit MethodWrapper(uint32_t index) : method_id_(index) {}
|
||||
uint32_t method_id_; // NOLINT(misc-non-private-member-variables-in-classes)
|
||||
|
||||
bool operator==(const MethodWrapper &other) const
|
||||
{
|
||||
return method_id_ == other.method_id_;
|
||||
}
|
||||
|
||||
bool operator<(const MethodWrapper &other) const
|
||||
{
|
||||
return method_id_ < other.method_id_;
|
||||
}
|
||||
};
|
||||
|
||||
struct ClassWrapper {
|
||||
explicit ClassWrapper(uint32_t index) : class_id_(index) {}
|
||||
uint32_t class_id_; // NOLINT(misc-non-private-member-variables-in-classes)
|
||||
|
||||
bool operator==(const ClassWrapper &other) const
|
||||
{
|
||||
return class_id_ == other.class_id_;
|
||||
}
|
||||
|
||||
bool operator<(const ClassWrapper &other) const
|
||||
{
|
||||
return class_id_ < other.class_id_;
|
||||
}
|
||||
};
|
||||
|
||||
struct ProfileLineData {
|
||||
explicit ProfileLineData(uint32_t file_checksum) : checksum(file_checksum) {}
|
||||
uint32_t checksum; // NOLINT(misc-non-private-member-variables-in-classes)
|
||||
PandaSet<MethodWrapper> method_wrapper_set; // NOLINT(misc-non-private-member-variables-in-classes)
|
||||
PandaSet<ClassWrapper> class_wrapper_set; // NOLINT(misc-non-private-member-variables-in-classes)
|
||||
|
||||
bool operator==(const ProfileLineData &other) const
|
||||
{
|
||||
return checksum == other.checksum && method_wrapper_set == other.method_wrapper_set &&
|
||||
class_wrapper_set == other.class_wrapper_set;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(readability-identifier-naming)
|
||||
bool empty() const
|
||||
{
|
||||
return method_wrapper_set.empty() && class_wrapper_set.empty();
|
||||
}
|
||||
};
|
||||
|
||||
ProfileLoadSatus LoadInternal(int fd, PandaString *error);
|
||||
ProfileLoadSatus ReadProfileHeader(int fd, uint32_t *number_of_lines, PandaString *error);
|
||||
ProfileLoadSatus ReadProfileLineHeader(int fd, ProfileLineHeader *line_header, PandaString *error);
|
||||
ProfileLoadSatus ReadProfileLine(int fd, const ProfileLineHeader &line_header, PandaString *error);
|
||||
// NOLINTNEXTLINE(google-runtime-references)
|
||||
bool ProcessLine(SerializerBuffer &line_buffer, uint32_t method_set_size, uint32_t class_set_size,
|
||||
uint32_t checksum, const PandaString &panda_file_location);
|
||||
|
||||
bool AddMethodWrapper(const PandaString &panda_file_location, uint32_t checksum,
|
||||
const MethodWrapper &method_to_add);
|
||||
bool AddClassWrapper(const PandaString &panda_file_location, uint32_t checksum, const ClassWrapper &class_to_add);
|
||||
bool AddResolvedClasses(const ExtractedResolvedClasses &classes);
|
||||
ProfileLineData *GetOrAddProfileLineData(const PandaString &panda_file_location, uint32_t checksum);
|
||||
|
||||
PandaMap<const PandaString, ProfileLineData> dump_info_;
|
||||
};
|
||||
|
||||
} // namespace panda
|
||||
|
||||
#endif // PANDA_RUNTIME_PROFILESAVER_PROFILE_DUMP_INFO_H_
|
||||
@@ -1,333 +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.
|
||||
*/
|
||||
|
||||
#include "runtime/profilesaver/profile_saver.h"
|
||||
|
||||
#include <cerrno>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include "runtime/include/class_linker.h"
|
||||
#include "runtime/include/runtime.h"
|
||||
#include "runtime/jit/jit.h"
|
||||
#include "trace/trace.h"
|
||||
|
||||
namespace panda {
|
||||
|
||||
ProfileSaver *ProfileSaver::instance = nullptr;
|
||||
// NOLINTNEXTLINE(fuchsia-statically-constructed-objects)
|
||||
std::thread ProfileSaver::profiler_saver_daemon_thread;
|
||||
|
||||
static bool CheckLocationForCompilation(const PandaString &location)
|
||||
{
|
||||
return !location.empty();
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
|
||||
ProfileSaver::ProfileSaver(const PandaString &output_filename, const PandaVector<PandaString> &code_paths,
|
||||
const PandaString &app_dir)
|
||||
: shutting_down_(false)
|
||||
{
|
||||
AddTrackedLocations(output_filename, code_paths, app_dir);
|
||||
}
|
||||
|
||||
// NB! it is the caller's responsibility to pass suitable output_filename, code_paths as well as app_data_dir.
|
||||
void ProfileSaver::AddTrackedLocations(const PandaString &output_filename, const PandaVector<PandaString> &code_paths,
|
||||
const PandaString &app_data_dir)
|
||||
{
|
||||
auto it = tracked_pandafile_base_locations_.find(output_filename);
|
||||
if (it == tracked_pandafile_base_locations_.end()) {
|
||||
tracked_pandafile_base_locations_.insert(
|
||||
std::make_pair(output_filename, PandaSet<PandaString>(code_paths.begin(), code_paths.end())));
|
||||
if (!app_data_dir.empty()) {
|
||||
app_data_dirs_.insert(app_data_dir);
|
||||
}
|
||||
} else {
|
||||
if (UNLIKELY(app_data_dirs_.count(app_data_dir) <= 0)) {
|
||||
LOG(INFO, RUNTIME) << "Cannot find app dir, bad output filename";
|
||||
return;
|
||||
}
|
||||
it->second.insert(code_paths.begin(), code_paths.end());
|
||||
}
|
||||
}
|
||||
|
||||
void ProfileSaver::Start(const PandaString &output_filename, const PandaVector<PandaString> &code_paths,
|
||||
const PandaString &app_data_dir)
|
||||
{
|
||||
if (Runtime::GetCurrent() == nullptr) {
|
||||
LOG(ERROR, RUNTIME) << "Runtime is nullptr";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Runtime::GetCurrent()->SaveProfileInfo()) {
|
||||
LOG(ERROR, RUNTIME) << "ProfileSaver is forbidden";
|
||||
return;
|
||||
}
|
||||
|
||||
if (output_filename.empty()) {
|
||||
LOG(ERROR, RUNTIME) << "Invalid output filename";
|
||||
return;
|
||||
}
|
||||
|
||||
PandaVector<PandaString> code_paths_to_profile;
|
||||
for (const PandaString &location : code_paths) {
|
||||
if (CheckLocationForCompilation(location)) {
|
||||
code_paths_to_profile.push_back(location);
|
||||
}
|
||||
}
|
||||
|
||||
if (code_paths_to_profile.empty()) {
|
||||
LOG(INFO, RUNTIME) << "No code paths should be profiled.";
|
||||
return;
|
||||
}
|
||||
|
||||
if (UNLIKELY(instance != nullptr)) {
|
||||
LOG(INFO, RUNTIME) << "Profile Saver Singleton already exists";
|
||||
instance->AddTrackedLocations(output_filename, code_paths_to_profile, app_data_dir);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG(INFO, RUNTIME) << "Starting dumping profile saver output file" << output_filename;
|
||||
|
||||
instance = new ProfileSaver(output_filename, code_paths_to_profile, app_data_dir);
|
||||
profiler_saver_daemon_thread = std::thread(ProfileSaver::RunProfileSaverThread, reinterpret_cast<void *>(instance));
|
||||
}
|
||||
|
||||
void ProfileSaver::Stop(bool dump_info)
|
||||
{
|
||||
{
|
||||
os::memory::LockHolder lock(profile_saver_lock_);
|
||||
|
||||
if (instance == nullptr) {
|
||||
LOG(ERROR, RUNTIME) << "Tried to stop a profile saver which was not started";
|
||||
return;
|
||||
}
|
||||
|
||||
if (instance->shutting_down_) {
|
||||
LOG(ERROR, RUNTIME) << "Tried to stop the profile saver twice";
|
||||
return;
|
||||
}
|
||||
|
||||
instance->shutting_down_ = true;
|
||||
|
||||
if (dump_info) {
|
||||
instance->DumpInfo();
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for the saver thread to stop.
|
||||
// NB! we must release profile_saver_lock_ here.
|
||||
profiler_saver_daemon_thread.join();
|
||||
|
||||
// Kill the singleton ...
|
||||
delete instance;
|
||||
instance = nullptr;
|
||||
}
|
||||
|
||||
bool ProfileSaver::IsStarted()
|
||||
{
|
||||
return instance != nullptr;
|
||||
}
|
||||
|
||||
void ProfileSaver::DumpInfo()
|
||||
{
|
||||
LOG(INFO, RUNTIME) << "ProfileSaver stopped" << '\n';
|
||||
}
|
||||
|
||||
void *ProfileSaver::RunProfileSaverThread(void *arg)
|
||||
{
|
||||
// NOLINTNEXTLINE(hicpp-use-auto, modernize-use-auto)
|
||||
ProfileSaver *profile_saver = reinterpret_cast<ProfileSaver *>(arg);
|
||||
profile_saver->Run();
|
||||
|
||||
LOG(INFO, RUNTIME) << "Profile saver shutdown";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ProfileSaver::Run()
|
||||
{
|
||||
while (!ShuttingDown()) {
|
||||
LOG(INFO, RUNTIME) << "Step1: Time Sleeping >>>>>>> ";
|
||||
uint32_t sleepytime = Runtime::GetOptions().GetProfilesaverSleepingTimeMs();
|
||||
constexpr uint32_t TIME_SECONDS_IN_MS = 1000;
|
||||
for (int i = 0; i < static_cast<int>(sleepytime / TIME_SECONDS_IN_MS); ++i) {
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
if (ShuttingDown()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LOG(INFO, RUNTIME) << "Step2: tranverse the resolved class and methods >>>>>>> ";
|
||||
TranverseAndCacheResolvedClassAndMethods();
|
||||
|
||||
LOG(INFO, RUNTIME) << "Step3: merge current profile file and save it back >>>>>>> ";
|
||||
MergeAndDumpProfileData();
|
||||
}
|
||||
}
|
||||
|
||||
bool ProfileSaver::ShuttingDown()
|
||||
{
|
||||
os::memory::LockHolder lock(profile_saver_lock_);
|
||||
return shutting_down_;
|
||||
}
|
||||
|
||||
void ProfileSaver::TranverseAndCacheResolvedClassAndMethods()
|
||||
{
|
||||
trace::ScopedTrace scoped_trace(__PRETTY_FUNCTION__);
|
||||
PandaSet<ExtractedResolvedClasses> resolved_classes;
|
||||
PandaVector<ExtractedMethod> methods;
|
||||
auto call_back = [&resolved_classes, &methods](Class *klass) -> bool {
|
||||
const panda_file::File *pandafile = klass->GetPandaFile();
|
||||
panda_file::File::EntityId classfieldid = klass->GetFileId();
|
||||
|
||||
if (pandafile == nullptr) {
|
||||
LOG(INFO, RUNTIME) << "panda file is nullptr";
|
||||
return false;
|
||||
}
|
||||
LOG(INFO, RUNTIME) << " pandafile name = " << pandafile->GetFilename()
|
||||
<< " classname = " << klass->GetName();
|
||||
|
||||
Span<Method> tmp_methods = klass->GetMethods();
|
||||
LOG(INFO, RUNTIME) << " methods size = " << tmp_methods.size();
|
||||
for (int i = 0; i < static_cast<int>(tmp_methods.Size()); ++i) {
|
||||
Method &method = tmp_methods[i];
|
||||
if (!method.IsNative()) {
|
||||
if (method.GetHotnessCounter() >= K_MIN_PROFILE_THRESHOLD) {
|
||||
ASSERT(method.GetPandaFile() != nullptr);
|
||||
LOG(INFO, RUNTIME) << " method pandafile name = " << method.GetPandaFile()->GetFilename();
|
||||
methods.emplace_back(ExtractedMethod(method.GetPandaFile(), method.GetFileId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ExtractedResolvedClasses tmp_resolved_classes(ConvertToString(pandafile->GetFilename()),
|
||||
pandafile->GetHeader()->checksum);
|
||||
LOG(INFO, RUNTIME) << " Add class " << klass->GetName();
|
||||
auto it = resolved_classes.find(tmp_resolved_classes);
|
||||
if (it != resolved_classes.end()) {
|
||||
it->AddClass(classfieldid.GetOffset());
|
||||
} else {
|
||||
tmp_resolved_classes.AddClass(classfieldid.GetOffset());
|
||||
resolved_classes.insert(tmp_resolved_classes);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
// NB: classliner == nullptr
|
||||
if (Runtime::GetCurrent()->GetClassLinker() == nullptr) {
|
||||
LOG(INFO, RUNTIME) << "classliner is nullptr";
|
||||
return;
|
||||
}
|
||||
|
||||
LOG(INFO, RUNTIME) << " Step2.1: tranverse the resolved class and methods";
|
||||
Runtime::GetCurrent()->GetClassLinker()->EnumerateClasses(call_back);
|
||||
LOG(INFO, RUNTIME) << " Step2.2: starting tracking all the pandafile locations and flush the cache";
|
||||
|
||||
for (const auto &it : tracked_pandafile_base_locations_) {
|
||||
const PandaString &filename = it.first;
|
||||
const PandaSet<PandaString> &locations = it.second;
|
||||
|
||||
PandaSet<ExtractedResolvedClasses> resolved_classes_for_location;
|
||||
PandaVector<ExtractedMethod> methods_for_location;
|
||||
|
||||
LOG(INFO, RUNTIME) << " all the locations are:";
|
||||
for (auto const &iter : locations) {
|
||||
LOG(INFO, RUNTIME) << iter << " ";
|
||||
}
|
||||
|
||||
LOG(INFO, RUNTIME) << " Methods name : ";
|
||||
for (const ExtractedMethod &ref : methods) {
|
||||
LOG(INFO, RUNTIME) << " " << ref.panda_file_->GetFilename();
|
||||
if (locations.find(ConvertToString(ref.panda_file_->GetFilename())) != locations.end()) {
|
||||
LOG(INFO, RUNTIME) << " bingo method!";
|
||||
methods_for_location.push_back(ref);
|
||||
}
|
||||
}
|
||||
LOG(INFO, RUNTIME) << std::endl;
|
||||
LOG(INFO, RUNTIME) << " Classes name";
|
||||
|
||||
for (const ExtractedResolvedClasses &classes : resolved_classes) {
|
||||
LOG(INFO, RUNTIME) << " " << classes.GetPandaFileLocation();
|
||||
if (locations.find(classes.GetPandaFileLocation()) != locations.end()) {
|
||||
LOG(INFO, RUNTIME) << " bingo class!";
|
||||
resolved_classes_for_location.insert(classes);
|
||||
}
|
||||
}
|
||||
|
||||
ProfileDumpInfo *info = GetOrAddCachedProfiledInfo(filename);
|
||||
LOG(INFO, RUNTIME) << " Adding Bingo Methods and Classes";
|
||||
info->AddMethodsAndClasses(methods_for_location, resolved_classes_for_location);
|
||||
}
|
||||
}
|
||||
|
||||
ProfileDumpInfo *ProfileSaver::GetOrAddCachedProfiledInfo(const PandaString &filename)
|
||||
{
|
||||
auto info_it = profile_cache_.find(filename);
|
||||
if (info_it == profile_cache_.end()) {
|
||||
LOG(INFO, RUNTIME) << " bingo profile_cache_!";
|
||||
auto ret = profile_cache_.insert(std::make_pair(filename, ProfileDumpInfo()));
|
||||
ASSERT(ret.second);
|
||||
info_it = ret.first;
|
||||
}
|
||||
return &(info_it->second);
|
||||
}
|
||||
|
||||
ProfileSaver::CntStats *ProfileSaver::GetOrAddCachedProfiledStatsInfo(const PandaString &filename)
|
||||
{
|
||||
auto info_it = statcache.find(filename);
|
||||
if (info_it == statcache.end()) {
|
||||
LOG(INFO, RUNTIME) << " bingo StatsInfo_cache_!";
|
||||
auto ret = statcache.insert(std::make_pair(filename, CntStats()));
|
||||
ASSERT(ret.second);
|
||||
info_it = ret.first;
|
||||
}
|
||||
return &(info_it->second);
|
||||
}
|
||||
|
||||
void ProfileSaver::MergeAndDumpProfileData()
|
||||
{
|
||||
trace::ScopedTrace scoped_trace(__PRETTY_FUNCTION__);
|
||||
for (const auto &it : tracked_pandafile_base_locations_) {
|
||||
if (ShuttingDown()) {
|
||||
return;
|
||||
}
|
||||
const PandaString &filename = it.first;
|
||||
LOG(INFO, RUNTIME) << " Step3.1 starting merging and save the following file ***";
|
||||
LOG(INFO, RUNTIME) << " filename = " << filename;
|
||||
|
||||
ProfileDumpInfo *cached_info = GetOrAddCachedProfiledInfo(filename);
|
||||
CntStats *cached_stat = GetOrAddCachedProfiledStatsInfo(filename);
|
||||
ASSERT(cached_info->GetNumberOfMethods() >= cached_stat->GetMethodCount());
|
||||
ASSERT(cached_info->GetNumberOfResolvedClasses() >= cached_stat->GetClassCount());
|
||||
uint64_t delta_number_of_methods = cached_info->GetNumberOfMethods() - cached_stat->GetMethodCount();
|
||||
uint64_t delta_number_of_classes = cached_info->GetNumberOfResolvedClasses() - cached_stat->GetClassCount();
|
||||
uint64_t numthreshold = Runtime::GetOptions().GetProfilesaverDeltaNumberThreshold();
|
||||
if (delta_number_of_methods < numthreshold && delta_number_of_classes < numthreshold) {
|
||||
LOG(INFO, RUNTIME) << " number of delta number/class not enough";
|
||||
continue;
|
||||
}
|
||||
|
||||
uint64_t bytes_written;
|
||||
if (cached_info->MergeAndSave(filename, &bytes_written, true)) {
|
||||
cached_stat->SetMethodCount(cached_info->GetNumberOfMethods());
|
||||
cached_stat->SetClassCount(cached_info->GetNumberOfResolvedClasses());
|
||||
} else {
|
||||
LOG(INFO, RUNTIME) << "Could not save profiling info to " << filename;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace panda
|
||||
@@ -1,170 +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 PANDA_RUNTIME_PROFILESAVER_PROFILE_SAVER_H_
|
||||
#define PANDA_RUNTIME_PROFILESAVER_PROFILE_SAVER_H_
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "libpandabase/macros.h"
|
||||
#include "libpandabase/os/mutex.h"
|
||||
#include "libpandabase/utils/logger.h"
|
||||
#include "runtime/include/mem/panda_containers.h"
|
||||
#include "runtime/include/mem/panda_string.h"
|
||||
#include "runtime/profilesaver/profile_dump_info.h"
|
||||
|
||||
namespace panda {
|
||||
|
||||
// NOLINTNEXTLINE(fuchsia-statically-constructed-objects, readability-identifier-naming)
|
||||
static os::memory::Mutex profile_saver_lock_;
|
||||
|
||||
/*
|
||||
* Note well! we take singleton pattern, multi-threading scenario should be taken more serious considerations.
|
||||
*
|
||||
* e.g. Global Mutex Management or corresponding alternatives should better than
|
||||
* global mutex profile_saver_lock_.
|
||||
*
|
||||
*/
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-special-member-functions, hicpp-special-member-functions)
|
||||
class ProfileSaver {
|
||||
public:
|
||||
/*
|
||||
* start the profile saver deamon thread
|
||||
*
|
||||
* output_filename records the profile name, code_paths stores all the locations contain pandafile(aka *.aex)
|
||||
* app_data_dir contains the location of application package
|
||||
*/
|
||||
static void Start(const PandaString &output_filename, const PandaVector<PandaString> &code_paths,
|
||||
const PandaString &app_data_dir);
|
||||
|
||||
/*
|
||||
* stop the profile saver deamon thread.
|
||||
*
|
||||
* if dump_info == true, dumps the debug information
|
||||
*/
|
||||
static void Stop(bool dump_info);
|
||||
|
||||
/*
|
||||
* whether profile saver instance exists.
|
||||
*/
|
||||
static bool IsStarted();
|
||||
|
||||
~ProfileSaver() = default;
|
||||
|
||||
private:
|
||||
ProfileSaver(const PandaString &output_filename, const PandaVector<PandaString> &code_paths,
|
||||
const PandaString &app_dir);
|
||||
|
||||
void AddTrackedLocations(const PandaString &output_filename, const PandaVector<PandaString> &code_paths,
|
||||
const PandaString &app_data_dir);
|
||||
|
||||
/*
|
||||
* Callback for pthread_create, we attach/detach this thread to Runtime here
|
||||
*/
|
||||
static void *RunProfileSaverThread(void *arg);
|
||||
|
||||
/*
|
||||
* Run loop for the saver.
|
||||
*/
|
||||
void Run();
|
||||
|
||||
/*
|
||||
* Returns true if the saver is shutting down (ProfileSaver::Stop() has been called, will set this flag).
|
||||
*/
|
||||
bool ShuttingDown();
|
||||
|
||||
/*
|
||||
* Dump functions, we leave it stub and for test until now.
|
||||
*/
|
||||
void DumpInfo();
|
||||
|
||||
/*
|
||||
* Fetches the current resolved classes and methods from the ClassLinker and stores them in the profile_cache_.
|
||||
*/
|
||||
void TranverseAndCacheResolvedClassAndMethods();
|
||||
|
||||
/*
|
||||
* Retrieves the cached ProfileDumpInfo for the given profile filename.
|
||||
* If no entry exists, a new empty one will be created, added to the cache and then returned.
|
||||
*/
|
||||
ProfileDumpInfo *GetOrAddCachedProfiledInfo(const PandaString &filename);
|
||||
|
||||
/*
|
||||
* Processes the existing profiling info from the jit code cache(if exists) and returns
|
||||
* true if it needed to be saved back to disk.
|
||||
*/
|
||||
void MergeAndDumpProfileData();
|
||||
|
||||
static ProfileSaver *instance;
|
||||
|
||||
static std::thread profiler_saver_daemon_thread;
|
||||
|
||||
PandaMap<PandaString, PandaSet<PandaString>> tracked_pandafile_base_locations_;
|
||||
|
||||
PandaMap<PandaString, ProfileDumpInfo> profile_cache_;
|
||||
|
||||
PandaSet<PandaString> app_data_dirs_;
|
||||
|
||||
bool shutting_down_ GUARDED_BY(profile_saver_lock_);
|
||||
|
||||
struct CntStats {
|
||||
public:
|
||||
uint64_t GetMethodCount()
|
||||
{
|
||||
return last_save_number_of_methods_;
|
||||
}
|
||||
|
||||
void SetMethodCount(uint64_t methodcnt)
|
||||
{
|
||||
last_save_number_of_methods_ = methodcnt;
|
||||
}
|
||||
|
||||
uint64_t GetClassCount()
|
||||
{
|
||||
return last_save_number_of_classes_;
|
||||
}
|
||||
|
||||
void SetClassCount(uint64_t classcnt)
|
||||
{
|
||||
last_save_number_of_classes_ = classcnt;
|
||||
}
|
||||
|
||||
private:
|
||||
uint64_t last_save_number_of_methods_ {0};
|
||||
uint64_t last_save_number_of_classes_ {0};
|
||||
};
|
||||
|
||||
PandaMap<PandaString, CntStats> statcache; // NOLINT(readability-identifier-naming)
|
||||
|
||||
/*
|
||||
* Retrieves the cached CntStats for the given profile filename.
|
||||
* If no entry exists, a new empty one will be created, added to the cache and then returned.
|
||||
*/
|
||||
CntStats *GetOrAddCachedProfiledStatsInfo(const PandaString &filename);
|
||||
|
||||
NO_COPY_SEMANTIC(ProfileSaver);
|
||||
NO_MOVE_SEMANTIC(ProfileSaver);
|
||||
};
|
||||
|
||||
} // namespace panda
|
||||
|
||||
#endif // PANDA_RUNTIME_PROFILESAVER_PROFILE_SAVER_H_
|
||||
@@ -58,7 +58,6 @@
|
||||
#include "runtime/mem/internal_allocator-inl.h"
|
||||
#include "runtime/core/core_class_linker_extension.h"
|
||||
#include "runtime/include/panda_vm.h"
|
||||
#include "runtime/profilesaver/profile_saver.h"
|
||||
#include "runtime/tooling/debugger.h"
|
||||
#include "runtime/tooling/pt_lang_ext_private.h"
|
||||
#include "runtime/include/file_manager.h"
|
||||
@@ -307,12 +306,6 @@ bool Runtime::Destroy()
|
||||
trace::ScopedTrace scoped_trace("Runtime shutdown");
|
||||
instance->GetPandaVM()->StopGC();
|
||||
|
||||
// NB! stop the profile saver thread before deleting the thread list to avoid dead loop here.
|
||||
// the following WaitForThreadStop makes sure profile saver can be shut down.
|
||||
if (instance->SaveProfileInfo()) {
|
||||
ProfileSaver::Stop(false);
|
||||
}
|
||||
|
||||
instance->GetPandaVM()->UninitializeThreads();
|
||||
|
||||
verifier::JobQueue::Stop(instance->GetVerificationOptions().Mode.OnlyVerify);
|
||||
@@ -883,9 +876,6 @@ void Runtime::RegisterAppInfo(const PandaVector<PandaString> &code_paths, const
|
||||
}(profile_output_filename);
|
||||
|
||||
StartDProfiler(app_name);
|
||||
|
||||
// this is exactly the place where start the profile saver
|
||||
ProfileSaver::Start(profile_output_filename, code_paths, PandaString(app_name));
|
||||
}
|
||||
|
||||
int Runtime::StartDProfiler(std::string_view app_name)
|
||||
|
||||
Reference in New Issue
Block a user