5_debug

Store definition

ExampleDebugSomeStore

// SPDX-FileCopyrightText: 2020-2023 Jochem Rutgers
//
// SPDX-License-Identifier: CC0-1.0

int32=4 i

ExampleDebugAnotherStore

// SPDX-FileCopyrightText: 2020-2023 Jochem Rutgers
//
// SPDX-License-Identifier: CC0-1.0

int32=101 j

Application

// SPDX-FileCopyrightText: 2020-2023 Jochem Rutgers
//
// SPDX-License-Identifier: CC0-1.0

/*!
 * \file
 * \brief Example with a debugger instance and two stores.
 */

#include <stored>

#include "ExampleDebugAnotherStore.h"
#include "ExampleDebugSomeStore.h"

#include <cstdio>

#ifdef STORED_COMPILER_MSVC
#	define strdup(s) _strdup(s)
#endif

// A 'physical layer' that sends the outgoing (encoded) data to print().
class PrintfPhysical : public stored::ProtocolLayer {
	STORED_CLASS_NOCOPY(PrintfPhysical)
public:
	typedef stored::ProtocolLayer base;

	explicit PrintfPhysical(ProtocolLayer& up)
		: base(&up)
		, m_encoding()
		, m_silenced()
	{
		up.setDown(this);
	}

	void decode(char const* frame)
	{
		char* s = strdup(frame);
		if(s)
			decode(s, strlen(s));
		free(s);
	}

	void decode(void* buffer, size_t len) final
	{
		if(!m_silenced)
			printf(">>   %.*s\n", (int)len, (char const*)buffer);
		base::decode(buffer, len);
	}

	void encode(void const* buffer, size_t len, bool last = true) final
	{
		if(!m_encoding) {
			if(!m_silenced)
				printf("<<   ");
			m_encoding = true;
		}

		if(len && !m_silenced)
			printf("%.*s", (int)len, (char const*)buffer);

		if(last) {
			if(!m_silenced)
				printf("\n");
			m_encoding = false;
		}
	}

	void silence(bool silenced)
	{
		m_silenced = silenced;
	}

private:
	bool m_encoding;
	bool m_silenced;
};

// Extend the capabilities with the 'z' command.
class ExtendedDebugger : public stored::Debugger {
	STORED_CLASS_NOCOPY(ExtendedDebugger)
public:
	typedef stored::Debugger base;
	explicit ExtendedDebugger(char const* identification = nullptr)
		: base(identification)
	{}

	virtual ~ExtendedDebugger() noexcept override is_default;

	virtual void capabilities(char*& caps, size_t& len, size_t reserve = 0) override
	{
		// Get the default capabilities.
		base::capabilities(caps, len, reserve + 1 /* add room for our 'z' cmd */);
		// Add our 'z' cmd.
		caps[len++] = 'z';
	}

	virtual void process(void const* frame, size_t len, ProtocolLayer& response) override
	{
		if(unlikely(!frame || len == 0))
			return;

		char const* p = static_cast<char const*>(frame);

		switch(p[0]) {
		case 'z':
			// That's our cmd. Let's respond with something useful...
			response.encode("Zzzz", 4);
			break;
		default:
			// Not for us, forward to our base.
			base::process(frame, len, response);
		}
	}
};

