Signed-off-by: z00522183 <zhanheng2@huawei.com>
Change-Id: I017e19b6d7c47a5017267084d55b2186c743c8ea
This commit is contained in:
zhanheng 2024-04-29 11:26:12 +08:00 committed by zhanheng
parent 658530428e
commit eca99a40c8
7 changed files with 112 additions and 89 deletions

View File

@ -13,6 +13,7 @@
* limitations under the License.
*/
#include <sys/wait.h>
#include "ecmascript/dfx/hprof/heap_profiler.h"
#include "ecmascript/checkpoint/thread_state_transition.h"
@ -25,6 +26,7 @@
#include "ecmascript/mem/concurrent_sweeper.h"
#include "ecmascript/mem/heap-inl.h"
#include "ecmascript/mem/shared_heap/shared_concurrent_sweeper.h"
#include "ecmascript/base/block_hook_scope.h"
#if defined(ENABLE_DUMP_IN_FAULTLOG)
#include "faultloggerd_client.h"
@ -135,7 +137,8 @@ void HeapProfiler::UpdateHeapObjects(HeapSnapshot *snapshot)
void HeapProfiler::DumpHeapSnapshot([[maybe_unused]] DumpFormat dumpFormat, [[maybe_unused]] bool isVmMode,
[[maybe_unused]] bool isPrivate, [[maybe_unused]] bool captureNumericValue,
[[maybe_unused]] bool isFullGC, [[maybe_unused]] bool isSimplify)
[[maybe_unused]] bool isFullGC, [[maybe_unused]] bool isSimplify,
[[maybe_unused]] bool isSync)
{
#if defined(ENABLE_DUMP_IN_FAULTLOG)
// Write in faultlog for heap leak.
@ -145,22 +148,17 @@ void HeapProfiler::DumpHeapSnapshot([[maybe_unused]] DumpFormat dumpFormat, [[ma
return;
}
FileDescriptorStream stream(fd);
DumpHeapSnapshot(dumpFormat, &stream, nullptr, isVmMode, isPrivate, captureNumericValue, isFullGC, isSimplify);
DumpHeapSnapshot(
dumpFormat, &stream, nullptr, isVmMode, isPrivate, captureNumericValue, isFullGC, isSimplify, isSync);
#endif
}
bool HeapProfiler::DumpHeapSnapshot(DumpFormat dumpFormat, Stream *stream, Progress *progress,
bool isVmMode, bool isPrivate, bool captureNumericValue,
bool isFullGC, bool isSimplify)
bool HeapProfiler::DoDump(DumpFormat dumpFormat, Stream *stream, Progress *progress,
bool isVmMode, bool isPrivate, bool captureNumericValue, bool isFullGC, bool isSimplify)
{
int32_t heapCount = 0;
HeapSnapshot *snapshot = nullptr;
{
if (isFullGC) {
[[maybe_unused]] bool heapClean = ForceFullGC(vm_);
ASSERT(heapClean);
}
LOG_ECMA(INFO) << "HeapProfiler DumpSnapshot start";
if (isFullGC) {
size_t heapSize = vm_->GetHeap()->GetLiveObjectSize();
LOG_ECMA(INFO) << "HeapProfiler DumpSnapshot heap size " << heapSize;
@ -170,7 +168,7 @@ bool HeapProfiler::DumpHeapSnapshot(DumpFormat dumpFormat, Stream *stream, Progr
}
}
snapshot = MakeHeapSnapshot(SampleType::ONE_SHOT, isVmMode, isPrivate, captureNumericValue,
false, isFullGC, isSimplify);
false, false, isSimplify);
ASSERT(snapshot != nullptr);
}
entryIdMap_->UpdateEntryIdMap(snapshot);
@ -189,6 +187,67 @@ bool HeapProfiler::DumpHeapSnapshot(DumpFormat dumpFormat, Stream *stream, Progr
return serializerResult;
}
static void WaitProcess(pid_t pid)
{
time_t startTime = time(nullptr);
constexpr int DUMP_TIME_OUT = 300;
constexpr int DEFAULT_SLEEP_TIME = 100000;
while (true) {
int status = 0;
pid_t p = waitpid(pid, &status, WNOHANG);
if (p < 0 || p == pid) {
break;
}
if (time(nullptr) > startTime + DUMP_TIME_OUT) {
LOG_GC(ERROR) << "DumpHeapSnapshot kill thread, wait " << DUMP_TIME_OUT << " s";
kill(pid, SIGTERM);
break;
}
usleep(DEFAULT_SLEEP_TIME);
}
}
bool HeapProfiler::DumpHeapSnapshot(DumpFormat dumpFormat, Stream *stream, Progress *progress,
bool isVmMode, bool isPrivate, bool captureNumericValue,
bool isFullGC, bool isSimplify, bool isSync)
{
bool res = false;
if (isFullGC) {
[[maybe_unused]] bool heapClean = ForceFullGC(vm_);
ASSERT(heapClean);
}
pid_t pid = -1;
{
base::BlockHookScope blockScope;
ThreadManagedScope managedScope(vm_->GetJSThread());
// suspend All.
SuspendAllScope suspendScope(vm_->GetAssociatedJSThread());
if (isFullGC) {
DISALLOW_GARBAGE_COLLECTION;
const_cast<Heap *>(vm_->GetHeap())->Prepare();
}
// fork
if ((pid = fork()) < 0) {
LOG_ECMA(ERROR) << "DumpHeapSnapshot fork failed!";
return false;
}
if (pid == 0) {
res = DoDump(dumpFormat, stream, progress, isVmMode, isPrivate, captureNumericValue, isFullGC, isSimplify);
_exit(0);
}
}
if (pid != 0) {
if (isSync) {
WaitProcess(pid);
} else {
std::thread thread(&WaitProcess, pid);
thread.detach();
}
stream->EndOfStream();
}
return res;
}
bool HeapProfiler::StartHeapTracking(double timeInterval, bool isVmMode, Stream *stream,
bool traceAllocation, bool newThread)
{

View File

@ -78,10 +78,10 @@ public:
*/
bool DumpHeapSnapshot(DumpFormat dumpFormat, Stream *stream, Progress *progress = nullptr,
bool isVmMode = true, bool isPrivate = false, bool captureNumericValue = false,
bool isFullGC = true, bool isSimplify = false) override;
bool isFullGC = true, bool isSimplify = false, bool isSync = true) override;
void DumpHeapSnapshot(DumpFormat dumpFormat, bool isVmMode = true, bool isPrivate = false,
bool captureNumericValue = false, bool isFullGC = true,
bool isSimplify = false) override;
bool isSimplify = false, bool isSync = true) override;
void AddSnapshot(HeapSnapshot *snapshot);
bool StartHeapTracking(double timeInterval, bool isVmMode = true, Stream *stream = nullptr,
@ -120,8 +120,9 @@ private:
*/
HeapSnapshot *MakeHeapSnapshot(SampleType sampleType, bool isVmMode = true,
bool isPrivate = false, bool captureNumericValue = false,
bool traceAllocation = false, bool isFullGC = true,
bool isSimplify = false);
bool traceAllocation = false, bool isFullGC = true, bool isSimplify = false);
bool DoDump(DumpFormat dumpFormat, Stream *stream, Progress *progress,
bool isVmMode, bool isPrivate, bool captureNumericValue, bool isFullGC, bool isSimplify);
std::string GenDumpFileName(DumpFormat dumpFormat);
CString GetTimeStamp();
void UpdateHeapObjects(HeapSnapshot *snapshot);

View File

@ -42,11 +42,11 @@ public:
virtual void MoveEvent(uintptr_t address, TaggedObject *forwardAddress, size_t size)= 0;
virtual bool DumpHeapSnapshot(DumpFormat dumpFormat, Stream *stream, Progress *progress = nullptr,
bool isVmMode = true, bool isPrivate = false, bool captureNumericValue = false,
bool isFullGC = true, bool isSimplify = false) = 0;
bool isFullGC = true, bool isSimplify = false, bool isSync = true) = 0;
// Provide an internal interface for oom dump.
virtual void DumpHeapSnapshot(DumpFormat dumpFormat, bool isVmMode = true, bool isPrivate = false,
bool captureNumericValue = false, bool isFullGC = true,
bool isSimplify = false) = 0;
bool isSimplify = false, bool isSync = true) = 0;
virtual bool StartHeapTracking(double timeInterval, bool isVmMode = true, Stream *stream = nullptr,
bool traceAllocation = false, bool newThread = true) = 0;

