Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qohoswatchdog.cpp
Go to the documentation of this file.
1// Copyright (C) 2025 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qohosutils.h"
6#include <hicollie/hicollie.h>
7#include <qarkui/qarkuiutils.h>
8#include <qohosbigdataeventlogging.h>
9#include <QtCore/private/qohoslogger_p.h>
10#include <QtCore/qcoreapplication.h>
11#include <QtCore/qcoreevent.h>
12#include <QtCore/qobject.h>
13#include <functional>
14#include <mutex>
15#include <optional>
16
17namespace ch = std::chrono;
18
19QT_BEGIN_NAMESPACE
20
21namespace QtOhosWatchdog {
22
23namespace {
24
25constexpr int resetRatio = 2;
26
27constexpr auto bigDataEnterFreezeStateEventName = "enterFreezeState";
28constexpr auto bigDataRecoveredFromFreezeStateEventName = "recoveredFromFreezeState";
29
30constexpr auto bigDataEventDescriptionPropertyName = "eventDescription";
31constexpr auto bigDataFreezeStateDurationPropertyName = "freezeStateDurationMs";
32
33#ifdef SUPPORT_ASAN
34constexpr auto checkIntervalTime = ch::seconds(45);
35#else
36constexpr auto checkIntervalTime = ch::seconds(3);
37#endif
38
39void logEnterFreezeStateBigDataEvent(ch::time_point<ch::system_clock> eventTime)
40{
41 qCDebug(QtForOhos, "%s", Q_FUNC_INFO);
42
43 auto eventBuilder = QtOhos::makeBigEventLoggingEventBuilder(
44 bigDataEnterFreezeStateEventName, ::EventType::STATISTIC, eventTime);
45 eventBuilder->addParam(bigDataEventDescriptionPropertyName, "The application triggered a 6 second freeze");
46 eventBuilder->buildEvent()->trySend();
47}
48
49void logRecoveredFromFreezeStateBigDataEvent(
50 ch::time_point<ch::system_clock> eventTime, ch::system_clock::duration freezeStateDuration)
51{
52 qCDebug(QtForOhos, "%s", Q_FUNC_INFO);
53
54 auto eventBuilder = QtOhos::makeBigEventLoggingEventBuilder(
55 bigDataRecoveredFromFreezeStateEventName, ::EventType::STATISTIC, eventTime);
56 eventBuilder->addParam(bigDataEventDescriptionPropertyName, "Recovered from the frozen state");
57 eventBuilder->addParam(
58 bigDataFreezeStateDurationPropertyName,
59 static_cast<std::int64_t>(ch::duration_cast<ch::milliseconds>(freezeStateDuration).count()));
60 eventBuilder->buildEvent()->trySend();
61}
62
63class QtWatchdog : public QObject
64{
65public:
66 QtWatchdog();
67
68 void runHiCollieStuckDetectionTask();
69
70 bool event(QEvent *ev) override;
71
72private:
73 void handleAppMainThreadAliveNotification();
74 void reportStuckEvent();
75
76 ch::steady_clock::time_point m_lastWatchTime;
77 bool m_appMainThreadIsAlive = false;
78 bool m_isSixSecondEvent = false;
79 std::optional<ch::system_clock::time_point> m_sixSecondEventDetectionTime;
80};
81
82QEvent::Type getCheckMainThreadIsAliveQEventType()
83{
84 static auto eventType = static_cast<QEvent::Type>(QEvent::registerEventType());
85 return eventType;
86}
87
88QtWatchdog::QtWatchdog()
89 : QObject()
90{
91 QCoreApplication::postEvent(this, new QEvent(getCheckMainThreadIsAliveQEventType()), Qt::HighEventPriority);
92}
93
94void QtWatchdog::handleAppMainThreadAliveNotification()
95{
96 qCDebug(QtForOhos, "QtWatchdog::handleAppMainThreadAliveNotification");
97 m_appMainThreadIsAlive = true;
98 m_isSixSecondEvent = false;
99 if (m_sixSecondEventDetectionTime.has_value()) {
100 const auto now = ch::system_clock::now();
101 const auto elapsedTime = now - m_sixSecondEventDetectionTime.value();
102 logRecoveredFromFreezeStateBigDataEvent(now, elapsedTime);
103 m_sixSecondEventDetectionTime.reset();
104 }
105}
106
107void QtWatchdog::runHiCollieStuckDetectionTask()
108{
109 qCDebug(QtForOhos, "QtWatchdog::runHiCollieStuckDetectionTask start");
110
111 if (m_appMainThreadIsAlive) {
112 qCDebug(QtForOhos, "QtWatchdog: m_appMainThreadIsAlive store false");
113 m_appMainThreadIsAlive = false;
114 } else {
115 qCWarning(QtForOhos, "QtWatchdog: AppMainThread is not alive");
116 reportStuckEvent();
117 }
118
119 QCoreApplication::postEvent(this, new QEvent(getCheckMainThreadIsAliveQEventType()), Qt::HighEventPriority);
120
121 auto now = ch::steady_clock::now();
122 if ((now - m_lastWatchTime) >= (checkIntervalTime / resetRatio))
123 m_lastWatchTime = now;
124
125 qCDebug(QtForOhos, "QtWatchdog::runHiCollieStuckDetectionTask end");
126}
127
128bool QtWatchdog::event(QEvent *ev)
129{
130 if (ev->type() == getCheckMainThreadIsAliveQEventType()) {
131 qCDebug(QtForOhos, "QtWatchdog: CheckMainThreadIsAlive event start (%d)", static_cast<int>(getCheckMainThreadIsAliveQEventType()));
132 handleAppMainThreadAliveNotification();
133 qCDebug(QtForOhos, "QCoreApplication: CheckMainThreadIsAlive event end");
134 return true;
135 } else {
136 return QObject::event(ev);
137 }
138}
139
140void QtWatchdog::reportStuckEvent()
141{
142 auto now = ch::steady_clock::now();
143 auto timeDelta = now - m_lastWatchTime;
144 if (timeDelta > resetRatio * checkIntervalTime || timeDelta < checkIntervalTime / resetRatio) {
145 qCWarning(
146 QtForOhos,
147 "QtWatchdog: Thread may be blocked, do not report this time. currTime: %.3f, lastTime: %.3f",
148 ch::duration<double>(now.time_since_epoch()).count(),
149 ch::duration<double>(m_lastWatchTime.time_since_epoch()).count());
150 return;
151 }
152
153 qCDebug(QtForOhos, "QtWatchdog: calling OH_HiCollie_Report(), m_isSixSecondEvent = %d", m_isSixSecondEvent);
154 HiCollie_ErrorCode reportRes = OH_HiCollie_Report(&m_isSixSecondEvent);
155 if (reportRes == HICOLLIE_SUCCESS) {
156 qCDebug(QtForOhos, "QtWatchdog: after OH_HiCollie_Report(), m_isSixSecondEvent = %d", m_isSixSecondEvent);
157 if (m_isSixSecondEvent && !m_sixSecondEventDetectionTime.has_value()) {
158 m_sixSecondEventDetectionTime = ch::system_clock::now();
159 logEnterFreezeStateBigDataEvent(m_sixSecondEventDetectionTime.value());
160 }
161 } else {
162 qCWarning(QtForOhos, "QtWatchdog: OH_HiCollie_Report() failed with code %d", static_cast<int>(reportRes));
163 }
164}
165
166void runWithQtWatchdogSharedPtr(const std::function<void(std::shared_ptr<QtWatchdog> &)> &runFunc)
167{
168 static std::mutex runMutex;
169 static std::shared_ptr<QtWatchdog> qtWatchdogPtr;
170
171 {
172 std::lock_guard<std::mutex> runLock(runMutex);
173 runFunc(qtWatchdogPtr);
174 }
175}
176
177}
178
179std::shared_ptr<void> makeWatchdog()
180{
181 runWithQtWatchdogSharedPtr(
182 [](auto &watchdogSharedPtr) {
183 watchdogSharedPtr = std::make_shared<QtWatchdog>();
184 });
185
186 auto watchdogHandle = QtOhos::makeDestroyNotifier(
187 []() {
188 runWithQtWatchdogSharedPtr(
189 [](auto &watchdogSharedPtr) {
190 watchdogSharedPtr.reset();
191 });
192 });
193
194 HiCollie_ErrorCode stuckDetectionInitRes = OH_HiCollie_Init_StuckDetection(
195 []() {
196 runWithQtWatchdogSharedPtr(
197 [](auto &watchdogSharedPtr) {
198 if (watchdogSharedPtr)
199 watchdogSharedPtr->runHiCollieStuckDetectionTask();
200 });
201 });
202
203 std::shared_ptr<void> result;
204 if (stuckDetectionInitRes == HICOLLIE_SUCCESS) {
205 result = watchdogHandle;
206 } else {
207 qCWarning(
208 QtForOhos,
209 "OH_HiCollie_Init_StuckDetection() failed with code %d, disabling QtWatchdog",
210 static_cast<int>(stuckDetectionInitRes));
211 result = nullptr;
212 }
213
214 return result;
215}
216
217}
218
219QT_END_NAMESPACE
std::shared_ptr< void > makeWatchdog()