mem_checker.cc (10703:41413f830836) mem_checker.cc (11793:ef606668d247)
1/*
2 * Copyright (c) 2014 ARM Limited
3 * All rights reserved
4 *
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder. You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions are
16 * met: redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer;
18 * redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution;
21 * neither the name of the copyright holders nor the names of its
22 * contributors may be used to endorse or promote products derived from
23 * this software without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 *
37 * Authors: Rune Holm
38 * Marco Elver
39 */
40
1/*
2 * Copyright (c) 2014 ARM Limited
3 * All rights reserved
4 *
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder. You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions are
16 * met: redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer;
18 * redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution;
21 * neither the name of the copyright holders nor the names of its
22 * contributors may be used to endorse or promote products derived from
23 * this software without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 *
37 * Authors: Rune Holm
38 * Marco Elver
39 */
40
41#include <cassert>
42
43#include "mem/mem_checker.hh"
44
41#include "mem/mem_checker.hh"
42
43#include <cassert>
44
45void
46MemChecker::WriteCluster::startWrite(MemChecker::Serial serial, Tick _start,
47 uint8_t data)
48{
49 assert(!isComplete());
50
51 if (start == TICK_FUTURE) {
52 // Initialize a fresh write cluster
53 start = _start;
54 }
55 chatty_assert(start <= _start, "WriteClusters must filled in order!");
56
57 ++numIncomplete;
58
59 if (complete != TICK_FUTURE) {
60 // Reopen a closed write cluster
61 assert(_start < complete); // should open a new write cluster, instead;
62 // also somewhat fishy wrt causality / ordering of calls vs time
63 // progression TODO: Check me!
64 complete = TICK_FUTURE;
65 }
66
67 // Create new transaction, and denote completion time to be in the future.
68 writes.insert(std::make_pair(serial,
69 MemChecker::Transaction(serial, _start, TICK_FUTURE, data)));
70}
71
72void
73MemChecker::WriteCluster::completeWrite(MemChecker::Serial serial, Tick _complete)
74{
75 auto it = writes.find(serial);
76
77 if (it == writes.end()) {
78 warn("Could not locate write transaction: serial = %d, complete = %d\n",
79 serial, _complete);
80 return;
81 }
82
83 // Record completion time of the write
84 assert(it->second.complete == TICK_FUTURE);
85 it->second.complete = _complete;
86
87 // Update max completion time for the cluster
88 if (completeMax < _complete) {
89 completeMax = _complete;
90 }
91
92 if (--numIncomplete == 0) {
93 // All writes have completed, this cluster is now complete and will be
94 // assigned the max of completion tick values among all writes.
95 //
96 // Note that we cannot simply keep updating complete, because that would
97 // count the cluster as closed already. Instead, we keep TICK_FUTURE
98 // until all writes have completed.
99 complete = completeMax;
100 }
101}
102
103void
104MemChecker::WriteCluster::abortWrite(MemChecker::Serial serial)
105{
106 if (!writes.erase(serial)) {
107 warn("Could not locate write transaction: serial = %d\n", serial);
108 return;
109 }
110
111 if (--numIncomplete == 0 && !writes.empty()) {
112 // This write cluster is now complete, and we can assign the current
113 // completeMax value.
114 complete = completeMax;
115 }
116
117 // Note: this WriteCluster is in pristine state if this was the only
118 // write present; the cluster will get reused through
119 // getIncompleteWriteCluster().
120}
121
122void
123MemChecker::ByteTracker::startRead(MemChecker::Serial serial, Tick start)
124{
125 outstandingReads.insert(std::make_pair(serial,
126 MemChecker::Transaction(serial, start, TICK_FUTURE)));
127}
128
129bool
130MemChecker::ByteTracker::inExpectedData(Tick start, Tick complete, uint8_t data)
131{
132 _lastExpectedData.clear();
133
134 bool wc_overlap = true;
135
136 // Find the last value read from the location
137 const Transaction& last_obs =
138 *lastCompletedTransaction(&readObservations, start);
139 bool last_obs_valid = (last_obs.complete != TICK_INITIAL);
140
141 // Scan backwards through the write clusters to find the closest younger
142 // preceding & overlapping writes.
143 for (auto cluster = writeClusters.rbegin();
144 cluster != writeClusters.rend() && wc_overlap; ++cluster) {
145 for (const auto& addr_write : cluster->writes) {
146 const Transaction& write = addr_write.second;
147
148 if (write.complete < last_obs.start) {
149 // If this write transaction completed before the last
150 // observation, we ignore it as the last_observation has the
151 // correct value
152 continue;
153 }
154
155 if (write.data == data) {
156 // Found a match, end search.
157 return true;
158 }
159
160 // Record possible, but non-matching data for debugging
161 _lastExpectedData.push_back(write.data);
162
163 if (write.complete > start) {
164 // This write overlapped with the transaction we want to check
165 // -> continue checking the overlapping write cluster
166 continue;
167 }
168
169 // This write cluster has writes that have completed before the
170 // checked transaction. There is no need to check an earlier
171 // write-cluster -> set the exit condition for the outer loop
172 wc_overlap = false;
173
174 if (last_obs.complete < write.start) {
175 // We found a write which started after the last observed read,
176 // therefore we can not longer consider the value seen by the
177 // last observation as a valid expected value.
178 //
179 // Once all writes have been iterated through, we can check if
180 // the last observation is still valid to compare against.
181 last_obs_valid = false;
182 }
183 }
184 }
185
186 // We have not found any matching write, so far; check other sources of
187 // confirmation
188 if (last_obs_valid) {
189 // The last observation is not outdated according to the writes we have
190 // seen so far.
191 assert(last_obs.complete <= start);
192 if (last_obs.data == data) {
193 // Matched data from last observation -> all good
194 return true;
195 }
196 // Record non-matching, but possible value
197 _lastExpectedData.push_back(last_obs.data);
198 } else {
199 // We have not seen any valid observation, and the only writes
200 // observed are overlapping, so anything (in particular the
201 // initialisation value) goes
202 // NOTE: We can overlap with multiple write clusters, here
203 if (!writeClusters.empty() && wc_overlap) {
204 // ensure that all write clusters really overlap this read
205 assert(writeClusters.begin()->start < complete &&
206 writeClusters.rbegin()->complete > start);
207 return true;
208 }
209 }
210
211 if (_lastExpectedData.empty()) {
212 assert(last_obs.complete == TICK_INITIAL);
213 // We have not found any possible (non-matching data). Can happen in
214 // initial system state
215 DPRINTF(MemChecker, "no last observation nor write! start = %d, "\
216 "complete = %d, data = %#x\n", start, complete, data);
217 return true;
218 }
219 return false;
220}
221
222bool
223MemChecker::ByteTracker::completeRead(MemChecker::Serial serial,
224 Tick complete, uint8_t data)
225{
226 auto it = outstandingReads.find(serial);
227
228 if (it == outstandingReads.end()) {
229 // Can happen if concurrent with reset_address_range
230 warn("Could not locate read transaction: serial = %d, complete = %d\n",
231 serial, complete);
232 return true;
233 }
234
235 Tick start = it->second.start;
236 outstandingReads.erase(it);
237
238 // Verify data
239 const bool result = inExpectedData(start, complete, data);
240
241 readObservations.emplace_back(serial, start, complete, data);
242 pruneTransactions();
243
244 return result;
245}
246
247MemChecker::WriteCluster*
248MemChecker::ByteTracker::getIncompleteWriteCluster()
249{
250 if (writeClusters.empty() || writeClusters.back().isComplete()) {
251 writeClusters.emplace_back();
252 }
253
254 return &writeClusters.back();
255}
256
257void
258MemChecker::ByteTracker::startWrite(MemChecker::Serial serial, Tick start,
259 uint8_t data)
260{
261 getIncompleteWriteCluster()->startWrite(serial, start, data);
262}
263
264void
265MemChecker::ByteTracker::completeWrite(MemChecker::Serial serial, Tick complete)
266{
267 getIncompleteWriteCluster()->completeWrite(serial, complete);
268 pruneTransactions();
269}
270
271void
272MemChecker::ByteTracker::abortWrite(MemChecker::Serial serial)
273{
274 getIncompleteWriteCluster()->abortWrite(serial);
275}
276
277void
278MemChecker::ByteTracker::pruneTransactions()
279{
280 // Obtain tick of first outstanding read. If there are no outstanding
281 // reads, we use curTick(), i.e. we will remove all readObservation except
282 // the most recent one.
283 const Tick before = outstandingReads.empty() ? curTick() :
284 outstandingReads.begin()->second.start;
285
286 // Pruning of readObservations
287 readObservations.erase(readObservations.begin(),
288 lastCompletedTransaction(&readObservations, before));
289
290 // Pruning of writeClusters
291 if (!writeClusters.empty()) {
292 writeClusters.erase(writeClusters.begin(),
293 lastCompletedTransaction(&writeClusters, before));
294 }
295}
296
297bool
298MemChecker::completeRead(MemChecker::Serial serial, Tick complete,
299 Addr addr, size_t size, uint8_t *data)
300{
301 bool result = true;
302
303 DPRINTF(MemChecker,
304 "completing read: serial = %d, complete = %d, "
305 "addr = %#llx, size = %d\n", serial, complete, addr, size);
306
307 for (size_t i = 0; i < size; ++i) {
308 ByteTracker *tracker = getByteTracker(addr + i);
309
310 if (!tracker->completeRead(serial, complete, data[i])) {
311 // Generate error message, and aggregate all failures for the bytes
312 // considered in this transaction in one message.
313 if (result) {
314 result = false;
315 errorMessage = "";
316 } else {
317 errorMessage += "\n";
318 }
319
320 errorMessage += csprintf(" Read transaction for address %#llx "
321 "failed: received %#x, expected ",
322 (unsigned long long)(addr + i), data[i]);
323
324 for (size_t j = 0; j < tracker->lastExpectedData().size(); ++j) {
325 errorMessage +=
326 csprintf("%#x%s",
327 tracker->lastExpectedData()[j],
328 (j == tracker->lastExpectedData().size() - 1)
329 ? "" : "|");
330 }
331 }
332 }
333
334 if (!result) {
335 DPRINTF(MemChecker, "read of %#llx @ cycle %d failed:\n%s\n", addr,
336 complete, errorMessage);
337 }
338
339 return result;
340}
341
342void
343MemChecker::reset(Addr addr, size_t size)
344{
345 for (size_t i = 0; i < size; ++i) {
346 byte_trackers.erase(addr + i);
347 }
348}
349
350MemChecker*
351MemCheckerParams::create()
352{
353 return new MemChecker(this);
354}
45void
46MemChecker::WriteCluster::startWrite(MemChecker::Serial serial, Tick _start,
47 uint8_t data)
48{
49 assert(!isComplete());
50
51 if (start == TICK_FUTURE) {
52 // Initialize a fresh write cluster
53 start = _start;
54 }
55 chatty_assert(start <= _start, "WriteClusters must filled in order!");
56
57 ++numIncomplete;
58
59 if (complete != TICK_FUTURE) {
60 // Reopen a closed write cluster
61 assert(_start < complete); // should open a new write cluster, instead;
62 // also somewhat fishy wrt causality / ordering of calls vs time
63 // progression TODO: Check me!
64 complete = TICK_FUTURE;
65 }
66
67 // Create new transaction, and denote completion time to be in the future.
68 writes.insert(std::make_pair(serial,
69 MemChecker::Transaction(serial, _start, TICK_FUTURE, data)));
70}
71
72void
73MemChecker::WriteCluster::completeWrite(MemChecker::Serial serial, Tick _complete)
74{
75 auto it = writes.find(serial);
76
77 if (it == writes.end()) {
78 warn("Could not locate write transaction: serial = %d, complete = %d\n",
79 serial, _complete);
80 return;
81 }
82
83 // Record completion time of the write
84 assert(it->second.complete == TICK_FUTURE);
85 it->second.complete = _complete;
86
87 // Update max completion time for the cluster
88 if (completeMax < _complete) {
89 completeMax = _complete;
90 }
91
92 if (--numIncomplete == 0) {
93 // All writes have completed, this cluster is now complete and will be
94 // assigned the max of completion tick values among all writes.
95 //
96 // Note that we cannot simply keep updating complete, because that would
97 // count the cluster as closed already. Instead, we keep TICK_FUTURE
98 // until all writes have completed.
99 complete = completeMax;
100 }
101}
102
103void
104MemChecker::WriteCluster::abortWrite(MemChecker::Serial serial)
105{
106 if (!writes.erase(serial)) {
107 warn("Could not locate write transaction: serial = %d\n", serial);
108 return;
109 }
110
111 if (--numIncomplete == 0 && !writes.empty()) {
112 // This write cluster is now complete, and we can assign the current
113 // completeMax value.
114 complete = completeMax;
115 }
116
117 // Note: this WriteCluster is in pristine state if this was the only
118 // write present; the cluster will get reused through
119 // getIncompleteWriteCluster().
120}
121
122void
123MemChecker::ByteTracker::startRead(MemChecker::Serial serial, Tick start)
124{
125 outstandingReads.insert(std::make_pair(serial,
126 MemChecker::Transaction(serial, start, TICK_FUTURE)));
127}
128
129bool
130MemChecker::ByteTracker::inExpectedData(Tick start, Tick complete, uint8_t data)
131{
132 _lastExpectedData.clear();
133
134 bool wc_overlap = true;
135
136 // Find the last value read from the location
137 const Transaction& last_obs =
138 *lastCompletedTransaction(&readObservations, start);
139 bool last_obs_valid = (last_obs.complete != TICK_INITIAL);
140
141 // Scan backwards through the write clusters to find the closest younger
142 // preceding & overlapping writes.
143 for (auto cluster = writeClusters.rbegin();
144 cluster != writeClusters.rend() && wc_overlap; ++cluster) {
145 for (const auto& addr_write : cluster->writes) {
146 const Transaction& write = addr_write.second;
147
148 if (write.complete < last_obs.start) {
149 // If this write transaction completed before the last
150 // observation, we ignore it as the last_observation has the
151 // correct value
152 continue;
153 }
154
155 if (write.data == data) {
156 // Found a match, end search.
157 return true;
158 }
159
160 // Record possible, but non-matching data for debugging
161 _lastExpectedData.push_back(write.data);
162
163 if (write.complete > start) {
164 // This write overlapped with the transaction we want to check
165 // -> continue checking the overlapping write cluster
166 continue;
167 }
168
169 // This write cluster has writes that have completed before the
170 // checked transaction. There is no need to check an earlier
171 // write-cluster -> set the exit condition for the outer loop
172 wc_overlap = false;
173
174 if (last_obs.complete < write.start) {
175 // We found a write which started after the last observed read,
176 // therefore we can not longer consider the value seen by the
177 // last observation as a valid expected value.
178 //
179 // Once all writes have been iterated through, we can check if
180 // the last observation is still valid to compare against.
181 last_obs_valid = false;
182 }
183 }
184 }
185
186 // We have not found any matching write, so far; check other sources of
187 // confirmation
188 if (last_obs_valid) {
189 // The last observation is not outdated according to the writes we have
190 // seen so far.
191 assert(last_obs.complete <= start);
192 if (last_obs.data == data) {
193 // Matched data from last observation -> all good
194 return true;
195 }
196 // Record non-matching, but possible value
197 _lastExpectedData.push_back(last_obs.data);
198 } else {
199 // We have not seen any valid observation, and the only writes
200 // observed are overlapping, so anything (in particular the
201 // initialisation value) goes
202 // NOTE: We can overlap with multiple write clusters, here
203 if (!writeClusters.empty() && wc_overlap) {
204 // ensure that all write clusters really overlap this read
205 assert(writeClusters.begin()->start < complete &&
206 writeClusters.rbegin()->complete > start);
207 return true;
208 }
209 }
210
211 if (_lastExpectedData.empty()) {
212 assert(last_obs.complete == TICK_INITIAL);
213 // We have not found any possible (non-matching data). Can happen in
214 // initial system state
215 DPRINTF(MemChecker, "no last observation nor write! start = %d, "\
216 "complete = %d, data = %#x\n", start, complete, data);
217 return true;
218 }
219 return false;
220}
221
222bool
223MemChecker::ByteTracker::completeRead(MemChecker::Serial serial,
224 Tick complete, uint8_t data)
225{
226 auto it = outstandingReads.find(serial);
227
228 if (it == outstandingReads.end()) {
229 // Can happen if concurrent with reset_address_range
230 warn("Could not locate read transaction: serial = %d, complete = %d\n",
231 serial, complete);
232 return true;
233 }
234
235 Tick start = it->second.start;
236 outstandingReads.erase(it);
237
238 // Verify data
239 const bool result = inExpectedData(start, complete, data);
240
241 readObservations.emplace_back(serial, start, complete, data);
242 pruneTransactions();
243
244 return result;
245}
246
247MemChecker::WriteCluster*
248MemChecker::ByteTracker::getIncompleteWriteCluster()
249{
250 if (writeClusters.empty() || writeClusters.back().isComplete()) {
251 writeClusters.emplace_back();
252 }
253
254 return &writeClusters.back();
255}
256
257void
258MemChecker::ByteTracker::startWrite(MemChecker::Serial serial, Tick start,
259 uint8_t data)
260{
261 getIncompleteWriteCluster()->startWrite(serial, start, data);
262}
263
264void
265MemChecker::ByteTracker::completeWrite(MemChecker::Serial serial, Tick complete)
266{
267 getIncompleteWriteCluster()->completeWrite(serial, complete);
268 pruneTransactions();
269}
270
271void
272MemChecker::ByteTracker::abortWrite(MemChecker::Serial serial)
273{
274 getIncompleteWriteCluster()->abortWrite(serial);
275}
276
277void
278MemChecker::ByteTracker::pruneTransactions()
279{
280 // Obtain tick of first outstanding read. If there are no outstanding
281 // reads, we use curTick(), i.e. we will remove all readObservation except
282 // the most recent one.
283 const Tick before = outstandingReads.empty() ? curTick() :
284 outstandingReads.begin()->second.start;
285
286 // Pruning of readObservations
287 readObservations.erase(readObservations.begin(),
288 lastCompletedTransaction(&readObservations, before));
289
290 // Pruning of writeClusters
291 if (!writeClusters.empty()) {
292 writeClusters.erase(writeClusters.begin(),
293 lastCompletedTransaction(&writeClusters, before));
294 }
295}
296
297bool
298MemChecker::completeRead(MemChecker::Serial serial, Tick complete,
299 Addr addr, size_t size, uint8_t *data)
300{
301 bool result = true;
302
303 DPRINTF(MemChecker,
304 "completing read: serial = %d, complete = %d, "
305 "addr = %#llx, size = %d\n", serial, complete, addr, size);
306
307 for (size_t i = 0; i < size; ++i) {
308 ByteTracker *tracker = getByteTracker(addr + i);
309
310 if (!tracker->completeRead(serial, complete, data[i])) {
311 // Generate error message, and aggregate all failures for the bytes
312 // considered in this transaction in one message.
313 if (result) {
314 result = false;
315 errorMessage = "";
316 } else {
317 errorMessage += "\n";
318 }
319
320 errorMessage += csprintf(" Read transaction for address %#llx "
321 "failed: received %#x, expected ",
322 (unsigned long long)(addr + i), data[i]);
323
324 for (size_t j = 0; j < tracker->lastExpectedData().size(); ++j) {
325 errorMessage +=
326 csprintf("%#x%s",
327 tracker->lastExpectedData()[j],
328 (j == tracker->lastExpectedData().size() - 1)
329 ? "" : "|");
330 }
331 }
332 }
333
334 if (!result) {
335 DPRINTF(MemChecker, "read of %#llx @ cycle %d failed:\n%s\n", addr,
336 complete, errorMessage);
337 }
338
339 return result;
340}
341
342void
343MemChecker::reset(Addr addr, size_t size)
344{
345 for (size_t i = 0; i < size; ++i) {
346 byte_trackers.erase(addr + i);
347 }
348}
349
350MemChecker*
351MemCheckerParams::create()
352{
353 return new MemChecker(this);
354}