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 ¶ms) 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 ¶ms, 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 ¶ms, 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 ¶ms) 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 ¶ms) 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 ¶ms) 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 ¶ms) 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