comm_monitor.cc revision 9542
1/* 2 * Copyright (c) 2012-2013 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: Thomas Grass 38 * Andreas Hansson 39 */ 40 41#include "base/callback.hh" 42#include "base/output.hh" 43#include "base/trace.hh" 44#include "debug/CommMonitor.hh" 45#include "mem/comm_monitor.hh" 46#include "proto/packet.pb.h" 47#include "sim/stats.hh" 48 49CommMonitor::CommMonitor(Params* params) 50 : MemObject(params), 51 masterPort(name() + "-master", *this), 52 slavePort(name() + "-slave", *this), 53 samplePeriodicEvent(this), 54 samplePeriodTicks(params->sample_period), 55 readAddrMask(params->read_addr_mask), 56 writeAddrMask(params->write_addr_mask), 57 stats(params), 58 traceStream(NULL) 59{ 60 // If we are using a trace file, then open the file, 61 if (params->trace_file != "") { 62 // If the trace file is not specified as an absolute path, 63 // append the current simulation output directory 64 std::string filename = simout.resolve(params->trace_file); 65 traceStream = new ProtoOutputStream(filename); 66 67 // Create a protobuf message for the header and write it to 68 // the stream 69 Message::PacketHeader header_msg; 70 header_msg.set_obj_id(name()); 71 header_msg.set_tick_freq(SimClock::Frequency); 72 traceStream->write(header_msg); 73 74 // Register a callback to compensate for the destructor not 75 // being called. The callback forces the stream to flush and 76 // closes the output file. 77 Callback* cb = new MakeCallback<CommMonitor, 78 &CommMonitor::closeStreams>(this); 79 registerExitCallback(cb); 80 } 81 82 // keep track of the sample period both in ticks and absolute time 83 samplePeriod.setTick(params->sample_period); 84 85 DPRINTF(CommMonitor, 86 "Created monitor %s with sample period %d ticks (%f s)\n", 87 name(), samplePeriodTicks, samplePeriod); 88} 89 90void 91CommMonitor::closeStreams() 92{ 93 if (traceStream != NULL) 94 delete traceStream; 95} 96 97CommMonitor* 98CommMonitorParams::create() 99{ 100 return new CommMonitor(this); 101} 102 103void 104CommMonitor::init() 105{ 106 // make sure both sides of the monitor are connected 107 if (!slavePort.isConnected() || !masterPort.isConnected()) 108 fatal("Communication monitor is not connected on both sides.\n"); 109} 110 111BaseMasterPort& 112CommMonitor::getMasterPort(const std::string& if_name, PortID idx) 113{ 114 if (if_name == "master") { 115 return masterPort; 116 } else { 117 return MemObject::getMasterPort(if_name, idx); 118 } 119} 120 121BaseSlavePort& 122CommMonitor::getSlavePort(const std::string& if_name, PortID idx) 123{ 124 if (if_name == "slave") { 125 return slavePort; 126 } else { 127 return MemObject::getSlavePort(if_name, idx); 128 } 129} 130 131void 132CommMonitor::recvFunctional(PacketPtr pkt) 133{ 134 masterPort.sendFunctional(pkt); 135} 136 137void 138CommMonitor::recvFunctionalSnoop(PacketPtr pkt) 139{ 140 slavePort.sendFunctionalSnoop(pkt); 141} 142 143Tick 144CommMonitor::recvAtomic(PacketPtr pkt) 145{ 146 return masterPort.sendAtomic(pkt); 147} 148 149Tick 150CommMonitor::recvAtomicSnoop(PacketPtr pkt) 151{ 152 return slavePort.sendAtomicSnoop(pkt); 153} 154 155bool 156CommMonitor::recvTimingReq(PacketPtr pkt) 157{ 158 // should always see a request 159 assert(pkt->isRequest()); 160 161 // Store relevant fields of packet, because packet may be modified 162 // or even deleted when sendTiming() is called. 163 bool isRead = pkt->isRead(); 164 bool isWrite = pkt->isWrite(); 165 int cmd = pkt->cmdToIndex(); 166 unsigned size = pkt->getSize(); 167 Addr addr = pkt->getAddr(); 168 bool needsResponse = pkt->needsResponse(); 169 bool memInhibitAsserted = pkt->memInhibitAsserted(); 170 171 // If a cache miss is served by a cache, a monitor near the memory 172 // would see a request which needs a response, but this response 173 // would be inhibited and not come back from the memory. Therefore 174 // we additionally have to check the inhibit flag. 175 if (needsResponse && !memInhibitAsserted && !stats.disableLatencyHists) { 176 pkt->pushSenderState(new CommMonitorSenderState(curTick())); 177 } 178 179 // Attempt to send the packet (always succeeds for inhibited 180 // packets) 181 bool successful = masterPort.sendTimingReq(pkt); 182 183 // If not successful, restore the sender state 184 if (!successful && needsResponse && !stats.disableLatencyHists) { 185 delete pkt->popSenderState(); 186 } 187 188 if (successful && traceStream != NULL) { 189 // Create a protobuf message representing the 190 // packet. Currently we do not preserve the flags in the 191 // trace. 192 Message::Packet pkt_msg; 193 pkt_msg.set_tick(curTick()); 194 pkt_msg.set_cmd(cmd); 195 pkt_msg.set_addr(addr); 196 pkt_msg.set_size(size); 197 198 traceStream->write(pkt_msg); 199 } 200 201 if (successful && isRead) { 202 DPRINTF(CommMonitor, "Forwarded read request\n"); 203 204 // Increment number of observed read transactions 205 if (!stats.disableTransactionHists) { 206 ++stats.readTrans; 207 } 208 209 // Get sample of burst length 210 if (!stats.disableBurstLengthHists) { 211 stats.readBurstLengthHist.sample(size); 212 } 213 214 // Sample the masked address 215 if (!stats.disableAddrDists) { 216 stats.readAddrDist.sample(addr & readAddrMask); 217 } 218 219 // If it needs a response increment number of outstanding read 220 // requests 221 if (!stats.disableOutstandingHists && needsResponse) { 222 ++stats.outstandingReadReqs; 223 } 224 225 if (!stats.disableITTDists) { 226 // Sample value of read-read inter transaction time 227 if (stats.timeOfLastRead != 0) { 228 stats.ittReadRead.sample(curTick() - stats.timeOfLastRead); 229 } 230 stats.timeOfLastRead = curTick(); 231 232 // Sample value of req-req inter transaction time 233 if (stats.timeOfLastReq != 0) { 234 stats.ittReqReq.sample(curTick() - stats.timeOfLastReq); 235 } 236 stats.timeOfLastReq = curTick(); 237 } 238 } else if (successful && isWrite) { 239 DPRINTF(CommMonitor, "Forwarded write request\n"); 240 241 // Same as for reads 242 if (!stats.disableTransactionHists) { 243 ++stats.writeTrans; 244 } 245 246 if (!stats.disableBurstLengthHists) { 247 stats.writeBurstLengthHist.sample(size); 248 } 249 250 // Update the bandwidth stats on the request 251 if (!stats.disableBandwidthHists) { 252 stats.writtenBytes += size; 253 stats.totalWrittenBytes += size; 254 } 255 256 // Sample the masked write address 257 if (!stats.disableAddrDists) { 258 stats.writeAddrDist.sample(addr & writeAddrMask); 259 } 260 261 if (!stats.disableOutstandingHists && needsResponse) { 262 ++stats.outstandingWriteReqs; 263 } 264 265 if (!stats.disableITTDists) { 266 // Sample value of write-to-write inter transaction time 267 if (stats.timeOfLastWrite != 0) { 268 stats.ittWriteWrite.sample(curTick() - stats.timeOfLastWrite); 269 } 270 stats.timeOfLastWrite = curTick(); 271 272 // Sample value of req-to-req inter transaction time 273 if (stats.timeOfLastReq != 0) { 274 stats.ittReqReq.sample(curTick() - stats.timeOfLastReq); 275 } 276 stats.timeOfLastReq = curTick(); 277 } 278 } else if (successful) { 279 DPRINTF(CommMonitor, "Forwarded non read/write request\n"); 280 } 281 282 return successful; 283} 284 285bool 286CommMonitor::recvTimingResp(PacketPtr pkt) 287{ 288 // should always see responses 289 assert(pkt->isResponse()); 290 291 // Store relevant fields of packet, because packet may be modified 292 // or even deleted when sendTiming() is called. 293 bool isRead = pkt->isRead(); 294 bool isWrite = pkt->isWrite(); 295 unsigned size = pkt->getSize(); 296 Tick latency = 0; 297 CommMonitorSenderState* commReceivedState = 298 dynamic_cast<CommMonitorSenderState*>(pkt->senderState); 299 300 if (!stats.disableLatencyHists) { 301 // Restore initial sender state 302 if (commReceivedState == NULL) 303 panic("Monitor got a response without monitor sender state\n"); 304 305 // Restore the sate 306 pkt->senderState = commReceivedState->predecessor; 307 } 308 309 // Attempt to send the packet 310 bool successful = slavePort.sendTimingResp(pkt); 311 312 if (!stats.disableLatencyHists) { 313 // If packet successfully send, sample value of latency, 314 // afterwards delete sender state, otherwise restore state 315 if (successful) { 316 latency = curTick() - commReceivedState->transmitTime; 317 DPRINTF(CommMonitor, "Latency: %d\n", latency); 318 delete commReceivedState; 319 } else { 320 // Don't delete anything and let the packet look like we 321 // did not touch it 322 pkt->senderState = commReceivedState; 323 } 324 } 325 326 if (successful && isRead) { 327 // Decrement number of outstanding read requests 328 DPRINTF(CommMonitor, "Received read response\n"); 329 if (!stats.disableOutstandingHists) { 330 assert(stats.outstandingReadReqs != 0); 331 --stats.outstandingReadReqs; 332 } 333 334 if (!stats.disableLatencyHists) { 335 stats.readLatencyHist.sample(latency); 336 } 337 338 // Update the bandwidth stats based on responses for reads 339 if (!stats.disableBandwidthHists) { 340 stats.readBytes += size; 341 stats.totalReadBytes += size; 342 } 343 344 } else if (successful && isWrite) { 345 // Decrement number of outstanding write requests 346 DPRINTF(CommMonitor, "Received write response\n"); 347 if (!stats.disableOutstandingHists) { 348 assert(stats.outstandingWriteReqs != 0); 349 --stats.outstandingWriteReqs; 350 } 351 352 if (!stats.disableLatencyHists) { 353 stats.writeLatencyHist.sample(latency); 354 } 355 } else if (successful) { 356 DPRINTF(CommMonitor, "Received non read/write response\n"); 357 } 358 return successful; 359} 360 361void 362CommMonitor::recvTimingSnoopReq(PacketPtr pkt) 363{ 364 slavePort.sendTimingSnoopReq(pkt); 365} 366 367bool 368CommMonitor::recvTimingSnoopResp(PacketPtr pkt) 369{ 370 return masterPort.sendTimingSnoopResp(pkt); 371} 372 373bool 374CommMonitor::isSnooping() const 375{ 376 // check if the connected master port is snooping 377 return slavePort.isSnooping(); 378} 379 380unsigned 381CommMonitor::deviceBlockSizeMaster() 382{ 383 return slavePort.peerBlockSize(); 384} 385 386unsigned 387CommMonitor::deviceBlockSizeSlave() 388{ 389 return masterPort.peerBlockSize(); 390} 391 392AddrRangeList 393CommMonitor::getAddrRanges() const 394{ 395 // get the address ranges of the connected slave port 396 return masterPort.getAddrRanges(); 397} 398 399void 400CommMonitor::recvRetryMaster() 401{ 402 slavePort.sendRetry(); 403} 404 405void 406CommMonitor::recvRetrySlave() 407{ 408 masterPort.sendRetry(); 409} 410 411void 412CommMonitor::recvRangeChange() 413{ 414 slavePort.sendRangeChange(); 415} 416 417void 418CommMonitor::regStats() 419{ 420 // Initialise all the monitor stats 421 using namespace Stats; 422 423 stats.readBurstLengthHist 424 .init(params()->burst_length_bins) 425 .name(name() + ".readBurstLengthHist") 426 .desc("Histogram of burst lengths of transmitted packets") 427 .flags(stats.disableBurstLengthHists ? nozero : pdf); 428 429 stats.writeBurstLengthHist 430 .init(params()->burst_length_bins) 431 .name(name() + ".writeBurstLengthHist") 432 .desc("Histogram of burst lengths of transmitted packets") 433 .flags(stats.disableBurstLengthHists ? nozero : pdf); 434 435 // Stats based on received responses 436 stats.readBandwidthHist 437 .init(params()->bandwidth_bins) 438 .name(name() + ".readBandwidthHist") 439 .desc("Histogram of read bandwidth per sample period (bytes/s)") 440 .flags(stats.disableBandwidthHists ? nozero : pdf); 441 442 stats.averageReadBW 443 .name(name() + ".averageReadBandwidth") 444 .desc("Average read bandwidth (bytes/s)") 445 .flags(stats.disableBandwidthHists ? nozero : pdf); 446 447 stats.totalReadBytes 448 .name(name() + ".totalReadBytes") 449 .desc("Number of bytes read") 450 .flags(stats.disableBandwidthHists ? nozero : pdf); 451 452 stats.averageReadBW = stats.totalReadBytes / simSeconds; 453 454 // Stats based on successfully sent requests 455 stats.writeBandwidthHist 456 .init(params()->bandwidth_bins) 457 .name(name() + ".writeBandwidthHist") 458 .desc("Histogram of write bandwidth (bytes/s)") 459 .flags(stats.disableBandwidthHists ? (pdf | nozero) : pdf); 460 461 stats.averageWriteBW 462 .name(name() + ".averageWriteBandwidth") 463 .desc("Average write bandwidth (bytes/s)") 464 .flags(stats.disableBandwidthHists ? nozero : pdf); 465 466 stats.totalWrittenBytes 467 .name(name() + ".totalWrittenBytes") 468 .desc("Number of bytes written") 469 .flags(stats.disableBandwidthHists ? nozero : pdf); 470 471 stats.averageWriteBW = stats.totalWrittenBytes / simSeconds; 472 473 stats.readLatencyHist 474 .init(params()->latency_bins) 475 .name(name() + ".readLatencyHist") 476 .desc("Read request-response latency") 477 .flags(stats.disableLatencyHists ? nozero : pdf); 478 479 stats.writeLatencyHist 480 .init(params()->latency_bins) 481 .name(name() + ".writeLatencyHist") 482 .desc("Write request-response latency") 483 .flags(stats.disableLatencyHists ? nozero : pdf); 484 485 stats.ittReadRead 486 .init(1, params()->itt_max_bin, params()->itt_max_bin / 487 params()->itt_bins) 488 .name(name() + ".ittReadRead") 489 .desc("Read-to-read inter transaction time") 490 .flags(stats.disableITTDists ? nozero : pdf); 491 492 stats.ittWriteWrite 493 .init(1, params()->itt_max_bin, params()->itt_max_bin / 494 params()->itt_bins) 495 .name(name() + ".ittWriteWrite") 496 .desc("Write-to-write inter transaction time") 497 .flags(stats.disableITTDists ? nozero : pdf); 498 499 stats.ittReqReq 500 .init(1, params()->itt_max_bin, params()->itt_max_bin / 501 params()->itt_bins) 502 .name(name() + ".ittReqReq") 503 .desc("Request-to-request inter transaction time") 504 .flags(stats.disableITTDists ? nozero : pdf); 505 506 stats.outstandingReadsHist 507 .init(params()->outstanding_bins) 508 .name(name() + ".outstandingReadsHist") 509 .desc("Outstanding read transactions") 510 .flags(stats.disableOutstandingHists ? nozero : pdf); 511 512 stats.outstandingWritesHist 513 .init(params()->outstanding_bins) 514 .name(name() + ".outstandingWritesHist") 515 .desc("Outstanding write transactions") 516 .flags(stats.disableOutstandingHists ? nozero : pdf); 517 518 stats.readTransHist 519 .init(params()->transaction_bins) 520 .name(name() + ".readTransHist") 521 .desc("Histogram of read transactions per sample period") 522 .flags(stats.disableTransactionHists ? nozero : pdf); 523 524 stats.writeTransHist 525 .init(params()->transaction_bins) 526 .name(name() + ".writeTransHist") 527 .desc("Histogram of read transactions per sample period") 528 .flags(stats.disableTransactionHists ? nozero : pdf); 529 530 stats.readAddrDist 531 .init(0) 532 .name(name() + ".readAddrDist") 533 .desc("Read address distribution") 534 .flags(stats.disableAddrDists ? nozero : pdf); 535 536 stats.writeAddrDist 537 .init(0) 538 .name(name() + ".writeAddrDist") 539 .desc("Write address distribution") 540 .flags(stats.disableAddrDists ? nozero : pdf); 541} 542 543void 544CommMonitor::samplePeriodic() 545{ 546 // the periodic stats update runs on the granularity of sample 547 // periods, but in combination with this there may also be a 548 // external resets and dumps of the stats (through schedStatEvent) 549 // causing the stats themselves to capture less than a sample 550 // period 551 552 // only capture if we have not reset the stats during the last 553 // sample period 554 if (simTicks.value() >= samplePeriodTicks) { 555 if (!stats.disableTransactionHists) { 556 stats.readTransHist.sample(stats.readTrans); 557 stats.writeTransHist.sample(stats.writeTrans); 558 } 559 560 if (!stats.disableBandwidthHists) { 561 stats.readBandwidthHist.sample(stats.readBytes / samplePeriod); 562 stats.writeBandwidthHist.sample(stats.writtenBytes / samplePeriod); 563 } 564 565 if (!stats.disableOutstandingHists) { 566 stats.outstandingReadsHist.sample(stats.outstandingReadReqs); 567 stats.outstandingWritesHist.sample(stats.outstandingWriteReqs); 568 } 569 } 570 571 // reset the sampled values 572 stats.readTrans = 0; 573 stats.writeTrans = 0; 574 575 stats.readBytes = 0; 576 stats.writtenBytes = 0; 577 578 schedule(samplePeriodicEvent, curTick() + samplePeriodTicks); 579} 580 581void 582CommMonitor::startup() 583{ 584 schedule(samplePeriodicEvent, curTick() + samplePeriodTicks); 585} 586