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 <chrono>
19 #include <iomanip>
20 #include <sstream>
21 #ifdef HICOLLIE_ENABLE
22 #include "xcollie/xcollie.h"
23 #include "xcollie/xcollie_define.h"
24 #endif
25
26 #include "avcodec_errors.h"
27 #include "avcodec_dump_utils.h"
28 #include "avcodec_log.h"
29 #include "avcodec_sysevent.h"
30
31 namespace {
32 constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, LOG_DOMAIN_FRAMEWORK, "AVCodecXCollie"};
33 constexpr uint32_t DUMP_XCOLLIE_INDEX = 0x01'00'00'00;
34 constexpr uint8_t DUMP_OFFSET_16 = 16;
35 constexpr uint8_t DUMP_OFFSET_8 = 8;
36 constexpr uint64_t COLLIE_INVALID_INDEX = 0;
37 }
38
39 namespace OHOS {
40 namespace MediaAVCodec {
GetTimeString(std::time_t time)41 static std::string GetTimeString(std::time_t time)
42 {
43 std::stringstream ss;
44 struct tm timeTm;
45 ss << std::put_time(localtime_r(&time, &timeTm), "%F %T");
46 return ss.str();
47 }
48
GetInstance()49 AVCodecXCollie &AVCodecXCollie::GetInstance()
50 {
51 static AVCodecXCollie instance;
52 return instance;
53 }
54
SetTimer(const std::string & name,bool recovery,uint32_t timeout,std::function<void (void *)> callback)55 int32_t AVCodecXCollie::SetTimer(const std::string &name, bool recovery, uint32_t timeout,
56 std::function<void(void *)> callback)
57 {
58 std::lock_guard<std::shared_mutex> lock(mutex_);
59
60 unsigned int flag = HiviewDFX::XCOLLIE_FLAG_LOG | HiviewDFX::XCOLLIE_FLAG_NOOP;
61 flag |= (recovery ? HiviewDFX::XCOLLIE_FLAG_RECOVERY : 0);
62
63 auto timerInfo = std::make_shared<TimerInfo>(
64 name, std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()), timeout);
65 auto id = HiviewDFX::XCollie::GetInstance().SetTimer(
66 name.data(), timeout, callback, reinterpret_cast<void *>(timerInfo.get()), flag);
67 if (id != HiviewDFX::INVALID_ID) {
68 dfxDumper_.emplace(id, timerInfo);
69 }
70 return id;
71 }
72
SetInterfaceTimer(const std::string & name,bool isService,bool recovery,uint32_t timeout)73 int32_t AVCodecXCollie::SetInterfaceTimer(const std::string &name, bool isService, bool recovery, uint32_t timeout)
74 {
75 #ifdef HICOLLIE_ENABLE
76 std::function<void (void *)> func = isService ?
77 [](void *data) { AVCodecXCollie::ServiceInterfaceTimerCallback(data); } :
78 [](void *data) { AVCodecXCollie::ClientInterfaceTimerCallback(data); };
79
80 return SetTimer(name, recovery, timeout, func);
81 #else
82 return COLLIE_INVALID_INDEX;
83 #endif
84 }
85
CancelTimer(int32_t timerId)86 void AVCodecXCollie::CancelTimer([[maybe_unused]]int32_t timerId)
87 {
88 #ifdef HICOLLIE_ENABLE
89 if (timerId == COLLIE_INVALID_INDEX) {
90 return;
91 }
92 std::lock_guard<std::shared_mutex> lock(mutex_);
93 HiviewDFX::XCollie::GetInstance().CancelTimer(timerId);
94
95 auto it = dfxDumper_.find(timerId);
96 if (it == dfxDumper_.end()) {
97 return;
98 }
99 dfxDumper_.erase(it);
100 #endif
101 }
102
Dump(int32_t fd)103 int32_t AVCodecXCollie::Dump(int32_t fd)
104 {
105 using namespace std::string_literals;
106 std::shared_lock<std::shared_mutex> lock(mutex_);
107 if (dfxDumper_.empty()) {
108 return AVCS_ERR_OK;
109 }
110
111 std::string dumpString = "[AVCodec_XCollie]\n";
112 AVCodecDumpControler dumpControler;
113 uint32_t dumperIndex = 1;
114 for (const auto &iter : dfxDumper_) {
115 uint32_t timeInfoIndex = 1;
116 auto titleIndex = DUMP_XCOLLIE_INDEX + (dumperIndex << DUMP_OFFSET_16);
117 dumpControler.AddInfo(titleIndex, "Timer_"s + std::to_string(dumperIndex));
118 dumpControler.AddInfo(titleIndex + (timeInfoIndex++ << DUMP_OFFSET_8), "TimerName", iter.second->name);
119 dumpControler.AddInfo(titleIndex + (timeInfoIndex++ << DUMP_OFFSET_8),
120 "StartTime", GetTimeString(iter.second->startTime).c_str());
121 dumpControler.AddInfo(titleIndex + (timeInfoIndex++ << DUMP_OFFSET_8),
122 "TimeLeft",
123 std::to_string(iter.second->timeout + iter.second->startTime -
124 std::chrono::system_clock::to_time_t(std::chrono::system_clock::now())));
125 dumperIndex++;
126 }
127
128 dumpControler.GetDumpString(dumpString);
129 if (fd != -1) {
130 write(fd, dumpString.c_str(), dumpString.size());
131 dumpString.clear();
132 }
133 return AVCS_ERR_OK;
134 }
135
ServiceInterfaceTimerCallback(void * data)136 void AVCodecXCollie::ServiceInterfaceTimerCallback(void *data)
137 {
138 static uint32_t threadDeadlockCount_ = 0;
139 threadDeadlockCount_++;
140 std::string name = data != nullptr ? reinterpret_cast<TimerInfo *>(data)->name.c_str() : "";
141
142 AVCODEC_LOGE("Service task %{public}s timeout", name.c_str());
143 FaultEventWrite(FaultType::FAULT_TYPE_FREEZE, std::string("Service task ") +
144 name + std::string(" timeout"), "Service");
145
146 static constexpr uint32_t threshold = 1; // >= 1 Restart service
147 if (threadDeadlockCount_ >= threshold) {
148 FaultEventWrite(FaultType::FAULT_TYPE_FREEZE,
149 "Process timeout, AVCodec service process exit.", "Service");
150 AVCODEC_LOGF("Process timeout, AVCodec service process exit.");
151 _exit(-1);
152 }
153 }
154
ClientInterfaceTimerCallback(void * data)155 void AVCodecXCollie::ClientInterfaceTimerCallback(void *data)
156 {
157 std::string name = data != nullptr ? reinterpret_cast<TimerInfo *>(data)->name.c_str() : "";
158 AVCODEC_LOGE("Client task %{public}s timeout", name.c_str());
159 FaultEventWrite(FaultType::FAULT_TYPE_FREEZE, std::string("Client task ") +
160 name + std::string(" timeout"), "Client");
161 }
162 } // namespace MediaAVCodec
163 } // namespace OHOS