]> git.saurik.com Git - apple/system_cmds.git/blobdiff - KDBG/MachineVoucher.hpp
system_cmds-643.30.1.tar.gz
[apple/system_cmds.git] / KDBG / MachineVoucher.hpp
diff --git a/KDBG/MachineVoucher.hpp b/KDBG/MachineVoucher.hpp
new file mode 100644 (file)
index 0000000..6660b0b
--- /dev/null
@@ -0,0 +1,174 @@
+//
+//  MachineVoucher.hpp
+//  KDBG
+//
+//  Created by James McIlree on 2/18/14.
+//  Copyright (c) 2014 Apple. All rights reserved.
+//
+
+#ifndef kernel_perf_cmds_MachineVoucher_hpp
+#define kernel_perf_cmds_MachineVoucher_hpp
+
+template <typename SIZE> class Machine;
+
+enum class kMachineVoucherFlag : std::uint32_t {
+        CreatedByVoucherCreate         = 0x00000001,
+       CreatedByFirstUse               = 0x00000002,
+       CreatedByPreviousMachineState   = 0x00000004,
+       IsNullVoucher                   = 0x00000008,
+       IsUnsetVoucher                  = 0x00000010,
+       IsDestroyed                     = 0x00000020
+};
+
+template <typename SIZE>
+class MachineVoucher {
+    protected:
+       AbsInterval                     _timespan;
+       uint8_t*                        _content_bytes;
+       uint32_t                        _content_bytes_size;
+       uint32_t                        _content_bytes_capacity;
+       uint32_t                        _id;
+       uint32_t                        _flags;
+       typename SIZE::ptr_t            _address;
+
+       static uint32_t voucher_id();
+
+       // Voucher pointers are used as unique identifiers for the lifespan
+       // of the voucher, which may exceed the lifespan of the Machine.
+       // We may not copy or "move" a voucher.
+
+       // Disable copy operators
+       MachineVoucher(const MachineVoucher& ignored) = delete;
+       MachineVoucher& operator=(const MachineVoucher& ignored) = delete;
+
+       // Disable move operators
+       MachineVoucher(MachineVoucher&& ignored) = delete;
+       MachineVoucher& operator=(MachineVoucher&& ignored) = delete;
+
+       friend class Machine<SIZE>;
+
+       void workaround_16898190(kMachineVoucherFlag flags, uint32_t content_bytes_capacity);
+
+       void add_content_bytes(uint8_t* bytes);
+
+       void set_destroyed(AbsTime timestamp);
+
+        // These are needed to make vouchers that are still alive at the
+        // end of an event trace appear correctly in searches/queries.
+        // However, when forwarding live vouchers to a future machine state,
+        // that work must be undone.
+       void set_timespan_to_end_of_time() {
+               ASSERT(is_live(), "Modifying timespan of destroyed voucher");
+               ASSERT(_timespan.length() == 0, "Modifying timespan after it has already been set");
+               _timespan.set_length(AbsTime(UINT64_MAX) - _timespan.location());
+       }
+
+       void set_timespan_to_zero_length() {
+               ASSERT(is_live(), "Modifying timespan of destroyed voucher");
+               ASSERT(_timespan.max() == UINT64_MAX, "Modifying timespan after it has already been set");
+               _timespan.set_length(AbsTime(0));
+       }
+       
+    public:
+       MachineVoucher(typename SIZE::ptr_t address, AbsInterval create_timespan, kMachineVoucherFlag flags, uint32_t content_bytes_capacity);
+
+       ~MachineVoucher() {
+               if (_content_bytes) {
+                       free(_content_bytes);
+                       _content_bytes = nullptr;
+               }
+       }
+
+       bool operator==(const MachineVoucher& rhs) const        { return this->_id == rhs._id; }
+       bool operator!=(const MachineVoucher& rhs) const        { return !(*this == rhs); }
+
+       bool is_live() const                                    { return (_flags & (uint32_t)kMachineVoucherFlag::IsDestroyed) == 0; }
+       bool is_destroyed() const                               { return (_flags & (uint32_t)kMachineVoucherFlag::IsDestroyed) > 0; }
+       bool is_null() const                                    { return (_flags & (uint32_t)kMachineVoucherFlag::IsNullVoucher) > 0; }
+       bool is_unset() const                                   { return (_flags & (uint32_t)kMachineVoucherFlag::IsUnsetVoucher) > 0; }
+       bool is_created_by_voucher_create() const               { return (_flags & (uint32_t)kMachineVoucherFlag::CreatedByVoucherCreate) > 0; }
+       bool is_created_by_first_use() const                    { return (_flags & (uint32_t)kMachineVoucherFlag::CreatedByFirstUse) > 0; }
+       bool is_created_by_previous_machine_state() const       { return (_flags & (uint32_t)kMachineVoucherFlag::CreatedByPreviousMachineState) > 0; }
+       bool has_valid_contents() const                         { return _content_bytes_size > 0 && _content_bytes_size == _content_bytes_capacity; }
+
+       typename SIZE::ptr_t address() const                    { return _address; }
+        AbsInterval timespan() const                           { return _timespan; }
+       const uint8_t* content_bytes() const                    { return _content_bytes; }
+       uint32_t content_size() const                           { return _content_bytes_capacity; }
+        uint32_t id() const                                    { return _id; }
+};
+
+template <typename SIZE>
+uint32_t MachineVoucher<SIZE>::voucher_id() {
+       static uint32_t voucher_id = 1;
+       return OSAtomicIncrement32Barrier((volatile int32_t*)&voucher_id);
+}
+
+template <typename SIZE>
+MachineVoucher<SIZE>::MachineVoucher(typename SIZE::ptr_t address, AbsInterval timespan, kMachineVoucherFlag flags, uint32_t content_bytes_capacity) :
+       _timespan(timespan),
+       _content_bytes((content_bytes_capacity > 0) ? (uint8_t*)malloc((size_t)content_bytes_capacity) : nullptr),
+       _content_bytes_size(0),
+       _content_bytes_capacity(content_bytes_capacity),
+       _id(voucher_id()),
+       _flags((uint32_t)flags),
+       _address(address)
+{
+       DEBUG_ONLY({
+               if (!is_null() && !is_unset()) {
+                       ASSERT(timespan.location() != 0 || is_created_by_first_use(), "Only implicitly created vouchers should have an unknown (0) create time");
+                       ASSERT(is_created_by_voucher_create() || is_created_by_first_use() , "Should have a create flag");
+                       ASSERT(content_bytes_capacity == 0 || is_created_by_voucher_create(), "Implicitly created vouchers should not have content");
+               }
+       })
+}
+
+template <typename SIZE>
+void MachineVoucher<SIZE>::workaround_16898190(kMachineVoucherFlag flags, uint32_t content_bytes_capacity) {
+       ASSERT(_content_bytes_capacity == 0, "Attempting to reset non-zero content_bytes_capacity");
+       ASSERT(!is_null(), "Sanity");
+       ASSERT(!is_unset(), "Sanity");
+       ASSERT(is_live(), "Should be live"); // This may be too strong, some races could have destroy before create
+
+       _flags |= (uint32_t)flags;
+       _content_bytes_capacity = content_bytes_capacity;
+}
+
+template <typename SIZE>
+void MachineVoucher<SIZE>::add_content_bytes(uint8_t* src) {
+       ASSERT(src, "Sanity");
+
+       // If the first reference we see to a voucher is an MACH_IPC_VOUCHER_CREATE_ATTR_DATA,
+       // we will not have contents.
+       if (!is_created_by_first_use()) {
+               size_t bytes_remaining = _content_bytes_capacity - _content_bytes_size;
+               ASSERT(bytes_remaining > 0, "Sanity");
+
+               // We either write an entire tracepoint worth of data,
+               // or the # of bytes remaining.
+               size_t bytes_to_write = std::min(bytes_remaining, sizeof(typename SIZE::ptr_t) * 4);
+               auto dest = &_content_bytes[_content_bytes_size];
+               memcpy(dest, src, bytes_to_write);
+               _content_bytes_size += bytes_to_write;
+       }
+}
+
+template <typename SIZE>
+void MachineVoucher<SIZE>::set_destroyed(AbsTime timestamp) {
+       ASSERT(!is_destroyed(), "Sanity");
+       ASSERT(timestamp > _timespan.location(), "Sanity");
+       ASSERT(_timespan.length() == 0, "Sanity");
+       
+        // It turns out this is too strong. The kernel has a limited amount of buffer space available
+        // to hold the voucher contents. If the voucher exceeds that, no contents are emitted, and we
+        // fail this assert.
+        // ASSERT(_content_bytes_capacity == _content_bytes_size, "Destroying voucher with incomplete contents");
+       
+       // +1 to make sure searches for this voucher at the destroy timestamp
+       // can find it.
+       _timespan.set_length((timestamp - _timespan.location()) + AbsTime(1));
+       _flags |= (uint32_t)kMachineVoucherFlag::IsDestroyed;
+}
+
+
+#endif