mem_checker.cc revision 10612:6332c9d471a8
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 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 } 199 200 if (_lastExpectedData.empty()) { 201 assert(last_obs.complete == TICK_INITIAL); 202 // We have not found any possible (non-matching data). Can happen in 203 // initial system state 204 DPRINTF(MemChecker, "no last observation nor write! start = %d, "\ 205 "complete = %d, data = %#x\n", start, complete, data); 206 return true; 207 } 208 return false; 209} 210 211bool 212MemChecker::ByteTracker::completeRead(MemChecker::Serial serial, 213 Tick complete, uint8_t data) 214{ 215 auto it = outstandingReads.find(serial); 216 217 if (it == outstandingReads.end()) { 218 // Can happen if concurrent with reset_address_range 219 warn("Could not locate read transaction: serial = %d, complete = %d\n", 220 serial, complete); 221 return true; 222 } 223 224 Tick start = it->second.start; 225 outstandingReads.erase(it); 226 227 // Verify data 228 const bool result = inExpectedData(start, complete, data); 229 230 readObservations.emplace_back(serial, start, complete, data); 231 pruneTransactions(); 232 233 return result; 234} 235 236MemChecker::WriteCluster* 237MemChecker::ByteTracker::getIncompleteWriteCluster() 238{ 239 if (writeClusters.empty() || writeClusters.back().isComplete()) { 240 writeClusters.emplace_back(); 241 } 242 243 return &writeClusters.back(); 244} 245 246void 247MemChecker::ByteTracker::startWrite(MemChecker::Serial serial, Tick start, 248 uint8_t data) 249{ 250 getIncompleteWriteCluster()->startWrite(serial, start, data); 251} 252 253void 254MemChecker::ByteTracker::completeWrite(MemChecker::Serial serial, Tick complete) 255{ 256 getIncompleteWriteCluster()->completeWrite(serial, complete); 257 pruneTransactions(); 258} 259 260void 261MemChecker::ByteTracker::abortWrite(MemChecker::Serial serial) 262{ 263 getIncompleteWriteCluster()->abortWrite(serial); 264} 265 266void 267MemChecker::ByteTracker::pruneTransactions() 268{ 269 // Obtain tick of first outstanding read. If there are no outstanding 270 // reads, we use curTick(), i.e. we will remove all readObservation except 271 // the most recent one. 272 const Tick before = outstandingReads.empty() ? curTick() : 273 outstandingReads.begin()->second.start; 274 275 // Pruning of readObservations 276 readObservations.erase(readObservations.begin(), 277 lastCompletedTransaction(&readObservations, before)); 278 279 // Pruning of writeClusters 280 if (!writeClusters.empty()) { 281 writeClusters.erase(writeClusters.begin(), 282 lastCompletedTransaction(&writeClusters, before)); 283 } 284} 285 286bool 287MemChecker::completeRead(MemChecker::Serial serial, Tick complete, 288 Addr addr, size_t size, uint8_t *data) 289{ 290 bool result = true; 291 292 DPRINTF(MemChecker, 293 "completing read: serial = %d, complete = %d, " 294 "addr = %#llx, size = %d\n", serial, complete, addr, size); 295 296 for (size_t i = 0; i < size; ++i) { 297 ByteTracker *tracker = getByteTracker(addr + i); 298 299 if (!tracker->completeRead(serial, complete, data[i])) { 300 // Generate error message, and aggregate all failures for the bytes 301 // considered in this transaction in one message. 302 if (result) { 303 result = false; 304 errorMessage = ""; 305 } else { 306 errorMessage += "\n"; 307 } 308 309 errorMessage += csprintf(" Read transaction for address %#llx " 310 "failed: received %#x, expected ", 311 (unsigned long long)(addr + i), data[i]); 312 313 for (size_t j = 0; j < tracker->lastExpectedData().size(); ++j) { 314 errorMessage += 315 csprintf("%#x%s", 316 tracker->lastExpectedData()[j], 317 (j == tracker->lastExpectedData().size() - 1) 318 ? "" : "|"); 319 } 320 } 321 } 322 323 if (!result) { 324 DPRINTF(MemChecker, "read of %#llx @ cycle %d failed:\n%s\n", addr, 325 complete, errorMessage); 326 } 327 328 return result; 329} 330 331void 332MemChecker::reset(Addr addr, size_t size) 333{ 334 for (size_t i = 0; i < size; ++i) { 335 byte_trackers.erase(addr + i); 336 } 337} 338 339MemChecker* 340MemCheckerParams::create() 341{ 342 return new MemChecker(this); 343} 344