1 /*
2 * Copyright (c) 2022-2023 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 "napi_hisysevent_adapter.h"
17
18 #include <cctype>
19 #include <memory>
20
21 #include "def.h"
22 #include "hilog/log.h"
23 #include "napi_hisysevent_util.h"
24 #include "napi/native_node_api.h"
25
26 #undef LOG_DOMAIN
27 #define LOG_DOMAIN 0xD002D08
28
29 #undef LOG_TAG
30 #define LOG_TAG "NAPI_HISYSEVENT_ADAPTER"
31
32 namespace OHOS {
33 namespace HiviewDFX {
34 namespace {
35 constexpr size_t ERR_INDEX = 0;
36 constexpr size_t VAL_INDEX = 1;
37 constexpr size_t RET_SIZE = 2;
38 constexpr int64_t DEFAULT_LINE_NUM = -1;
39 constexpr char FUNC_SOURCE_NAME[] = "JSHiSysEventWrite";
40 constexpr int FUNC_NAME_INDEX = 1;
41 constexpr int LINE_INFO_INDEX = 2;
42 constexpr int LINE_INDEX = 1;
43 constexpr char CALL_FUNC_INFO_DELIMITER = ' ';
44 constexpr char CALL_LINE_INFO_DELIMITER = ':';
45 constexpr char PATH_DELIMITER = '/';
46
Split(const std::string & origin,char delimiter,std::vector<std::string> & ret)47 void Split(const std::string& origin, char delimiter, std::vector<std::string>& ret)
48 {
49 std::string::size_type start = 0;
50 std::string::size_type end = origin.find(delimiter);
51 while (end != std::string::npos) {
52 if (end == start) {
53 start++;
54 end = origin.find(delimiter, start);
55 continue;
56 }
57 ret.emplace_back(origin.substr(start, end - start));
58 start = end + 1;
59 end = origin.find(delimiter, start);
60 }
61 if (start != origin.length()) {
62 ret.emplace_back(origin.substr(start));
63 }
64 }
65
ParseCallerInfoFromStackTrace(const std::string & stackTrace,JsCallerInfo & callerInfo)66 void ParseCallerInfoFromStackTrace(const std::string& stackTrace, JsCallerInfo& callerInfo)
67 {
68 if (stackTrace.empty()) {
69 HILOG_ERROR(LOG_CORE, "js stack trace is invalid.");
70 return;
71 }
72 std::vector<std::string> callInfos;
73 Split(stackTrace, CALL_FUNC_INFO_DELIMITER, callInfos);
74 if (callInfos.size() <= FUNC_NAME_INDEX) {
75 HILOG_ERROR(LOG_CORE, "js function name parsed failed.");
76 return;
77 }
78 callerInfo.first = callInfos[FUNC_NAME_INDEX];
79 if (callInfos.size() <= LINE_INFO_INDEX) {
80 HILOG_ERROR(LOG_CORE, "js function line info parsed failed.");
81 return;
82 }
83 std::string callInfo = callInfos[LINE_INFO_INDEX];
84 std::vector<std::string> lineInfos;
85 Split(callInfo, CALL_LINE_INFO_DELIMITER, lineInfos);
86 if (lineInfos.size() <= LINE_INDEX) {
87 HILOG_ERROR(LOG_CORE, "js function line number parsed failed.");
88 return;
89 }
90 if (callerInfo.first == "anonymous") {
91 auto fileName = lineInfos[LINE_INDEX - 1];
92 auto pos = fileName.find_last_of(PATH_DELIMITER);
93 callerInfo.first = (pos == std::string::npos) ? fileName : fileName.substr(++pos);
94 }
95 auto lineInfo = lineInfos[LINE_INDEX];
96 if (std::any_of(lineInfo.begin(), lineInfo.end(), [] (auto& c) {
97 return !isdigit(c);
98 })) {
99 callerInfo.second = DEFAULT_LINE_NUM;
100 return;
101 }
102 callerInfo.second = static_cast<int64_t>(std::stoll(lineInfos[LINE_INDEX]));
103 }
104 }
105
ParseJsCallerInfo(const napi_env env,JsCallerInfo & callerInfo)106 void NapiHiSysEventAdapter::ParseJsCallerInfo(const napi_env env, JsCallerInfo& callerInfo)
107 {
108 std::string stackTrace;
109 if (napi_get_stack_trace(env, stackTrace) != napi_ok) {
110 HILOG_ERROR(LOG_CORE, "js stack trace build failed.");
111 return;
112 }
113 ParseCallerInfoFromStackTrace(stackTrace, callerInfo);
114 }
115
CheckThenWriteSysEvent(HiSysEventAsyncContext * eventAsyncContext)116 void NapiHiSysEventAdapter::CheckThenWriteSysEvent(HiSysEventAsyncContext* eventAsyncContext)
117 {
118 if (eventAsyncContext == nullptr) {
119 return;
120 }
121 if (eventAsyncContext->eventWroteResult != SUCCESS) {
122 return;
123 }
124 auto eventInfo = eventAsyncContext->eventInfo;
125 auto jsCallerInfo = eventAsyncContext->jsCallerInfo;
126 ControlParam param {
127 .period = HISYSEVENT_DEFAULT_PERIOD,
128 .threshold = HISYSEVENT_DEFAULT_THRESHOLD,
129 };
130 CallerInfo info = {
131 .func = jsCallerInfo.first.c_str(),
132 .line = jsCallerInfo.second,
133 .timeStamp = eventAsyncContext->timeStamp,
134 };
135 uint64_t timeStamp = WriteController::CheckLimitWritingEvent(param, eventInfo.domain.c_str(),
136 eventInfo.name.c_str(), info);
137 if (timeStamp == INVALID_TIME_STAMP) {
138 eventAsyncContext->eventWroteResult = ERR_WRITE_IN_HIGH_FREQ;
139 return;
140 }
141 eventAsyncContext->eventWroteResult = Write(eventInfo, timeStamp);
142 }
143
Write(const napi_env env,HiSysEventAsyncContext * eventAsyncContext)144 void NapiHiSysEventAdapter::Write(const napi_env env, HiSysEventAsyncContext* eventAsyncContext)
145 {
146 napi_value resource = nullptr;
147 NapiHiSysEventUtil::CreateStringValue(env, FUNC_SOURCE_NAME, resource);
148 eventAsyncContext->timeStamp = WriteController::GetCurrentTimeMills();
149 napi_create_async_work(
150 env, nullptr, resource,
151 [] (napi_env env, void* data) {
152 HiSysEventAsyncContext* eventAsyncContext = reinterpret_cast<HiSysEventAsyncContext*>(data);
153 CheckThenWriteSysEvent(eventAsyncContext);
154 },
155 [] (napi_env env, napi_status status, void* data) {
156 HiSysEventAsyncContext* eventAsyncContext = reinterpret_cast<HiSysEventAsyncContext*>(data);
157 napi_value results[RET_SIZE] = {0};
158 auto isNormalWrote = eventAsyncContext->eventWroteResult == SUCCESS &&
159 !NapiHiSysEventUtil::HasStrParamLenOverLimit(eventAsyncContext->eventInfo);
160 if (isNormalWrote) {
161 NapiHiSysEventUtil::CreateNull(env, results[ERR_INDEX]);
162 NapiHiSysEventUtil::CreateInt32Value(env, eventAsyncContext->eventWroteResult, results[VAL_INDEX]);
163 } else {
164 NapiHiSysEventUtil::CreateNull(env, results[VAL_INDEX]);
165 auto errorCode = eventAsyncContext->eventWroteResult == SUCCESS ? ERR_VALUE_LENGTH_TOO_LONG :
166 eventAsyncContext->eventWroteResult;
167 results[ERR_INDEX] = NapiHiSysEventUtil::CreateErrorByRet(env, errorCode);
168 }
169 if (eventAsyncContext->deferred != nullptr) { // promise
170 isNormalWrote ? napi_resolve_deferred(env, eventAsyncContext->deferred, results[VAL_INDEX]) :
171 napi_reject_deferred(env, eventAsyncContext->deferred, results[ERR_INDEX]);
172 } else {
173 napi_value callback = nullptr;
174 napi_get_reference_value(env, eventAsyncContext->callback, &callback);
175 napi_value retValue = nullptr;
176 napi_call_function(env, nullptr, callback, RET_SIZE, results, &retValue);
177 napi_delete_reference(env, eventAsyncContext->callback);
178 }
179 napi_delete_async_work(env, eventAsyncContext->asyncWork);
180 delete eventAsyncContext;
181 }, reinterpret_cast<void*>(eventAsyncContext), &eventAsyncContext->asyncWork);
182 napi_queue_async_work_with_qos(env, eventAsyncContext->asyncWork, napi_qos_default);
183 }
184
InnerWrite(HiSysEvent::EventBase & eventBase,const HiSysEventInfo & eventInfo)185 void NapiHiSysEventAdapter::InnerWrite(HiSysEvent::EventBase& eventBase,
186 const HiSysEventInfo& eventInfo)
187 {
188 AppendParams(eventBase, eventInfo.params);
189 }
190
Write(const HiSysEventInfo & eventInfo,uint64_t timeStamp)191 int NapiHiSysEventAdapter::Write(const HiSysEventInfo& eventInfo, uint64_t timeStamp)
192 {
193 if (!StringFilter::GetInstance().IsValidName(eventInfo.domain, MAX_DOMAIN_LENGTH)) {
194 return HiSysEvent::ExplainThenReturnRetCode(ERR_DOMAIN_NAME_INVALID);
195 }
196 if (!StringFilter::GetInstance().IsValidName(eventInfo.name, MAX_EVENT_NAME_LENGTH)) {
197 return HiSysEvent::ExplainThenReturnRetCode(ERR_EVENT_NAME_INVALID);
198 }
199 HiSysEvent::EventBase eventBase(eventInfo.domain, eventInfo.name, eventInfo.eventType, timeStamp);
200 HiSysEvent::WritebaseInfo(eventBase);
201 if (HiSysEvent::IsError(eventBase)) {
202 return HiSysEvent::ExplainThenReturnRetCode(eventBase.GetRetCode());
203 }
204
205 InnerWrite(eventBase, eventInfo);
206 HiSysEvent::InnerWrite(eventBase);
207 if (HiSysEvent::IsError(eventBase)) {
208 return HiSysEvent::ExplainThenReturnRetCode(eventBase.GetRetCode());
209 }
210
211 HiSysEvent::SendSysEvent(eventBase);
212 return eventBase.GetRetCode();
213 }
214 } // namespace HiviewDFX
215 } // namespace OHOS
216