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
25 using Result = panda::ecmascript::tooling::Result;
26 namespace OHOS::ArkCompiler::Toolchain {
27 static constexpr int32_t SAMPLING_INTERVAL = 16384;
DispatcherCmd(const std::string &cmd, const std::string &arg)28 bool 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
HeapDumpCommand()54 int 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
AllocationTrackCommand()77 int 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
AllocationTrackStopCommand()98 int 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
Enable()119 int 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
Disable()139 int 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
Samping()159 int 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
SampingStop()180 int 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
CollectGarbage()200 int 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
RecvReply(std::unique_ptr<PtJson> json)220 void 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
SaveHeapSnapshotAndAllocationTrackData(const std::string &chunk)268 void 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
SaveHeapsamplingData(std::unique_ptr<PtJson> result)304 void 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
WriteHeapProfilerForFile(const std::string &fileName, const std::string &data)331 bool 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