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", &params);
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