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
18namespace panda::ecmascript::tooling {
19static constexpr int32_t MILLI_TO_MICRO = 1000;
20static 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
23void HeapProfilerImpl::InitializeExtendedProtocolsList()
24{
25    std::vector<std::string> heapProfilerProtocolList {};
26    heapProfilerExtendedProtocols_ = std::move(heapProfilerProtocolList);
27}
28
29void 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
76HeapProfilerImpl::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
107void 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
118void HeapProfilerImpl::DispatcherImpl::CollectGarbage(const DispatchRequest &request)
119{
120    DispatchResponse response = heapprofiler_->CollectGarbage();
121    SendResponse(request, response);
122}
123
124void 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
132void HeapProfilerImpl::DispatcherImpl::Disable(const DispatchRequest &request)
133{
134    DispatchResponse response = heapprofiler_->Disable();
135    SendResponse(request, response);
136}
137
138void 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
152void 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
171void 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
185void 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
196void 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
209void 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
221void 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
232void 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
243bool HeapProfilerImpl::Frontend::AllowNotify() const
244{
245    return channel_ != nullptr;
246}
247
248void 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
263void 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
277void 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
293void 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
307void HeapProfilerImpl::Frontend::ResetProfiles()
308{
309    if (!AllowNotify()) {
310        return;
311    }
312}
313
314HeapProfilerImpl::~HeapProfilerImpl()
315{
316    uv_timer_stop(&handle_);
317}
318
319DispatchResponse HeapProfilerImpl::AddInspectedHeapObject([[maybe_unused]] const AddInspectedHeapObjectParams &params)
320{
321    return DispatchResponse::Fail("AddInspectedHeapObject not support now");
322}
323
324DispatchResponse HeapProfilerImpl::CollectGarbage()
325{
326    panda::JSNApi::TriggerGC(vm_, panda::JSNApi::TRIGGER_GC_TYPE::FULL_GC);
327    panda::JSNApi::TriggerGC(vm_, panda::JSNApi::TRIGGER_GC_TYPE::SHARED_FULL_GC);
328    return DispatchResponse::Ok();
329}
330
331DispatchResponse HeapProfilerImpl::Enable()
332{
333    return DispatchResponse::Ok();
334}
335
336DispatchResponse HeapProfilerImpl::Disable()
337{
338    panda::DFXJSNApi::DestroyHeapProfiler(vm_);
339    return DispatchResponse::Ok();
340}
341
342DispatchResponse HeapProfilerImpl::GetHeapObjectId([[maybe_unused]] const GetHeapObjectIdParams &params,
343    HeapSnapshotObjectId *objectId)
344{
345    ASSERT(objectId != nullptr);
346    *objectId = 0;
347    return DispatchResponse::Fail("GetHeapObjectId not support now");
348}
349
350DispatchResponse HeapProfilerImpl::GetObjectByHeapObjectId([[maybe_unused]] const GetObjectByHeapObjectIdParams &params,
351    [[maybe_unused]] std::unique_ptr<RemoteObject> *remoteObjectResult)
352{
353    return DispatchResponse::Fail("GetObjectByHeapObjectId not support now");
354}
355
356DispatchResponse HeapProfilerImpl::GetSamplingProfile([[maybe_unused]] std::unique_ptr<SamplingHeapProfile> *profile)
357{
358    auto samplingInfo = panda::DFXJSNApi::GetAllocationProfile(vm_);
359    if (samplingInfo == nullptr) {
360        return DispatchResponse::Fail("GetSamplingProfile fail");
361    }
362    *profile = SamplingHeapProfile::FromSamplingInfo(std::move(samplingInfo));
363    return DispatchResponse::Ok();
364}
365
366DispatchResponse HeapProfilerImpl::StartSampling([[maybe_unused]] const StartSamplingParams &params)
367{
368    panda::JSNApi::SetProfilerState(vm_, true);
369    uint64_t samplingInterval = static_cast<uint64_t>(params.GetSamplingInterval());
370    bool result = panda::DFXJSNApi::StartSampling(vm_, samplingInterval);
371    if (result) {
372        return DispatchResponse::Ok();
373    }
374    return DispatchResponse::Fail("StartSampling fail");
375}
376
377DispatchResponse HeapProfilerImpl::StopSampling([[maybe_unused]] std::unique_ptr<SamplingHeapProfile> *profile)
378{
379    DispatchResponse samplingProfile = GetSamplingProfile(profile);
380    if (samplingProfile.IsOk()) {
381        panda::DFXJSNApi::StopSampling(vm_);
382        panda::JSNApi::SetProfilerState(vm_, false);
383        return DispatchResponse::Ok();
384    }
385    return DispatchResponse::Fail("StopSampling fail");
386}
387
388DispatchResponse HeapProfilerImpl::StartTrackingHeapObjects(const StartTrackingHeapObjectsParams &params)
389{
390    panda::JSNApi::SetProfilerState(vm_, true);
391    if (uv_is_active(reinterpret_cast<uv_handle_t*>(&handle_))) {
392        return DispatchResponse::Ok();
393    }
394    bool traceAllocation = params.GetTrackAllocations();
395    bool result = panda::DFXJSNApi::StartHeapTracking(vm_, INTERVAL, true, &stream_, traceAllocation, false);
396
397    uv_loop_t *loop = reinterpret_cast<uv_loop_t *>(vm_->GetLoop());
398    if (loop == nullptr) {
399        return DispatchResponse::Fail("Loop is nullptr");
400    }
401    uv_timer_init(loop, &handle_);
402    handle_.data = this;
403    uv_timer_start(&handle_, HeapTrackingCallback, 0, INTERVAL * MILLI_TO_MICRO);
404    if (DebuggerApi::IsMainThread()) {
405        uv_async_send(&loop->wq_async);
406    } else {
407        uv_work_t *work = new uv_work_t;
408        uv_queue_work(loop, work, [](uv_work_t *) { }, [](uv_work_t *work, int32_t) { delete work; });
409    }
410
411    if (result) {
412        return DispatchResponse::Ok();
413    } else {
414        return DispatchResponse::Fail("StartHeapTracking fail");
415    }
416}
417
418void HeapProfilerImpl::HeapTrackingCallback(uv_timer_t* handle)
419{
420    HeapProfilerImpl *heapProfilerImpl = static_cast<HeapProfilerImpl *>(handle->data);
421    if (heapProfilerImpl == nullptr) {
422        return;
423    }
424    panda::DFXJSNApi::UpdateHeapTracking(heapProfilerImpl->vm_, &(heapProfilerImpl->stream_));
425}
426
427DispatchResponse HeapProfilerImpl::StopTrackingHeapObjects(const StopTrackingHeapObjectsParams &params)
428{
429    uv_timer_stop(&handle_);
430    bool result = false;
431    if (params.GetReportProgress()) {
432        HeapProfilerProgress progress(&frontend_);
433        result = panda::DFXJSNApi::StopHeapTracking(vm_, &stream_, &progress, false);
434    } else {
435        result = panda::DFXJSNApi::StopHeapTracking(vm_, &stream_, nullptr, false);
436    }
437    if (result) {
438        panda::JSNApi::SetProfilerState(vm_, false);
439        return DispatchResponse::Ok();
440    } else {
441        return DispatchResponse::Fail("StopHeapTracking fail");
442    }
443}
444
445DispatchResponse HeapProfilerImpl::TakeHeapSnapshot(const StopTrackingHeapObjectsParams &params)
446{
447    bool captureNumericValue = params.GetCaptureNumericValue();
448    DumpSnapShotOption dumpOption;
449    dumpOption.dumpFormat = DumpFormat::JSON;
450    dumpOption.isVmMode = true;
451    dumpOption.isPrivate = false;
452    dumpOption.captureNumericValue = captureNumericValue;
453    if (params.GetReportProgress()) {
454        HeapProfilerProgress progress(&frontend_);
455        panda::DFXJSNApi::DumpHeapSnapshot(vm_, &stream_, dumpOption, &progress);
456    } else {
457        panda::DFXJSNApi::DumpHeapSnapshot(vm_, &stream_, dumpOption);
458    }
459    return DispatchResponse::Ok();
460}
461}  // namespace panda::ecmascript::tooling
462