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