comm_monitor.cc revision 10744
1/* 2 * Copyright (c) 2012-2013, 2015 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 // do stack distance calculations if enabled 180 if (stackDistCalc) 181 stackDistCalc->update(pkt->cmd, pkt->getAddr()); 182 183 // if tracing enabled, store the packet information 184 // to the trace stream 185 if (traceStream != NULL) { 186 ProtoMessage::Packet pkt_msg; 187 pkt_msg.set_tick(curTick()); 188 pkt_msg.set_cmd(pkt->cmdToIndex()); 189 pkt_msg.set_flags(pkt->req->getFlags()); 190 pkt_msg.set_addr(pkt->getAddr()); 191 pkt_msg.set_size(pkt->getSize()); 192 193 traceStream->write(pkt_msg); 194 } 195 196 return masterPort.sendAtomic(pkt); 197} 198 199Tick 200CommMonitor::recvAtomicSnoop(PacketPtr pkt) 201{ 202 return slavePort.sendAtomicSnoop(pkt); 203} 204 205bool 206CommMonitor::recvTimingReq(PacketPtr pkt) 207{ 208 // should always see a request 209 assert(pkt->isRequest()); 210 211 // Store relevant fields of packet, because packet may be modified 212 // or even deleted when sendTiming() is called. 213 bool is_read = pkt->isRead(); 214 bool is_write = pkt->isWrite(); 215 MemCmd cmd = pkt->cmd; 216 int cmd_idx = pkt->cmdToIndex(); 217 Request::FlagsType req_flags = pkt->req->getFlags(); 218 unsigned size = pkt->getSize(); 219 Addr addr = pkt->getAddr(); 220 bool expects_response = pkt->needsResponse() && !pkt->memInhibitAsserted(); 221 222 // If a cache miss is served by a cache, a monitor near the memory 223 // would see a request which needs a response, but this response 224 // would be inhibited and not come back from the memory. Therefore 225 // we additionally have to check the inhibit flag. 226 if (expects_response && !stats.disableLatencyHists) { 227 pkt->pushSenderState(new CommMonitorSenderState(curTick())); 228 } 229 230 // Attempt to send the packet (always succeeds for inhibited 231 // packets) 232 bool successful = masterPort.sendTimingReq(pkt); 233 234 // If not successful, restore the sender state 235 if (!successful && expects_response && !stats.disableLatencyHists) { 236 delete pkt->popSenderState(); 237 } 238 239 // If successful and we are calculating stack distances, update 240 // the calculator 241 if (successful && stackDistCalc) 242 stackDistCalc->update(cmd, addr); 243 244 if (successful && traceStream != NULL) { 245 // Create a protobuf message representing the 246 // packet. Currently we do not preserve the flags in the 247 // trace. 248 ProtoMessage::Packet pkt_msg; 249 pkt_msg.set_tick(curTick()); 250 pkt_msg.set_cmd(cmd_idx); 251 pkt_msg.set_flags(req_flags); 252 pkt_msg.set_addr(addr); 253 pkt_msg.set_size(size); 254 255 traceStream->write(pkt_msg); 256 } 257 258 if (successful && is_read) { 259 DPRINTF(CommMonitor, "Forwarded read request\n"); 260 261 // Increment number of observed read transactions 262 if (!stats.disableTransactionHists) { 263 ++stats.readTrans; 264 } 265 266 // Get sample of burst length 267 if (!stats.disableBurstLengthHists) { 268 stats.readBurstLengthHist.sample(size); 269 } 270 271 // Sample the masked address 272 if (!stats.disableAddrDists) { 273 stats.readAddrDist.sample(addr & readAddrMask); 274 } 275 276 // If it needs a response increment number of outstanding read 277 // requests 278 if (!stats.disableOutstandingHists && expects_response) { 279 ++stats.outstandingReadReqs; 280 } 281 282 if (!stats.disableITTDists) { 283 // Sample value of read-read inter transaction time 284 if (stats.timeOfLastRead != 0) { 285 stats.ittReadRead.sample(curTick() - stats.timeOfLastRead); 286 } 287 stats.timeOfLastRead = curTick(); 288 289 // Sample value of req-req inter transaction time 290 if (stats.timeOfLastReq != 0) { 291 stats.ittReqReq.sample(curTick() - stats.timeOfLastReq); 292 } 293 stats.timeOfLastReq = curTick(); 294 } 295 } else if (successful && is_write) { 296 DPRINTF(CommMonitor, "Forwarded write request\n"); 297 298 // Same as for reads 299 if (!stats.disableTransactionHists) { 300 ++stats.writeTrans; 301 } 302 303 if (!stats.disableBurstLengthHists) { 304 stats.writeBurstLengthHist.sample(size); 305 } 306 307 // Update the bandwidth stats on the request 308 if (!stats.disableBandwidthHists) { 309 stats.writtenBytes += size; 310 stats.totalWrittenBytes += size; 311 } 312 313 // Sample the masked write address 314 if (!stats.disableAddrDists) { 315 stats.writeAddrDist.sample(addr & writeAddrMask); 316 } 317 318 if (!stats.disableOutstandingHists && expects_response) { 319 ++stats.outstandingWriteReqs; 320 } 321 322 if (!stats.disableITTDists) { 323 // Sample value of write-to-write inter transaction time 324 if (stats.timeOfLastWrite != 0) { 325 stats.ittWriteWrite.sample(curTick() - stats.timeOfLastWrite); 326 } 327 stats.timeOfLastWrite = curTick(); 328 329 // Sample value of req-to-req inter transaction time 330 if (stats.timeOfLastReq != 0) { 331 stats.ittReqReq.sample(curTick() - stats.timeOfLastReq); 332 } 333 stats.timeOfLastReq = curTick(); 334 } 335 } else if (successful) { 336 DPRINTF(CommMonitor, "Forwarded non read/write request\n"); 337 } 338 339 return successful; 340} 341 342bool 343CommMonitor::recvTimingResp(PacketPtr pkt) 344{ 345 // should always see responses 346 assert(pkt->isResponse()); 347 348 // Store relevant fields of packet, because packet may be modified 349 // or even deleted when sendTiming() is called. 350 bool is_read = pkt->isRead(); 351 bool is_write = pkt->isWrite(); 352 unsigned size = pkt->getSize(); 353 Tick latency = 0; 354 CommMonitorSenderState* received_state = 355 dynamic_cast<CommMonitorSenderState*>(pkt->senderState); 356 357 if (!stats.disableLatencyHists) { 358 // Restore initial sender state 359 if (received_state == NULL) 360 panic("Monitor got a response without monitor sender state\n"); 361 362 // Restore the sate 363 pkt->senderState = received_state->predecessor; 364 } 365 366 // Attempt to send the packet 367 bool successful = slavePort.sendTimingResp(pkt); 368 369 if (!stats.disableLatencyHists) { 370 // If packet successfully send, sample value of latency, 371 // afterwards delete sender state, otherwise restore state 372 if (successful) { 373 latency = curTick() - received_state->transmitTime; 374 DPRINTF(CommMonitor, "Latency: %d\n", latency); 375 delete received_state; 376 } else { 377 // Don't delete anything and let the packet look like we 378 // did not touch it 379 pkt->senderState = received_state; 380 } 381 } 382 383 if (successful && is_read) { 384 // Decrement number of outstanding read requests 385 DPRINTF(CommMonitor, "Received read response\n"); 386 if (!stats.disableOutstandingHists) { 387 assert(stats.outstandingReadReqs != 0); 388 --stats.outstandingReadReqs; 389 } 390 391 if (!stats.disableLatencyHists) { 392 stats.readLatencyHist.sample(latency); 393 } 394 395 // Update the bandwidth stats based on responses for reads 396 if (!stats.disableBandwidthHists) { 397 stats.readBytes += size; 398 stats.totalReadBytes += size; 399 } 400 401 } else if (successful && is_write) { 402 // Decrement number of outstanding write requests 403 DPRINTF(CommMonitor, "Received write response\n"); 404 if (!stats.disableOutstandingHists) { 405 assert(stats.outstandingWriteReqs != 0); 406 --stats.outstandingWriteReqs; 407 } 408 409 if (!stats.disableLatencyHists) { 410 stats.writeLatencyHist.sample(latency); 411 } 412 } else if (successful) { 413 DPRINTF(CommMonitor, "Received non read/write response\n"); 414 } 415 return successful; 416} 417 418void 419CommMonitor::recvTimingSnoopReq(PacketPtr pkt) 420{ 421 slavePort.sendTimingSnoopReq(pkt); 422} 423 424bool 425CommMonitor::recvTimingSnoopResp(PacketPtr pkt) 426{ 427 return masterPort.sendTimingSnoopResp(pkt); 428} 429 430bool 431CommMonitor::isSnooping() const 432{ 433 // check if the connected master port is snooping 434 return slavePort.isSnooping(); 435} 436 437AddrRangeList 438CommMonitor::getAddrRanges() const 439{ 440 // get the address ranges of the connected slave port 441 return masterPort.getAddrRanges(); 442} 443 444void 445CommMonitor::recvReqRetry() 446{ 447 slavePort.sendRetryReq(); 448} 449 450void 451CommMonitor::recvRespRetry() 452{ 453 masterPort.sendRetryResp(); 454} 455 456void 457CommMonitor::recvRangeChange() 458{ 459 slavePort.sendRangeChange(); 460} 461 462void 463CommMonitor::regStats() 464{ 465 // Initialise all the monitor stats 466 using namespace Stats; 467 468 stats.readBurstLengthHist 469 .init(params()->burst_length_bins) 470 .name(name() + ".readBurstLengthHist") 471 .desc("Histogram of burst lengths of transmitted packets") 472 .flags(stats.disableBurstLengthHists ? nozero : pdf); 473 474 stats.writeBurstLengthHist 475 .init(params()->burst_length_bins) 476 .name(name() + ".writeBurstLengthHist") 477 .desc("Histogram of burst lengths of transmitted packets") 478 .flags(stats.disableBurstLengthHists ? nozero : pdf); 479 480 // Stats based on received responses 481 stats.readBandwidthHist 482 .init(params()->bandwidth_bins) 483 .name(name() + ".readBandwidthHist") 484 .desc("Histogram of read bandwidth per sample period (bytes/s)") 485 .flags(stats.disableBandwidthHists ? nozero : pdf); 486 487 stats.averageReadBW 488 .name(name() + ".averageReadBandwidth") 489 .desc("Average read bandwidth (bytes/s)") 490 .flags(stats.disableBandwidthHists ? nozero : pdf); 491 492 stats.totalReadBytes 493 .name(name() + ".totalReadBytes") 494 .desc("Number of bytes read") 495 .flags(stats.disableBandwidthHists ? nozero : pdf); 496 497 stats.averageReadBW = stats.totalReadBytes / simSeconds; 498 499 // Stats based on successfully sent requests 500 stats.writeBandwidthHist 501 .init(params()->bandwidth_bins) 502 .name(name() + ".writeBandwidthHist") 503 .desc("Histogram of write bandwidth (bytes/s)") 504 .flags(stats.disableBandwidthHists ? (pdf | nozero) : pdf); 505 506 stats.averageWriteBW 507 .name(name() + ".averageWriteBandwidth") 508 .desc("Average write bandwidth (bytes/s)") 509 .flags(stats.disableBandwidthHists ? nozero : pdf); 510 511 stats.totalWrittenBytes 512 .name(name() + ".totalWrittenBytes") 513 .desc("Number of bytes written") 514 .flags(stats.disableBandwidthHists ? nozero : pdf); 515 516 stats.averageWriteBW = stats.totalWrittenBytes / simSeconds; 517 518 stats.readLatencyHist 519 .init(params()->latency_bins) 520 .name(name() + ".readLatencyHist") 521 .desc("Read request-response latency") 522 .flags(stats.disableLatencyHists ? nozero : pdf); 523 524 stats.writeLatencyHist 525 .init(params()->latency_bins) 526 .name(name() + ".writeLatencyHist") 527 .desc("Write request-response latency") 528 .flags(stats.disableLatencyHists ? nozero : pdf); 529 530 stats.ittReadRead 531 .init(1, params()->itt_max_bin, params()->itt_max_bin / 532 params()->itt_bins) 533 .name(name() + ".ittReadRead") 534 .desc("Read-to-read inter transaction time") 535 .flags(stats.disableITTDists ? nozero : pdf); 536 537 stats.ittWriteWrite 538 .init(1, params()->itt_max_bin, params()->itt_max_bin / 539 params()->itt_bins) 540 .name(name() + ".ittWriteWrite") 541 .desc("Write-to-write inter transaction time") 542 .flags(stats.disableITTDists ? nozero : pdf); 543 544 stats.ittReqReq 545 .init(1, params()->itt_max_bin, params()->itt_max_bin / 546 params()->itt_bins) 547 .name(name() + ".ittReqReq") 548 .desc("Request-to-request inter transaction time") 549 .flags(stats.disableITTDists ? nozero : pdf); 550 551 stats.outstandingReadsHist 552 .init(params()->outstanding_bins) 553 .name(name() + ".outstandingReadsHist") 554 .desc("Outstanding read transactions") 555 .flags(stats.disableOutstandingHists ? nozero : pdf); 556 557 stats.outstandingWritesHist 558 .init(params()->outstanding_bins) 559 .name(name() + ".outstandingWritesHist") 560 .desc("Outstanding write transactions") 561 .flags(stats.disableOutstandingHists ? nozero : pdf); 562 563 stats.readTransHist 564 .init(params()->transaction_bins) 565 .name(name() + ".readTransHist") 566 .desc("Histogram of read transactions per sample period") 567 .flags(stats.disableTransactionHists ? nozero : pdf); 568 569 stats.writeTransHist 570 .init(params()->transaction_bins) 571 .name(name() + ".writeTransHist") 572 .desc("Histogram of read transactions per sample period") 573 .flags(stats.disableTransactionHists ? nozero : pdf); 574 575 stats.readAddrDist 576 .init(0) 577 .name(name() + ".readAddrDist") 578 .desc("Read address distribution") 579 .flags(stats.disableAddrDists ? nozero : pdf); 580 581 stats.writeAddrDist 582 .init(0) 583 .name(name() + ".writeAddrDist") 584 .desc("Write address distribution") 585 .flags(stats.disableAddrDists ? nozero : pdf); 586} 587 588void 589CommMonitor::samplePeriodic() 590{ 591 // the periodic stats update runs on the granularity of sample 592 // periods, but in combination with this there may also be a 593 // external resets and dumps of the stats (through schedStatEvent) 594 // causing the stats themselves to capture less than a sample 595 // period 596 597 // only capture if we have not reset the stats during the last 598 // sample period 599 if (simTicks.value() >= samplePeriodTicks) { 600 if (!stats.disableTransactionHists) { 601 stats.readTransHist.sample(stats.readTrans); 602 stats.writeTransHist.sample(stats.writeTrans); 603 } 604 605 if (!stats.disableBandwidthHists) { 606 stats.readBandwidthHist.sample(stats.readBytes / samplePeriod); 607 stats.writeBandwidthHist.sample(stats.writtenBytes / samplePeriod); 608 } 609 610 if (!stats.disableOutstandingHists) { 611 stats.outstandingReadsHist.sample(stats.outstandingReadReqs); 612 stats.outstandingWritesHist.sample(stats.outstandingWriteReqs); 613 } 614 } 615 616 // reset the sampled values 617 stats.readTrans = 0; 618 stats.writeTrans = 0; 619 620 stats.readBytes = 0; 621 stats.writtenBytes = 0; 622 623 schedule(samplePeriodicEvent, curTick() + samplePeriodTicks); 624} 625 626void 627CommMonitor::startup() 628{ 629 schedule(samplePeriodicEvent, curTick() + samplePeriodTicks); 630} 631