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