189 lines
7.8 KiB
C++
189 lines
7.8 KiB
C++
#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<uint32> *activeIndices;
|
|
Vector<uint32> *activeSizes;
|
|
FastPollingMutexSem *activeMutex;
|
|
volatile bool *anyBreakFlag;
|
|
Vector<uint32> *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<uint32> *activeIndices,
|
|
Vector<uint32> *activeSizes,
|
|
FastPollingMutexSem *activeMutex,
|
|
volatile bool *anyBreakFlag,
|
|
Vector<uint32> *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
|