#ifndef DEBUGSERVICEI_H #define DEBUGSERVICEI_H /** * @file DebugServiceI.h * @brief Abstract interface for the MARTe2 debug/instrumentation service. * * All broker wrappers (DebugBrokerWrapper) depend solely on this interface, * decoupling them from the concrete TCP/UDP implementation. Alternative * transports — TTY, WebSocket, shared-memory ring, etc. — can be plugged in * by providing a new concrete implementation without touching any broker or * application code. * * The interface is split into two logical groups: * * RT-path API * Called from broker threads on every RT cycle. Implementations must * keep these methods as cheap as possible; the only permissible * synchronisation primitive is a fast spinlock (FastPollingMutexSem). * * Control-path API * Called from server / command-handler threads (TCP, TTY, Web …). * Latency here is acceptable; correctness and thread-safety matter. * * Concrete implementations register themselves during Initialise() via * SetInstance(). Broker wrappers retrieve the active instance via * GetInstance(); there is no ORD search on the hot path. */ #include "DebugCore.h" #include "FastPollingMutexSem.h" #include "Object.h" #include "StreamString.h" #include "TypeDescriptor.h" namespace MARTe { // Forward declarations — concrete types are only needed in the implementation. class MemoryMapBroker; struct SignalAlias { StreamString name; uint32 signalIndex; }; struct BrokerInfo { DebugSignalInfo **signalPointers; uint32 numSignals; MemoryMapBroker *broker; volatile bool *anyActiveFlag; Vector *activeIndices; Vector *activeSizes; FastPollingMutexSem *activeMutex; volatile bool *anyBreakFlag; Vector *breakIndices; StreamString gamName; bool isOutput; }; /** * @brief Abstract debug-service interface. */ class DebugServiceI { public: // ------------------------------------------------------------------------- // Static instance registry // ------------------------------------------------------------------------- /** * @brief Return the currently registered debug-service instance, or NULL. * * Called on every broker Init() path and from OutpautPauseAndStep(). * Returns NULL when no debug service has been initialised, in which case * all instrumentation is a no-op. */ static DebugServiceI *GetInstance() { return instance; } /** * @brief Register @p inst as the global debug-service. * * Concrete implementations call this from their Initialise() method. * Passing NULL deregisters the current instance (called from the * destructor so dangling pointers are never visible to broker threads). */ static void SetInstance(DebugServiceI *inst) { instance = inst; } virtual ~DebugServiceI() {} // ========================================================================= // RT-path API (called from broker execute threads every cycle) // ========================================================================= /** * @brief Register a signal memory region with the debug service. * * Called once per signal during broker Init(). Returns a pointer to the * internal DebugSignalInfo that the broker caches for use on the hot path. * Thread-safe; must not be called after the RT loop has started. */ virtual DebugSignalInfo *RegisterSignal(void *memoryAddress, TypeDescriptor type, const char8 *name, uint8 numberOfDimensions = 0, uint32 numberOfElements = 1) = 0; /** * @brief Process one signal on the RT path. * * Applies forced values (memcpy into signal memory) and, when tracing is * enabled and the decimation counter fires, pushes a sample to the trace * ring buffer. Called under the broker's activeMutex; implementations * must not acquire any lock that is also held by the Server thread. */ virtual void ProcessSignal(DebugSignalInfo *signalInfo, uint32 size, uint64 timestamp) = 0; /** * @brief Register a broker so the service can push active/break index * updates to it without iterating every signal. */ virtual void RegisterBroker(DebugSignalInfo **signalPointers, uint32 numSignals, MemoryMapBroker *broker, volatile bool *anyActiveFlag, Vector *activeIndices, Vector *activeSizes, FastPollingMutexSem *activeMutex, volatile bool *anyBreakFlag, Vector *breakIndices, const char8 *gamName = NULL_PTR(const char8 *), bool isOutput = false) = 0; /** @brief Return true if the RT loop is currently held at a pause/breakpoint. */ virtual bool IsPaused() const = 0; /** @brief Set or clear the paused state (called by break-condition logic). */ virtual void SetPaused(bool paused) = 0; /** @brief Return true if a step count is pending (stepRemaining > 0). */ virtual bool IsStepPending() const = 0; /** * @brief Consume one step credit for the current output-broker cycle. * * Called by every output broker after Execute(). No-op when stepRemaining * is zero (the common case); only acquires a mutex when stepping is active. */ virtual void ConsumeStepIfNeeded( const char8 *gamName, const char8 *threadName = NULL_PTR(const char8 *)) = 0; // ========================================================================= // Control-path API (called from server / command-handler threads) // ========================================================================= virtual uint32 ForceSignal (const char8 *name, const char8 *valueStr) = 0; virtual uint32 UnforceSignal(const char8 *name) = 0; virtual uint32 TraceSignal (const char8 *name, bool enable, uint32 decimation = 1) = 0; virtual uint32 SetBreak (const char8 *name, uint8 op, float64 threshold) = 0; virtual uint32 ClearBreak (const char8 *name) = 0; virtual bool IsInstrumented(const char8 *fullPath, bool &traceable, bool &forcable) = 0; virtual uint32 RegisterMonitorSignal(const char8 *path, uint32 periodMs) = 0; virtual uint32 UnmonitorSignal (const char8 *path) = 0; // ========================================================================= // Utility (implementation-agnostic, defined in DebugService.cpp) // ========================================================================= /** * @brief Resolve the fully-qualified ORD path of @p obj into @p fullPath. * * Static so it can be called without a service instance. All concrete * implementations (and InitSignals in DebugBrokerWrapper.h) use this * to build canonical signal names. The definition lives in * DebugService.cpp alongside FindPathInContainer(). */ static bool GetFullObjectName(const Object &obj, StreamString &fullPath); protected: /** Pointer to the single active debug-service instance. */ static DebugServiceI *instance; }; } // namespace MARTe #endif // DEBUGSERVICEI_H