1+ /*
2+ * Copyright 2025 The Android Open Source Project
3+ *
4+ * Licensed under the Apache License, Version 2.0 (the "License");
5+ * you may not use this file except in compliance with the License.
6+ * You may obtain a copy of the License at
7+ *
8+ * http://www.apache.org/licenses/LICENSE-2.0
9+ *
10+ * Unless required by applicable law or agreed to in writing, software
11+ * distributed under the License is distributed on an "AS IS" BASIS,
12+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+ * See the License for the specific language governing permissions and
14+ * limitations under the License.
15+ */
16+
17+ // #define LOG_NDEBUG 0
18+ #undef LOG_TAG
19+ #define LOG_TAG " DependencyMonitor"
20+
21+ #include < ui/DependencyMonitor.h>
22+ #include < ui/Fence.h>
23+ #include < utils/Timers.h>
24+
25+ #include < inttypes.h>
26+
27+ namespace android {
28+
29+ void DependencyMonitor::addIngress (FenceTimePtr fence, std::string annotation) {
30+ std::lock_guard lock (mMutex );
31+ resolveLocked ();
32+ if (mDependencies .isFull () && !mDependencies .front ().updateSignalTimes (true )) {
33+ ALOGD (" %s: Clobbering unresolved dependencies -- make me bigger!" , mToken .c_str ());
34+ }
35+
36+ auto & entry = mDependencies .next ();
37+ entry.reset (mToken .c_str ());
38+ ALOGV (" %" PRId64 " /%s: addIngress at CPU time %" PRId64 " (%s)" , mDependencies .back ().id ,
39+ mToken .c_str (), systemTime (), annotation.c_str ());
40+
41+ mDependencies .back ().ingress = {std::move (fence), std::move (annotation)};
42+ }
43+
44+ void DependencyMonitor::addAccessCompletion (FenceTimePtr fence, std::string annotation) {
45+ std::lock_guard lock (mMutex );
46+ if (mDependencies .size () == 0 ) {
47+ return ;
48+ }
49+ ALOGV (" %" PRId64 " /%s: addAccessCompletion at CPU time %" PRId64 " (%s)" ,
50+ mDependencies .back ().id , mToken .c_str (), systemTime (), annotation.c_str ());
51+ mDependencies .back ().accessCompletions .emplace_back (std::move (fence), std::move (annotation));
52+ }
53+
54+ void DependencyMonitor::addEgress (FenceTimePtr fence, std::string annotation) {
55+ std::lock_guard lock (mMutex );
56+ if (mDependencies .size () == 0 ) {
57+ return ;
58+ }
59+ ALOGV (" %" PRId64 " /%s: addEgress at CPU time %" PRId64 " (%s)" , mDependencies .back ().id ,
60+ mToken .c_str (), systemTime (), annotation.c_str ());
61+ mDependencies .back ().egress = {std::move (fence), std::move (annotation)};
62+ }
63+
64+ void DependencyMonitor::resolveLocked () {
65+ if (mDependencies .size () == 0 ) {
66+ return ;
67+ }
68+
69+ for (size_t i = mDependencies .size (); i > 0 ; i--) {
70+ auto & dependencyBlock = mDependencies [i - 1 ];
71+
72+ if (dependencyBlock.validated ) {
73+ continue ;
74+ }
75+
76+ if (!dependencyBlock.updateSignalTimes (false )) {
77+ break ;
78+ }
79+
80+ dependencyBlock.validated = true ;
81+ dependencyBlock.checkUnsafeAccess ();
82+ }
83+ }
84+
85+ bool DependencyMonitor::DependencyBlock::updateSignalTimes (bool excludeIngress) {
86+ if (egress.fence ->getSignalTime () == Fence::SIGNAL_TIME_PENDING) {
87+ return false ;
88+ }
89+
90+ if (!excludeIngress && ingress.fence ->getSignalTime () == Fence::SIGNAL_TIME_PENDING) {
91+ return false ;
92+ }
93+
94+ for (auto & accessCompletion : accessCompletions) {
95+ if (accessCompletion.fence ->getSignalTime () == Fence::SIGNAL_TIME_PENDING) {
96+ return false ;
97+ }
98+ }
99+
100+ return true ;
101+ }
102+
103+ void DependencyMonitor::DependencyBlock::checkUnsafeAccess () const {
104+ const nsecs_t egressTime = egress.fence ->getCachedSignalTime ();
105+ const nsecs_t ingressTime = ingress.fence ->getCachedSignalTime ();
106+
107+ ALOGV_IF (egressTime != Fence::SIGNAL_TIME_INVALID,
108+ " %" PRId64 " /%s: Egress time: %" PRId64 " (%s)" , token, id, egressTime,
109+ egress.annotation .c_str ());
110+ ALOGV_IF (Fence::isValidTimestamp (egressTime) && Fence::isValidTimestamp (ingressTime) &&
111+ egressTime < ingressTime,
112+ " %" PRId64 " /%s: Detected egress before ingress!: %" PRId64 " (%s) < %" PRId64 " (%s)" ,
113+ id, token, egressTime, egress.annotation , ingressTime, ingress.annotation .c_str ());
114+
115+ for (auto & accessCompletion : accessCompletions) {
116+ const nsecs_t accessCompletionTime = accessCompletion.fence ->getCachedSignalTime ();
117+ if (!Fence::isValidTimestamp (accessCompletionTime)) {
118+ ALOGI (" %" PRId64 " /%s: Detected invalid access completion! <%s>" , id, token,
119+ accessCompletion.annotation .c_str ());
120+ continue ;
121+ } else {
122+ ALOGV (" %" PRId64 " /%s: Access completion time: %" PRId64 " <%s>" , id, token,
123+ accessCompletionTime, accessCompletion.annotation .c_str ());
124+ }
125+
126+ ALOGI_IF (Fence::isValidTimestamp (egressTime) && accessCompletionTime > egressTime,
127+ " %" PRId64 " /%s: Detected access completion after egress!: %" PRId64
128+ " (%s) > %" PRId64 " (%s)" ,
129+ id, token, accessCompletionTime, accessCompletion.annotation .c_str (), egressTime,
130+ egress.annotation .c_str ());
131+
132+ ALOGI_IF (Fence::isValidTimestamp (ingressTime) && accessCompletionTime < ingressTime,
133+ " %" PRId64 " /%s: Detected access completion prior to ingress!: %" PRId64
134+ " (%s) < %" PRId64 " (%s)" ,
135+ id, token, accessCompletionTime, accessCompletion.annotation .c_str (), ingressTime,
136+ ingress.annotation .c_str ());
137+ }
138+
139+ ALOGV_IF (ingressTime != Fence::SIGNAL_TIME_INVALID,
140+ " %" PRId64 " /%s: Ingress time: %" PRId64 " (%s)" , id, token, ingressTime,
141+ ingress.annotation .c_str ());
142+ }
143+
144+ } // namespace android
0 commit comments