#pragma once // Step 566: Allocation/Ownership Trace Hooks #include "DebugValidationUtil.h" #include #include #include #include #include enum class AllocationTraceEventType { Alloc, Transfer, Free }; struct AllocationTraceEvent { std::string eventId; std::string sessionId; std::string allocationId; AllocationTraceEventType type = AllocationTraceEventType::Alloc; std::uint64_t timestamp = 0; std::uint64_t address = 0; std::uint64_t sizeBytes = 0; std::string ownerFrom; std::string ownerTo; }; class AllocationOwnershipTraceHooks { public: bool recordAllocation(const std::string& sessionId, const std::string& allocationId, std::uint64_t address, std::uint64_t sizeBytes, const std::string& owner, std::uint64_t timestamp, std::string* error) { if (!error) return false; error->clear(); if (!hasRequiredDebugIds(allocationId, sessionId)) return fail(error, "allocation_or_session_missing"); if (!isNonZeroU64(address)) return fail(error, "allocation_address_invalid"); if (!isNonZeroU64(sizeBytes)) return fail(error, "allocation_size_invalid"); if (owner.empty()) return fail(error, "allocation_owner_missing"); if (activeOwners_.count(allocationId) != 0) return fail(error, "allocation_id_active_duplicate"); activeOwners_[allocationId] = owner; AllocationTraceEvent event; event.eventId = nextEventId(); event.sessionId = sessionId; event.allocationId = allocationId; event.type = AllocationTraceEventType::Alloc; event.timestamp = timestamp; event.address = address; event.sizeBytes = sizeBytes; event.ownerTo = owner; appendEvent(event); return true; } bool transferOwnership(const std::string& sessionId, const std::string& allocationId, const std::string& newOwner, std::uint64_t timestamp, std::string* error) { if (!error) return false; error->clear(); if (!hasRequiredDebugIds(allocationId, sessionId)) return fail(error, "allocation_or_session_missing"); if (newOwner.empty()) return fail(error, "transfer_owner_missing"); auto it = activeOwners_.find(allocationId); if (it == activeOwners_.end()) return fail(error, "allocation_not_active"); if (it->second == newOwner) return fail(error, "transfer_owner_unchanged"); AllocationTraceEvent event; event.eventId = nextEventId(); event.sessionId = sessionId; event.allocationId = allocationId; event.type = AllocationTraceEventType::Transfer; event.timestamp = timestamp; event.ownerFrom = it->second; event.ownerTo = newOwner; it->second = newOwner; appendEvent(event); return true; } bool recordFree(const std::string& sessionId, const std::string& allocationId, std::uint64_t timestamp, std::string* error) { if (!error) return false; error->clear(); if (!hasRequiredDebugIds(allocationId, sessionId)) return fail(error, "allocation_or_session_missing"); auto it = activeOwners_.find(allocationId); if (it == activeOwners_.end()) return fail(error, "allocation_not_active"); AllocationTraceEvent event; event.eventId = nextEventId(); event.sessionId = sessionId; event.allocationId = allocationId; event.type = AllocationTraceEventType::Free; event.timestamp = timestamp; event.ownerFrom = it->second; activeOwners_.erase(it); appendEvent(event); return true; } std::string ownerFor(const std::string& allocationId) const { auto it = activeOwners_.find(allocationId); if (it == activeOwners_.end()) return ""; return it->second; } std::size_t activeAllocationCount() const { return activeOwners_.size(); } std::vector timelineForSession(const std::string& sessionId) const { std::vector filtered; for (const auto& event : events_) { if (sessionId.empty() || event.sessionId == sessionId) filtered.push_back(event); } return filtered; } private: std::vector events_; std::map activeOwners_; std::uint64_t nextId_ = 1; static bool fail(std::string* error, const char* code) { *error = code; return false; } std::string nextEventId() { return "alloc-event-" + std::to_string(nextId_++); } void appendEvent(const AllocationTraceEvent& event) { events_.push_back(event); std::stable_sort(events_.begin(), events_.end(), [](const AllocationTraceEvent& a, const AllocationTraceEvent& b) { if (a.timestamp != b.timestamp) return a.timestamp < b.timestamp; return a.eventId < b.eventId; }); } };