GTraceTypes.hpp   [plain text]


//
//  GTraceTypes.hpp
//  IOGraphics
//
//  Created by Jeremy Tran on 8/8/17.
//  Rewritten by Godfrey van der Linden on 2018-08-17
//  Shared file between kernel and user land
//

#ifndef GTraceTypes_hpp
#define GTraceTypes_hpp

#include <stdint.h>
#include <os/base.h>

#include <string.h>

#ifndef GTRACE_ARCHAIC_CPP
#define GTRACE_ARCHAIC_CPP (__cplusplus < 201103L)
#endif

#define GTRACE_REVISION         0x2

#define kGTraceMaximumBufferCount 32

#pragma mark - Masks
#define kGTRACE_COMPONENT_MASK      0x00000000FFFFFFFFULL       // 32 bits
#define kTHREAD_ID_MASK             0x0000000000FFFFFFULL       // 24 bits
#define kGTRACE_REGISTRYID_MASK     0x00000000FFFFFFFFULL       // 32 bits


// Argument Tag Bits, might be better as a 16 value(4 bit) field, but for the
// time being lets not confuse the binary interpretation of existing data.
#define kGTRACE_ARGUMENT_Reserved1   0x1000  // Future Use
#define kGTRACE_ARGUMENT_STRING      0x2000  // Argument string may be swapped to Host byte order
#define kGTRACE_ARGUMENT_BESTRING    0x4000  // Argument string is in big endian byte order
#define kGTRACE_ARGUMENT_POINTER     0x8000  // Arguments tagged with this bit are pointers and will be obfuscated when copied/printed. Must not be used in the first ARG position

#define kGTRACE_ARGUMENT_STRING_MASK (kGTRACE_ARGUMENT_STRING | kGTRACE_ARGUMENT_BESTRING)
#define kGTRACE_ARGUMENT_MASK        0xF000

// Helpers
#define MAKEGTRACETAG(x) static_cast<uint16_t>(x)
#define MAKEGTRACEARG(x) static_cast<uint64_t>(x)

#define GMUL8(v)                     ((v) << 3)
#define GMUL16(v)                    ((v) << 4)
#define GMUL32(v)                    ((v) << 5)
#define GPACKNODATA                                         0                           // 0 is a valid value, NoData used to distinguish
#define GPACKBITS(shift, value) \
    (MAKEGTRACEARG(value) & ((1ULL << (shift)) - 1))

/* GPACKUINT8T
 * value: uint8_t value
 * ui8idx: index of uint8_t in uint64_t result; valid range: [0, 7] */
#define GPACKUINT8T(ui8idx,   value) \
    (MAKEGTRACEARG(value & 0x000000ff) << GMUL8(ui8idx))
#define GUNPACKUINT8T(ui8idx, value) \
    static_cast<uint8_t>(MAKEGTRACEARG(value) >> GMUL8(ui8idx))

/* GPACKUINT16T
 * value: uint16_t value
 * ui16idx: index of uint16_t in uint64_t result; valid range: [0, 3] */
#define GPACKUINT16T(ui16idx, value) \
    (MAKEGTRACEARG(value & 0x0000ffff) << GMUL16(ui16idx))
#define GUNPACKUINT16T(ui16idx, value) \
    static_cast<uint16_t>(MAKEGTRACEARG(value) >> GMUL16(ui16idx))

/* GPACKUINT32T
 * value: uint32_t value
 * ui8idx: index of uint32_t in uint64_t result; valid range: [0, 1] */
#define GPACKUINT32T(ui32idx, value) \
    (MAKEGTRACEARG(value & 0xffffffff) << GMUL32(ui32idx))
#define GUNPACKUINT32T(ui32idx, value) \
    static_cast<uint32_t>(MAKEGTRACEARG(value) >> GMUL32(ui32idx))

#define GPACKUINT64T(value)    (value)
#define GUNPACKUINT64T(value)  (value)

#define GPACKSTRINGCAST(ia)  reinterpret_cast<char *>(ia)
#define GPACKSTRING(ia, str) strlcpy(GPACKSTRINGCAST(ia), str, sizeof(ia))

// User client commands
OS_ENUM(gtrace_command, uint64_t,
    kGTraceCmdFetch = 'Ftch',
);