View File

@ -64,18 +64,18 @@ public:
bool DumpHeapSnapshot(DumpFormat dumpFormat, Stream *stream, Progress *progress = nullptr,
bool isVmMode = true, bool isPrivate = false, bool captureNumericValue = false,
bool isFullGC = true, bool isSimplify = false) override
bool isFullGC = true, bool isSimplify = false, bool isSync = false) override
{
return profiler_->DumpHeapSnapshot(dumpFormat, stream, progress, isVmMode, isPrivate, captureNumericValue,
isFullGC, isSimplify);
isFullGC, isSimplify, isSync);
}
void DumpHeapSnapshot(DumpFormat dumpFormat, bool isVmMode = true, bool isPrivate = false,
bool captureNumericValue = false, bool isFullGC = true,
bool isSimplify = false) override
bool isSimplify = false, bool isSync = false) override
{
profiler_->DumpHeapSnapshot(dumpFormat, isVmMode, isPrivate, captureNumericValue,
isFullGC, isSimplify);
isFullGC, isSimplify, isSync);
}
bool StartHeapTracking(double timeInterval, bool isVmMode = true, Stream *stream = nullptr,

View File

@ -1084,7 +1084,7 @@ void Heap::DumpHeapSnapshotBeforeOOM([[maybe_unused]] bool isFullGC)
<< " value:" << std::to_string(pid);
}
// Vm should always allocate young space successfully. Really OOM will occur in the non-young spaces.
heapProfile->DumpHeapSnapshot(DumpFormat::JSON, true, false, false, isFullGC, true);
heapProfile->DumpHeapSnapshot(DumpFormat::JSON, true, false, false, isFullGC, true, true);
HeapProfilerInterface::Destroy(ecmaVm_);
#endif // ENABLE_DUMP_IN_FAULTLOG
#endif // ECMASCRIPT_SUPPORT_SNAPSHOT

