• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/file_stream.h"
19 #include "ecmascript/dfx/hprof/heap_snapshot.h"
20 #include "ecmascript/ecma_vm.h"
21 #include "ecmascript/js_thread.h"
22 #include "ecmascript/mem/assert_scope.h"
23 #include "ecmascript/mem/concurrent_sweeper.h"
24 #include "ecmascript/mem/heap-inl.h"
25 
26 namespace panda::ecmascript {
FindId(Address addr)27 std::pair<bool, uint32_t> EntryIdMap::FindId(Address addr)
28 {
29     auto it = idMap_.find(addr);
30     if (it == idMap_.end()) {
31         return std::make_pair(false, GetNextId()); // return nextId if entry not exits
32     } else {
33         return std::make_pair(true, it->second);
34     }
35 }
36 
InsertId(Address addr,uint32_t id)37 bool EntryIdMap::InsertId(Address addr, uint32_t id)
38 {
39     auto it = idMap_.find(addr);
40     if (it == idMap_.end()) {
41         idMap_.emplace(addr, id);
42         return true;
43     }
44     idMap_[addr] = id;
45     return false;
46 }
47 
EraseId(Address addr)48 bool EntryIdMap::EraseId(Address addr)
49 {
50     auto it = idMap_.find(addr);
51     if (it == idMap_.end()) {
52         return false;
53     }
54     idMap_.erase(it);
55     return true;
56 }
57 
Move(Address oldAddr,Address forwardAddr)58 bool EntryIdMap::Move(Address oldAddr, Address forwardAddr)
59 {
60     if (oldAddr == forwardAddr) {
61         return true;
62     }
63     auto it = idMap_.find(oldAddr);
64     if (it != idMap_.end()) {
65         uint32_t id = it->second;
66         idMap_.erase(it);
67         idMap_[forwardAddr] = id;
68         return true;
69     }
70     return false;
71 }
72 
RemoveDeadEntryId(HeapSnapshot * snapshot)73 void EntryIdMap::RemoveDeadEntryId(HeapSnapshot *snapshot)
74 {
75     auto nodes = snapshot->GetNodes();
76     CUnorderedMap<Address, uint32_t> newIdMap;
77     for (auto node : *nodes) {
78         auto addr = node->GetAddress();
79         auto it = idMap_.find(addr);
80         if (it != idMap_.end()) {
81             newIdMap.emplace(addr, it->second);
82         }
83     }
84     idMap_ = newIdMap;
85 }
86 
HeapProfiler(const EcmaVM * vm)87 HeapProfiler::HeapProfiler(const EcmaVM *vm) : vm_(vm), chunk_(vm->GetNativeAreaAllocator())
88 {
89     isProfiling_ = false;
90     entryIdMap_ = GetChunk()->New<EntryIdMap>();
91     jsonSerializer_ = GetChunk()->New<HeapSnapshotJSONSerializer>();
92     if (UNLIKELY(jsonSerializer_ == nullptr)) {
93         LOG_FULL(FATAL) << "alloc snapshot json serializer failed";
94         UNREACHABLE();
95     }
96 }
97 
~HeapProfiler()98 HeapProfiler::~HeapProfiler()
99 {
100     ClearSnapshot();
101     GetChunk()->Delete(entryIdMap_);
102     GetChunk()->Delete(jsonSerializer_);
103     jsonSerializer_ = nullptr;
104 }
105 
AllocationEvent(TaggedObject * address,size_t size)106 void HeapProfiler::AllocationEvent(TaggedObject *address, size_t size)
107 {
108     DISALLOW_GARBAGE_COLLECTION;
109     if (isProfiling_) {
110         // Id will be allocated later while add new node
111         if (heapTracker_ != nullptr) {
112             heapTracker_->AllocationEvent(address, size);
113         }
114     }
115 }
116 
MoveEvent(uintptr_t address,TaggedObject * forwardAddress,size_t size)117 void HeapProfiler::MoveEvent(uintptr_t address, TaggedObject *forwardAddress, size_t size)
118 {
119     os::memory::LockHolder lock(mutex_);
120     if (isProfiling_) {
121         entryIdMap_->Move(address, reinterpret_cast<Address>(forwardAddress));
122         if (heapTracker_ != nullptr) {
123             heapTracker_->MoveEvent(address, forwardAddress, size);
124         }
125     }
126 }
127 
UpdateHeapObjects(HeapSnapshot * snapshot)128 void HeapProfiler::UpdateHeapObjects(HeapSnapshot *snapshot)
129 {
130     vm_->CollectGarbage(TriggerGCType::OLD_GC);
131     vm_->GetHeap()->GetSweeper()->EnsureAllTaskFinished();
132     snapshot->UpdateNodes();
133 }
134 
DumpHeapSnapshot(DumpFormat dumpFormat,Stream * stream,Progress * progress,bool isVmMode,bool isPrivate,bool captureNumericValue)135 bool HeapProfiler::DumpHeapSnapshot(DumpFormat dumpFormat, Stream *stream, Progress *progress,
136                                     bool isVmMode, bool isPrivate, bool captureNumericValue)
137 {
138     [[maybe_unused]] bool heapClean = ForceFullGC(vm_);
139     ASSERT(heapClean);
140     LOG_ECMA(INFO) << "HeapProfiler DumpSnapshot start";
141     size_t heapSize = vm_->GetHeap()->GetHeapObjectSize();
142     LOG_ECMA(INFO) << "HeapProfiler DumpSnapshot heap size " << heapSize;
143     int32_t heapCount = static_cast<int32_t>(vm_->GetHeap()->GetHeapObjectCount());
144     if (progress != nullptr) {
145         progress->ReportProgress(0, heapCount);
146     }
147     HeapSnapshot *snapshot = MakeHeapSnapshot(SampleType::ONE_SHOT, isVmMode, isPrivate, captureNumericValue);
148     ASSERT(snapshot != nullptr);
149     entryIdMap_->RemoveDeadEntryId(snapshot);
150     isProfiling_ = true;
151     if (progress != nullptr) {
152         progress->ReportProgress(heapCount, heapCount);
153     }
154     if (!stream->Good()) {
155         FileStream newStream(GenDumpFileName(dumpFormat));
156         auto serializerResult = jsonSerializer_->Serialize(snapshot, &newStream);
157         GetChunk()->Delete(snapshot);
158         return serializerResult;
159     }
160     auto serializerResult = jsonSerializer_->Serialize(snapshot, stream);
161     GetChunk()->Delete(snapshot);
162     return serializerResult;
163 }
164 
StartHeapTracking(double timeInterval,bool isVmMode,Stream * stream,bool traceAllocation,bool newThread)165 bool HeapProfiler::StartHeapTracking(double timeInterval, bool isVmMode, Stream *stream,
166                                      bool traceAllocation, bool newThread)
167 {
168     HeapSnapshot *snapshot = MakeHeapSnapshot(SampleType::REAL_TIME, isVmMode, false, false, traceAllocation);
169     if (snapshot == nullptr) {
170         return false;
171     }
172     isProfiling_ = true;
173     UpdateHeapObjects(snapshot);
174     heapTracker_ = std::make_unique<HeapTracker>(snapshot, timeInterval, stream);
175     const_cast<EcmaVM *>(vm_)->StartHeapTracking();
176     if (newThread) {
177         heapTracker_->StartTracing();
178     }
179 
180     return true;
181 }
182 
UpdateHeapTracking(Stream * stream)183 bool HeapProfiler::UpdateHeapTracking(Stream *stream)
184 {
185     if (heapTracker_ == nullptr) {
186         return false;
187     }
188     HeapSnapshot *snapshot = heapTracker_->GetHeapSnapshot();
189     if (snapshot == nullptr) {
190         return false;
191     }
192     snapshot->RecordSampleTime();
193     UpdateHeapObjects(snapshot);
194 
195     if (stream != nullptr) {
196         snapshot->PushHeapStat(stream);
197     }
198 
199     return true;
200 }
201 
StopHeapTracking(Stream * stream,Progress * progress,bool newThread)202 bool HeapProfiler::StopHeapTracking(Stream *stream, Progress *progress, bool newThread)
203 {
204     if (heapTracker_ == nullptr) {
205         return false;
206     }
207     int32_t heapCount = static_cast<int32_t>(vm_->GetHeap()->GetHeapObjectCount());
208 
209     const_cast<EcmaVM *>(vm_)->StopHeapTracking();
210     if (newThread) {
211         heapTracker_->StopTracing();
212     }
213 
214     HeapSnapshot *snapshot = heapTracker_->GetHeapSnapshot();
215     if (snapshot == nullptr) {
216         return false;
217     }
218 
219     if (progress != nullptr) {
220         progress->ReportProgress(0, heapCount);
221     }
222     snapshot->FinishSnapshot();
223     isProfiling_ = false;
224     if (progress != nullptr) {
225         progress->ReportProgress(heapCount, heapCount);
226     }
227     return jsonSerializer_->Serialize(snapshot, stream);
228 }
229 
GenDumpFileName(DumpFormat dumpFormat)230 std::string HeapProfiler::GenDumpFileName(DumpFormat dumpFormat)
231 {
232     CString filename("hprof_");
233     switch (dumpFormat) {
234         case DumpFormat::JSON:
235             filename.append(GetTimeStamp());
236             break;
237         case DumpFormat::BINARY:
238             filename.append("unimplemented");
239             break;
240         case DumpFormat::OTHER:
241             filename.append("unimplemented");
242             break;
243         default:
244             filename.append("unimplemented");
245             break;
246     }
247     filename.append(".heapsnapshot");
248     return ConvertToStdString(filename);
249 }
250 
GetTimeStamp()251 CString HeapProfiler::GetTimeStamp()
252 {
253     std::time_t timeSource = std::time(nullptr);
254     struct tm tm {
255     };
256     struct tm *timeData = localtime_r(&timeSource, &tm);
257     if (timeData == nullptr) {
258         LOG_FULL(FATAL) << "localtime_r failed";
259         UNREACHABLE();
260     }
261     CString stamp;
262     const int TIME_START = 1900;
263     stamp.append(ToCString(timeData->tm_year + TIME_START))
264         .append("-")
265         .append(ToCString(timeData->tm_mon + 1))
266         .append("-")
267         .append(ToCString(timeData->tm_mday))
268         .append("_")
269         .append(ToCString(timeData->tm_hour))
270         .append("-")
271         .append(ToCString(timeData->tm_min))
272         .append("-")
273         .append(ToCString(timeData->tm_sec));
274     return stamp;
275 }
276 
ForceFullGC(const EcmaVM * vm)277 bool HeapProfiler::ForceFullGC(const EcmaVM *vm)
278 {
279     if (vm->IsInitialized()) {
280         const_cast<Heap *>(vm->GetHeap())->CollectGarbage(TriggerGCType::FULL_GC);
281         return true;
282     }
283     return false;
284 }
285 
MakeHeapSnapshot(SampleType sampleType,bool isVmMode,bool isPrivate,bool captureNumericValue,bool traceAllocation)286 HeapSnapshot *HeapProfiler::MakeHeapSnapshot(SampleType sampleType, bool isVmMode, bool isPrivate,
287                                              bool captureNumericValue, bool traceAllocation)
288 {
289     LOG_ECMA(INFO) << "HeapProfiler::MakeHeapSnapshot";
290     DISALLOW_GARBAGE_COLLECTION;
291     const_cast<Heap *>(vm_->GetHeap())->Prepare();
292     switch (sampleType) {
293         case SampleType::ONE_SHOT: {
294             auto *snapshot = GetChunk()->New<HeapSnapshot>(vm_, isVmMode, isPrivate, captureNumericValue,
295                                                            traceAllocation, entryIdMap_, GetChunk());
296             if (snapshot == nullptr) {
297                 LOG_FULL(FATAL) << "alloc snapshot failed";
298                 UNREACHABLE();
299             }
300             snapshot->BuildUp();
301             return snapshot;
302         }
303         case SampleType::REAL_TIME: {
304             auto *snapshot = GetChunk()->New<HeapSnapshot>(vm_, isVmMode, isPrivate, captureNumericValue,
305                                                            traceAllocation, entryIdMap_, GetChunk());
306             if (snapshot == nullptr) {
307                 LOG_FULL(FATAL) << "alloc snapshot failed";
308                 UNREACHABLE();
309             }
310             AddSnapshot(snapshot);
311             snapshot->PrepareSnapshot();
312             return snapshot;
313         }
314         default:
315             return nullptr;
316     }
317 }
318 
AddSnapshot(HeapSnapshot * snapshot)319 void HeapProfiler::AddSnapshot(HeapSnapshot *snapshot)
320 {
321     if (hprofs_.size() >= MAX_NUM_HPROF) {
322         ClearSnapshot();
323     }
324     ASSERT(snapshot != nullptr);
325     hprofs_.emplace_back(snapshot);
326 }
327 
ClearSnapshot()328 void HeapProfiler::ClearSnapshot()
329 {
330     for (auto *snapshot : hprofs_) {
331         GetChunk()->Delete(snapshot);
332     }
333     hprofs_.clear();
334 }
335 
StartHeapSampling(uint64_t samplingInterval,int stackDepth)336 bool HeapProfiler::StartHeapSampling(uint64_t samplingInterval, int stackDepth)
337 {
338     if (heapSampling_.get()) {
339         LOG_ECMA(ERROR) << "Do not start heap sampling twice in a row.";
340         return false;
341     }
342     heapSampling_ = std::make_unique<HeapSampling>(vm_, const_cast<Heap *>(vm_->GetHeap()),
343                                                    samplingInterval, stackDepth);
344     return true;
345 }
346 
StopHeapSampling()347 void HeapProfiler::StopHeapSampling()
348 {
349     heapSampling_.reset();
350 }
351 
GetAllocationProfile()352 const struct SamplingInfo *HeapProfiler::GetAllocationProfile()
353 {
354     if (!heapSampling_.get()) {
355         LOG_ECMA(ERROR) << "Heap sampling was not started, please start firstly.";
356         return nullptr;
357     }
358     return heapSampling_->GetAllocationProfile();
359 }
360 }  // namespace panda::ecmascript
361