1 /*
2  * Copyright (C) 2021-2022 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_faultlogger.h"
16 
17 #include <cinttypes>
18 #include <sstream>
19 #include <unistd.h>
20 
21 #include "napi/native_api.h"
22 #include "napi/native_node_api.h"
23 
24 #include "common_utils.h"
25 #include "hiview_logger.h"
26 
27 #include "faultlog_query_result.h"
28 #include "napi_error.h"
29 #include "napi_util.h"
30 
31 namespace OHOS {
32 namespace HiviewDFX {
33 DEFINE_LOG_LABEL(0xD002D11, "Faultlogger-napi");
34 namespace {
35     constexpr size_t ONE_PARAMETER = 1;
36     constexpr size_t TWO_PARAMETER = 2;
37     constexpr size_t THREE_PARAMETER = 3;
38     constexpr size_t FOUR_PARAMETER = 4;
39     std::mutex g_mutex;
40 }
ConversionInform(std::unique_ptr<FaultLogInfo> faultLogInfo)41 static FaultLogNapiInfo ConversionInform(std::unique_ptr<FaultLogInfo> faultLogInfo)
42 {
43     FaultLogNapiInfo ret = {
44         .pid = faultLogInfo->GetProcessId(),
45         .uid = faultLogInfo->GetId(),
46         .type = faultLogInfo->GetFaultType(),
47         .ts = faultLogInfo->GetTimeStamp(),
48         .reason = faultLogInfo->GetFaultReason(),
49         .module = faultLogInfo->GetModuleName(),
50         .summary = faultLogInfo->GetFaultSummary(),
51     };
52     int fd = faultLogInfo->GetRawFileDescriptor();
53     if (fd < 0) {
54         HIVIEW_LOGE("pid %{public}d Fail to get fd:%{public}d\n", faultLogInfo->GetProcessId(), fd);
55         ret.fullLog = "Fail to get log, fd is " + std::to_string(fd);
56         return ret;
57     }
58     while (true) {
59         char buf[BUF_SIZE_512] = {0};
60         int nread = TEMP_FAILURE_RETRY(read((fd), buf, BUF_SIZE_512 - 1));
61         if (nread == -1) {
62             if (errno == EAGAIN) {
63                 continue;
64             } else {
65                 break;
66             }
67         } else if (nread == 0) {
68             break;
69         }
70         ret.fullLog += buf;
71     }
72 
73     return ret;
74 }
75 
FaultLogExecuteCallback(napi_env env,void * data)76 static void FaultLogExecuteCallback(napi_env env, void *data)
77 {
78     std::lock_guard<std::mutex> lock(g_mutex);
79     FaultLogInfoContext* faultLogInfoContext = static_cast<FaultLogInfoContext *>(data);
80     const int maxQueryCount = 10;
81     int currentCount = 0;
82     auto faultLogResult = QuerySelfFaultLog((FaultLogType)faultLogInfoContext->faultType,
83         maxQueryCount);
84     if (faultLogResult == nullptr) {
85         faultLogInfoContext->resolved = true;
86         return;
87     }
88 
89     while (faultLogResult->HasNext()) {
90         if (currentCount >= maxQueryCount) {
91             break;
92         }
93         auto faultLogInfo = faultLogResult->Next();
94         if (faultLogInfo == nullptr) {
95             break;
96         }
97         currentCount++;
98         faultLogInfoContext->infoVector.push_back(ConversionInform(std::move(faultLogInfo)));
99     }
100     faultLogInfoContext->resolved = true;
101 }
102 
FaultLogCompleteCallback(napi_env env,napi_status status,void * data)103 static void FaultLogCompleteCallback(napi_env env, napi_status status, void *data)
104 {
105     FaultLogInfoContext* faultLogInfoContext = static_cast<FaultLogInfoContext *>(data);
106     if (faultLogInfoContext == nullptr) {
107         return;
108     }
109     napi_value callbackValue = nullptr;
110     if (faultLogInfoContext->resolved) {
111         napi_create_array(env, &callbackValue);
112         int i = 0;
113         for (auto& infoItem : faultLogInfoContext->infoVector) {
114             napi_value info = nullptr;
115             napi_create_object(env, &info);
116             NapiUtil::SetPropertyInt32(env, info, "pid", infoItem.pid);
117             NapiUtil::SetPropertyInt32(env, info, "uid", infoItem.uid);
118             NapiUtil::SetPropertyInt32(env, info, "type", infoItem.type);
119             NapiUtil::SetPropertyInt64(env, info, "timestamp", infoItem.ts);
120             NapiUtil::SetPropertyStringUtf8(env, info, "reason", infoItem.reason);
121             NapiUtil::SetPropertyStringUtf8(env, info, "module", infoItem.module);
122             NapiUtil::SetPropertyStringUtf8(env, info, "summary", infoItem.summary);
123             NapiUtil::SetPropertyStringUtf8(env, info, "fullLog", infoItem.fullLog);
124             napi_set_element(env, callbackValue, i, info);
125             ++i;
126             HIVIEW_LOGI("add element when resovled pid = %{public}d, uid = %{public}d, ts = %{public}" PRId64,
127                 infoItem.pid, infoItem.uid, infoItem.ts);
128         }
129     } else {
130         callbackValue = NapiUtil::CreateErrorMessage(env, "get signal info list failed");
131     }
132 
133     if (faultLogInfoContext->callbackRef != nullptr) {
134         napi_value callbackFunc = nullptr;
135         napi_get_reference_value(env, faultLogInfoContext->callbackRef, &callbackFunc);
136         napi_value callbackValues[] = {nullptr, nullptr};
137         callbackValues[0] = faultLogInfoContext->resolved ? NapiUtil::CreateUndefined(env) : callbackValue;
138         callbackValues[1] = faultLogInfoContext->resolved ? callbackValue : NapiUtil::CreateUndefined(env);
139         napi_value recv = NapiUtil::CreateUndefined(env);
140         napi_value result = nullptr;
141         napi_call_function(env, recv, callbackFunc, std::size(callbackValues), callbackValues, &result);
142         napi_delete_reference(env, faultLogInfoContext->callbackRef);
143     } else if (faultLogInfoContext->deferred != nullptr) {
144         if (faultLogInfoContext->resolved) {
145             napi_resolve_deferred(env, faultLogInfoContext->deferred, callbackValue);
146         } else {
147             napi_reject_deferred(env, faultLogInfoContext->deferred, callbackValue);
148         }
149     }
150 
151     napi_delete_async_work(env, faultLogInfoContext->work);
152     delete faultLogInfoContext;
153 }
154 
QuerySelfFaultLog(napi_env env,napi_callback_info info)155 static napi_value QuerySelfFaultLog(napi_env env, napi_callback_info info)
156 {
157     size_t parameterCount = 2;
158     napi_value parameters[2] = {0};
159     napi_value thisVar = nullptr;
160     void *data = nullptr;
161     NAPI_CALL(env, napi_get_cb_info(env, info, &parameterCount, parameters, &thisVar, &data));
162 
163     napi_value result = NapiUtil::CreateUndefined(env);
164     if (parameterCount < ONE_PARAMETER || parameterCount > TWO_PARAMETER) {
165         HIVIEW_LOGE("parameterCount Incorrect %{public}zu", parameterCount);
166         return result;
167     }
168 
169     if (!NapiUtil::IsMatchType(env, parameters[ONE_PARAMETER - 1], napi_number)) {
170         HIVIEW_LOGE("parameters[0] type isn't number");
171         return result;
172     }
173 
174     auto faultLogInfoContext = std::make_unique<FaultLogInfoContext>().release();
175     if (faultLogInfoContext == nullptr) {
176         HIVIEW_LOGE("faultLogInfoContext == nullptr");
177         return result;
178     }
179 
180     napi_get_value_int32(env, parameters[ONE_PARAMETER - 1], &faultLogInfoContext->faultType);
181     if (parameterCount == ONE_PARAMETER) {
182         napi_create_promise(env, &faultLogInfoContext->deferred, &result);
183     } else if (parameterCount == TWO_PARAMETER) {
184         if (!NapiUtil::IsMatchType(env, parameters[TWO_PARAMETER - 1], napi_function)) {
185             HIVIEW_LOGE("parameters[1] type isn't function");
186             delete faultLogInfoContext;
187             return result;
188         }
189         NAPI_CALL(env, napi_create_reference(env, parameters[TWO_PARAMETER - 1], 1, &faultLogInfoContext->callbackRef));
190     }
191 
192     napi_value resource = NapiUtil::CreateUndefined(env);
193     napi_value resourceName = nullptr;
194     napi_create_string_utf8(env, "QuerySelfFaultLog", NAPI_AUTO_LENGTH, &resourceName);
195     napi_create_async_work(env, resource, resourceName, FaultLogExecuteCallback,
196         FaultLogCompleteCallback, (void *)faultLogInfoContext, &faultLogInfoContext->work);
197     napi_queue_async_work_with_qos(env, faultLogInfoContext->work, napi_qos_default);
198     return result;
199 }
200 
AddFaultLog(napi_env env,napi_callback_info info)201 static napi_value AddFaultLog(napi_env env, napi_callback_info info)
202 {
203     size_t parameterCount = 4;
204     napi_value parameters[4] = {0};
205     napi_value thisVar = nullptr;
206     void *data = nullptr;
207     NAPI_CALL(env, napi_get_cb_info(env, info, &parameterCount, parameters, &thisVar, &data));
208 
209     napi_value result = NapiUtil::CreateUndefined(env);
210     if (parameterCount != FOUR_PARAMETER) {
211         return result;
212     }
213     if (!NapiUtil::IsMatchType(env, parameters[ONE_PARAMETER - 1], napi_number)) {
214         HIVIEW_LOGE("parameters[0] type isn't number");
215         return result;
216     }
217     if (!NapiUtil::IsMatchType(env, parameters[TWO_PARAMETER - 1], napi_number)) {
218         HIVIEW_LOGE("parameters[1] type isn't number");
219         return result;
220     }
221     if (!NapiUtil::IsMatchType(env, parameters[THREE_PARAMETER - 1], napi_string)) {
222         HIVIEW_LOGE("parameters[2] type isn't napi_string");
223         return result;
224     }
225     if (!NapiUtil::IsMatchType(env, parameters[FOUR_PARAMETER - 1], napi_string)) {
226         HIVIEW_LOGE("parameters[3] type isn't napi_string");
227         return result;
228     }
229     int32_t nowTmp;
230     size_t resultString;
231     napi_get_value_int32(env, parameters[ONE_PARAMETER - 1], &nowTmp);
232     int64_t now = time(nullptr) + nowTmp;
233     int32_t logType;
234     napi_get_value_int32(env, parameters[TWO_PARAMETER - 1], &logType);
235 
236     char module[BUF_SIZE_64];
237     napi_get_value_string_utf8(env, parameters[THREE_PARAMETER - 1], module, BUF_SIZE_64, &resultString);
238     char summary[BUF_SIZE_64];
239     napi_get_value_string_utf8(env, parameters[FOUR_PARAMETER - 1], summary, BUF_SIZE_64, &resultString);
240     constexpr int64_t SEC_TO_MILLISEC = 1000;
241     AddFaultLog(now * SEC_TO_MILLISEC, logType, module, summary);
242     return result;
243 }
244 
Query(napi_env env,napi_callback_info info)245 static napi_value Query(napi_env env, napi_callback_info info)
246 {
247     size_t parameterCount = 2;
248     napi_value parameters[2] = {0};
249     napi_value thisVar = nullptr;
250     void *data = nullptr;
251     NAPI_CALL(env, napi_get_cb_info(env, info, &parameterCount, parameters, &thisVar, &data));
252 
253     napi_value result = NapiUtil::CreateUndefined(env);
254     if (parameterCount < ONE_PARAMETER || parameterCount > TWO_PARAMETER) {
255         HIVIEW_LOGE("parameterCount Incorrect %{public}zu", parameterCount);
256         NapiUtil::ThrowError(env, NapiError::ERR_INPUT_PARAM, NapiUtil::CreateParamCntErrMsg());
257         return result;
258     }
259 
260     if (!NapiUtil::IsMatchType(env, parameters[ONE_PARAMETER - 1], napi_number)) {
261         HIVIEW_LOGE("parameters[0] type isn't number");
262         NapiUtil::ThrowError(env, NapiError::ERR_INPUT_PARAM, NapiUtil::CreateErrMsg("type", napi_number));
263         return result;
264     }
265     if (!CheckFaultloggerStatus()) {
266         NapiUtil::ThrowError(env, NapiError::ERR_SERVICE_STATUS, NapiUtil::CreateServiceErrMsg());
267         return result;
268     }
269 
270     auto faultLogInfoContext = std::make_unique<FaultLogInfoContext>().release();
271     if (faultLogInfoContext == nullptr) {
272         HIVIEW_LOGE("faultLogInfoContext == nullptr");
273         return result;
274     }
275 
276     napi_get_value_int32(env, parameters[ONE_PARAMETER - 1], &faultLogInfoContext->faultType);
277     if (parameterCount == ONE_PARAMETER) {
278         napi_create_promise(env, &faultLogInfoContext->deferred, &result);
279     } else if (parameterCount == TWO_PARAMETER) {
280         if (!NapiUtil::IsMatchType(env, parameters[TWO_PARAMETER - 1], napi_function)) {
281             HIVIEW_LOGE("parameters[1] type isn't function");
282             delete faultLogInfoContext;
283             NapiUtil::ThrowError(env, NapiError::ERR_INPUT_PARAM,
284                 NapiUtil::CreateErrMsg("callback", napi_function), true);
285             return result;
286         }
287         NAPI_CALL(env, napi_create_reference(env, parameters[TWO_PARAMETER - 1], 1, &faultLogInfoContext->callbackRef));
288     }
289 
290     napi_value resource = NapiUtil::CreateUndefined(env);
291     napi_value resourceName = nullptr;
292     napi_create_string_utf8(env, "QuerySelfFaultLog", NAPI_AUTO_LENGTH, &resourceName);
293     napi_create_async_work(env, resource, resourceName, FaultLogExecuteCallback,
294         FaultLogCompleteCallback, (void *)faultLogInfoContext, &faultLogInfoContext->work);
295     napi_queue_async_work_with_qos(env, faultLogInfoContext->work, napi_qos_default);
296     return result;
297 }
298 
FaultLogTypeConstructor(napi_env env,napi_callback_info info)299 napi_value FaultLogTypeConstructor(napi_env env, napi_callback_info info)
300 {
301     napi_value thisArg = nullptr;
302     void* data = nullptr;
303 
304     napi_get_cb_info(env, info, nullptr, nullptr, &thisArg, &data);
305 
306     napi_value global = nullptr;
307     napi_get_global(env, &global);
308 
309     return thisArg;
310 }
311 
FaultLogTypeEnumInit(napi_env env,napi_value exports)312 static void FaultLogTypeEnumInit(napi_env env, napi_value exports)
313 {
314     napi_value noSpecific = nullptr;
315     napi_value cppCrash = nullptr;
316     napi_value jsCrash = nullptr;
317     napi_value appCrash = nullptr;
318 
319     napi_create_int32(env, FaultLogType::NO_SPECIFIC, &noSpecific);
320     napi_create_int32(env, FaultLogType::CPP_CRASH, &cppCrash);
321     napi_create_int32(env, FaultLogType::JS_CRASH, &jsCrash);
322     napi_create_int32(env, FaultLogType::APP_FREEZE, &appCrash);
323 
324     napi_property_descriptor descriptors[] = {
325         DECLARE_NAPI_STATIC_PROPERTY("NO_SPECIFIC", noSpecific),
326         DECLARE_NAPI_STATIC_PROPERTY("CPP_CRASH", cppCrash),
327         DECLARE_NAPI_STATIC_PROPERTY("JS_CRASH", jsCrash),
328         DECLARE_NAPI_STATIC_PROPERTY("APP_FREEZE", appCrash),
329     };
330 
331     napi_value result = nullptr;
332     napi_define_class(env, "FaultType", NAPI_AUTO_LENGTH, FaultLogTypeConstructor, nullptr,
333                       sizeof(descriptors) / sizeof(*descriptors), descriptors, &result);
334 
335     napi_set_named_property(env, exports, "FaultType", result);
336 }
337 
FaultLogInfoConstructor(napi_env env,napi_callback_info info)338 napi_value FaultLogInfoConstructor(napi_env env, napi_callback_info info)
339 {
340     napi_value thisArg = nullptr;
341     void* data = nullptr;
342 
343     napi_get_cb_info(env, info, nullptr, nullptr, &thisArg, &data);
344 
345     napi_value global = nullptr;
346     napi_get_global(env, &global);
347 
348     return thisArg;
349 }
350 
FaultLogInfoClassInit(napi_env env,napi_value exports)351 static void FaultLogInfoClassInit(napi_env env, napi_value exports)
352 {
353     napi_value pid = nullptr;
354     napi_value uid = nullptr;
355     napi_value type = nullptr;
356     napi_value ts = nullptr;
357     napi_value reason = nullptr;
358     napi_value module = nullptr;
359     napi_value summary = nullptr;
360     napi_value fullLog = nullptr;
361 
362     napi_create_int32(env, 0, &pid);
363     napi_create_int32(env, 0, &uid);
364     napi_create_int32(env, FaultLogType::NO_SPECIFIC, &type);
365     napi_create_int64(env, 0, &ts);
366     napi_create_string_latin1(env, "reason", NAPI_AUTO_LENGTH, &reason);
367     napi_create_string_latin1(env, "module", NAPI_AUTO_LENGTH, &module);
368     napi_create_string_latin1(env, "summary", NAPI_AUTO_LENGTH, &summary);
369     napi_create_string_latin1(env, "fullLog", NAPI_AUTO_LENGTH, &fullLog);
370 
371     napi_property_descriptor descriptors[] = {
372         DECLARE_NAPI_PROPERTY("pid", pid),
373         DECLARE_NAPI_PROPERTY("uid", uid),
374         DECLARE_NAPI_PROPERTY("type", type),
375         DECLARE_NAPI_PROPERTY("timestamp", ts),
376         DECLARE_NAPI_PROPERTY("reason", reason),
377         DECLARE_NAPI_PROPERTY("module", module),
378         DECLARE_NAPI_PROPERTY("summary", summary),
379         DECLARE_NAPI_PROPERTY("fullLog", fullLog),
380     };
381 
382     napi_value result = nullptr;
383     napi_define_class(env, "FaultLogInfo", NAPI_AUTO_LENGTH, FaultLogInfoConstructor, nullptr,
384                       sizeof(descriptors) / sizeof(*descriptors), descriptors, &result);
385 
386     napi_set_named_property(env, exports, "FaultLogInfo", result);
387 }
388 
389 EXTERN_C_START
InitNapiRegistry(napi_env env,napi_value exports)390 napi_value InitNapiRegistry(napi_env env, napi_value exports)
391 {
392     napi_property_descriptor desc[] = {
393         DECLARE_NAPI_FUNCTION("querySelfFaultLog", QuerySelfFaultLog),
394         DECLARE_NAPI_FUNCTION("addFaultLog", AddFaultLog),
395         DECLARE_NAPI_FUNCTION("query", Query),
396     };
397     NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
398     FaultLogTypeEnumInit(env, exports);
399     FaultLogInfoClassInit(env, exports);
400     return exports;
401 }
402 EXTERN_C_END
403 
404 static napi_module _stateRegistryModule = {
405     .nm_version = 1,
406     .nm_flags = 0,
407     .nm_filename = NULL,
408     .nm_register_func = InitNapiRegistry,
409     .nm_modname = "faultLogger",
410     .nm_priv = ((void *)0),
411     .reserved = {(void *)0},
412 };
413 
RegisterFaultLoggerModule(void)414 extern "C" __attribute__((constructor)) void RegisterFaultLoggerModule(void)
415 {
416     napi_module_register(&_stateRegistryModule);
417 }
418 }  // namespace HiviewDFX
419 }  // namespace OHOS
420