1 /*
2  * Copyright (c) 2022-2024 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 #include "napi_app_event_holder.h"
16 
17 #include <cinttypes>
18 
19 #include "app_event_store.h"
20 #include "hilog/log.h"
21 #include "napi_error.h"
22 #include "napi_util.h"
23 
24 #undef LOG_DOMAIN
25 #define LOG_DOMAIN 0xD002D07
26 
27 #undef LOG_TAG
28 #define LOG_TAG "NapiHolder"
29 
30 namespace OHOS {
31 namespace HiviewDFX {
32 namespace {
33 constexpr size_t PARAM_NUM = 1;
34 constexpr int DEFAULT_ROW_NUM = 1;
35 constexpr int DEFAULT_SIZE = 512 * 1024; // 512 * 1024: 512KB
36 const std::string HOLDER_CLASS_NAME = "AppEventPackageHolder";
37 
GetObserverSeqByName(const std::string & name)38 int64_t GetObserverSeqByName(const std::string& name)
39 {
40     int64_t observerSeq = -1;
41     if (observerSeq = AppEventStore::GetInstance().QueryObserverSeq(name); observerSeq <= 0) {
42         HILOG_WARN(LOG_CORE, "failed to query seq by name=%{public}s", name.c_str());
43         return -1;
44     }
45     return observerSeq;
46 }
47 }
48 thread_local napi_ref NapiAppEventHolder::constructor_ = nullptr;
49 
NapiAppEventHolder(const std::string & name,int64_t observerSeq)50 NapiAppEventHolder::NapiAppEventHolder(const std::string& name, int64_t observerSeq)
51     : name_(name), observerSeq_(observerSeq), hasSetRow_(false), hasSetSize_(false)
52 {
53     takeRow_ = DEFAULT_ROW_NUM;
54     takeSize_ = DEFAULT_SIZE;
55     packageId_ = 0; // id is incremented from 0
56 
57     // if the seq is invalid, need to get seq by the name(for js constructor)
58     if (observerSeq_ <= 0) {
59         observerSeq_ = GetObserverSeqByName(name_);
60     }
61 }
62 
NapiConstructor(napi_env env,napi_callback_info info)63 napi_value NapiAppEventHolder::NapiConstructor(napi_env env, napi_callback_info info)
64 {
65     size_t paramNum = PARAM_NUM;
66     napi_value params[PARAM_NUM] = { 0 };
67     napi_value thisVar = nullptr;
68     NAPI_CALL(env, napi_get_cb_info(env, info, &paramNum, params, &thisVar, nullptr));
69 
70     if (paramNum < PARAM_NUM) {
71         HILOG_ERROR(LOG_CORE, "hodler failed to construct: invalid param num");
72         return thisVar;
73     }
74     auto holder = new(std::nothrow) NapiAppEventHolder(NapiUtil::GetString(env, params[0]));
75     if (holder == nullptr) {
76         return thisVar;
77     }
78     napi_wrap(
79         env, thisVar, holder,
80         [](napi_env env, void* data, void* hint) {
81             NapiAppEventHolder* holder = (NapiAppEventHolder*)data;
82             delete holder;
83         },
84         nullptr, nullptr);
85     return thisVar;
86 }
87 
NapiExport(napi_env env,napi_value exports)88 napi_value NapiAppEventHolder::NapiExport(napi_env env, napi_value exports)
89 {
90     napi_property_descriptor properties[] = {
91         DECLARE_NAPI_FUNCTION("setRow", NapiSetRow),
92         DECLARE_NAPI_FUNCTION("setSize", NapiSetSize),
93         DECLARE_NAPI_FUNCTION("takeNext", NapiTakeNext)
94     };
95     napi_value holderClass = nullptr;
96     napi_define_class(env, HOLDER_CLASS_NAME.c_str(), HOLDER_CLASS_NAME.size(), NapiConstructor, nullptr,
97         sizeof(properties) / sizeof(properties[0]), properties, &holderClass);
98     NapiUtil::SetNamedProperty(env, exports, HOLDER_CLASS_NAME, holderClass);
99     constructor_ = NapiUtil::CreateReference(env, holderClass);
100     return exports;
101 }
102 
NapiSetRow(napi_env env,napi_callback_info info)103 napi_value NapiAppEventHolder::NapiSetRow(napi_env env, napi_callback_info info)
104 {
105     size_t paramNum = PARAM_NUM;
106     napi_value params[PARAM_NUM] = { 0 };
107     napi_value thisVar = nullptr;
108     NAPI_CALL(env, napi_get_cb_info(env, info, &paramNum, params, &thisVar, nullptr));
109     if (paramNum < PARAM_NUM) {
110         NapiUtil::ThrowError(env, NapiError::ERR_PARAM, NapiUtil::CreateErrMsg("size"));
111         return nullptr;
112     }
113     if (!NapiUtil::IsNumber(env, params[0])) {
114         NapiUtil::ThrowError(env, NapiError::ERR_PARAM, NapiUtil::CreateErrMsg("size", "number"));
115         return nullptr;
116     }
117     int num = NapiUtil::GetInt32(env, params[0]);
118     if (num <= 0) {
119         NapiUtil::ThrowError(env, NapiError::ERR_INVALID_SIZE, "Invalid size value.");
120         return NapiUtil::CreateUndefined(env);
121     }
122     NapiAppEventHolder* holder = nullptr;
123     if (napi_unwrap(env, thisVar, (void**)&holder) != napi_ok || holder == nullptr) {
124         return nullptr;
125     }
126     holder->SetRow(num);
127     return NapiUtil::CreateUndefined(env);
128 }
129 
NapiSetSize(napi_env env,napi_callback_info info)130 napi_value NapiAppEventHolder::NapiSetSize(napi_env env, napi_callback_info info)
131 {
132     size_t paramNum = PARAM_NUM;
133     napi_value params[PARAM_NUM] = { 0 };
134     napi_value thisVar = nullptr;
135     NAPI_CALL(env, napi_get_cb_info(env, info, &paramNum, params, &thisVar, nullptr));
136     if (paramNum < PARAM_NUM) {
137         NapiUtil::ThrowError(env, NapiError::ERR_PARAM, NapiUtil::CreateErrMsg("size"));
138         return nullptr;
139     }
140     if (!NapiUtil::IsNumber(env, params[0])) {
141         NapiUtil::ThrowError(env, NapiError::ERR_PARAM, NapiUtil::CreateErrMsg("size", "number"));
142         return nullptr;
143     }
144     int num = NapiUtil::GetInt32(env, params[0]);
145     if (num < 0) {
146         NapiUtil::ThrowError(env, NapiError::ERR_INVALID_SIZE, "Invalid size value.");
147         return NapiUtil::CreateUndefined(env);
148     }
149     NapiAppEventHolder* holder = nullptr;
150     if (napi_unwrap(env, thisVar, (void**)&holder) != napi_ok || holder == nullptr) {
151         return nullptr;
152     }
153     holder->SetSize(num);
154     return NapiUtil::CreateUndefined(env);
155 }
156 
NapiTakeNext(napi_env env,napi_callback_info info)157 napi_value NapiAppEventHolder::NapiTakeNext(napi_env env, napi_callback_info info)
158 {
159     napi_value thisVar = nullptr;
160     NAPI_CALL(env, napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr));
161     NapiAppEventHolder* holder = nullptr;
162     if (napi_unwrap(env, thisVar, (void**)&holder) != napi_ok || holder == nullptr) {
163         return NapiUtil::CreateNull(env);
164     }
165     auto package = holder->TakeNext();
166     if (package == nullptr) {
167         return NapiUtil::CreateNull(env);
168     }
169     napi_value packageObj = NapiUtil::CreateObject(env);
170     NapiUtil::SetNamedProperty(env, packageObj, "packageId", NapiUtil::CreateInt32(env, package->packageId));
171     NapiUtil::SetNamedProperty(env, packageObj, "row", NapiUtil::CreateInt32(env, package->row));
172     NapiUtil::SetNamedProperty(env, packageObj, "size", NapiUtil::CreateInt32(env, package->size));
173     NapiUtil::SetNamedProperty(env, packageObj, "data", NapiUtil::CreateStrings(env, package->data));
174     NapiUtil::SetNamedProperty(env, packageObj, "appEventInfos", NapiUtil::CreateEventInfoArray(env, package->events));
175     return packageObj;
176 }
177 
SetRow(int row)178 void NapiAppEventHolder::SetRow(int row)
179 {
180     HILOG_INFO(LOG_CORE, "hodler seq=%{public}" PRId64 " set row=%{public}d", observerSeq_, row);
181     takeRow_ = row;
182     hasSetRow_ = true;
183 }
184 
SetSize(int size)185 void NapiAppEventHolder::SetSize(int size)
186 {
187     HILOG_INFO(LOG_CORE, "hodler seq=%{public}" PRId64 " set size=%{public}d", observerSeq_, size);
188     takeSize_ = size;
189     hasSetSize_ = true;
190 }
191 
TakeNext()192 std::shared_ptr<AppEventPackage> NapiAppEventHolder::TakeNext()
193 {
194     std::vector<std::shared_ptr<AppEventPack>> events;
195     bool shouldTakeSize = hasSetSize_ && !hasSetRow_;
196     int rowNum = shouldTakeSize ? 0 : takeRow_;
197     if (AppEventStore::GetInstance().QueryEvents(events, observerSeq_, rowNum) != 0) {
198         HILOG_WARN(LOG_CORE, "failed to query events, seq=%{public}" PRId64, observerSeq_);
199         return nullptr;
200     }
201     if (events.empty()) {
202         HILOG_DEBUG(LOG_CORE, "end to query events, seq=%{public}" PRId64, observerSeq_);
203         return nullptr;
204     }
205 
206     std::vector<int64_t> eventSeqs;
207     std::vector<std::string> eventStrs;
208     size_t totalSize = 0;
209     auto package = std::make_shared<AppEventPackage>();
210     for (auto event : events) {
211         std::string eventStr = event->GetEventStr();
212         if (shouldTakeSize && static_cast<int>(totalSize + eventStr.size()) > takeSize_) {
213             HILOG_INFO(LOG_CORE, "stop to take data, totalSize=%{public}zu, takeSize=%{public}d",
214                 totalSize, takeSize_);
215             break;
216         }
217         totalSize += eventStr.size();
218         eventStrs.emplace_back(eventStr);
219         eventSeqs.emplace_back(event->GetSeq());
220         package->events.emplace_back(event);
221     }
222     if (eventStrs.empty()) {
223         HILOG_INFO(LOG_CORE, "take data is empty, seq=%{public}" PRId64, observerSeq_);
224         return nullptr;
225     }
226     if (!AppEventStore::GetInstance().DeleteData(observerSeq_, eventSeqs)) {
227         HILOG_INFO(LOG_CORE, "failed to delete mapping data, seq=%{public}" PRId64, observerSeq_);
228         return nullptr;
229     }
230 
231     package->packageId = packageId_++;
232     package->row = static_cast<int>(eventStrs.size());
233     package->size = static_cast<int>(totalSize);
234     package->data = eventStrs;
235     return package;
236 }
237 } // namespace HiviewDFX
238 } // namespace OHOS
239