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