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