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