!3460 BugFix on Cpuprofiler long time recording deadlock

Merge pull request !3460 from chenjingxiang/deadlock
This commit is contained in:
openharmony_ci 2023-02-07 11:38:04 +00:00 committed by Gitee
commit 1dfc6ef552
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
9 changed files with 287 additions and 78 deletions

View File

@ -295,6 +295,7 @@ void CpuProfiler::GetFrameStack(FrameIterator &it)
{
const CMap<struct MethodKey, struct FrameInfo> &stackInfo = generator_->GetStackInfo();
bool topFrame = true;
generator_->ResetFrameLength();
for (; !it.Done(); it.Advance<>()) {
auto method = it.CheckAndGetMethod();
if (method == nullptr) {
@ -328,10 +329,12 @@ void CpuProfiler::GetFrameStack(FrameIterator &it)
return;
}
}
generator_->PostFrame();
}
bool CpuProfiler::GetFrameStackCallNapi(JSThread *thread)
{
[[maybe_unused]] CallNapiScope scope(generator_);
const CMap<struct MethodKey, struct FrameInfo> &stackInfo = generator_->GetStackInfo();
generator_->ClearNapiStack();
bool topFrame = true;
@ -371,6 +374,7 @@ bool CpuProfiler::GetFrameStackCallNapi(JSThread *thread)
return false;
}
}
generator_->PostNapiFrame();
return true;
}
@ -483,17 +487,7 @@ void CpuProfiler::GetNativeStack(const FrameIterator &it, char *functionName, si
void CpuProfiler::GetStackBeforeCallNapi(JSThread *thread, const std::string &methodAddr)
{
generator_->SetBeforeGetCallNapiStackFlag(true);
if (GetFrameStackCallNapi(thread)) {
generator_->SetCallNapiFlag(true);
generator_->SetAfterGetCallNapiStackFlag(true);
if (generator_->SemWait(2) != 0) { // 2: signal 2
LOG_ECMA(ERROR) << "sem_[2] wait failed";
return;
}
} else {
generator_->SetBeforeGetCallNapiStackFlag(false);
}
GetFrameStackCallNapi(thread);
RecordCallNapiInfo(methodAddr);
}

View File

@ -64,10 +64,26 @@ public:
thread_->SetRuntimeState(oldState_);
}
private:
bool oldState_;
bool oldState_ = false;
JSThread *thread_ = nullptr;
};
class CallNapiScope {
public:
inline explicit CallNapiScope(SamplesRecord *generator)
{
generator_ = generator;
generator_->SetFrameStackCallNapi(true);
}
inline ~CallNapiScope()
{
generator_->SetFrameStackCallNapi(false);
}
private:
SamplesRecord *generator_ = nullptr;
};
class CpuProfiler {
public:
bool InHeaderOrTail(uint64_t pc, uint64_t entryBegin, uint64_t entryDuration, uint64_t headerSize,

View File

@ -45,6 +45,7 @@ SamplesRecord::SamplesRecord()
if (tid != -1) {
profileInfo_->tid = static_cast<uint64_t>(tid);
}
samplesQueue_ = new SamplesQueue();
}
SamplesRecord::~SamplesRecord()
@ -52,24 +53,23 @@ SamplesRecord::~SamplesRecord()
if (fileHandle_.is_open()) {
fileHandle_.close();
}
if (samplesQueue_ != nullptr) {
delete samplesQueue_;
}
}
void SamplesRecord::AddSample(uint64_t sampleTimeStamp)
void SamplesRecord::AddSample(FrameStackAndInfo *frame)
{
if (isBreakSample_.load()) {
frameStackLength_ = 0;
frameInfoTempLength_ = 0;
return;
}
FrameInfoTempToMap();
FrameInfoTempToMap(frame->frameInfoTemps, frame->frameInfoTempsLength);
struct NodeKey nodeKey;
struct CpuProfileNode methodNode;
if (frameStackLength_ != 0) {
frameStackLength_--;
int frameStackLength = frame->frameStackLength;
if (frameStackLength != 0) {
frameStackLength--;
}
methodNode.id = 1;
for (; frameStackLength_ >= 1; frameStackLength_--) {
nodeKey.methodKey = frameStack_[frameStackLength_ - 1];
for (; frameStackLength >= 1; frameStackLength--) {
nodeKey.methodKey = frame->frameStack[frameStackLength - 1];
methodNode.parentId = nodeKey.parentId = methodNode.id;
auto result = nodeMap_.find(nodeKey);
if (result == nodeMap_.end()) {
@ -86,14 +86,14 @@ void SamplesRecord::AddSample(uint64_t sampleTimeStamp)
}
int sampleNodeId = previousId_ == 0 ? 1 : methodNode.id;
int timeDelta = static_cast<int>(sampleTimeStamp -
int timeDelta = static_cast<int>(frame->timeStamp -
(threadStartTime_ == 0 ? profileInfo_->startTime : threadStartTime_));
StatisticStateTime(timeDelta, previousState_);
previousState_ = nodeKey.methodKey.state;
profileInfo_->nodes[sampleNodeId - 1].hitCount++;
profileInfo_->samples.push_back(sampleNodeId);
profileInfo_->timeDeltas.push_back(timeDelta);
threadStartTime_ = sampleTimeStamp;
threadStartTime_ = frame->timeStamp;
}
void SamplesRecord::AddSampleCallNapi(uint64_t *sampleTimeStamp)
@ -653,14 +653,14 @@ bool SamplesRecord::PushNapiStackInfo(const FrameInfoTemp &frameInfoTemp)
return true;
}
void SamplesRecord::FrameInfoTempToMap()
void SamplesRecord::FrameInfoTempToMap(FrameInfoTemp *frameInfoTemps, int frameInfoTempLength)
{
if (frameInfoTempLength_ == 0) {
if (frameInfoTempLength == 0) {
return;
}
struct FrameInfo frameInfo;
for (int i = 0; i < frameInfoTempLength_; ++i) {
frameInfo.url = frameInfoTemps_[i].url;
for (int i = 0; i < frameInfoTempLength; ++i) {
frameInfo.url = frameInfoTemps[i].url;
auto iter = scriptIdMap_.find(frameInfo.url);
if (iter == scriptIdMap_.end()) {
scriptIdMap_.emplace(frameInfo.url, scriptIdMap_.size() + 1);
@ -668,13 +668,13 @@ void SamplesRecord::FrameInfoTempToMap()
} else {
frameInfo.scriptId = iter->second;
}
frameInfo.functionName = AddRunningState(frameInfoTemps_[i].functionName,
frameInfoTemps_[i].methodKey.state,
frameInfoTemps_[i].methodKey.deoptType);
frameInfo.codeType = frameInfoTemps_[i].codeType;
frameInfo.columnNumber = frameInfoTemps_[i].columnNumber;
frameInfo.lineNumber = frameInfoTemps_[i].lineNumber;
stackInfoMap_.emplace(frameInfoTemps_[i].methodKey, frameInfo);
frameInfo.functionName = AddRunningState(frameInfoTemps[i].functionName,
frameInfoTemps[i].methodKey.state,
frameInfoTemps[i].methodKey.deoptType);
frameInfo.codeType = frameInfoTemps[i].codeType;
frameInfo.columnNumber = frameInfoTemps[i].columnNumber;
frameInfo.lineNumber = frameInfoTemps[i].lineNumber;
stackInfoMap_.emplace(frameInfoTemps[i].methodKey, frameInfo);
}
frameInfoTempLength_ = 0;
}
@ -719,4 +719,168 @@ void SamplesRecord::RecordCallNapiAddr(const std::string &methodAddr)
{
napiCallAddrVec_.emplace_back(methodAddr);
}
void SamplesRecord::PostFrame()
{
samplesQueue_->PostFrame(frameInfoTemps_, frameStack_, frameInfoTempLength_, frameStackLength_);
}
void SamplesRecord::PostNapiFrame()
{
samplesQueue_->PostNapiFrame(napiFrameInfoTemps_, napiFrameStack_);
}
void SamplesRecord::ResetFrameLength()
{
frameStackLength_ = 0;
frameInfoTempLength_ = 0;
}
void SamplesRecord::SetFrameStackCallNapi(bool flag)
{
samplesQueue_->SetFrameStackCallNapi(flag);
}
uint64_t SamplesRecord::GetCallTimeStamp()
{
return callTimeStamp_;
}
void SamplesRecord::SetCallTimeStamp(uint64_t timeStamp)
{
callTimeStamp_ = timeStamp;
}
// SamplesQueue
void SamplesQueue::PostFrame(FrameInfoTemp *frameInfoTemps, MethodKey *frameStack,
int frameInfoTempsLength, int frameStackLength)
{
if (GetFrameStackCallNapi()) {
return;
}
os::memory::LockHolder holder(mtx_);
if (!IsFull()) {
// frameInfoTemps
for (int i = 0; i < frameInfoTempsLength; i++) {
CheckAndCopy(frameInfoTemps[i].functionName,
sizeof(frameInfoTemps[i].functionName), frameInfoTemps[i].functionName);
frames_[rear_].frameInfoTemps[i].columnNumber = frameInfoTemps[i].columnNumber;
frames_[rear_].frameInfoTemps[i].lineNumber = frameInfoTemps[i].lineNumber;
frames_[rear_].frameInfoTemps[i].scriptId = frameInfoTemps[i].scriptId;
CheckAndCopy(frameInfoTemps[i].url,
sizeof(frameInfoTemps[i].url), frameInfoTemps[i].url);
frames_[rear_].frameInfoTemps[i].methodKey.methodIdentifier = frameInfoTemps[i].methodKey.methodIdentifier;
frames_[rear_].frameInfoTemps[i].methodKey.state = frameInfoTemps[i].methodKey.state;
frames_[rear_].frameInfoTemps[i].methodKey.napiCallCount = frameInfoTemps[i].methodKey.napiCallCount;
}
// frameStack
for (int i = 0; i < frameStackLength; i++) {
frames_[rear_].frameStack[i].methodIdentifier = frameStack[i].methodIdentifier;
frames_[rear_].frameStack[i].state = frameStack[i].state;
frames_[rear_].frameStack[i].napiCallCount = frameStack[i].napiCallCount;
}
// frameStackLength
frames_[rear_].frameStackLength = frameStackLength;
// frameInfoTempsLength
frames_[rear_].frameInfoTempsLength = frameInfoTempsLength;
// timeStamp
frames_[rear_].timeStamp = SamplingProcessor::GetMicrosecondsTimeStamp();
rear_ = (rear_ + 1) % QUEUE_CAPACITY;
}
}
void SamplesQueue::PostNapiFrame(CVector<FrameInfoTemp> &napiFrameInfoTemps, CVector<MethodKey> &napiFrameStack)
{
os::memory::LockHolder holder(mtx_);
if (!IsFull()) {
int frameInfoTempsLength = napiFrameInfoTemps.size();
int frameStackLength = napiFrameStack.size();
// napiFrameInfoTemps
for (int i = 0; i < frameInfoTempsLength; i++) {
CheckAndCopy(frames_[rear_].frameInfoTemps[i].functionName,
sizeof(frames_[rear_].frameInfoTemps[i].functionName), napiFrameInfoTemps[i].functionName);
frames_[rear_].frameInfoTemps[i].columnNumber = napiFrameInfoTemps[i].columnNumber;
frames_[rear_].frameInfoTemps[i].lineNumber = napiFrameInfoTemps[i].lineNumber;
frames_[rear_].frameInfoTemps[i].scriptId = napiFrameInfoTemps[i].scriptId;
CheckAndCopy(frames_[rear_].frameInfoTemps[i].url,
sizeof(frames_[rear_].frameInfoTemps[i].url), napiFrameInfoTemps[i].url);
frames_[rear_].frameInfoTemps[i].methodKey.methodIdentifier =
napiFrameInfoTemps[i].methodKey.methodIdentifier;
frames_[rear_].frameInfoTemps[i].methodKey.state = napiFrameInfoTemps[i].methodKey.state;
frames_[rear_].frameInfoTemps[i].methodKey.napiCallCount = napiFrameInfoTemps[i].methodKey.napiCallCount;
}
// napiFrameStack
for (int i = 0; i < frameStackLength; i++) {
frames_[rear_].frameStack[i].methodIdentifier = napiFrameStack[i].methodIdentifier;
frames_[rear_].frameStack[i].state = napiFrameStack[i].state;
frames_[rear_].frameStack[i].napiCallCount = napiFrameStack[i].napiCallCount;
}
// frameStackLength
frames_[rear_].frameStackLength = frameStackLength;
// frameInfoTempsLength
frames_[rear_].frameInfoTempsLength = frameInfoTempsLength;
// timeStamp
frames_[rear_].timeStamp = SamplingProcessor::GetMicrosecondsTimeStamp();
rear_ = (rear_ + 1) % QUEUE_CAPACITY;
}
}
FrameStackAndInfo *SamplesQueue::PopFrame()
{
os::memory::LockHolder holder(mtx_);
if (!IsEmpty()) {
FrameStackAndInfo *frame = &frames_[front_];
front_ = (front_ + 1) % QUEUE_CAPACITY;
return frame;
}
return nullptr;
}
bool SamplesQueue::IsEmpty()
{
return front_ == rear_;
}
bool SamplesQueue::IsFull()
{
return (rear_ + 1) % QUEUE_CAPACITY == front_;
}
int SamplesQueue::GetSize()
{
return (rear_ + QUEUE_CAPACITY - front_) % QUEUE_CAPACITY;
}
int SamplesQueue::GetFront()
{
return front_;
}
int SamplesQueue::GetRear()
{
return rear_;
}
void SamplesQueue::SetFrameStackCallNapi(bool flag)
{
isFrameStackCallNapi.store(flag);
}
bool SamplesQueue::GetFrameStackCallNapi()
{
return isFrameStackCallNapi.load();
}
bool SamplesQueue::CheckAndCopy(char *dest, size_t length, const char *src) const
{
int srcLength = strlen(src);
if (length <= static_cast<size_t>(srcLength) || strcpy_s(dest, srcLength + 1, src) != EOK) {
LOG_ECMA(ERROR) << "SamplesQueue PostFrame strcpy_s failed, maybe srcLength more than destLength";
return false;
}
dest[srcLength] = '\0';
return true;
}
} // namespace panda::ecmascript

View File

@ -18,8 +18,8 @@
#include <atomic>
#include <ctime>
#include <fstream>
#include <cstring>
#include <fstream>
#include <semaphore.h>
#include "ecmascript/compiler/gate_meta_data.h"
@ -27,11 +27,13 @@
#include "ecmascript/js_thread.h"
#include "ecmascript/jspandafile/method_literal.h"
#include "ecmascript/mem/c_containers.h"
#include "libpandabase/os/mutex.h"
namespace panda::ecmascript {
const int MAX_ARRAY_COUNT = 100; // 100:the maximum size of the array
const int MAX_NODE_COUNT = 10000; // 10000:the maximum size of the array
const int MIN_TIME_DELTA = 10; // 10: the minimum value of the time delta
const int QUEUE_CAPACITY = 11; // the capacity of the circular queue is QUEUE_CAPACITY - 1
const size_t NAPI_CALL_SETP = 2; // 2: step size of the variable napiCallIdx in while loop
const size_t PRE_IDX_RANGE = 5; // 5: length of variable preIdx looping backward
enum class RunningState : size_t {
@ -119,12 +121,49 @@ struct FrameInfoTemp {
struct MethodKey methodKey = {};
};
struct FrameStackAndInfo {
struct FrameInfoTemp frameInfoTemps[MAX_ARRAY_COUNT] = {};
struct MethodKey frameStack[MAX_ARRAY_COUNT] = {};
int frameInfoTempsLength;
int frameStackLength;
uint64_t timeStamp;
};
class SamplesQueue {
public:
SamplesQueue() = default;
~SamplesQueue() = default;
NO_COPY_SEMANTIC(SamplesQueue);
NO_MOVE_SEMANTIC(SamplesQueue);
void PostFrame(FrameInfoTemp *frameInfoTemps, MethodKey *frameStack,
int frameInfoTempsLength, int frameStackLength);
void PostNapiFrame(CVector<FrameInfoTemp> &napiFrameInfoTemps, CVector<MethodKey> &napiFrameStack);
FrameStackAndInfo *PopFrame();
bool IsEmpty();
bool IsFull();
int GetSize();
int GetFront();
int GetRear();
void SetFrameStackCallNapi(bool flag);
bool GetFrameStackCallNapi();
bool CheckAndCopy(char *dest, size_t length, const char *src) const;
private:
FrameStackAndInfo frames_[QUEUE_CAPACITY] = {};
int front_ = 0;
int rear_ = 0;
std::atomic_bool isFrameStackCallNapi = false;
os::memory::Mutex mtx_;
};
class SamplesRecord {
public:
SamplesRecord();
virtual ~SamplesRecord();
void AddSample(uint64_t sampleTimeStamp);
void AddSample(FrameStackAndInfo *frame);
void AddSampleCallNapi(uint64_t *sampleTimeStamp);
void StringifySampleData();
int GetMethodNodeCount() const;
@ -169,7 +208,14 @@ public:
void FindSampleAndFinetune(size_t findIdx, size_t napiCallIdx, size_t &sampleIdx,
uint64_t startSampleTime, uint64_t &sampleTime);
void FinetuneTimeDeltas(size_t idx, uint64_t napiTime, uint64_t &sampleTime, bool isEndSample);
void PostFrame();
void PostNapiFrame();
void ResetFrameLength();
void SetFrameStackCallNapi(bool flag);
uint64_t GetCallTimeStamp();
void SetCallTimeStamp(uint64_t timeStamp);
std::ofstream fileHandle_;
SamplesQueue *samplesQueue_ {nullptr};
void SetEnableVMTag(bool flag)
{
@ -182,7 +228,7 @@ private:
void StringifySamples();
struct FrameInfo GetMethodInfo(struct MethodKey &methodKey);
std::string AddRunningState(char *functionName, RunningState state, kungfu::DeoptType type);
void FrameInfoTempToMap();
void FrameInfoTempToMap(FrameInfoTemp *frameInfoTemps, int frameInfoTempLength);
void NapiFrameInfoTempToMap();
void StatisticStateTime(int timeDelta, RunningState state);
@ -214,6 +260,7 @@ private:
CVector<uint64_t> napiCallTimeVec_;
CVector<std::string> napiCallAddrVec_;
bool enableVMTag_ {false};
uint64_t callTimeStamp_ = 0;
};
} // namespace panda::ecmascript
#endif // ECMASCRIPT_SAMPLES_RECORD_H

View File

@ -45,43 +45,27 @@ bool SamplingProcessor::Run([[maybe_unused]] uint32_t threadIndex)
uint64_t endTime = startTime;
generator_->SetThreadStartTime(startTime);
while (generator_->GetIsStart()) {
if (generator_->GetBeforeGetCallNapiStackFlag()) {
generator_->SetBeforeGetCallNapiStackFlag(false);
while (!generator_->GetAfterGetCallNapiStackFlag()) {
usleep(10); // 10: sleep 10 us;
}
generator_->SetAfterGetCallNapiStackFlag(false);
if (pthread_kill(pid_, SIGPROF) != 0) {
LOG(ERROR, RUNTIME) << "pthread_kill signal failed";
return false;
}
if (!generator_->GetCallNapiFlag()) {
if (pthread_kill(pid_, SIGPROF) != 0) {
LOG(ERROR, RUNTIME) << "pthread_kill signal failed";
return false;
}
if (generator_->SemWait(0) != 0) {
LOG_ECMA(ERROR) << "sem_[0] wait failed";
return false;
}
startTime = GetMicrosecondsTimeStamp();
int64_t ts = static_cast<int64_t>(interval_) - static_cast<int64_t>(startTime - endTime);
endTime = startTime;
if (ts > 0 && !generator_->GetBeforeGetCallNapiStackFlag() && !generator_->GetCallNapiFlag()) {
usleep(ts);
endTime = GetMicrosecondsTimeStamp();
}
if (generator_->GetMethodNodeCount() + generator_->GetframeStackLength() >= MAX_NODE_COUNT) {
break;
}
generator_->AddSample(endTime);
} else {
if (generator_->GetMethodNodeCount() + generator_->GetNapiFrameStackLength() >= MAX_NODE_COUNT) {
break;
}
generator_->AddSampleCallNapi(&endTime);
generator_->SetCallNapiFlag(false);
if (generator_->SemPost(2) != 0) { // 2: signal 2
LOG_ECMA(ERROR) << "sem_[2] post failed";
return false;
}
if (generator_->SemWait(0) != 0) {
LOG_ECMA(ERROR) << "sem_[0] wait failed";
return false;
}
startTime = GetMicrosecondsTimeStamp();
int64_t ts = static_cast<int64_t>(interval_) - static_cast<int64_t>(startTime - endTime);
endTime = startTime;
if (ts > 0) {
usleep(ts);
endTime = GetMicrosecondsTimeStamp();
}
if (generator_->GetMethodNodeCount() + generator_->GetframeStackLength() >= MAX_NODE_COUNT) {
break;
}
while (!generator_->samplesQueue_->IsEmpty()) {
FrameStackAndInfo *frame = generator_->samplesQueue_->PopFrame();
generator_->AddSample(frame);
}
generator_->SetIsBreakSampleFlag(false);
}

View File

@ -48,7 +48,7 @@ int FrameIterator::GetCallSiteDelta(uintptr_t returnAddr) const
Method *FrameIterator::CheckAndGetMethod() const
{
auto function = GetFunction();
if (function.IsJSFunctionBase() || function.IsJSProxy()) {
if (function.CheckIsJSFunctionBase() || function.IsJSProxy()) {
return ECMAObject::Cast(function.GetTaggedObject())->GetCallTarget();
}
return nullptr;

View File

@ -966,6 +966,11 @@ inline bool JSTaggedValue::IsJSFunctionBase() const
return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSFunctionBase();
}
inline bool JSTaggedValue::CheckIsJSFunctionBase() const
{
return IsHeapObject() && GetTaggedObject() != nullptr && GetTaggedObject()->GetClass()->IsJSFunctionBase();
}
inline bool JSTaggedValue::IsBoundFunction() const
{
return IsHeapObject() && GetTaggedObject()->GetClass()->IsJsBoundFunction();

View File

@ -545,6 +545,7 @@ public:
bool IsClassPrototype() const;
bool IsJSFunction() const;
bool IsJSFunctionBase() const;
bool CheckIsJSFunctionBase() const;
bool IsECMAObject() const;
bool IsJSPrimitiveRef() const;
bool IsJSPrimitive() const;

View File

@ -272,12 +272,10 @@ void DFXJSNApi::SetCpuSamplingInterval(const EcmaVM *vm, int interval)
CpuProfiler *profiler = vm->GetProfiler();
if (profiler == nullptr) {
profiler = new CpuProfiler(vm, interval);
profiler->SetCallNapiGetStack(false);
const_cast<EcmaVM *>(vm)->SetProfiler(profiler);
return;
}
profiler->SetCpuSamplingInterval(interval);
profiler->SetCallNapiGetStack(false);
}
#endif