packet.cc revision 11793
15245Sgblack@eecs.umich.edu/*
25245Sgblack@eecs.umich.edu * Copyright (c) 2011-2016 ARM Limited
35245Sgblack@eecs.umich.edu * All rights reserved
45245Sgblack@eecs.umich.edu *
57087Snate@binkert.org * The license below extends only to copyright in the software and shall
67087Snate@binkert.org * not be construed as granting a license to any other intellectual
77087Snate@binkert.org * property including but not limited to intellectual property relating
87087Snate@binkert.org * to a hardware implementation of the functionality of the software
97087Snate@binkert.org * licensed hereunder.  You may use the software subject to the license
107087Snate@binkert.org * terms below provided that you ensure that this notice is replicated
117087Snate@binkert.org * unmodified and in its entirety in all distributions of the software,
127087Snate@binkert.org * modified or unmodified, in source code or in binary form.
135245Sgblack@eecs.umich.edu *
147087Snate@binkert.org * Copyright (c) 2006 The Regents of The University of Michigan
157087Snate@binkert.org * Copyright (c) 2010,2015 Advanced Micro Devices, Inc.
167087Snate@binkert.org * All rights reserved.
177087Snate@binkert.org *
187087Snate@binkert.org * Redistribution and use in source and binary forms, with or without
197087Snate@binkert.org * modification, are permitted provided that the following conditions are
207087Snate@binkert.org * met: redistributions of source code must retain the above copyright
217087Snate@binkert.org * notice, this list of conditions and the following disclaimer;
225245Sgblack@eecs.umich.edu * redistributions in binary form must reproduce the above copyright
237087Snate@binkert.org * notice, this list of conditions and the following disclaimer in the
245245Sgblack@eecs.umich.edu * documentation and/or other materials provided with the distribution;
255245Sgblack@eecs.umich.edu * neither the name of the copyright holders nor the names of its
265245Sgblack@eecs.umich.edu * contributors may be used to endorse or promote products derived from
275245Sgblack@eecs.umich.edu * this software without specific prior written permission.
285245Sgblack@eecs.umich.edu *
295245Sgblack@eecs.umich.edu * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
305245Sgblack@eecs.umich.edu * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
315245Sgblack@eecs.umich.edu * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
325245Sgblack@eecs.umich.edu * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
335245Sgblack@eecs.umich.edu * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
345245Sgblack@eecs.umich.edu * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
355245Sgblack@eecs.umich.edu * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
365245Sgblack@eecs.umich.edu * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
375245Sgblack@eecs.umich.edu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
385245Sgblack@eecs.umich.edu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
395245Sgblack@eecs.umich.edu * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
405245Sgblack@eecs.umich.edu *
415245Sgblack@eecs.umich.edu * Authors: Ali Saidi
425245Sgblack@eecs.umich.edu *          Steve Reinhardt
435245Sgblack@eecs.umich.edu */
445245Sgblack@eecs.umich.edu
455245Sgblack@eecs.umich.edu/**
465245Sgblack@eecs.umich.edu * @file
476216Snate@binkert.org * Definition of the Packet Class, a packet is a transaction occuring
485245Sgblack@eecs.umich.edu * between a single level of the memory heirarchy (ie L1->L2).
495245Sgblack@eecs.umich.edu */
505245Sgblack@eecs.umich.edu
517901Shestness@cs.utexas.edu#include "mem/packet.hh"
528832SAli.Saidi@ARM.com
535245Sgblack@eecs.umich.edu#include <cstring>
545245Sgblack@eecs.umich.edu#include <iostream>
555245Sgblack@eecs.umich.edu
565245Sgblack@eecs.umich.edu#include "base/cprintf.hh"
575245Sgblack@eecs.umich.edu#include "base/misc.hh"
585245Sgblack@eecs.umich.edu#include "base/trace.hh"
595245Sgblack@eecs.umich.edu
605245Sgblack@eecs.umich.eduusing namespace std;
617912Shestness@cs.utexas.edu
628922Swilliam.wang@arm.com// The one downside to bitsets is that static initializers can get ugly.
635245Sgblack@eecs.umich.edu#define SET1(a1)                     (1 << (a1))
645245Sgblack@eecs.umich.edu#define SET2(a1, a2)                 (SET1(a1) | SET1(a2))
655245Sgblack@eecs.umich.edu#define SET3(a1, a2, a3)             (SET2(a1, a2) | SET1(a3))
668922Swilliam.wang@arm.com#define SET4(a1, a2, a3, a4)         (SET3(a1, a2, a3) | SET1(a4))
675245Sgblack@eecs.umich.edu#define SET5(a1, a2, a3, a4, a5)     (SET4(a1, a2, a3, a4) | SET1(a5))
685245Sgblack@eecs.umich.edu#define SET6(a1, a2, a3, a4, a5, a6) (SET5(a1, a2, a3, a4, a5) | SET1(a6))
695245Sgblack@eecs.umich.edu#define SET7(a1, a2, a3, a4, a5, a6, a7) (SET6(a1, a2, a3, a4, a5, a6) | \
708832SAli.Saidi@ARM.com                                          SET1(a7))
715245Sgblack@eecs.umich.edu
728975Sandreas.hansson@arm.comconst MemCmd::CommandInfo
7310713Sandreas.hansson@arm.comMemCmd::commandInfo[] =
745245Sgblack@eecs.umich.edu{
755245Sgblack@eecs.umich.edu    /* InvalidCmd */
767912Shestness@cs.utexas.edu    { 0, InvalidCmd, "InvalidCmd" },
777912Shestness@cs.utexas.edu    /* ReadReq - Read issued by a non-caching agent such as a CPU or
785245Sgblack@eecs.umich.edu     * device, with no restrictions on alignment. */
797912Shestness@cs.utexas.edu    { SET3(IsRead, IsRequest, NeedsResponse), ReadResp, "ReadReq" },
809044SAli.Saidi@ARM.com    /* ReadResp */
817912Shestness@cs.utexas.edu    { SET3(IsRead, IsResponse, HasData), InvalidCmd, "ReadResp" },
829701Sgedare@rtems.org    /* ReadRespWithInvalidate */
837912Shestness@cs.utexas.edu    { SET4(IsRead, IsResponse, HasData, IsInvalidate),
847912Shestness@cs.utexas.edu            InvalidCmd, "ReadRespWithInvalidate" },
857912Shestness@cs.utexas.edu    /* WriteReq */
867912Shestness@cs.utexas.edu    { SET5(IsWrite, NeedsWritable, IsRequest, NeedsResponse, HasData),
877912Shestness@cs.utexas.edu            WriteResp, "WriteReq" },
887912Shestness@cs.utexas.edu    /* WriteResp */
897912Shestness@cs.utexas.edu    { SET2(IsWrite, IsResponse), InvalidCmd, "WriteResp" },
907912Shestness@cs.utexas.edu    /* WritebackDirty */
917912Shestness@cs.utexas.edu    { SET5(IsWrite, IsRequest, IsEviction, HasData, FromCache),
927912Shestness@cs.utexas.edu            InvalidCmd, "WritebackDirty" },
937912Shestness@cs.utexas.edu    /* WritebackClean - This allows the upstream cache to writeback a
945245Sgblack@eecs.umich.edu     * line to the downstream cache without it being considered
957912Shestness@cs.utexas.edu     * dirty. */
968832SAli.Saidi@ARM.com    { SET5(IsWrite, IsRequest, IsEviction, HasData, FromCache),
977912Shestness@cs.utexas.edu            InvalidCmd, "WritebackClean" },
987912Shestness@cs.utexas.edu    /* CleanEvict */
997912Shestness@cs.utexas.edu    { SET3(IsRequest, IsEviction, FromCache), InvalidCmd, "CleanEvict" },
1007912Shestness@cs.utexas.edu    /* SoftPFReq */
1017912Shestness@cs.utexas.edu    { SET4(IsRead, IsRequest, IsSWPrefetch, NeedsResponse),
1027912Shestness@cs.utexas.edu            SoftPFResp, "SoftPFReq" },
1037912Shestness@cs.utexas.edu    /* HardPFReq */
1047912Shestness@cs.utexas.edu    { SET5(IsRead, IsRequest, IsHWPrefetch, NeedsResponse, FromCache),
1057912Shestness@cs.utexas.edu            HardPFResp, "HardPFReq" },
1067912Shestness@cs.utexas.edu    /* SoftPFResp */
1077912Shestness@cs.utexas.edu    { SET4(IsRead, IsResponse, IsSWPrefetch, HasData),
1087912Shestness@cs.utexas.edu            InvalidCmd, "SoftPFResp" },
1097912Shestness@cs.utexas.edu    /* HardPFResp */
1107912Shestness@cs.utexas.edu    { SET4(IsRead, IsResponse, IsHWPrefetch, HasData),
1117912Shestness@cs.utexas.edu            InvalidCmd, "HardPFResp" },
1127912Shestness@cs.utexas.edu    /* WriteLineReq */
1137912Shestness@cs.utexas.edu    { SET5(IsWrite, NeedsWritable, IsRequest, NeedsResponse, HasData),
1147912Shestness@cs.utexas.edu            WriteResp, "WriteLineReq" },
1157912Shestness@cs.utexas.edu    /* UpgradeReq */
1167912Shestness@cs.utexas.edu    { SET6(IsInvalidate, NeedsWritable, IsUpgrade, IsRequest, NeedsResponse,
1177912Shestness@cs.utexas.edu            FromCache),
1187912Shestness@cs.utexas.edu            UpgradeResp, "UpgradeReq" },
1197912Shestness@cs.utexas.edu    /* SCUpgradeReq: response could be UpgradeResp or UpgradeFailResp */
1207912Shestness@cs.utexas.edu    { SET7(IsInvalidate, NeedsWritable, IsUpgrade, IsLlsc,
1217912Shestness@cs.utexas.edu           IsRequest, NeedsResponse, FromCache),
1227912Shestness@cs.utexas.edu            UpgradeResp, "SCUpgradeReq" },
1237912Shestness@cs.utexas.edu    /* UpgradeResp */
1247912Shestness@cs.utexas.edu    { SET2(IsUpgrade, IsResponse),
1257912Shestness@cs.utexas.edu            InvalidCmd, "UpgradeResp" },
1267912Shestness@cs.utexas.edu    /* SCUpgradeFailReq: generates UpgradeFailResp but still gets the data */
1278953Sgblack@eecs.umich.edu    { SET7(IsRead, NeedsWritable, IsInvalidate,
1287912Shestness@cs.utexas.edu           IsLlsc, IsRequest, NeedsResponse, FromCache),
1297912Shestness@cs.utexas.edu            UpgradeFailResp, "SCUpgradeFailReq" },
1307912Shestness@cs.utexas.edu    /* UpgradeFailResp - Behaves like a ReadExReq, but notifies an SC
1317912Shestness@cs.utexas.edu     * that it has failed, acquires line as Dirty*/
1327912Shestness@cs.utexas.edu    { SET3(IsRead, IsResponse, HasData),
1337912Shestness@cs.utexas.edu            InvalidCmd, "UpgradeFailResp" },
1347912Shestness@cs.utexas.edu    /* ReadExReq - Read issues by a cache, always cache-line aligned,
1357912Shestness@cs.utexas.edu     * and the response is guaranteed to be writeable (exclusive or
1367912Shestness@cs.utexas.edu     * even modified) */
1377912Shestness@cs.utexas.edu    { SET6(IsRead, NeedsWritable, IsInvalidate, IsRequest, NeedsResponse,
1387912Shestness@cs.utexas.edu            FromCache),
1397912Shestness@cs.utexas.edu            ReadExResp, "ReadExReq" },
1407912Shestness@cs.utexas.edu    /* ReadExResp - Response matching a read exclusive, as we check
1417912Shestness@cs.utexas.edu     * the need for exclusive also on responses */
1427912Shestness@cs.utexas.edu    { SET3(IsRead, IsResponse, HasData),
1437912Shestness@cs.utexas.edu            InvalidCmd, "ReadExResp" },
1447912Shestness@cs.utexas.edu    /* ReadCleanReq - Read issued by a cache, always cache-line
1457912Shestness@cs.utexas.edu     * aligned, and the response is guaranteed to not contain dirty data
1467912Shestness@cs.utexas.edu     * (exclusive or shared).*/
1477912Shestness@cs.utexas.edu    { SET4(IsRead, IsRequest, NeedsResponse, FromCache),
1487912Shestness@cs.utexas.edu            ReadResp, "ReadCleanReq" },
1497912Shestness@cs.utexas.edu    /* ReadSharedReq - Read issued by a cache, always cache-line
1507912Shestness@cs.utexas.edu     * aligned, response is shared, possibly exclusive, owned or even
1517912Shestness@cs.utexas.edu     * modified. */
1527912Shestness@cs.utexas.edu    { SET4(IsRead, IsRequest, NeedsResponse, FromCache),
1539542Sandreas.hansson@arm.com            ReadResp, "ReadSharedReq" },
1549542Sandreas.hansson@arm.com    /* LoadLockedReq: note that we use plain ReadResp as response, so that
1557912Shestness@cs.utexas.edu     *                we can also use ReadRespWithInvalidate when needed */
1567912Shestness@cs.utexas.edu    { SET4(IsRead, IsLlsc, IsRequest, NeedsResponse),
1577912Shestness@cs.utexas.edu            ReadResp, "LoadLockedReq" },
1587912Shestness@cs.utexas.edu    /* StoreCondReq */
1597912Shestness@cs.utexas.edu    { SET6(IsWrite, NeedsWritable, IsLlsc,
1607912Shestness@cs.utexas.edu           IsRequest, NeedsResponse, HasData),
1617912Shestness@cs.utexas.edu            StoreCondResp, "StoreCondReq" },
1628953Sgblack@eecs.umich.edu    /* StoreCondFailReq: generates failing StoreCondResp */
1639294Sandreas.hansson@arm.com    { SET6(IsWrite, NeedsWritable, IsLlsc,
1649294Sandreas.hansson@arm.com           IsRequest, NeedsResponse, HasData),
1657912Shestness@cs.utexas.edu            StoreCondResp, "StoreCondFailReq" },
1667912Shestness@cs.utexas.edu    /* StoreCondResp */
1675245Sgblack@eecs.umich.edu    { SET3(IsWrite, IsLlsc, IsResponse),
1685245Sgblack@eecs.umich.edu            InvalidCmd, "StoreCondResp" },
1695245Sgblack@eecs.umich.edu    /* SwapReq -- for Swap ldstub type operations */
1708832SAli.Saidi@ARM.com    { SET6(IsRead, IsWrite, NeedsWritable, IsRequest, HasData, NeedsResponse),
1715245Sgblack@eecs.umich.edu        SwapResp, "SwapReq" },
1729701Sgedare@rtems.org    /* SwapResp -- for Swap ldstub type operations */
1739701Sgedare@rtems.org    { SET4(IsRead, IsWrite, IsResponse, HasData),
1749701Sgedare@rtems.org            InvalidCmd, "SwapResp" },
1759701Sgedare@rtems.org    /* IntReq -- for interrupts */
1769701Sgedare@rtems.org    { SET4(IsWrite, IsRequest, NeedsResponse, HasData),
1779701Sgedare@rtems.org        MessageResp, "MessageReq" },
17810654Sandreas.hansson@arm.com    /* IntResp -- for interrupts */
17910654Sandreas.hansson@arm.com    { SET2(IsWrite, IsResponse), InvalidCmd, "MessageResp" },
18010654Sandreas.hansson@arm.com    /* MemFenceReq -- for synchronization requests */
18112088Sspwilson2@wisc.edu    {SET2(IsRequest, NeedsResponse), MemFenceResp, "MemFenceReq"},
18210654Sandreas.hansson@arm.com    /* MemFenceResp -- for synchronization responses */
1837912Shestness@cs.utexas.edu    {SET1(IsResponse), InvalidCmd, "MemFenceResp"},
1848975Sandreas.hansson@arm.com    /* InvalidDestError  -- packet dest field invalid */
18510713Sandreas.hansson@arm.com    { SET2(IsResponse, IsError), InvalidCmd, "InvalidDestError" },
1867912Shestness@cs.utexas.edu    /* BadAddressError   -- memory address invalid */
1875245Sgblack@eecs.umich.edu    { SET2(IsResponse, IsError), InvalidCmd, "BadAddressError" },
1885245Sgblack@eecs.umich.edu    /* FunctionalReadError */
1895245Sgblack@eecs.umich.edu    { SET3(IsRead, IsResponse, IsError), InvalidCmd, "FunctionalReadError" },
1905245Sgblack@eecs.umich.edu    /* FunctionalWriteError */
1915245Sgblack@eecs.umich.edu    { SET3(IsWrite, IsResponse, IsError), InvalidCmd, "FunctionalWriteError" },
1925245Sgblack@eecs.umich.edu    /* PrintReq */
1935245Sgblack@eecs.umich.edu    { SET2(IsRequest, IsPrint), InvalidCmd, "PrintReq" },
1945245Sgblack@eecs.umich.edu    /* Flush Request */
1955245Sgblack@eecs.umich.edu    { SET3(IsRequest, IsFlush, NeedsWritable), InvalidCmd, "FlushReq" },
1965245Sgblack@eecs.umich.edu    /* Invalidation Request */
1978832SAli.Saidi@ARM.com    { SET5(IsInvalidate, IsRequest, NeedsWritable, NeedsResponse, FromCache),
1988832SAli.Saidi@ARM.com      InvalidateResp, "InvalidateReq" },
1998832SAli.Saidi@ARM.com    /* Invalidation Response */
2008832SAli.Saidi@ARM.com    { SET2(IsInvalidate, IsResponse),
2018832SAli.Saidi@ARM.com      InvalidCmd, "InvalidateResp" }
2028832SAli.Saidi@ARM.com};
2035245Sgblack@eecs.umich.edu
2047912Shestness@cs.utexas.edubool
2058832SAli.Saidi@ARM.comPacket::checkFunctional(Printable *obj, Addr addr, bool is_secure, int size,
2069701Sgedare@rtems.org                        uint8_t *_data)
20710654Sandreas.hansson@arm.com{
20812088Sspwilson2@wisc.edu    Addr func_start = getAddr();
2095245Sgblack@eecs.umich.edu    Addr func_end   = getAddr() + getSize() - 1;
2105245Sgblack@eecs.umich.edu    Addr val_start  = addr;
2115245Sgblack@eecs.umich.edu    Addr val_end    = val_start + size - 1;
2125245Sgblack@eecs.umich.edu
2135245Sgblack@eecs.umich.edu    if (is_secure != _isSecure || func_start > val_end ||
214        val_start > func_end) {
215        // no intersection
216        return false;
217    }
218
219    // check print first since it doesn't require data
220    if (isPrint()) {
221        assert(!_data);
222        safe_cast<PrintReqState*>(senderState)->printObj(obj);
223        return false;
224    }
225
226    // we allow the caller to pass NULL to signify the other packet
227    // has no data
228    if (!_data) {
229        return false;
230    }
231
232    // offset of functional request into supplied value (could be
233    // negative if partial overlap)
234    int offset = func_start - val_start;
235
236    if (isRead()) {
237        if (func_start >= val_start && func_end <= val_end) {
238            memcpy(getPtr<uint8_t>(), _data + offset, getSize());
239            if (bytesValid.empty())
240                bytesValid.resize(getSize(), true);
241            // complete overlap, and as the current packet is a read
242            // we are done
243            return true;
244        } else {
245            // Offsets and sizes to copy in case of partial overlap
246            int func_offset;
247            int val_offset;
248            int overlap_size;
249
250            // calculate offsets and copy sizes for the two byte arrays
251            if (val_start < func_start && val_end <= func_end) {
252                // the one we are checking against starts before and
253                // ends before or the same
254                val_offset = func_start - val_start;
255                func_offset = 0;
256                overlap_size = val_end - func_start;
257            } else if (val_start >= func_start && val_end > func_end) {
258                // the one we are checking against starts after or the
259                // same, and ends after
260                val_offset = 0;
261                func_offset = val_start - func_start;
262                overlap_size = func_end - val_start;
263            } else if (val_start >= func_start && val_end <= func_end) {
264                // the one we are checking against is completely
265                // subsumed in the current packet, possibly starting
266                // and ending at the same address
267                val_offset = 0;
268                func_offset = val_start - func_start;
269                overlap_size = size;
270            } else if (val_start < func_start && val_end > func_end) {
271                // the current packet is completely subsumed in the
272                // one we are checking against
273                val_offset = func_start - val_start;
274                func_offset = 0;
275                overlap_size = func_end - func_start;
276            } else {
277                panic("Missed a case for checkFunctional with "
278                      " %s 0x%x size %d, against 0x%x size %d\n",
279                      cmdString(), getAddr(), getSize(), addr, size);
280            }
281
282            // copy partial data into the packet's data array
283            uint8_t *dest = getPtr<uint8_t>() + func_offset;
284            uint8_t *src = _data + val_offset;
285            memcpy(dest, src, overlap_size);
286
287            // initialise the tracking of valid bytes if we have not
288            // used it already
289            if (bytesValid.empty())
290                bytesValid.resize(getSize(), false);
291
292            // track if we are done filling the functional access
293            bool all_bytes_valid = true;
294
295            int i = 0;
296
297            // check up to func_offset
298            for (; all_bytes_valid && i < func_offset; ++i)
299                all_bytes_valid &= bytesValid[i];
300
301            // update the valid bytes
302            for (i = func_offset; i < func_offset + overlap_size; ++i)
303                bytesValid[i] = true;
304
305            // check the bit after the update we just made
306            for (; all_bytes_valid && i < getSize(); ++i)
307                all_bytes_valid &= bytesValid[i];
308
309            return all_bytes_valid;
310        }
311    } else if (isWrite()) {
312        if (offset >= 0) {
313            memcpy(_data + offset, getConstPtr<uint8_t>(),
314                   (min(func_end, val_end) - func_start) + 1);
315        } else {
316            // val_start > func_start
317            memcpy(_data, getConstPtr<uint8_t>() - offset,
318                   (min(func_end, val_end) - val_start) + 1);
319        }
320    } else {
321        panic("Don't know how to handle command %s\n", cmdString());
322    }
323
324    // keep going with request by default
325    return false;
326}
327
328void
329Packet::pushSenderState(Packet::SenderState *sender_state)
330{
331    assert(sender_state != NULL);
332    sender_state->predecessor = senderState;
333    senderState = sender_state;
334}
335
336Packet::SenderState *
337Packet::popSenderState()
338{
339    assert(senderState != NULL);
340    SenderState *sender_state = senderState;
341    senderState = sender_state->predecessor;
342    sender_state->predecessor = NULL;
343    return sender_state;
344}
345
346void
347Packet::print(ostream &o, const int verbosity, const string &prefix) const
348{
349    ccprintf(o, "%s%s [%x:%x]%s%s%s%s", prefix, cmdString(),
350             getAddr(), getAddr() + getSize() - 1,
351             req->isSecure() ? " (s)" : "",
352             req->isInstFetch() ? " IF" : "",
353             req->isUncacheable() ? " UC" : "",
354             isExpressSnoop() ? " ES" : "");
355}
356
357std::string
358Packet::print() const {
359    ostringstream str;
360    print(str);
361    return str.str();
362}
363
364Packet::PrintReqState::PrintReqState(ostream &_os, int _verbosity)
365    : curPrefixPtr(new string("")), os(_os), verbosity(_verbosity)
366{
367    labelStack.push_back(LabelStackEntry("", curPrefixPtr));
368}
369
370Packet::PrintReqState::~PrintReqState()
371{
372    labelStack.pop_back();
373    assert(labelStack.empty());
374    delete curPrefixPtr;
375}
376
377Packet::PrintReqState::
378LabelStackEntry::LabelStackEntry(const string &_label, string *_prefix)
379    : label(_label), prefix(_prefix), labelPrinted(false)
380{
381}
382
383void
384Packet::PrintReqState::pushLabel(const string &lbl, const string &prefix)
385{
386    labelStack.push_back(LabelStackEntry(lbl, curPrefixPtr));
387    curPrefixPtr = new string(*curPrefixPtr);
388    *curPrefixPtr += prefix;
389}
390
391void
392Packet::PrintReqState::popLabel()
393{
394    delete curPrefixPtr;
395    curPrefixPtr = labelStack.back().prefix;
396    labelStack.pop_back();
397    assert(!labelStack.empty());
398}
399
400void
401Packet::PrintReqState::printLabels()
402{
403    if (!labelStack.back().labelPrinted) {
404        LabelStack::iterator i = labelStack.begin();
405        LabelStack::iterator end = labelStack.end();
406        while (i != end) {
407            if (!i->labelPrinted) {
408                ccprintf(os, "%s%s\n", *(i->prefix), i->label);
409                i->labelPrinted = true;
410            }
411            i++;
412        }
413    }
414}
415
416
417void
418Packet::PrintReqState::printObj(Printable *obj)
419{
420    printLabels();
421    obj->print(os, verbosity, curPrefix());
422}
423