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