1/*
2 * Copyright (C) 2023 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 "avcodec_xcollie.h"
17#include <unistd.h>
18#include "avcodec_errors.h"
19#include "param_wrapper.h"
20#include "avcodec_dump_utils.h"
21#include "avcodec_log.h"
22#include "avcodec_sysevent.h"
23#ifdef HICOLLIE_ENABLE
24#include "xcollie/xcollie.h"
25#include "xcollie/xcollie_define.h"
26#endif
27
28namespace {
29    constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, LOG_DOMAIN_FRAMEWORK, "AVCodecXCollie"};
30    constexpr uint32_t DUMP_XCOLLIE_INDEX = 0x01000000;
31    constexpr uint8_t DUMP_OFFSET_16 = 16;
32    constexpr uint64_t COLLIE_INVALID_INDEX = 0;
33}
34
35namespace OHOS {
36namespace MediaAVCodec {
37AVCodecXCollie &AVCodecXCollie::GetInstance()
38{
39    static AVCodecXCollie instance;
40    return instance;
41}
42
43void AVCodecXCollie::ServiceTimerCallback(void *data)
44{
45    std::lock_guard<std::mutex> lock(mutex_);
46    threadDeadlockCount_++;
47    std::string name = data != nullptr ? (char *)data : "";
48
49    AVCODEC_LOGE("Service task %{public}s timeout", name.c_str());
50    FaultEventWrite(FaultType::FAULT_TYPE_FREEZE, std::string("Service task ") +
51        name + std::string(" timeout"), "Service");
52
53    static constexpr uint32_t threshold = 1; // >= 1 Restart service
54    if (threadDeadlockCount_ >= threshold) {
55        FaultEventWrite(FaultType::FAULT_TYPE_FREEZE,
56            "Process timeout, AVCodec service process exit.", "Service");
57        AVCODEC_LOGF("Process timeout, AVCodec service process exit.");
58        _exit(-1);
59    }
60}
61
62void AVCodecXCollie::ClientTimerCallback(void *data)
63{
64    std::lock_guard<std::mutex> lock(mutex_);
65    std::string name = data != nullptr ? (char *)data : "";
66    AVCODEC_LOGE("Client task %{public}s timeout", name.c_str());
67    FaultEventWrite(FaultType::FAULT_TYPE_FREEZE, std::string("Client task ") +
68        name + std::string(" timeout"), "Client");
69}
70
71int32_t AVCodecXCollie::Dump(int32_t fd)
72{
73    if (dfxDumper_.empty()) {
74        return AVCS_ERR_OK;
75    }
76
77    std::lock_guard<std::mutex> lock(mutex_);
78    std::string dumpString = "[AVCodec_XCollie]\n";
79    AVCodecDumpControler dumpControler;
80    uint32_t dumperIndex = 1;
81    for (const auto &iter : dfxDumper_) {
82        dumpControler.AddInfo(DUMP_XCOLLIE_INDEX + (dumperIndex << DUMP_OFFSET_16), "Timer_Name", iter.second.second);
83        dumperIndex++;
84    }
85    dumpControler.GetDumpString(dumpString);
86    if (fd != -1) {
87        write(fd, dumpString.c_str(), dumpString.size());
88        dumpString.clear();
89    }
90    return AVCS_ERR_OK;
91}
92
93
94uint64_t AVCodecXCollie::SetTimer(const std::string &name, bool isService, bool recovery, uint32_t timeout)
95{
96#ifdef HICOLLIE_ENABLE
97    std::lock_guard<std::mutex> lock(mutex_);
98
99    std::function<void (void *)> func;
100    if (isService) {
101        func = [this](void *data) { this->ServiceTimerCallback(data); };
102    } else {
103        func = [this](void *data) { this->ClientTimerCallback(data); };
104    }
105
106    unsigned int flag = HiviewDFX::XCOLLIE_FLAG_LOG | HiviewDFX::XCOLLIE_FLAG_NOOP;
107    if (recovery) {
108        flag |= HiviewDFX::XCOLLIE_FLAG_RECOVERY;
109    }
110    uint64_t tempIndex = dumperIndex_++;
111    dfxDumper_.emplace(tempIndex, std::pair<int32_t, std::string>(HiviewDFX::INVALID_ID, name));
112    int32_t id = HiviewDFX::XCollie::GetInstance().SetTimer(name,
113        timeout, func, (void *)dfxDumper_[tempIndex].second.c_str(), flag);
114    if (id == HiviewDFX::INVALID_ID) {
115        auto it = dfxDumper_.find(tempIndex);
116        if (it != dfxDumper_.end()) {
117            dfxDumper_.erase(it);
118            return COLLIE_INVALID_INDEX;
119        }
120    }
121    dfxDumper_[tempIndex].first = id;
122    return tempIndex;
123#else
124    (void)dumperIndex_;
125    return COLLIE_INVALID_INDEX;
126#endif
127}
128
129void AVCodecXCollie::CancelTimer(uint64_t index)
130{
131#ifdef HICOLLIE_ENABLE
132    if (index == COLLIE_INVALID_INDEX) {
133        return;
134    }
135    std::lock_guard<std::mutex> lock(mutex_);
136    auto it = dfxDumper_.find(index);
137    if (it == dfxDumper_.end()) {
138        return;
139    }
140    int32_t id = it->second.first;
141    dfxDumper_.erase(it);
142    return HiviewDFX::XCollie::GetInstance().CancelTimer(id);
143#else
144    (void)index;
145    return;
146#endif
147}
148} // namespace MediaAVCodec
149} // namespace OHOS