/* * Copyright (c) 2022-2023 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "napi_hisysevent_adapter.h" #include #include #include "def.h" #include "hilog/log.h" #include "napi_hisysevent_util.h" #include "napi/native_node_api.h" #undef LOG_DOMAIN #define LOG_DOMAIN 0xD002D08 #undef LOG_TAG #define LOG_TAG "NAPI_HISYSEVENT_ADAPTER" namespace OHOS { namespace HiviewDFX { namespace { constexpr size_t ERR_INDEX = 0; constexpr size_t VAL_INDEX = 1; constexpr size_t RET_SIZE = 2; constexpr int64_t DEFAULT_LINE_NUM = -1; constexpr char FUNC_SOURCE_NAME[] = "JSHiSysEventWrite"; constexpr int FUNC_NAME_INDEX = 1; constexpr int LINE_INFO_INDEX = 2; constexpr int LINE_INDEX = 1; constexpr char CALL_FUNC_INFO_DELIMITER = ' '; constexpr char CALL_LINE_INFO_DELIMITER = ':'; constexpr char PATH_DELIMITER = '/'; void Split(const std::string& origin, char delimiter, std::vector& ret) { std::string::size_type start = 0; std::string::size_type end = origin.find(delimiter); while (end != std::string::npos) { if (end == start) { start++; end = origin.find(delimiter, start); continue; } ret.emplace_back(origin.substr(start, end - start)); start = end + 1; end = origin.find(delimiter, start); } if (start != origin.length()) { ret.emplace_back(origin.substr(start)); } } void ParseCallerInfoFromStackTrace(const std::string& stackTrace, JsCallerInfo& callerInfo) { if (stackTrace.empty()) { HILOG_ERROR(LOG_CORE, "js stack trace is invalid."); return; } std::vector callInfos; Split(stackTrace, CALL_FUNC_INFO_DELIMITER, callInfos); if (callInfos.size() <= FUNC_NAME_INDEX) { HILOG_ERROR(LOG_CORE, "js function name parsed failed."); return; } callerInfo.first = callInfos[FUNC_NAME_INDEX]; if (callInfos.size() <= LINE_INFO_INDEX) { HILOG_ERROR(LOG_CORE, "js function line info parsed failed."); return; } std::string callInfo = callInfos[LINE_INFO_INDEX]; std::vector lineInfos; Split(callInfo, CALL_LINE_INFO_DELIMITER, lineInfos); if (lineInfos.size() <= LINE_INDEX) { HILOG_ERROR(LOG_CORE, "js function line number parsed failed."); return; } if (callerInfo.first == "anonymous") { auto fileName = lineInfos[LINE_INDEX - 1]; auto pos = fileName.find_last_of(PATH_DELIMITER); callerInfo.first = (pos == std::string::npos) ? fileName : fileName.substr(++pos); } auto lineInfo = lineInfos[LINE_INDEX]; if (std::any_of(lineInfo.begin(), lineInfo.end(), [] (auto& c) { return !isdigit(c); })) { callerInfo.second = DEFAULT_LINE_NUM; return; } callerInfo.second = static_cast(std::stoll(lineInfos[LINE_INDEX])); } } void NapiHiSysEventAdapter::ParseJsCallerInfo(const napi_env env, JsCallerInfo& callerInfo) { std::string stackTrace; if (napi_get_stack_trace(env, stackTrace) != napi_ok) { HILOG_ERROR(LOG_CORE, "js stack trace build failed."); return; } ParseCallerInfoFromStackTrace(stackTrace, callerInfo); } void NapiHiSysEventAdapter::CheckThenWriteSysEvent(HiSysEventAsyncContext* eventAsyncContext) { if (eventAsyncContext == nullptr) { return; } if (eventAsyncContext->eventWroteResult != SUCCESS) { return; } auto eventInfo = eventAsyncContext->eventInfo; auto jsCallerInfo = eventAsyncContext->jsCallerInfo; ControlParam param { .period = HISYSEVENT_DEFAULT_PERIOD, .threshold = HISYSEVENT_DEFAULT_THRESHOLD, }; CallerInfo info = { .func = jsCallerInfo.first.c_str(), .line = jsCallerInfo.second, .timeStamp = eventAsyncContext->timeStamp, }; uint64_t timeStamp = WriteController::CheckLimitWritingEvent(param, eventInfo.domain.c_str(), eventInfo.name.c_str(), info); if (timeStamp == INVALID_TIME_STAMP) { eventAsyncContext->eventWroteResult = ERR_WRITE_IN_HIGH_FREQ; return; } eventAsyncContext->eventWroteResult = Write(eventInfo, timeStamp); } void NapiHiSysEventAdapter::Write(const napi_env env, HiSysEventAsyncContext* eventAsyncContext) { napi_value resource = nullptr; NapiHiSysEventUtil::CreateStringValue(env, FUNC_SOURCE_NAME, resource); eventAsyncContext->timeStamp = WriteController::GetCurrentTimeMills(); napi_create_async_work( env, nullptr, resource, [] (napi_env env, void* data) { HiSysEventAsyncContext* eventAsyncContext = reinterpret_cast(data); CheckThenWriteSysEvent(eventAsyncContext); }, [] (napi_env env, napi_status status, void* data) { HiSysEventAsyncContext* eventAsyncContext = reinterpret_cast(data); napi_value results[RET_SIZE] = {0}; auto isNormalWrote = eventAsyncContext->eventWroteResult == SUCCESS && !NapiHiSysEventUtil::HasStrParamLenOverLimit(eventAsyncContext->eventInfo); if (isNormalWrote) { NapiHiSysEventUtil::CreateNull(env, results[ERR_INDEX]); NapiHiSysEventUtil::CreateInt32Value(env, eventAsyncContext->eventWroteResult, results[VAL_INDEX]); } else { NapiHiSysEventUtil::CreateNull(env, results[VAL_INDEX]); auto errorCode = eventAsyncContext->eventWroteResult == SUCCESS ? ERR_VALUE_LENGTH_TOO_LONG : eventAsyncContext->eventWroteResult; results[ERR_INDEX] = NapiHiSysEventUtil::CreateErrorByRet(env, errorCode); } if (eventAsyncContext->deferred != nullptr) { // promise isNormalWrote ? napi_resolve_deferred(env, eventAsyncContext->deferred, results[VAL_INDEX]) : napi_reject_deferred(env, eventAsyncContext->deferred, results[ERR_INDEX]); } else { napi_value callback = nullptr; napi_get_reference_value(env, eventAsyncContext->callback, &callback); napi_value retValue = nullptr; napi_call_function(env, nullptr, callback, RET_SIZE, results, &retValue); napi_delete_reference(env, eventAsyncContext->callback); } napi_delete_async_work(env, eventAsyncContext->asyncWork); delete eventAsyncContext; }, reinterpret_cast(eventAsyncContext), &eventAsyncContext->asyncWork); napi_queue_async_work_with_qos(env, eventAsyncContext->asyncWork, napi_qos_default); } void NapiHiSysEventAdapter::InnerWrite(HiSysEvent::EventBase& eventBase, const HiSysEventInfo& eventInfo) { AppendParams(eventBase, eventInfo.params); } int NapiHiSysEventAdapter::Write(const HiSysEventInfo& eventInfo, uint64_t timeStamp) { if (!StringFilter::GetInstance().IsValidName(eventInfo.domain, MAX_DOMAIN_LENGTH)) { return HiSysEvent::ExplainThenReturnRetCode(ERR_DOMAIN_NAME_INVALID); } if (!StringFilter::GetInstance().IsValidName(eventInfo.name, MAX_EVENT_NAME_LENGTH)) { return HiSysEvent::ExplainThenReturnRetCode(ERR_EVENT_NAME_INVALID); } HiSysEvent::EventBase eventBase(eventInfo.domain, eventInfo.name, eventInfo.eventType, timeStamp); HiSysEvent::WritebaseInfo(eventBase); if (HiSysEvent::IsError(eventBase)) { return HiSysEvent::ExplainThenReturnRetCode(eventBase.GetRetCode()); } InnerWrite(eventBase, eventInfo); HiSysEvent::InnerWrite(eventBase); if (HiSysEvent::IsError(eventBase)) { return HiSysEvent::ExplainThenReturnRetCode(eventBase.GetRetCode()); } HiSysEvent::SendSysEvent(eventBase); return eventBase.GetRetCode(); } } // namespace HiviewDFX } // namespace OHOS