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 "agent/heapprofiler_impl.h"
17
18 namespace panda::ecmascript::tooling {
19 static constexpr int32_t MILLI_TO_MICRO = 1000;
20 static constexpr double INTERVAL = 0.2;
21 // Whenever adding a new protocol which is not a standard CDP protocol,
22 // must add its methodName to the heapProfilerProtocolList
InitializeExtendedProtocolsList()23 void HeapProfilerImpl::InitializeExtendedProtocolsList()
24 {
25 std::vector<std::string> heapProfilerProtocolList {};
26 heapProfilerExtendedProtocols_ = std::move(heapProfilerProtocolList);
27 }
28
Dispatch(const DispatchRequest & request)29 void HeapProfilerImpl::DispatcherImpl::Dispatch(const DispatchRequest &request)
30 {
31 Method method = GetMethodEnum(request.GetMethod());
32 LOG_DEBUGGER(DEBUG) << "dispatch [" << request.GetMethod() << "] to HeapProfilerImpl";
33 switch (method) {
34 case Method::ADD_INSPECTED_HEAP_OBJECT:
35 AddInspectedHeapObject(request);
36 break;
37 case Method::COLLECT_GARBAGE:
38 CollectGarbage(request);
39 break;
40 case Method::ENABLE:
41 Enable(request);
42 break;
43 case Method::DISABLE:
44 Disable(request);
45 break;
46 case Method::GET_HEAP_OBJECT_ID:
47 GetHeapObjectId(request);
48 break;
49 case Method::GET_OBJECT_BY_HEAP_OBJECT_ID:
50 GetObjectByHeapObjectId(request);
51 break;
52 case Method::GET_SAMPLING_PROFILE:
53 GetSamplingProfile(request);
54 break;
55 case Method::START_SAMPLING:
56 StartSampling(request);
57 break;
58 case Method::START_TRACKING_HEAP_OBJECTS:
59 StartTrackingHeapObjects(request);
60 break;
61 case Method::STOP_SAMPLING:
62 StopSampling(request);
63 break;
64 case Method::STOP_TRACKING_HEAP_OBJECTS:
65 StopTrackingHeapObjects(request);
66 break;
67 case Method::TAKE_HEAP_SNAPSHOT:
68 TakeHeapSnapshot(request);
69 break;
70 default:
71 SendResponse(request, DispatchResponse::Fail("Unknown method: " + request.GetMethod()));
72 break;
73 }
74 }
75
GetMethodEnum(const std::string & method)76 HeapProfilerImpl::DispatcherImpl::Method HeapProfilerImpl::DispatcherImpl::GetMethodEnum(const std::string& method)
77 {
78 if (method == "addInspectedHeapObject") {
79 return Method::ADD_INSPECTED_HEAP_OBJECT;
80 } else if (method == "collectGarbage") {
81 return Method::COLLECT_GARBAGE;
82 } else if (method == "enable") {
83 return Method::ENABLE;
84 } else if (method == "disable") {
85 return Method::DISABLE;
86 } else if (method == "getHeapObjectId") {
87 return Method::GET_HEAP_OBJECT_ID;
88 } else if (method == "getObjectByHeapObjectId") {
89 return Method::GET_OBJECT_BY_HEAP_OBJECT_ID;
90 } else if (method == "getSamplingProfile") {
91 return Method::GET_SAMPLING_PROFILE;
92 } else if (method == "startSampling") {
93 return Method::START_SAMPLING;
94 } else if (method == "startTrackingHeapObjects") {
95 return Method::START_TRACKING_HEAP_OBJECTS;
96 } else if (method == "stopSampling") {
97 return Method::STOP_SAMPLING;
98 } else if (method == "stopTrackingHeapObjects") {
99 return Method::STOP_TRACKING_HEAP_OBJECTS;
100 } else if (method == "takeHeapSnapshot") {
101 return Method::TAKE_HEAP_SNAPSHOT;
102 } else {
103 return Method::UNKNOWN;
104 }
105 }
106
AddInspectedHeapObject(const DispatchRequest & request)107 void HeapProfilerImpl::DispatcherImpl::AddInspectedHeapObject(const DispatchRequest &request)
108 {
109 std::unique_ptr<AddInspectedHeapObjectParams> params = AddInspectedHeapObjectParams::Create(request.GetParams());
110 if (params == nullptr) {
111 SendResponse(request, DispatchResponse::Fail("wrong params"));
112 return;
113 }
114 DispatchResponse response = heapprofiler_->AddInspectedHeapObject(*params);
115 SendResponse(request, response);
116 }
117
CollectGarbage(const DispatchRequest & request)118 void HeapProfilerImpl::DispatcherImpl::CollectGarbage(const DispatchRequest &request)
119 {
120 DispatchResponse response = heapprofiler_->CollectGarbage();
121 SendResponse(request, response);
122 }
123
Enable(const DispatchRequest & request)124 void HeapProfilerImpl::DispatcherImpl::Enable(const DispatchRequest &request)
125 {
126 DispatchResponse response = heapprofiler_->Enable();
127 heapprofiler_->InitializeExtendedProtocolsList();
128 EnableReturns result(heapprofiler_->heapProfilerExtendedProtocols_);
129 SendResponse(request, response, result);
130 }
131
Disable(const DispatchRequest & request)132 void HeapProfilerImpl::DispatcherImpl::Disable(const DispatchRequest &request)
133 {
134 DispatchResponse response = heapprofiler_->Disable();
135 SendResponse(request, response);
136 }
137
GetHeapObjectId(const DispatchRequest & request)138 void HeapProfilerImpl::DispatcherImpl::GetHeapObjectId(const DispatchRequest &request)
139 {
140 std::unique_ptr<GetHeapObjectIdParams> params = GetHeapObjectIdParams::Create(request.GetParams());
141 if (params == nullptr) {
142 SendResponse(request, DispatchResponse::Fail("wrong params"));
143 return;
144 }
145
146 HeapSnapshotObjectId objectId;
147 DispatchResponse response = heapprofiler_->GetHeapObjectId(*params, &objectId);
148 GetHeapObjectIdReturns result(std::move(objectId));
149 SendResponse(request, response, result);
150 }
151
GetObjectByHeapObjectId(const DispatchRequest & request)152 void HeapProfilerImpl::DispatcherImpl::GetObjectByHeapObjectId(const DispatchRequest &request)
153 {
154 std::unique_ptr<GetObjectByHeapObjectIdParams> params = GetObjectByHeapObjectIdParams::Create(request.GetParams());
155 if (params == nullptr) {
156 SendResponse(request, DispatchResponse::Fail("wrong params"));
157 return;
158 }
159
160 std::unique_ptr<RemoteObject> remoteObjectResult;
161 DispatchResponse response = heapprofiler_->GetObjectByHeapObjectId(*params, &remoteObjectResult);
162 if (remoteObjectResult == nullptr) {
163 SendResponse(request, response);
164 return;
165 }
166
167 GetObjectByHeapObjectIdReturns result(std::move(remoteObjectResult));
168 SendResponse(request, response, result);
169 }
170
GetSamplingProfile(const DispatchRequest & request)171 void HeapProfilerImpl::DispatcherImpl::GetSamplingProfile(const DispatchRequest &request)
172 {
173 std::unique_ptr<SamplingHeapProfile> profile;
174 DispatchResponse response = heapprofiler_->GetSamplingProfile(&profile);
175 if (profile == nullptr) {
176 SendResponse(request, response);
177 return;
178 }
179
180 // The return value type of GetSamplingProfile is the same as of StopSampling.
181 StopSamplingReturns result(std::move(profile));
182 SendResponse(request, response, result);
183 }
184
StartSampling(const DispatchRequest & request)185 void HeapProfilerImpl::DispatcherImpl::StartSampling(const DispatchRequest &request)
186 {
187 std::unique_ptr<StartSamplingParams> params = StartSamplingParams::Create(request.GetParams());
188 if (params == nullptr) {
189 SendResponse(request, DispatchResponse::Fail("wrong params"));
190 return;
191 }
192 DispatchResponse response = heapprofiler_->StartSampling(*params);
193 SendResponse(request, response);
194 }
195
StopSampling(const DispatchRequest & request)196 void HeapProfilerImpl::DispatcherImpl::StopSampling(const DispatchRequest &request)
197 {
198 std::unique_ptr<SamplingHeapProfile> profile;
199 DispatchResponse response = heapprofiler_->StopSampling(&profile);
200 if (profile == nullptr) {
201 SendResponse(request, response);
202 return;
203 }
204
205 StopSamplingReturns result(std::move(profile));
206 SendResponse(request, response, result);
207 }
208
StartTrackingHeapObjects(const DispatchRequest & request)209 void HeapProfilerImpl::DispatcherImpl::StartTrackingHeapObjects(const DispatchRequest &request)
210 {
211 std::unique_ptr<StartTrackingHeapObjectsParams> params =
212 StartTrackingHeapObjectsParams::Create(request.GetParams());
213 if (params == nullptr) {
214 SendResponse(request, DispatchResponse::Fail("wrong params"));
215 return;
216 }
217 DispatchResponse response = heapprofiler_->StartTrackingHeapObjects(*params);
218 SendResponse(request, response);
219 }
220
StopTrackingHeapObjects(const DispatchRequest & request)221 void HeapProfilerImpl::DispatcherImpl::StopTrackingHeapObjects(const DispatchRequest &request)
222 {
223 std::unique_ptr<StopTrackingHeapObjectsParams> params = StopTrackingHeapObjectsParams::Create(request.GetParams());
224 if (params == nullptr) {
225 SendResponse(request, DispatchResponse::Fail("wrong params"));
226 return;
227 }
228 DispatchResponse response = heapprofiler_->StopTrackingHeapObjects(*params);
229 SendResponse(request, response);
230 }
231
TakeHeapSnapshot(const DispatchRequest & request)232 void HeapProfilerImpl::DispatcherImpl::TakeHeapSnapshot(const DispatchRequest &request)
233 {
234 std::unique_ptr<StopTrackingHeapObjectsParams> params = StopTrackingHeapObjectsParams::Create(request.GetParams());
235 if (params == nullptr) {
236 SendResponse(request, DispatchResponse::Fail("wrong params"));
237 return;
238 }
239 DispatchResponse response = heapprofiler_->TakeHeapSnapshot(*params);
240 SendResponse(request, response);
241 }
242
AllowNotify() const243 bool HeapProfilerImpl::Frontend::AllowNotify() const
244 {
245 return channel_ != nullptr;
246 }
247
AddHeapSnapshotChunk(char * data,int32_t size)248 void HeapProfilerImpl::Frontend::AddHeapSnapshotChunk(char *data, int32_t size)
249 {
250 if (!AllowNotify()) {
251 return;
252 }
253
254 tooling::AddHeapSnapshotChunk addHeapSnapshotChunk;
255 addHeapSnapshotChunk.GetChunk().resize(size);
256 for (int32_t i = 0; i < size; ++i) {
257 addHeapSnapshotChunk.GetChunk()[i] = data[i];
258 }
259
260 channel_->SendNotification(addHeapSnapshotChunk);
261 }
262
ReportHeapSnapshotProgress(int32_t done,int32_t total)263 void HeapProfilerImpl::Frontend::ReportHeapSnapshotProgress(int32_t done, int32_t total)
264 {
265 if (!AllowNotify()) {
266 return;
267 }
268
269 tooling::ReportHeapSnapshotProgress reportHeapSnapshotProgress;
270 reportHeapSnapshotProgress.SetDone(done).SetTotal(total);
271 if (done >= total) {
272 reportHeapSnapshotProgress.SetFinished(true);
273 }
274 channel_->SendNotification(reportHeapSnapshotProgress);
275 }
276
HeapStatsUpdate(HeapStat * updateData,int32_t count)277 void HeapProfilerImpl::Frontend::HeapStatsUpdate(HeapStat* updateData, int32_t count)
278 {
279 if (!AllowNotify()) {
280 return;
281 }
282 std::vector<int32_t> statsDiff;
283 for (int32_t i = 0; i < count; ++i) {
284 statsDiff.emplace_back(updateData[i].index_);
285 statsDiff.emplace_back(updateData[i].count_);
286 statsDiff.emplace_back(updateData[i].size_);
287 }
288 tooling::HeapStatsUpdate heapStatsUpdate;
289 heapStatsUpdate.SetStatsUpdate(std::move(statsDiff));
290 channel_->SendNotification(heapStatsUpdate);
291 }
292
LastSeenObjectId(int32_t lastSeenObjectId,int64_t timeStampUs)293 void HeapProfilerImpl::Frontend::LastSeenObjectId(int32_t lastSeenObjectId, int64_t timeStampUs)
294 {
295 if (!AllowNotify()) {
296 return;
297 }
298
299 tooling::LastSeenObjectId lastSeenObjectIdEvent;
300 lastSeenObjectIdEvent.SetLastSeenObjectId(lastSeenObjectId);
301 const int THOUSAND = 1000;
302 double timestampMS = static_cast<double>(timeStampUs) / THOUSAND;
303 lastSeenObjectIdEvent.SetTimestamp(timestampMS);
304 channel_->SendNotification(lastSeenObjectIdEvent);
305 }
306
ResetProfiles()307 void HeapProfilerImpl::Frontend::ResetProfiles()
308 {
309 if (!AllowNotify()) {
310 return;
311 }
312 }
313
~HeapProfilerImpl()314 HeapProfilerImpl::~HeapProfilerImpl()
315 {
316 uv_loop_t *loop = reinterpret_cast<uv_loop_t *>(vm_->GetLoop());
317 if (handle_!= nullptr && loop != nullptr) {
318 uv_close(reinterpret_cast<uv_handle_t*>(handle_), [](uv_handle_t* handle) {
319 delete reinterpret_cast<uv_timer_t*>(handle);
320 handle = nullptr;
321 });
322 }
323 }
324
AddInspectedHeapObject(const AddInspectedHeapObjectParams & params)325 DispatchResponse HeapProfilerImpl::AddInspectedHeapObject([[maybe_unused]] const AddInspectedHeapObjectParams ¶ms)
326 {
327 return DispatchResponse::Fail("AddInspectedHeapObject not support now");
328 }
329
CollectGarbage()330 DispatchResponse HeapProfilerImpl::CollectGarbage()
331 {
332 panda::JSNApi::TriggerGC(vm_, panda::JSNApi::TRIGGER_GC_TYPE::FULL_GC);
333 panda::JSNApi::TriggerGC(vm_, panda::JSNApi::TRIGGER_GC_TYPE::SHARED_FULL_GC);
334 return DispatchResponse::Ok();
335 }
336
Enable()337 DispatchResponse HeapProfilerImpl::Enable()
338 {
339 return DispatchResponse::Ok();
340 }
341
Disable()342 DispatchResponse HeapProfilerImpl::Disable()
343 {
344 panda::DFXJSNApi::DestroyHeapProfiler(vm_);
345 return DispatchResponse::Ok();
346 }
347
GetHeapObjectId(const GetHeapObjectIdParams & params,HeapSnapshotObjectId * objectId)348 DispatchResponse HeapProfilerImpl::GetHeapObjectId([[maybe_unused]] const GetHeapObjectIdParams ¶ms,
349 HeapSnapshotObjectId *objectId)
350 {
351 ASSERT(objectId != nullptr);
352 *objectId = 0;
353 return DispatchResponse::Fail("GetHeapObjectId not support now");
354 }
355
GetObjectByHeapObjectId(const GetObjectByHeapObjectIdParams & params,std::unique_ptr<RemoteObject> * remoteObjectResult)356 DispatchResponse HeapProfilerImpl::GetObjectByHeapObjectId([[maybe_unused]] const GetObjectByHeapObjectIdParams ¶ms,
357 [[maybe_unused]] std::unique_ptr<RemoteObject> *remoteObjectResult)
358 {
359 return DispatchResponse::Fail("GetObjectByHeapObjectId not support now");
360 }
361
GetSamplingProfile(std::unique_ptr<SamplingHeapProfile> * profile)362 DispatchResponse HeapProfilerImpl::GetSamplingProfile([[maybe_unused]] std::unique_ptr<SamplingHeapProfile> *profile)
363 {
364 auto samplingInfo = panda::DFXJSNApi::GetAllocationProfile(vm_);
365 if (samplingInfo == nullptr) {
366 return DispatchResponse::Fail("GetSamplingProfile fail");
367 }
368 *profile = SamplingHeapProfile::FromSamplingInfo(std::move(samplingInfo));
369 return DispatchResponse::Ok();
370 }
371
StartSampling(const StartSamplingParams & params)372 DispatchResponse HeapProfilerImpl::StartSampling([[maybe_unused]] const StartSamplingParams ¶ms)
373 {
374 panda::JSNApi::SetProfilerState(vm_, true);
375 uint64_t samplingInterval = static_cast<uint64_t>(params.GetSamplingInterval());
376 bool result = panda::DFXJSNApi::StartSampling(vm_, samplingInterval);
377 if (result) {
378 return DispatchResponse::Ok();
379 }
380 return DispatchResponse::Fail("StartSampling fail");
381 }
382
StopSampling(std::unique_ptr<SamplingHeapProfile> * profile)383 DispatchResponse HeapProfilerImpl::StopSampling([[maybe_unused]] std::unique_ptr<SamplingHeapProfile> *profile)
384 {
385 DispatchResponse samplingProfile = GetSamplingProfile(profile);
386 if (samplingProfile.IsOk()) {
387 panda::DFXJSNApi::StopSampling(vm_);
388 panda::JSNApi::SetProfilerState(vm_, false);
389 return DispatchResponse::Ok();
390 }
391 return DispatchResponse::Fail("StopSampling fail");
392 }
393
StartTrackingHeapObjects(const StartTrackingHeapObjectsParams & params)394 DispatchResponse HeapProfilerImpl::StartTrackingHeapObjects(const StartTrackingHeapObjectsParams ¶ms)
395 {
396 panda::JSNApi::SetProfilerState(vm_, true);
397 if (handle_ != nullptr && uv_is_active(reinterpret_cast<uv_handle_t*>(handle_))) {
398 return DispatchResponse::Ok();
399 }
400 bool traceAllocation = params.GetTrackAllocations();
401 bool result = panda::DFXJSNApi::StartHeapTracking(vm_, INTERVAL, true, &stream_, traceAllocation, false);
402
403 uv_loop_t *loop = reinterpret_cast<uv_loop_t *>(vm_->GetLoop());
404 if (loop == nullptr) {
405 return DispatchResponse::Fail("Loop is nullptr");
406 }
407 if (handle_ == nullptr) {
408 handle_ = new uv_timer_t;
409 }
410 uv_timer_init(loop, handle_);
411 handle_->data = this;
412 uv_timer_start(handle_, HeapTrackingCallback, 0, INTERVAL * MILLI_TO_MICRO);
413 if (DebuggerApi::IsMainThread()) {
414 uv_async_send(&loop->wq_async);
415 } else {
416 uv_work_t *work = new uv_work_t;
417 uv_queue_work(loop, work, [](uv_work_t *) { }, [](uv_work_t *work, int32_t) { delete work; });
418 }
419
420 if (result) {
421 return DispatchResponse::Ok();
422 } else {
423 return DispatchResponse::Fail("StartHeapTracking fail");
424 }
425 }
426
HeapTrackingCallback(uv_timer_t * handle)427 void HeapProfilerImpl::HeapTrackingCallback(uv_timer_t* handle)
428 {
429 HeapProfilerImpl *heapProfilerImpl = static_cast<HeapProfilerImpl *>(handle->data);
430 if (heapProfilerImpl == nullptr) {
431 return;
432 }
433 panda::DFXJSNApi::UpdateHeapTracking(heapProfilerImpl->vm_, &(heapProfilerImpl->stream_));
434 }
435
StopTrackingHeapObjects(const StopTrackingHeapObjectsParams & params)436 DispatchResponse HeapProfilerImpl::StopTrackingHeapObjects(const StopTrackingHeapObjectsParams ¶ms)
437 {
438 if (handle_ != nullptr) {
439 uv_timer_stop(handle_);
440 }
441 bool result = false;
442 if (params.GetReportProgress()) {
443 HeapProfilerProgress progress(&frontend_);
444 result = panda::DFXJSNApi::StopHeapTracking(vm_, &stream_, &progress, false);
445 } else {
446 result = panda::DFXJSNApi::StopHeapTracking(vm_, &stream_, nullptr, false);
447 }
448 if (result) {
449 panda::JSNApi::SetProfilerState(vm_, false);
450 return DispatchResponse::Ok();
451 } else {
452 return DispatchResponse::Fail("StopHeapTracking fail");
453 }
454 }
455
TakeHeapSnapshot(const StopTrackingHeapObjectsParams & params)456 DispatchResponse HeapProfilerImpl::TakeHeapSnapshot(const StopTrackingHeapObjectsParams ¶ms)
457 {
458 bool captureNumericValue = params.GetCaptureNumericValue();
459 DumpSnapShotOption dumpOption;
460 dumpOption.dumpFormat = DumpFormat::JSON;
461 dumpOption.isVmMode = true;
462 dumpOption.isPrivate = false;
463 dumpOption.captureNumericValue = captureNumericValue;
464 if (params.GetReportProgress()) {
465 HeapProfilerProgress progress(&frontend_);
466 panda::DFXJSNApi::DumpHeapSnapshot(vm_, &stream_, dumpOption, &progress);
467 } else {
468 panda::DFXJSNApi::DumpHeapSnapshot(vm_, &stream_, dumpOption);
469 }
470 return DispatchResponse::Ok();
471 }
472 } // namespace panda::ecmascript::tooling
473