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/napi/include/dfx_jsnapi.h"
17
18 #include "ecmascript/base/block_hook_scope.h"
19 #include "ecmascript/dfx/hprof/heap_profiler.h"
20 #include "ecmascript/dfx/stackinfo/js_stackinfo.h"
21 #include "ecmascript/ecma_vm.h"
22 #include "ecmascript/mem/c_string.h"
23 #include "ecmascript/mem/heap-inl.h"
24 #include "ecmascript/mem/gc_stats.h"
25 #include "ecmascript/dfx/hprof/file_stream.h"
26
27 #if defined(ECMASCRIPT_SUPPORT_CPUPROFILER)
28 #include "ecmascript/dfx/cpu_profiler/cpu_profiler.h"
29 #include "ecmascript/dfx/cpu_profiler/samples_record.h"
30 #endif
31 #if defined(ENABLE_DUMP_IN_FAULTLOG)
32 #include "include/faultloggerd_client.h"
33 #endif
34
35 namespace panda {
36 using ecmascript::CString;
37 #if defined(ECMASCRIPT_SUPPORT_CPUPROFILER)
38 using ecmascript::CpuProfiler;
39 #endif
40 using ecmascript::EcmaString;
41 using ecmascript::JSTaggedValue;
42 using ecmascript::GCStats;
43 template<typename T>
44 using JSHandle = ecmascript::JSHandle<T>;
45 using ecmascript::FileStream;
46 using ecmascript::FileDescriptorStream;
47 using ecmascript::Stream;
48 using ecmascript::CMap;
49
DumpHeapSnapshot(const EcmaVM * vm,int dumpFormat,const std::string & path,bool isVmMode,bool isPrivate)50 void DFXJSNApi::DumpHeapSnapshot(const EcmaVM *vm, int dumpFormat,
51 const std::string &path, bool isVmMode, bool isPrivate)
52 {
53 FileStream stream(path);
54 DumpHeapSnapshot(vm, dumpFormat, &stream, nullptr, isVmMode, isPrivate);
55 }
56
DumpHeapSnapshot(const EcmaVM * vm,int dumpFormat,Stream * stream,Progress * progress,bool isVmMode,bool isPrivate)57 void DFXJSNApi::DumpHeapSnapshot(const EcmaVM *vm, int dumpFormat, Stream *stream, Progress *progress,
58 bool isVmMode, bool isPrivate)
59 {
60 ecmascript::base::BlockHookScope blockScope;
61 ecmascript::HeapProfilerInterface *heapProfile = ecmascript::HeapProfilerInterface::GetInstance(
62 const_cast<EcmaVM *>(vm));
63 heapProfile->DumpHeapSnapshot(ecmascript::DumpFormat(dumpFormat), stream, progress, isVmMode, isPrivate);
64 ecmascript::HeapProfilerInterface::Destroy(const_cast<EcmaVM *>(vm));
65 }
66
DumpHeapSnapshot(const EcmaVM * vm,int dumpFormat,bool isVmMode,bool isPrivate)67 void DFXJSNApi::DumpHeapSnapshot([[maybe_unused]] const EcmaVM *vm, [[maybe_unused]] int dumpFormat,
68 [[maybe_unused]] bool isVmMode, [[maybe_unused]] bool isPrivate)
69 {
70 #if defined(ENABLE_DUMP_IN_FAULTLOG)
71 #if defined(ECMASCRIPT_SUPPORT_CPUPROFILER)
72 DFXJSNApi::StopCpuProfilerForFile(vm);
73 #endif
74 auto &options = const_cast<EcmaVM *>(vm)->GetJSOptions();
75 options.SwitchStartGlobalLeakCheck();
76 if (options.EnableGlobalLeakCheck() && options.IsStartGlobalLeakCheck()) {
77 int32_t stackTraceFd = RequestFileDescriptor(static_cast<int32_t>(FaultLoggerType::JS_STACKTRACE));
78 if (stackTraceFd < 0) {
79 options.SwitchStartGlobalLeakCheck();
80 } else {
81 vm->GetJSThread()->SetStackTraceFd(stackTraceFd);
82 }
83 }
84 // Write in faultlog for heap leak.
85 int32_t fd = RequestFileDescriptor(static_cast<int32_t>(FaultLoggerType::JS_HEAP_SNAPSHOT));
86 if (fd < 0) {
87 LOG_ECMA(ERROR) << "Write FD failed, fd" << fd;
88 return;
89 }
90 FileDescriptorStream stream(fd);
91 DumpHeapSnapshot(vm, dumpFormat, &stream, nullptr, isVmMode, isPrivate);
92 #endif
93 }
94
BuildNativeAndJsStackTrace(const EcmaVM * vm,std::string & stackTraceStr)95 bool DFXJSNApi::BuildNativeAndJsStackTrace(const EcmaVM *vm, std::string &stackTraceStr)
96 {
97 LOG_ECMA(INFO) <<"BuildJsStackInfoList start";
98 std::vector<JsFrameInfo> jf = ecmascript::JsStackInfo::BuildJsStackInfo(vm->GetAssociatedJSThread());
99 LOG_ECMA(INFO) <<"BuildJsStackInfoList JsFrameInfo";
100 for (uint32_t i = 0; i < jf.size(); ++i) {
101 std::string functionname = jf[i].functionName;
102 std::string filename = jf[i].fileName;
103 std::string pos = jf[i].pos;
104 uintptr_t *nativepointer = jf[i].nativePointer;
105 LOG_ECMA(INFO) << "BuildJsStackInfoList functionname: " << functionname;
106 LOG_ECMA(INFO) << "BuildJsStackInfoList filenaem: " << filename;
107 LOG_ECMA(INFO) << "BuildJsStackInfoList pos: " << pos;
108 LOG_ECMA(INFO) << "BuildJsStackInfoList nativepointer: " << nativepointer;
109 }
110 stackTraceStr = ecmascript::JsStackInfo::BuildJsStackTrace(vm->GetAssociatedJSThread(), true);
111 if (stackTraceStr.empty()) {
112 return false;
113 }
114 return true;
115 }
116
BuildJsStackTrace(const EcmaVM * vm,std::string & stackTraceStr)117 bool DFXJSNApi::BuildJsStackTrace(const EcmaVM *vm, std::string &stackTraceStr)
118 {
119 stackTraceStr = ecmascript::JsStackInfo::BuildJsStackTrace(vm->GetAssociatedJSThread(), false);
120 if (stackTraceStr.empty()) {
121 return false;
122 }
123 return true;
124 }
125
StartHeapTracking(const EcmaVM * vm,double timeInterval,bool isVmMode,Stream * stream,bool traceAllocation,bool newThread)126 bool DFXJSNApi::StartHeapTracking(const EcmaVM *vm, double timeInterval, bool isVmMode,
127 Stream *stream, bool traceAllocation, bool newThread)
128 {
129 ecmascript::base::BlockHookScope blockScope;
130 ecmascript::HeapProfilerInterface *heapProfile = ecmascript::HeapProfilerInterface::GetInstance(
131 const_cast<EcmaVM *>(vm));
132 return heapProfile->StartHeapTracking(timeInterval, isVmMode, stream, traceAllocation, newThread);
133 }
134
UpdateHeapTracking(const EcmaVM * vm,Stream * stream)135 bool DFXJSNApi::UpdateHeapTracking(const EcmaVM *vm, Stream *stream)
136 {
137 ecmascript::base::BlockHookScope blockScope;
138 ecmascript::HeapProfilerInterface *heapProfile = ecmascript::HeapProfilerInterface::GetInstance(
139 const_cast<EcmaVM *>(vm));
140 return heapProfile->UpdateHeapTracking(stream);
141 }
142
StopHeapTracking(const EcmaVM * vm,const std::string & filePath,bool newThread)143 bool DFXJSNApi::StopHeapTracking(const EcmaVM *vm, const std::string &filePath, bool newThread)
144 {
145 FileStream stream(filePath);
146 return StopHeapTracking(vm, &stream, nullptr, newThread);
147 }
148
StopHeapTracking(const EcmaVM * vm,Stream * stream,Progress * progress,bool newThread)149 bool DFXJSNApi::StopHeapTracking(const EcmaVM *vm, Stream* stream, Progress *progress, bool newThread)
150 {
151 ecmascript::base::BlockHookScope blockScope;
152 bool result = false;
153 ecmascript::HeapProfilerInterface *heapProfile = ecmascript::HeapProfilerInterface::GetInstance(
154 const_cast<EcmaVM *>(vm));
155 result = heapProfile->StopHeapTracking(stream, progress, newThread);
156 ecmascript::HeapProfilerInterface::Destroy(const_cast<EcmaVM *>(vm));
157 return result;
158 }
159
PrintStatisticResult(const EcmaVM * vm)160 void DFXJSNApi::PrintStatisticResult(const EcmaVM *vm)
161 {
162 ecmascript::GCStats gcstats(vm->GetHeap());
163 gcstats.PrintStatisticResult(true);
164 }
165
StartRuntimeStat(EcmaVM * vm)166 void DFXJSNApi::StartRuntimeStat(EcmaVM *vm)
167 {
168 vm->SetRuntimeStatEnable(true);
169 }
170
StopRuntimeStat(EcmaVM * vm)171 void DFXJSNApi::StopRuntimeStat(EcmaVM *vm)
172 {
173 vm->SetRuntimeStatEnable(false);
174 }
175
GetArrayBufferSize(const EcmaVM * vm)176 size_t DFXJSNApi::GetArrayBufferSize(const EcmaVM *vm)
177 {
178 return vm->GetHeap()->GetArrayBufferSize();
179 }
180
GetHeapTotalSize(const EcmaVM * vm)181 size_t DFXJSNApi::GetHeapTotalSize(const EcmaVM *vm)
182 {
183 return vm->GetHeap()->GetCommittedSize();
184 }
185
GetHeapUsedSize(const EcmaVM * vm)186 size_t DFXJSNApi::GetHeapUsedSize(const EcmaVM *vm)
187 {
188 return vm->GetHeap()->GetHeapObjectSize();
189 }
190
NotifyApplicationState(EcmaVM * vm,bool inBackground)191 void DFXJSNApi::NotifyApplicationState(EcmaVM *vm, bool inBackground)
192 {
193 const_cast<ecmascript::Heap *>(vm->GetHeap())->ChangeGCParams(inBackground);
194 }
195
NotifyIdleTime(const EcmaVM * vm,int idleMicroSec)196 void DFXJSNApi::NotifyIdleTime(const EcmaVM *vm, int idleMicroSec)
197 {
198 const_cast<ecmascript::Heap *>(vm->GetHeap())->TriggerIdleCollection(idleMicroSec);
199 }
200
NotifyMemoryPressure(EcmaVM * vm,bool inHighMemoryPressure)201 void DFXJSNApi::NotifyMemoryPressure(EcmaVM *vm, bool inHighMemoryPressure)
202 {
203 const_cast<ecmascript::Heap *>(vm->GetHeap())->NotifyMemoryPressure(inHighMemoryPressure);
204 }
205 #if defined(ECMASCRIPT_SUPPORT_CPUPROFILER)
StartCpuProfilerForFile(const EcmaVM * vm,const std::string & fileName,const int interval)206 void DFXJSNApi::StartCpuProfilerForFile(const EcmaVM *vm, const std::string &fileName, const int interval)
207 {
208 if (vm == nullptr) {
209 return;
210 }
211 CpuProfiler *profiler = vm->GetProfiler();
212 if (profiler == nullptr) {
213 profiler = new CpuProfiler(vm, interval);
214 const_cast<EcmaVM *>(vm)->SetProfiler(profiler);
215 }
216 profiler->StartCpuProfilerForFile(fileName);
217 }
218
StopCpuProfilerForFile(const EcmaVM * vm)219 void DFXJSNApi::StopCpuProfilerForFile(const EcmaVM *vm)
220 {
221 if (vm == nullptr) {
222 return;
223 }
224 CpuProfiler *profiler = vm->GetProfiler();
225 if (profiler == nullptr) {
226 return;
227 }
228 profiler->StopCpuProfilerForFile();
229 delete profiler;
230 profiler = nullptr;
231 const_cast<EcmaVM *>(vm)->SetProfiler(nullptr);
232 }
233
StartCpuProfilerForInfo(const EcmaVM * vm,const int interval)234 void DFXJSNApi::StartCpuProfilerForInfo(const EcmaVM *vm, const int interval)
235 {
236 if (vm == nullptr) {
237 return;
238 }
239 CpuProfiler *profiler = vm->GetProfiler();
240 if (profiler == nullptr) {
241 profiler = new CpuProfiler(vm, interval);
242 const_cast<EcmaVM *>(vm)->SetProfiler(profiler);
243 }
244 profiler->StartCpuProfilerForInfo();
245 }
246
StopCpuProfilerForInfo(const EcmaVM * vm)247 std::unique_ptr<ProfileInfo> DFXJSNApi::StopCpuProfilerForInfo(const EcmaVM *vm)
248 {
249 if (vm == nullptr) {
250 return nullptr;
251 }
252 CpuProfiler *profiler = vm->GetProfiler();
253 if (profiler == nullptr) {
254 return nullptr;
255 }
256 auto profile = profiler->StopCpuProfilerForInfo();
257 if (profile == nullptr) {
258 LOG_DEBUGGER(ERROR) << "Transfer CpuProfiler::StopCpuProfilerImpl is failure";
259 }
260 delete profiler;
261 profiler = nullptr;
262 const_cast<EcmaVM *>(vm)->SetProfiler(nullptr);
263 return profile;
264 }
265
SetCpuSamplingInterval(const EcmaVM * vm,int interval)266 void DFXJSNApi::SetCpuSamplingInterval(const EcmaVM *vm, int interval)
267 {
268 LOG_ECMA(INFO) << "SetCpuProfilerSamplingInterval, Sampling interval is: " << interval;
269 if (vm == nullptr) {
270 return;
271 }
272 CpuProfiler *profiler = vm->GetProfiler();
273 if (profiler == nullptr) {
274 profiler = new CpuProfiler(vm, interval);
275 const_cast<EcmaVM *>(vm)->SetProfiler(profiler);
276 return;
277 }
278 profiler->SetCpuSamplingInterval(interval);
279 }
280 #endif
281
SuspendVM(const EcmaVM * vm)282 bool DFXJSNApi::SuspendVM(const EcmaVM *vm)
283 {
284 ecmascript::VmThreadControl* vmThreadControl = vm->GetAssociatedJSThread()->GetVmThreadControl();
285 return vmThreadControl->NotifyVMThreadSuspension();
286 }
287
ResumeVM(const EcmaVM * vm)288 void DFXJSNApi::ResumeVM(const EcmaVM *vm)
289 {
290 ecmascript::VmThreadControl* vmThreadControl = vm->GetAssociatedJSThread()->GetVmThreadControl();
291 vmThreadControl->ResumeVM();
292 }
293
IsSuspended(const EcmaVM * vm)294 bool DFXJSNApi::IsSuspended(const EcmaVM *vm)
295 {
296 ecmascript::VmThreadControl* vmThreadControl = vm->GetAssociatedJSThread()->GetVmThreadControl();
297 return vmThreadControl->IsSuspended();
298 }
299
CheckSafepoint(const EcmaVM * vm)300 bool DFXJSNApi::CheckSafepoint(const EcmaVM *vm)
301 {
302 ecmascript::JSThread* thread = vm->GetJSThread();
303 return thread->CheckSafepoint();
304 }
305
BuildJsStackInfoList(const EcmaVM * hostVm,uint32_t tid,std::vector<JsFrameInfo> & jsFrames)306 bool DFXJSNApi::BuildJsStackInfoList(const EcmaVM *hostVm, uint32_t tid, std::vector<JsFrameInfo>& jsFrames)
307 {
308 EcmaVM *vm;
309 if (hostVm->GetAssociatedJSThread()->GetThreadId() == tid) {
310 vm = const_cast<EcmaVM*>(hostVm);
311 } else {
312 vm = hostVm->GetWorkerVm(tid);
313 if (vm == nullptr) {
314 LOG_ECMA(INFO) << "WorkerVm is nullptr or has been damaged!";
315 return false;
316 }
317 }
318 jsFrames = ecmascript::JsStackInfo::BuildJsStackInfo(vm->GetAssociatedJSThread());
319 if (jsFrames.size() > 0) {
320 return true;
321 }
322 return false;
323 }
324 } // namespace panda
325