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 <cstdio>
17 #include <functional>
18 #include <map>
19 #include <string>
20 #include <hilog/log.h>
21 #include "hitrace_meter.h"
22 #include "napi_hitrace_meter.h"
23 
24 using namespace OHOS::HiviewDFX;
25 namespace {
26 constexpr int FIRST_ARG_INDEX = 0;
27 constexpr int SECOND_ARG_INDEX = 1;
28 constexpr int ARGC_NUMBER_TWO = 2;
29 constexpr int ARGC_NUMBER_THREE = 3;
30 #undef LOG_DOMAIN
31 #define LOG_DOMAIN 0xD002D33
32 
33 #undef LOG_TAG
34 #define LOG_TAG "HITRACE_METER_JS"
35 
36 using STR_NUM_PARAM_FUNC = std::function<bool(std::string, napi_value&)>;
37 std::map<std::string, uint64_t> g_tagsMap = {
38     {"ohos", HITRACE_TAG_OHOS}, {"ability", HITRACE_TAG_ABILITY_MANAGER}, {"camera", HITRACE_TAG_ZCAMERA},
39     {"media", HITRACE_TAG_ZMEDIA}, {"image", HITRACE_TAG_ZIMAGE}, {"audio", HITRACE_TAG_ZAUDIO},
40     {"distributeddatamgr", HITRACE_TAG_DISTRIBUTEDDATA}, {"graphic", HITRACE_TAG_GRAPHIC_AGP},
41     {"ace", HITRACE_TAG_ACE}, {"notification", HITRACE_TAG_NOTIFICATION}, {"misc", HITRACE_TAG_MISC},
42     {"multimodalinput", HITRACE_TAG_MULTIMODALINPUT}, {"rpc", HITRACE_TAG_RPC}, {"ark", HITRACE_TAG_ARK},
43     {"window", HITRACE_TAG_WINDOW_MANAGER}, {"dscreen", HITRACE_TAG_DISTRIBUTED_SCREEN},
44     {"dcamera", HITRACE_TAG_DISTRIBUTED_CAMERA}, {"dhfwk", HITRACE_TAG_DISTRIBUTED_HARDWARE_FWK},
45     {"gresource", HITRACE_TAG_GLOBAL_RESMGR}, {"devicemanager", HITRACE_TAG_DEVICE_MANAGER},
46     {"samgr", HITRACE_TAG_SAMGR}, {"power", HITRACE_TAG_POWER}, {"dsched", HITRACE_TAG_DISTRIBUTED_SCHEDULE},
47     {"dinput", HITRACE_TAG_DISTRIBUTED_INPUT}, {"bluetooth", HITRACE_TAG_BLUETOOTH}, {"ffrt", HITRACE_TAG_FFRT},
48     {"commonlibrary", HITRACE_TAG_COMMONLIBRARY}, {"hdf", HITRACE_TAG_HDF}, {"net", HITRACE_TAG_NET},
49     {"nweb", HITRACE_TAG_NWEB}, {"daudio", HITRACE_TAG_DISTRIBUTED_AUDIO},
50     {"filemanagement", HITRACE_TAG_FILEMANAGEMENT}, {"app", HITRACE_TAG_APP}
51 };
52 
ParseParams(napi_env & env,napi_callback_info & info,size_t & argc,napi_value * argv)53 napi_value ParseParams(napi_env& env, napi_callback_info& info, size_t& argc, napi_value* argv)
54 {
55     napi_value thisVar;
56     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, NULL));
57     return nullptr;
58 }
59 
TypeCheck(const napi_env & env,const napi_value & value,const napi_valuetype expectType)60 bool TypeCheck(const napi_env& env, const napi_value& value, const napi_valuetype expectType)
61 {
62     napi_valuetype valueType;
63     napi_status status = napi_typeof(env, value, &valueType);
64     if (status != napi_ok) {
65         HILOG_ERROR(LOG_CORE, "Failed to get the type of the argument.");
66         return false;
67     }
68     if (valueType != expectType) {
69         HILOG_DEBUG(LOG_CORE, "Type of the parameter is invalid.");
70         return false;
71     }
72     return true;
73 }
74 
GetStringParam(const napi_env & env,const napi_value & value,std::string & dest)75 void GetStringParam(const napi_env& env, const napi_value& value, std::string& dest)
76 {
77     constexpr int nameMaxSize = 1024;
78     char buf[nameMaxSize] = {0};
79     size_t len = 0;
80     napi_get_value_string_utf8(env, value, buf, nameMaxSize, &len);
81     dest = std::string {buf};
82 }
83 
ParseStringParam(const napi_env & env,const napi_value & value,std::string & dest)84 bool ParseStringParam(const napi_env& env, const napi_value& value, std::string& dest)
85 {
86     if (TypeCheck(env, value, napi_string)) {
87         GetStringParam(env, value, dest);
88         return true;
89     }
90     if (TypeCheck(env, value, napi_number)) {
91         int64_t destI64;
92         napi_get_value_int64(env, value, &destI64);
93         dest = std::to_string(destI64);
94         return true;
95     }
96     if (TypeCheck(env, value, napi_undefined)) {
97         dest = "undefined";
98         return true;
99     }
100     if (TypeCheck(env, value, napi_null)) {
101         dest = "null";
102         return true;
103     }
104     return false;
105 }
106 
ParseInt32Param(const napi_env & env,const napi_value & value,int & dest)107 bool ParseInt32Param(const napi_env& env, const napi_value& value, int& dest)
108 {
109     if (!TypeCheck(env, value, napi_number)) {
110         return false;
111     }
112     napi_get_value_int32(env, value, &dest);
113     return true;
114 }
115 
ParseInt64Param(const napi_env & env,const napi_value & value,int64_t & dest)116 bool ParseInt64Param(const napi_env& env, const napi_value& value, int64_t& dest)
117 {
118     if (!TypeCheck(env, value, napi_number)) {
119         return false;
120     }
121     napi_get_value_int64(env, value, &dest);
122     return true;
123 }
124 
SetTagsParam(const napi_env & env,const napi_value & value,uint64_t & tags)125 void SetTagsParam(const napi_env& env, const napi_value& value, uint64_t& tags)
126 {
127     uint32_t arrayLength;
128     napi_status status = napi_get_array_length(env, value, &arrayLength);
129     if (status != napi_ok) {
130         HILOG_ERROR(LOG_CORE, "Failed to get the length of the array.");
131         return;
132     }
133 
134     for (uint32_t i = 0; i < arrayLength; i++) {
135         napi_value tag;
136         status = napi_get_element(env, value, i, &tag);
137         if (status != napi_ok) {
138             HILOG_ERROR(LOG_CORE, "Failed to get element.");
139             return;
140         }
141 
142         if (!TypeCheck(env, tag, napi_string)) {
143             HILOG_ERROR(LOG_CORE, "tag is invalid, not a napi_string");
144             return;
145         }
146 
147         std::string tagStr = "";
148         GetStringParam(env, tag, tagStr);
149         if (g_tagsMap.count(tagStr) > 0) {
150             tags |= g_tagsMap[tagStr];
151         }
152     }
153 }
154 
ParseTagsParam(const napi_env & env,const napi_value & value,uint64_t & tags)155 bool ParseTagsParam(const napi_env& env, const napi_value& value, uint64_t& tags)
156 {
157     bool isArray = false;
158     napi_status status = napi_is_array(env, value, &isArray);
159     if (status != napi_ok) {
160         HILOG_ERROR(LOG_CORE, "Failed to get array type.");
161         return false;
162     }
163 
164     if (isArray) {
165         SetTagsParam(env, value, tags);
166         return true;
167     } else {
168         HILOG_ERROR(LOG_CORE, "The argument isn't an array type.");
169         return false;
170     }
171 }
172 
JsStrNumParamsFunc(napi_env & env,napi_callback_info & info,STR_NUM_PARAM_FUNC nativeCall)173 bool JsStrNumParamsFunc(napi_env& env, napi_callback_info& info, STR_NUM_PARAM_FUNC nativeCall)
174 {
175     size_t argc = static_cast<size_t>(ARGC_NUMBER_TWO);
176     napi_value argv[ARGC_NUMBER_TWO];
177     ParseParams(env, info, argc, argv);
178     if (argc != ARGC_NUMBER_TWO) {
179         HILOG_ERROR(LOG_CORE, "Wrong number of parameters.");
180         return false;
181     }
182     std::string name;
183     if (!ParseStringParam(env, argv[FIRST_ARG_INDEX], name)) {
184         return false;
185     }
186     if (!nativeCall(name, argv[SECOND_ARG_INDEX])) {
187         return false;
188     }
189     return true;
190 }
191 }
192 
JSTraceStart(napi_env env,napi_callback_info info)193 static napi_value JSTraceStart(napi_env env, napi_callback_info info)
194 {
195     size_t argc = ARGC_NUMBER_THREE;
196     napi_value argv[ARGC_NUMBER_THREE];
197     ParseParams(env, info, argc, argv);
198     NAPI_ASSERT(env, argc >= ARGC_NUMBER_TWO, "Wrong number of arguments");
199     std::string name;
200     if (!ParseStringParam(env, argv[FIRST_ARG_INDEX], name)) {
201         return nullptr;
202     }
203     if (name == "null" || name == "undefined") {
204         return nullptr;
205     }
206     int taskId = 0;
207     if (!ParseInt32Param(env, argv[SECOND_ARG_INDEX], taskId)) {
208         return nullptr;
209     }
210     StartAsyncTrace(HITRACE_TAG_APP, name, taskId);
211     return nullptr;
212 }
213 
JSTraceFinish(napi_env env,napi_callback_info info)214 static napi_value JSTraceFinish(napi_env env, napi_callback_info info)
215 {
216     size_t argc = ARGC_NUMBER_TWO;
217     napi_value argv[ARGC_NUMBER_TWO];
218     napi_value thisVar;
219     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, NULL));
220     NAPI_ASSERT(env, argc == ARGC_NUMBER_TWO, "Wrong number of arguments");
221     (void)JsStrNumParamsFunc(env, info, [&env] (std::string name, napi_value& nValue) -> bool {
222         if (name == "null" || name == "undefined") {
223             return false;
224         }
225         int taskId = 0;
226         if (!ParseInt32Param(env, nValue, taskId)) {
227             return false;
228         }
229         FinishAsyncTrace(HITRACE_TAG_APP, name, taskId);
230         return true;
231     });
232     return nullptr;
233 }
234 
JSTraceCount(napi_env env,napi_callback_info info)235 static napi_value JSTraceCount(napi_env env, napi_callback_info info)
236 {
237     size_t argc = ARGC_NUMBER_TWO;
238     napi_value argv[ARGC_NUMBER_TWO];
239     napi_value thisVar;
240     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, NULL));
241     NAPI_ASSERT(env, argc == ARGC_NUMBER_TWO, "Wrong number of arguments");
242     (void)JsStrNumParamsFunc(env, info, [&env] (std::string name, napi_value& nValue) -> bool {
243         if (name == "null" || name == "undefined") {
244             return false;
245         }
246         int64_t count = 0;
247         if (!ParseInt64Param(env, nValue, count)) {
248             return false;
249         }
250         CountTrace(HITRACE_TAG_APP, name, count);
251         return true;
252     });
253     return nullptr;
254 }
255 
JSStartCaptureAppTrace(napi_env env,napi_callback_info info)256 static napi_value JSStartCaptureAppTrace(napi_env env, napi_callback_info info)
257 {
258     size_t argc = ARGC_NUMBER_THREE;
259     napi_value argv[ARGC_NUMBER_THREE];
260     ParseParams(env, info, argc, argv);
261     NAPI_ASSERT(env, argc >= ARGC_NUMBER_THREE, "Wrong number of arguments");
262 
263     uint64_t tags = HITRACE_TAG_APP;
264     if (!ParseTagsParam(env, argv[FIRST_ARG_INDEX], tags)) {
265         return nullptr;
266     }
267 
268     int64_t flag = 0;
269     if (!ParseInt64Param(env, argv[SECOND_ARG_INDEX], flag)) {
270         return nullptr;
271     }
272 
273     int64_t limitSize = 0;
274     if (!ParseInt64Param(env, argv[ARGC_NUMBER_TWO], limitSize)) {
275         return nullptr;
276     }
277 
278     std::string file = "";
279     if (StartCaptureAppTrace((TraceFlag)flag, tags, limitSize, file) != RET_SUCC) {
280         HILOG_ERROR(LOG_CORE, "StartCaptureAppTrace failed");
281         return nullptr;
282     }
283 
284     napi_value napiFile;
285     napi_status status = napi_create_string_utf8(env, file.c_str(), file.length(), &napiFile);
286     if (status != napi_ok) {
287         HILOG_ERROR(LOG_CORE, "create napi string failed: %{public}d(%{public}s)", errno, strerror(errno));
288         return nullptr;
289     }
290 
291     return napiFile;
292 }
293 
JSStopCaptureAppTrace(napi_env env,napi_callback_info info)294 static napi_value JSStopCaptureAppTrace(napi_env env, napi_callback_info info)
295 {
296     StopCaptureAppTrace();
297     return nullptr;
298 }
299 
300 /*
301  * function for module exports
302  */
303 EXTERN_C_START
HiTraceMeterInit(napi_env env,napi_value exports)304 static napi_value HiTraceMeterInit(napi_env env, napi_value exports)
305 {
306     napi_property_descriptor desc[] = {
307         DECLARE_NAPI_FUNCTION("startTrace", JSTraceStart),
308         DECLARE_NAPI_FUNCTION("finishTrace", JSTraceFinish),
309         DECLARE_NAPI_FUNCTION("traceByValue", JSTraceCount),
310         DECLARE_NAPI_FUNCTION("startCaptureAppTrace", JSStartCaptureAppTrace),
311         DECLARE_NAPI_FUNCTION("stopCaptureAppTrace", JSStopCaptureAppTrace),
312     };
313     NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
314     return exports;
315 }
316 EXTERN_C_END
317 
318 /*
319  * hiTraceMeter module definition
320  */
321 static napi_module hitracemeter_module = {
322     .nm_version = 1,
323     .nm_flags = 0,
324     .nm_filename = nullptr,
325     .nm_register_func = HiTraceMeterInit,
326     .nm_modname = "hiTraceMeter",
327     .nm_priv = ((void *)0),
328     .reserved = {0}
329 };
330 
331 /*
332  * Module registration
333  */
RegisterModule(void)334 extern "C" __attribute__((constructor)) void RegisterModule(void)
335 {
336     napi_module_register(&hitracemeter_module);
337 }
338