int main()
{
	// Create a few stores.
	stored::ExampleDebugSomeStore someStore1;
	stored::ExampleDebugSomeStore someStore2;
	stored::ExampleDebugAnotherStore anotherStore;

	// Register them to a debugger.
	stored::Debugger debugger("5_debug");
	debugger.setVersions("123");
	debugger.map(someStore1, "/SomeStore");
	debugger.map(someStore2, "/OtherInstanceOfSomeStore");
	debugger.map(anotherStore); // Use default name.

	// Some accesses to the stores objects using the full prefix.
	int32_t i;
	// The stored::DebugVariant is a bit more expensive than directly
	// accessing the store's accessors, but allows a template-independant interface,
	// as the debugger will operate only on such an interface.
	stored::DebugVariant i1 = debugger.find("/SomeStore/i");
	i1.get(&i, sizeof(i));
	printf("/SomeStore/i = %" PRId32 "\n", i);
	i++;
	i1.set(&i, sizeof(i));
	printf("/SomeStore/i = %" PRId32 "\n", someStore1.i.get());

	stored::DebugVariant i2 = debugger.find("/OtherInstanceOfSomeStore/i");
	i2.get(&i, sizeof(i));
	printf("/OtherInstanceOfSomeStore/i = %" PRId32 "\n", i);

	stored::DebugVariant j = debugger.find("/ExampleDebugAnotherStore/j");
	j.get(&i, sizeof(i));
	printf("/ExampleDebugAnotherStore/j = %" PRId32 "\n", i);

	// DebugVariants are small, copyable and assignable, so they can be used in
	// std::map, for example.
	i2 = i1; // let i2 point to /SomeStore/i
	i2.get(&i, sizeof(i));
	printf("i2 = %" PRId32 "\n", i);

	// Now process some Embedded Debugger messages
	PrintfPhysical phy(debugger);
	phy.decode("?");
	phy.decode("i");
	phy.decode("r/ExampleDebugAnotherStore/j");
	phy.decode("wf00f/SomeStore/i");
	phy.decode("r/SomeStore/i");
	phy.decode("eHello World!!1");
	phy.decode("l");
	phy.decode("a0/SomeStore/i");
	phy.decode("r0");
	phy.decode("m* r0 e; r0 e; r/ExampleDebugAnotherStore/j");
	phy.decode("*");
	phy.decode("m*");

	// Suppress output, such that the application always prints the same.
	// This is handy for testing the behavior of the application by unit tests.
	phy.silence(true);

	int mem = 0xbeef;

	if(stored::Config::DebuggerReadMem) {
		// flawfinder: ignore
		char buffer[32] = {};
		snprintf(buffer, sizeof(buffer), "R%" PRIxPTR " %zu", (uintptr_t)&mem, sizeof(mem));
		phy.decode(buffer);
	}

	if(stored::Config::DebuggerWriteMem) {
		// flawfinder: ignore
		char buffer[32] = {};
		snprintf(buffer, sizeof(buffer), "W%" PRIxPTR " cafe", (uintptr_t)&mem);
		phy.decode(buffer);

		printf("mem = 0x%x\n", mem);
	}

	phy.silence(false);

	phy.decode("s");
	debugger.stream('A', "Hello");
	phy.decode("s");
	phy.decode("sA");
	debugger.stream('A', "stream!!1");
	phy.decode("sA");
	phy.decode("s");
	phy.decode("sA/");
	phy.decode("sB/");


	// Test our debugger with the z capability.
	ExtendedDebugger extdebugger;
	PrintfPhysical extphy(extdebugger);
	extphy.decode("?");
	extphy.decode("z");

	return 0;
}

Output

/SomeStore/i = 4
/SomeStore/i = 5
/OtherInstanceOfSomeStore/i = 4
/ExampleDebugAnotherStore/j = 101
i2 = 5
>>   ?
<<   ?rwelamivst
>>   i
<<   5_debug
>>   r/ExampleDebugAnotherStore/j
<<   65
>>   wf00f/SomeStore/i
<<   !
>>   r/SomeStore/i
<<   f00f
>>   eHello World!!1
<<   Hello World!!1
>>   l
<<   3b4/ExampleDebugAnotherStore/j
3b4/OtherInstanceOfSomeStore/i
3b4/SomeStore/i

>>   a0/SomeStore/i
<<   !
>>   r0
<<   f00f
>>   m* r0 e; r0 e; r/ExampleDebugAnotherStore/j
<<   !
>>   *
<<   f00f;f00f;65
>>   m*
<<   !
>>   s
<<   ?
>>   s
<<   A
>>   sA
<<   Hello
>>   sA
<<   stream!!1
>>   s
<<   ?
>>   sA/
<<   /
>>   sB/
<<   ?
>>   ?
<<   ?rwelamvstz
>>   z
<<   Zzzz

Store reference

template<typename Base_, typename Implementation_>
class ExampleDebugSomeStoreObjects

All ExampleDebugSomeStoreBase’s objects.

Public Types

typedef Base_ Base
typedef Implementation_ Implementation

Public Members

impl::StoreVariable<Base, Implementation, int32_t, 0u, 4> i

i

