Implemented common base for debug services
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -3,293 +3,104 @@
|
||||
|
||||
#include "BasicTCPSocket.h"
|
||||
#include "BasicUDPSocket.h"
|
||||
#include "ConfigurationDatabase.h"
|
||||
#include "DebugServiceI.h"
|
||||
#include "DebugServiceBase.h"
|
||||
#include "EmbeddedServiceMethodBinderI.h"
|
||||
#include "MessageI.h"
|
||||
#include "ReferenceContainer.h"
|
||||
#include "ReferenceT.h"
|
||||
#include "SingleThreadService.h"
|
||||
|
||||
namespace MARTe {
|
||||
|
||||
class DataSourceI;
|
||||
|
||||
|
||||
/**
|
||||
* @brief TCP/UDP implementation of DebugServiceI.
|
||||
* @brief TCP/UDP implementation of DebugServiceI (via DebugServiceBase).
|
||||
*
|
||||
* Provides signal tracing (UDP), forced-value injection, break conditions,
|
||||
* and a text command channel (TCP) with a log-forwarding sidecar (TcpLogger).
|
||||
* Alternative transports (TTY, WebSocket, …) can be added by implementing
|
||||
* DebugServiceI without touching this class or any broker wrapper code.
|
||||
* All signal management and command dispatch logic lives in DebugServiceBase.
|
||||
*/
|
||||
class DebugService : public ReferenceContainer,
|
||||
class DebugService : public DebugServiceBase,
|
||||
public MessageI,
|
||||
public EmbeddedServiceMethodBinderI,
|
||||
public DebugServiceI {
|
||||
public EmbeddedServiceMethodBinderI {
|
||||
public:
|
||||
friend class DebugServiceTest;
|
||||
CLASS_REGISTER_DECLARATION()
|
||||
friend class DebugServiceTest;
|
||||
CLASS_REGISTER_DECLARATION()
|
||||
|
||||
DebugService();
|
||||
virtual ~DebugService();
|
||||
DebugService();
|
||||
virtual ~DebugService();
|
||||
|
||||
virtual bool Initialise(StructuredDataI &data);
|
||||
virtual bool Initialise(StructuredDataI &data);
|
||||
|
||||
// DebugServiceI RT-path overrides
|
||||
virtual DebugSignalInfo *RegisterSignal(void *memoryAddress, TypeDescriptor type,
|
||||
const char8 *name,
|
||||
uint8 numberOfDimensions = 0,
|
||||
uint32 numberOfElements = 1);
|
||||
virtual void ProcessSignal(DebugSignalInfo *signalInfo, uint32 size,
|
||||
uint64 timestamp);
|
||||
virtual void RegisterBroker(DebugSignalInfo **signalPointers, uint32 numSignals,
|
||||
MemoryMapBroker *broker, volatile bool *anyActiveFlag,
|
||||
Vec<uint32> *activeIndices, Vec<uint32> *activeSizes,
|
||||
FastPollingMutexSem *activeMutex,
|
||||
volatile bool *anyBreakFlag, Vec<uint32> *breakIndices,
|
||||
const char8 *gamName = NULL_PTR(const char8 *),
|
||||
bool isOutput = false);
|
||||
virtual bool IsPaused() const { return isPaused; }
|
||||
virtual void SetPaused(bool paused) { isPaused = paused; }
|
||||
virtual bool IsStepPending() const { return stepRemaining > 0u; }
|
||||
virtual void ConsumeStepIfNeeded(const char8 *gamName,
|
||||
const char8 *threadName = NULL_PTR(const char8 *));
|
||||
virtual ErrorManagement::ErrorType Execute(ExecutionInfo &info);
|
||||
virtual ErrorManagement::ErrorType HandleMessage(ReferenceT<Message> &data);
|
||||
|
||||
// DebugServiceI control-path overrides
|
||||
virtual uint32 ForceSignal (const char8 *name, const char8 *valueStr);
|
||||
virtual uint32 UnforceSignal(const char8 *name);
|
||||
virtual uint32 TraceSignal (const char8 *name, bool enable, uint32 decimation = 1);
|
||||
virtual uint32 SetBreak (const char8 *name, uint8 op, float64 threshold);
|
||||
virtual uint32 ClearBreak (const char8 *name);
|
||||
virtual bool IsInstrumented(const char8 *fullPath, bool &traceable, bool &forcable);
|
||||
virtual uint32 RegisterMonitorSignal(const char8 *path, uint32 periodMs);
|
||||
virtual uint32 UnmonitorSignal (const char8 *path);
|
||||
|
||||
virtual ErrorManagement::ErrorType Execute(ExecutionInfo &info);
|
||||
virtual ErrorManagement::ErrorType HandleMessage(ReferenceT<Message> &data);
|
||||
|
||||
// TCP-transport-specific methods (not part of DebugServiceI)
|
||||
void GetStepStatus(BasicTCPSocket *client);
|
||||
void Step(uint32 n, const char8 *threadName = NULL_PTR(const char8 *));
|
||||
void GetSignalValue(const char8 *name, BasicTCPSocket *client);
|
||||
void Discover(BasicTCPSocket *client);
|
||||
void InfoNode(const char8 *path, BasicTCPSocket *client);
|
||||
void ListNodes(const char8 *path, BasicTCPSocket *client);
|
||||
void ServeConfig(BasicTCPSocket *client);
|
||||
void SetFullConfig(ConfigurationDatabase &config);
|
||||
void RebuildConfigFromRegistry();
|
||||
|
||||
struct MonitoredSignal {
|
||||
ReferenceT<DataSourceI> dataSource;
|
||||
uint32 signalIdx;
|
||||
uint32 internalID;
|
||||
uint32 periodMs;
|
||||
uint64 lastPollTime;
|
||||
uint32 size;
|
||||
StreamString path;
|
||||
};
|
||||
protected:
|
||||
// Transport-specific overrides of DebugServiceBase hooks
|
||||
virtual void GetServiceInfo(StreamString &out);
|
||||
virtual void RebuildTransportConfig();
|
||||
|
||||
private:
|
||||
void HandleCommand(StreamString cmd, BasicTCPSocket *client);
|
||||
void UpdateBrokersActiveStatus();
|
||||
void UpdateBrokersBreakStatus();
|
||||
void InjectTcpLoggerIfNeeded();
|
||||
void InjectTcpLoggerIfNeeded();
|
||||
|
||||
uint32 ExportTree(ReferenceContainer *container, StreamString &json, const char8 *pathPrefix);
|
||||
void PatchRegistry();
|
||||
ErrorManagement::ErrorType Server (ExecutionInfo &info);
|
||||
ErrorManagement::ErrorType Streamer(ExecutionInfo &info);
|
||||
|
||||
void EnrichWithConfig(const char8 *path, StreamString &json);
|
||||
static void JsonifyDatabase(ConfigurationDatabase &db, StreamString &json);
|
||||
// TCP/UDP-specific configuration
|
||||
uint16 controlPort;
|
||||
uint16 streamPort;
|
||||
uint16 logPort;
|
||||
StreamString streamIP;
|
||||
bool isServer;
|
||||
bool suppressTimeoutLogs;
|
||||
|
||||
ErrorManagement::ErrorType Server(ExecutionInfo &info);
|
||||
ErrorManagement::ErrorType Streamer(ExecutionInfo &info);
|
||||
BasicTCPSocket tcpServer;
|
||||
BasicUDPSocket udpSocket;
|
||||
|
||||
uint16 controlPort;
|
||||
uint16 streamPort;
|
||||
uint16 logPort;
|
||||
StreamString streamIP;
|
||||
bool isServer;
|
||||
bool suppressTimeoutLogs;
|
||||
volatile bool isPaused;
|
||||
volatile uint32 stepRemaining;
|
||||
StreamString pausedAtGam;
|
||||
StreamString stepThreadFilter; // empty = all threads; non-empty = only this OS thread name
|
||||
class ServiceBinder : public EmbeddedServiceMethodBinderI {
|
||||
public:
|
||||
enum ServiceType { ServerType, StreamerType };
|
||||
ServiceBinder(DebugService *parent, ServiceType type)
|
||||
: parent(parent), type(type) {}
|
||||
virtual ErrorManagement::ErrorType Execute(ExecutionInfo &info) {
|
||||
if (type == StreamerType) return parent->Streamer(info);
|
||||
return parent->Server(info);
|
||||
}
|
||||
private:
|
||||
DebugService *parent;
|
||||
ServiceType type;
|
||||
};
|
||||
|
||||
BasicTCPSocket tcpServer;
|
||||
BasicUDPSocket udpSocket;
|
||||
ServiceBinder binderServer;
|
||||
ServiceBinder binderStreamer;
|
||||
SingleThreadService threadService;
|
||||
SingleThreadService streamerService;
|
||||
|
||||
class ServiceBinder : public EmbeddedServiceMethodBinderI {
|
||||
public:
|
||||
enum ServiceType { ServerType, StreamerType };
|
||||
ServiceBinder(DebugService *parent, ServiceType type)
|
||||
: parent(parent), type(type) {}
|
||||
virtual ErrorManagement::ErrorType Execute(ExecutionInfo &info) {
|
||||
if (type == StreamerType) {
|
||||
return parent->Streamer(info);
|
||||
}
|
||||
return parent->Server(info);
|
||||
}
|
||||
ThreadIdentifier serverThreadId;
|
||||
ThreadIdentifier streamerThreadId;
|
||||
|
||||
private:
|
||||
DebugService *parent;
|
||||
ServiceType type;
|
||||
};
|
||||
// activeClient — exclusively owned by Server thread
|
||||
FastPollingMutexSem clientMutex;
|
||||
BasicTCPSocket *activeClient;
|
||||
|
||||
ServiceBinder binderServer;
|
||||
ServiceBinder binderStreamer;
|
||||
// Streamer assembly buffer
|
||||
static const uint32 STREAMER_MTU = 1400u;
|
||||
static const uint32 STREAMER_BUFFER_SIZE = 4096u;
|
||||
uint8 streamerPacketBuffer[STREAMER_BUFFER_SIZE];
|
||||
uint32 streamerPacketOffset;
|
||||
uint32 streamerSequenceNumber;
|
||||
|
||||
SingleThreadService threadService;
|
||||
SingleThreadService streamerService;
|
||||
// Rate-limiting and idle-timeout constants / state
|
||||
static const uint32 CMD_RATE_LIMIT = 100u;
|
||||
static const uint32 CLIENT_IDLE_TIMEOUT_MS = 30000u;
|
||||
static const uint32 INPUT_BUFFER_MAX = 8192u;
|
||||
|
||||
ThreadIdentifier serverThreadId;
|
||||
ThreadIdentifier streamerThreadId;
|
||||
uint32 cmdCountInWindow;
|
||||
uint64 cmdWindowStartMs;
|
||||
uint64 lastDataTimeMs;
|
||||
|
||||
Vec<DebugSignalInfo *> signals;
|
||||
Vec<SignalAlias> aliases;
|
||||
Vec<BrokerInfo> brokers;
|
||||
Vec<MonitoredSignal> monitoredSignals;
|
||||
|
||||
FastPollingMutexSem mutex;
|
||||
TraceRingBuffer traceBuffer;
|
||||
|
||||
/**
|
||||
* @brief Mutex protecting traceBuffer.Push() from concurrent RT broker threads.
|
||||
*
|
||||
* TraceRingBuffer is designed for single-producer / single-consumer access.
|
||||
* In practice, multiple RT threads (one per RealTimeThread) call ProcessSignal()
|
||||
* concurrently, making it a multi-producer scenario. Without a lock they can
|
||||
* both pass the free-space check, both pick the same write index, and corrupt
|
||||
* each other's samples. This mutex serialises the Push path.
|
||||
*
|
||||
* The Streamer thread (sole consumer) never holds this lock, so the lock is
|
||||
* only contended between RT threads — not between RT and Streamer.
|
||||
*
|
||||
* FIX #2 — TraceRingBuffer multi-producer race.
|
||||
*/
|
||||
FastPollingMutexSem tracePushMutex;
|
||||
|
||||
/**
|
||||
* @brief Mutex protecting the activeClient pointer.
|
||||
*
|
||||
* activeClient is currently owned exclusively by the Server thread:
|
||||
* it is created, read, and destroyed only inside Server(). The mutex
|
||||
* enforces this invariant and makes future cross-thread access safe.
|
||||
*
|
||||
* Rules:
|
||||
* - Hold clientMutex whenever assigning (including to NULL) activeClient.
|
||||
* - Do NOT hold clientMutex across blocking I/O calls (Read / Write)
|
||||
* to avoid starving threads that only need the pointer.
|
||||
* - Any future code that needs to send data to the active client from a
|
||||
* non-Server thread must lock clientMutex, copy the pointer, release
|
||||
* the lock, then do the Write.
|
||||
*
|
||||
* FIX #2 — activeClient cross-thread safety.
|
||||
*/
|
||||
FastPollingMutexSem clientMutex;
|
||||
|
||||
BasicTCPSocket *activeClient;
|
||||
|
||||
/**
|
||||
* Maximum payload bytes in a single UDP datagram (leave room for IP+UDP headers
|
||||
* on a standard 1500-byte Ethernet MTU). Used in the Streamer to decide when
|
||||
* to flush the current packet and start a new one.
|
||||
*
|
||||
* FIX #1 — named constant to replace the magic number 1400.
|
||||
*/
|
||||
static const uint32 STREAMER_MTU = 1400u;
|
||||
|
||||
/**
|
||||
* Size of the streamer assembly buffer. Must be >= STREAMER_MTU so at least
|
||||
* one full UDP payload can be staged before flushing. A sample that on its own
|
||||
* would exceed (STREAMER_BUFFER_SIZE - sizeof(TraceHeader) - 16) bytes is
|
||||
* silently dropped and logged — it can never fit in any packet.
|
||||
*
|
||||
* FIX #1 — named constant; prevents silent integer arithmetic relying on
|
||||
* the array bound being "obviously 4096".
|
||||
*/
|
||||
static const uint32 STREAMER_BUFFER_SIZE = 4096u;
|
||||
|
||||
// Streamer state persisted across Execute() calls (framework loops Execute)
|
||||
uint8 streamerPacketBuffer[STREAMER_BUFFER_SIZE];
|
||||
uint32 streamerPacketOffset;
|
||||
uint32 streamerSequenceNumber;
|
||||
|
||||
/**
|
||||
* @brief Maximum commands allowed per 1-second sliding window per client.
|
||||
*
|
||||
* A client sending more than this many commands in one second is disconnected.
|
||||
* This prevents a tight command loop from monopolising the Server thread and
|
||||
* starving normal usage. The limit is intentionally generous (100/s) so that
|
||||
* legitimate scripted tooling is unaffected.
|
||||
*
|
||||
* FIX #6 — TCP command rate limiting.
|
||||
*/
|
||||
static const uint32 CMD_RATE_LIMIT = 100u;
|
||||
|
||||
/**
|
||||
* @brief Close the active TCP client if no data is received for this many ms.
|
||||
*
|
||||
* A client that sends a partial command and never completes it would otherwise
|
||||
* hold the single active-client slot open indefinitely, blocking all other
|
||||
* connections. After CLIENT_IDLE_TIMEOUT_MS of silence the connection is
|
||||
* closed and the slot freed.
|
||||
*
|
||||
* FIX #8 — active-client idle timeout.
|
||||
*/
|
||||
static const uint32 CLIENT_IDLE_TIMEOUT_MS = 30000u;
|
||||
|
||||
/**
|
||||
* @brief Maximum array elements returned by GetSignalValue / VALUE command.
|
||||
*
|
||||
* Without a cap, a 1-million-element uint8 array would produce a multi-MB
|
||||
* response string, exhausting heap and blocking the Server thread. Elements
|
||||
* beyond this limit are omitted; the response includes a "Truncated" flag.
|
||||
*
|
||||
* FIX #9 — GetSignalValue unbounded array output.
|
||||
*/
|
||||
static const uint32 GET_VALUE_MAX_ELEMENTS = 256u;
|
||||
|
||||
// Rate-limiter state (per active TCP connection, reset on each new connection)
|
||||
uint32 cmdCountInWindow; // commands received in the current 1-second window
|
||||
uint64 cmdWindowStartMs; // start of the current window, in milliseconds
|
||||
|
||||
// Idle-timeout state
|
||||
uint64 lastDataTimeMs; // last time the active client sent any data (ms)
|
||||
|
||||
/**
|
||||
* @brief Carry-over buffer for multi-segment TCP commands.
|
||||
*
|
||||
* A single TCP Read() call may return only part of a command if the OS
|
||||
* delivers it in multiple segments. Bytes after the last '\n' in the
|
||||
* current receive window are saved here and prepended to the next Read().
|
||||
* This ensures that "FORCE Signal " arriving in one segment and "123\n"
|
||||
* arriving in the next are assembled into the single command
|
||||
* "FORCE Signal 123" before dispatch.
|
||||
*
|
||||
* The buffer is bounded by INPUT_BUFFER_MAX (8 KiB). If a client sends
|
||||
* more than that without a newline it is disconnected.
|
||||
*
|
||||
* FIX #10 — incomplete input buffer handling for multi-line commands.
|
||||
*/
|
||||
StreamString inputBuffer;
|
||||
|
||||
/**
|
||||
* @brief Maximum carry-over bytes allowed before a client is disconnected.
|
||||
*
|
||||
* A client that sends a very long line without a newline would otherwise
|
||||
* grow inputBuffer without bound. At 8 KiB the limit is generous enough
|
||||
* for any legitimate command while still protecting against memory exhaustion.
|
||||
*
|
||||
* FIX #10 — DoS guard for the input carry-over buffer.
|
||||
*/
|
||||
static const uint32 INPUT_BUFFER_MAX = 8192u;
|
||||
|
||||
ConfigurationDatabase fullConfig;
|
||||
bool manualConfigSet;
|
||||
// Carry-over buffer for multi-segment TCP commands
|
||||
StreamString inputBuffer;
|
||||
};
|
||||
|
||||
} // namespace MARTe
|
||||
|
||||
#endif
|
||||
#endif // DEBUGSERVICE_H
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,190 @@
|
||||
#ifndef DEBUGSERVICEBASE_H
|
||||
#define DEBUGSERVICEBASE_H
|
||||
|
||||
/**
|
||||
* @file DebugServiceBase.h
|
||||
* @brief Shared base class for DebugService and WebDebugService.
|
||||
*
|
||||
* Extracts all signal-management, command-handling and config logic that is
|
||||
* common to both the TCP/UDP transport (DebugService) and the HTTP/SSE
|
||||
* transport (WebDebugService), eliminating the previous code duplication.
|
||||
*/
|
||||
|
||||
#include "ConfigurationDatabase.h"
|
||||
#include "DataSourceI.h"
|
||||
#include "DebugServiceI.h"
|
||||
#include "FastPollingMutexSem.h"
|
||||
#include "ReferenceContainer.h"
|
||||
#include "ReferenceT.h"
|
||||
#include "StreamString.h"
|
||||
|
||||
namespace MARTe {
|
||||
|
||||
/**
|
||||
* @brief Intermediate base class that holds all shared debugging logic.
|
||||
*
|
||||
* Inherits from ReferenceContainer (to be a MARTe2 Object) and DebugServiceI
|
||||
* (to expose the RT-path / control-path interface). Transport subclasses
|
||||
* (DebugService, WebDebugService) inherit from this class and add only their
|
||||
* transport-specific threading and socket logic.
|
||||
*/
|
||||
class DebugServiceBase : public ReferenceContainer, public DebugServiceI {
|
||||
public:
|
||||
friend class DebugServiceTest;
|
||||
|
||||
DebugServiceBase();
|
||||
virtual ~DebugServiceBase();
|
||||
|
||||
// =========================================================================
|
||||
// DebugServiceI RT-path overrides (shared implementation)
|
||||
// =========================================================================
|
||||
|
||||
virtual DebugSignalInfo *RegisterSignal(void *memoryAddress,
|
||||
TypeDescriptor type,
|
||||
const char8 *name,
|
||||
uint8 numberOfDimensions = 0,
|
||||
uint32 numberOfElements = 1);
|
||||
|
||||
virtual void ProcessSignal(DebugSignalInfo *signalInfo, uint32 size,
|
||||
uint64 timestamp);
|
||||
|
||||
virtual void RegisterBroker(DebugSignalInfo **signalPointers,
|
||||
uint32 numSignals,
|
||||
MemoryMapBroker *broker,
|
||||
volatile bool *anyActiveFlag,
|
||||
Vec<uint32> *activeIndices,
|
||||
Vec<uint32> *activeSizes,
|
||||
FastPollingMutexSem *activeMutex,
|
||||
volatile bool *anyBreakFlag,
|
||||
Vec<uint32> *breakIndices,
|
||||
const char8 *gamName = NULL_PTR(const char8 *),
|
||||
bool isOutput = false);
|
||||
|
||||
virtual bool IsPaused() const { return isPaused; }
|
||||
virtual void SetPaused(bool paused) { isPaused = paused; }
|
||||
virtual bool IsStepPending() const { return stepRemaining > 0u; }
|
||||
virtual void ConsumeStepIfNeeded(const char8 *gamName,
|
||||
const char8 *threadName = NULL_PTR(const char8 *));
|
||||
|
||||
// =========================================================================
|
||||
// DebugServiceI control-path overrides (shared implementation)
|
||||
// =========================================================================
|
||||
|
||||
virtual uint32 ForceSignal (const char8 *name, const char8 *valueStr);
|
||||
virtual uint32 UnforceSignal(const char8 *name);
|
||||
virtual uint32 TraceSignal (const char8 *name, bool enable, uint32 decimation = 1);
|
||||
virtual uint32 SetBreak (const char8 *name, uint8 op, float64 threshold);
|
||||
virtual uint32 ClearBreak (const char8 *name);
|
||||
virtual bool IsInstrumented(const char8 *fullPath, bool &traceable, bool &forcable);
|
||||
virtual uint32 RegisterMonitorSignal(const char8 *path, uint32 periodMs);
|
||||
virtual uint32 UnmonitorSignal (const char8 *path);
|
||||
|
||||
// =========================================================================
|
||||
// Shared control-path methods (used by transport subclasses)
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* @brief Parse and dispatch a debug command; write text response to @p out.
|
||||
*
|
||||
* Handles: TRACE, FORCE, UNFORCE, BREAK, PAUSE, RESUME, STEP, STEP_STATUS,
|
||||
* VALUE, DISCOVER, TREE, INFO, LS, CONFIG, MONITOR, UNMONITOR,
|
||||
* SERVICE_INFO, MSG.
|
||||
*
|
||||
* SERVICE_INFO delegates to GetServiceInfo() so each transport can fill in
|
||||
* its own port/state information.
|
||||
*/
|
||||
void HandleCommand(const StreamString &cmdIn, StreamString &out);
|
||||
|
||||
void GetStepStatus(StreamString &out);
|
||||
void GetSignalValue(const char8 *name, StreamString &out);
|
||||
void Discover (StreamString &out);
|
||||
void InfoNode (const char8 *path, StreamString &out);
|
||||
void ListNodes (const char8 *path, StreamString &out);
|
||||
void ServeConfig (StreamString &out);
|
||||
|
||||
void SetFullConfig(ConfigurationDatabase &config);
|
||||
void RebuildConfigFromRegistry();
|
||||
|
||||
// =========================================================================
|
||||
// Struct shared by both transports
|
||||
// =========================================================================
|
||||
|
||||
struct MonitoredSignal {
|
||||
ReferenceT<DataSourceI> dataSource;
|
||||
uint32 signalIdx;
|
||||
uint32 internalID;
|
||||
uint32 periodMs;
|
||||
uint64 lastPollTime;
|
||||
uint32 size;
|
||||
StreamString path;
|
||||
};
|
||||
|
||||
// =========================================================================
|
||||
// Constants
|
||||
// =========================================================================
|
||||
|
||||
static const uint32 GET_VALUE_MAX_ELEMENTS = 256u;
|
||||
|
||||
protected:
|
||||
// =========================================================================
|
||||
// Virtual hooks for transport subclasses
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* @brief Fill the SERVICE_INFO response text (transport-specific).
|
||||
*
|
||||
* Called by HandleCommand when the SERVICE_INFO command is received.
|
||||
* The base implementation writes nothing; subclasses override to append
|
||||
* e.g. "OK SERVICE_INFO TCP_CTRL:8080 UDP_STREAM:8081 STATE:RUNNING\n".
|
||||
*/
|
||||
virtual void GetServiceInfo(StreamString &out) = 0;
|
||||
|
||||
/**
|
||||
* @brief Write back transport-specific config keys after RebuildConfigFromRegistry().
|
||||
*
|
||||
* Called at the end of RebuildConfigFromRegistry() so that port numbers
|
||||
* and other transport parameters that were read in Initialise() are
|
||||
* reflected back into fullConfig (ExportData on ReferenceContainers
|
||||
* doesn't re-emit config-file parameters).
|
||||
*/
|
||||
virtual void RebuildTransportConfig() {}
|
||||
|
||||
// =========================================================================
|
||||
// Shared protected helpers
|
||||
// =========================================================================
|
||||
|
||||
void Step(uint32 n, const char8 *threadName = NULL_PTR(const char8 *));
|
||||
void UpdateBrokersActiveStatus();
|
||||
void UpdateBrokersBreakStatus();
|
||||
void PatchRegistry();
|
||||
|
||||
uint32 ExportTree(ReferenceContainer *container, StreamString &json,
|
||||
const char8 *pathPrefix);
|
||||
void EnrichWithConfig(const char8 *path, StreamString &json);
|
||||
static void JsonifyDatabase(ConfigurationDatabase &db, StreamString &json);
|
||||
|
||||
// =========================================================================
|
||||
// Shared data members
|
||||
// =========================================================================
|
||||
|
||||
Vec<DebugSignalInfo *> signals;
|
||||
Vec<SignalAlias> aliases;
|
||||
Vec<BrokerInfo> brokers;
|
||||
Vec<MonitoredSignal> monitoredSignals;
|
||||
|
||||
FastPollingMutexSem mutex;
|
||||
FastPollingMutexSem tracePushMutex;
|
||||
TraceRingBuffer traceBuffer;
|
||||
|
||||
volatile bool isPaused;
|
||||
volatile uint32 stepRemaining;
|
||||
StreamString pausedAtGam;
|
||||
StreamString stepThreadFilter;
|
||||
|
||||
ConfigurationDatabase fullConfig;
|
||||
bool manualConfigSet;
|
||||
};
|
||||
|
||||
} // namespace MARTe
|
||||
|
||||
#endif // DEBUGSERVICEBASE_H
|
||||
@@ -23,7 +23,7 @@
|
||||
# $Id: Makefile.inc 3 2012-01-15 16:26:07Z aneto $
|
||||
#
|
||||
#############################################################
|
||||
OBJSX=DebugService.x
|
||||
OBJSX=DebugService.x DebugServiceBase.x
|
||||
|
||||
PACKAGE=Components/Interfaces
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
OBJSX=WebDebugService.x
|
||||
OBJSX=WebDebugService.x DebugServiceBase.x
|
||||
|
||||
PACKAGE=Components/Interfaces
|
||||
|
||||
@@ -26,9 +26,16 @@ INCLUDES += -I$(MARTe2_DIR)/Source/Core/Scheduler/L5GAMs
|
||||
INCLUDES += -I$(MARTe2_DIR)/Source/Core/FileSystem/L1Portability
|
||||
INCLUDES += -I$(MARTe2_DIR)/Source/Core/FileSystem/L3Streams
|
||||
|
||||
DEBUGSERVICE_SRC=$(ROOT_DIR)/Source/Components/Interfaces/DebugService
|
||||
|
||||
all: $(OBJS) $(SUBPROJ) \
|
||||
$(BUILD_DIR)/WebDebugService$(LIBEXT) \
|
||||
$(BUILD_DIR)/WebDebugService$(DLLEXT)
|
||||
echo $(OBJS)
|
||||
|
||||
include $(MAKEDEFAULTDIR)/MakeStdLibRules.$(TARGET)
|
||||
|
||||
# Explicit rule to compile DebugServiceBase.cpp from the DebugService directory
|
||||
$(BUILD_DIR)/DebugServiceBase.o: $(DEBUGSERVICE_SRC)/DebugServiceBase.cpp
|
||||
$(COMPILER) -c $(OPTIM) $(INCLUDES) $(CPPFLAGS) $(CFLAGSPEC) $(DEBUG) \
|
||||
$(DEBUGSERVICE_SRC)/DebugServiceBase.cpp -o $(BUILD_DIR)/DebugServiceBase.o
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,18 +2,15 @@
|
||||
#define WEBDEBUGSERVICE_H
|
||||
|
||||
#include "BasicTCPSocket.h"
|
||||
#include "ConfigurationDatabase.h"
|
||||
#include "DataSourceI.h"
|
||||
#include "DebugServiceI.h"
|
||||
#include "DebugServiceBase.h"
|
||||
#include "EmbeddedServiceMethodBinderI.h"
|
||||
#include "ReferenceContainer.h"
|
||||
#include "ReferenceT.h"
|
||||
#include "SingleThreadService.h"
|
||||
|
||||
namespace MARTe {
|
||||
|
||||
/**
|
||||
* @brief HTTP/SSE implementation of DebugServiceI.
|
||||
* @brief HTTP/SSE implementation of DebugServiceI (via DebugServiceBase).
|
||||
*
|
||||
* Serves a single-page web UI on httpPort (default 8090).
|
||||
* Endpoints:
|
||||
@@ -21,20 +18,11 @@ namespace MARTe {
|
||||
* POST /api/command — execute a debug command; returns text response
|
||||
* GET /api/events — Server-Sent Events stream for real-time telemetry
|
||||
*
|
||||
* SSE event types (JSON payload on each "data:" line):
|
||||
* {"type":"trace", "name":"…","ts":<ns>,"value":<f64>}
|
||||
* {"type":"monitor", "name":"…","ts":<ns>,"value":<f64>}
|
||||
* {"type":"log", "level":"…","msg":"…"}
|
||||
* {"type":"status", "paused":<bool>,"gam":"…","remaining":<u32>}
|
||||
* {"type":"discover","signals":[…]}
|
||||
* {"type":"tree", "tree":{…}}
|
||||
*
|
||||
* This is an alternative to DebugService (TCP/UDP) and registers itself as the
|
||||
* global DebugServiceI singleton on Initialise().
|
||||
* All signal management and command dispatch logic lives in DebugServiceBase.
|
||||
* This class adds only the HTTP/SSE transport.
|
||||
*/
|
||||
class WebDebugService : public ReferenceContainer,
|
||||
public EmbeddedServiceMethodBinderI,
|
||||
public DebugServiceI {
|
||||
class WebDebugService : public DebugServiceBase,
|
||||
public EmbeddedServiceMethodBinderI {
|
||||
public:
|
||||
friend class WebDebugServiceTest;
|
||||
CLASS_REGISTER_DECLARATION()
|
||||
@@ -44,60 +32,13 @@ public:
|
||||
|
||||
virtual bool Initialise(StructuredDataI &data);
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// DebugServiceI RT-path overrides
|
||||
// -------------------------------------------------------------------------
|
||||
virtual DebugSignalInfo *RegisterSignal(void *memoryAddress,
|
||||
TypeDescriptor type,
|
||||
const char8 *name,
|
||||
uint8 numberOfDimensions = 0,
|
||||
uint32 numberOfElements = 1);
|
||||
virtual void ProcessSignal(DebugSignalInfo *signalInfo, uint32 size,
|
||||
uint64 timestamp);
|
||||
virtual void RegisterBroker(DebugSignalInfo **signalPointers,
|
||||
uint32 numSignals, MemoryMapBroker *broker,
|
||||
volatile bool *anyActiveFlag,
|
||||
Vec<uint32> *activeIndices,
|
||||
Vec<uint32> *activeSizes,
|
||||
FastPollingMutexSem *activeMutex,
|
||||
volatile bool *anyBreakFlag,
|
||||
Vec<uint32> *breakIndices,
|
||||
const char8 *gamName = NULL_PTR(const char8 *),
|
||||
bool isOutput = false);
|
||||
virtual bool IsPaused() const { return isPaused; }
|
||||
virtual void SetPaused(bool paused) { isPaused = paused; }
|
||||
virtual bool IsStepPending() const { return stepRemaining > 0u; }
|
||||
virtual void ConsumeStepIfNeeded(const char8 *gamName,
|
||||
const char8 *threadName = NULL_PTR(const char8 *));
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// DebugServiceI control-path overrides
|
||||
// -------------------------------------------------------------------------
|
||||
virtual uint32 ForceSignal (const char8 *name, const char8 *valueStr);
|
||||
virtual uint32 UnforceSignal (const char8 *name);
|
||||
virtual uint32 TraceSignal (const char8 *name, bool enable, uint32 decimation = 1);
|
||||
virtual uint32 SetBreak (const char8 *name, uint8 op, float64 threshold);
|
||||
virtual uint32 ClearBreak (const char8 *name);
|
||||
virtual bool IsInstrumented(const char8 *fullPath, bool &traceable, bool &forcable);
|
||||
virtual uint32 RegisterMonitorSignal(const char8 *path, uint32 periodMs);
|
||||
virtual uint32 UnmonitorSignal (const char8 *path);
|
||||
|
||||
// EmbeddedServiceMethodBinderI (framework dispatches to sub-binders)
|
||||
virtual ErrorManagement::ErrorType Execute(ExecutionInfo &info);
|
||||
|
||||
// Inject the full config CDB (call after ConfigureApplication)
|
||||
void SetFullConfig(ConfigurationDatabase &config);
|
||||
void RebuildConfigFromRegistry();
|
||||
|
||||
struct MonitoredSignal {
|
||||
ReferenceT<DataSourceI> dataSource;
|
||||
uint32 signalIdx;
|
||||
uint32 internalID;
|
||||
uint32 periodMs;
|
||||
uint64 lastPollTime;
|
||||
uint32 size;
|
||||
StreamString path;
|
||||
};
|
||||
protected:
|
||||
// Transport-specific overrides of DebugServiceBase hooks
|
||||
virtual void GetServiceInfo(StreamString &out);
|
||||
virtual void RebuildTransportConfig() {} // no transport-specific keys
|
||||
|
||||
private:
|
||||
// HTTP server helpers
|
||||
@@ -109,29 +50,6 @@ private:
|
||||
const StreamString &path, const StreamString &body);
|
||||
void UpgradeToSse(BasicTCPSocket *client);
|
||||
|
||||
// Command handler — writes response to StreamString (not a socket)
|
||||
void HandleCommand(const StreamString &cmd, StreamString &out);
|
||||
void Discover (StreamString &out);
|
||||
void InfoNode (const char8 *path, StreamString &out);
|
||||
void ListNodes (const char8 *path, StreamString &out);
|
||||
void GetSignalValue(const char8 *name, StreamString &out);
|
||||
void ServeConfig (StreamString &out);
|
||||
void GetStepStatus(StreamString &out);
|
||||
void Step(uint32 n, const char8 *threadName = NULL_PTR(const char8 *));
|
||||
|
||||
// Tree / config helpers
|
||||
uint32 ExportTree(ReferenceContainer *container, StreamString &json,
|
||||
const char8 *pathPrefix);
|
||||
void EnrichWithConfig(const char8 *path, StreamString &json);
|
||||
static void JsonifyDatabase(ConfigurationDatabase &db, StreamString &json);
|
||||
|
||||
// Registry patching (same as DebugService)
|
||||
void PatchRegistry();
|
||||
|
||||
// Broker index maintenance
|
||||
void UpdateBrokersActiveStatus();
|
||||
void UpdateBrokersBreakStatus();
|
||||
|
||||
// SSE broadcast
|
||||
void BroadcastSse(const char8 *eventType, const StreamString &data);
|
||||
void RemoveDeadSseClients();
|
||||
@@ -156,27 +74,13 @@ private:
|
||||
|
||||
uint16 httpPort;
|
||||
|
||||
volatile bool isPaused;
|
||||
volatile uint32 stepRemaining;
|
||||
StreamString pausedAtGam;
|
||||
StreamString stepThreadFilter;
|
||||
|
||||
BasicTCPSocket tcpServer;
|
||||
|
||||
WdsBinder binderHttp;
|
||||
WdsBinder binderStream;
|
||||
WdsBinder binderHttp;
|
||||
WdsBinder binderStream;
|
||||
SingleThreadService httpService;
|
||||
SingleThreadService streamerService;
|
||||
|
||||
Vec<DebugSignalInfo *> signals;
|
||||
Vec<SignalAlias> aliases;
|
||||
Vec<BrokerInfo> brokers;
|
||||
Vec<MonitoredSignal> monitoredSignals;
|
||||
|
||||
FastPollingMutexSem mutex;
|
||||
FastPollingMutexSem tracePushMutex;
|
||||
TraceRingBuffer traceBuffer;
|
||||
|
||||
// SSE client list
|
||||
static const uint32 MAX_SSE_CLIENTS = 8u;
|
||||
BasicTCPSocket *sseClients[MAX_SSE_CLIENTS];
|
||||
@@ -186,11 +90,6 @@ private:
|
||||
uint32 streamerSeq;
|
||||
uint64 streamerT0Ns; // nanosecond origin for relative timestamps sent over SSE
|
||||
|
||||
ConfigurationDatabase fullConfig;
|
||||
bool manualConfigSet;
|
||||
|
||||
static const uint32 GET_VALUE_MAX_ELEMENTS = 256u;
|
||||
|
||||
// Log history buffer — replayed to newly connecting SSE clients
|
||||
static const uint32 LOG_HISTORY_SIZE = 64u;
|
||||
static const uint32 LOG_ENTRY_MAX_SIZE = 512u;
|
||||
|
||||
@@ -48,14 +48,14 @@ public:
|
||||
service.UnforceSignal("Z");
|
||||
|
||||
// 2. Commands
|
||||
service.HandleCommand("TREE", NULL_PTR(BasicTCPSocket*));
|
||||
service.HandleCommand("DISCOVER", NULL_PTR(BasicTCPSocket*));
|
||||
service.HandleCommand("CONFIG", NULL_PTR(BasicTCPSocket*));
|
||||
service.HandleCommand("PAUSE", NULL_PTR(BasicTCPSocket*));
|
||||
service.HandleCommand("RESUME", NULL_PTR(BasicTCPSocket*));
|
||||
service.HandleCommand("LS /", NULL_PTR(BasicTCPSocket*));
|
||||
service.HandleCommand("INFO X.Y.Z", NULL_PTR(BasicTCPSocket*));
|
||||
service.HandleCommand("MSG DebugService DummyFunc 0 K=V", NULL_PTR(BasicTCPSocket*));
|
||||
{ StreamString out; service.HandleCommand("TREE", out); }
|
||||
{ StreamString out; service.HandleCommand("DISCOVER", out); }
|
||||
{ StreamString out; service.HandleCommand("CONFIG", out); }
|
||||
{ StreamString out; service.HandleCommand("PAUSE", out); }
|
||||
{ StreamString out; service.HandleCommand("RESUME", out); }
|
||||
{ StreamString out; service.HandleCommand("LS /", out); }
|
||||
{ StreamString out; service.HandleCommand("INFO X.Y.Z", out); }
|
||||
{ StreamString out; service.HandleCommand("MSG DebugService DummyFunc 0 K=V", out); }
|
||||
|
||||
// 3. Broker Active Status
|
||||
volatile bool active = false;
|
||||
@@ -111,12 +111,12 @@ public:
|
||||
service.SetPaused(false);
|
||||
service.Step(0u, NULL_PTR(const char8 *));
|
||||
|
||||
// 5. VALUE command (NULL client — smoke test, no crash)
|
||||
service.HandleCommand("VALUE X.Y.Z", NULL_PTR(BasicTCPSocket*));
|
||||
service.HandleCommand("VALUE NoSuchSignal", NULL_PTR(BasicTCPSocket*));
|
||||
service.HandleCommand("STEP 1 ThreadA", NULL_PTR(BasicTCPSocket*));
|
||||
// 5. VALUE command — smoke test, no crash
|
||||
{ StreamString out; service.HandleCommand("VALUE X.Y.Z", out); }
|
||||
{ StreamString out; service.HandleCommand("VALUE NoSuchSignal", out); }
|
||||
{ StreamString out; service.HandleCommand("STEP 1 ThreadA", out); }
|
||||
assert(service.stepThreadFilter == "ThreadA");
|
||||
service.HandleCommand("STEP 3", NULL_PTR(BasicTCPSocket*));
|
||||
{ StreamString out; service.HandleCommand("STEP 3", out); }
|
||||
assert(service.stepThreadFilter.Size() == 0u);
|
||||
}
|
||||
|
||||
@@ -282,8 +282,8 @@ public:
|
||||
for (uint32 i = 0; i < 512; i++) bigBuf[i] = (uint8)(i & 0xFF);
|
||||
service.RegisterSignal(bigBuf, UnsignedInteger8Bit, "Big.Signal", 1, 512);
|
||||
|
||||
// VALUE command with NULL client is a smoke test — must not crash or loop
|
||||
service.HandleCommand("VALUE Signal", NULL_PTR(BasicTCPSocket*));
|
||||
// VALUE command — smoke test, must not crash or loop
|
||||
{ StreamString out; service.HandleCommand("VALUE Signal", out); }
|
||||
printf(" -> PASS: VALUE on large array completed without hang\n");
|
||||
|
||||
// Verify directly: nElem is capped before the byte limit
|
||||
@@ -334,12 +334,11 @@ public:
|
||||
cmd += "=value";
|
||||
|
||||
// Must not crash or assert
|
||||
service.HandleCommand(cmd, NULL_PTR(BasicTCPSocket*));
|
||||
{ StreamString out; service.HandleCommand(cmd, out); }
|
||||
printf(" -> PASS: oversized key handled without buffer overflow\n");
|
||||
|
||||
// A normal-length key should still be accepted (regression check)
|
||||
service.HandleCommand("MSG DebugService DummyFunc 0 NormalKey=NormalValue",
|
||||
NULL_PTR(BasicTCPSocket*));
|
||||
{ StreamString out; service.HandleCommand("MSG DebugService DummyFunc 0 NormalKey=NormalValue", out); }
|
||||
printf(" -> PASS: normal key not rejected by bounds check\n");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user