1/* 2 * Copyright (c) 2023 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 "tooling/client/domain/heapprofiler_client.h" 17#include "common/log_wrapper.h" 18#include "tooling/utils/utils.h" 19#include "tooling/client/session/session.h" 20 21#include <map> 22#include <functional> 23#include <cstring> 24 25using Result = panda::ecmascript::tooling::Result; 26namespace OHOS::ArkCompiler::Toolchain { 27static constexpr int32_t SAMPLING_INTERVAL = 16384; 28bool HeapProfilerClient::DispatcherCmd(const std::string &cmd, const std::string &arg) 29{ 30 path_ = arg; 31 32 std::map<std::string, std::function<int()>> dispatcherTable { 33 { "allocationtrack", std::bind(&HeapProfilerClient::AllocationTrackCommand, this)}, 34 { "allocationtrack-stop", std::bind(&HeapProfilerClient::AllocationTrackStopCommand, this)}, 35 { "heapdump", std::bind(&HeapProfilerClient::HeapDumpCommand, this)}, 36 { "heapprofiler-enable", std::bind(&HeapProfilerClient::Enable, this)}, 37 { "heapprofiler-disable", std::bind(&HeapProfilerClient::Disable, this)}, 38 { "sampling", std::bind(&HeapProfilerClient::Samping, this)}, 39 { "sampling-stop", std::bind(&HeapProfilerClient::SampingStop, this)}, 40 { "collectgarbage", std::bind(&HeapProfilerClient::CollectGarbage, this)} 41 }; 42 43 auto entry = dispatcherTable.find(cmd); 44 if (entry != dispatcherTable.end() && entry->second != nullptr) { 45 entry->second(); 46 LOGI("DispatcherCmd reqStr1: %{public}s", cmd.c_str()); 47 return true; 48 } 49 50 LOGI("unknown command: %{public}s", cmd.c_str()); 51 return false; 52} 53 54int HeapProfilerClient::HeapDumpCommand() 55{ 56 Session *session = SessionManager::getInstance().GetSessionById(sessionId_); 57 uint32_t id = session->GetMessageId(); 58 59 idEventMap_.emplace(id, HEAPDUMP); 60 std::unique_ptr<PtJson> request = PtJson::CreateObject(); 61 request->Add("id", id); 62 request->Add("method", "HeapProfiler.takeHeapSnapshot"); 63 64 std::unique_ptr<PtJson> params = PtJson::CreateObject(); 65 params->Add("reportProgress", true); 66 params->Add("captureNumericValue", true); 67 params->Add("exposeInternals", false); 68 request->Add("params", params); 69 70 std::string message = request->Stringify(); 71 if (session->ClientSendReq(message)) { 72 session->GetDomainManager().SetDomainById(id, "HeapProfiler"); 73 } 74 return 0; 75} 76 77int HeapProfilerClient::AllocationTrackCommand() 78{ 79 Session *session = SessionManager::getInstance().GetSessionById(sessionId_); 80 uint32_t id = session->GetMessageId(); 81 82 idEventMap_.emplace(id, ALLOCATION); 83 std::unique_ptr<PtJson> request = PtJson::CreateObject(); 84 request->Add("id", id); 85 request->Add("method", "HeapProfiler.startTrackingHeapObjects"); 86 87 std::unique_ptr<PtJson> params = PtJson::CreateObject(); 88 params->Add("trackAllocations", true); 89 request->Add("params", params); 90 91 std::string message = request->Stringify(); 92 if (session->ClientSendReq(message)) { 93 session->GetDomainManager().SetDomainById(id, "HeapProfiler"); 94 } 95 return 0; 96} 97 98int HeapProfilerClient::AllocationTrackStopCommand() 99{ 100 Session *session = SessionManager::getInstance().GetSessionById(sessionId_); 101 uint32_t id = session->GetMessageId(); 102 103 idEventMap_.emplace(id, ALLOCATION_STOP); 104 std::unique_ptr<PtJson> request = PtJson::CreateObject(); 105 request->Add("id", id); 106 request->Add("method", "HeapProfiler.stopTrackingHeapObjects"); 107 108 std::unique_ptr<PtJson> params = PtJson::CreateObject(); 109 params->Add("reportProgress", true); 110 request->Add("params", params); 111 112 std::string message = request->Stringify(); 113 if (session->ClientSendReq(message)) { 114 session->GetDomainManager().SetDomainById(id, "HeapProfiler"); 115 } 116 return 0; 117} 118 119int HeapProfilerClient::Enable() 120{ 121 Session *session = SessionManager::getInstance().GetSessionById(sessionId_); 122 uint32_t id = session->GetMessageId(); 123 124 idEventMap_.emplace(id, ENABLE); 125 std::unique_ptr<PtJson> request = PtJson::CreateObject(); 126 request->Add("id", id); 127 request->Add("method", "HeapProfiler.enable"); 128 129 std::unique_ptr<PtJson> params = PtJson::CreateObject(); 130 request->Add("params", params); 131 132 std::string message = request->Stringify(); 133 if (session->ClientSendReq(message)) { 134 session->GetDomainManager().SetDomainById(id, "HeapProfiler"); 135 } 136 return 0; 137} 138 139int HeapProfilerClient::Disable() 140{ 141 Session *session = SessionManager::getInstance().GetSessionById(sessionId_); 142 uint32_t id = session->GetMessageId(); 143 144 idEventMap_.emplace(id, DISABLE); 145 std::unique_ptr<PtJson> request = PtJson::CreateObject(); 146 request->Add("id", id); 147 request->Add("method", "HeapProfiler.disable"); 148 149 std::unique_ptr<PtJson> params = PtJson::CreateObject(); 150 request->Add("params", params); 151 152 std::string message = request->Stringify(); 153 if (session->ClientSendReq(message)) { 154 session->GetDomainManager().SetDomainById(id, "HeapProfiler"); 155 } 156 return 0; 157} 158 159int HeapProfilerClient::Samping() 160{ 161 Session *session = SessionManager::getInstance().GetSessionById(sessionId_); 162 uint32_t id = session->GetMessageId(); 163 164 idEventMap_.emplace(id, SAMPLING); 165 std::unique_ptr<PtJson> request = PtJson::CreateObject(); 166 request->Add("id", id); 167 request->Add("method", "HeapProfiler.startSampling"); 168 169 std::unique_ptr<PtJson> params = PtJson::CreateObject(); 170 params->Add("samplingInterval", SAMPLING_INTERVAL); 171 request->Add("params", params); 172 173 std::string message = request->Stringify(); 174 if (session->ClientSendReq(message)) { 175 session->GetDomainManager().SetDomainById(id, "HeapProfiler"); 176 } 177 return 0; 178} 179 180int HeapProfilerClient::SampingStop() 181{ 182 Session *session = SessionManager::getInstance().GetSessionById(sessionId_); 183 uint32_t id = session->GetMessageId(); 184 185 idEventMap_.emplace(id, SAMPLING_STOP); 186 std::unique_ptr<PtJson> request = PtJson::CreateObject(); 187 request->Add("id", id); 188 request->Add("method", "HeapProfiler.stopSampling"); 189 190 std::unique_ptr<PtJson> params = PtJson::CreateObject(); 191 request->Add("params", params); 192 193 std::string message = request->Stringify(); 194 if (session->ClientSendReq(message)) { 195 session->GetDomainManager().SetDomainById(id, "HeapProfiler"); 196 } 197 return 0; 198} 199 200int HeapProfilerClient::CollectGarbage() 201{ 202 Session *session = SessionManager::getInstance().GetSessionById(sessionId_); 203 uint32_t id = session->GetMessageId(); 204 205 idEventMap_.emplace(id, COLLECT_GARBAGE); 206 std::unique_ptr<PtJson> request = PtJson::CreateObject(); 207 request->Add("id", id); 208 request->Add("method", "HeapProfiler.collectGarbage"); 209 210 std::unique_ptr<PtJson> params = PtJson::CreateObject(); 211 request->Add("params", params); 212 213 std::string message = request->Stringify(); 214 if (session->ClientSendReq(message)) { 215 session->GetDomainManager().SetDomainById(id, "HeapProfiler"); 216 } 217 return 0; 218} 219 220void HeapProfilerClient::RecvReply(std::unique_ptr<PtJson> json) 221{ 222 if (json == nullptr) { 223 LOGE("json parse error"); 224 return; 225 } 226 if (!json->IsObject()) { 227 LOGE("json parse format error"); 228 json->ReleaseRoot(); 229 return; 230 } 231 std::unique_ptr<PtJson> result; 232 Result ret = json->GetObject("result", &result); 233 if (ret == Result::SUCCESS) { 234 SaveHeapsamplingData(std::move(result)); 235 return; 236 } 237 std::string wholeMethod; 238 std::string method; 239 ret = json->GetString("method", &wholeMethod); 240 if (ret != Result::SUCCESS || wholeMethod.empty()) { 241 LOGE("find method error"); 242 return; 243 } 244 std::string::size_type length = wholeMethod.length(); 245 std::string::size_type indexPoint = 0; 246 indexPoint = wholeMethod.find_first_of('.', 0); 247 if (indexPoint == std::string::npos || indexPoint == 0 || indexPoint == length - 1) { 248 return; 249 } 250 method = wholeMethod.substr(indexPoint + 1, length); 251 if (method == "lastSeenObjectId") { 252 isAllocationMsg_ = true; 253 } 254 std::unique_ptr<PtJson> params; 255 ret = json->GetObject("params", ¶ms); 256 if (ret != Result::SUCCESS) { 257 LOGE("find params error"); 258 return; 259 } 260 std::string chunk; 261 ret = params->GetString("chunk", &chunk); 262 if (ret == Result::SUCCESS) { 263 SaveHeapSnapshotAndAllocationTrackData(chunk); 264 } 265 return; 266} 267 268void HeapProfilerClient::SaveHeapSnapshotAndAllocationTrackData(const std::string &chunk) 269{ 270 std::string head = "{\"snapshot\":\n"; 271 std::string heapFile; 272 if (!strncmp(chunk.c_str(), head.c_str(), head.length())) { 273 char date[16]; 274 char time[16]; 275 bool res = Utils::GetCurrentTime(date, time, sizeof(date)); 276 if (!res) { 277 LOGE("arkdb: get time failed"); 278 return; 279 } 280 if (isAllocationMsg_) { 281 heapFile = "Heap-" + std::to_string(sessionId_) + "-" + std::string(date) + "T" + std::string(time) + 282 ".heaptimeline"; 283 fileName_ = path_ + heapFile; 284 std::cout << "heaptimeline file name is " << fileName_ << std::endl; 285 } else { 286 heapFile = "Heap-"+ std::to_string(sessionId_) + "-" + std::string(date) + "T" + std::string(time) + 287 ".heapsnapshot"; 288 fileName_ = path_ + heapFile; 289 std::cout << "heapsnapshot file name is " << fileName_ << std::endl; 290 } 291 std::cout << ">>> "; 292 fflush(stdout); 293 } 294 295 std::string tail = "]\n}\n"; 296 std::string subStr = chunk.substr(chunk.length() - tail.length(), chunk.length()); 297 if (!strncmp(subStr.c_str(), tail.c_str(), tail.length())) { 298 isAllocationMsg_ = false; 299 } 300 WriteHeapProfilerForFile(fileName_, chunk); 301 return; 302} 303 304void HeapProfilerClient::SaveHeapsamplingData(std::unique_ptr<PtJson> result) 305{ 306 std::unique_ptr<PtJson> profile; 307 std::string heapFile; 308 Result ret = result->GetObject("profile", &profile); 309 if (ret != Result::SUCCESS) { 310 LOGE("arkdb: get profile failed"); 311 return; 312 } 313 char date[16]; 314 char time[16]; 315 bool res = Utils::GetCurrentTime(date, time, sizeof(date)); 316 if (!res) { 317 LOGE("arkdb: get time failed"); 318 return; 319 } 320 heapFile = "Heap-" + std::to_string(sessionId_) + "-" + std::string(date) + "T" + std::string(time) + 321 ".heapprofile"; 322 fileName_ = path_ + heapFile; 323 std::cout << "heapprofile file name is " << fileName_ << std::endl; 324 std::cout << ">>> "; 325 fflush(stdout); 326 327 WriteHeapProfilerForFile(fileName_, profile->Stringify()); 328 return; 329} 330 331bool HeapProfilerClient::WriteHeapProfilerForFile(const std::string &fileName, const std::string &data) 332{ 333 std::ofstream ofs; 334 std::string realPath; 335 bool res = Utils::RealPath(fileName, realPath, false); 336 if (!res) { 337 LOGE("arkdb: path is not realpath!"); 338 return false; 339 } 340 ofs.open(fileName.c_str(), std::ios::app); 341 if (!ofs.is_open()) { 342 LOGE("arkdb: file open error!"); 343 return false; 344 } 345 size_t strSize = data.size(); 346 ofs.write(data.c_str(), strSize); 347 ofs.close(); 348 ofs.clear(); 349 return true; 350} 351} // OHOS::ArkCompiler::Toolchain