• 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 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