// These structures must be power of 2 for performance, alignment, and the
// buffer-to-line calculations
#pragma pack(push, 1)

struct GTraceEntry {
    // Internal structures
    struct ID {
        uint64_t fID;

#if !GTRACE_ARCHAIC_CPP
        ID(uint64_t id) : fID(id) {}
        ID(uint64_t line, uint64_t component)
            : fID((line & 0xffff) | (component << 16)) {}
#endif

        uint64_t id() const        { return fID; }
        uint16_t line() const      { return static_cast<uint16_t>(fID); }
        uint64_t component() const { return static_cast<uint64_t>(fID) >> 16; }
    };

    struct ThreadInfo {
        uint64_t fTi;

#if !GTRACE_ARCHAIC_CPP
        ThreadInfo(uint64_t cpu, uint64_t threadID, uint64_t registryID)
            : fTi( (cpu & 0xff) | ((threadID & 0xffffff) << 8)
                 | ((registryID & 0xffffffff) << 32)) {}
#endif

        uint8_t  cpu() const        { return static_cast<uint8_t>(fTi); }
        uint32_t threadID() const   { return static_cast<uint32_t>(fTi) >> 8; }
        uint32_t registryID() const { return static_cast<uint32_t>(fTi >> 32); }
    };

    struct ArgsTag {
        static const int kNum = 4;
        union {
            uint16_t    fTarg[kNum];
            uint64_t    fTag64;
        };

#if !GTRACE_ARCHAIC_CPP
        ArgsTag(uint64_t tag) : fTag64{tag} {}
        ArgsTag(uint16_t tag0, uint16_t tag1, uint16_t tag2, uint16_t tag3)
        {
            fTarg[0] = tag0; fTarg[1] = tag1; fTarg[2] = tag2; fTarg[3] = tag3;
        }
#endif

        uint64_t tag() const                     { return fTag64;     }
        const uint16_t& tag(const int idx) const { return fTarg[idx]; }
        uint16_t& tag(const int idx)             { return fTarg[idx]; }
    };

    struct Args {
        union {
            uint64_t    fU64s[4];
            uint32_t    fU32s[8];
            uint16_t    fU16s[16];
            uint8_t     fU8s[32];
            char        fStr[32];
        };

#if !GTRACE_ARCHAIC_CPP
        Args(uint64_t arg0, uint64_t arg1, uint64_t arg2, uint64_t arg3)
        {
            fU64s[0] = arg0; fU64s[1] = arg1; fU64s[2] = arg2; fU64s[3] = arg3;
        }
#endif

        const uint64_t& u64(const int idx) const { return fU64s[idx]; }
        const uint32_t& u32(const int idx) const { return fU32s[idx]; }
        const uint16_t& u16(const int idx) const { return fU16s[idx]; }
        const uint8_t&  u8(const int idx)  const { return fU8s[idx];  }
        const char* str()                  const { return fStr; }
        uint64_t& u64(const int idx)             { return fU64s[idx]; }
        uint32_t& u32(const int idx)             { return fU32s[idx]; }
        uint16_t& u16(const int idx)             { return fU16s[idx]; }
        uint8_t&  u8(const int idx)              { return fU8s[idx];  }
        char* str()                              { return fStr; }
    };

    // GTraceEntry data
    union {
        struct {
            uint64_t   fTimestamp;      // mach continuous time
            ID         fID;             // unique ID to entry
            ThreadInfo fThreadInfo;     // CPU, thread info
            ArgsTag    fArgsTag;        // Argument tags
            Args       fArgs;           // Argument data
        };
#if !GTRACE_ARCHAIC_CPP
        uint64_t       fEntry[8] = { 0 };
#else
        uint64_t       fEntry[8];
#endif
    };

#if !GTRACE_ARCHAIC_CPP
    GTraceEntry() {}

    // Special entry used as the first entry in a binary GTrace file
    // If the timestamp/fEntry[0] == -1, then fEntry[1] contains a count of the
    // number of buffers in this file.  The buffers themselves are self
    // describing and contain version information.
    GTraceEntry(const int16_t numBuffers, const uint64_t gtraceVersion)
    {
        memset(&fEntry[0], -1, sizeof(fEntry));
        fID = ID(numBuffers, gtraceVersion);
    }