View File

@ -35,7 +35,6 @@
#include "ecmascript/mem/shared_heap/shared_concurrent_sweeper.h"
#if defined(ECMASCRIPT_SUPPORT_CPUPROFILER)
#include <sys/wait.h>
#include "ecmascript/dfx/cpu_profiler/cpu_profiler.h"
#include "ecmascript/dfx/cpu_profiler/samples_record.h"
#endif
@ -83,7 +82,7 @@ void DFXJSNApi::DumpHeapSnapshot([[maybe_unused]] const EcmaVM *vm, [[maybe_unus
ecmascript::HeapProfilerInterface *heapProfile = ecmascript::HeapProfilerInterface::GetInstance(
const_cast<EcmaVM *>(vm));
heapProfile->DumpHeapSnapshot(ecmascript::DumpFormat(dumpFormat), stream, progress,
isVmMode, isPrivate, captureNumericValue, isFullGC);
isVmMode, isPrivate, captureNumericValue, isFullGC, false, true);
#else
LOG_ECMA(ERROR) << "Not support arkcompiler heap snapshot";
#endif
@ -122,28 +121,6 @@ bool DFXJSNApi::ForceFullGC(const EcmaVM *vm)
return false;
}
#if defined(ENABLE_DUMP_IN_FAULTLOG)
static void WaitProcess(pid_t pid)
{
time_t startTime = time(nullptr);
constexpr int DUMP_TIME_OUT = 30;
constexpr int DEFAULT_SLEEP_TIME = 100000;
while (true) {
int status = 0;
pid_t p = waitpid(pid, &status, WNOHANG);
if (p < 0 || p == pid) {
break;
}
if (time(nullptr) > startTime + DUMP_TIME_OUT) {
LOG_GC(ERROR) << "wait " << DUMP_TIME_OUT << " s";
kill(pid, SIGTERM);
break;
}
usleep(DEFAULT_SLEEP_TIME);
}
}
#endif // ENABLE_DUMP_IN_FAULTLOG
void DFXJSNApi::DumpHeapSnapshot([[maybe_unused]] const EcmaVM *vm, [[maybe_unused]] int dumpFormat,
[[maybe_unused]] bool isVmMode, [[maybe_unused]] bool isPrivate,
[[maybe_unused]] bool captureNumericValue, [[maybe_unused]] bool isFullGC)
@ -161,37 +138,19 @@ void DFXJSNApi::DumpHeapSnapshot([[maybe_unused]] const EcmaVM *vm, [[maybe_unus
vm->GetJSThread()->SetStackTraceFd(stackTraceFd);
}
}
if (isFullGC) {
[[maybe_unused]] bool heapClean = ForceFullGC(vm);
ASSERT(heapClean);
}
ecmascript::base::BlockHookScope blockScope;
// suspend All.
ecmascript::SuspendAllScope suspendScope(vm->GetAssociatedJSThread());
if (isFullGC) {
DISALLOW_GARBAGE_COLLECTION;
const_cast<ecmascript::Heap *>(vm->GetHeap())->Prepare();
}
// fork
pid_t pid = -1;
if ((pid = fork()) < 0) {
LOG_ECMA(ERROR) << "DumpHeapSnapshot fork failed!";
// Write in faultlog for heap leak.
int32_t fd = RequestFileDescriptor(static_cast<int32_t>(FaultLoggerType::JS_HEAP_SNAPSHOT));
if (fd < 0) {
LOG_ECMA(ERROR) << "Write FD failed, fd" << fd;
return;
}
if (pid == 0) {
// Write in faultlog for heap leak.
int32_t fd = RequestFileDescriptor(static_cast<int32_t>(FaultLoggerType::JS_HEAP_SNAPSHOT));
if (fd < 0) {
LOG_ECMA(ERROR) << "Write FD failed, fd" << fd;
return;
}
FileDescriptorStream stream(fd);
DumpHeapSnapshot(vm, dumpFormat, &stream, nullptr, isVmMode, isPrivate, captureNumericValue, false);
_exit(0);
} else {
std::thread thread(&WaitProcess, pid);
thread.detach();
}
FileDescriptorStream stream(fd);
ecmascript::HeapProfilerInterface *heapProfile = ecmascript::HeapProfilerInterface::GetInstance(
const_cast<EcmaVM *>(vm));
heapProfile->DumpHeapSnapshot(ecmascript::DumpFormat(dumpFormat), &stream, nullptr,
isVmMode, isPrivate, captureNumericValue, isFullGC, false, false);
sem_post(&g_heapdumpCnt);
#endif // ENABLE_DUMP_IN_FAULTLOG
#else
@ -213,14 +172,13 @@ void DFXJSNApi::DumpHeapSnapshot([[maybe_unused]] const EcmaVM *vm, [[maybe_unus
sem_init(&g_heapdumpCnt, 0, THREAD_COUNT);
uint32_t curTid = vm->GetTid();
LOG_ECMA(INFO) << "DumpHeapSnapshot tid " << tid << " curTid " << curTid;
if ((tid == 0) || ((tid != 0) && (tid == curTid))) {
DumpHeapSnapshotWithVm(vm, dumpFormat, isVmMode, isPrivate, captureNumericValue, isFullGC);
}
DumpHeapSnapshotWithVm(vm, dumpFormat, isVmMode, isPrivate, captureNumericValue, isFullGC, tid);
}
void DFXJSNApi::DumpHeapSnapshotWithVm([[maybe_unused]] const EcmaVM *vm, [[maybe_unused]] int dumpFormat,
[[maybe_unused]] bool isVmMode, [[maybe_unused]] bool isPrivate,
[[maybe_unused]] bool captureNumericValue, [[maybe_unused]] bool isFullGC)
[[maybe_unused]] bool captureNumericValue, [[maybe_unused]] bool isFullGC,
[[maybe_unused]] uint32_t tid)
{
#if defined(ECMASCRIPT_SUPPORT_SNAPSHOT)
#if defined(ENABLE_DUMP_IN_FAULTLOG)
@ -243,19 +201,24 @@ void DFXJSNApi::DumpHeapSnapshotWithVm([[maybe_unused]] const EcmaVM *vm, [[mayb
return;
}
int ret = uv_queue_work(loop, work, [](uv_work_t *) {}, [](uv_work_t *work, int32_t) {
struct DumpForSnapShotStruct *dump = static_cast<struct DumpForSnapShotStruct *>(work->data);
DFXJSNApi::GetHeapPrepare(dump->vm);
DumpHeapSnapshot(dump->vm, dump->dumpFormat, dump->isVmMode, dump->isPrivate,
dump->captureNumericValue, dump->isFullGC);
delete dump;
delete work;
});
uint32_t curTid = vm->GetTid();
int ret = 0;
if ((tid == 0) || ((tid != 0) && (tid == curTid))) {
ret = uv_queue_work(loop, work, [](uv_work_t *) {}, [](uv_work_t *work, int32_t) {
struct DumpForSnapShotStruct *dump = static_cast<struct DumpForSnapShotStruct *>(work->data);
DFXJSNApi::GetHeapPrepare(dump->vm);
DumpHeapSnapshot(dump->vm, dump->dumpFormat, dump->isVmMode, dump->isPrivate,
dump->captureNumericValue, dump->isFullGC);
delete dump;
delete work;
});
}
// dump worker vm
const_cast<EcmaVM *>(vm)->EnumerateWorkerVm([&](const EcmaVM *workerVm) -> void {
uint32_t curTid = workerVm->GetTid();
LOG_ECMA(INFO) << "DumpHeapSnapshot workthread curTid " << curTid;
DumpHeapSnapshotWithVm(workerVm, dumpFormat, isVmMode, isPrivate, captureNumericValue, isFullGC);
DumpHeapSnapshotWithVm(workerVm, dumpFormat, isVmMode, isPrivate, captureNumericValue, isFullGC, tid);
return;
});

View File

@ -76,7 +76,7 @@ public:
static void DumpHeapSnapshot(const EcmaVM *vm, int dumpFormat, bool isVmMode, bool isPrivate,
bool captureNumericValue, bool isFullGC, uint32_t tid);
static void DumpHeapSnapshotWithVm(const EcmaVM *vm, int dumpFormat, bool isVmMode, bool isPrivate,
bool captureNumericValue, bool isFullGC);
bool captureNumericValue, bool isFullGC, uint32_t tid);
static void TriggerGC(const EcmaVM *vm, uint32_t tid);
static bool ForceFullGC(const EcmaVM *vm);
static void TriggerGCWithVm(const EcmaVM *vm);