template<typename Implementation_>
class ExampleDebugSomeStoreBase : public stored::ExampleDebugSomeStoreObjects<ExampleDebugSomeStoreBase<Implementation_>, Implementation_>

Base class with default interface of all ExampleDebugSomeStore implementations.

Although there are no virtual functions in the base class, subclasses can override them. The (lowest) subclass must pass the Implementation_ template paramater to its base, such that all calls from the base class can be directed to the proper overridden implementation.

The base class cannot be instantiated. If a default implementation is required, which does not have side effects to functions, instantiate stored::ExampleDebugSomeStore. This class contains all data of all variables, so it can be large. So, be aware when instantiating it on the stack. Heap is fine. Static allocations is fine too, as the constructor and destructor are trivial.

To inherit the base class, you can use the following template:

class ExampleDebugSomeStore : public stored::store<ExampleDebugSomeStore, ExampleDebugSomeStoreBase>::type {
    STORE_CLASS(ExampleDebugSomeStore, ExampleDebugSomeStoreBase)
public:
    // Your class implementation, such as:
    ExampleDebugSomeStore() is_default
    // ...
};

Some compilers or tools may get confused by the inheritance using stored::store or stored::store_t. Alternatively, use STORE_T(...) instead, providing the template parameters of stored::store as macro arguments.

See also

stored::ExampleDebugSomeStoreData

Subclassed by stored::ExampleDebugSomeStoreDefaultFunctions< ExampleDebugSomeStoreBase< ExampleDebugSomeStore > >

Public Types

enum [anonymous]

Values:

enumerator ObjectCount

Number of objects in the store.

enumerator VariableCount

Number of variables in the store.

enumerator FunctionCount

Number of functions in the store.

enumerator BufferSize

Buffer size.

typedef Implementation_ Implementation

Type of the actual implementation, which is the (lowest) subclass.

typedef uintptr_t Key

Type of a key.

See also

bufferToKey()

typedef Map<String::type, Variant<Implementation>>::type ObjectMap

Map as generated by map().

typedef ExampleDebugSomeStoreObjects<ExampleDebugSomeStoreBase, Implementation_> Objects
typedef ExampleDebugSomeStoreBase root

We are the root, as used by STORE_CLASS.

typedef ExampleDebugSomeStoreBase self

Define self for stored::store.

Public Functions

inline ~ExampleDebugSomeStoreBase()
inline Key bufferToKey(void const *buffer) const noexcept

Converts a variable’s buffer to a key.

A key is unique for all variables of the same store, but identical for the same variables across different instances of the same store class. Therefore, the key can be used to synchronize between instances of the same store. A key does not contain meta data, such as type or length. It is up to the synchronization library to make sure that these properties are handled well.

For synchronization, when hookEntryX() or hookEntryRO() is invoked, one can compute the key of the object that is accessed. The key can be used, for example, in a key-to-Variant map. When data arrives from another party, the key can be used to find the proper Variant in the map.

This way, data exchange is type-safe, as the Variant can check if the data format matches the expected type. However, one cannot process data if the key is not set yet in the map.

inline Type::type bufferToType(void const *buffer) noexcept

Return the type of the variable, given its buffer.

inline Variant<Implementation> find(char const *name, size_t len = std::numeric_limits<size_t>::max()) noexcept

Finds an object with the given name.

Returns:

the object, or an invalid stored::Variant if not found.

template<typename T>
inline Function<T, Implementation> function(char const *name, size_t len = std::numeric_limits<size_t>::max()) noexcept

Finds a function with the given name.

The function, when it exists, must have the given (fixed) type.

inline Implementation const &implementation() const noexcept

Returns the reference to the implementation.

inline Implementation &implementation() noexcept

Returns the reference to the implementation.

template<typename F>
inline void list(F &&f) noexcept

Calls a callback for every object in the longDirectory().

See also

stored::list()

template<typename F>
inline void list(F f, void *arg, char const *prefix, String::type *nameBuffer) noexcept

Calls a callback for every object in the longDirectory().

See also

stored::list()

template<typename F>
inline void list(F f, void *arg, char const *prefix = nullptr) noexcept

Calls a callback for every object in the longDirectory().

See also

stored::list()

inline uint8_t const *longDirectory() const noexcept

Retuns the long directory.

When not available, the short directory is returned.

inline ObjectMap map(char const *prefix = nullptr)