    // Buffer bound Entry, inserted during sorting for output
    GTraceEntry(const uint64_t timestamp, const uint64_t component,
                const uint16_t funcid)
    {
        fTimestamp = timestamp;
        fID = ID{0, component};
        fArgsTag.fTarg[0] = funcid;
    }

    GTraceEntry(const uint64_t timestamp,   const uint16_t line,
                const uint64_t componentID, const uint8_t  cpu,
                const uint64_t tID,         const uint32_t regID,
                const uint16_t tag1,        const uint64_t arg1,
                const uint16_t tag2,        const uint64_t arg2,
                const uint16_t tag3,        const uint64_t arg3,
                const uint16_t tag4,        const uint64_t arg4)
        : fTimestamp{timestamp}, fID{line, componentID},
          fThreadInfo{cpu, tID, regID},
          fArgsTag{tag1, tag2, tag3, tag4}, fArgs{arg1, arg2, arg3, arg4} {}
#endif // !GTRACE_ARCHAIC_CPP

    GTraceEntry(const uint64_t timestamp,   const uint16_t line,
                const uint64_t componentID, const uint8_t  cpu,
                const uint64_t tID,         const uint32_t regID,
                const uint64_t tags,        const uint64_t arg1,
                const uint64_t arg2,        const uint64_t arg3,
                const uint64_t arg4)
        : fTimestamp{timestamp}, fID{line, componentID},
          fThreadInfo{cpu, tID, regID},
          fArgsTag{tags}, fArgs{arg1, arg2, arg3, arg4} {}

    // Copy constructor and assignment
    GTraceEntry(const GTraceEntry& other)
        { memcpy(&fEntry[0], &other.fEntry[0], sizeof(fEntry)); }
    GTraceEntry& operator=(const GTraceEntry& other)
    {
        memcpy(&fEntry[0], &other.fEntry[0], sizeof(fEntry));
        return *this;
    }

    // Accessors
    uint64_t timestamp() const          { return fTimestamp;               }
    uint64_t id() const                 { return fID.id();                 }
    uint16_t line() const               { return fID.line();               }
    uint64_t component() const          { return fID.component();          }
    uint8_t  cpu() const                { return fThreadInfo.cpu();        }
    uint32_t threadID() const           { return fThreadInfo.threadID();   }
    uint32_t registryID() const         { return fThreadInfo.registryID(); }
    uint64_t tag() const                { return fArgsTag.tag(); }
    const uint16_t& tag(const int idx) const   { return fArgsTag.tag(idx); }
    const uint64_t& arg64(const int idx) const { return fArgs.u64(idx);    }
    const uint32_t& arg32(const int idx) const { return fArgs.u32(idx);    }
    const uint16_t& arg16(const int idx) const { return fArgs.u16(idx);    }
    const uint8_t & arg8(const int idx)  const { return fArgs.u8(idx);     }
    uint16_t& tag(const int idx)   { return fArgsTag.tag(idx); }
    uint64_t& arg64(const int idx) { return fArgs.u64(idx);    }
    uint32_t& arg32(const int idx) { return fArgs.u32(idx);    }
    uint16_t& arg16(const int idx) { return fArgs.u16(idx);    }
    uint8_t & arg8(const int idx)  { return fArgs.u8(idx);     }

    // For sorting, timestamp, ID, CPUThreadID as uint64_t
    friend bool operator<(const GTraceEntry& lhs, const GTraceEntry& rhs);
};
#define kGTraceEntrySize static_cast<int>(sizeof(GTraceEntry))

struct GTraceHeader
{                                // [ind] uint64_ts
    uint64_t fDecoderName[4];    //   0   32 byte decoder module name
    uint64_t fBufferName[4];     //   4   32 byte buffer name
    uint64_t fCreationTime;      //   8   creation mach continuous time
    uint32_t fBufferID;          //   9   Unique ID of buffer
    uint32_t fBufferSize;        //   9   Bytes, buffer size including header
    uint32_t fTokenLine;         //  10   Current out token
    uint16_t fVersion;           //  10   GTrace version
    uint16_t fBufferIndex;       //  10   Index of buffer in system
    uint16_t fTokensMask;        //  11   Mask from fTokenLine to index
    uint16_t fBreadcrumbTokens;  //  11   Size in tokens of breadcrumb
    uint16_t fTokensCopied;      //  11   Number of fTokens copied
};

