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
addIngress(FenceTimePtr fence,std::string annotation)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
addAccessCompletion(FenceTimePtr fence,std::string annotation)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
addEgress(FenceTimePtr fence,std::string annotation)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
resolveLocked()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
updateSignalTimes(bool excludeIngress)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
checkUnsafeAccess() const103 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