!1740 heapprofiler support traceAllocation

Merge pull request !1740 from Riachel/master
This commit is contained in:
openharmony_ci
2022-07-09 15:17:20 +00:00
committed by Gitee
11 changed files with 371 additions and 26 deletions
+6 -6
View File
@@ -55,13 +55,13 @@ bool HeapProfiler::DumpHeapSnapshot(DumpFormat dumpFormat, Stream *stream, Progr
return jsonSerializer_->Serialize(snapshot, stream);
}
bool HeapProfiler::StartHeapTracking(double timeInterval, bool isVmMode, Stream *stream)
bool HeapProfiler::StartHeapTracking(double timeInterval, bool isVmMode, Stream *stream, bool traceAllocation)
{
HeapSnapshot *snapshot = MakeHeapSnapshot(SampleType::REAL_TIME, isVmMode);
HeapSnapshot *snapshot = MakeHeapSnapshot(SampleType::REAL_TIME, isVmMode, false, traceAllocation);
if (snapshot == nullptr) {
return false;
}
heapTracker_ = std::make_unique<HeapTracker>(snapshot, timeInterval, stream);
const_cast<EcmaVM *>(vm_)->StartHeapTracking(heapTracker_.get());
heapTracker_->StartTracing();
@@ -150,7 +150,7 @@ bool HeapProfiler::ForceFullGC(const EcmaVM *vm)
return false;
}
HeapSnapshot *HeapProfiler::MakeHeapSnapshot(SampleType sampleType, bool isVmMode, bool isPrivate)
HeapSnapshot *HeapProfiler::MakeHeapSnapshot(SampleType sampleType, bool isVmMode, bool isPrivate, bool traceAllocation)
{
LOG_ECMA(ERROR) << "HeapProfiler::MakeHeapSnapshot";
DISALLOW_GARBAGE_COLLECTION;
@@ -158,7 +158,7 @@ HeapSnapshot *HeapProfiler::MakeHeapSnapshot(SampleType sampleType, bool isVmMod
switch (sampleType) {
case SampleType::ONE_SHOT: {
auto *snapshot = const_cast<NativeAreaAllocator *>(vm_->GetNativeAreaAllocator())
->New<HeapSnapshot>(vm_, isVmMode, isPrivate);
->New<HeapSnapshot>(vm_, isVmMode, isPrivate, traceAllocation);
if (snapshot == nullptr) {
LOG_FULL(FATAL) << "alloc snapshot failed";
UNREACHABLE();
@@ -169,7 +169,7 @@ HeapSnapshot *HeapProfiler::MakeHeapSnapshot(SampleType sampleType, bool isVmMod
}
case SampleType::REAL_TIME: {
auto *snapshot = const_cast<NativeAreaAllocator *>(vm_->GetNativeAreaAllocator())
->New<HeapSnapshot>(vm_, isVmMode, isPrivate);
->New<HeapSnapshot>(vm_, isVmMode, isPrivate, traceAllocation);
if (snapshot == nullptr) {
LOG_FULL(FATAL) << "alloc snapshot failed";
UNREACHABLE();
+4 -2
View File
@@ -53,7 +53,8 @@ public:
void AddSnapshot(HeapSnapshot *snapshot);
bool StartHeapTracking(double timeInterval, bool isVmMode = true, Stream *stream = nullptr) override;
bool StartHeapTracking(double timeInterval, bool isVmMode = true,
Stream *stream = nullptr, bool traceAllocation = false) override;
bool StopHeapTracking(Stream *stream, Progress *progress = nullptr) override;
private:
@@ -65,7 +66,8 @@ private:
/**
* make a new heap snapshot and put it into a container eg, vector
*/
HeapSnapshot *MakeHeapSnapshot(SampleType sampleType, bool isVmMode = true, bool isPrivate = false);
HeapSnapshot *MakeHeapSnapshot(SampleType sampleType, bool isVmMode = true,
bool isPrivate = false, bool traceAllocation = false);
std::string GenDumpFileName(DumpFormat dumpFormat);
CString GetTimeStamp();
void ClearSnapshot();
@@ -35,7 +35,8 @@ public:
virtual bool DumpHeapSnapshot(DumpFormat dumpFormat, Stream *stream, Progress *progress = nullptr,
bool isVmMode = true, bool isPrivate = false) = 0;
virtual bool StartHeapTracking(double timeInterval, bool isVmMode = true, Stream *stream = nullptr) = 0;
virtual bool StartHeapTracking(double timeInterval, bool isVmMode = true,
Stream *stream = nullptr, bool traceAllocation = false) = 0;
virtual bool StopHeapTracking(Stream *stream, Progress *progress = nullptr) = 0;
NO_MOVE_SEMANTIC(HeapProfilerInterface);
+166 -3
View File
@@ -33,6 +33,7 @@
#include "ecmascript/property_attributes.h"
#include "ecmascript/tagged_array.h"
#include "ecmascript/tagged_dictionary.h"
#include "ecmascript/jspandafile/js_pandafile_manager.h"
namespace panda::ecmascript {
CString *HeapSnapshot::GetString(const CString &as)
@@ -72,6 +73,11 @@ HeapSnapshot::~HeapSnapshot()
}
nodes_.clear();
edges_.clear();
traceInfoStack_.clear();
stackInfo_.clear();
scriptIdMap_.clear();
methodToTraceNodeId_.clear();
traceNodeIndex_.clear();
}
bool HeapSnapshot::BuildUp()
@@ -91,6 +97,9 @@ bool HeapSnapshot::Verify()
void HeapSnapshot::PrepareSnapshot()
{
FillNodes();
if (trackAllocations()) {
PrepareTraceInfo();
}
}
void HeapSnapshot::UpdateNode()
@@ -162,20 +171,23 @@ void HeapSnapshot::PushHeapStat(Stream* stream)
stream->UpdateLastSeenObjectId(sequenceId_);
}
void HeapSnapshot::AddNode(TaggedObject* address)
Node *HeapSnapshot::AddNode(TaggedObject* address)
{
GenerateNode(JSTaggedValue(address));
return GenerateNode(JSTaggedValue(address));
}
void HeapSnapshot::MoveNode(uintptr_t address, TaggedObject* forward_address)
{
int sequenceId = -1;
int traceNodeId = 0;
Node *node = entryMap_.FindAndEraseNode(address);
if (node != nullptr) {
sequenceId = static_cast<int>(node->GetId());
traceNodeId = node->GetStackTraceId();
EraseNodeUnique(node);
}
GenerateNode(JSTaggedValue(forward_address), sequenceId);
node = GenerateNode(JSTaggedValue(forward_address), sequenceId);
node->SetTraceId(traceNodeId);
}
// NOLINTNEXTLINE(readability-function-size)
@@ -599,6 +611,157 @@ Node *HeapSnapshot::GenerateNode(JSTaggedValue entry, int sequenceId)
return node;
}
TraceNode::TraceNode(TraceTree* tree, uint32_t nodeIndex)
: tree_(tree),
nodeIndex_(nodeIndex),
totalSize_(0),
totalCount_(0),
id_(tree->GetNextNodeId())
{
}
TraceNode::~TraceNode()
{
for (TraceNode* node : children_) {
delete node;
}
children_.clear();
}
TraceNode* TraceTree::AddNodeToTree(CVector<uint32_t> traceNodeIndex)
{
uint32_t len = traceNodeIndex.size();
if (len == 0) {
return nullptr;
}
TraceNode* node = GetRoot();
for (int i = len - 1; i >= 0; i--) {
node = node->FindOrAddChild(traceNodeIndex[i]);
}
return node;
}
TraceNode* TraceNode::FindOrAddChild(uint32_t nodeIndex)
{
TraceNode* child = FindChild(nodeIndex);
if (child == nullptr) {
child = new TraceNode(tree_, nodeIndex);
children_.push_back(child);
}
return child;
}
TraceNode* TraceNode::FindChild(uint32_t nodeIndex)
{
for (TraceNode* node : children_) {
if (node->GetNodeIndex() == nodeIndex) {
return node;
}
}
return nullptr;
}
void HeapSnapshot::AddTraceNodeId(JSMethod *method)
{
uint32_t traceNodeId = 0;
auto result = methodToTraceNodeId_.find(method);
if (result == methodToTraceNodeId_.end()) {
traceNodeId = traceInfoStack_.size() - 1;
methodToTraceNodeId_.insert(std::make_pair(method, traceNodeId));
} else {
traceNodeId = result->second;
}
traceNodeIndex_.emplace_back(traceNodeId);
}
int HeapSnapshot::AddTraceNode(int sequenceId, int size)
{
traceNodeIndex_.clear();
FrameHandler frameHandler(vm_->GetAssociatedJSThread());
for (; frameHandler.HasFrame(); frameHandler.PrevInterpretedFrame()) {
if (!frameHandler.IsInterpretedFrame()) {
continue;
}
auto method = frameHandler.CheckAndGetMethod();
if (method == nullptr || method->IsNativeWithCallField()) {
continue;
}
if (stackInfo_.count(method) == 0) {
AddMethodInfo(method, frameHandler, sequenceId);
}
AddTraceNodeId(method);
}
TraceNode* topNode = traceTree_.AddNodeToTree(traceNodeIndex_);
if (topNode == nullptr) {
return -1;
}
int totalSize = topNode->GetTotalSize();
totalSize += size;
topNode->SetTotalSize(totalSize);
int totalCount = topNode->GetTotalCount();
topNode->SetTotalCount(++totalCount);
return topNode->GetId();
}
void HeapSnapshot::AddMethodInfo(JSMethod *method, const FrameHandler &frameHandler, int sequenceId)
{
struct FunctionInfo codeEntry;
codeEntry.functionId = sequenceId;
const std::string &functionName = method->ParseFunctionName();
if (functionName.empty()) {
codeEntry.functionName = "anonymous";
} else {
codeEntry.functionName = functionName;
}
GetString(codeEntry.functionName.c_str());
// source file
tooling::JSPtExtractor *debugExtractor =
JSPandaFileManager::GetInstance()->GetJSPtExtractor(method->GetJSPandaFile());
const std::string &sourceFile = debugExtractor->GetSourceFile(method->GetMethodId());
if (sourceFile.empty()) {
codeEntry.scriptName = "";
} else {
codeEntry.scriptName = sourceFile;
auto iter = scriptIdMap_.find(codeEntry.scriptName);
if (iter == scriptIdMap_.end()) {
scriptIdMap_.insert(std::make_pair(codeEntry.scriptName, scriptIdMap_.size() + 1));
codeEntry.scriptId = static_cast<int>(scriptIdMap_.size());
} else {
codeEntry.scriptId = iter->second;
}
}
GetString(codeEntry.scriptName.c_str());
// line number
int32_t lineNumber = 0;
int32_t columnNumber = 0;
auto callbackLineFunc = [&](int32_t line) -> bool {
lineNumber = line + 1;
return true;
};
auto callbackColumnFunc = [&](int32_t column) -> bool {
columnNumber = column + 1;
return true;
};
panda_file::File::EntityId methodId = method->GetMethodId();
uint32_t offset = frameHandler.GetBytecodeOffset();
if (!debugExtractor->MatchLineWithOffset(callbackLineFunc, methodId, offset) ||
!debugExtractor->MatchColumnWithOffset(callbackColumnFunc, methodId, offset)) {
codeEntry.lineNumber = 0;
codeEntry.columnNumber = 0;
} else {
codeEntry.lineNumber = lineNumber;
codeEntry.columnNumber = columnNumber;
}
traceInfoStack_.emplace_back(codeEntry);
stackInfo_.insert(std::make_pair(method, codeEntry));
return;
}
Node *HeapSnapshot::GenerateStringNode(JSTaggedValue entry, int sequenceId)
{
Node *node = nullptr;
+123 -3
View File
@@ -30,6 +30,7 @@
#include "ecmascript/mem/c_containers.h"
#include "os/mem.h"
#include "ecmascript/tooling/interface/stream.h"
#include "ecmascript/interpreter/frame_handler.h"
namespace panda::ecmascript {
// Define the Object Graphic
@@ -107,6 +108,10 @@ public:
{
isLive_ = isLive;
}
void SetTraceId(uint64_t traceId)
{
traceId_ = traceId;
}
static Node *NewNode(const EcmaVM *vm, size_t id, size_t index, CString *name, NodeType type, size_t size,
TaggedObject *entry, bool isLive = true);
template<typename T>
@@ -256,13 +261,95 @@ private:
CUnorderedMap<Address, Node *> nodesMap_ {};
};
struct FunctionInfo {
int functionId = 0;
std::string functionName = "";
std::string scriptName = "";
int scriptId = 0;
int columnNumber = -1;
int lineNumber = -1;
};
class TraceTree;
class TraceNode {
public:
TraceNode(TraceTree* tree, uint32_t nodeIndex);
~TraceNode();
TraceNode(const TraceNode&) = delete;
TraceNode& operator=(const TraceNode&) = delete;
TraceNode* FindChild(uint32_t nodeIndex);
TraceNode* FindOrAddChild(uint32_t nodeIndex);
uint32_t GetNodeIndex() const
{
return nodeIndex_;
}
uint32_t GetTotalSize() const
{
return totalSize_;
}
uint32_t GetTotalCount() const
{
return totalCount_;
}
uint32_t GetId() const
{
return id_;
}
const std::vector<TraceNode*>& GetChildren() const
{
return children_;
}
TraceNode &SetTotalSize(uint32_t totalSize)
{
totalSize_ = totalSize;
return *this;
}
TraceNode &SetTotalCount(uint32_t totalCount)
{
totalCount_ = totalCount;
return *this;
}
private:
TraceTree* tree_;
uint32_t nodeIndex_;
uint32_t totalSize_;
uint32_t totalCount_;
uint32_t id_;
std::vector<TraceNode*> children_;
};
class TraceTree {
public:
TraceTree() : nextNodeId_(1), root_(this, 0)
{
}
~TraceTree() = default;
TraceTree(const TraceTree&) = delete;
TraceTree& operator=(const TraceTree&) = delete;
TraceNode* AddNodeToTree(CVector<uint32_t> traceNodeIndex);
TraceNode* GetRoot()
{
return &root_;
}
uint32_t GetNextNodeId()
{
return nextNodeId_++;
}
private:
uint32_t nextNodeId_;
TraceNode root_;
};
class HeapSnapshot {
public:
static constexpr int SEQ_STEP = 2;
NO_MOVE_SEMANTIC(HeapSnapshot);
NO_COPY_SEMANTIC(HeapSnapshot);
explicit HeapSnapshot(const EcmaVM *vm, const bool isVmMode, const bool isPrivate)
: stringTable_(vm), vm_(vm), isVmMode_(isVmMode), isPrivate_(isPrivate)
explicit HeapSnapshot(const EcmaVM *vm, const bool isVmMode, const bool isPrivate, const bool trackAllocations)
: stringTable_(vm), vm_(vm), isVmMode_(isVmMode), isPrivate_(isPrivate), trackAllocations_(trackAllocations)
{
}
~HeapSnapshot();
@@ -271,11 +358,14 @@ public:
void PrepareSnapshot();
void UpdateNode();
void AddNode(TaggedObject* address);
Node *AddNode(TaggedObject* address);
void MoveNode(uintptr_t address, TaggedObject* forward_address);
void RecordSampleTime();
bool FinishSnapshot();
void PushHeapStat(Stream* stream);
int AddTraceNode(int sequenceId, int size);
void AddMethodInfo(JSMethod *method, const FrameHandler &frameHandler, int sequenceId);
void AddTraceNodeId(JSMethod *method);
const CVector<TimeStamp> &GetTimeStamps() const
{
@@ -329,6 +419,29 @@ public:
return isPrivate_;
}
bool trackAllocations() const
{
return trackAllocations_;
}
const CVector<FunctionInfo> &GetTrackAllocationsStack() const
{
return traceInfoStack_;
}
TraceTree* GetTraceTree()
{
return &traceTree_;
}
void PrepareTraceInfo()
{
struct FunctionInfo info;
info.functionName = "(root)";
GetString(info.functionName.c_str());
traceInfoStack_.push_back(info);
}
private:
void FillNodes();
Node *GenerateNode(JSTaggedValue entry, int sequenceId = -1);
@@ -359,6 +472,13 @@ private:
bool isVmMode_ {true};
bool isPrivate_ {false};
Node* privateStringNode_ {nullptr};
bool trackAllocations_ {false};
CVector<FunctionInfo> traceInfoStack_ {};
CMap<JSMethod *, struct FunctionInfo> stackInfo_;
CMap<std::string, int> scriptIdMap_;
TraceTree traceTree_;
CMap<JSMethod *, uint32_t> methodToTraceNodeId_;
CVector<uint32_t> traceNodeIndex_;
};
class EntryVisitor {
@@ -76,8 +76,7 @@ void HeapSnapshotJSONSerializer::SerializeSnapshotHeader()
stringBuffer_ << "\"location_fields\":[\"object_index\",\"script_id\",\"line\",\"column\"]},\n"; // 10.
stringBuffer_ << "\"node_count\":" << snapshot_->GetNodeCount() << ",\n"; // 11.
stringBuffer_ << "\"edge_count\":" << snapshot_->GetEdgeCount() << ",\n"; // 12.
stringBuffer_ << "\"trace_function_count\":"
<< "0\n"; // 13.
stringBuffer_ << "\"trace_function_count\":" << snapshot_->GetTrackAllocationsStack().size() << "\n"; // 13.
stringBuffer_ << "},\n"; // 14.
}
@@ -133,12 +132,60 @@ void HeapSnapshotJSONSerializer::SerializeEdges()
void HeapSnapshotJSONSerializer::SerializeTraceFunctionInfo()
{
stringBuffer_ << "\"trace_function_infos\":[],\n"; // Empty
const CVector<FunctionInfo> trackAllocationsStack = snapshot_->GetTrackAllocationsStack();
const StringHashMap *stringTable = snapshot_->GetEcmaStringTable();
stringBuffer_ << "\"trace_function_infos\":["; // Empty
size_t i = 0;
for (const auto &info : trackAllocationsStack) {
if (i > 0) { // add comma except the first line
stringBuffer_ << ",";
}
stringBuffer_ << info.functionId << ",";
CString functionName(info.functionName.c_str());
stringBuffer_ << stringTable->GetStringId(&functionName) << ",";
CString scriptName(info.scriptName.c_str());
stringBuffer_ << stringTable->GetStringId(&scriptName) << ",";
stringBuffer_ << info.scriptId << ",";
stringBuffer_ << info.columnNumber << ",";
stringBuffer_ << info.lineNumber << "\n";
i++;
}
stringBuffer_ << "],\n";
}
void HeapSnapshotJSONSerializer::SerializeTraceTree()
{
stringBuffer_ << "\"trace_tree\":[],\n"; // Empty
stringBuffer_ << "\"trace_tree\":[";
TraceTree* tree = snapshot_->GetTraceTree();
if (tree != nullptr) {
SerializeTraceNode(tree->GetRoot());
}
stringBuffer_ << "],\n";
}
void HeapSnapshotJSONSerializer::SerializeTraceNode(TraceNode* node)
{
if (node == nullptr) {
return;
}
stringBuffer_ << node->GetId() << ",";
stringBuffer_ << node->GetNodeIndex() << ",";
stringBuffer_ << node->GetTotalCount() << ",";
stringBuffer_ << node->GetTotalSize() << ",";
stringBuffer_ << "[";
int i = 0;
for (TraceNode* child : node->GetChildren()) {
if (i > 0) {
stringBuffer_ << ",";
}
SerializeTraceNode(child);
i++;
}
stringBuffer_ << "]";
}
void HeapSnapshotJSONSerializer::SerializeSamples()
@@ -28,6 +28,7 @@ using fstream = std::fstream;
using stringstream = std::stringstream;
class HeapSnapshot;
class TraceNode;
class HeapSnapshotJSONSerializer {
public:
@@ -43,6 +44,7 @@ private:
void SerializeEdges();
void SerializeTraceFunctionInfo();
void SerializeTraceTree();
void SerializeTraceNode(TraceNode* node);
void SerializeSamples();
void SerializeLocations();
void SerializeStringTable();
+10 -1
View File
@@ -33,8 +33,17 @@ void HeapTrackerSample::Run()
void HeapTracker::AllocationEvent(TaggedObject* address)
{
Node *node;
if (snapshot_ != nullptr) {
snapshot_->AddNode(address);
node = snapshot_->AddNode(address);
if (node != nullptr && snapshot_->trackAllocations()) {
int size = node->GetSelfSize();
int sequenceId = node->GetId();
int traceId = snapshot_->AddTraceNode(sequenceId, size);
if (traceId != -1) {
node->SetTraceId(static_cast<uint64_t>(traceId));
}
}
}
}
+3 -2
View File
@@ -90,10 +90,11 @@ bool DFXJSNApi::BuildJsStackTrace(const EcmaVM *vm, std::string &stackTraceStr)
return true;
}
bool DFXJSNApi::StartHeapTracking(const EcmaVM *vm, double timeInterval, bool isVmMode, Stream *stream)
bool DFXJSNApi::StartHeapTracking(const EcmaVM *vm, double timeInterval, bool isVmMode,
Stream *stream, bool traceAllocation)
{
ecmascript::HeapProfilerInterface *heapProfile = ecmascript::HeapProfilerInterface::GetInstance(vm);
return heapProfile->StartHeapTracking(timeInterval, isVmMode, stream);
return heapProfile->StartHeapTracking(timeInterval, isVmMode, stream, traceAllocation);
}
bool DFXJSNApi::StopHeapTracking(const EcmaVM *vm, const std::string &filePath)
+1 -1
View File
@@ -52,7 +52,7 @@ public:
static bool BuildNativeAndJsStackTrace(const EcmaVM *vm, std::string &stackTraceStr);
static bool BuildJsStackTrace(const EcmaVM *vm, std::string &stackTraceStr);
static bool StartHeapTracking(const EcmaVM *vm, double timeInterval, bool isVmMode = true,
Stream *stream = nullptr);
Stream *stream = nullptr, bool traceAllocation = false);
static bool StopHeapTracking(const EcmaVM *vm, const std::string &filePath);
static bool StopHeapTracking(const EcmaVM *vm, Stream *stream, Progress *progress = nullptr);
static void PrintStatisticResult(const EcmaVM *vm);
@@ -298,10 +298,10 @@ DispatchResponse HeapProfilerImpl::StartSampling([[maybe_unused]]const StartSamp
return DispatchResponse::Fail("StartSampling not support now");
}
DispatchResponse HeapProfilerImpl::StartTrackingHeapObjects(
[[maybe_unused]]const StartTrackingHeapObjectsParams &params)
DispatchResponse HeapProfilerImpl::StartTrackingHeapObjects(const StartTrackingHeapObjectsParams &params)
{
bool result = panda::DFXJSNApi::StartHeapTracking(vm_, INTERVAL, true, &stream_);
bool traceAllocation = params.GetTrackAllocations();
bool result = panda::DFXJSNApi::StartHeapTracking(vm_, INTERVAL, true, &stream_, traceAllocation);
if (result) {
return DispatchResponse::Ok();
} else {