1/*
2 * Copyright (c) 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
16#include "dfx_sigdump_handler.h"
17
18#include <cinttypes>
19#include <csignal>
20#include <ctime>
21#include <mutex>
22#include <sigchain.h>
23#include <sys/syscall.h>
24#include <thread>
25#include <unistd.h>
26
27#include "backtrace_local.h"
28#include "dfx_define.h"
29#include "dfx_dump_res.h"
30#include "dfx_log.h"
31#include "faultloggerd_client.h"
32
33namespace OHOS {
34namespace HiviewDFX {
35namespace {
36#ifdef LOG_DOMAIN
37#undef LOG_DOMAIN
38#define LOG_DOMAIN 0xD002D11
39#endif
40
41#ifdef LOG_TAG
42#undef LOG_TAG
43#define LOG_TAG "DfxSigDumpHandler"
44#endif
45}
46
47static const struct timespec SIG_WAIT_TIMEOUT = {
48    .tv_sec = 1,
49    .tv_nsec = 0,
50};
51
52class DfxSigDumpHandler {
53public:
54    static DfxSigDumpHandler& GetInstance(void);
55    bool Init(void);
56    void Deinit(void);
57    bool IsThreadRunning(void) const;
58    int GetRunThreadId(void) const;
59    void SetRunThreadId(int tid);
60private:
61    DfxSigDumpHandler() = default;
62    DfxSigDumpHandler(DfxSigDumpHandler&) = delete;
63    DfxSigDumpHandler& operator=(const DfxSigDumpHandler&)=delete;
64    static void RunThread(void);
65    static void SignalDumpRetranHandler(int signo, siginfo_t* si, void* context);
66    bool isThreadRunning_{false};
67    int runThreadId_{0};
68};
69
70DfxSigDumpHandler& DfxSigDumpHandler::GetInstance()
71{
72    static DfxSigDumpHandler sigDumperHandler;
73    return sigDumperHandler;
74}
75
76bool DfxSigDumpHandler::IsThreadRunning() const
77{
78    return isThreadRunning_;
79}
80
81int DfxSigDumpHandler::GetRunThreadId() const
82{
83    return runThreadId_;
84}
85
86void DfxSigDumpHandler::SetRunThreadId(int tid)
87{
88    runThreadId_ = tid;
89}
90
91void DfxSigDumpHandler::SignalDumpRetranHandler(int signo, siginfo_t* si, void* context)
92{
93    int tid = DfxSigDumpHandler::GetInstance().GetRunThreadId();
94    if (tid == 0) {
95        return;
96    }
97    if (syscall(SYS_tkill, tid, SIGDUMP) != 0) {
98        return;
99    }
100}
101
102void DfxSigDumpHandler::RunThread()
103{
104    sigset_t set;
105    sigemptyset(&set);
106    sigaddset(&set, SIGDUMP);
107    if (pthread_sigmask(SIG_BLOCK, &set, nullptr) != 0) {
108        DFXLOGE("pthread sigmask failed, err(%{public}d)", errno);
109    }
110    DfxSigDumpHandler::GetInstance().SetRunThreadId(gettid());
111    while (DfxSigDumpHandler::GetInstance().IsThreadRunning()) {
112        siginfo_t si;
113        if (OHOS_TEMP_FAILURE_RETRY(sigtimedwait(&set, &si, &SIG_WAIT_TIMEOUT)) == -1) {
114            continue;
115        }
116        int32_t resFd = -1;
117        int res = DUMP_ESUCCESS;
118        int32_t pid = getpid();
119        FaultLoggerPipeType jsonType = FaultLoggerPipeType::PIPE_FD_JSON_WRITE_RES;
120        int32_t fd = RequestPipeFd(pid, FaultLoggerPipeType::PIPE_FD_JSON_WRITE_BUF);
121        if (fd < 0) {
122            fd = RequestPipeFd(pid, FaultLoggerPipeType::PIPE_FD_WRITE_BUF);
123            jsonType = FaultLoggerPipeType::PIPE_FD_WRITE_RES;
124        }
125        if (fd < 0) {
126            DFXLOGE("Pid %{public}d GetPipeFd Failed", pid);
127            continue;
128        }
129        resFd = RequestPipeFd(pid, jsonType);
130        if (resFd < 0) {
131            DFXLOGE("Pid %{public}d GetPipeResFd Failed", pid);
132            close(fd);
133            continue;
134        }
135        std::string dumpInfo = OHOS::HiviewDFX::GetProcessStacktrace();
136        const ssize_t nwrite = static_cast<ssize_t>(dumpInfo.length());
137        if (!dumpInfo.empty() &&
138                OHOS_TEMP_FAILURE_RETRY(write(fd, dumpInfo.data(), dumpInfo.length())) != nwrite) {
139            DFXLOGE("Pid %{public}d Write Buf Pipe Failed(%{public}d), nwrite(%{public}zd)", pid, errno, nwrite);
140            res = DUMP_EBADFRAME;
141        } else if (dumpInfo.empty()) {
142            res = DUMP_ENOINFO;
143        }
144        ssize_t nres = OHOS_TEMP_FAILURE_RETRY(write(resFd, &res, sizeof(res)));
145        if (nres != sizeof(res)) {
146            DFXLOGE("Pid %{public}d Write Res Pipe Failed(%{public}d), nres(%{public}zd)", pid, errno, nres);
147        }
148        close(fd);
149        close(resFd);
150    }
151}
152
153bool DfxSigDumpHandler::Init()
154{
155    if (IsThreadRunning()) {
156        DFXLOGI("SigDumpHandler Thread has been inited");
157        return true;
158    }
159    remove_all_special_handler(SIGDUMP);
160    struct sigaction action;
161    (void)memset_s(&action, sizeof(action), 0, sizeof(action));
162    sigemptyset(&action.sa_mask);
163    sigaddset(&action.sa_mask, SIGDUMP);
164    action.sa_flags = SA_RESTART | SA_SIGINFO;
165    action.sa_sigaction = DfxSigDumpHandler::SignalDumpRetranHandler;
166    DFXLOGI("Init Install signal handler");
167    sigaction(SIGDUMP, &action, nullptr);
168    isThreadRunning_ = true;
169    std::thread catchThread = std::thread(&DfxSigDumpHandler::RunThread);
170    catchThread.detach();
171    return true;
172}
173
174void DfxSigDumpHandler::Deinit()
175{
176    isThreadRunning_ = false;
177}
178} // namespace HiviewDFX
179} // namespace OHOS
180
181bool InitSigDumpHandler()
182{
183    return OHOS::HiviewDFX::DfxSigDumpHandler::GetInstance().Init();
184}
185
186void DeinitSigDumpHandler()
187{
188    OHOS::HiviewDFX::DfxSigDumpHandler::GetInstance().Deinit();
189}
190