• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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