1 /*
2 * Copyright (c) 2022 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 <cerrno>
17 #include <codecvt>
18 #include <fstream>
19 #include <string>
20 #include <memory>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <fcntl.h>
24 #include <ctime>
25 #include <malloc.h>
26 #include <unistd.h>
27
28 #include "application_context.h"
29 #include "context.h"
30 #include "directory_ex.h"
31 #include "dump_usage.h"
32 #include "hilog/log.h"
33 #include "iservice_registry.h"
34 #include "napi/native_api.h"
35 #include "napi/native_node_api.h"
36 #include "native_engine/native_engine.h"
37 #include "refbase.h"
38
39 namespace OHOS {
40 namespace HiviewDFX {
41 namespace {
42 constexpr HiLogLabel LABEL = { LOG_CORE, 0xD002D0A, "HiDebug_NAPI" };
43 constexpr int ONE_VALUE_LIMIT = 1;
44 constexpr int ARRAY_INDEX_FIRST = 0;
45 const std::string SLASH_STR = "/";
46 const std::string DEFAULT_FILENAME = "undefined";
47 const std::string JSON_FILE = ".json";
48 const std::string HEAPSNAPSHOT_FILE = ".heapsnapshot";
49 enum ErrorCode {
50 PERMISSION_ERROR = 201,
51 PARAMETER_ERROR = 401,
52 VERSION_ERROR = 801,
53 SYSTEM_ABILITY_NOT_FOUND = 11400101
54 };
55 }
56
MatchValueType(napi_env env,napi_value value,napi_valuetype targetType)57 static bool MatchValueType(napi_env env, napi_value value, napi_valuetype targetType)
58 {
59 napi_valuetype valueType = napi_undefined;
60 napi_typeof(env, value, &valueType);
61 return valueType == targetType;
62 }
63
CreateFile(const std::string & path)64 static bool CreateFile(const std::string &path)
65 {
66 if (access(path.c_str(), F_OK) == 0) {
67 if (access(path.c_str(), W_OK) == 0) {
68 return true;
69 }
70 return false;
71 }
72 const mode_t defaultMode = S_IRUSR | S_IWUSR | S_IRGRP; // -rw-r-----
73 int fd = creat(path.c_str(), defaultMode);
74 if (fd == -1) {
75 HiLog::Error(LABEL, "file create failed, errno = %{public}d", errno);
76 return false;
77 } else {
78 close(fd);
79 return true;
80 }
81 }
82
IsLegalPath(const std::string & path)83 static bool IsLegalPath(const std::string& path)
84 {
85 if (path.find("./") != std::string::npos ||
86 path.find("../") != std::string::npos) {
87 return false;
88 }
89 return true;
90 }
91
IsArrayForNapiValue(napi_env env,napi_value param,uint32_t & arraySize)92 static bool IsArrayForNapiValue(napi_env env, napi_value param, uint32_t &arraySize)
93 {
94 bool isArray = false;
95 arraySize = 0;
96 if (napi_is_array(env, param, &isArray) != napi_ok || isArray == false) {
97 return false;
98 }
99 if (napi_get_array_length(env, param, &arraySize) != napi_ok) {
100 return false;
101 }
102 return true;
103 }
104
GetDumpParam(napi_env env,napi_callback_info info,int & serviceId,int & fd,std::vector<std::u16string> & args)105 static bool GetDumpParam(napi_env env, napi_callback_info info,
106 int& serviceId, int& fd, std::vector<std::u16string>& args)
107 {
108 const int valueNum = 3;
109 size_t argc = valueNum;
110 napi_value argv[valueNum] = {nullptr};
111 napi_value thisVar = nullptr;
112 void *data = nullptr;
113 napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
114 if (argc != valueNum) {
115 HiLog::Error(LABEL, "invalid number = %{public}d of params.", ONE_VALUE_LIMIT);
116 return false;
117 }
118 int thirdPos = 2;
119 if (!MatchValueType(env, argv[0], napi_number) &&
120 !MatchValueType(env, argv[1], napi_number) &&
121 !MatchValueType(env, argv[thirdPos], napi_object)) {
122 HiLog::Error(LABEL, "params type error.");
123 return false;
124 }
125 if (napi_get_value_int32(env, argv[0], &serviceId) != napi_ok) {
126 HiLog::Error(LABEL, "Get input serviceId failed.");
127 return false;
128 }
129 if (napi_get_value_int32(env, argv[1], &fd) != napi_ok) {
130 HiLog::Error(LABEL, "Get input fd failed.");
131 return false;
132 }
133 uint32_t arraySize = 0;
134 if (!IsArrayForNapiValue(env, argv[thirdPos], arraySize)) {
135 HiLog::Error(LABEL, "Get input args failed.");
136 return false;
137 }
138 for (uint32_t i = 0; i < arraySize; i++) {
139 napi_value jsValue = nullptr;
140 if (napi_get_element(env, argv[thirdPos], i, &jsValue) != napi_ok) {
141 HiLog::Error(LABEL, "get_element -> Get input args failed.");
142 return false;
143 }
144 const size_t bufSize = 256;
145 size_t bufLen = 0;
146 char buf[bufSize] = {0};
147 if (napi_get_value_string_utf8(env, jsValue, buf, bufSize - 1, &bufLen) != napi_ok) {
148 HiLog::Error(LABEL, "get_value -> Get input args failed.");
149 return false;
150 }
151 std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> strCnv;
152 args.push_back(strCnv.from_bytes(buf));
153 }
154 return true;
155 }
156
GetFileNameParam(napi_env env,napi_callback_info info)157 static std::string GetFileNameParam(napi_env env, napi_callback_info info)
158 {
159 size_t argc = ONE_VALUE_LIMIT;
160 napi_value argv[ONE_VALUE_LIMIT] = { nullptr };
161 napi_value thisVar = nullptr;
162 void *data = nullptr;
163 napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
164 if (argc != ONE_VALUE_LIMIT) {
165 HiLog::Error(LABEL, "invalid number = %{public}d of params.", ONE_VALUE_LIMIT);
166 return DEFAULT_FILENAME;
167 }
168 if (!MatchValueType(env, argv[ARRAY_INDEX_FIRST], napi_string)) {
169 HiLog::Error(LABEL, "Type error, should be string type!");
170 return DEFAULT_FILENAME;
171 }
172 size_t bufLen = 0;
173 napi_status status = napi_get_value_string_utf8(env, argv[0], nullptr, 0, &bufLen);
174 if (status != napi_ok) {
175 HiLog::Error(LABEL, "Get input filename param length failed.");
176 return DEFAULT_FILENAME;
177 }
178 const int bufMax = 128;
179 if (bufLen > bufMax || bufLen == 0) {
180 HiLog::Error(LABEL, "input filename param length is illegal.");
181 return DEFAULT_FILENAME;
182 }
183 char buf[bufLen + 1];
184 napi_get_value_string_utf8(env, argv[0], buf, bufLen + 1, &bufLen);
185 std::string fileName = buf;
186 return fileName;
187 }
188
GetFileNameParamThrowErrorVersion(napi_env env,napi_callback_info info,std::string & fileName)189 static bool GetFileNameParamThrowErrorVersion(napi_env env, napi_callback_info info, std::string &fileName)
190 {
191 size_t argc = ONE_VALUE_LIMIT;
192 napi_value argv[ONE_VALUE_LIMIT] = { nullptr };
193 napi_value thisVar = nullptr;
194 void *data = nullptr;
195 napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
196 if (argc != ONE_VALUE_LIMIT) {
197 HiLog::Error(LABEL, "invalid number = %{public}d of params.", ONE_VALUE_LIMIT);
198 return false;
199 }
200 if (!MatchValueType(env, argv[ARRAY_INDEX_FIRST], napi_string)) {
201 HiLog::Error(LABEL, "Type error, should be string type!");
202 return false;
203 }
204 size_t bufLen = 0;
205 napi_status status = napi_get_value_string_utf8(env, argv[0], nullptr, 0, &bufLen);
206 if (status != napi_ok) {
207 HiLog::Error(LABEL, "Get input filename param length failed.");
208 return false;
209 }
210 const int bufMax = 128;
211 if (bufLen > bufMax || bufLen == 0) {
212 HiLog::Error(LABEL, "input filename param length is illegal.");
213 return false;
214 }
215 char buf[bufLen + 1];
216 napi_get_value_string_utf8(env, argv[0], buf, bufLen + 1, &bufLen);
217 fileName = buf;
218 return true;
219 }
220
CreateUndefined(napi_env env)221 static napi_value CreateUndefined(napi_env env)
222 {
223 napi_value res = nullptr;
224 napi_get_undefined(env, &res);
225 return res;
226 }
227
CreateErrorMessage(napi_env env,std::string msg)228 static napi_value CreateErrorMessage(napi_env env, std::string msg)
229 {
230 napi_value result = nullptr;
231 napi_value message = nullptr;
232 napi_create_string_utf8(env, (char *)msg.data(), msg.size(), &message);
233 napi_create_error(env, nullptr, message, &result);
234 return result;
235 }
236
StartProfiling(napi_env env,napi_callback_info info)237 napi_value StartProfiling(napi_env env, napi_callback_info info)
238 {
239 std::string fileName = GetFileNameParam(env, info);
240 auto context = OHOS::AbilityRuntime::Context::GetApplicationContext();
241 if (context == nullptr) {
242 return CreateErrorMessage(env, "Get ApplicationContext failed.");
243 }
244 std::string filesDir = context->GetFilesDir();
245 if (filesDir.empty()) {
246 return CreateErrorMessage(env, "Get App files dir failed.");
247 }
248 std::string filePath = filesDir + SLASH_STR + fileName + JSON_FILE;
249 if (!IsLegalPath(filePath)) {
250 return CreateErrorMessage(env, "input fileName is illegal.");
251 }
252 if (!CreateFile(filePath)) {
253 return CreateErrorMessage(env, "file created failed.");
254 }
255 NativeEngine *engine = reinterpret_cast<NativeEngine*>(env);
256 engine->StartCpuProfiler(filePath);
257 return CreateUndefined(env);
258 }
259
StartJsCpuProfiling(napi_env env,napi_callback_info info)260 napi_value StartJsCpuProfiling(napi_env env, napi_callback_info info)
261 {
262 std::string fileName;
263 if (!GetFileNameParamThrowErrorVersion(env, info, fileName)) {
264 std::string paramErrorMessage = "Invalid parameter, require a string parameter.";
265 napi_throw_error(env, std::to_string(ErrorCode::PARAMETER_ERROR).c_str(), paramErrorMessage.c_str());
266 return CreateUndefined(env);
267 }
268 HiLog::Info(LABEL, "filename: %{public}s.", fileName.c_str());
269 auto context = OHOS::AbilityRuntime::Context::GetApplicationContext();
270 if (context == nullptr) {
271 return CreateErrorMessage(env, "Get ApplicationContext failed.");
272 }
273 std::string filesDir = context->GetFilesDir();
274 if (filesDir.empty()) {
275 return CreateErrorMessage(env, "Get App files dir failed.");
276 }
277 std::string filePath = filesDir + SLASH_STR + fileName + JSON_FILE;
278 if (!IsLegalPath(filePath)) {
279 return CreateErrorMessage(env, "input fileName is illegal.");
280 }
281 if (!CreateFile(filePath)) {
282 return CreateErrorMessage(env, "file created failed.");
283 }
284 NativeEngine *engine = reinterpret_cast<NativeEngine*>(env);
285 engine->StartCpuProfiler(filePath);
286 return CreateUndefined(env);
287 }
288
StopProfiling(napi_env env,napi_callback_info info)289 napi_value StopProfiling(napi_env env, napi_callback_info info)
290 {
291 NativeEngine *engine = reinterpret_cast<NativeEngine*>(env);
292 engine->StopCpuProfiler();
293 return CreateUndefined(env);
294 }
295
StopJsCpuProfiling(napi_env env,napi_callback_info info)296 napi_value StopJsCpuProfiling(napi_env env, napi_callback_info info)
297 {
298 NativeEngine *engine = reinterpret_cast<NativeEngine*>(env);
299 engine->StopCpuProfiler();
300 return CreateUndefined(env);
301 }
302
DumpHeapData(napi_env env,napi_callback_info info)303 napi_value DumpHeapData(napi_env env, napi_callback_info info)
304 {
305 std::string fileName = GetFileNameParam(env, info);
306 auto context = OHOS::AbilityRuntime::Context::GetApplicationContext();
307 if (context == nullptr) {
308 return CreateErrorMessage(env, "Get ApplicationContext failed.");
309 }
310 std::string filesDir = context->GetFilesDir();
311 if (filesDir.empty()) {
312 return CreateErrorMessage(env, "Get App files dir failed.");
313 }
314 std::string filePath = filesDir + SLASH_STR + fileName + HEAPSNAPSHOT_FILE;
315 if (!IsLegalPath(filePath)) {
316 return CreateErrorMessage(env, "input fileName is illegal.");
317 }
318 if (!CreateFile(filePath)) {
319 return CreateErrorMessage(env, "file created failed.");
320 }
321 NativeEngine *engine = reinterpret_cast<NativeEngine*>(env);
322 engine->DumpHeapSnapshot(filePath);
323 return CreateUndefined(env);
324 }
325
DumpJsHeapData(napi_env env,napi_callback_info info)326 napi_value DumpJsHeapData(napi_env env, napi_callback_info info)
327 {
328 std::string fileName;
329 if (!GetFileNameParamThrowErrorVersion(env, info, fileName)) {
330 std::string paramErrorMessage = "Invalid parameter, require a string parameter.";
331 napi_throw_error(env, std::to_string(ErrorCode::PARAMETER_ERROR).c_str(), paramErrorMessage.c_str());
332 return CreateUndefined(env);
333 }
334 HiLog::Error(LABEL, "filename: %{public}s.", fileName.c_str());
335 auto context = OHOS::AbilityRuntime::Context::GetApplicationContext();
336 if (context == nullptr) {
337 return CreateErrorMessage(env, "Get ApplicationContext failed.");
338 }
339 std::string filesDir = context->GetFilesDir();
340 if (filesDir.empty()) {
341 return CreateErrorMessage(env, "Get App files dir failed.");
342 }
343 std::string filePath = filesDir + SLASH_STR + fileName + HEAPSNAPSHOT_FILE;
344 if (!IsLegalPath(filePath)) {
345 return CreateErrorMessage(env, "input fileName is illegal.");
346 }
347 if (!CreateFile(filePath)) {
348 return CreateErrorMessage(env, "file created failed.");
349 }
350 NativeEngine *engine = reinterpret_cast<NativeEngine*>(env);
351 engine->DumpHeapSnapshot(filePath);
352 return CreateUndefined(env);
353 }
354
GetPss(napi_env env,napi_callback_info info)355 napi_value GetPss(napi_env env, napi_callback_info info)
356 {
357 napi_value pss;
358 std::unique_ptr<DumpUsage> dumpUsage = std::make_unique<DumpUsage>();
359 if (dumpUsage) {
360 int pid = getpid();
361 uint64_t pssInfo = dumpUsage->GetPss(pid);
362 napi_create_bigint_uint64(env, pssInfo, &pss);
363 } else {
364 napi_create_bigint_uint64(env, 0, &pss);
365 }
366 return pss;
367 }
368
GetSharedDirty(napi_env env,napi_callback_info info)369 napi_value GetSharedDirty(napi_env env, napi_callback_info info)
370 {
371 napi_value sharedDirty;
372 std::unique_ptr<DumpUsage> dumpUsage = std::make_unique<DumpUsage>();
373 if (dumpUsage) {
374 int pid = getpid();
375 uint64_t sharedDirtyInfo = dumpUsage->GetSharedDirty(pid);
376 napi_create_bigint_uint64(env, sharedDirtyInfo, &sharedDirty);
377 } else {
378 napi_create_bigint_uint64(env, 0, &sharedDirty);
379 }
380 return sharedDirty;
381 }
382
GetPrivateDirty(napi_env env,napi_callback_info info)383 napi_value GetPrivateDirty(napi_env env, napi_callback_info info)
384 {
385 napi_value privateDirtyValue;
386 std::unique_ptr<DumpUsage> dumpUsage = std::make_unique<DumpUsage>();
387 if (dumpUsage) {
388 pid_t pid = getpid();
389 uint64_t privateDirty = dumpUsage->GetPrivateDirty(pid);
390 napi_create_bigint_uint64(env, privateDirty, &privateDirtyValue);
391 } else {
392 napi_create_bigint_uint64(env, 0, &privateDirtyValue);
393 }
394 return privateDirtyValue;
395 }
396
GetCpuUsage(napi_env env,napi_callback_info info)397 napi_value GetCpuUsage(napi_env env, napi_callback_info info)
398 {
399 napi_value cpuUsageValue;
400 std::unique_ptr<DumpUsage> dumpUsage = std::make_unique<DumpUsage>();
401 if (dumpUsage) {
402 pid_t pid = getpid();
403 float tmpCpuUsage = dumpUsage->GetCpuUsage(pid);
404 double cpuUsage = double(tmpCpuUsage);
405 napi_create_double(env, cpuUsage, &cpuUsageValue);
406 } else {
407 napi_create_double(env, 0, &cpuUsageValue);
408 }
409 return cpuUsageValue;
410 }
411
GetNativeHeapSize(napi_env env,napi_callback_info info)412 napi_value GetNativeHeapSize(napi_env env, napi_callback_info info)
413 {
414 struct mallinfo mi = mallinfo();
415 napi_value nativeHeapSize;
416 napi_create_bigint_uint64(env, uint64_t(mi.uordblks + mi.fordblks), &nativeHeapSize);
417 return nativeHeapSize;
418 }
419
GetNativeHeapAllocatedSize(napi_env env,napi_callback_info info)420 napi_value GetNativeHeapAllocatedSize(napi_env env, napi_callback_info info)
421 {
422 struct mallinfo mi = mallinfo();
423 napi_value nativeHeapAllocatedSize;
424 napi_create_bigint_uint64(env, uint64_t(mi.uordblks), &nativeHeapAllocatedSize);
425 return nativeHeapAllocatedSize;
426 }
427
GetNativeHeapFreeSize(napi_env env,napi_callback_info info)428 napi_value GetNativeHeapFreeSize(napi_env env, napi_callback_info info)
429 {
430 struct mallinfo mi = mallinfo();
431 napi_value nativeHeapFreeSize;
432 napi_create_bigint_uint64(env, uint64_t(mi.fordblks), &nativeHeapFreeSize);
433 return nativeHeapFreeSize;
434 }
435
GetServiceDump(napi_env env,napi_callback_info info)436 static napi_value GetServiceDump(napi_env env, napi_callback_info info)
437 {
438 int serviceAbilityId = 0;
439 int fd = 0;
440 std::vector<std::u16string> args;
441 if (!GetDumpParam(env, info, serviceAbilityId, fd, args)) {
442 std::string paramErrorMessage = "The parameter check failed.";
443 napi_throw_error(env, std::to_string(ErrorCode::PARAMETER_ERROR).c_str(), paramErrorMessage.c_str());
444 return CreateUndefined(env);
445 }
446
447 sptr<ISystemAbilityManager> sam = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
448 if (!sam) {
449 return CreateUndefined(env);
450 }
451 sptr<IRemoteObject> sa = sam->CheckSystemAbility(serviceAbilityId);
452 if (sa == nullptr) {
453 HiLog::Error(LABEL, "no this system ability.");
454 std::string idErrorMessage = "service id is invalid, system ability is not exist.";
455 napi_throw_error(env, std::to_string(ErrorCode::SYSTEM_ABILITY_NOT_FOUND).c_str(), idErrorMessage.c_str());
456 return CreateUndefined(env);
457 }
458 int dumpResult = sa->Dump(fd, args);
459 HiLog::Info(LABEL, "Dump result: %{public}d", dumpResult);
460 return CreateUndefined(env);
461 }
462
DeclareHiDebugInterface(napi_env env,napi_value exports)463 napi_value DeclareHiDebugInterface(napi_env env, napi_value exports)
464 {
465 napi_property_descriptor desc[] = {
466 DECLARE_NAPI_FUNCTION("startProfiling", StartProfiling),
467 DECLARE_NAPI_FUNCTION("stopProfiling", StopProfiling),
468 DECLARE_NAPI_FUNCTION("dumpHeapData", DumpHeapData),
469 DECLARE_NAPI_FUNCTION("startJsCpuProfiling", StartJsCpuProfiling),
470 DECLARE_NAPI_FUNCTION("stopJsCpuProfiling", StopJsCpuProfiling),
471 DECLARE_NAPI_FUNCTION("dumpJsHeapData", DumpJsHeapData),
472 DECLARE_NAPI_FUNCTION("getPss", GetPss),
473 DECLARE_NAPI_FUNCTION("getSharedDirty", GetSharedDirty),
474 DECLARE_NAPI_FUNCTION("getPrivateDirty", GetPrivateDirty),
475 DECLARE_NAPI_FUNCTION("getCpuUsage", GetCpuUsage),
476 DECLARE_NAPI_FUNCTION("getServiceDump", GetServiceDump),
477 DECLARE_NAPI_FUNCTION("getNativeHeapSize", GetNativeHeapSize),
478 DECLARE_NAPI_FUNCTION("getNativeHeapAllocatedSize", GetNativeHeapAllocatedSize),
479 DECLARE_NAPI_FUNCTION("getNativeHeapFreeSize", GetNativeHeapFreeSize)
480 };
481 NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
482 return exports;
483 }
484
485 static napi_module hidebugModule = {
486 .nm_version = 1,
487 .nm_flags = 0,
488 .nm_filename = nullptr,
489 .nm_register_func = HiviewDFX::DeclareHiDebugInterface,
490 .nm_modname = "hidebug",
491 .nm_priv = ((void *)0),
492 .reserved = {0}
493 };
494
HiDebugRegisterModule(void)495 extern "C" __attribute__((constructor)) void HiDebugRegisterModule(void)
496 {
497 napi_module_register(&hidebugModule);
498 }
499 } // HiviewDFX
500 } // OHOS
501