Files
ark_js_runtime/ecmascript/dfx/cpu_profiler/cpu_profiler.cpp
T
wengchangcheng 0b4981525d refactor stl container
1. using runtime container instead of stl container
2. change debugger line amd column type to int32_t

issue: https://gitee.com/openharmony/ark_js_runtime/issues/I50NHW

Signed-off-by: wengchangcheng <wengchangcheng@huawei.com>
Change-Id: I0eb4651f17c6f6894f11de1ba904bcbe83a57db7
2022-04-06 16:05:22 +08:00

315 lines
11 KiB
C++

/*
* 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 "ecmascript/dfx/cpu_profiler/cpu_profiler.h"
#include <atomic>
#include <chrono>
#include <climits>
#include <csignal>
#include <fstream>
#include "ecmascript/jspandafile/js_pandafile_manager.h"
#include "ecmascript/taskpool/taskpool.h"
namespace panda::ecmascript {
CMap<JSMethod *, struct StackInfo> CpuProfiler::staticStackInfo_ = CMap<JSMethod *, struct StackInfo>();
std::atomic<CpuProfiler*> CpuProfiler::singleton_ = nullptr;
sem_t CpuProfiler::sem_ = sem_t {};
CMap<std::string, int> CpuProfiler::scriptIdMap_ = CMap<std::string, int>();
CVector<JSMethod *> CpuProfiler::staticFrameStack_ = CVector<JSMethod *>();
os::memory::Mutex CpuProfiler::synchronizationMutex_;
CpuProfiler::CpuProfiler()
{
generator_ = new ProfileGenerator();
if (sem_init(&sem_, 0, 1) != 0) {
LOG(ERROR, RUNTIME) << "sem_ init failed";
}
if (sem_wait(&sem_) != 0) {
LOG(ERROR, RUNTIME) << "sem_ wait failed";
}
}
CpuProfiler *CpuProfiler::GetInstance()
{
CpuProfiler *temp = singleton_;
if (temp == nullptr) {
os::memory::LockHolder lock(synchronizationMutex_);
if ((temp = CpuProfiler::singleton_) == nullptr) {
CpuProfiler::singleton_ = temp = new CpuProfiler();
}
}
return CpuProfiler::singleton_;
}
void CpuProfiler::StartCpuProfiler(const EcmaVM *vm, const std::string &fileName)
{
if (isOnly_) {
return;
}
isOnly_ = true;
std::string absoluteFilePath("");
if (!CheckFileName(fileName, absoluteFilePath)) {
LOG(ERROR, RUNTIME) << "The fileName contains illegal characters";
isOnly_ = false;
return;
}
fileName_ = absoluteFilePath;
if (fileName_.empty()) {
fileName_ = GetProfileName();
}
generator_->SetFileName(fileName_);
generator_->fileHandle_.open(fileName_.c_str());
if (generator_->fileHandle_.fail()) {
LOG(ERROR, RUNTIME) << "File open failed";
isOnly_ = false;
return;
}
#if ECMASCRIPT_ENABLE_ACTIVE_CPUPROFILER
#else
struct sigaction sa;
sa.sa_handler = &GetStackSignalHandler;
if (sigemptyset(&sa.sa_mask) != 0) {
LOG(ERROR, RUNTIME) << "Parameter set signal set initialization and emptying failed";
isOnly_ = false;
return;
}
sa.sa_flags = SA_RESTART;
if (sigaction(SIGINT, &sa, nullptr) != 0) {
LOG(ERROR, RUNTIME) << "sigaction failed to set signal";
isOnly_ = false;
return;
}
#endif
uint64_t ts = ProfileProcessor::GetMicrosecondsTimeStamp();
ts = ts % TIME_CHANGE;
SetProfileStart(ts);
Taskpool::GetCurrentTaskpool()->PostTask(std::make_unique<ProfileProcessor>(generator_, vm, interval_));
}
void CpuProfiler::StopCpuProfiler()
{
if (!isOnly_) {
LOG(ERROR, RUNTIME) << "Do not execute stop cpuprofiler twice in a row or didn't execute the start\
or the sampling thread is not started";
return;
}
if (static_cast<long>(tid_) != syscall(SYS_gettid)) {
LOG(ERROR, RUNTIME) << "Thread attempted to close other sampling threads";
return;
}
isOnly_ = false;
ProfileProcessor::SetIsStart(false);
if (sem_wait(&sem_) != 0) {
LOG(ERROR, RUNTIME) << "sem_ wait failed";
return;
}
generator_->WriteMethodsAndSampleInfo(true);
generator_->fileHandle_ << generator_->GetSampleData();
if (singleton_ != nullptr) {
delete singleton_;
singleton_ = nullptr;
}
}
CpuProfiler::~CpuProfiler()
{
if (sem_destroy(&sem_) != 0) {
LOG(ERROR, RUNTIME) << "sem_ destroy failed";
}
if (generator_ != nullptr) {
delete generator_;
generator_ = nullptr;
}
}
void CpuProfiler::SetProfileStart(uint64_t nowTimeStamp)
{
uint64_t ts = ProfileProcessor::GetMicrosecondsTimeStamp();
ts = ts % TIME_CHANGE;
struct CurrentProcessInfo currentProcessInfo = {0};
GetCurrentProcessInfo(currentProcessInfo);
std::string data = "";
data = "[{\"args\":{\"data\":{\"frames\":[{\"processId\":" + std::to_string(currentProcessInfo.pid) + "}]"
+ ",\"persistentIds\":true}},\"cat\":\"disabled-by-default-devtools.timeline\","
+ "\"name\":\"TracingStartedInBrowser\",\"ph\":\"I\",\"pid\":"
+ std::to_string(currentProcessInfo.pid) + ",\"s\":\"t\",\"tid\":"
+ std::to_string(currentProcessInfo.tid) + ",\"ts\":"
+ std::to_string(ts) + ",\"tts\":178460227},\n";
ts = ProfileProcessor::GetMicrosecondsTimeStamp();
ts = ts % TIME_CHANGE;
data += "{\"args\":{\"data\":{\"startTime\":" + std::to_string(nowTimeStamp) + "}},"
+ "\"cat\":\"disabled-by-default-ark.cpu_profiler\",\"id\":\"0x2\","
+ "\"name\":\"Profile\",\"ph\":\"P\",\"pid\":"
+ std::to_string(currentProcessInfo.pid) + ",\"tid\":"
+ std::to_string(currentProcessInfo.tid) + ",\"ts\":"
+ std::to_string(ts) + ",\"tts\":" + std::to_string(currentProcessInfo.tts)
+ "},\n";
generator_->SetStartsampleData(data);
}
void CpuProfiler::GetCurrentProcessInfo(struct CurrentProcessInfo &currentProcessInfo)
{
currentProcessInfo.nowTimeStamp = ProfileProcessor::GetMicrosecondsTimeStamp() % TIME_CHANGE;
currentProcessInfo.pid = getpid();
tid_ = currentProcessInfo.tid = syscall(SYS_gettid);
struct timespec time = {0, 0};
clock_gettime(CLOCK_MONOTONIC, &time);
currentProcessInfo.tts = time.tv_nsec / 1000; // 1000:Nanoseconds to milliseconds.
}
void CpuProfiler::GetFrameStack(JSThread *thread)
{
staticFrameStack_.clear();
ProfileGenerator::staticGcState_ = thread->GetGcState();
if (!ProfileGenerator::staticGcState_) {
JSTaggedType *sp_ = const_cast<JSTaggedType *>(thread->GetCurrentSPFrame());
InterpretedFrameHandler frameHandler(sp_);
for (; frameHandler.HasFrame(); frameHandler.PrevInterpretedFrame()) {
if (frameHandler.IsBreakFrame()) {
continue;
}
auto *method = frameHandler.GetMethod();
if (method != nullptr && staticStackInfo_.count(method) == 0) {
ParseMethodInfo(method, frameHandler);
}
staticFrameStack_.push_back(method);
}
}
}
void CpuProfiler::ParseMethodInfo(JSMethod *method, InterpretedFrameHandler frameHandler)
{
struct StackInfo codeEntry;
if (method != nullptr && method->IsNative()) {
codeEntry.codeType = "other";
codeEntry.functionName = "native";
staticStackInfo_.insert(std::make_pair(method, codeEntry));
} else if (method != nullptr) {
codeEntry.codeType = "JS";
const CString &functionName = method->ParseFunctionName();
if (functionName.empty()) {
codeEntry.functionName = "anonymous";
} else {
codeEntry.functionName = functionName.c_str();
}
// source file
tooling::ecmascript::JSPtExtractor *debugExtractor =
JSPandaFileManager::GetInstance()->GetJSPtExtractor(method->GetJSPandaFile());
const CString &sourceFile = debugExtractor->GetSourceFile(method->GetFileId());
if (sourceFile.empty()) {
codeEntry.url = "";
} else {
codeEntry.url = sourceFile.c_str();
auto iter = scriptIdMap_.find(codeEntry.url);
if (iter == scriptIdMap_.end()) {
scriptIdMap_.insert(std::make_pair(codeEntry.url, scriptIdMap_.size() + 1));
codeEntry.scriptId = scriptIdMap_.size();
} else {
codeEntry.scriptId = iter->second;
}
}
// line number
int32_t lineNumber = 0;
int32_t columnNumber = 0;
auto callbackFunc = [&](int32_t line, int32_t column) -> bool {
lineNumber = line + 1;
columnNumber = column + 1;
return true;
};
if (!debugExtractor->MatchWithOffset(callbackFunc, method->GetFileId(), frameHandler.GetBytecodeOffset())) {
codeEntry.lineNumber = 0;
codeEntry.columnNumber = 0;
} else {
codeEntry.lineNumber = lineNumber;
codeEntry.columnNumber = columnNumber;
}
staticStackInfo_.insert(std::make_pair(method, codeEntry));
}
}
void CpuProfiler::IsNeedAndGetStack(JSThread *thread)
{
if (thread->GetStackSignal()) {
GetFrameStack(thread);
if (sem_post(&CpuProfiler::sem_) != 0) {
LOG(ERROR, RUNTIME) << "sem_ post failed";
return;
}
thread->SetGetStackSignal(false);
}
}
void CpuProfiler::GetStackSignalHandler([[maybe_unused]] int signal)
{
JSThread *thread = ProfileProcessor::GetJSThread();
GetFrameStack(thread);
if (sem_post(&CpuProfiler::sem_) != 0) {
LOG(ERROR, RUNTIME) << "sem_ post failed";
return;
}
}
std::string CpuProfiler::GetProfileName() const
{
char time1[16] = {0}; // 16:Time format length
char time2[16] = {0}; // 16:Time format length
time_t timep = std::time(nullptr);
struct tm nowTime1;
localtime_r(&timep, &nowTime1);
size_t result = 0;
result = strftime(time1, sizeof(time1), "%Y%m%d", &nowTime1);
if (result == 0) {
LOG(ERROR, RUNTIME) << "get time failed";
return "";
}
result = strftime(time2, sizeof(time2), "%H%M%S", &nowTime1);
if (result == 0) {
LOG(ERROR, RUNTIME) << "get time failed";
return "";
}
std::string profileName = "cpuprofile-";
profileName += time1;
profileName += "TO";
profileName += time2;
profileName += ".json";
return profileName;
}
bool CpuProfiler::CheckFileName(const std::string &fileName, std::string &absoluteFilePath) const
{
if (fileName.empty()) {
return true;
}
if (fileName.size() > PATH_MAX) {
return false;
}
CVector<char> resolvedPath(PATH_MAX);
auto result = realpath(fileName.c_str(), resolvedPath.data());
if (result == nullptr) {
LOG(INFO, RUNTIME) << "The file path does not exist";
}
std::ofstream file(resolvedPath.data());
if (!file.good()) {
return false;
}
file.close();
absoluteFilePath = resolvedPath.data();
return true;
}
} // namespace panda::ecmascript