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