// Note every section is a multiple of sizeof(GTraceEntry), i.e. 64, bytes.
#define kGTraceHeaderEntries 2
#define kGTraceHeaderSize (kGTraceHeaderEntries * kGTraceEntrySize)
struct IOGTraceBuffer
{
    union {
        GTraceHeader fHeader;
        GTraceEntry _padding[kGTraceHeaderEntries];
    };
    // Breadcrumb, fTokens[0]–fTokens[fHeader.fBreadcrumbTokens] inclusive
    // Tokens, fTokens[fHeader.fBreadcrumbTokens]–fTokens[fHeader.fTokensCopied]
    GTraceEntry fTokens[];
};

#pragma pack(pop)

// May need to redesign if this assumption changes, test for it
static_assert(sizeof(GTraceHeader) <= kGTraceHeaderSize,
    "header doesnt fit in two entries, change union to preserve alignment");
static_assert(kGTraceEntrySize == 8 * sizeof(uint64_t),
    "GTraceEntry != 64 bytes");

#if !KERNEL
#include <vector>
#include <utility> // for std::pair<T1, T2>
class GTraceBuffer
{
public:
    using vector_type = std::vector<GTraceEntry>;
    using vector_iter_type = vector_type::iterator;
    using vector_citer_type = vector_type::const_iterator;

    using breadcrumb_type = std::pair<void*, size_t>;
    using tokens_type = std::pair<vector_iter_type, vector_iter_type>;
    using ctokens_type = std::pair<vector_citer_type, vector_citer_type>;

    explicit GTraceBuffer(vector_type&& entries)
        : fData(std::move(entries))
    {}
    GTraceBuffer(vector_citer_type begin, vector_citer_type end)
        : fData(vector_type(begin, end))
    {}

    // Move constructor and assignment
    GTraceBuffer(GTraceBuffer&& other)
        : fData(std::move(other.fData))
    {}
    GTraceBuffer& operator=(GTraceBuffer&& other)
    {
        if (this != &other)
            fData = std::move(other.fData);
        return *this;
    }

    // copy constructor and assignment
    GTraceBuffer(const GTraceBuffer& other)            = delete;
    GTraceBuffer& operator=(const GTraceBuffer& other) = delete;

    // Accessors
    GTraceHeader& header()
        { return *reinterpret_cast<GTraceHeader*>(fData.data()); }
    const GTraceHeader& header() const
        { return *reinterpret_cast<const GTraceHeader*>(fData.data()); }

    breadcrumb_type breadcrumb()
    {
        const size_t bcl = header().fBreadcrumbTokens;
        auto& firstBCEntry = fData.at(2);
        void* bcP = reinterpret_cast<void*>(&firstBCEntry);
        return breadcrumb_type{(bcl ?  bcP : nullptr), bcl};
    }
    tokens_type tokens()
    {
        tokens_type ret{fData.begin(), fData.begin()};  // Zero length
        if (header().fTokensCopied) {
            const uint32_t& bct = header().fBreadcrumbTokens;
            ret = tokens_type{fData.begin() + 2 + bct, fData.end()};
        }
        return ret;
    }
    ctokens_type ctokens() const
    {
        ctokens_type ret{fData.cend(), fData.cend()};  // Zero length
        if (header().fTokensCopied) {
            const uint32_t& bct = header().fBreadcrumbTokens;
            ret = ctokens_type{fData.cbegin() + 2 + bct, fData.cend()};
        }
        return ret;
    }
    const vector_type& vec() const      { return fData; }
    const GTraceEntry* data() const     { return fData.data(); }
    vector_type::size_type size() const { return fData.size(); }

    void removeTokens()
    {
        ctokens_type ctokenpair = ctokens();
        fData.erase(ctokenpair.first, ctokenpair.second);
        fData.shrink_to_fit();
    }

private:
    std::vector<GTraceEntry> fData;
};
#endif // !KERNEL
#endif /* GTraceTypes_hpp */