1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "ecmascript/dfx/hprof/heap_profiler.h"
17
18 #include "ecmascript/dfx/hprof/heap_snapshot.h"
19 #include "ecmascript/ecma_vm.h"
20 #include "ecmascript/js_thread.h"
21 #include "ecmascript/mem/assert_scope.h"
22 #include "ecmascript/mem/concurrent_sweeper.h"
23 #include "ecmascript/mem/heap-inl.h"
24 #include "ecmascript/dfx/hprof/file_stream.h"
25
26 namespace panda::ecmascript {
HeapProfiler(const EcmaVM * vm)27 HeapProfiler::HeapProfiler(const EcmaVM *vm) : vm_(vm), chunk_(vm->GetNativeAreaAllocator())
28 {
29 jsonSerializer_ = GetChunk()->New<HeapSnapshotJSONSerializer>();
30 if (UNLIKELY(jsonSerializer_ == nullptr)) {
31 LOG_FULL(FATAL) << "alloc snapshot json serializer failed";
32 UNREACHABLE();
33 }
34 }
~HeapProfiler()35 HeapProfiler::~HeapProfiler()
36 {
37 ClearSnapshot();
38 GetChunk()->Delete(jsonSerializer_);
39 jsonSerializer_ = nullptr;
40 }
41
UpdateHeapObjects(HeapSnapshot * snapshot)42 void HeapProfiler::UpdateHeapObjects(HeapSnapshot *snapshot)
43 {
44 vm_->CollectGarbage(TriggerGCType::OLD_GC);
45 vm_->GetHeap()->GetSweeper()->EnsureAllTaskFinished();
46 snapshot->UpdateNodes();
47 }
48
DumpHeapSnapshot(DumpFormat dumpFormat,Stream * stream,Progress * progress,bool isVmMode,bool isPrivate)49 bool HeapProfiler::DumpHeapSnapshot(DumpFormat dumpFormat, Stream *stream, Progress *progress,
50 bool isVmMode, bool isPrivate)
51 {
52 [[maybe_unused]] bool heapClean = ForceFullGC(vm_);
53 ASSERT(heapClean);
54 LOG_ECMA(INFO) << "HeapProfiler DumpSnapshot start";
55 size_t heapSize = vm_->GetHeap()->GetHeapObjectSize();
56 LOG_ECMA(ERROR) << "HeapProfiler DumpSnapshot heap size " << heapSize;
57 int32_t heapCount = vm_->GetHeap()->GetHeapObjectCount();
58 if (progress != nullptr) {
59 progress->ReportProgress(0, heapCount);
60 }
61 HeapSnapshot *snapshot = MakeHeapSnapshot(SampleType::ONE_SHOT, isVmMode, isPrivate);
62 if (progress != nullptr) {
63 progress->ReportProgress(heapCount, heapCount);
64 }
65 ASSERT(snapshot != nullptr);
66 if (!stream->Good()) {
67 FileStream newStream(GenDumpFileName(dumpFormat));
68 return jsonSerializer_->Serialize(snapshot, &newStream);
69 }
70 return jsonSerializer_->Serialize(snapshot, stream);
71 }
72
StartHeapTracking(double timeInterval,bool isVmMode,Stream * stream,bool traceAllocation,bool newThread)73 bool HeapProfiler::StartHeapTracking(double timeInterval, bool isVmMode, Stream *stream,
74 bool traceAllocation, bool newThread)
75 {
76 HeapSnapshot *snapshot = MakeHeapSnapshot(SampleType::REAL_TIME, isVmMode, false, traceAllocation);
77 if (snapshot == nullptr) {
78 return false;
79 }
80
81 UpdateHeapObjects(snapshot);
82 heapTracker_ = std::make_unique<HeapTracker>(snapshot, timeInterval, stream);
83 const_cast<EcmaVM *>(vm_)->StartHeapTracking(heapTracker_.get());
84
85 if (newThread) {
86 heapTracker_->StartTracing();
87 }
88
89 return true;
90 }
91
UpdateHeapTracking(Stream * stream)92 bool HeapProfiler::UpdateHeapTracking(Stream *stream)
93 {
94 HeapSnapshot *snapshot = hprofs_.at(0);
95 if (snapshot == nullptr) {
96 return false;
97 }
98
99 snapshot->RecordSampleTime();
100 UpdateHeapObjects(snapshot);
101
102 if (stream != nullptr) {
103 snapshot->PushHeapStat(stream);
104 }
105
106 return true;
107 }
108
StopHeapTracking(Stream * stream,Progress * progress,bool newThread)109 bool HeapProfiler::StopHeapTracking(Stream *stream, Progress *progress, bool newThread)
110 {
111 if (heapTracker_ == nullptr) {
112 return false;
113 }
114 int32_t heapCount = vm_->GetHeap()->GetHeapObjectCount();
115
116 const_cast<EcmaVM *>(vm_)->StopHeapTracking();
117 if (newThread) {
118 heapTracker_->StopTracing();
119 }
120
121 HeapSnapshot *snapshot = hprofs_.at(0);
122 if (snapshot == nullptr) {
123 return false;
124 }
125
126 if (progress != nullptr) {
127 progress->ReportProgress(0, heapCount);
128 }
129 snapshot->FinishSnapshot();
130 if (progress != nullptr) {
131 progress->ReportProgress(heapCount, heapCount);
132 }
133 return jsonSerializer_->Serialize(snapshot, stream);
134 }
135
GenDumpFileName(DumpFormat dumpFormat)136 std::string HeapProfiler::GenDumpFileName(DumpFormat dumpFormat)
137 {
138 CString filename("hprof_");
139 switch (dumpFormat) {
140 case DumpFormat::JSON:
141 filename.append(GetTimeStamp());
142 break;
143 case DumpFormat::BINARY:
144 filename.append("unimplemented");
145 break;
146 case DumpFormat::OTHER:
147 filename.append("unimplemented");
148 break;
149 default:
150 filename.append("unimplemented");
151 break;
152 }
153 filename.append(".heapsnapshot");
154 return CstringConvertToStdString(filename);
155 }
156
GetTimeStamp()157 CString HeapProfiler::GetTimeStamp()
158 {
159 std::time_t timeSource = std::time(nullptr);
160 struct tm tm {
161 };
162 struct tm *timeData = localtime_r(&timeSource, &tm);
163 if (timeData == nullptr) {
164 LOG_FULL(FATAL) << "localtime_r failed";
165 UNREACHABLE();
166 }
167 CString stamp;
168 const int TIME_START = 1900;
169 stamp.append(ToCString(timeData->tm_year + TIME_START))
170 .append("-")
171 .append(ToCString(timeData->tm_mon + 1))
172 .append("-")
173 .append(ToCString(timeData->tm_mday))
174 .append("_")
175 .append(ToCString(timeData->tm_hour))
176 .append("-")
177 .append(ToCString(timeData->tm_min))
178 .append("-")
179 .append(ToCString(timeData->tm_sec));
180 return stamp;
181 }
182
ForceFullGC(const EcmaVM * vm)183 bool HeapProfiler::ForceFullGC(const EcmaVM *vm)
184 {
185 if (vm->IsInitialized()) {
186 const_cast<Heap *>(vm->GetHeap())->CollectGarbage(TriggerGCType::FULL_GC);
187 return true;
188 }
189 return false;
190 }
191
MakeHeapSnapshot(SampleType sampleType,bool isVmMode,bool isPrivate,bool traceAllocation)192 HeapSnapshot *HeapProfiler::MakeHeapSnapshot(SampleType sampleType, bool isVmMode, bool isPrivate, bool traceAllocation)
193 {
194 LOG_ECMA(INFO) << "HeapProfiler::MakeHeapSnapshot";
195 DISALLOW_GARBAGE_COLLECTION;
196 const_cast<Heap *>(vm_->GetHeap())->Prepare();
197 switch (sampleType) {
198 case SampleType::ONE_SHOT: {
199 auto *snapshot = GetChunk()->New<HeapSnapshot>(vm_, isVmMode, isPrivate, traceAllocation, GetChunk());
200 if (snapshot == nullptr) {
201 LOG_FULL(FATAL) << "alloc snapshot failed";
202 UNREACHABLE();
203 }
204 snapshot->BuildUp();
205 AddSnapshot(snapshot);
206 return snapshot;
207 }
208 case SampleType::REAL_TIME: {
209 auto *snapshot = GetChunk()->New<HeapSnapshot>(vm_, isVmMode, isPrivate, traceAllocation, GetChunk());
210 if (snapshot == nullptr) {
211 LOG_FULL(FATAL) << "alloc snapshot failed";
212 UNREACHABLE();
213 }
214 AddSnapshot(snapshot);
215 snapshot->PrepareSnapshot();
216 return snapshot;
217 }
218 default:
219 return nullptr;
220 }
221 }
222
AddSnapshot(HeapSnapshot * snapshot)223 void HeapProfiler::AddSnapshot(HeapSnapshot *snapshot)
224 {
225 if (hprofs_.size() >= MAX_NUM_HPROF) {
226 ClearSnapshot();
227 }
228 ASSERT(snapshot != nullptr);
229 hprofs_.emplace_back(snapshot);
230 }
231
ClearSnapshot()232 void HeapProfiler::ClearSnapshot()
233 {
234 for (auto *snapshot : hprofs_) {
235 GetChunk()->Delete(snapshot);
236 }
237 hprofs_.clear();
238 }
239 } // namespace panda::ecmascript
240