1/*
2 * Copyright (c) 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
16#include "hichecker.h"
17
18#include <csignal>
19#include <cerrno>
20#include <sys/types.h>
21#include <unistd.h>
22#include <cstring>
23#include <cstdio>
24#include <cstdlib>
25#include <parameter.h>
26
27#include "securec.h"
28
29#include "dfx_dump_catcher.h"
30#include "hilog/log_c.h"
31#include "hilog/log_cpp.h"
32
33namespace OHOS {
34namespace HiviewDFX {
35#define PARAM_BUF_LEN 128
36#define QUERYNAME_LEN 80
37#undef LOG_DOMAIN
38#define LOG_DOMAIN 0xD002D0B
39#undef LOG_TAG
40#define LOG_TAG "HICHECKER"
41constexpr int BASE_TAG = 10;
42constexpr uint64_t ALLOWED_RULE = Rule::RULE_CHECK_ARKUI_PERFORMANCE;
43
44std::mutex HiChecker::mutexLock_;
45volatile bool HiChecker::checkMode_;
46volatile uint64_t HiChecker::processRules_;
47thread_local uint64_t HiChecker::threadLocalRules_;
48
49void HiChecker::AddRule(uint64_t rule)
50{
51    std::lock_guard<std::mutex> lock(mutexLock_);
52    if (!CheckRule(rule)) {
53        return;
54    }
55    if ((Rule::RULE_CHECK_SLOW_EVENT & rule)) {
56        checkMode_ = true;
57    }
58    threadLocalRules_ |= (Rule::ALL_THREAD_RULES | Rule::ALL_CAUTION_RULES) & rule;
59    processRules_ |= (Rule::ALL_PROCESS_RULES | Rule::ALL_CAUTION_RULES) & rule;
60}
61
62void HiChecker::RemoveRule(uint64_t rule)
63{
64    std::lock_guard<std::mutex> lock(mutexLock_);
65    if (!CheckRule(rule)) {
66        return;
67    }
68    if ((Rule::RULE_CHECK_SLOW_EVENT & rule)) {
69        checkMode_ = false;
70    }
71    threadLocalRules_ ^= threadLocalRules_ & rule;
72    processRules_ ^= processRules_ & rule;
73}
74
75uint64_t HiChecker::GetRule()
76{
77    std::lock_guard<std::mutex> lock(mutexLock_);
78    return (threadLocalRules_ | processRules_);
79}
80
81bool HiChecker::Contains(uint64_t rule)
82{
83    std::lock_guard<std::mutex> lock(mutexLock_);
84    if (!CheckRule(rule)) {
85        return false;
86    }
87    return rule == (rule & (threadLocalRules_ | processRules_));
88}
89
90void HiChecker::NotifySlowProcess(const std::string& tag)
91{
92    if ((threadLocalRules_ & Rule::RULE_THREAD_CHECK_SLOW_PROCESS) == 0) {
93        return;
94    }
95    std::string stackTrace;
96    DumpStackTrace(stackTrace);
97    Caution caution(Rule::RULE_THREAD_CHECK_SLOW_PROCESS,
98        "trigger:RULE_THREAD_CHECK_SLOW_PROCESS," + tag, stackTrace);
99    HandleCaution(caution);
100}
101
102void HiChecker::NotifySlowEvent(const std::string& tag)
103{
104    if ((processRules_ & Rule::RULE_CHECK_SLOW_EVENT) == 0) {
105        return;
106    }
107    std::string stackTrace;
108    DumpStackTrace(stackTrace);
109    Caution caution(Rule::RULE_CHECK_SLOW_EVENT,
110        "trigger:RULE_CHECK_SLOW_EVENT," + tag, stackTrace);
111    HandleCaution(caution);
112}
113
114void HiChecker::NotifyAbilityConnectionLeak(const Caution& caution)
115{
116    if ((processRules_ & Rule::RULE_CHECK_ABILITY_CONNECTION_LEAK) == 0) {
117        return;
118    }
119    HandleCaution(caution);
120}
121
122void HiChecker::NotifyCaution(uint64_t rule, const std::string& tag, Caution& caution)
123{
124    if ((threadLocalRules_ & rule) == 0 && (processRules_ & rule) == 0) {
125        return;
126    }
127    std::string msg;
128    switch (rule) {
129        case Rule::RULE_THREAD_CHECK_SLOW_PROCESS:
130            msg = "trigger:RULE_THREAD_CHECK_SLOW_PROCESS," + tag;
131            break;
132        case Rule::RULE_CHECK_SLOW_EVENT:
133            msg = "trigger:RULE_CHECK_SLOW_EVENT," + tag;
134            break;
135        case Rule::RULE_CHECK_ARKUI_PERFORMANCE:
136            msg = "trigger:RULE_CHECK_ARKUI_PERFORMANCE," + tag;
137            break;
138        default:
139            break;
140    }
141    if (Rule::RULE_CHECK_ABILITY_CONNECTION_LEAK != rule) {
142        std::string stackTrace;
143        DumpStackTrace(stackTrace);
144        caution.SetCautionMsg(msg);
145        caution.SetStackTrace(stackTrace);
146    }
147    HandleCaution(caution);
148}
149
150void HiChecker::HandleCaution(const Caution& caution)
151{
152    uint64_t triggerRule = caution.GetTriggerRule();
153    if ((threadLocalRules_ & triggerRule)) {
154        CautionDetail cautionDetail(caution, threadLocalRules_);
155        OnThreadCautionFound(cautionDetail);
156        return;
157    }
158    if ((processRules_ & triggerRule)) {
159        CautionDetail cautionDetail(caution, processRules_);
160        OnProcessCautionFound(cautionDetail);
161        return;
162    }
163}
164
165void HiChecker::OnThreadCautionFound(CautionDetail& cautionDetail)
166{
167    if ((cautionDetail.rules_ & Rule::ALL_CAUTION_RULES) == 0) {
168        cautionDetail.rules_ |= Rule::RULE_CAUTION_PRINT_LOG;
169    }
170    if (cautionDetail.CautionEnable(Rule::RULE_CAUTION_PRINT_LOG)
171        && !cautionDetail.CautionEnable(Rule::RULE_CAUTION_TRIGGER_CRASH)) {
172        PrintLog(cautionDetail);
173    }
174    if (cautionDetail.CautionEnable(Rule::RULE_CAUTION_TRIGGER_CRASH)) {
175        TriggerCrash(cautionDetail);
176    }
177}
178
179void HiChecker::OnProcessCautionFound(CautionDetail& cautionDetail)
180{
181    OnThreadCautionFound(cautionDetail);
182}
183
184void HiChecker::PrintLog(const CautionDetail& cautionDetail)
185{
186    HILOG_INFO(LOG_CORE,
187        "HiChecker caution with RULE_CAUTION_PRINT_LOG.\nCautionMsg:%{public}s\nStackTrace:\n%{public}s",
188        cautionDetail.caution_.GetCautionMsg().c_str(),
189        cautionDetail.caution_.GetStackTrace().c_str());
190}
191
192void HiChecker::TriggerCrash(const CautionDetail& cautionDetail)
193{
194    HILOG_INFO(LOG_CORE,
195        "HiChecker caution with RULE_CAUTION_TRIGGER_CRASH; exit.\nCautionMsg:%{public}s\nStackTrace:\n%{public}s",
196        cautionDetail.caution_.GetCautionMsg().c_str(),
197        cautionDetail.caution_.GetStackTrace().c_str());
198    kill(getpid(), SIGABRT);
199}
200
201bool HiChecker::NeedCheckSlowEvent()
202{
203    return checkMode_;
204}
205
206bool HiChecker::HasCautionRule(uint64_t rules)
207{
208    return (rules & Rule::ALL_CAUTION_RULES);
209}
210
211void HiChecker::DumpStackTrace(std::string& msg)
212{
213    DfxDumpCatcher dumplog;
214    if (!dumplog.DumpCatch(getpid(), gettid(), msg)) {
215        HILOG_INFO(LOG_CORE, "HiChecker DumpStackTrace fail.");
216    }
217}
218
219bool HiChecker::CheckRule(uint64_t rule)
220{
221    if (rule <= 0 || Rule::ALL_RULES != (Rule::ALL_RULES | rule)) {
222        HILOG_INFO(LOG_CORE, "input rule is not exist,please check.");
223        return false;
224    }
225    return true;
226}
227
228void HiChecker::InitHicheckerParam(const char *processName)
229{
230    char checkerName[QUERYNAME_LEN] = "hiviewdfx.hichecker.";
231    errno_t err = 0;
232    err = strcat_s(checkerName, sizeof(checkerName), processName);
233    if (err != EOK) {
234        HILOG_INFO(LOG_CORE, "checker strcat_s query name failed.");
235        return;
236    }
237
238    char paramOutBuf[PARAM_BUF_LEN] = { 0 };
239    char defStrValue[PARAM_BUF_LEN] = { 0 };
240    int retLen = GetParameter(checkerName, defStrValue, paramOutBuf, PARAM_BUF_LEN);
241    if (retLen <= 0 || retLen > PARAM_BUF_LEN - 1) {
242        HILOG_INFO(LOG_CORE, "hichecker param is empty.");
243        return;
244    }
245    paramOutBuf[retLen] = '\0';
246    HILOG_INFO(LOG_CORE, "hichecker param value is %{public}s", paramOutBuf);
247    char *endPtr = nullptr;
248    uint64_t rule = strtoull(paramOutBuf, &endPtr, BASE_TAG);
249    if (!(rule & ALLOWED_RULE)) {
250        HILOG_ERROR(LOG_CORE, "not allowed param.");
251        return;
252    }
253    AddRule(rule & ALLOWED_RULE);
254    return;
255}
256} // HiviewDFX
257} // OHOS
258