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