Create a name to Variant map for the store.

Generating the map may be expensive and the result is not cached.

inline char const *name() const noexcept

Returns the name of store, which can be used as prefix for stored::Debugger.

inline uint8_t const *shortDirectory() const noexcept

Returns the short directory.

template<typename T>
inline Variable<T, Implementation> variable(char const *name, size_t len = std::numeric_limits<size_t>::max()) noexcept

Finds a variable with the given name.

The variable, when it exists, must have the given (fixed) type.

Public Members

impl::StoreVariable<Base, Implementation, int32_t, 0u, 4> i

i

Public Static Functions

template<typename T>
static inline constexpr FreeFunction<T, Implementation> freeFunction(char const *name, size_t len = std::numeric_limits<size_t>::max()) noexcept

Finds a function with the given name.

The function, when it exists, must have the given (fixed) type. It is returned as a free function; it is not bound yet to a specific store instance. This function is constexpr for C++14.

template<typename T>
static inline constexpr FreeVariable<T, Implementation> freeVariable(char const *name, size_t len = std::numeric_limits<size_t>::max()) noexcept

Finds a variable with the given name.

The variable, when it exists, must have the given (fixed) type. It is returned as a free variable; it is not bound yet to a specific store instance. This function is constexpr for C++14.

static inline constexpr char const *hash() noexcept

Returns a unique hash of the store.

Friends

friend class impl::StoreFunction
friend class impl::StoreVariable
friend class impl::StoreVariantF
friend class impl::StoreVariantV
friend class stored::FreeVariable
friend class stored::Variant< void >
class ExampleDebugSomeStore : public stored::ExampleDebugSomeStoreDefaultFunctions<ExampleDebugSomeStoreBase<ExampleDebugSomeStore>>

Default ExampleDebugSomeStoreBase implementation.

Public Functions

ExampleDebugSomeStore() = default

Default constructor.

template<typename Base_, typename Implementation_>
class ExampleDebugAnotherStoreObjects

All ExampleDebugAnotherStoreBase’s objects.

Public Types

typedef Base_ Base
typedef Implementation_ Implementation

Public Members

impl::StoreVariable<Base, Implementation, int32_t, 0u, 4> j

j

template<typename Implementation_>
class ExampleDebugAnotherStoreBase : public stored::ExampleDebugAnotherStoreObjects<ExampleDebugAnotherStoreBase<Implementation_>, Implementation_>

Base class with default interface of all ExampleDebugAnotherStore implementations.

Although there are no virtual functions in the base class, subclasses can override them. The (lowest) subclass must pass the Implementation_ template paramater to its base, such that all calls from the base class can be directed to the proper overridden implementation.

The base class cannot be instantiated. If a default implementation is required, which does not have side effects to functions, instantiate stored::ExampleDebugAnotherStore. This class contains all data of all variables, so it can be large. So, be aware when instantiating it on the stack. Heap is fine. Static allocations is fine too, as the constructor and destructor are trivial.

To inherit the base class, you can use the following template:

class ExampleDebugAnotherStore : public stored::store<ExampleDebugAnotherStore, ExampleDebugAnotherStoreBase>::type {
    STORE_CLASS(ExampleDebugAnotherStore, ExampleDebugAnotherStoreBase)
public:
    // Your class implementation, such as:
    ExampleDebugAnotherStore() is_default
    // ...
};

Some compilers or tools may get confused by the inheritance using stored::store or stored::store_t. Alternatively, use STORE_T(...) instead, providing the template parameters of stored::store as macro arguments.

See also

stored::ExampleDebugAnotherStoreData

Subclassed by stored::ExampleDebugAnotherStoreDefaultFunctions< ExampleDebugAnotherStoreBase< ExampleDebugAnotherStore > >

Public Types

enum [anonymous]

Values:

enumerator ObjectCount

Number of objects in the store.

enumerator VariableCount

Number of variables in the store.

enumerator FunctionCount

Number of functions in the store.

enumerator BufferSize

Buffer size.

typedef Implementation_ Implementation

Type of the actual implementation, which is the (lowest) subclass.

typedef uintptr_t Key

Type of a key.

See also

bufferToKey()

typedef Map<String::type, Variant<Implementation>>::type ObjectMap

Map as generated by map().

