* Copyright (c) 2021-2023 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ECMASCRIPT_DFX_HPROF_HEAP_PROFILER_H
#define ECMASCRIPT_DFX_HPROF_HEAP_PROFILER_H
#include "common_components/heap/heap.h"
#include "ecmascript/ecma_macros.h"
#include "ecmascript/dfx/hprof/file_stream.h"
#include "ecmascript/dfx/hprof/heap_profiler_interface.h"
#include "ecmascript/dfx/hprof/heap_snapshot_json_serializer.h"
#include "ecmascript/dfx/hprof/heap_tracker.h"
#include "ecmascript/dfx/hprof/heap_sampling.h"
#include "ecmascript/dfx/hprof/progress.h"
#include "ecmascript/dfx/hprof/string_hashmap.h"
#include "ecmascript/dfx/hprof/heap_marker.h"
#include "ecmascript/mem/c_containers.h"
#include "ecmascript/mem/clock_scope.h"
namespace panda::test {
class MockHeapProfiler;
class HeapDumpTestHelper;
};
namespace panda::ecmascript {
class HeapSnapshot;
class EcmaVM;
using NodeId = uint64_t;
class EntryIdMap {
public:
EntryIdMap() = default;
~EntryIdMap() = default;
NO_COPY_SEMANTIC(EntryIdMap);
NO_MOVE_SEMANTIC(EntryIdMap);
static constexpr uint64_t SEQ_STEP = 2;
std::pair<bool, NodeId> FindId(JSTaggedType addr);
NodeId FindOrInsertNodeId(JSTaggedType addr);
bool InsertId(JSTaggedType addr, NodeId id);
bool EraseId(JSTaggedType addr);
bool Move(JSTaggedType oldAddr, JSTaggedType forwardAddr);
void UpdateEntryIdMap(HeapSnapshot *snapshot);
void RemoveUnmarkedObjects(HeapMarker &marker);
NodeId GetNextId()
{
nextId_ += SEQ_STEP;
return nextId_ - SEQ_STEP;
}
NodeId GetLastId()
{
return nextId_ - SEQ_STEP;
}
size_t GetIdCount()
{
return idMap_.size();
}
CUnorderedMap<JSTaggedType, NodeId>* GetIdMap()
{
return &idMap_;
}
NodeId GetId()
{
return nextId_;
}
void SetId(NodeId id)
{
nextId_ = id;
}
private:
NodeId nextId_ {3U};
CUnorderedMap<JSTaggedType, NodeId> idMap_ {};
};
struct ScopeWrapper {
LocalScope *localScope_ {nullptr};
EcmaHandleScope *ecmaHandleScope_ {nullptr};
ClockScope clockScope_;
ScopeWrapper(LocalScope *localScope, EcmaHandleScope *ecmaHandleScope)
: localScope_(localScope), ecmaHandleScope_(ecmaHandleScope) {}
};
enum class DumpHeapSnapshotStatus : uint8_t {
SUCCESS,
FORK_FAILED,
FAILED_TO_WAIT,
WAIT_FORK_PROCESS_TIMEOUT,
};
class HeapProfiler : public HeapProfilerInterface {
public:
NO_MOVE_SEMANTIC(HeapProfiler);
NO_COPY_SEMANTIC(HeapProfiler);
explicit HeapProfiler(const EcmaVM *vm);
~HeapProfiler() override;
enum class SampleType { ONE_SHOT, REAL_TIME };
void AllocationEvent(TaggedObject *address, size_t size) override;
void MoveEvent(uintptr_t address, TaggedObject *forwardAddress, size_t size) override;
* dump the specific snapshot in target format
*/
bool DumpHeapSnapshot(Stream *stream, const DumpSnapShotOption &dumpOption, Progress *progress = nullptr,
std::function<void(uint8_t)> callback = [] (uint8_t) {}) override;
void DumpHeapSnapshotForOOM(const DumpSnapShotOption &dumpOption, bool fromSharedGC = false) override;
void AddSnapshot(HeapSnapshot *snapshot);
bool StartHeapTracking(double timeInterval, bool isVmMode = true, Stream *stream = nullptr,
bool traceAllocation = false, bool newThread = true) override;
bool StopHeapTracking(Stream *stream, Progress *progress = nullptr, bool newThread = true) override;
bool UpdateHeapTracking(Stream *stream) override;
bool StartHeapSampling(uint64_t samplingInterval, int stackDepth = 128) override;
void StopHeapSampling() override;
const struct SamplingInfo *GetAllocationProfile() override;
static bool TryStartOOMDump();
static void ResetOOMDump();
size_t GetIdCount() override
{
return entryIdMap_->GetIdCount();
}
EntryIdMap *GetEntryIdMap() const
{
return const_cast<EntryIdMap *>(entryIdMap_);
}
CUnorderedMap<uintptr_t, NodeId> GetNodeAddressIdMap()
{
return nodeAddressIdMap_;
}
Chunk *GetChunk() const
{
return const_cast<Chunk *>(&chunk_);
}
StringHashMap *GetEcmaStringTable() const
{
return const_cast<StringHashMap *>(&stringTable_);
}
bool IsStartLocalHandleLeakDetect() const;
void SwitchStartLocalHandleLeakDetect();
void IncreaseScopeCount();
void DecreaseScopeCount();
void PushToActiveScopeStack(LocalScope *localScope, EcmaHandleScope *ecmaHandleScope);
void PopFromActiveScopeStack();
void ClearHandleBackTrace();
std::string_view GetBackTraceOfHandle(uintptr_t handle) const;
void WriteToLeakStackTraceFd(std::ostringstream &buffer) const;
void SetLeakStackTraceFd(int32_t fd);
int32_t GetLeakStackTraceFd() const;
void CloseLeakStackTraceFd();
void StorePotentiallyLeakHandles(uintptr_t handle);
private:
static bool oomDumpActive_;
* trigger full gc to make sure no unreachable objects in heap
*/
bool ForceFullGC(const EcmaVM *vm);
void ForceSharedGC();
bool DumpHeapSnapshotFromSharedGC(Stream *stream, const DumpSnapShotOption &dumpOption);
void DumpHeapSnapshotFromSharedGCForOOM(Stream *stream, const DumpSnapShotOption &dumpOption);
* Helper used by DumpHeapSnapshot to fork a child process and execute the
* appropriate dump logic. Returns the pid of the child on success, or -1
* if fork failed.
*/
pid_t ForkAndPerformDump(Stream *stream, const DumpSnapShotOption &dumpOption,
Progress *progress);
* make a new heap snapshot and put it into a container eg, vector
*/
HeapSnapshot *MakeHeapSnapshot(SampleType sampleType, const DumpSnapShotOption &dumpOption,
bool traceAllocation = false);
bool DoDump(Stream *stream, Progress *progress, const DumpSnapShotOption &dumpOption);
void UpdateNodeAddressIdMap();
std::string GenDumpFileName(DumpFormat dumpFormat);
CString GetTimeStamp();
void UpdateHeapObjects(HeapSnapshot *snapshot);
void ClearSnapshot();
void FillIdMap();
bool BinaryDump(Stream *stream, const DumpSnapShotOption &dumpOption);
uint32_t GetScopeCount() const;
std::shared_ptr<ScopeWrapper> GetLastActiveScope() const;
bool InsertHandleBackTrace(uintptr_t handle, const std::string &backTrace);
const std::string RAWHEAP_FILE_NAME = "/data/log/reliability/resource_leak/memory_leak/memleak";
const size_t MAX_NUM_HPROF = 5;
const EcmaVM *vm_;
CVector<HeapSnapshot *> hprofs_;
StringHashMap stringTable_;
bool isProfiling_ {false};
EntryIdMap *entryIdMap_;
CUnorderedMap<uintptr_t, NodeId> nodeAddressIdMap_ {};
std::unique_ptr<HeapTracker> heapTracker_;
Chunk chunk_;
std::unique_ptr<HeapSampling> heapSampling_ {nullptr};
Mutex mutex_;
static const long LOCAL_HANDLE_LEAK_TIME_MS {5000};
bool startLocalHandleLeakDetect_ {false};
uint32_t scopeCount_ {0};
std::stack<std::shared_ptr<ScopeWrapper>> activeScopeStack_;
std::map<uintptr_t, std::string> handleBackTrace_;
int32_t leakStackTraceFd_ {-1};
uint32_t moveEventCbId_ {0};
pid_t appPid_ {0};
long appTid_ {0};
friend class HeapProfilerFriendTest;
friend class panda::test::MockHeapProfiler;
friend class panda::test::HeapDumpTestHelper;
};
class NodeIdCacheClearScope {
public:
#if defined(ECMASCRIPT_SUPPORT_SNAPSHOT)
NodeIdCacheClearScope(const EcmaVM *vm, const DumpSnapShotOption &opt)
: vm_(vm), opt_(opt) {}
~NodeIdCacheClearScope()
{
if (opt_.isClearNodeIdCache && vm_ != nullptr) {
LOG_ECMA(INFO) << "NodeIdCacheClearScope HeapProfile delete.";
const_cast<EcmaVM *>(vm_)->DeleteHeapProfile();
}
}
private:
const EcmaVM *vm_ {nullptr};
const DumpSnapShotOption &opt_;
#else
NodeIdCacheClearScope(const EcmaVM *, const DumpSnapShotOption &) {}
~NodeIdCacheClearScope() = default;
#endif
};
}
#endif