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