typedef ExampleDebugAnotherStoreObjects<ExampleDebugAnotherStoreBase, Implementation_> Objects
typedef ExampleDebugAnotherStoreBase root

We are the root, as used by STORE_CLASS.

typedef ExampleDebugAnotherStoreBase self

Define self for stored::store.

Public Functions

inline ~ExampleDebugAnotherStoreBase()
inline Key bufferToKey(void const *buffer) const noexcept

Converts a variable’s buffer to a key.

A key is unique for all variables of the same store, but identical for the same variables across different instances of the same store class. Therefore, the key can be used to synchronize between instances of the same store. A key does not contain meta data, such as type or length. It is up to the synchronization library to make sure that these properties are handled well.

For synchronization, when hookEntryX() or hookEntryRO() is invoked, one can compute the key of the object that is accessed. The key can be used, for example, in a key-to-Variant map. When data arrives from another party, the key can be used to find the proper Variant in the map.

This way, data exchange is type-safe, as the Variant can check if the data format matches the expected type. However, one cannot process data if the key is not set yet in the map.

inline Type::type bufferToType(void const *buffer) noexcept

Return the type of the variable, given its buffer.

inline Variant<Implementation> find(char const *name, size_t len = std::numeric_limits<size_t>::max()) noexcept

Finds an object with the given name.

Returns:

the object, or an invalid stored::Variant if not found.

template<typename T>
inline Function<T, Implementation> function(char const *name, size_t len = std::numeric_limits<size_t>::max()) noexcept

Finds a function with the given name.

The function, when it exists, must have the given (fixed) type.

inline Implementation const &implementation() const noexcept

Returns the reference to the implementation.

inline Implementation &implementation() noexcept

Returns the reference to the implementation.

template<typename F>
inline void list(F &&f) noexcept

Calls a callback for every object in the longDirectory().

See also

stored::list()

template<typename F>
inline void list(F f, void *arg, char const *prefix, String::type *nameBuffer) noexcept

Calls a callback for every object in the longDirectory().

See also

stored::list()

template<typename F>
inline void list(F f, void *arg, char const *prefix = nullptr) noexcept

Calls a callback for every object in the longDirectory().

See also

stored::list()

inline uint8_t const *longDirectory() const noexcept

Retuns the long directory.

When not available, the short directory is returned.

inline ObjectMap map(char const *prefix = nullptr)

Create a name to Variant map for the store.

Generating the map may be expensive and the result is not cached.

inline char const *name() const noexcept

Returns the name of store, which can be used as prefix for stored::Debugger.

inline uint8_t const *shortDirectory() const noexcept

Returns the short directory.

template<typename T>
inline Variable<T, Implementation> variable(char const *name, size_t len = std::numeric_limits<size_t>::max()) noexcept

Finds a variable with the given name.

The variable, when it exists, must have the given (fixed) type.

Public Members

impl::StoreVariable<Base, Implementation, int32_t, 0u, 4> j

j

Public Static Functions

template<typename T>
static inline constexpr FreeFunction<T, Implementation> freeFunction(char const *name, size_t len = std::numeric_limits<size_t>::max()) noexcept

Finds a function with the given name.

The function, when it exists, must have the given (fixed) type. It is returned as a free function; it is not bound yet to a specific store instance. This function is constexpr for C++14.

template<typename T>
static inline constexpr FreeVariable<T, Implementation> freeVariable(char const *name, size_t len = std::numeric_limits<size_t>::max()) noexcept

Finds a variable with the given name.

The variable, when it exists, must have the given (fixed) type. It is returned as a free variable; it is not bound yet to a specific store instance. This function is constexpr for C++14.

static inline constexpr char const *hash() noexcept

Returns a unique hash of the store.

Friends

friend class impl::StoreFunction
friend class impl::StoreVariable
friend class impl::StoreVariantF
friend class impl::StoreVariantV
friend class stored::FreeVariable
friend class stored::Variant< void >
class ExampleDebugAnotherStore : public stored::ExampleDebugAnotherStoreDefaultFunctions<ExampleDebugAnotherStoreBase<ExampleDebugAnotherStore>>

Default ExampleDebugAnotherStoreBase implementation.

Public Functions

ExampleDebugAnotherStore() = default

Default constructor.