• 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/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