mirror of
https://git.rwth-aachen.de/acs/public/villas/node/
synced 2025-03-09 00:00:00 +01:00
Apply clang-format changes
Signed-off-by: Steffen Vogel <steffen.vogel@opal-rt.com>
This commit is contained in:
parent
fd93e2be0a
commit
b6219e9163
76 changed files with 4920 additions and 5658 deletions
|
@ -17,35 +17,35 @@
|
|||
// E.g. a cross can be constructed by combining all line fragments:
|
||||
// BOX_UDLR
|
||||
#if 0 // Alternate character set
|
||||
#define BOX(chr) "\e(0" chr "\e(B"
|
||||
#define BOX_LR BOX("\x71") // Boxdrawing: ─
|
||||
#define BOX_UD BOX("\x78") // Boxdrawing: │
|
||||
#define BOX_UDR BOX("\x74") // Boxdrawing: ├
|
||||
#define BOX_UDLR BOX("\x6E") // Boxdrawing: ┼
|
||||
#define BOX_UDL BOX("\x75") // Boxdrawing: ┤
|
||||
#define BOX_ULR BOX("\x76") // Boxdrawing: ┴
|
||||
#define BOX_UL BOX("\x6A") // Boxdrawing: ┘
|
||||
#define BOX_DLR BOX("\x77") // Boxdrawing: ┘
|
||||
#define BOX_DL BOX("\x6B") // Boxdrawing: ┘
|
||||
#elif 1 // UTF-8
|
||||
#define BOX_LR "─" // Boxdrawing: ─
|
||||
#define BOX_UD "│" // Boxdrawing: │
|
||||
#define BOX_UDR "├" // Boxdrawing: ├
|
||||
#define BOX_UDLR "┼" // Boxdrawing: ┼
|
||||
#define BOX_UDL "┤" // Boxdrawing: ┤
|
||||
#define BOX_ULR "┴" // Boxdrawing: ┴
|
||||
#define BOX_UL "┘" // Boxdrawing: ┘
|
||||
#define BOX_DLR "┬" // Boxdrawing: ┘
|
||||
#define BOX_DL "┐" // Boxdrawing: ┘
|
||||
#define BOX_UR "└" // Boxdrawing: └
|
||||
#else // ASCII
|
||||
#define BOX_LR "-" // Boxdrawing: ─
|
||||
#define BOX_UD "|" // Boxdrawing: │
|
||||
#define BOX_UDR "+" // Boxdrawing: ├
|
||||
#define BOX_UDLR "+" // Boxdrawing: ┼
|
||||
#define BOX_UDL "+" // Boxdrawing: ┤
|
||||
#define BOX_ULR "+" // Boxdrawing: ┴
|
||||
#define BOX_UL "+" // Boxdrawing: ┘
|
||||
#define BOX_DLR "+" // Boxdrawing: ┘
|
||||
#define BOX_DL "+" // Boxdrawing: ┘
|
||||
#define BOX(chr) "\e(0" chr "\e(B"
|
||||
#define BOX_LR BOX("\x71") // Boxdrawing: ─
|
||||
#define BOX_UD BOX("\x78") // Boxdrawing: │
|
||||
#define BOX_UDR BOX("\x74") // Boxdrawing: ├
|
||||
#define BOX_UDLR BOX("\x6E") // Boxdrawing: ┼
|
||||
#define BOX_UDL BOX("\x75") // Boxdrawing: ┤
|
||||
#define BOX_ULR BOX("\x76") // Boxdrawing: ┴
|
||||
#define BOX_UL BOX("\x6A") // Boxdrawing: ┘
|
||||
#define BOX_DLR BOX("\x77") // Boxdrawing: ┘
|
||||
#define BOX_DL BOX("\x6B") // Boxdrawing: ┘
|
||||
#elif 1 // UTF-8
|
||||
#define BOX_LR "─" // Boxdrawing: ─
|
||||
#define BOX_UD "│" // Boxdrawing: │
|
||||
#define BOX_UDR "├" // Boxdrawing: ├
|
||||
#define BOX_UDLR "┼" // Boxdrawing: ┼
|
||||
#define BOX_UDL "┤" // Boxdrawing: ┤
|
||||
#define BOX_ULR "┴" // Boxdrawing: ┴
|
||||
#define BOX_UL "┘" // Boxdrawing: ┘
|
||||
#define BOX_DLR "┬" // Boxdrawing: ┘
|
||||
#define BOX_DL "┐" // Boxdrawing: ┘
|
||||
#define BOX_UR "└" // Boxdrawing: └
|
||||
#else // ASCII
|
||||
#define BOX_LR "-" // Boxdrawing: ─
|
||||
#define BOX_UD "|" // Boxdrawing: │
|
||||
#define BOX_UDR "+" // Boxdrawing: ├
|
||||
#define BOX_UDLR "+" // Boxdrawing: ┼
|
||||
#define BOX_UDL "+" // Boxdrawing: ┤
|
||||
#define BOX_ULR "+" // Boxdrawing: ┴
|
||||
#define BOX_UL "+" // Boxdrawing: ┘
|
||||
#define BOX_DLR "+" // Boxdrawing: ┘
|
||||
#define BOX_DL "+" // Boxdrawing: ┘
|
||||
#endif
|
||||
|
|
|
@ -18,28 +18,20 @@ namespace villas {
|
|||
class Buffer : public std::vector<char> {
|
||||
|
||||
protected:
|
||||
static
|
||||
int callback(const char *data, size_t len, void *ctx);
|
||||
static int callback(const char *data, size_t len, void *ctx);
|
||||
|
||||
public:
|
||||
Buffer(const char *buf, size_type len) :
|
||||
std::vector<char>(buf, buf+len)
|
||||
{ }
|
||||
Buffer(const char *buf, size_type len) : std::vector<char>(buf, buf + len) {}
|
||||
|
||||
Buffer(size_type count = 0) :
|
||||
std::vector<char>(count, 0)
|
||||
{ }
|
||||
Buffer(size_type count = 0) : std::vector<char>(count, 0) {}
|
||||
|
||||
// Encode JSON document /p j and append it to the buffer
|
||||
int encode(json_t *j, int flags = 0);
|
||||
// Encode JSON document /p j and append it to the buffer
|
||||
int encode(json_t *j, int flags = 0);
|
||||
|
||||
// Decode JSON document from the beginning of the buffer
|
||||
json_t * decode();
|
||||
// Decode JSON document from the beginning of the buffer
|
||||
json_t *decode();
|
||||
|
||||
void append(const char *data, size_t len)
|
||||
{
|
||||
insert(end(), data, data + len);
|
||||
}
|
||||
void append(const char *data, size_t len) { insert(end(), data, data + len); }
|
||||
};
|
||||
|
||||
} // namespace villas
|
||||
|
|
|
@ -10,22 +10,22 @@
|
|||
#include <villas/config.hpp>
|
||||
|
||||
// CPP stringification
|
||||
#define XSTR(x) STR(x)
|
||||
#define STR(x) #x
|
||||
#define XSTR(x) STR(x)
|
||||
#define STR(x) #x
|
||||
|
||||
// Some color escape codes for pretty log messages
|
||||
#ifdef LOG_COLOR_DISABLE
|
||||
#define CLR(clr, str) str
|
||||
#define CLR(clr, str) str
|
||||
#else
|
||||
#define CLR(clr, str) "\e[" XSTR(clr) "m" str "\e[0m"
|
||||
#define CLR(clr, str) "\e[" XSTR(clr) "m" str "\e[0m"
|
||||
#endif
|
||||
|
||||
#define CLR_GRY(str) CLR(30, str) // Print str in gray
|
||||
#define CLR_RED(str) CLR(31, str) // Print str in red
|
||||
#define CLR_GRN(str) CLR(32, str) // Print str in green
|
||||
#define CLR_YEL(str) CLR(33, str) // Print str in yellow
|
||||
#define CLR_BLU(str) CLR(34, str) // Print str in blue
|
||||
#define CLR_MAG(str) CLR(35, str) // Print str in magenta
|
||||
#define CLR_CYN(str) CLR(36, str) // Print str in cyan
|
||||
#define CLR_WHT(str) CLR(37, str) // Print str in white
|
||||
#define CLR_BLD(str) CLR( 1, str) // Print str in bold
|
||||
#define CLR_GRY(str) CLR(30, str) // Print str in gray
|
||||
#define CLR_RED(str) CLR(31, str) // Print str in red
|
||||
#define CLR_GRN(str) CLR(32, str) // Print str in green
|
||||
#define CLR_YEL(str) CLR(33, str) // Print str in yellow
|
||||
#define CLR_BLU(str) CLR(34, str) // Print str in blue
|
||||
#define CLR_MAG(str) CLR(35, str) // Print str in magenta
|
||||
#define CLR_CYN(str) CLR(36, str) // Print str in cyan
|
||||
#define CLR_WHT(str) CLR(37, str) // Print str in white
|
||||
#define CLR_BLD(str) CLR(1, str) // Print str in bold
|
||||
|
|
|
@ -11,20 +11,20 @@
|
|||
|
||||
// Common states for most objects in VILLAScommon (paths, nodes, hooks, plugins)
|
||||
enum class State {
|
||||
DESTROYED = 0,
|
||||
INITIALIZED = 1,
|
||||
PARSED = 2,
|
||||
CHECKED = 3,
|
||||
STARTED = 4,
|
||||
STOPPED = 5,
|
||||
PENDING_CONNECT = 6,
|
||||
CONNECTED = 7,
|
||||
PAUSED = 8,
|
||||
STARTING = 9,
|
||||
STOPPING = 10,
|
||||
PAUSING = 11,
|
||||
RESUMING = 12,
|
||||
PREPARED = 13
|
||||
DESTROYED = 0,
|
||||
INITIALIZED = 1,
|
||||
PARSED = 2,
|
||||
CHECKED = 3,
|
||||
STARTED = 4,
|
||||
STOPPED = 5,
|
||||
PENDING_CONNECT = 6,
|
||||
CONNECTED = 7,
|
||||
PAUSED = 8,
|
||||
STARTING = 9,
|
||||
STOPPING = 10,
|
||||
PAUSING = 11,
|
||||
RESUMING = 12,
|
||||
PREPARED = 13
|
||||
};
|
||||
|
||||
// Callback to destroy list elements.
|
||||
|
|
|
@ -18,26 +18,27 @@ int json_dumpfd(const json_t *json, int output, size_t flags);
|
|||
json_t *json_loadfd(int input, size_t flags, json_error_t *error);
|
||||
#endif
|
||||
|
||||
#if defined(WITH_CONFIG) && (LIBCONFIG_VER_MAJOR <= 1) && (LIBCONFIG_VER_MINOR < 5)
|
||||
#include <libconfig.h>
|
||||
#if defined(WITH_CONFIG) && (LIBCONFIG_VER_MAJOR <= 1) && \
|
||||
(LIBCONFIG_VER_MINOR < 5)
|
||||
#include <libconfig.h>
|
||||
|
||||
#define config_setting_lookup config_lookup_from
|
||||
#define config_setting_lookup config_lookup_from
|
||||
#endif
|
||||
|
||||
#ifdef __MACH__
|
||||
#include <libkern/OSByteOrder.h>
|
||||
#include <libkern/OSByteOrder.h>
|
||||
|
||||
#define le16toh(x) OSSwapLittleToHostInt16(x)
|
||||
#define le32toh(x) OSSwapLittleToHostInt32(x)
|
||||
#define le64toh(x) OSSwapLittleToHostInt64(x)
|
||||
#define be16toh(x) OSSwapBigToHostInt16(x)
|
||||
#define be32toh(x) OSSwapBigToHostInt32(x)
|
||||
#define be64toh(x) OSSwapBigToHostInt64(x)
|
||||
#define le16toh(x) OSSwapLittleToHostInt16(x)
|
||||
#define le32toh(x) OSSwapLittleToHostInt32(x)
|
||||
#define le64toh(x) OSSwapLittleToHostInt64(x)
|
||||
#define be16toh(x) OSSwapBigToHostInt16(x)
|
||||
#define be32toh(x) OSSwapBigToHostInt32(x)
|
||||
#define be64toh(x) OSSwapBigToHostInt64(x)
|
||||
|
||||
#define htole16(x) OSSwapHostToLittleInt16(x)
|
||||
#define htole32(x) OSSwapHostToLittleInt32(x)
|
||||
#define htole64(x) OSSwapHostToLittleInt64(x)
|
||||
#define htobe16(x) OSSwapHostToBigInt16(x)
|
||||
#define htobe32(x) OSSwapHostToBigInt32(x)
|
||||
#define htobe64(x) OSSwapHostToBigInt64(x)
|
||||
#define htole16(x) OSSwapHostToLittleInt16(x)
|
||||
#define htole32(x) OSSwapHostToLittleInt32(x)
|
||||
#define htole64(x) OSSwapHostToLittleInt64(x)
|
||||
#define htobe16(x) OSSwapHostToBigInt16(x)
|
||||
#define htobe32(x) OSSwapHostToBigInt32(x)
|
||||
#define htobe64(x) OSSwapHostToBigInt64(x)
|
||||
#endif // __MACH__
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
|
||||
#ifdef __linux__
|
||||
|
||||
#include <sched.h>
|
||||
#include <cstdint>
|
||||
#include <sched.h>
|
||||
|
||||
#include <villas/exceptions.hpp>
|
||||
|
||||
|
@ -20,153 +20,104 @@ namespace utils {
|
|||
class CpuSet {
|
||||
|
||||
protected:
|
||||
cpu_set_t *setp;
|
||||
cpu_set_t *setp;
|
||||
|
||||
unsigned num_cpus;
|
||||
size_t sz;
|
||||
unsigned num_cpus;
|
||||
size_t sz;
|
||||
|
||||
public:
|
||||
CpuSet() : num_cpus(sizeof(uintmax_t) * 8), sz(CPU_ALLOC_SIZE(num_cpus)) {
|
||||
|
||||
CpuSet() :
|
||||
num_cpus(sizeof(uintmax_t) * 8),
|
||||
sz(CPU_ALLOC_SIZE(num_cpus))
|
||||
{
|
||||
setp = CPU_ALLOC(num_cpus);
|
||||
if (!setp)
|
||||
throw MemoryAllocationError();
|
||||
|
||||
setp = CPU_ALLOC(num_cpus);
|
||||
if (!setp)
|
||||
throw MemoryAllocationError();
|
||||
zero();
|
||||
}
|
||||
|
||||
zero();
|
||||
}
|
||||
// Parses string with list of CPU ranges.
|
||||
//
|
||||
// @param str Human readable representation of the set.
|
||||
CpuSet(const std::string &str);
|
||||
|
||||
// Parses string with list of CPU ranges.
|
||||
//
|
||||
// @param str Human readable representation of the set.
|
||||
CpuSet(const std::string &str);
|
||||
CpuSet(const char *str);
|
||||
|
||||
CpuSet(const char *str);
|
||||
// Convert integer to cpu_set_t.
|
||||
//
|
||||
// @param set An integer number which is used as the mask
|
||||
CpuSet(uintmax_t set);
|
||||
|
||||
// Convert integer to cpu_set_t.
|
||||
//
|
||||
// @param set An integer number which is used as the mask
|
||||
CpuSet(uintmax_t set);
|
||||
// Convert cpu_set_t to an integer. */
|
||||
operator uintmax_t();
|
||||
|
||||
// Convert cpu_set_t to an integer. */
|
||||
operator uintmax_t();
|
||||
operator const cpu_set_t *() { return setp; }
|
||||
|
||||
operator const cpu_set_t*()
|
||||
{
|
||||
return setp;
|
||||
}
|
||||
// Returns human readable representation of the cpuset.
|
||||
//
|
||||
// The output format is a list of CPUs with ranges (for example, "0,1,3-9").
|
||||
operator std::string();
|
||||
|
||||
// Returns human readable representation of the cpuset.
|
||||
//
|
||||
// The output format is a list of CPUs with ranges (for example, "0,1,3-9").
|
||||
operator std::string();
|
||||
~CpuSet() { CPU_FREE(setp); }
|
||||
|
||||
~CpuSet()
|
||||
{
|
||||
CPU_FREE(setp);
|
||||
}
|
||||
CpuSet(const CpuSet &src) : CpuSet(src.num_cpus) {
|
||||
memcpy(setp, src.setp, sz);
|
||||
}
|
||||
|
||||
CpuSet(const CpuSet &src) :
|
||||
CpuSet(src.num_cpus)
|
||||
{
|
||||
memcpy(setp, src.setp, sz);
|
||||
}
|
||||
bool empty() const { return count() == 0; }
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
return count() == 0;
|
||||
}
|
||||
bool full() const { return count() == num_cpus; }
|
||||
|
||||
bool full() const
|
||||
{
|
||||
return count() == num_cpus;
|
||||
}
|
||||
unsigned count() const { return CPU_COUNT_S(sz, setp); }
|
||||
|
||||
unsigned count() const
|
||||
{
|
||||
return CPU_COUNT_S(sz, setp);
|
||||
}
|
||||
void zero() { CPU_ZERO_S(sz, setp); }
|
||||
|
||||
void zero()
|
||||
{
|
||||
CPU_ZERO_S(sz, setp);
|
||||
}
|
||||
size_t size() const { return sz; }
|
||||
|
||||
size_t size() const
|
||||
{
|
||||
return sz;
|
||||
}
|
||||
CpuSet operator~() {
|
||||
CpuSet full = UINTMAX_MAX;
|
||||
|
||||
CpuSet operator~()
|
||||
{
|
||||
CpuSet full = UINTMAX_MAX;
|
||||
return full ^ *this;
|
||||
}
|
||||
|
||||
return full ^ *this;
|
||||
}
|
||||
bool operator==(const CpuSet &rhs) { return CPU_EQUAL_S(sz, setp, rhs.setp); }
|
||||
|
||||
bool operator==(const CpuSet &rhs)
|
||||
{
|
||||
return CPU_EQUAL_S(sz, setp, rhs.setp);
|
||||
}
|
||||
CpuSet &operator&=(const CpuSet &rhs) {
|
||||
CPU_AND_S(sz, setp, setp, rhs.setp);
|
||||
return *this;
|
||||
}
|
||||
|
||||
CpuSet & operator&=(const CpuSet &rhs)
|
||||
{
|
||||
CPU_AND_S(sz, setp, setp, rhs.setp);
|
||||
return *this;
|
||||
}
|
||||
CpuSet &operator|=(const CpuSet &rhs) {
|
||||
CPU_OR_S(sz, setp, setp, rhs.setp);
|
||||
return *this;
|
||||
}
|
||||
|
||||
CpuSet & operator|=(const CpuSet &rhs)
|
||||
{
|
||||
CPU_OR_S(sz, setp, setp, rhs.setp);
|
||||
return *this;
|
||||
}
|
||||
CpuSet &operator^=(const CpuSet &rhs) {
|
||||
CPU_XOR_S(sz, setp, setp, rhs.setp);
|
||||
return *this;
|
||||
}
|
||||
|
||||
CpuSet & operator^=(const CpuSet &rhs)
|
||||
{
|
||||
CPU_XOR_S(sz, setp, setp, rhs.setp);
|
||||
return *this;
|
||||
}
|
||||
friend CpuSet operator&(CpuSet lhs, const CpuSet &rhs) {
|
||||
lhs &= rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
friend CpuSet operator&(CpuSet lhs, const CpuSet &rhs)
|
||||
{
|
||||
lhs &= rhs;
|
||||
return lhs;
|
||||
}
|
||||
friend CpuSet operator|(CpuSet lhs, const CpuSet &rhs) {
|
||||
lhs |= rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
friend CpuSet operator|(CpuSet lhs, const CpuSet &rhs)
|
||||
{
|
||||
lhs |= rhs;
|
||||
return lhs;
|
||||
}
|
||||
friend CpuSet operator^(CpuSet lhs, const CpuSet &rhs) {
|
||||
lhs ^= rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
friend CpuSet operator^(CpuSet lhs, const CpuSet &rhs)
|
||||
{
|
||||
lhs ^= rhs;
|
||||
return lhs;
|
||||
}
|
||||
bool operator[](size_t cpu) const { return isSet(cpu); }
|
||||
|
||||
bool operator[](size_t cpu) const
|
||||
{
|
||||
return isSet(cpu);
|
||||
}
|
||||
bool isSet(size_t cpu) const { return CPU_ISSET_S(cpu, sz, setp); }
|
||||
|
||||
bool isSet(size_t cpu) const
|
||||
{
|
||||
return CPU_ISSET_S(cpu, sz, setp);
|
||||
}
|
||||
void clear(size_t cpu) { CPU_CLR_S(cpu, sz, setp); }
|
||||
|
||||
void clear(size_t cpu)
|
||||
{
|
||||
CPU_CLR_S(cpu, sz, setp);
|
||||
}
|
||||
|
||||
void set(size_t cpu)
|
||||
{
|
||||
CPU_SET_S(cpu, sz, setp);
|
||||
}
|
||||
void set(size_t cpu) { CPU_SET_S(cpu, sz, setp); }
|
||||
};
|
||||
|
||||
} // namespace utils
|
||||
|
|
|
@ -12,25 +12,20 @@
|
|||
namespace villas {
|
||||
namespace dsp {
|
||||
|
||||
template<typename T>
|
||||
class ExponentialWindow {
|
||||
template <typename T> class ExponentialWindow {
|
||||
|
||||
protected:
|
||||
T a;
|
||||
T last;
|
||||
T a;
|
||||
T last;
|
||||
|
||||
public:
|
||||
ExponentialWindow(T _a, T i = 0) :
|
||||
a(a),
|
||||
last(i)
|
||||
{ }
|
||||
ExponentialWindow(T _a, T i = 0) : a(a), last(i) {}
|
||||
|
||||
T update(T in)
|
||||
{
|
||||
last = a * in + (1 - a) * last;
|
||||
T update(T in) {
|
||||
last = a * in + (1 - a) * last;
|
||||
|
||||
return last;
|
||||
}
|
||||
return last;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace dsp
|
||||
|
|
|
@ -13,27 +13,22 @@
|
|||
namespace villas {
|
||||
namespace dsp {
|
||||
|
||||
template<typename T>
|
||||
class MovingAverageWindow : public Window<T> {
|
||||
template <typename T> class MovingAverageWindow : public Window<T> {
|
||||
|
||||
protected:
|
||||
T state;
|
||||
T state;
|
||||
|
||||
public:
|
||||
MovingAverageWindow(size_t len, T i = 0) :
|
||||
Window<T>(len, i),
|
||||
state(i)
|
||||
{ }
|
||||
MovingAverageWindow(size_t len, T i = 0) : Window<T>(len, i), state(i) {}
|
||||
|
||||
T update(T in)
|
||||
{
|
||||
T out = Window<T>::update(in);
|
||||
T update(T in) {
|
||||
T out = Window<T>::update(in);
|
||||
|
||||
state += in;
|
||||
state -= out;
|
||||
state += in;
|
||||
state -= out;
|
||||
|
||||
return state / (double) Window<T>::getLength();
|
||||
}
|
||||
return state / (double)Window<T>::getLength();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace dsp
|
||||
|
|
|
@ -13,26 +13,26 @@ namespace dsp {
|
|||
class PID {
|
||||
|
||||
protected:
|
||||
double dt;
|
||||
double max;
|
||||
double min;
|
||||
double Kp;
|
||||
double Kd;
|
||||
double Ki;
|
||||
double pre_error;
|
||||
double integral;
|
||||
double dt;
|
||||
double max;
|
||||
double min;
|
||||
double Kp;
|
||||
double Kd;
|
||||
double Ki;
|
||||
double pre_error;
|
||||
double integral;
|
||||
|
||||
public:
|
||||
// Kp - proportional gain
|
||||
// Ki - Integral gain
|
||||
// Kd - derivative gain
|
||||
// dt - loop interval time
|
||||
// max - maximum value of manipulated variable
|
||||
// min - minimum value of manipulated variable
|
||||
PID(double _dt, double _max, double _min, double _Kp, double _Kd, double _Ki);
|
||||
// Kp - proportional gain
|
||||
// Ki - Integral gain
|
||||
// Kd - derivative gain
|
||||
// dt - loop interval time
|
||||
// max - maximum value of manipulated variable
|
||||
// min - minimum value of manipulated variable
|
||||
PID(double _dt, double _max, double _min, double _Kp, double _Kd, double _Ki);
|
||||
|
||||
// Returns the manipulated variable given a setpoint and current process value
|
||||
double calculate(double setpoint, double pv);
|
||||
// Returns the manipulated variable given a setpoint and current process value
|
||||
double calculate(double setpoint, double pv);
|
||||
};
|
||||
|
||||
} // namespace dsp
|
||||
|
|
|
@ -12,79 +12,61 @@
|
|||
namespace villas {
|
||||
namespace dsp {
|
||||
|
||||
template<typename T, typename Container = std::deque<T>>
|
||||
template <typename T, typename Container = std::deque<T>>
|
||||
class Window : protected Container {
|
||||
|
||||
public:
|
||||
using iterator = typename Container::iterator;
|
||||
using size_type = typename Container::size_type;
|
||||
using iterator = typename Container::iterator;
|
||||
using size_type = typename Container::size_type;
|
||||
|
||||
protected:
|
||||
virtual T filter(T in, size_type i) const { return in; }
|
||||
|
||||
virtual
|
||||
T filter(T in, size_type i) const
|
||||
{
|
||||
return in;
|
||||
}
|
||||
class transform_iterator : public Container::const_iterator {
|
||||
protected:
|
||||
const Window *window;
|
||||
|
||||
class transform_iterator : public Container::const_iterator {
|
||||
protected:
|
||||
const Window *window;
|
||||
using base_iterator = typename Container::const_iterator;
|
||||
|
||||
using base_iterator = typename Container::const_iterator;
|
||||
public:
|
||||
transform_iterator(const Window<T> *w)
|
||||
: base_iterator(w->Container::begin()), window(w) {}
|
||||
|
||||
public:
|
||||
transform_iterator(const Window<T> *w) :
|
||||
base_iterator(w->Container::begin()),
|
||||
window(w)
|
||||
{ }
|
||||
T operator*() {
|
||||
auto i = (*this) - window->begin();
|
||||
const auto &v = base_iterator::operator*();
|
||||
|
||||
T operator*()
|
||||
{
|
||||
auto i = (*this) - window->begin();
|
||||
const auto &v = base_iterator::operator*();
|
||||
|
||||
return window->filter(v, i);
|
||||
}
|
||||
};
|
||||
return window->filter(v, i);
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
Window(size_type l = 0, T i = 0) :
|
||||
Container(l, i)
|
||||
{ }
|
||||
Window(size_type l = 0, T i = 0) : Container(l, i) {}
|
||||
|
||||
T val(size_type pos)
|
||||
{
|
||||
return this->Container::operator[](pos);
|
||||
}
|
||||
T val(size_type pos) { return this->Container::operator[](pos); }
|
||||
|
||||
T update(T in)
|
||||
{
|
||||
Container::push_back(in);
|
||||
T update(T in) {
|
||||
Container::push_back(in);
|
||||
|
||||
auto out = (*this)[0];
|
||||
auto out = (*this)[0];
|
||||
|
||||
Container::pop_front();
|
||||
Container::pop_front();
|
||||
|
||||
return out;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// Expose a limited number of functions from deque
|
||||
// Expose a limited number of functions from deque
|
||||
|
||||
using Container::size;
|
||||
using Container::end;
|
||||
using Container::end;
|
||||
using Container::size;
|
||||
|
||||
transform_iterator begin() const noexcept
|
||||
{
|
||||
return transform_iterator(this);
|
||||
}
|
||||
transform_iterator begin() const noexcept { return transform_iterator(this); }
|
||||
|
||||
T operator[](size_type i) const noexcept
|
||||
{
|
||||
auto v = Container::operator[](i);
|
||||
T operator[](size_type i) const noexcept {
|
||||
auto v = Container::operator[](i);
|
||||
|
||||
return filter(v, i);
|
||||
}
|
||||
return filter(v, i);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace dsp
|
||||
|
|
|
@ -16,97 +16,82 @@
|
|||
namespace villas {
|
||||
namespace dsp {
|
||||
|
||||
template<typename T>// a0 = 1.0, double a1 = 0.0, double a2 = 0.0, double a3 = 0.0, double a4 = 0.0>
|
||||
template <
|
||||
typename T> // a0 = 1.0, double a1 = 0.0, double a2 = 0.0, double a3 = 0.0, double a4 = 0.0>
|
||||
class CosineWindow : public Window<T> {
|
||||
|
||||
public:
|
||||
using size_type = typename Window<T>::size_type;
|
||||
using size_type = typename Window<T>::size_type;
|
||||
|
||||
protected:
|
||||
std::vector<T> coefficients;
|
||||
std::vector<T> coefficients;
|
||||
|
||||
T correctionFactor;
|
||||
T correctionFactor;
|
||||
|
||||
virtual
|
||||
T filter(T in, size_type i) const
|
||||
{
|
||||
return in * coefficients[i];
|
||||
}
|
||||
virtual T filter(T in, size_type i) const { return in * coefficients[i]; }
|
||||
|
||||
public:
|
||||
CosineWindow(double a0, double a1, double a2, double a3, double a4, size_type len, T i = 0) :
|
||||
Window<T>(len, i),
|
||||
coefficients(len),
|
||||
correctionFactor(0)
|
||||
{
|
||||
for (unsigned i = 0; i < len; i++) {
|
||||
coefficients[i] = a0
|
||||
- a1 * cos(2 * M_PI * i / len)
|
||||
+ a2 * cos(4 * M_PI * i / len)
|
||||
- a3 * cos(6 * M_PI * i / len)
|
||||
+ a4 * cos(8 * M_PI * i / len);
|
||||
CosineWindow(double a0, double a1, double a2, double a3, double a4,
|
||||
size_type len, T i = 0)
|
||||
: Window<T>(len, i), coefficients(len), correctionFactor(0) {
|
||||
for (unsigned i = 0; i < len; i++) {
|
||||
coefficients[i] =
|
||||
a0 - a1 * cos(2 * M_PI * i / len) + a2 * cos(4 * M_PI * i / len) -
|
||||
a3 * cos(6 * M_PI * i / len) + a4 * cos(8 * M_PI * i / len);
|
||||
|
||||
correctionFactor += coefficients[i];
|
||||
}
|
||||
correctionFactor += coefficients[i];
|
||||
}
|
||||
|
||||
correctionFactor /= len;
|
||||
}
|
||||
correctionFactor /= len;
|
||||
}
|
||||
|
||||
virtual
|
||||
T getCorrectionFactor() const
|
||||
{
|
||||
return correctionFactor;
|
||||
}
|
||||
virtual T getCorrectionFactor() const { return correctionFactor; }
|
||||
};
|
||||
|
||||
// From: https://en.wikipedia.org/wiki/Window_function#Cosine-sum_windows
|
||||
|
||||
template<typename T>
|
||||
class RectangularWindow : public CosineWindow<T> {
|
||||
template <typename T> class RectangularWindow : public CosineWindow<T> {
|
||||
|
||||
public:
|
||||
RectangularWindow(typename Window<T>::size_type len, T i = 0) :
|
||||
CosineWindow<T>(1, 0., 0., 0., 0., len, i) {}
|
||||
RectangularWindow(typename Window<T>::size_type len, T i = 0)
|
||||
: CosineWindow<T>(1, 0., 0., 0., 0., len, i) {}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class HannWindow : public CosineWindow<T> {
|
||||
template <typename T> class HannWindow : public CosineWindow<T> {
|
||||
|
||||
public:
|
||||
HannWindow(typename Window<T>::size_type len, T i = 0) :
|
||||
CosineWindow<T>(0.5, 0.5, 0., 0., 0., len, i) {}
|
||||
HannWindow(typename Window<T>::size_type len, T i = 0)
|
||||
: CosineWindow<T>(0.5, 0.5, 0., 0., 0., len, i) {}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class HammingWindow : public CosineWindow<T> {
|
||||
template <typename T> class HammingWindow : public CosineWindow<T> {
|
||||
|
||||
public:
|
||||
HammingWindow(typename Window<T>::size_type len, T i = 0) :
|
||||
CosineWindow<T>(25./46, 1 - 25./46, 0., 0., 0., len, i) {}
|
||||
HammingWindow(typename Window<T>::size_type len, T i = 0)
|
||||
: CosineWindow<T>(25. / 46, 1 - 25. / 46, 0., 0., 0., len, i) {}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class FlattopWindow : public CosineWindow<T> {
|
||||
template <typename T> class FlattopWindow : public CosineWindow<T> {
|
||||
|
||||
public:
|
||||
FlattopWindow(typename Window<T>::size_type len, T i = 0) :
|
||||
CosineWindow<T>(0.21557895, 0.41663158, 0.277263158, 0.083578947, 0.006947368, len, i) {}
|
||||
FlattopWindow(typename Window<T>::size_type len, T i = 0)
|
||||
: CosineWindow<T>(0.21557895, 0.41663158, 0.277263158, 0.083578947,
|
||||
0.006947368, len, i) {}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class NuttallWindow : public CosineWindow<T> {
|
||||
template <typename T> class NuttallWindow : public CosineWindow<T> {
|
||||
|
||||
public:
|
||||
NuttallWindow(typename Window<T>::size_type len, T i = 0) :
|
||||
CosineWindow<T>(0.355768, 0.487396, 0.144232, 0.012604, 0., len, i) {}
|
||||
NuttallWindow(typename Window<T>::size_type len, T i = 0)
|
||||
: CosineWindow<T>(0.355768, 0.487396, 0.144232, 0.012604, 0., len, i) {}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class BlackmanWindow : public CosineWindow<T> {
|
||||
template <typename T> class BlackmanWindow : public CosineWindow<T> {
|
||||
|
||||
public:
|
||||
BlackmanWindow(typename Window<T>::size_type len, T i = 0) :
|
||||
CosineWindow<T>(0.3635819, 0.4891775, 0.1365995, 0.0106411, 0., len, i) {}
|
||||
BlackmanWindow(typename Window<T>::size_type len, T i = 0)
|
||||
: CosineWindow<T>(0.3635819, 0.4891775, 0.1365995, 0.0106411, 0., len,
|
||||
i) {}
|
||||
};
|
||||
|
||||
} // namespace dsp
|
||||
|
|
|
@ -7,11 +7,11 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <cerrno>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
#include <utility>
|
||||
#include <cerrno>
|
||||
|
||||
#include <fmt/core.h>
|
||||
#include <jansson.h>
|
||||
|
@ -23,151 +23,125 @@ namespace villas {
|
|||
class SystemError : public std::system_error {
|
||||
|
||||
public:
|
||||
SystemError(const std::string &what) :
|
||||
std::system_error(
|
||||
errno,
|
||||
std::system_category(),
|
||||
what
|
||||
)
|
||||
{ }
|
||||
SystemError(const std::string &what)
|
||||
: std::system_error(errno, std::system_category(), what) {}
|
||||
|
||||
template<typename... Args>
|
||||
SystemError(const std::string &what, Args&&... args) :
|
||||
SystemError(fmt::format(what, std::forward<Args>(args)...))
|
||||
{ }
|
||||
template <typename... Args>
|
||||
SystemError(const std::string &what, Args &&...args)
|
||||
: SystemError(fmt::format(what, std::forward<Args>(args)...)) {}
|
||||
};
|
||||
|
||||
class RuntimeError : public std::runtime_error {
|
||||
|
||||
public:
|
||||
template<typename... Args>
|
||||
RuntimeError(const std::string &what, Args&&... args) :
|
||||
std::runtime_error(fmt::format(what, std::forward<Args>(args)...))
|
||||
{ }
|
||||
template <typename... Args>
|
||||
RuntimeError(const std::string &what, Args &&...args)
|
||||
: std::runtime_error(fmt::format(what, std::forward<Args>(args)...)) {}
|
||||
};
|
||||
|
||||
class MemoryAllocationError : public RuntimeError {
|
||||
|
||||
public:
|
||||
MemoryAllocationError() :
|
||||
RuntimeError("Failed to allocate memory")
|
||||
{ }
|
||||
MemoryAllocationError() : RuntimeError("Failed to allocate memory") {}
|
||||
};
|
||||
|
||||
class JsonError : public std::runtime_error {
|
||||
|
||||
protected:
|
||||
const json_t *setting;
|
||||
json_error_t error;
|
||||
const json_t *setting;
|
||||
json_error_t error;
|
||||
|
||||
public:
|
||||
template<typename... Args>
|
||||
JsonError(const json_t *s, const json_error_t &e, const std::string &what = std::string(), Args&&... args) :
|
||||
std::runtime_error(fmt::format(what, std::forward<Args>(args)...)),
|
||||
setting(s),
|
||||
error(e)
|
||||
{ }
|
||||
template <typename... Args>
|
||||
JsonError(const json_t *s, const json_error_t &e,
|
||||
const std::string &what = std::string(), Args &&...args)
|
||||
: std::runtime_error(fmt::format(what, std::forward<Args>(args)...)),
|
||||
setting(s), error(e) {}
|
||||
|
||||
virtual
|
||||
const char * what() const noexcept
|
||||
{
|
||||
return fmt::format("{}: {} in {}:{}:{}",
|
||||
std::runtime_error::what(),
|
||||
error.text, error.source, error.line, error.column
|
||||
).c_str();
|
||||
}
|
||||
virtual const char *what() const noexcept {
|
||||
return fmt::format("{}: {} in {}:{}:{}", std::runtime_error::what(),
|
||||
error.text, error.source, error.line, error.column)
|
||||
.c_str();
|
||||
}
|
||||
};
|
||||
|
||||
class ConfigError : public std::runtime_error {
|
||||
|
||||
protected:
|
||||
// A setting-id referencing the setting.
|
||||
std::string id;
|
||||
json_t *setting;
|
||||
json_error_t error;
|
||||
// A setting-id referencing the setting.
|
||||
std::string id;
|
||||
json_t *setting;
|
||||
json_error_t error;
|
||||
|
||||
char *msg;
|
||||
char *msg;
|
||||
|
||||
std::string getMessage() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
std::string getMessage() const {
|
||||
std::stringstream ss;
|
||||
|
||||
ss << std::runtime_error::what() << std::endl;
|
||||
ss << std::runtime_error::what() << std::endl;
|
||||
|
||||
if (error.position >= 0) {
|
||||
ss << std::endl;
|
||||
ss << " " << error.text << " in " << error.source << ":" << error.line << ":" << error.column << std::endl;
|
||||
}
|
||||
if (error.position >= 0) {
|
||||
ss << std::endl;
|
||||
ss << " " << error.text << " in " << error.source << ":" << error.line
|
||||
<< ":" << error.column << std::endl;
|
||||
}
|
||||
|
||||
ss << std::endl << " Please consult the user documentation for details: " << std::endl;
|
||||
ss << " " << docUri();
|
||||
ss << std::endl
|
||||
<< " Please consult the user documentation for details: " << std::endl;
|
||||
ss << " " << docUri();
|
||||
|
||||
ss << std::endl;
|
||||
ss << std::endl;
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
public:
|
||||
~ConfigError()
|
||||
{
|
||||
if (msg)
|
||||
free(msg);
|
||||
}
|
||||
~ConfigError() {
|
||||
if (msg)
|
||||
free(msg);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
ConfigError(json_t *s, const std::string &i, const std::string &what = "Failed to parse configuration") :
|
||||
std::runtime_error(what),
|
||||
id(i),
|
||||
setting(s)
|
||||
{
|
||||
error.position = -1;
|
||||
template <typename... Args>
|
||||
ConfigError(json_t *s, const std::string &i,
|
||||
const std::string &what = "Failed to parse configuration")
|
||||
: std::runtime_error(what), id(i), setting(s) {
|
||||
error.position = -1;
|
||||
|
||||
msg = strdup(getMessage().c_str());
|
||||
}
|
||||
msg = strdup(getMessage().c_str());
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
ConfigError(json_t *s, const std::string &i, const std::string &what, Args&&... args) :
|
||||
std::runtime_error(fmt::format(what, std::forward<Args>(args)...)),
|
||||
id(i),
|
||||
setting(s)
|
||||
{
|
||||
error.position = -1;
|
||||
template <typename... Args>
|
||||
ConfigError(json_t *s, const std::string &i, const std::string &what,
|
||||
Args &&...args)
|
||||
: std::runtime_error(fmt::format(what, std::forward<Args>(args)...)),
|
||||
id(i), setting(s) {
|
||||
error.position = -1;
|
||||
|
||||
msg = strdup(getMessage().c_str());
|
||||
}
|
||||
msg = strdup(getMessage().c_str());
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
ConfigError(json_t *s, const json_error_t &e, const std::string &i, const std::string &what = "Failed to parse configuration") :
|
||||
std::runtime_error(what),
|
||||
id(i),
|
||||
setting(s),
|
||||
error(e)
|
||||
{
|
||||
msg = strdup(getMessage().c_str());
|
||||
}
|
||||
template <typename... Args>
|
||||
ConfigError(json_t *s, const json_error_t &e, const std::string &i,
|
||||
const std::string &what = "Failed to parse configuration")
|
||||
: std::runtime_error(what), id(i), setting(s), error(e) {
|
||||
msg = strdup(getMessage().c_str());
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
ConfigError(json_t *s, const json_error_t &e, const std::string &i, const std::string &what, Args&&... args) :
|
||||
std::runtime_error(fmt::format(what, std::forward<Args>(args)...)),
|
||||
id(i),
|
||||
setting(s),
|
||||
error(e)
|
||||
{
|
||||
msg = strdup(getMessage().c_str());
|
||||
}
|
||||
template <typename... Args>
|
||||
ConfigError(json_t *s, const json_error_t &e, const std::string &i,
|
||||
const std::string &what, Args &&...args)
|
||||
: std::runtime_error(fmt::format(what, std::forward<Args>(args)...)),
|
||||
id(i), setting(s), error(e) {
|
||||
msg = strdup(getMessage().c_str());
|
||||
}
|
||||
|
||||
std::string docUri() const
|
||||
{
|
||||
std::string baseUri = "https://villas.fein-aachen.org/doc/jump?";
|
||||
std::string docUri() const {
|
||||
std::string baseUri = "https://villas.fein-aachen.org/doc/jump?";
|
||||
|
||||
return baseUri + id;
|
||||
}
|
||||
return baseUri + id;
|
||||
}
|
||||
|
||||
virtual
|
||||
const char * what() const noexcept
|
||||
{
|
||||
return msg;
|
||||
}
|
||||
virtual const char *what() const noexcept { return msg; }
|
||||
};
|
||||
|
||||
} // namespace villas
|
||||
|
|
|
@ -7,274 +7,248 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <stdexcept>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
#include <villas/log.hpp>
|
||||
#include <villas/graph/vertex.hpp>
|
||||
#include <villas/graph/edge.hpp>
|
||||
#include <villas/graph/vertex.hpp>
|
||||
#include <villas/log.hpp>
|
||||
|
||||
namespace villas {
|
||||
namespace graph {
|
||||
|
||||
template<typename VertexType = Vertex, typename EdgeType = Edge>
|
||||
template <typename VertexType = Vertex, typename EdgeType = Edge>
|
||||
class DirectedGraph {
|
||||
public:
|
||||
using VertexIdentifier = Vertex::Identifier;
|
||||
using EdgeIdentifier = Edge::Identifier;
|
||||
using Path = std::list<EdgeIdentifier>;
|
||||
|
||||
using VertexIdentifier = Vertex::Identifier;
|
||||
using EdgeIdentifier = Edge::Identifier;
|
||||
using Path = std::list<EdgeIdentifier>;
|
||||
DirectedGraph(const std::string &name = "DirectedGraph")
|
||||
: lastVertexId(0), lastEdgeId(0), logger(logging.get(name)) {}
|
||||
|
||||
DirectedGraph(const std::string &name = "DirectedGraph") :
|
||||
lastVertexId(0),
|
||||
lastEdgeId(0),
|
||||
logger(logging.get(name))
|
||||
{ }
|
||||
std::shared_ptr<VertexType> getVertex(VertexIdentifier vertexId) const {
|
||||
// Cannot use [] operator, because creates non-existing elements
|
||||
// at() will throw std::out_of_range if element does not exist
|
||||
return vertices.at(vertexId);
|
||||
}
|
||||
|
||||
std::shared_ptr<VertexType> getVertex(VertexIdentifier vertexId) const
|
||||
{
|
||||
// Cannot use [] operator, because creates non-existing elements
|
||||
// at() will throw std::out_of_range if element does not exist
|
||||
return vertices.at(vertexId);
|
||||
}
|
||||
template <class UnaryPredicate>
|
||||
VertexIdentifier findVertex(UnaryPredicate p) {
|
||||
for (auto &v : vertices) {
|
||||
auto &vertexId = v.first;
|
||||
auto &vertex = v.second;
|
||||
|
||||
template<class UnaryPredicate>
|
||||
VertexIdentifier findVertex(UnaryPredicate p)
|
||||
{
|
||||
for (auto &v : vertices) {
|
||||
auto &vertexId = v.first;
|
||||
auto &vertex = v.second;
|
||||
if (p(vertex)) {
|
||||
return vertexId;
|
||||
}
|
||||
}
|
||||
|
||||
if (p(vertex)) {
|
||||
return vertexId;
|
||||
}
|
||||
}
|
||||
throw std::out_of_range("vertex not found");
|
||||
}
|
||||
|
||||
throw std::out_of_range("vertex not found");
|
||||
}
|
||||
std::shared_ptr<EdgeType> getEdge(EdgeIdentifier edgeId) const {
|
||||
if (edgeId >= lastEdgeId)
|
||||
throw std::invalid_argument("edge doesn't exist");
|
||||
|
||||
std::shared_ptr<EdgeType> getEdge(EdgeIdentifier edgeId) const
|
||||
{
|
||||
if (edgeId >= lastEdgeId)
|
||||
throw std::invalid_argument("edge doesn't exist");
|
||||
// Cannot use [] operator, because creates non-existing elements
|
||||
// at() will throw std::out_of_range if element does not exist
|
||||
return edges.at(edgeId);
|
||||
}
|
||||
|
||||
// Cannot use [] operator, because creates non-existing elements
|
||||
// at() will throw std::out_of_range if element does not exist
|
||||
return edges.at(edgeId);
|
||||
}
|
||||
std::size_t getEdgeCount() const { return edges.size(); }
|
||||
|
||||
std::size_t getEdgeCount() const
|
||||
{
|
||||
return edges.size();
|
||||
}
|
||||
std::size_t getVertexCount() const { return vertices.size(); }
|
||||
|
||||
std::size_t getVertexCount() const
|
||||
{
|
||||
return vertices.size();
|
||||
}
|
||||
VertexIdentifier addVertex(std::shared_ptr<VertexType> vertex) {
|
||||
vertex->id = lastVertexId++;
|
||||
|
||||
VertexIdentifier addVertex(std::shared_ptr<VertexType> vertex)
|
||||
{
|
||||
vertex->id = lastVertexId++;
|
||||
logger->debug("New vertex: {}", vertex->toString());
|
||||
vertices[vertex->id] = vertex;
|
||||
|
||||
logger->debug("New vertex: {}", vertex->toString());
|
||||
vertices[vertex->id] = vertex;
|
||||
return vertex->id;
|
||||
}
|
||||
|
||||
return vertex->id;
|
||||
}
|
||||
EdgeIdentifier addEdge(std::shared_ptr<EdgeType> edge,
|
||||
VertexIdentifier fromVertexId,
|
||||
VertexIdentifier toVertexId) {
|
||||
// Allocate edge id
|
||||
edge->id = lastEdgeId++;
|
||||
|
||||
EdgeIdentifier addEdge(std::shared_ptr<EdgeType> edge,
|
||||
VertexIdentifier fromVertexId,
|
||||
VertexIdentifier toVertexId)
|
||||
{
|
||||
// Allocate edge id
|
||||
edge->id = lastEdgeId++;
|
||||
// Connect it
|
||||
edge->from = fromVertexId;
|
||||
edge->to = toVertexId;
|
||||
|
||||
// Connect it
|
||||
edge->from = fromVertexId;
|
||||
edge->to = toVertexId;
|
||||
logger->debug("New edge {}: {} -> {}", edge->toString(), edge->from,
|
||||
edge->to);
|
||||
|
||||
logger->debug("New edge {}: {} -> {}", edge->toString(), edge->from, edge->to);
|
||||
// This is a directed graph, so only push edge to starting vertex
|
||||
getVertex(edge->from)->edges.push_back(edge->id);
|
||||
|
||||
// This is a directed graph, so only push edge to starting vertex
|
||||
getVertex(edge->from)->edges.push_back(edge->id);
|
||||
// Add new edge to graph
|
||||
edges[edge->id] = edge;
|
||||
|
||||
// Add new edge to graph
|
||||
edges[edge->id] = edge;
|
||||
return edge->id;
|
||||
}
|
||||
|
||||
return edge->id;
|
||||
}
|
||||
EdgeIdentifier addDefaultEdge(VertexIdentifier fromVertexId,
|
||||
VertexIdentifier toVertexId) {
|
||||
// Create a new edge
|
||||
std::shared_ptr<EdgeType> edge(new EdgeType);
|
||||
|
||||
EdgeIdentifier addDefaultEdge(VertexIdentifier fromVertexId,
|
||||
VertexIdentifier toVertexId)
|
||||
{
|
||||
// Create a new edge
|
||||
std::shared_ptr<EdgeType> edge(new EdgeType);
|
||||
return addEdge(edge, fromVertexId, toVertexId);
|
||||
}
|
||||
|
||||
return addEdge(edge, fromVertexId, toVertexId);
|
||||
}
|
||||
void removeEdge(EdgeIdentifier edgeId) {
|
||||
auto edge = getEdge(edgeId);
|
||||
auto startVertex = getVertex(edge->from);
|
||||
|
||||
void removeEdge(EdgeIdentifier edgeId)
|
||||
{
|
||||
auto edge = getEdge(edgeId);
|
||||
auto startVertex = getVertex(edge->from);
|
||||
// Remove edge only from starting vertex (this is a directed graph)
|
||||
logger->debug("Remove edge {} from vertex {}", edgeId, edge->from);
|
||||
startVertex->edges.remove(edgeId);
|
||||
|
||||
// Remove edge only from starting vertex (this is a directed graph)
|
||||
logger->debug("Remove edge {} from vertex {}", edgeId, edge->from);
|
||||
startVertex->edges.remove(edgeId);
|
||||
logger->debug("Remove edge {}", edgeId);
|
||||
edges.erase(edgeId);
|
||||
}
|
||||
|
||||
logger->debug("Remove edge {}", edgeId);
|
||||
edges.erase(edgeId);
|
||||
}
|
||||
void removeVertex(VertexIdentifier vertexId) {
|
||||
// Delete every edge that start or ends at this vertex
|
||||
auto it = edges.begin();
|
||||
while (it != edges.end()) {
|
||||
auto &edgeId = it->first;
|
||||
auto &edge = it->second;
|
||||
|
||||
void removeVertex(VertexIdentifier vertexId)
|
||||
{
|
||||
// Delete every edge that start or ends at this vertex
|
||||
auto it = edges.begin();
|
||||
while (it != edges.end()) {
|
||||
auto &edgeId = it->first;
|
||||
auto &edge = it->second;
|
||||
bool removeEdge = false;
|
||||
|
||||
bool removeEdge = false;
|
||||
if (edge->to == vertexId) {
|
||||
logger->debug("Remove edge {} from vertex {}'s edge list", edgeId,
|
||||
edge->from);
|
||||
|
||||
if (edge->to == vertexId) {
|
||||
logger->debug("Remove edge {} from vertex {}'s edge list",
|
||||
edgeId, edge->from);
|
||||
removeEdge = true;
|
||||
|
||||
removeEdge = true;
|
||||
auto startVertex = getVertex(edge->from);
|
||||
startVertex->edges.remove(edge->id);
|
||||
}
|
||||
|
||||
auto startVertex = getVertex(edge->from);
|
||||
startVertex->edges.remove(edge->id);
|
||||
if ((edge->from == vertexId) or removeEdge) {
|
||||
logger->debug("Remove edge {}", edgeId);
|
||||
// Remove edge from global edge list
|
||||
it = edges.erase(it);
|
||||
} else
|
||||
++it;
|
||||
}
|
||||
|
||||
}
|
||||
logger->debug("Remove vertex {}", vertexId);
|
||||
vertices.erase(vertexId);
|
||||
lastVertexId--;
|
||||
}
|
||||
|
||||
if ((edge->from == vertexId) or removeEdge) {
|
||||
logger->debug("Remove edge {}", edgeId);
|
||||
// Remove edge from global edge list
|
||||
it = edges.erase(it);
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
const std::list<EdgeIdentifier> &
|
||||
vertexGetEdges(VertexIdentifier vertexId) const {
|
||||
return getVertex(vertexId)->edges;
|
||||
}
|
||||
|
||||
logger->debug("Remove vertex {}", vertexId);
|
||||
vertices.erase(vertexId);
|
||||
lastVertexId--;
|
||||
}
|
||||
using check_path_fn = std::function<bool(const Path &)>;
|
||||
|
||||
const std::list<EdgeIdentifier>& vertexGetEdges(VertexIdentifier vertexId) const
|
||||
{
|
||||
return getVertex(vertexId)->edges;
|
||||
}
|
||||
static bool checkPath(const Path &) { return true; }
|
||||
|
||||
using check_path_fn = std::function<bool(const Path&)>;
|
||||
bool getPath(VertexIdentifier fromVertexId, VertexIdentifier toVertexId,
|
||||
Path &path, check_path_fn pathCheckFunc = checkPath) {
|
||||
if (fromVertexId == toVertexId)
|
||||
// Arrived at the destination
|
||||
return true;
|
||||
else {
|
||||
auto fromVertex = getVertex(fromVertexId);
|
||||
|
||||
static
|
||||
bool checkPath(const Path&)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
for (auto &edgeId : fromVertex->edges) {
|
||||
auto edgeOfFromVertex = getEdge(edgeId);
|
||||
|
||||
bool getPath(VertexIdentifier fromVertexId,
|
||||
VertexIdentifier toVertexId,
|
||||
Path &path,
|
||||
check_path_fn pathCheckFunc = checkPath)
|
||||
{
|
||||
if (fromVertexId == toVertexId)
|
||||
// Arrived at the destination
|
||||
return true;
|
||||
else {
|
||||
auto fromVertex = getVertex(fromVertexId);
|
||||
// Loop detection
|
||||
bool loop = false;
|
||||
for (auto &edgeIdInPath : path) {
|
||||
auto edgeInPath = getEdge(edgeIdInPath);
|
||||
if (edgeInPath->from == edgeOfFromVertex->to) {
|
||||
loop = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &edgeId : fromVertex->edges) {
|
||||
auto edgeOfFromVertex = getEdge(edgeId);
|
||||
if (loop) {
|
||||
logger->debug("Loop detected via edge {}", edgeId);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Loop detection
|
||||
bool loop = false;
|
||||
for (auto &edgeIdInPath : path) {
|
||||
auto edgeInPath = getEdge(edgeIdInPath);
|
||||
if (edgeInPath->from == edgeOfFromVertex->to) {
|
||||
loop = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Remember the path we're investigating to detect loops
|
||||
path.push_back(edgeId);
|
||||
|
||||
if (loop) {
|
||||
logger->debug("Loop detected via edge {}", edgeId);
|
||||
continue;
|
||||
}
|
||||
// Recursive, depth-first search
|
||||
if (getPath(edgeOfFromVertex->to, toVertexId, path, pathCheckFunc) and
|
||||
pathCheckFunc(path))
|
||||
// Path found, we're done
|
||||
return true;
|
||||
else
|
||||
// Tear down path that didn't lead to the destination
|
||||
path.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
// Remember the path we're investigating to detect loops
|
||||
path.push_back(edgeId);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Recursive, depth-first search
|
||||
if (getPath(edgeOfFromVertex->to, toVertexId, path, pathCheckFunc) and pathCheckFunc(path))
|
||||
// Path found, we're done
|
||||
return true;
|
||||
else
|
||||
// Tear down path that didn't lead to the destination
|
||||
path.pop_back();
|
||||
}
|
||||
}
|
||||
void dump(const std::string &fileName = "") const {
|
||||
logger->info("Vertices:");
|
||||
for (auto &v : vertices) {
|
||||
auto &vertex = v.second;
|
||||
|
||||
return false;
|
||||
}
|
||||
// Format connected vertices into a list
|
||||
std::stringstream ssEdges;
|
||||
for (auto &edge : vertex->edges) {
|
||||
ssEdges << getEdge(edge)->to << " ";
|
||||
}
|
||||
|
||||
void dump(const std::string &fileName = "") const
|
||||
{
|
||||
logger->info("Vertices:");
|
||||
for (auto &v : vertices) {
|
||||
auto &vertex = v.second;
|
||||
logger->info(" {} connected to: {}", vertex->toString(), ssEdges.str());
|
||||
}
|
||||
|
||||
// Format connected vertices into a list
|
||||
std::stringstream ssEdges;
|
||||
for (auto &edge : vertex->edges) {
|
||||
ssEdges << getEdge(edge)->to << " ";
|
||||
}
|
||||
std::fstream s(fileName, s.out | s.trunc);
|
||||
if (s.is_open()) {
|
||||
s << "digraph memgraph {" << std::endl;
|
||||
}
|
||||
|
||||
logger->info(" {} connected to: {}", vertex->toString(), ssEdges.str());
|
||||
}
|
||||
logger->info("Edges:");
|
||||
for (auto &e : edges) {
|
||||
auto &edge = e.second;
|
||||
|
||||
std::fstream s(fileName, s.out | s.trunc);
|
||||
if (s.is_open()) {
|
||||
s << "digraph memgraph {" << std::endl;
|
||||
}
|
||||
logger->info(" {}: {} -> {}", edge->toString(), edge->from, edge->to);
|
||||
if (s.is_open()) {
|
||||
auto from = getVertex(edge->from);
|
||||
auto to = getVertex(edge->to);
|
||||
|
||||
logger->info("Edges:");
|
||||
for (auto &e : edges) {
|
||||
auto &edge = e.second;
|
||||
s << std::dec;
|
||||
s << " \"" << *from << "\" -> \"" << *to << "\""
|
||||
<< " [label=\"" << *edge << "\"];" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
logger->info(" {}: {} -> {}", edge->toString(), edge->from, edge->to);
|
||||
if (s.is_open()) {
|
||||
auto from = getVertex(edge->from);
|
||||
auto to = getVertex(edge->to);
|
||||
|
||||
s << std::dec;
|
||||
s << " \"" << *from << "\" -> \"" << *to << "\""
|
||||
<< " [label=\"" << *edge << "\"];" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
if (s.is_open()) {
|
||||
s << "}" << std::endl;
|
||||
s.close();
|
||||
}
|
||||
}
|
||||
if (s.is_open()) {
|
||||
s << "}" << std::endl;
|
||||
s.close();
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
VertexIdentifier lastVertexId;
|
||||
EdgeIdentifier lastEdgeId;
|
||||
VertexIdentifier lastVertexId;
|
||||
EdgeIdentifier lastEdgeId;
|
||||
|
||||
std::map<VertexIdentifier, std::shared_ptr<VertexType>> vertices;
|
||||
std::map<EdgeIdentifier, std::shared_ptr<EdgeType>> edges;
|
||||
std::map<VertexIdentifier, std::shared_ptr<VertexType>> vertices;
|
||||
std::map<EdgeIdentifier, std::shared_ptr<EdgeType>> edges;
|
||||
|
||||
Logger logger;
|
||||
Logger logger;
|
||||
};
|
||||
|
||||
} // namespace graph
|
||||
|
|
|
@ -11,43 +11,31 @@ namespace villas {
|
|||
namespace graph {
|
||||
|
||||
class Edge {
|
||||
template<typename VertexType, typename EdgeType>
|
||||
friend class DirectedGraph;
|
||||
template <typename VertexType, typename EdgeType> friend class DirectedGraph;
|
||||
|
||||
public:
|
||||
using Identifier = std::size_t;
|
||||
using Identifier = std::size_t;
|
||||
|
||||
friend
|
||||
std::ostream& operator<< (std::ostream &stream, const Edge &edge)
|
||||
{
|
||||
return stream << edge.id;
|
||||
}
|
||||
friend std::ostream &operator<<(std::ostream &stream, const Edge &edge) {
|
||||
return stream << edge.id;
|
||||
}
|
||||
|
||||
bool operator==(const Edge &other)
|
||||
{
|
||||
return this->id == other.id;
|
||||
}
|
||||
bool operator==(const Edge &other) { return this->id == other.id; }
|
||||
|
||||
Vertex::Identifier getVertexTo() const
|
||||
{
|
||||
return to;
|
||||
}
|
||||
Vertex::Identifier getVertexTo() const { return to; }
|
||||
|
||||
Vertex::Identifier getVertexFrom() const
|
||||
{
|
||||
return from;
|
||||
}
|
||||
Vertex::Identifier getVertexFrom() const { return from; }
|
||||
|
||||
std::string toString() {
|
||||
std::stringstream ss;
|
||||
ss << *this;
|
||||
return ss.str();
|
||||
}
|
||||
std::string toString() {
|
||||
std::stringstream ss;
|
||||
ss << *this;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
private:
|
||||
Identifier id;
|
||||
Vertex::Identifier from;
|
||||
Vertex::Identifier to;
|
||||
Identifier id;
|
||||
Vertex::Identifier from;
|
||||
Vertex::Identifier to;
|
||||
};
|
||||
|
||||
} // namespace graph
|
||||
|
|
|
@ -11,42 +11,31 @@ namespace villas {
|
|||
namespace graph {
|
||||
|
||||
class Vertex {
|
||||
template<typename VertexType, typename EdgeType>
|
||||
friend class DirectedGraph;
|
||||
template <typename VertexType, typename EdgeType> friend class DirectedGraph;
|
||||
|
||||
public:
|
||||
using Identifier = std::size_t;
|
||||
using Identifier = std::size_t;
|
||||
|
||||
Vertex() :
|
||||
id(0)
|
||||
{ }
|
||||
Vertex() : id(0) {}
|
||||
|
||||
const Identifier& getIdentifier() const
|
||||
{
|
||||
return id;
|
||||
}
|
||||
const Identifier &getIdentifier() const { return id; }
|
||||
|
||||
friend
|
||||
std::ostream& operator<<(std::ostream &stream, const Vertex &vertex)
|
||||
{
|
||||
return stream << vertex.id;
|
||||
}
|
||||
friend std::ostream &operator<<(std::ostream &stream, const Vertex &vertex) {
|
||||
return stream << vertex.id;
|
||||
}
|
||||
|
||||
bool operator==(const Vertex &other)
|
||||
{
|
||||
return this->id == other.id;
|
||||
}
|
||||
bool operator==(const Vertex &other) { return this->id == other.id; }
|
||||
|
||||
std::string toString() {
|
||||
std::stringstream ss;
|
||||
ss << *this;
|
||||
return ss.str();
|
||||
}
|
||||
std::string toString() {
|
||||
std::stringstream ss;
|
||||
ss << *this;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
private:
|
||||
Identifier id;
|
||||
// HACK: how to resolve this circular type dependency?
|
||||
std::list<std::size_t> edges;
|
||||
Identifier id;
|
||||
// HACK: how to resolve this circular type dependency?
|
||||
std::list<std::size_t> edges;
|
||||
};
|
||||
|
||||
} // namespace graph
|
||||
|
|
|
@ -13,8 +13,8 @@
|
|||
|
||||
#include <villas/log.hpp>
|
||||
|
||||
#define HEIGHT (LOG_WIDTH - 55)
|
||||
#define SEQ 17
|
||||
#define HEIGHT (LOG_WIDTH - 55)
|
||||
#define SEQ 17
|
||||
|
||||
namespace villas {
|
||||
|
||||
|
@ -22,96 +22,78 @@ namespace villas {
|
|||
class Hist {
|
||||
|
||||
public:
|
||||
using cnt_t = uintmax_t;
|
||||
using idx_t = std::vector<cnt_t>::difference_type;
|
||||
using cnt_t = uintmax_t;
|
||||
using idx_t = std::vector<cnt_t>::difference_type;
|
||||
|
||||
// Initialize struct Hist with supplied values and allocate memory for buckets.
|
||||
Hist(int buckets = 0, cnt_t warmup = 0);
|
||||
// Initialize struct Hist with supplied values and allocate memory for buckets.
|
||||
Hist(int buckets = 0, cnt_t warmup = 0);
|
||||
|
||||
// Reset all counters and values back to zero.
|
||||
void reset();
|
||||
// Reset all counters and values back to zero.
|
||||
void reset();
|
||||
|
||||
// Count a value within its corresponding bucket.
|
||||
void put(double value);
|
||||
// Count a value within its corresponding bucket.
|
||||
void put(double value);
|
||||
|
||||
// Calcluate the variance of all counted values.
|
||||
double getVar() const;
|
||||
// Calcluate the variance of all counted values.
|
||||
double getVar() const;
|
||||
|
||||
// Calculate the mean average of all counted values.
|
||||
double getMean() const;
|
||||
// Calculate the mean average of all counted values.
|
||||
double getMean() const;
|
||||
|
||||
// Calculate the standard derivation of all counted values.
|
||||
double getStddev() const;
|
||||
// Calculate the standard derivation of all counted values.
|
||||
double getStddev() const;
|
||||
|
||||
// Print all statistical properties of distribution including a graphilcal plot of the histogram.
|
||||
void print(Logger logger, bool details) const;
|
||||
// Print all statistical properties of distribution including a graphilcal plot of the histogram.
|
||||
void print(Logger logger, bool details) const;
|
||||
|
||||
// Print ASCII style plot of histogram.
|
||||
void plot(Logger logger) const;
|
||||
// Print ASCII style plot of histogram.
|
||||
void plot(Logger logger) const;
|
||||
|
||||
// Dump histogram data in Matlab format.
|
||||
//
|
||||
// @return The string containing the dump. The caller is responsible to free() the buffer.
|
||||
char * dump() const;
|
||||
// Dump histogram data in Matlab format.
|
||||
//
|
||||
// @return The string containing the dump. The caller is responsible to free() the buffer.
|
||||
char *dump() const;
|
||||
|
||||
// Prints Matlab struct containing all infos to file.
|
||||
int dumpMatlab(FILE *f) const;
|
||||
// Prints Matlab struct containing all infos to file.
|
||||
int dumpMatlab(FILE *f) const;
|
||||
|
||||
// Write the histogram in JSON format to fiel \p f.
|
||||
int dumpJson(FILE *f) const;
|
||||
// Write the histogram in JSON format to fiel \p f.
|
||||
int dumpJson(FILE *f) const;
|
||||
|
||||
// Build a libjansson / JSON object of the histogram.
|
||||
json_t * toJson() const;
|
||||
// Build a libjansson / JSON object of the histogram.
|
||||
json_t *toJson() const;
|
||||
|
||||
double getHigh() const
|
||||
{
|
||||
return high;
|
||||
}
|
||||
double getHigh() const { return high; }
|
||||
|
||||
double getLow() const
|
||||
{
|
||||
return low;
|
||||
}
|
||||
double getLow() const { return low; }
|
||||
|
||||
double getHighest() const
|
||||
{
|
||||
return highest;
|
||||
}
|
||||
double getHighest() const { return highest; }
|
||||
|
||||
double getLowest() const
|
||||
{
|
||||
return lowest;
|
||||
}
|
||||
double getLowest() const { return lowest; }
|
||||
|
||||
double getLast() const
|
||||
{
|
||||
return last;
|
||||
}
|
||||
double getLast() const { return last; }
|
||||
|
||||
cnt_t getTotal() const
|
||||
{
|
||||
return total;
|
||||
}
|
||||
cnt_t getTotal() const { return total; }
|
||||
|
||||
protected:
|
||||
double resolution; // The distance between two adjacent buckets.
|
||||
double resolution; // The distance between two adjacent buckets.
|
||||
|
||||
double high; // The value of the highest bucket.
|
||||
double low; // The value of the lowest bucket.
|
||||
double high; // The value of the highest bucket.
|
||||
double low; // The value of the lowest bucket.
|
||||
|
||||
double highest; // The highest value observed (may be higher than #high).
|
||||
double lowest; // The lowest value observed (may be lower than #low).
|
||||
double last; // The last value which has been put into the buckets.
|
||||
double highest; // The highest value observed (may be higher than #high).
|
||||
double lowest; // The lowest value observed (may be lower than #low).
|
||||
double last; // The last value which has been put into the buckets.
|
||||
|
||||
cnt_t total; // Total number of counted values.
|
||||
cnt_t warmup; // Number of values which are used during warmup phase.
|
||||
cnt_t total; // Total number of counted values.
|
||||
cnt_t warmup; // Number of values which are used during warmup phase.
|
||||
|
||||
cnt_t higher; // The number of values which are higher than #high.
|
||||
cnt_t lower; // The number of values which are lower than #low.
|
||||
cnt_t higher; // The number of values which are higher than #high.
|
||||
cnt_t lower; // The number of values which are lower than #low.
|
||||
|
||||
std::vector<cnt_t> data; // Bucket counters.
|
||||
std::vector<cnt_t> data; // Bucket counters.
|
||||
|
||||
double _m[2], _s[2]; // Private variables for online variance calculation.
|
||||
double _m[2], _s[2]; // Private variables for online variance calculation.
|
||||
};
|
||||
|
||||
} // namespace villas
|
||||
|
|
|
@ -60,7 +60,7 @@ int getHugePageSize();
|
|||
int get_cpu_frequency(uint64_t *freq);
|
||||
|
||||
// Set SMP affinity of IRQ
|
||||
int setIRQAffinity(unsigned irq, uintmax_t aff , uintmax_t *old);
|
||||
int setIRQAffinity(unsigned irq, uintmax_t aff, uintmax_t *old);
|
||||
|
||||
} // namespace villas
|
||||
} // namespace kernel
|
||||
} // namespace villas
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <fstream>
|
||||
#include <list>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
@ -19,119 +19,105 @@ namespace villas {
|
|||
namespace kernel {
|
||||
namespace pci {
|
||||
|
||||
#define PCI_SLOT(devfn) (((devfn) >> 3) & 0x1f)
|
||||
#define PCI_FUNC(devfn) ((devfn) & 0x07)
|
||||
#define PCI_SLOT(devfn) (((devfn) >> 3) & 0x1f)
|
||||
#define PCI_FUNC(devfn) ((devfn)&0x07)
|
||||
|
||||
class Id {
|
||||
public:
|
||||
Id(const std::string &str);
|
||||
Id(const std::string &str);
|
||||
|
||||
Id(int vid = 0, int did = 0, int cc = 0) :
|
||||
vendor(vid),
|
||||
device(did),
|
||||
class_code(cc)
|
||||
{ }
|
||||
Id(int vid = 0, int did = 0, int cc = 0)
|
||||
: vendor(vid), device(did), class_code(cc) {}
|
||||
|
||||
bool operator==(const Id &i);
|
||||
bool operator==(const Id &i);
|
||||
|
||||
unsigned int vendor;
|
||||
unsigned int device;
|
||||
unsigned int class_code;
|
||||
unsigned int vendor;
|
||||
unsigned int device;
|
||||
unsigned int class_code;
|
||||
};
|
||||
|
||||
class Slot {
|
||||
public:
|
||||
Slot(const std::string &str);
|
||||
Slot(const std::string &str);
|
||||
|
||||
Slot(int dom = 0, int b = 0, int dev = 0, int fcn = 0) :
|
||||
domain(dom),
|
||||
bus(b),
|
||||
device(dev),
|
||||
function(fcn)
|
||||
{ }
|
||||
Slot(int dom = 0, int b = 0, int dev = 0, int fcn = 0)
|
||||
: domain(dom), bus(b), device(dev), function(fcn) {}
|
||||
|
||||
bool operator==(const Slot &s);
|
||||
bool operator==(const Slot &s);
|
||||
|
||||
unsigned int domain;
|
||||
unsigned int bus;
|
||||
unsigned int device;
|
||||
unsigned int function;
|
||||
unsigned int domain;
|
||||
unsigned int bus;
|
||||
unsigned int device;
|
||||
unsigned int function;
|
||||
};
|
||||
|
||||
struct Region {
|
||||
int num;
|
||||
uintptr_t start;
|
||||
uintptr_t end;
|
||||
unsigned long long flags;
|
||||
int num;
|
||||
uintptr_t start;
|
||||
uintptr_t end;
|
||||
unsigned long long flags;
|
||||
};
|
||||
|
||||
class Device {
|
||||
public:
|
||||
Device(Id i, Slot s) :
|
||||
id(i),
|
||||
slot(s),
|
||||
log(logging.get("kernel:pci"))
|
||||
{ }
|
||||
Device(Id i, Slot s) : id(i), slot(s), log(logging.get("kernel:pci")) {}
|
||||
|
||||
Device(Id i) :
|
||||
id(i),
|
||||
log(logging.get("kernel:pci"))
|
||||
{ }
|
||||
Device(Id i) : id(i), log(logging.get("kernel:pci")) {}
|
||||
|
||||
Device(Slot s) :
|
||||
slot(s),
|
||||
log(logging.get("kernel:pci"))
|
||||
{ }
|
||||
Device(Slot s) : slot(s), log(logging.get("kernel:pci")) {}
|
||||
|
||||
bool
|
||||
operator==(const Device &other);
|
||||
bool operator==(const Device &other);
|
||||
|
||||
// Get currently loaded driver for device
|
||||
std::string getDriver() const;
|
||||
// Get currently loaded driver for device
|
||||
std::string getDriver() const;
|
||||
|
||||
// Bind a new LKM to the PCI device
|
||||
bool attachDriver(const std::string &driver) const;
|
||||
// Bind a new LKM to the PCI device
|
||||
bool attachDriver(const std::string &driver) const;
|
||||
|
||||
// Return the IOMMU group of this PCI device or -1 if the device is not in a group
|
||||
int getIommuGroup() const;
|
||||
// Return the IOMMU group of this PCI device or -1 if the device is not in a group
|
||||
int getIommuGroup() const;
|
||||
|
||||
std::list<Region> getRegions() const;
|
||||
std::list<Region> getRegions() const;
|
||||
|
||||
// Write 32-bit BAR value from to the PCI configuration space
|
||||
void writeBar(uint32_t addr, unsigned bar = 0);
|
||||
// Write 32-bit BAR value from to the PCI configuration space
|
||||
void writeBar(uint32_t addr, unsigned bar = 0);
|
||||
|
||||
// If BAR values in config space and in the kernel do not match, rewrite
|
||||
// the BAR value of the kernel to PCIe config space
|
||||
void rewriteBar(unsigned bar = 0);
|
||||
// If BAR values in config space and in the kernel do not match, rewrite
|
||||
// the BAR value of the kernel to PCIe config space
|
||||
void rewriteBar(unsigned bar = 0);
|
||||
|
||||
// Read 32-bit BAR value from the PCI configuration space
|
||||
uint32_t readBar(unsigned bar = 0) const;
|
||||
// Read 32-bit BAR value from the PCI configuration space
|
||||
uint32_t readBar(unsigned bar = 0) const;
|
||||
|
||||
// Read 32-bit BAR value from the devices resource file.
|
||||
// This is what the kernel thinks the BAR should be.
|
||||
uint32_t readHostBar(unsigned bar = 0) const;
|
||||
// Read 32-bit BAR value from the devices resource file.
|
||||
// This is what the kernel thinks the BAR should be.
|
||||
uint32_t readHostBar(unsigned bar = 0) const;
|
||||
|
||||
Id id;
|
||||
Slot slot;
|
||||
|
||||
Id id;
|
||||
Slot slot;
|
||||
private:
|
||||
villas::Logger log;
|
||||
villas::Logger log;
|
||||
|
||||
protected:
|
||||
std::fstream openSysFs(const std::string &subPath, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) const;
|
||||
std::fstream
|
||||
openSysFs(const std::string &subPath,
|
||||
std::ios_base::openmode mode = std::ios_base::in |
|
||||
std::ios_base::out) const;
|
||||
};
|
||||
|
||||
class DeviceList : public std::list<std::shared_ptr<Device>> {
|
||||
public:
|
||||
// Initialize Linux PCI handle.
|
||||
//
|
||||
// This search for all available PCI devices under /sys/bus/pci
|
||||
DeviceList();
|
||||
// Initialize Linux PCI handle.
|
||||
//
|
||||
// This search for all available PCI devices under /sys/bus/pci
|
||||
DeviceList();
|
||||
|
||||
DeviceList::value_type lookupDevice(const Slot &s);
|
||||
DeviceList::value_type lookupDevice(const Slot &s);
|
||||
|
||||
DeviceList::value_type lookupDevice(const Id &i);
|
||||
DeviceList::value_type lookupDevice(const Id &i);
|
||||
|
||||
DeviceList::value_type lookupDevice(const Device &f);
|
||||
DeviceList::value_type lookupDevice(const Device &f);
|
||||
};
|
||||
|
||||
} // namespace pci
|
||||
|
|
|
@ -29,6 +29,6 @@ void setPriority(int priority);
|
|||
// @retval false Kernel is not patched.
|
||||
bool isPreemptible();
|
||||
|
||||
} // namespace villas
|
||||
} // namespace kernel
|
||||
} // namespace rt
|
||||
} // namespace kernel
|
||||
} // namespace villas
|
||||
|
|
|
@ -12,8 +12,8 @@
|
|||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <linux/vfio.h>
|
||||
#include <sys/mman.h>
|
||||
|
@ -26,63 +26,57 @@ namespace vfio {
|
|||
|
||||
// Backwards compatability with older kernels
|
||||
#ifdef VFIO_UPDATE_VADDR
|
||||
static constexpr
|
||||
size_t EXTENSION_SIZE = VFIO_UPDATE_VADDR+1;
|
||||
static constexpr size_t EXTENSION_SIZE = VFIO_UPDATE_VADDR + 1;
|
||||
#elif defined(VFIO_UNMAP_ALL)
|
||||
static constexpr
|
||||
size_t EXTENSION_SIZE = VFIO_UNMAP_ALL+1;
|
||||
static constexpr size_t EXTENSION_SIZE = VFIO_UNMAP_ALL + 1;
|
||||
#else
|
||||
static constexpr
|
||||
size_t EXTENSION_SIZE = VFIO_NOIOMMU_IOMMU+1;
|
||||
static constexpr size_t EXTENSION_SIZE = VFIO_NOIOMMU_IOMMU + 1;
|
||||
#endif
|
||||
|
||||
class Container {
|
||||
public:
|
||||
Container();
|
||||
Container();
|
||||
|
||||
// No copying allowed because we manage the vfio state in constructor and destructors
|
||||
Container(Container const&) = delete;
|
||||
void operator=(Container const&) = delete;
|
||||
// No copying allowed because we manage the vfio state in constructor and destructors
|
||||
Container(Container const &) = delete;
|
||||
void operator=(Container const &) = delete;
|
||||
|
||||
~Container();
|
||||
~Container();
|
||||
|
||||
void dump();
|
||||
void dump();
|
||||
|
||||
void attachGroup(std::shared_ptr<Group> group);
|
||||
void attachGroup(std::shared_ptr<Group> group);
|
||||
|
||||
std::shared_ptr<Device> attachDevice(const std::string& name, int groupIndex);
|
||||
std::shared_ptr<Device> attachDevice(pci::Device &pdev);
|
||||
std::shared_ptr<Device> attachDevice(const std::string &name, int groupIndex);
|
||||
std::shared_ptr<Device> attachDevice(pci::Device &pdev);
|
||||
|
||||
// Map VM to an IOVA, which is accessible by devices in the container
|
||||
//
|
||||
// @param virt virtual address of memory
|
||||
// @param phys IOVA where to map @p virt, -1 to use VFIO internal allocator
|
||||
// @param length size of memory region in bytes
|
||||
// @return IOVA address, UINTPTR_MAX on failure
|
||||
uintptr_t memoryMap(uintptr_t virt, uintptr_t phys, size_t length);
|
||||
// Map VM to an IOVA, which is accessible by devices in the container
|
||||
//
|
||||
// @param virt virtual address of memory
|
||||
// @param phys IOVA where to map @p virt, -1 to use VFIO internal allocator
|
||||
// @param length size of memory region in bytes
|
||||
// @return IOVA address, UINTPTR_MAX on failure
|
||||
uintptr_t memoryMap(uintptr_t virt, uintptr_t phys, size_t length);
|
||||
|
||||
// munmap() a region which has been mapped by vfio_map_region()
|
||||
bool memoryUnmap(uintptr_t phys, size_t length);
|
||||
// munmap() a region which has been mapped by vfio_map_region()
|
||||
bool memoryUnmap(uintptr_t phys, size_t length);
|
||||
|
||||
bool isIommuEnabled() const
|
||||
{
|
||||
return this->hasIommu;
|
||||
}
|
||||
bool isIommuEnabled() const { return this->hasIommu; }
|
||||
|
||||
private:
|
||||
std::shared_ptr<Group> getOrAttachGroup(int index);
|
||||
std::shared_ptr<Group> getOrAttachGroup(int index);
|
||||
|
||||
int fd;
|
||||
int version;
|
||||
int fd;
|
||||
int version;
|
||||
|
||||
std::array<bool, EXTENSION_SIZE> extensions;
|
||||
uint64_t iova_next; // Next free IOVA address
|
||||
bool hasIommu;
|
||||
std::array<bool, EXTENSION_SIZE> extensions;
|
||||
uint64_t iova_next; // Next free IOVA address
|
||||
bool hasIommu;
|
||||
|
||||
// All groups bound to this container
|
||||
std::list<std::shared_ptr<Group>> groups;
|
||||
// All groups bound to this container
|
||||
std::list<std::shared_ptr<Group>> groups;
|
||||
|
||||
Logger log;
|
||||
Logger log;
|
||||
};
|
||||
|
||||
} // namespace vfio
|
||||
|
|
|
@ -12,9 +12,9 @@
|
|||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <linux/vfio.h>
|
||||
#include <sys/mman.h>
|
||||
|
@ -28,74 +28,66 @@ namespace vfio {
|
|||
|
||||
class Device {
|
||||
public:
|
||||
Device(const std::string &name, int groupFileDescriptor, const kernel::pci::Device *pci_device = nullptr);
|
||||
~Device();
|
||||
Device(const std::string &name, int groupFileDescriptor,
|
||||
const kernel::pci::Device *pci_device = nullptr);
|
||||
~Device();
|
||||
|
||||
// No copying allowed because we manage the vfio state in constructor and destructors
|
||||
Device(Device const&) = delete;
|
||||
void operator=(Device const&) = delete;
|
||||
// No copying allowed because we manage the vfio state in constructor and destructors
|
||||
Device(Device const &) = delete;
|
||||
void operator=(Device const &) = delete;
|
||||
|
||||
bool reset();
|
||||
bool reset();
|
||||
|
||||
std::string getName() { return name; };
|
||||
|
||||
// Map a device memory region to the application address space (e.g. PCI BARs)
|
||||
void* regionMap(size_t index);
|
||||
|
||||
// munmap() a region which has been mapped by vfio_map_region()
|
||||
bool regionUnmap(size_t index);
|
||||
// munmap() a region which has been mapped by vfio_map_region()
|
||||
bool regionUnmap(size_t index);
|
||||
|
||||
// Get the size of a device memory region
|
||||
size_t regionGetSize(size_t index);
|
||||
// Get the size of a device memory region
|
||||
size_t regionGetSize(size_t index);
|
||||
|
||||
// Enable memory accesses and bus mastering for PCI device
|
||||
bool pciEnable();
|
||||
// Enable memory accesses and bus mastering for PCI device
|
||||
bool pciEnable();
|
||||
|
||||
int pciMsiInit(int efds[32]);
|
||||
int pciMsiDeinit(int efds[32]);
|
||||
bool pciMsiFind(int nos[32]);
|
||||
int pciMsiInit(int efds[32]);
|
||||
int pciMsiDeinit(int efds[32]);
|
||||
bool pciMsiFind(int nos[32]);
|
||||
|
||||
bool isVfioPciDevice() const;
|
||||
bool pciHotReset();
|
||||
bool isVfioPciDevice() const;
|
||||
bool pciHotReset();
|
||||
|
||||
int getFileDescriptor() const
|
||||
{
|
||||
return fd;
|
||||
}
|
||||
int getFileDescriptor() const { return fd; }
|
||||
|
||||
void dump();
|
||||
void dump();
|
||||
|
||||
bool isAttachedToGroup() const
|
||||
{
|
||||
return attachedToGroup;
|
||||
}
|
||||
bool isAttachedToGroup() const { return attachedToGroup; }
|
||||
|
||||
void setAttachedToGroup()
|
||||
{
|
||||
this->attachedToGroup = true;
|
||||
}
|
||||
void setAttachedToGroup() { this->attachedToGroup = true; }
|
||||
|
||||
private:
|
||||
// Name of the device as listed under
|
||||
// /sys/kernel/iommu_groups/[vfio_group::index]/devices/
|
||||
std::string name;
|
||||
// Name of the device as listed under
|
||||
// /sys/kernel/iommu_groups/[vfio_group::index]/devices/
|
||||
std::string name;
|
||||
|
||||
// VFIO device file descriptor
|
||||
int fd;
|
||||
// VFIO device file descriptor
|
||||
int fd;
|
||||
|
||||
bool attachedToGroup;
|
||||
int groupFd;
|
||||
bool attachedToGroup;
|
||||
int groupFd;
|
||||
|
||||
struct vfio_device_info info;
|
||||
struct vfio_device_info info;
|
||||
|
||||
std::vector<struct vfio_irq_info> irqs;
|
||||
std::vector<struct vfio_region_info> regions;
|
||||
std::vector<void*> mappings;
|
||||
std::vector<struct vfio_irq_info> irqs;
|
||||
std::vector<struct vfio_region_info> regions;
|
||||
std::vector<void *> mappings;
|
||||
|
||||
// libpci handle of the device
|
||||
const kernel::pci::Device *pci_device;
|
||||
// libpci handle of the device
|
||||
const kernel::pci::Device *pci_device;
|
||||
|
||||
Logger log;
|
||||
Logger log;
|
||||
};
|
||||
|
||||
} // namespace vfio
|
||||
|
|
|
@ -12,8 +12,8 @@
|
|||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <linux/vfio.h>
|
||||
#include <sys/mman.h>
|
||||
|
@ -25,60 +25,50 @@ namespace villas {
|
|||
namespace kernel {
|
||||
namespace vfio {
|
||||
|
||||
#define VFIO_PATH "/dev/vfio/"
|
||||
#define VFIO_DEV VFIO_PATH "vfio"
|
||||
#define VFIO_PATH "/dev/vfio/"
|
||||
#define VFIO_DEV VFIO_PATH "vfio"
|
||||
|
||||
class Group {
|
||||
public:
|
||||
Group(int index, bool iommuEnabled);
|
||||
~Group();
|
||||
Group(int index, bool iommuEnabled);
|
||||
~Group();
|
||||
|
||||
// No copying allowed because we manage the vfio state in constructor and destructors
|
||||
Group(Group const&) = delete;
|
||||
void operator=(Group const&) = delete;
|
||||
// No copying allowed because we manage the vfio state in constructor and destructors
|
||||
Group(Group const &) = delete;
|
||||
void operator=(Group const &) = delete;
|
||||
|
||||
void setAttachedToContainer()
|
||||
{
|
||||
attachedToContainer = true;
|
||||
}
|
||||
void setAttachedToContainer() { attachedToContainer = true; }
|
||||
|
||||
bool isAttachedToContainer()
|
||||
{
|
||||
return attachedToContainer;
|
||||
}
|
||||
bool isAttachedToContainer() { return attachedToContainer; }
|
||||
|
||||
int getFileDescriptor()
|
||||
{
|
||||
return fd;
|
||||
}
|
||||
int getFileDescriptor() { return fd; }
|
||||
|
||||
int getIndex()
|
||||
{
|
||||
return index;
|
||||
}
|
||||
int getIndex() { return index; }
|
||||
|
||||
std::shared_ptr<Device> attachDevice(std::shared_ptr<Device> device);
|
||||
std::shared_ptr<Device> attachDevice(const std::string& name, const kernel::pci::Device *pci_device = nullptr);
|
||||
std::shared_ptr<Device> attachDevice(std::shared_ptr<Device> device);
|
||||
std::shared_ptr<Device>
|
||||
attachDevice(const std::string &name,
|
||||
const kernel::pci::Device *pci_device = nullptr);
|
||||
|
||||
bool checkStatus();
|
||||
void dump();
|
||||
bool checkStatus();
|
||||
void dump();
|
||||
|
||||
private:
|
||||
// VFIO group file descriptor
|
||||
int fd;
|
||||
// VFIO group file descriptor
|
||||
int fd;
|
||||
|
||||
// Index of the IOMMU group as listed under /sys/kernel/iommu_groups/
|
||||
int index;
|
||||
// Index of the IOMMU group as listed under /sys/kernel/iommu_groups/
|
||||
int index;
|
||||
|
||||
bool attachedToContainer;
|
||||
bool attachedToContainer;
|
||||
|
||||
// Status of group
|
||||
struct vfio_group_status status;
|
||||
// Status of group
|
||||
struct vfio_group_status status;
|
||||
|
||||
// All devices owned by this group
|
||||
std::list<std::shared_ptr<Device>> devices;
|
||||
// All devices owned by this group
|
||||
std::list<std::shared_ptr<Device>> devices;
|
||||
|
||||
Logger log;
|
||||
Logger log;
|
||||
};
|
||||
|
||||
} // namespace vfio
|
||||
|
|
|
@ -13,34 +13,33 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <pthread.h>
|
||||
#include <string>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <villas/common.hpp>
|
||||
|
||||
#define LIST_CHUNKSIZE 16
|
||||
#define LIST_CHUNKSIZE 16
|
||||
|
||||
// Static list initialization
|
||||
#define LIST_INIT_STATIC(l) \
|
||||
__attribute__((constructor(105))) static \
|
||||
void UNIQUE(__ctor)() { \
|
||||
int ret __attribute__((unused)); \
|
||||
ret = list_init(l); \
|
||||
} \
|
||||
__attribute__((destructor(105))) static \
|
||||
void UNIQUE(__dtor)() { \
|
||||
int ret __attribute__((unused)); \
|
||||
ret = list_destroy(l, nullptr, false); \
|
||||
}
|
||||
#define LIST_INIT_STATIC(l) \
|
||||
__attribute__((constructor(105))) static void UNIQUE(__ctor)() { \
|
||||
int ret __attribute__((unused)); \
|
||||
ret = list_init(l); \
|
||||
} \
|
||||
__attribute__((destructor(105))) static void UNIQUE(__dtor)() { \
|
||||
int ret __attribute__((unused)); \
|
||||
ret = list_destroy(l, nullptr, false); \
|
||||
}
|
||||
|
||||
#define list_length(list) ((list)->length)
|
||||
#define list_at_safe(list, index) ((list)->length > index ? (list)->array[index] : nullptr)
|
||||
#define list_at(list, index) ((list)->array[index])
|
||||
#define list_length(list) ((list)->length)
|
||||
#define list_at_safe(list, index) \
|
||||
((list)->length > index ? (list)->array[index] : nullptr)
|
||||
#define list_at(list, index) ((list)->array[index])
|
||||
|
||||
#define list_first(list) list_at(list, 0)
|
||||
#define list_last(list) list_at(list, (list)->length-1)
|
||||
#define list_first(list) list_at(list, 0)
|
||||
#define list_last(list) list_at(list, (list)->length - 1)
|
||||
|
||||
namespace villas {
|
||||
|
||||
|
@ -49,24 +48,25 @@ typedef int (*cmp_cb_t)(const void *, const void *);
|
|||
|
||||
// The list data structure.
|
||||
struct List {
|
||||
enum State state; // The state of this list.
|
||||
void **array; // Array of pointers to list elements.
|
||||
size_t capacity; // Size of list::array in elements.
|
||||
size_t length; // Number of elements of list::array which are in use.
|
||||
pthread_mutex_t lock; // A mutex to allow thread-safe accesses.
|
||||
enum State state; // The state of this list.
|
||||
void **array; // Array of pointers to list elements.
|
||||
size_t capacity; // Size of list::array in elements.
|
||||
size_t length; // Number of elements of list::array which are in use.
|
||||
pthread_mutex_t lock; // A mutex to allow thread-safe accesses.
|
||||
};
|
||||
|
||||
// Initialize a list.
|
||||
//
|
||||
// @param l A pointer to the list data structure.
|
||||
int list_init(struct List *l) __attribute__ ((warn_unused_result));
|
||||
int list_init(struct List *l) __attribute__((warn_unused_result));
|
||||
|
||||
// Destroy a list and call destructors for all list elements
|
||||
//
|
||||
// @param free free() all list members during when calling list_destroy()
|
||||
// @param dtor A function pointer to a desctructor which will be called for every list item when the list is destroyed.
|
||||
// @param l A pointer to the list data structure.
|
||||
int list_destroy(struct List *l, dtor_cb_t dtor = nullptr, bool free = false) __attribute__ ((warn_unused_result));
|
||||
int list_destroy(struct List *l, dtor_cb_t dtor = nullptr, bool free = false)
|
||||
__attribute__((warn_unused_result));
|
||||
|
||||
// Append an element to the end of the list.
|
||||
void list_push(struct List *l, void *p);
|
||||
|
@ -82,7 +82,7 @@ int list_remove(struct List *l, size_t idx);
|
|||
int list_insert(struct List *l, size_t idx, void *p);
|
||||
|
||||
// Return the first element of the list for which cmp returns zero.
|
||||
void * list_search(struct List *l, cmp_cb_t cmp, const void *ctx);
|
||||
void *list_search(struct List *l, cmp_cb_t cmp, const void *ctx);
|
||||
|
||||
// Returns the number of occurences for which cmp returns zero when called on all list elements.
|
||||
int list_count(struct List *l, cmp_cb_t cmp, void *ctx);
|
||||
|
@ -109,24 +109,25 @@ void list_extend(struct List *l, size_t len, void *val);
|
|||
void list_filter(struct List *l, dtor_cb_t cb);
|
||||
|
||||
// Lookup an element from the list based on a name.
|
||||
template<typename T>
|
||||
T * list_lookup_name(struct List *l, const std::string &name)
|
||||
{
|
||||
return (T *) list_search(l, [](const void *a, const void *b) -> int {
|
||||
auto *e = reinterpret_cast<const T *>(a);
|
||||
auto *s = reinterpret_cast<const std::string *>(b);
|
||||
template <typename T>
|
||||
T *list_lookup_name(struct List *l, const std::string &name) {
|
||||
return (T *)list_search(
|
||||
l,
|
||||
[](const void *a, const void *b) -> int {
|
||||
auto *e = reinterpret_cast<const T *>(a);
|
||||
auto *s = reinterpret_cast<const std::string *>(b);
|
||||
|
||||
return *s == e->name ? 0 : 1;
|
||||
}, &name);
|
||||
return *s == e->name ? 0 : 1;
|
||||
},
|
||||
&name);
|
||||
}
|
||||
|
||||
// Lookup index of list element based on name.
|
||||
template<typename T>
|
||||
ssize_t list_lookup_index(struct List *l, const std::string &name)
|
||||
{
|
||||
auto *f = list_lookup_name<T>(l, name);
|
||||
template <typename T>
|
||||
ssize_t list_lookup_index(struct List *l, const std::string &name) {
|
||||
auto *f = list_lookup_name<T>(l, name);
|
||||
|
||||
return f ? list_index(l, f) : -1;
|
||||
return f ? list_index(l, f) : -1;
|
||||
}
|
||||
|
||||
} // namespace villas
|
||||
|
|
|
@ -8,13 +8,13 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <list>
|
||||
#include <string>
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <spdlog/fmt/ostr.h>
|
||||
#include <spdlog/sinks/dist_sink.h>
|
||||
#include <spdlog/sinks/stdout_color_sinks.h>
|
||||
#include <spdlog/fmt/ostr.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include <jansson.h>
|
||||
|
||||
|
@ -25,75 +25,68 @@ class Log;
|
|||
|
||||
using Logger = std::shared_ptr<spdlog::logger>;
|
||||
|
||||
extern
|
||||
Log logging;
|
||||
extern Log logging;
|
||||
|
||||
class Log {
|
||||
|
||||
public:
|
||||
using Level = spdlog::level::level_enum;
|
||||
using DefaultSink = std::shared_ptr<spdlog::sinks::stderr_color_sink_mt>;
|
||||
using DistSink = std::shared_ptr<spdlog::sinks::dist_sink_mt>;
|
||||
using Formatter = std::shared_ptr<spdlog::pattern_formatter>;
|
||||
using Level = spdlog::level::level_enum;
|
||||
using DefaultSink = std::shared_ptr<spdlog::sinks::stderr_color_sink_mt>;
|
||||
using DistSink = std::shared_ptr<spdlog::sinks::dist_sink_mt>;
|
||||
using Formatter = std::shared_ptr<spdlog::pattern_formatter>;
|
||||
|
||||
class Expression {
|
||||
public:
|
||||
std::string name;
|
||||
Level level;
|
||||
class Expression {
|
||||
public:
|
||||
std::string name;
|
||||
Level level;
|
||||
|
||||
Expression(const std::string &n, Level lvl) :
|
||||
name(n),
|
||||
level(lvl)
|
||||
{ }
|
||||
Expression(const std::string &n, Level lvl) : name(n), level(lvl) {}
|
||||
|
||||
Expression(json_t *json);
|
||||
};
|
||||
Expression(json_t *json);
|
||||
};
|
||||
|
||||
protected:
|
||||
DistSink sinks;
|
||||
DefaultSink sink;
|
||||
Formatter formatter;
|
||||
DistSink sinks;
|
||||
DefaultSink sink;
|
||||
Formatter formatter;
|
||||
|
||||
Level level;
|
||||
Level level;
|
||||
|
||||
std::string pattern; // Logging format.
|
||||
std::string prefix; // Prefix each line with this string.
|
||||
std::string pattern; // Logging format.
|
||||
std::string prefix; // Prefix each line with this string.
|
||||
|
||||
std::list<Expression> expressions;
|
||||
std::list<Expression> expressions;
|
||||
|
||||
public:
|
||||
Log(Level level = Level::info);
|
||||
|
||||
Log(Level level = Level::info);
|
||||
// Get the real usable log output width which fits into one line.
|
||||
int getWidth();
|
||||
|
||||
// Get the real usable log output width which fits into one line.
|
||||
int getWidth();
|
||||
void parse(json_t *json);
|
||||
|
||||
void parse(json_t *json);
|
||||
Logger get(const std::string &name);
|
||||
|
||||
Logger get(const std::string &name);
|
||||
void setFormatter(const std::string &pattern, const std::string &pfx = "");
|
||||
void setLevel(Level lvl);
|
||||
void setLevel(const std::string &lvl);
|
||||
|
||||
void setFormatter(const std::string &pattern, const std::string &pfx = "");
|
||||
void setLevel(Level lvl);
|
||||
void setLevel(const std::string &lvl);
|
||||
Level getLevel() const;
|
||||
std::string getLevelName() const;
|
||||
|
||||
Level getLevel() const;
|
||||
std::string getLevelName() const;
|
||||
void addSink(std::shared_ptr<spdlog::sinks::sink> sink) {
|
||||
sink->set_formatter(formatter->clone());
|
||||
sink->set_level(level);
|
||||
|
||||
void addSink(std::shared_ptr<spdlog::sinks::sink> sink)
|
||||
{
|
||||
sink->set_formatter(formatter->clone());
|
||||
sink->set_level(level);
|
||||
sinks->add_sink(sink);
|
||||
}
|
||||
|
||||
sinks->add_sink(sink);
|
||||
}
|
||||
void replaceStdSink(std::shared_ptr<spdlog::sinks::sink> sink) {
|
||||
sink->set_formatter(formatter->clone());
|
||||
sink->set_level(level);
|
||||
|
||||
void replaceStdSink(std::shared_ptr<spdlog::sinks::sink> sink)
|
||||
{
|
||||
sink->set_formatter(formatter->clone());
|
||||
sink->set_level(level);
|
||||
|
||||
sinks->sinks()[0] = sink;
|
||||
}
|
||||
sinks->sinks()[0] = sink;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace villas
|
||||
|
|
|
@ -21,34 +21,26 @@ namespace villas {
|
|||
// reside anywhere and represent different types of memory.
|
||||
class MemoryBlock {
|
||||
public:
|
||||
using deallocator_fn = std::function<void(MemoryBlock*)>;
|
||||
using deallocator_fn = std::function<void(MemoryBlock *)>;
|
||||
|
||||
using Ptr = std::shared_ptr<MemoryBlock>;
|
||||
using UniquePtr = std::unique_ptr<MemoryBlock, deallocator_fn>;
|
||||
using Ptr = std::shared_ptr<MemoryBlock>;
|
||||
using UniquePtr = std::unique_ptr<MemoryBlock, deallocator_fn>;
|
||||
|
||||
// cppcheck-suppress passedByValue
|
||||
MemoryBlock(size_t offset, size_t size, MemoryManager::AddressSpaceId addrSpaceId) :
|
||||
offset(offset), size(size), addrSpaceId(addrSpaceId) {}
|
||||
// cppcheck-suppress passedByValue
|
||||
MemoryBlock(size_t offset, size_t size,
|
||||
MemoryManager::AddressSpaceId addrSpaceId)
|
||||
: offset(offset), size(size), addrSpaceId(addrSpaceId) {}
|
||||
|
||||
MemoryManager::AddressSpaceId getAddrSpaceId() const
|
||||
{
|
||||
return addrSpaceId;
|
||||
}
|
||||
MemoryManager::AddressSpaceId getAddrSpaceId() const { return addrSpaceId; }
|
||||
|
||||
size_t getSize() const
|
||||
{
|
||||
return size;
|
||||
}
|
||||
size_t getSize() const { return size; }
|
||||
|
||||
size_t getOffset() const
|
||||
{
|
||||
return offset;
|
||||
}
|
||||
size_t getOffset() const { return offset; }
|
||||
|
||||
protected:
|
||||
size_t offset; // Offset (or address) inside address space
|
||||
size_t size; // Size in bytes of this block
|
||||
MemoryManager::AddressSpaceId addrSpaceId; // Identifier in memory graph
|
||||
size_t offset; // Offset (or address) inside address space
|
||||
size_t size; // Size in bytes of this block
|
||||
MemoryManager::AddressSpaceId addrSpaceId; // Identifier in memory graph
|
||||
};
|
||||
|
||||
// Wrapper for a MemoryBlock to access the underlying memory directly
|
||||
|
@ -60,59 +52,54 @@ protected:
|
|||
// with a moved unique pointer. Otherwise, it just stores a reference to the
|
||||
// memory block and it's the users responsibility to take care that the memory
|
||||
// block is valid.
|
||||
template<typename T>
|
||||
class MemoryAccessor {
|
||||
template <typename T> class MemoryAccessor {
|
||||
public:
|
||||
using Type = T;
|
||||
using Type = T;
|
||||
|
||||
// Take ownership of the MemoryBlock
|
||||
MemoryAccessor(std::unique_ptr<MemoryBlock, MemoryBlock::deallocator_fn> mem) :
|
||||
translation(MemoryManager::get().getTranslationFromProcess(mem->getAddrSpaceId())),
|
||||
memoryBlock(std::move(mem))
|
||||
{ }
|
||||
// Take ownership of the MemoryBlock
|
||||
MemoryAccessor(std::unique_ptr<MemoryBlock, MemoryBlock::deallocator_fn> mem)
|
||||
: translation(MemoryManager::get().getTranslationFromProcess(
|
||||
mem->getAddrSpaceId())),
|
||||
memoryBlock(std::move(mem)) {}
|
||||
|
||||
// Just act as an accessor, do not take ownership of MemoryBlock
|
||||
MemoryAccessor(const MemoryBlock &mem) :
|
||||
translation(MemoryManager::get().getTranslationFromProcess(mem.getAddrSpaceId()))
|
||||
{ }
|
||||
// Just act as an accessor, do not take ownership of MemoryBlock
|
||||
MemoryAccessor(const MemoryBlock &mem)
|
||||
: translation(MemoryManager::get().getTranslationFromProcess(
|
||||
mem.getAddrSpaceId())) {}
|
||||
|
||||
MemoryAccessor(const MemoryTranslation &translation) :
|
||||
translation(translation)
|
||||
{ }
|
||||
MemoryAccessor(const MemoryTranslation &translation)
|
||||
: translation(translation) {}
|
||||
|
||||
T & operator*() const
|
||||
{
|
||||
return *reinterpret_cast<T*>(translation.getLocalAddr(0));
|
||||
}
|
||||
T &operator*() const {
|
||||
return *reinterpret_cast<T *>(translation.getLocalAddr(0));
|
||||
}
|
||||
|
||||
T & operator[](size_t idx) const
|
||||
{
|
||||
const size_t offset = sizeof(T) * idx;
|
||||
return *reinterpret_cast<T*>(translation.getLocalAddr(offset));
|
||||
}
|
||||
T &operator[](size_t idx) const {
|
||||
const size_t offset = sizeof(T) * idx;
|
||||
return *reinterpret_cast<T *>(translation.getLocalAddr(offset));
|
||||
}
|
||||
|
||||
T* operator&() const
|
||||
{
|
||||
return reinterpret_cast<T*>(translation.getLocalAddr(0));
|
||||
}
|
||||
T *operator&() const {
|
||||
return reinterpret_cast<T *>(translation.getLocalAddr(0));
|
||||
}
|
||||
|
||||
T* operator->() const
|
||||
{
|
||||
return reinterpret_cast<T*>(translation.getLocalAddr(0));
|
||||
}
|
||||
T *operator->() const {
|
||||
return reinterpret_cast<T *>(translation.getLocalAddr(0));
|
||||
}
|
||||
|
||||
const MemoryBlock&
|
||||
getMemoryBlock() const
|
||||
{
|
||||
if (not memoryBlock) throw std::bad_alloc(); else return *memoryBlock;
|
||||
}
|
||||
const MemoryBlock &getMemoryBlock() const {
|
||||
if (not memoryBlock)
|
||||
throw std::bad_alloc();
|
||||
else
|
||||
return *memoryBlock;
|
||||
}
|
||||
|
||||
private:
|
||||
// Cached memory translation for fast access
|
||||
MemoryTranslation translation;
|
||||
// Cached memory translation for fast access
|
||||
MemoryTranslation translation;
|
||||
|
||||
// Take the unique pointer in case user wants this class to have ownership
|
||||
std::unique_ptr<MemoryBlock, MemoryBlock::deallocator_fn> memoryBlock;
|
||||
// Take the unique pointer in case user wants this class to have ownership
|
||||
std::unique_ptr<MemoryBlock, MemoryBlock::deallocator_fn> memoryBlock;
|
||||
};
|
||||
|
||||
// Base memory allocator
|
||||
|
@ -121,101 +108,89 @@ private:
|
|||
// The concept is explained here at [1].
|
||||
//
|
||||
// [1] https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern
|
||||
template<typename DerivedAllocator>
|
||||
class BaseAllocator {
|
||||
template <typename DerivedAllocator> class BaseAllocator {
|
||||
public:
|
||||
// memoryAddrSpaceId: memory that is managed by this allocator
|
||||
BaseAllocator(MemoryManager::AddressSpaceId memoryAddrSpaceId) :
|
||||
memoryAddrSpaceId(memoryAddrSpaceId)
|
||||
{
|
||||
// CRTP
|
||||
derivedAlloc = static_cast<DerivedAllocator*>(this);
|
||||
std::string loggerName = fmt::format("memory:", derivedAlloc->getName());
|
||||
logger = logging.get(loggerName);
|
||||
// memoryAddrSpaceId: memory that is managed by this allocator
|
||||
BaseAllocator(MemoryManager::AddressSpaceId memoryAddrSpaceId)
|
||||
: memoryAddrSpaceId(memoryAddrSpaceId) {
|
||||
// CRTP
|
||||
derivedAlloc = static_cast<DerivedAllocator *>(this);
|
||||
std::string loggerName = fmt::format("memory:", derivedAlloc->getName());
|
||||
logger = logging.get(loggerName);
|
||||
|
||||
// Default deallocation callback
|
||||
free = [&](MemoryBlock* mem) {
|
||||
logger->warn("no free callback defined for addr space {}, not freeing",
|
||||
mem->getAddrSpaceId());
|
||||
// Default deallocation callback
|
||||
free = [&](MemoryBlock *mem) {
|
||||
logger->warn("no free callback defined for addr space {}, not freeing",
|
||||
mem->getAddrSpaceId());
|
||||
|
||||
removeMemoryBlock(*mem);
|
||||
};
|
||||
}
|
||||
removeMemoryBlock(*mem);
|
||||
};
|
||||
}
|
||||
|
||||
BaseAllocator(std::unique_ptr<MemoryBlock, MemoryBlock::deallocator_fn> mem) :
|
||||
BaseAllocator(mem->getAddrSpaceId())
|
||||
{
|
||||
// cppcheck-suppress useInitializationList
|
||||
memoryBlock = std::move(mem);
|
||||
derivedAlloc = nullptr;
|
||||
}
|
||||
BaseAllocator(std::unique_ptr<MemoryBlock, MemoryBlock::deallocator_fn> mem)
|
||||
: BaseAllocator(mem->getAddrSpaceId()) {
|
||||
// cppcheck-suppress useInitializationList
|
||||
memoryBlock = std::move(mem);
|
||||
derivedAlloc = nullptr;
|
||||
}
|
||||
|
||||
virtual
|
||||
std::unique_ptr<MemoryBlock, MemoryBlock::deallocator_fn>
|
||||
virtual std::unique_ptr<MemoryBlock, MemoryBlock::deallocator_fn>
|
||||
|
||||
allocateBlock(size_t size) = 0;
|
||||
allocateBlock(size_t size) = 0;
|
||||
|
||||
template<typename T>
|
||||
MemoryAccessor<T>
|
||||
allocate(size_t num)
|
||||
{
|
||||
if (num == 0) {
|
||||
// Doesn't make sense to allocate an empty block
|
||||
logger->error("Trying to allocate empty memory");
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
template <typename T> MemoryAccessor<T> allocate(size_t num) {
|
||||
if (num == 0) {
|
||||
// Doesn't make sense to allocate an empty block
|
||||
logger->error("Trying to allocate empty memory");
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
const size_t size = num * sizeof(T);
|
||||
auto mem = allocateBlock(size);
|
||||
const size_t size = num * sizeof(T);
|
||||
auto mem = allocateBlock(size);
|
||||
|
||||
// Check if the allocated memory is really accessible by writing to the
|
||||
// allocated memory and reading back. Exponentially increase offset to
|
||||
// speed up testing.
|
||||
MemoryAccessor<volatile uint8_t> byteAccessor(*mem);
|
||||
size_t idx = 0;
|
||||
for (int i = 0; idx < mem->getSize(); i++, idx = (1 << i)) {
|
||||
auto val = static_cast<uint8_t>(i);
|
||||
byteAccessor[idx] = val;
|
||||
if (byteAccessor[idx] != val) {
|
||||
logger->error("Cannot access allocated memory");
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
}
|
||||
// Check if the allocated memory is really accessible by writing to the
|
||||
// allocated memory and reading back. Exponentially increase offset to
|
||||
// speed up testing.
|
||||
MemoryAccessor<volatile uint8_t> byteAccessor(*mem);
|
||||
size_t idx = 0;
|
||||
for (int i = 0; idx < mem->getSize(); i++, idx = (1 << i)) {
|
||||
auto val = static_cast<uint8_t>(i);
|
||||
byteAccessor[idx] = val;
|
||||
if (byteAccessor[idx] != val) {
|
||||
logger->error("Cannot access allocated memory");
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
}
|
||||
|
||||
return MemoryAccessor<T>(std::move(mem));
|
||||
}
|
||||
return MemoryAccessor<T>(std::move(mem));
|
||||
}
|
||||
|
||||
protected:
|
||||
void insertMemoryBlock(const MemoryBlock &mem)
|
||||
{
|
||||
auto & mm = MemoryManager::get();
|
||||
mm.createMapping(mem.getOffset(), 0, mem.getSize(),
|
||||
derivedAlloc->getName(),
|
||||
memoryAddrSpaceId,
|
||||
mem.getAddrSpaceId());
|
||||
}
|
||||
void insertMemoryBlock(const MemoryBlock &mem) {
|
||||
auto &mm = MemoryManager::get();
|
||||
mm.createMapping(mem.getOffset(), 0, mem.getSize(), derivedAlloc->getName(),
|
||||
memoryAddrSpaceId, mem.getAddrSpaceId());
|
||||
}
|
||||
|
||||
void removeMemoryBlock(const MemoryBlock &mem)
|
||||
{
|
||||
// This will also remove any mapping to and from the memory block
|
||||
auto & mm = MemoryManager::get();
|
||||
mm.removeAddressSpace(mem.getAddrSpaceId());
|
||||
}
|
||||
void removeMemoryBlock(const MemoryBlock &mem) {
|
||||
// This will also remove any mapping to and from the memory block
|
||||
auto &mm = MemoryManager::get();
|
||||
mm.removeAddressSpace(mem.getAddrSpaceId());
|
||||
}
|
||||
|
||||
MemoryManager::AddressSpaceId getAddrSpaceId() const
|
||||
{
|
||||
return memoryAddrSpaceId;
|
||||
}
|
||||
MemoryManager::AddressSpaceId getAddrSpaceId() const {
|
||||
return memoryAddrSpaceId;
|
||||
}
|
||||
|
||||
MemoryBlock::deallocator_fn free;
|
||||
Logger logger;
|
||||
MemoryBlock::deallocator_fn free;
|
||||
Logger logger;
|
||||
|
||||
// Optional, if allocator should own the memory block
|
||||
std::unique_ptr<MemoryBlock, MemoryBlock::deallocator_fn> memoryBlock;
|
||||
// Optional, if allocator should own the memory block
|
||||
std::unique_ptr<MemoryBlock, MemoryBlock::deallocator_fn> memoryBlock;
|
||||
|
||||
private:
|
||||
MemoryManager::AddressSpaceId memoryAddrSpaceId;
|
||||
DerivedAllocator* derivedAlloc;
|
||||
MemoryManager::AddressSpaceId memoryAddrSpaceId;
|
||||
DerivedAllocator *derivedAlloc;
|
||||
};
|
||||
|
||||
// Linear memory allocator
|
||||
|
@ -227,46 +202,35 @@ private:
|
|||
// together.
|
||||
class LinearAllocator : public BaseAllocator<LinearAllocator> {
|
||||
public:
|
||||
LinearAllocator(MemoryManager::AddressSpaceId memoryAddrSpaceId,
|
||||
size_t memorySize,
|
||||
size_t internalOffset = 0);
|
||||
LinearAllocator(MemoryManager::AddressSpaceId memoryAddrSpaceId,
|
||||
size_t memorySize, size_t internalOffset = 0);
|
||||
|
||||
LinearAllocator(std::unique_ptr<MemoryBlock, MemoryBlock::deallocator_fn> mem) :
|
||||
LinearAllocator(mem->getAddrSpaceId(), mem->getSize())
|
||||
{
|
||||
memoryBlock = std::move(mem);
|
||||
}
|
||||
LinearAllocator(std::unique_ptr<MemoryBlock, MemoryBlock::deallocator_fn> mem)
|
||||
: LinearAllocator(mem->getAddrSpaceId(), mem->getSize()) {
|
||||
memoryBlock = std::move(mem);
|
||||
}
|
||||
|
||||
size_t getAvailableMemory() const
|
||||
{
|
||||
return memorySize - nextFreeAddress;
|
||||
}
|
||||
size_t getAvailableMemory() const { return memorySize - nextFreeAddress; }
|
||||
|
||||
size_t getSize() const
|
||||
{
|
||||
return memorySize;
|
||||
}
|
||||
size_t getSize() const { return memorySize; }
|
||||
|
||||
std::string getName() const;
|
||||
std::string getName() const;
|
||||
|
||||
std::unique_ptr<MemoryBlock, MemoryBlock::deallocator_fn>
|
||||
allocateBlock(size_t size);
|
||||
std::unique_ptr<MemoryBlock, MemoryBlock::deallocator_fn>
|
||||
allocateBlock(size_t size);
|
||||
|
||||
private:
|
||||
static constexpr
|
||||
size_t alignBytes = sizeof(uintptr_t);
|
||||
static constexpr
|
||||
size_t alignMask = alignBytes - 1;
|
||||
static constexpr size_t alignBytes = sizeof(uintptr_t);
|
||||
static constexpr size_t alignMask = alignBytes - 1;
|
||||
|
||||
size_t getAlignmentPadding(uintptr_t addr) const
|
||||
{
|
||||
return (alignBytes - (addr & alignMask)) & alignMask;
|
||||
}
|
||||
size_t getAlignmentPadding(uintptr_t addr) const {
|
||||
return (alignBytes - (addr & alignMask)) & alignMask;
|
||||
}
|
||||
|
||||
size_t nextFreeAddress; // Next chunk will be allocated here
|
||||
size_t memorySize; // Total size of managed memory
|
||||
size_t internalOffset; // Offset in address space (usually 0)
|
||||
size_t allocationCount; // Number of individual allocations present
|
||||
size_t nextFreeAddress; // Next chunk will be allocated here
|
||||
size_t memorySize; // Total size of managed memory
|
||||
size_t internalOffset; // Offset in address space (usually 0)
|
||||
size_t allocationCount; // Number of individual allocations present
|
||||
};
|
||||
|
||||
// Wrapper around mmap() to create villas memory blocks
|
||||
|
@ -275,71 +239,48 @@ private:
|
|||
// host memory via the OS.
|
||||
class HostRam {
|
||||
public:
|
||||
class HostRamAllocator : public BaseAllocator<HostRamAllocator> {
|
||||
public:
|
||||
HostRamAllocator();
|
||||
class HostRamAllocator : public BaseAllocator<HostRamAllocator> {
|
||||
public:
|
||||
HostRamAllocator();
|
||||
|
||||
std::string getName() const
|
||||
{
|
||||
return "HostRamAlloc";
|
||||
}
|
||||
std::string getName() const { return "HostRamAlloc"; }
|
||||
|
||||
std::unique_ptr<MemoryBlock, MemoryBlock::deallocator_fn>
|
||||
allocateBlock(size_t size);
|
||||
};
|
||||
std::unique_ptr<MemoryBlock, MemoryBlock::deallocator_fn>
|
||||
allocateBlock(size_t size);
|
||||
};
|
||||
|
||||
static
|
||||
HostRamAllocator& getAllocator()
|
||||
{
|
||||
return allocator;
|
||||
}
|
||||
static HostRamAllocator &getAllocator() { return allocator; }
|
||||
|
||||
private:
|
||||
static
|
||||
HostRamAllocator allocator;
|
||||
static HostRamAllocator allocator;
|
||||
};
|
||||
|
||||
class HostDmaRam {
|
||||
public:
|
||||
class HostDmaRamAllocator : public LinearAllocator {
|
||||
public:
|
||||
HostDmaRamAllocator(int num);
|
||||
class HostDmaRamAllocator : public LinearAllocator {
|
||||
public:
|
||||
HostDmaRamAllocator(int num);
|
||||
|
||||
virtual
|
||||
~HostDmaRamAllocator();
|
||||
virtual ~HostDmaRamAllocator();
|
||||
|
||||
std::string getName() const
|
||||
{
|
||||
return getUdmaBufName(num);
|
||||
}
|
||||
std::string getName() const { return getUdmaBufName(num); }
|
||||
|
||||
private:
|
||||
int num;
|
||||
};
|
||||
private:
|
||||
int num;
|
||||
};
|
||||
|
||||
static
|
||||
HostDmaRamAllocator&
|
||||
getAllocator(int num = 0);
|
||||
static HostDmaRamAllocator &getAllocator(int num = 0);
|
||||
|
||||
private:
|
||||
static
|
||||
std::map<int, std::unique_ptr<HostDmaRamAllocator>> allocators;
|
||||
static std::map<int, std::unique_ptr<HostDmaRamAllocator>> allocators;
|
||||
|
||||
static
|
||||
std::string
|
||||
getUdmaBufName(int num);
|
||||
static std::string getUdmaBufName(int num);
|
||||
|
||||
static
|
||||
std::string
|
||||
getUdmaBufBasePath(int num);
|
||||
static std::string getUdmaBufBasePath(int num);
|
||||
|
||||
static
|
||||
size_t
|
||||
getUdmaBufBufSize(int num);
|
||||
static size_t getUdmaBufBufSize(int num);
|
||||
|
||||
static
|
||||
uintptr_t
|
||||
getUdmaBufPhysAddr(int num);
|
||||
static uintptr_t getUdmaBufPhysAddr(int num);
|
||||
};
|
||||
|
||||
} // namespace villas
|
||||
|
|
|
@ -8,13 +8,13 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <villas/log.hpp>
|
||||
#include <villas/graph/directed.hpp>
|
||||
#include <villas/log.hpp>
|
||||
|
||||
namespace villas {
|
||||
|
||||
|
@ -25,43 +25,32 @@ namespace villas {
|
|||
// multiple hops (memory mappings).
|
||||
class MemoryTranslation {
|
||||
public:
|
||||
// MemoryTranslation
|
||||
// @param src Base address of local address space
|
||||
// @param dst Base address of foreign address space
|
||||
// @param size Size of "memory window"
|
||||
MemoryTranslation(uintptr_t src, uintptr_t dst, size_t size)
|
||||
: src(src), dst(dst), size(size) {}
|
||||
|
||||
// MemoryTranslation
|
||||
// @param src Base address of local address space
|
||||
// @param dst Base address of foreign address space
|
||||
// @param size Size of "memory window"
|
||||
MemoryTranslation(uintptr_t src, uintptr_t dst, size_t size) :
|
||||
src(src),
|
||||
dst(dst),
|
||||
size(size)
|
||||
{ }
|
||||
uintptr_t getLocalAddr(uintptr_t addrInForeignAddrSpace) const;
|
||||
|
||||
uintptr_t getLocalAddr(uintptr_t addrInForeignAddrSpace) const;
|
||||
uintptr_t getForeignAddr(uintptr_t addrInLocalAddrSpace) const;
|
||||
|
||||
uintptr_t getForeignAddr(uintptr_t addrInLocalAddrSpace) const;
|
||||
size_t getSize() const { return size; }
|
||||
|
||||
size_t getSize() const
|
||||
{
|
||||
return size;
|
||||
}
|
||||
friend std::ostream &operator<<(std::ostream &stream,
|
||||
const MemoryTranslation &translation) {
|
||||
return stream << std::hex << "(src=0x" << translation.src << ", dst=0x"
|
||||
<< translation.dst << ", size=0x" << translation.size << ")";
|
||||
}
|
||||
|
||||
friend
|
||||
std::ostream& operator<< (std::ostream &stream, const MemoryTranslation &translation)
|
||||
{
|
||||
return stream << std::hex
|
||||
<< "(src=0x" << translation.src
|
||||
<< ", dst=0x" << translation.dst
|
||||
<< ", size=0x" << translation.size
|
||||
<< ")";
|
||||
}
|
||||
|
||||
// Merge two MemoryTranslations together
|
||||
MemoryTranslation &operator+=(const MemoryTranslation &other);
|
||||
// Merge two MemoryTranslations together
|
||||
MemoryTranslation &operator+=(const MemoryTranslation &other);
|
||||
|
||||
private:
|
||||
uintptr_t src; // Base address of local address space
|
||||
uintptr_t dst; // Base address of foreign address space
|
||||
size_t size; // Size of "memory window"
|
||||
uintptr_t src; // Base address of local address space
|
||||
uintptr_t dst; // Base address of foreign address space
|
||||
size_t size; // Size of "memory window"
|
||||
};
|
||||
|
||||
// Global memory manager to resolve addresses across address spaces
|
||||
|
@ -73,41 +62,39 @@ private:
|
|||
// memory mappings.
|
||||
class MemoryManager {
|
||||
private:
|
||||
// This is a singleton, so private constructor ...
|
||||
MemoryManager() :
|
||||
memoryGraph("memory:graph"),
|
||||
logger(logging.get("memory:manager"))
|
||||
{
|
||||
pathCheckFunc = [&](const MemoryGraph::Path &path) {
|
||||
return this->pathCheck(path);
|
||||
};
|
||||
}
|
||||
// This is a singleton, so private constructor ...
|
||||
MemoryManager()
|
||||
: memoryGraph("memory:graph"), logger(logging.get("memory:manager")) {
|
||||
pathCheckFunc = [&](const MemoryGraph::Path &path) {
|
||||
return this->pathCheck(path);
|
||||
};
|
||||
}
|
||||
|
||||
// ... and no copying or assigning
|
||||
MemoryManager(const MemoryManager&) = delete;
|
||||
MemoryManager &operator=(const MemoryManager&) = delete;
|
||||
// ... and no copying or assigning
|
||||
MemoryManager(const MemoryManager &) = delete;
|
||||
MemoryManager &operator=(const MemoryManager &) = delete;
|
||||
|
||||
// Custom edge in memory graph representing a memory mapping
|
||||
//
|
||||
// A memory mapping maps from one address space into another and can only be
|
||||
// traversed in the forward direction which reflects the nature of real
|
||||
// memory mappings.
|
||||
//
|
||||
// Implementation Notes:
|
||||
// The member #src is the address in the "from" address space, where the
|
||||
// destination address space is mapped. The member #dest is the address in
|
||||
// the destination address space, where the mapping points to. Often, #dest
|
||||
// will be zero for mappings to hardware, but consider the example when
|
||||
// mapping FPGA to application memory:
|
||||
// The application allocates a block 1kB at address 0x843001000 in its
|
||||
// address space. The mapping would then have a #dest address of 0x843001000
|
||||
// and a #size of 1024.
|
||||
class Mapping : public graph::Edge {
|
||||
public:
|
||||
std::string name; // Human-readable name
|
||||
uintptr_t src; // Base address in "from" address space
|
||||
uintptr_t dest; // Base address in "to" address space
|
||||
size_t size; // Size of the mapping
|
||||
// Custom edge in memory graph representing a memory mapping
|
||||
//
|
||||
// A memory mapping maps from one address space into another and can only be
|
||||
// traversed in the forward direction which reflects the nature of real
|
||||
// memory mappings.
|
||||
//
|
||||
// Implementation Notes:
|
||||
// The member #src is the address in the "from" address space, where the
|
||||
// destination address space is mapped. The member #dest is the address in
|
||||
// the destination address space, where the mapping points to. Often, #dest
|
||||
// will be zero for mappings to hardware, but consider the example when
|
||||
// mapping FPGA to application memory:
|
||||
// The application allocates a block 1kB at address 0x843001000 in its
|
||||
// address space. The mapping would then have a #dest address of 0x843001000
|
||||
// and a #size of 1024.
|
||||
class Mapping : public graph::Edge {
|
||||
public:
|
||||
std::string name; // Human-readable name
|
||||
uintptr_t src; // Base address in "from" address space
|
||||
uintptr_t dest; // Base address in "to" address space
|
||||
size_t size; // Size of the mapping
|
||||
|
||||
friend std::ostream&
|
||||
operator<< (std::ostream &stream, const Mapping &mapping)
|
||||
|
@ -125,129 +112,118 @@ private:
|
|||
{
|
||||
std::stringstream s;
|
||||
s << *this;
|
||||
return s.str();
|
||||
return s.str();
|
||||
}
|
||||
};
|
||||
|
||||
// Custom vertex in memory graph representing an address space
|
||||
//
|
||||
// Since most information in the memory graph is stored in the edges (memory
|
||||
// mappings), this is just a small extension to the default vertex. It only
|
||||
// associates an additional string #name for human-readability.
|
||||
class AddressSpace : public graph::Vertex {
|
||||
public:
|
||||
std::string name; // Human-readable name
|
||||
// Custom vertex in memory graph representing an address space
|
||||
//
|
||||
// Since most information in the memory graph is stored in the edges (memory
|
||||
// mappings), this is just a small extension to the default vertex. It only
|
||||
// associates an additional string #name for human-readability.
|
||||
class AddressSpace : public graph::Vertex {
|
||||
public:
|
||||
std::string name; // Human-readable name
|
||||
|
||||
friend std::ostream&
|
||||
operator<< (std::ostream &stream, const AddressSpace &addrSpace)
|
||||
{
|
||||
return stream << static_cast<const Vertex&>(addrSpace) << " = "
|
||||
<< addrSpace.name;
|
||||
}
|
||||
};
|
||||
friend std::ostream &operator<<(std::ostream &stream,
|
||||
const AddressSpace &addrSpace) {
|
||||
return stream << static_cast<const Vertex &>(addrSpace) << " = "
|
||||
<< addrSpace.name;
|
||||
}
|
||||
};
|
||||
|
||||
// Memory graph with custom edges and vertices for address resolution
|
||||
using MemoryGraph = graph::DirectedGraph<AddressSpace, Mapping>;
|
||||
// Memory graph with custom edges and vertices for address resolution
|
||||
using MemoryGraph = graph::DirectedGraph<AddressSpace, Mapping>;
|
||||
|
||||
public:
|
||||
using AddressSpaceId = MemoryGraph::VertexIdentifier;
|
||||
using MappingId = MemoryGraph::EdgeIdentifier;
|
||||
using AddressSpaceId = MemoryGraph::VertexIdentifier;
|
||||
using MappingId = MemoryGraph::EdgeIdentifier;
|
||||
|
||||
struct InvalidTranslation : public std::exception {};
|
||||
struct InvalidTranslation : public std::exception {};
|
||||
|
||||
// Get singleton instance
|
||||
static
|
||||
MemoryManager& get();
|
||||
// Get singleton instance
|
||||
static MemoryManager &get();
|
||||
|
||||
MemoryGraph & getGraph()
|
||||
{
|
||||
return memoryGraph;
|
||||
}
|
||||
MemoryGraph &getGraph() { return memoryGraph; }
|
||||
|
||||
AddressSpaceId getProcessAddressSpace()
|
||||
{
|
||||
return getOrCreateAddressSpace("process");
|
||||
}
|
||||
AddressSpaceId getProcessAddressSpace() {
|
||||
return getOrCreateAddressSpace("process");
|
||||
}
|
||||
|
||||
AddressSpaceId getPciAddressSpace()
|
||||
{
|
||||
return getOrCreateAddressSpace("pcie");
|
||||
}
|
||||
AddressSpaceId getPciAddressSpace() {
|
||||
return getOrCreateAddressSpace("pcie");
|
||||
}
|
||||
|
||||
AddressSpaceId getProcessAddressSpaceMemoryBlock(const std::string &memoryBlock)
|
||||
{
|
||||
return getOrCreateAddressSpace(getSlaveAddrSpaceName("process", memoryBlock));
|
||||
}
|
||||
AddressSpaceId
|
||||
getProcessAddressSpaceMemoryBlock(const std::string &memoryBlock) {
|
||||
return getOrCreateAddressSpace(
|
||||
getSlaveAddrSpaceName("process", memoryBlock));
|
||||
}
|
||||
|
||||
AddressSpaceId getOrCreateAddressSpace(std::string name);
|
||||
AddressSpaceId getOrCreateAddressSpace(std::string name);
|
||||
|
||||
void removeAddressSpace(const AddressSpaceId &addrSpaceId)
|
||||
{
|
||||
memoryGraph.removeVertex(addrSpaceId);
|
||||
}
|
||||
void removeAddressSpace(const AddressSpaceId &addrSpaceId) {
|
||||
memoryGraph.removeVertex(addrSpaceId);
|
||||
}
|
||||
|
||||
// Create a default mapping
|
||||
MappingId createMapping(uintptr_t src, uintptr_t dest, size_t size,
|
||||
const std::string &name,
|
||||
AddressSpaceId fromAddrSpace,
|
||||
AddressSpaceId toAddrSpace);
|
||||
// Create a default mapping
|
||||
MappingId createMapping(uintptr_t src, uintptr_t dest, size_t size,
|
||||
const std::string &name, AddressSpaceId fromAddrSpace,
|
||||
AddressSpaceId toAddrSpace);
|
||||
|
||||
// Add a mapping
|
||||
//
|
||||
// Can be used to derive from Mapping in order to implement custom
|
||||
// constructor/destructor.
|
||||
MappingId addMapping(std::shared_ptr<Mapping> mapping,
|
||||
AddressSpaceId fromAddrSpace,
|
||||
AddressSpaceId toAddrSpace);
|
||||
// Add a mapping
|
||||
//
|
||||
// Can be used to derive from Mapping in order to implement custom
|
||||
// constructor/destructor.
|
||||
MappingId addMapping(std::shared_ptr<Mapping> mapping,
|
||||
AddressSpaceId fromAddrSpace,
|
||||
AddressSpaceId toAddrSpace);
|
||||
|
||||
AddressSpaceId findAddressSpace(const std::string &name);
|
||||
AddressSpaceId findAddressSpace(const std::string &name);
|
||||
|
||||
std::list<AddressSpaceId> findPath(const AddressSpaceId &fromAddrSpaceId, const AddressSpaceId &toAddrSpaceId);
|
||||
std::list<AddressSpaceId> findPath(const AddressSpaceId &fromAddrSpaceId,
|
||||
const AddressSpaceId &toAddrSpaceId);
|
||||
|
||||
MemoryTranslation getTranslation(const AddressSpaceId &fromAddrSpaceId, const AddressSpaceId &toAddrSpaceId);
|
||||
MemoryTranslation getTranslation(const AddressSpaceId &fromAddrSpaceId,
|
||||
const AddressSpaceId &toAddrSpaceId);
|
||||
|
||||
// cppcheck-suppress passedByValue
|
||||
MemoryTranslation getTranslationFromProcess(AddressSpaceId foreignAddrSpaceId)
|
||||
{
|
||||
return getTranslation(getProcessAddressSpace(), foreignAddrSpaceId);
|
||||
}
|
||||
// cppcheck-suppress passedByValue
|
||||
MemoryTranslation
|
||||
getTranslationFromProcess(AddressSpaceId foreignAddrSpaceId) {
|
||||
return getTranslation(getProcessAddressSpace(), foreignAddrSpaceId);
|
||||
}
|
||||
|
||||
static
|
||||
std::string getSlaveAddrSpaceName(const std::string &ipInstance, const std::string &memoryBlock)
|
||||
{
|
||||
return ipInstance + "/" + memoryBlock;
|
||||
}
|
||||
static std::string getSlaveAddrSpaceName(const std::string &ipInstance,
|
||||
const std::string &memoryBlock) {
|
||||
return ipInstance + "/" + memoryBlock;
|
||||
}
|
||||
|
||||
static
|
||||
std::string getMasterAddrSpaceName(const std::string &ipInstance, const std::string &busInterface)
|
||||
{
|
||||
return ipInstance + ":" + busInterface;
|
||||
}
|
||||
static std::string getMasterAddrSpaceName(const std::string &ipInstance,
|
||||
const std::string &busInterface) {
|
||||
return ipInstance + ":" + busInterface;
|
||||
}
|
||||
|
||||
private:
|
||||
// Convert a Mapping to MemoryTranslation for calculations
|
||||
static
|
||||
MemoryTranslation getTranslationFromMapping(const Mapping &mapping)
|
||||
{
|
||||
return MemoryTranslation(mapping.src, mapping.dest, mapping.size);
|
||||
}
|
||||
// Convert a Mapping to MemoryTranslation for calculations
|
||||
static MemoryTranslation getTranslationFromMapping(const Mapping &mapping) {
|
||||
return MemoryTranslation(mapping.src, mapping.dest, mapping.size);
|
||||
}
|
||||
|
||||
bool pathCheck(const MemoryGraph::Path &path);
|
||||
bool pathCheck(const MemoryGraph::Path &path);
|
||||
|
||||
// Directed graph that stores address spaces and memory mappings
|
||||
MemoryGraph memoryGraph;
|
||||
// Directed graph that stores address spaces and memory mappings
|
||||
MemoryGraph memoryGraph;
|
||||
|
||||
// Cache mapping of names to address space ids for fast lookup
|
||||
std::map<std::string, AddressSpaceId> addrSpaceLookup;
|
||||
// Cache mapping of names to address space ids for fast lookup
|
||||
std::map<std::string, AddressSpaceId> addrSpaceLookup;
|
||||
|
||||
// Logger for universal access in this class
|
||||
Logger logger;
|
||||
// Logger for universal access in this class
|
||||
Logger logger;
|
||||
|
||||
MemoryGraph::check_path_fn pathCheckFunc;
|
||||
MemoryGraph::check_path_fn pathCheckFunc;
|
||||
|
||||
// Static pointer to global instance, because this is a singleton
|
||||
static
|
||||
MemoryManager* instance;
|
||||
// Static pointer to global instance, because this is a singleton
|
||||
static MemoryManager *instance;
|
||||
};
|
||||
|
||||
} // namespace villas
|
||||
|
|
|
@ -9,13 +9,13 @@
|
|||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <jansson.h>
|
||||
#include <list>
|
||||
#include <spdlog/fmt/ostr.h>
|
||||
#include <string>
|
||||
|
||||
#include <villas/log.hpp>
|
||||
#include <villas/common.hpp>
|
||||
#include <villas/log.hpp>
|
||||
|
||||
namespace villas {
|
||||
namespace plugin {
|
||||
|
@ -24,163 +24,129 @@ namespace plugin {
|
|||
class Plugin;
|
||||
class Registry;
|
||||
|
||||
extern
|
||||
Registry *registry;
|
||||
extern Registry *registry;
|
||||
|
||||
template<typename T = Plugin>
|
||||
using List = std::list<T *>;
|
||||
template <typename T = Plugin> using List = std::list<T *>;
|
||||
|
||||
class SubRegistry {
|
||||
|
||||
public:
|
||||
virtual
|
||||
List<> lookup() = 0;
|
||||
virtual List<> lookup() = 0;
|
||||
};
|
||||
|
||||
class Registry {
|
||||
|
||||
protected:
|
||||
List<> plugins;
|
||||
List<SubRegistry> registries;
|
||||
List<> plugins;
|
||||
List<SubRegistry> registries;
|
||||
|
||||
public:
|
||||
Logger getLogger() { return logging.get("plugin:registry"); }
|
||||
|
||||
Logger getLogger()
|
||||
{
|
||||
return logging.get("plugin:registry");
|
||||
}
|
||||
void add(Plugin *p) { plugins.push_back(p); }
|
||||
|
||||
void add(Plugin *p)
|
||||
{
|
||||
plugins.push_back(p);
|
||||
}
|
||||
void addSubRegistry(SubRegistry *sr) { registries.push_back(sr); }
|
||||
|
||||
void addSubRegistry(SubRegistry *sr)
|
||||
{
|
||||
registries.push_back(sr);
|
||||
}
|
||||
void remove(Plugin *p) { plugins.remove(p); }
|
||||
|
||||
void remove(Plugin *p)
|
||||
{
|
||||
plugins.remove(p);
|
||||
}
|
||||
// Get all plugins including sub-registries
|
||||
List<> lookup() {
|
||||
List<> all;
|
||||
|
||||
// Get all plugins including sub-registries
|
||||
List<> lookup() {
|
||||
List<> all;
|
||||
all.insert(all.end(), plugins.begin(), plugins.end());
|
||||
|
||||
all.insert(all.end(), plugins.begin(), plugins.end());
|
||||
for (auto r : registries) {
|
||||
auto p = r->lookup();
|
||||
|
||||
for (auto r : registries) {
|
||||
auto p = r->lookup();
|
||||
all.insert(all.end(), p.begin(), p.end());
|
||||
}
|
||||
|
||||
all.insert(all.end(), p.begin(), p.end());
|
||||
}
|
||||
return all;
|
||||
}
|
||||
|
||||
return all;
|
||||
}
|
||||
// Get all plugins of specific type
|
||||
template <typename T = Plugin> List<T> lookup() {
|
||||
List<T> list;
|
||||
|
||||
// Get all plugins of specific type
|
||||
template<typename T = Plugin>
|
||||
List<T> lookup()
|
||||
{
|
||||
List<T> list;
|
||||
for (Plugin *p : lookup()) {
|
||||
T *t = dynamic_cast<T *>(p);
|
||||
if (t)
|
||||
list.push_back(t);
|
||||
}
|
||||
|
||||
for (Plugin *p : lookup()) {
|
||||
T *t = dynamic_cast<T *>(p);
|
||||
if (t)
|
||||
list.push_back(t);
|
||||
}
|
||||
// Sort alphabetically
|
||||
list.sort(
|
||||
[](const T *a, const T *b) { return a->getName() < b->getName(); });
|
||||
|
||||
// Sort alphabetically
|
||||
list.sort([](const T *a, const T *b) {
|
||||
return a->getName() < b->getName();
|
||||
});
|
||||
return list;
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
// Get all plugins of specific type and name
|
||||
template <typename T = Plugin> T *lookup(const std::string &name) {
|
||||
for (T *p : lookup<T>()) {
|
||||
if (p->getName() != name)
|
||||
continue;
|
||||
|
||||
// Get all plugins of specific type and name
|
||||
template<typename T = Plugin>
|
||||
T * lookup(const std::string &name)
|
||||
{
|
||||
for (T *p : lookup<T>()) {
|
||||
if (p->getName() != name)
|
||||
continue;
|
||||
return p;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename T = Plugin>
|
||||
void dump();
|
||||
template <typename T = Plugin> void dump();
|
||||
};
|
||||
|
||||
class Plugin {
|
||||
|
||||
friend plugin::Registry;
|
||||
friend plugin::Registry;
|
||||
|
||||
protected:
|
||||
Logger logger;
|
||||
Logger logger;
|
||||
|
||||
public:
|
||||
Plugin();
|
||||
Plugin();
|
||||
|
||||
virtual
|
||||
~Plugin();
|
||||
virtual ~Plugin();
|
||||
|
||||
// Copying a plugin doesn't make sense, so explicitly deny it
|
||||
Plugin(Plugin const&) = delete;
|
||||
void operator=(Plugin const&) = delete;
|
||||
// Copying a plugin doesn't make sense, so explicitly deny it
|
||||
Plugin(Plugin const &) = delete;
|
||||
void operator=(Plugin const &) = delete;
|
||||
|
||||
virtual
|
||||
void dump();
|
||||
virtual void dump();
|
||||
|
||||
// Get plugin name
|
||||
virtual
|
||||
std::string getName() const = 0;
|
||||
// Get plugin name
|
||||
virtual std::string getName() const = 0;
|
||||
|
||||
// Get plugin type
|
||||
virtual
|
||||
std::string getType() const = 0;
|
||||
// Get plugin type
|
||||
virtual std::string getType() const = 0;
|
||||
|
||||
// Get plugin description
|
||||
virtual
|
||||
std::string getDescription() const = 0;
|
||||
// Get plugin description
|
||||
virtual std::string getDescription() const = 0;
|
||||
|
||||
virtual
|
||||
Logger getLogger()
|
||||
{
|
||||
if (!logger) {
|
||||
auto name = fmt::format("{}:{}", getType(), getName());
|
||||
logger = logging.get(name);
|
||||
}
|
||||
virtual Logger getLogger() {
|
||||
if (!logger) {
|
||||
auto name = fmt::format("{}:{}", getType(), getName());
|
||||
logger = logging.get(name);
|
||||
}
|
||||
|
||||
return logger;
|
||||
}
|
||||
|
||||
// Custom formatter for spdlog
|
||||
template<typename OStream>
|
||||
friend OStream &operator<<(OStream &os, const class Plugin &p)
|
||||
{
|
||||
return os << p.getName();
|
||||
}
|
||||
return logger;
|
||||
}
|
||||
|
||||
// Custom formatter for spdlog
|
||||
template <typename OStream>
|
||||
friend OStream &operator<<(OStream &os, const class Plugin &p) {
|
||||
return os << p.getName();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
Registry::dump()
|
||||
{
|
||||
getLogger()->info("Available plugins:");
|
||||
template <typename T> void Registry::dump() {
|
||||
getLogger()->info("Available plugins:");
|
||||
|
||||
for (Plugin *p : plugins) {
|
||||
T *t = dynamic_cast<T *>(p);
|
||||
if (t)
|
||||
getLogger()->info(" - {}: {}", p->getName(), p->getDescription());
|
||||
}
|
||||
for (Plugin *p : plugins) {
|
||||
T *t = dynamic_cast<T *>(p);
|
||||
if (t)
|
||||
getLogger()->info(" - {}: {}", p->getName(), p->getDescription());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace plugin
|
||||
|
|
|
@ -11,11 +11,11 @@
|
|||
|
||||
#include <ext/stdio_filebuf.h>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <istream>
|
||||
#include <ostream>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
|
||||
namespace villas {
|
||||
namespace utils {
|
||||
|
@ -23,90 +23,69 @@ namespace utils {
|
|||
class Popen {
|
||||
|
||||
public:
|
||||
using arg_list = std::vector<std::string>;
|
||||
using env_map = std::map<std::string, std::string>;
|
||||
using arg_list = std::vector<std::string>;
|
||||
using env_map = std::map<std::string, std::string>;
|
||||
|
||||
using char_type = char;
|
||||
using stdio_buf = __gnu_cxx::stdio_filebuf<char_type>;
|
||||
using char_type = char;
|
||||
using stdio_buf = __gnu_cxx::stdio_filebuf<char_type>;
|
||||
|
||||
Popen(const std::string &cmd,
|
||||
const arg_list &args = arg_list(),
|
||||
const env_map &env = env_map(),
|
||||
const std::string &wd = std::string(),
|
||||
bool shell = false);
|
||||
~Popen();
|
||||
Popen(const std::string &cmd, const arg_list &args = arg_list(),
|
||||
const env_map &env = env_map(), const std::string &wd = std::string(),
|
||||
bool shell = false);
|
||||
~Popen();
|
||||
|
||||
void open();
|
||||
int close();
|
||||
void kill(int signal = SIGINT);
|
||||
void open();
|
||||
int close();
|
||||
void kill(int signal = SIGINT);
|
||||
|
||||
int getFdIn()
|
||||
{
|
||||
return fd_in;
|
||||
}
|
||||
int getFdIn() { return fd_in; }
|
||||
|
||||
int getFdOut()
|
||||
{
|
||||
return fd_out;
|
||||
}
|
||||
int getFdOut() { return fd_out; }
|
||||
|
||||
pid_t getPid() const
|
||||
{
|
||||
return pid;
|
||||
}
|
||||
pid_t getPid() const { return pid; }
|
||||
|
||||
protected:
|
||||
std::string command;
|
||||
std::string working_dir;
|
||||
arg_list arguments;
|
||||
env_map environment;
|
||||
bool shell;
|
||||
pid_t pid;
|
||||
std::string command;
|
||||
std::string working_dir;
|
||||
arg_list arguments;
|
||||
env_map environment;
|
||||
bool shell;
|
||||
pid_t pid;
|
||||
|
||||
int fd_in, fd_out;
|
||||
int fd_in, fd_out;
|
||||
};
|
||||
|
||||
class PopenStream : public Popen {
|
||||
|
||||
public:
|
||||
PopenStream(const std::string &cmd,
|
||||
const arg_list &args = arg_list(),
|
||||
const env_map &env = env_map(),
|
||||
const std::string &wd = std::string(),
|
||||
bool shell = false);
|
||||
~PopenStream();
|
||||
PopenStream(const std::string &cmd, const arg_list &args = arg_list(),
|
||||
const env_map &env = env_map(),
|
||||
const std::string &wd = std::string(), bool shell = false);
|
||||
~PopenStream();
|
||||
|
||||
std::istream &cin()
|
||||
{
|
||||
return *(input.stream);
|
||||
}
|
||||
std::istream &cin() { return *(input.stream); }
|
||||
|
||||
std::ostream &cout()
|
||||
{
|
||||
return *(output.stream);
|
||||
}
|
||||
std::ostream &cout() { return *(output.stream); }
|
||||
|
||||
void open();
|
||||
int close();
|
||||
void open();
|
||||
int close();
|
||||
|
||||
protected:
|
||||
struct {
|
||||
std::unique_ptr<std::istream> stream;
|
||||
std::unique_ptr<stdio_buf> buffer;
|
||||
} input;
|
||||
|
||||
struct {
|
||||
std::unique_ptr<std::istream> stream;
|
||||
std::unique_ptr<stdio_buf> buffer;
|
||||
} input;
|
||||
|
||||
struct {
|
||||
std::unique_ptr<std::ostream> stream;
|
||||
std::unique_ptr<stdio_buf> buffer;
|
||||
} output;
|
||||
struct {
|
||||
std::unique_ptr<std::ostream> stream;
|
||||
std::unique_ptr<stdio_buf> buffer;
|
||||
} output;
|
||||
};
|
||||
|
||||
} // namespace utils
|
||||
} // namespace villas
|
||||
|
||||
template<typename T>
|
||||
std::istream &operator>>(villas::utils::PopenStream &po, T &value)
|
||||
{
|
||||
return *(po.input.stream) >> value;
|
||||
template <typename T>
|
||||
std::istream &operator>>(villas::utils::PopenStream &po, T &value) {
|
||||
return *(po.input.stream) >> value;
|
||||
}
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <villas/log.hpp>
|
||||
|
||||
|
@ -18,69 +18,52 @@ class Table;
|
|||
|
||||
class TableColumn {
|
||||
|
||||
friend Table;
|
||||
friend Table;
|
||||
|
||||
public:
|
||||
enum class Alignment {
|
||||
LEFT,
|
||||
RIGHT
|
||||
};
|
||||
enum class Alignment { LEFT, RIGHT };
|
||||
|
||||
protected:
|
||||
int _width; // The real width of this column. Calculated by Table::resize().
|
||||
int _width; // The real width of this column. Calculated by Table::resize().
|
||||
|
||||
int width; // Width of the column.
|
||||
int width; // Width of the column.
|
||||
|
||||
public:
|
||||
TableColumn(int w, enum Alignment a, const std::string &t, const std::string &f, const std::string &u = "") :
|
||||
_width(0),
|
||||
width(w),
|
||||
title(t),
|
||||
format(f),
|
||||
unit(u),
|
||||
align(a)
|
||||
{ }
|
||||
TableColumn(int w, enum Alignment a, const std::string &t,
|
||||
const std::string &f, const std::string &u = "")
|
||||
: _width(0), width(w), title(t), format(f), unit(u), align(a) {}
|
||||
|
||||
std::string title; // The title as shown in the table header.
|
||||
std::string format; // The format which is used to print the table rows.
|
||||
std::string unit; // An optional unit which will be shown in the table header.
|
||||
std::string title; // The title as shown in the table header.
|
||||
std::string format; // The format which is used to print the table rows.
|
||||
std::string unit; // An optional unit which will be shown in the table header.
|
||||
|
||||
enum Alignment align;
|
||||
enum Alignment align;
|
||||
|
||||
int getWidth() const
|
||||
{
|
||||
return _width;
|
||||
}
|
||||
int getWidth() const { return _width; }
|
||||
};
|
||||
|
||||
class Table {
|
||||
|
||||
protected:
|
||||
int resize(int w);
|
||||
int resize(int w);
|
||||
|
||||
int width;
|
||||
int width;
|
||||
|
||||
std::vector<TableColumn> columns;
|
||||
std::vector<TableColumn> columns;
|
||||
|
||||
Logger logger;
|
||||
Logger logger;
|
||||
|
||||
public:
|
||||
Table(Logger log, const std::vector<TableColumn> &cols) :
|
||||
width(-1),
|
||||
columns(cols),
|
||||
logger(log)
|
||||
{ }
|
||||
Table(Logger log, const std::vector<TableColumn> &cols)
|
||||
: width(-1), columns(cols), logger(log) {}
|
||||
|
||||
// Print a table header consisting of \p n columns.
|
||||
void header();
|
||||
// Print a table header consisting of \p n columns.
|
||||
void header();
|
||||
|
||||
// Print table rows.
|
||||
void row(int count, ...);
|
||||
// Print table rows.
|
||||
void row(int count, ...);
|
||||
|
||||
int getWidth() const
|
||||
{
|
||||
return width;
|
||||
}
|
||||
int getWidth() const { return width; }
|
||||
};
|
||||
|
||||
} // namespace villas
|
||||
|
|
|
@ -7,66 +7,66 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
|
||||
#include <ctime>
|
||||
|
||||
// We can choose between two periodic task implementations
|
||||
//#define PERIODIC_TASK_IMPL NANOSLEEP
|
||||
#define TIMERFD 1
|
||||
#define CLOCK_NANOSLEEP 2
|
||||
#define NANOSLEEP 3
|
||||
#define RDTSC 4
|
||||
#define TIMERFD 1
|
||||
#define CLOCK_NANOSLEEP 2
|
||||
#define NANOSLEEP 3
|
||||
#define RDTSC 4
|
||||
|
||||
#if defined(__MACH__)
|
||||
#define PERIODIC_TASK_IMPL NANOSLEEP
|
||||
#define PERIODIC_TASK_IMPL NANOSLEEP
|
||||
#elif defined(__linux__)
|
||||
#define PERIODIC_TASK_IMPL TIMERFD
|
||||
#define PERIODIC_TASK_IMPL TIMERFD
|
||||
#else
|
||||
#error "Platform not supported"
|
||||
#error "Platform not supported"
|
||||
#endif
|
||||
|
||||
#if PERIODIC_TASK_IMPL == RDTSC
|
||||
#include <villas/tsc.hpp>
|
||||
#include <villas/tsc.hpp>
|
||||
#endif
|
||||
|
||||
struct Task {
|
||||
int clock; // CLOCK_{MONOTONIC,REALTIME}
|
||||
int clock; // CLOCK_{MONOTONIC,REALTIME}
|
||||
|
||||
#if PERIODIC_TASK_IMPL == RDTSC // We use cycle counts in RDTSC mode
|
||||
uint64_t period;
|
||||
uint64_t next;
|
||||
#if PERIODIC_TASK_IMPL == RDTSC // We use cycle counts in RDTSC mode
|
||||
uint64_t period;
|
||||
uint64_t next;
|
||||
#else
|
||||
struct timespec period; // The period of periodic invations of this task
|
||||
struct timespec next; // The timer value for the next invocation
|
||||
struct timespec period; // The period of periodic invations of this task
|
||||
struct timespec next; // The timer value for the next invocation
|
||||
#endif
|
||||
|
||||
#if PERIODIC_TASK_IMPL == TIMERFD
|
||||
int fd; // The timerfd_create(2) file descriptior.
|
||||
int fd; // The timerfd_create(2) file descriptior.
|
||||
#elif PERIODIC_TASK_IMPL == RDTSC
|
||||
struct Tsc tsc; // Initialized by tsc_init().
|
||||
struct Tsc tsc; // Initialized by tsc_init().
|
||||
#endif
|
||||
|
||||
// Create a new task with the given rate.
|
||||
Task(int clock = CLOCK_REALTIME);
|
||||
// Create a new task with the given rate.
|
||||
Task(int clock = CLOCK_REALTIME);
|
||||
|
||||
~Task();
|
||||
~Task();
|
||||
|
||||
// Wait until task elapsed
|
||||
//
|
||||
// @retval 0 An error occured. Maybe the task was stopped.
|
||||
// @retval >0 The nummer of runs this task already fired.
|
||||
uint64_t wait();
|
||||
// Wait until task elapsed
|
||||
//
|
||||
// @retval 0 An error occured. Maybe the task was stopped.
|
||||
// @retval >0 The nummer of runs this task already fired.
|
||||
uint64_t wait();
|
||||
|
||||
void setNext(const struct timespec *next);
|
||||
void setTimeout(double to);
|
||||
void setRate(double rate);
|
||||
void setNext(const struct timespec *next);
|
||||
void setTimeout(double to);
|
||||
void setRate(double rate);
|
||||
|
||||
void stop();
|
||||
void stop();
|
||||
|
||||
// Returns a poll'able file descriptor which becomes readable when the timer expires.
|
||||
//
|
||||
// Note: currently not supported on all platforms.
|
||||
int getFD() const;
|
||||
// Returns a poll'able file descriptor which becomes readable when the timer expires.
|
||||
//
|
||||
// Note: currently not supported on all platforms.
|
||||
int getFD() const;
|
||||
};
|
||||
|
|
|
@ -17,37 +17,31 @@ namespace villas {
|
|||
class Terminal {
|
||||
|
||||
protected:
|
||||
struct winsize window; // Size of the terminal window.
|
||||
struct winsize window; // Size of the terminal window.
|
||||
|
||||
bool isTty;
|
||||
bool isTty;
|
||||
|
||||
static
|
||||
class Terminal *current;
|
||||
static class Terminal *current;
|
||||
|
||||
public:
|
||||
Terminal();
|
||||
Terminal();
|
||||
|
||||
// Signal handler for TIOCGWINSZ
|
||||
static
|
||||
void resize(int signal, siginfo_t *sinfo, void *ctx);
|
||||
// Signal handler for TIOCGWINSZ
|
||||
static void resize(int signal, siginfo_t *sinfo, void *ctx);
|
||||
|
||||
static
|
||||
int getCols()
|
||||
{
|
||||
if (!current)
|
||||
current = new Terminal();
|
||||
static int getCols() {
|
||||
if (!current)
|
||||
current = new Terminal();
|
||||
|
||||
return current->window.ws_col;
|
||||
}
|
||||
return current->window.ws_col;
|
||||
}
|
||||
|
||||
static
|
||||
int getRows()
|
||||
{
|
||||
if (!current)
|
||||
current = new Terminal();
|
||||
static int getRows() {
|
||||
if (!current)
|
||||
current = new Terminal();
|
||||
|
||||
return current->window.ws_row;
|
||||
}
|
||||
return current->window.ws_row;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace villas
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
|
||||
#include <ctime>
|
||||
|
||||
|
@ -16,10 +16,12 @@
|
|||
ssize_t time_cmp(const struct timespec *a, const struct timespec *b);
|
||||
|
||||
// Get delta between two timespec structs.
|
||||
struct timespec time_diff(const struct timespec *start, const struct timespec *end);
|
||||
struct timespec time_diff(const struct timespec *start,
|
||||
const struct timespec *end);
|
||||
|
||||
// Get sum of two timespec structs.
|
||||
struct timespec time_add(const struct timespec *start, const struct timespec *end);
|
||||
struct timespec time_add(const struct timespec *start,
|
||||
const struct timespec *end);
|
||||
|
||||
// Return current time as a struct timespec.
|
||||
struct timespec time_now();
|
||||
|
|
|
@ -7,10 +7,10 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <villas/log.hpp>
|
||||
#include <villas/colors.hpp>
|
||||
#include <villas/config.hpp>
|
||||
#include <villas/exceptions.hpp>
|
||||
#include <villas/log.hpp>
|
||||
#include <villas/utils.hpp>
|
||||
|
||||
namespace villas {
|
||||
|
@ -18,50 +18,36 @@ namespace villas {
|
|||
class Tool {
|
||||
|
||||
protected:
|
||||
Logger logger;
|
||||
Logger logger;
|
||||
|
||||
int argc;
|
||||
char **argv;
|
||||
int argc;
|
||||
char **argv;
|
||||
|
||||
std::string name;
|
||||
std::string name;
|
||||
|
||||
static
|
||||
Tool *current_tool;
|
||||
static Tool *current_tool;
|
||||
|
||||
static
|
||||
void staticHandler(int signal, siginfo_t *sinfo, void *ctx);
|
||||
static void staticHandler(int signal, siginfo_t *sinfo, void *ctx);
|
||||
|
||||
virtual
|
||||
void handler(int, siginfo_t *, void *)
|
||||
{ }
|
||||
virtual void handler(int, siginfo_t *, void *) {}
|
||||
|
||||
std::list<int> handlerSignals;
|
||||
std::list<int> handlerSignals;
|
||||
|
||||
static
|
||||
void printCopyright();
|
||||
static void printCopyright();
|
||||
|
||||
static
|
||||
void printVersion();
|
||||
static void printVersion();
|
||||
|
||||
public:
|
||||
Tool(int ac, char *av[], const std::string &name, const std::list<int> &sigs = { });
|
||||
Tool(int ac, char *av[], const std::string &name,
|
||||
const std::list<int> &sigs = {});
|
||||
|
||||
virtual
|
||||
int main()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
virtual int main() { return 0; }
|
||||
|
||||
virtual
|
||||
void usage()
|
||||
{ }
|
||||
virtual void usage() {}
|
||||
|
||||
virtual
|
||||
void parse()
|
||||
{ }
|
||||
virtual void parse() {}
|
||||
|
||||
virtual
|
||||
int run();
|
||||
virtual int run();
|
||||
};
|
||||
|
||||
} // namespace villas
|
||||
|
|
|
@ -8,38 +8,34 @@
|
|||
#pragma once
|
||||
|
||||
#if !(__x86_64__ || __i386__)
|
||||
#error this header is for x86 only
|
||||
#error this header is for x86 only
|
||||
#endif
|
||||
|
||||
#include <cpuid.h>
|
||||
#include <cinttypes>
|
||||
#include <cpuid.h>
|
||||
#include <x86intrin.h>
|
||||
|
||||
#include <villas/kernel/kernel.hpp>
|
||||
|
||||
#ifndef bit_TSC
|
||||
#define bit_TSC (1 << 4)
|
||||
#define bit_TSC (1 << 4)
|
||||
#endif
|
||||
|
||||
#define bit_TSC_INVARIANT (1 << 8)
|
||||
#define bit_RDTSCP (1 << 27)
|
||||
#define bit_TSC_INVARIANT (1 << 8)
|
||||
#define bit_RDTSCP (1 << 27)
|
||||
|
||||
struct Tsc {
|
||||
uint64_t frequency;
|
||||
uint64_t frequency;
|
||||
|
||||
bool rdtscp_supported;
|
||||
bool is_invariant;
|
||||
bool rdtscp_supported;
|
||||
bool is_invariant;
|
||||
};
|
||||
|
||||
__attribute__((unused)) static
|
||||
uint64_t tsc_now(struct Tsc *t)
|
||||
{
|
||||
uint32_t tsc_aux;
|
||||
return t->rdtscp_supported
|
||||
? __rdtscp(&tsc_aux)
|
||||
: __rdtsc();
|
||||
__attribute__((unused)) static uint64_t tsc_now(struct Tsc *t) {
|
||||
uint32_t tsc_aux;
|
||||
return t->rdtscp_supported ? __rdtscp(&tsc_aux) : __rdtsc();
|
||||
}
|
||||
|
||||
int tsc_init(struct Tsc *t) __attribute__ ((warn_unused_result));
|
||||
int tsc_init(struct Tsc *t) __attribute__((warn_unused_result));
|
||||
|
||||
uint64_t tsc_rate_to_cycles(struct Tsc *t, double rate);
|
||||
|
|
|
@ -8,15 +8,15 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
|
||||
#include <signal.h>
|
||||
#include <cstdlib>
|
||||
#include <cstdint>
|
||||
#include <sched.h>
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <sched.h>
|
||||
#include <signal.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <openssl/sha.h>
|
||||
|
@ -24,110 +24,120 @@
|
|||
#include <villas/config.hpp>
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define LIKELY(x) __builtin_expect((x),1)
|
||||
#define UNLIKELY(x) __builtin_expect((x),0)
|
||||
#define LIKELY(x) __builtin_expect((x), 1)
|
||||
#define UNLIKELY(x) __builtin_expect((x), 0)
|
||||
#else
|
||||
#define LIKELY(x) (x)
|
||||
#define UNLIKELY(x) (x)
|
||||
#define LIKELY(x) (x)
|
||||
#define UNLIKELY(x) (x)
|
||||
#endif
|
||||
|
||||
// Check assertion and exit if failed.
|
||||
#ifndef assert
|
||||
#define assert(exp) do { \
|
||||
if (!EXPECT(exp, 0)) \
|
||||
error("Assertion failed: '%s' in %s(), %s:%d", \
|
||||
XSTR(exp), __FUNCTION__, __BASE_FILE__, __LINE__); \
|
||||
} while (0)
|
||||
#define assert(exp) \
|
||||
do { \
|
||||
if (!EXPECT(exp, 0)) \
|
||||
error("Assertion failed: '%s' in %s(), %s:%d", XSTR(exp), __FUNCTION__, \
|
||||
__BASE_FILE__, __LINE__); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
// CPP stringification
|
||||
#define XSTR(x) STR(x)
|
||||
#define STR(x) #x
|
||||
#define XSTR(x) STR(x)
|
||||
#define STR(x) #x
|
||||
|
||||
#define CONCAT_DETAIL(x, y) x##y
|
||||
#define CONCAT(x, y) CONCAT_DETAIL(x, y)
|
||||
#define UNIQUE(x) CONCAT(x, __COUNTER__)
|
||||
#define CONCAT_DETAIL(x, y) x##y
|
||||
#define CONCAT(x, y) CONCAT_DETAIL(x, y)
|
||||
#define UNIQUE(x) CONCAT(x, __COUNTER__)
|
||||
|
||||
#ifdef ALIGN
|
||||
#undef ALIGN
|
||||
#undef ALIGN
|
||||
#endif
|
||||
#define ALIGN(x, a) ALIGN_MASK(x, (uintptr_t) (a) - 1)
|
||||
#define ALIGN_MASK(x, m) (((uintptr_t) (x) + (m)) & ~(m))
|
||||
#define IS_ALIGNED(x, a) (ALIGN(x, a) == (uintptr_t) x)
|
||||
#define ALIGN(x, a) ALIGN_MASK(x, (uintptr_t)(a)-1)
|
||||
#define ALIGN_MASK(x, m) (((uintptr_t)(x) + (m)) & ~(m))
|
||||
#define IS_ALIGNED(x, a) (ALIGN(x, a) == (uintptr_t)x)
|
||||
|
||||
#define SWAP(x, y) do { \
|
||||
auto t = x; \
|
||||
x = y; \
|
||||
y = t; \
|
||||
} while (0)
|
||||
#define SWAP(x, y) \
|
||||
do { \
|
||||
auto t = x; \
|
||||
x = y; \
|
||||
y = t; \
|
||||
} while (0)
|
||||
|
||||
// Round-up integer division
|
||||
#define CEIL(x, y) (((x) + (y) - 1) / (y))
|
||||
#define CEIL(x, y) (((x) + (y)-1) / (y))
|
||||
|
||||
// Get nearest up-rounded power of 2
|
||||
#define LOG2_CEIL(x) (1 << (villas::utils::log2i((x) - 1) + 1))
|
||||
#define LOG2_CEIL(x) (1 << (villas::utils::log2i((x)-1) + 1))
|
||||
|
||||
// Check if the number is a power of 2
|
||||
#define IS_POW2(x) (((x) != 0) && !((x) & ((x) - 1)))
|
||||
#define IS_POW2(x) (((x) != 0) && !((x) & ((x)-1)))
|
||||
|
||||
// Calculate the number of elements in an array.
|
||||
#define ARRAY_LEN(a) ( sizeof (a) / sizeof (a)[0] )
|
||||
#define ARRAY_LEN(a) (sizeof(a) / sizeof(a)[0])
|
||||
|
||||
// Return the bigger value
|
||||
#ifdef MAX
|
||||
#undef MAX
|
||||
#undef MAX
|
||||
#endif
|
||||
#define MAX(a, b) ({ __typeof__ (a) _a = (a); \
|
||||
__typeof__ (b) _b = (b); \
|
||||
_a > _b ? _a : _b; })
|
||||
#define MAX(a, b) \
|
||||
({ \
|
||||
__typeof__(a) _a = (a); \
|
||||
__typeof__(b) _b = (b); \
|
||||
_a > _b ? _a : _b; \
|
||||
})
|
||||
|
||||
// Return the smaller value
|
||||
#ifdef MIN
|
||||
#undef MIN
|
||||
#undef MIN
|
||||
#endif
|
||||
#define MIN(a, b) ({ __typeof__ (a) _a = (a); \
|
||||
__typeof__ (b) _b = (b); \
|
||||
_a < _b ? _a : _b; })
|
||||
#define MIN3(a, b, c) MIN(MIN((a), (b)), (c))
|
||||
#define MIN(a, b) \
|
||||
({ \
|
||||
__typeof__(a) _a = (a); \
|
||||
__typeof__(b) _b = (b); \
|
||||
_a < _b ? _a : _b; \
|
||||
})
|
||||
#define MIN3(a, b, c) MIN(MIN((a), (b)), (c))
|
||||
|
||||
#ifndef offsetof
|
||||
#define offsetof(type, member) __builtin_offsetof(type, member)
|
||||
#define offsetof(type, member) __builtin_offsetof(type, member)
|
||||
#endif
|
||||
|
||||
#ifndef container_of
|
||||
#define container_of(ptr, type, member) ({ const typeof( ((type *) 0)->member ) *__mptr = (ptr); \
|
||||
(type *) ( (char *) __mptr - offsetof(type, member) ); \
|
||||
})
|
||||
#define container_of(ptr, type, member) \
|
||||
({ \
|
||||
const typeof(((type *)0)->member) *__mptr = (ptr); \
|
||||
(type *)((char *)__mptr - offsetof(type, member)); \
|
||||
})
|
||||
#endif
|
||||
|
||||
#define BITS_PER_LONGLONG (sizeof(long long) * 8)
|
||||
#define BITS_PER_LONGLONG (sizeof(long long) * 8)
|
||||
|
||||
// Some helper macros
|
||||
#define BITMASK(h, l) (((~0ULL) << (l)) & (~0ULL >> (BITS_PER_LONGLONG - 1 - (h))))
|
||||
#define BIT(nr) (1UL << (nr))
|
||||
#define BITMASK(h, l) \
|
||||
(((~0ULL) << (l)) & (~0ULL >> (BITS_PER_LONGLONG - 1 - (h))))
|
||||
#define BIT(nr) (1UL << (nr))
|
||||
|
||||
namespace villas {
|
||||
namespace utils {
|
||||
|
||||
std::vector<std::string>
|
||||
tokenize(std::string s, const std::string &delimiter);
|
||||
std::vector<std::string> tokenize(std::string s, const std::string &delimiter);
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
assertExcept(bool condition, const T &exception)
|
||||
{
|
||||
if (not condition)
|
||||
throw exception;
|
||||
template <typename T> void assertExcept(bool condition, const T &exception) {
|
||||
if (not condition)
|
||||
throw exception;
|
||||
}
|
||||
|
||||
// Register a exit callback for program termination: SIGINT, SIGKILL & SIGALRM.
|
||||
int signalsInit(void (*cb)(int signal, siginfo_t *sinfo, void *ctx), std::list<int> cbSignals = {}, std::list<int> ignoreSignals = { SIGCHLD }) __attribute__ ((warn_unused_result));
|
||||
int signalsInit(void (*cb)(int signal, siginfo_t *sinfo, void *ctx),
|
||||
std::list<int> cbSignals = {},
|
||||
std::list<int> ignoreSignals = {SIGCHLD})
|
||||
__attribute__((warn_unused_result));
|
||||
|
||||
// Fill buffer with random data.
|
||||
ssize_t readRandom(char *buf, size_t len);
|
||||
|
||||
// Remove ANSI control sequences for colored output.
|
||||
char * decolor(char *str);
|
||||
char *decolor(char *str);
|
||||
|
||||
// Normal random variate generator using the Box-Muller method
|
||||
//
|
||||
|
@ -148,18 +158,18 @@ double randf();
|
|||
// @param fmt A format string like for printf()
|
||||
// @param ... Optional parameters like for printf()
|
||||
// @retval The the new value of the dest buffer.
|
||||
char * strcatf(char **dest, const char *fmt, ...)
|
||||
__attribute__ ((format(printf, 2, 3)));
|
||||
char *strcatf(char **dest, const char *fmt, ...)
|
||||
__attribute__((format(printf, 2, 3)));
|
||||
|
||||
// Variadic version of strcatf()
|
||||
char * vstrcatf(char **dest, const char *fmt, va_list va)
|
||||
__attribute__ ((format(printf, 2, 0)));
|
||||
char *vstrcatf(char **dest, const char *fmt, va_list va)
|
||||
__attribute__((format(printf, 2, 0)));
|
||||
|
||||
char * strf(const char *fmt, ...);
|
||||
char * vstrf(const char *fmt, va_list va);
|
||||
char *strf(const char *fmt, ...);
|
||||
char *vstrf(const char *fmt, va_list va);
|
||||
|
||||
// Allocate and copy memory.
|
||||
void * memdup(const void *src, size_t bytes);
|
||||
void *memdup(const void *src, size_t bytes);
|
||||
|
||||
// Call quit() in the main thread.
|
||||
void die();
|
||||
|
@ -194,12 +204,12 @@ bool isContainer();
|
|||
bool isPrivileged();
|
||||
|
||||
// helper type for std::visit
|
||||
template<class... Ts>
|
||||
struct overloaded : Ts... { using Ts::operator()...; };
|
||||
template <class... Ts> struct overloaded : Ts... {
|
||||
using Ts::operator()...;
|
||||
};
|
||||
|
||||
// explicit deduction guide (not needed as of C++20)
|
||||
template<class... Ts>
|
||||
overloaded(Ts...) -> overloaded<Ts...>;
|
||||
template <class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
|
||||
|
||||
namespace base64 {
|
||||
|
||||
|
|
|
@ -21,7 +21,8 @@ namespace uuid {
|
|||
std::string toString(const uuid_t in);
|
||||
|
||||
// Generate an UUID by MD5 hashing the provided string
|
||||
int generateFromString(uuid_t out, const std::string &data, const std::string &ns = "");
|
||||
int generateFromString(uuid_t out, const std::string &data,
|
||||
const std::string &ns = "");
|
||||
|
||||
// Generate an UUID by MD5 hashing the provided string
|
||||
int generateFromString(uuid_t out, const std::string &data, const uuid_t ns);
|
||||
|
|
|
@ -15,53 +15,28 @@ namespace utils {
|
|||
class Version {
|
||||
|
||||
protected:
|
||||
int components[3];
|
||||
int components[3];
|
||||
|
||||
static
|
||||
int cmp(const Version &lhs, const Version &rhs);
|
||||
static int cmp(const Version &lhs, const Version &rhs);
|
||||
|
||||
public:
|
||||
// Parse a dotted version string.
|
||||
Version(const std::string &s);
|
||||
// Parse a dotted version string.
|
||||
Version(const std::string &s);
|
||||
|
||||
Version(int maj, int min = 0, int pat = 0);
|
||||
Version(int maj, int min = 0, int pat = 0);
|
||||
|
||||
inline
|
||||
bool operator==(const Version &rhs)
|
||||
{
|
||||
return cmp(*this, rhs) == 0;
|
||||
}
|
||||
inline bool operator==(const Version &rhs) { return cmp(*this, rhs) == 0; }
|
||||
|
||||
inline
|
||||
bool operator!=(const Version &rhs)
|
||||
{
|
||||
return cmp(*this, rhs) != 0;
|
||||
}
|
||||
inline bool operator!=(const Version &rhs) { return cmp(*this, rhs) != 0; }
|
||||
|
||||
inline
|
||||
bool operator<(const Version &rhs)
|
||||
{
|
||||
return cmp(*this, rhs) < 0;
|
||||
}
|
||||
inline bool operator<(const Version &rhs) { return cmp(*this, rhs) < 0; }
|
||||
|
||||
inline
|
||||
bool operator>(const Version &rhs)
|
||||
{
|
||||
return cmp(*this, rhs) > 0;
|
||||
}
|
||||
inline bool operator>(const Version &rhs) { return cmp(*this, rhs) > 0; }
|
||||
|
||||
inline
|
||||
bool operator<=(const Version &rhs)
|
||||
{
|
||||
return cmp(*this, rhs) <= 0;
|
||||
}
|
||||
inline bool operator<=(const Version &rhs) { return cmp(*this, rhs) <= 0; }
|
||||
|
||||
inline
|
||||
bool operator>=(const Version &rhs)
|
||||
{
|
||||
return cmp(*this, rhs) >= 0;
|
||||
}
|
||||
inline bool operator>=(const Version &rhs) { return cmp(*this, rhs) >= 0; }
|
||||
};
|
||||
|
||||
} // namespace villas
|
||||
} // namespace utils
|
||||
} // namespace villas
|
||||
|
|
|
@ -5,10 +5,10 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
#include <cstdint>
|
||||
|
||||
#include <villas/utils.hpp>
|
||||
|
||||
|
@ -16,101 +16,104 @@ namespace villas {
|
|||
namespace utils {
|
||||
namespace base64 {
|
||||
|
||||
static
|
||||
const char kEncodeLookup[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
static
|
||||
const char kPadCharacter = '=';
|
||||
static const char kEncodeLookup[] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
static const char kPadCharacter = '=';
|
||||
|
||||
std::string encode(const std::vector<byte>& input)
|
||||
{
|
||||
std::string encoded;
|
||||
encoded.reserve(((input.size() / 3) + (input.size() % 3 > 0)) * 4);
|
||||
std::string encode(const std::vector<byte> &input) {
|
||||
std::string encoded;
|
||||
encoded.reserve(((input.size() / 3) + (input.size() % 3 > 0)) * 4);
|
||||
|
||||
std::uint32_t temp{};
|
||||
auto it = input.begin();
|
||||
std::uint32_t temp{};
|
||||
auto it = input.begin();
|
||||
|
||||
for (std::size_t i = 0; i < input.size() / 3; ++i) {
|
||||
temp = (*it++) << 16;
|
||||
temp += (*it++) << 8;
|
||||
temp += (*it++);
|
||||
encoded.append(1, kEncodeLookup[(temp & 0x00FC0000) >> 18]);
|
||||
encoded.append(1, kEncodeLookup[(temp & 0x0003F000) >> 12]);
|
||||
encoded.append(1, kEncodeLookup[(temp & 0x00000FC0) >> 6 ]);
|
||||
encoded.append(1, kEncodeLookup[(temp & 0x0000003F) ]);
|
||||
}
|
||||
for (std::size_t i = 0; i < input.size() / 3; ++i) {
|
||||
temp = (*it++) << 16;
|
||||
temp += (*it++) << 8;
|
||||
temp += (*it++);
|
||||
encoded.append(1, kEncodeLookup[(temp & 0x00FC0000) >> 18]);
|
||||
encoded.append(1, kEncodeLookup[(temp & 0x0003F000) >> 12]);
|
||||
encoded.append(1, kEncodeLookup[(temp & 0x00000FC0) >> 6]);
|
||||
encoded.append(1, kEncodeLookup[(temp & 0x0000003F)]);
|
||||
}
|
||||
|
||||
switch (input.size() % 3) {
|
||||
case 1:
|
||||
temp = (*it++) << 16;
|
||||
encoded.append(1, kEncodeLookup[(temp & 0x00FC0000) >> 18]);
|
||||
encoded.append(1, kEncodeLookup[(temp & 0x0003F000) >> 12]);
|
||||
encoded.append(2, kPadCharacter);
|
||||
break;
|
||||
switch (input.size() % 3) {
|
||||
case 1:
|
||||
temp = (*it++) << 16;
|
||||
encoded.append(1, kEncodeLookup[(temp & 0x00FC0000) >> 18]);
|
||||
encoded.append(1, kEncodeLookup[(temp & 0x0003F000) >> 12]);
|
||||
encoded.append(2, kPadCharacter);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
temp = (*it++) << 16;
|
||||
temp += (*it++) << 8;
|
||||
encoded.append(1, kEncodeLookup[(temp & 0x00FC0000) >> 18]);
|
||||
encoded.append(1, kEncodeLookup[(temp & 0x0003F000) >> 12]);
|
||||
encoded.append(1, kEncodeLookup[(temp & 0x00000FC0) >> 6 ]);
|
||||
encoded.append(1, kPadCharacter);
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
temp = (*it++) << 16;
|
||||
temp += (*it++) << 8;
|
||||
encoded.append(1, kEncodeLookup[(temp & 0x00FC0000) >> 18]);
|
||||
encoded.append(1, kEncodeLookup[(temp & 0x0003F000) >> 12]);
|
||||
encoded.append(1, kEncodeLookup[(temp & 0x00000FC0) >> 6]);
|
||||
encoded.append(1, kPadCharacter);
|
||||
break;
|
||||
}
|
||||
|
||||
return encoded;
|
||||
return encoded;
|
||||
}
|
||||
|
||||
std::vector<byte> decode(const std::string &input)
|
||||
{
|
||||
if (input.length() % 4)
|
||||
throw std::runtime_error("Invalid base64 length!");
|
||||
std::vector<byte> decode(const std::string &input) {
|
||||
if (input.length() % 4)
|
||||
throw std::runtime_error("Invalid base64 length!");
|
||||
|
||||
std::size_t padding{};
|
||||
std::size_t padding{};
|
||||
|
||||
if (input.length()) {
|
||||
if (input[input.length() - 1] == kPadCharacter) padding++;
|
||||
if (input[input.length() - 2] == kPadCharacter) padding++;
|
||||
}
|
||||
if (input.length()) {
|
||||
if (input[input.length() - 1] == kPadCharacter)
|
||||
padding++;
|
||||
if (input[input.length() - 2] == kPadCharacter)
|
||||
padding++;
|
||||
}
|
||||
|
||||
std::vector<byte> decoded;
|
||||
decoded.reserve(((input.length() / 4) * 3) - padding);
|
||||
std::vector<byte> decoded;
|
||||
decoded.reserve(((input.length() / 4) * 3) - padding);
|
||||
|
||||
std::uint32_t temp{};
|
||||
auto it = input.begin();
|
||||
std::uint32_t temp{};
|
||||
auto it = input.begin();
|
||||
|
||||
while (it < input.end()) {
|
||||
for (std::size_t i = 0; i < 4; ++i) {
|
||||
temp <<= 6;
|
||||
if (*it >= 0x41 && *it <= 0x5A) temp |= *it - 0x41;
|
||||
else if (*it >= 0x61 && *it <= 0x7A) temp |= *it - 0x47;
|
||||
else if (*it >= 0x30 && *it <= 0x39) temp |= *it + 0x04;
|
||||
else if (*it == 0x2B) temp |= 0x3E;
|
||||
else if (*it == 0x2F) temp |= 0x3F;
|
||||
else if (*it == kPadCharacter) {
|
||||
switch(input.end() - it) {
|
||||
case 1:
|
||||
decoded.push_back((temp >> 16) & 0x000000FF);
|
||||
decoded.push_back((temp >> 8 ) & 0x000000FF);
|
||||
return decoded;
|
||||
case 2:
|
||||
decoded.push_back((temp >> 10) & 0x000000FF);
|
||||
return decoded;
|
||||
default:
|
||||
throw std::runtime_error("Invalid padding in base64!");
|
||||
}
|
||||
}
|
||||
else
|
||||
throw std::runtime_error("Invalid character in base64!");
|
||||
while (it < input.end()) {
|
||||
for (std::size_t i = 0; i < 4; ++i) {
|
||||
temp <<= 6;
|
||||
if (*it >= 0x41 && *it <= 0x5A)
|
||||
temp |= *it - 0x41;
|
||||
else if (*it >= 0x61 && *it <= 0x7A)
|
||||
temp |= *it - 0x47;
|
||||
else if (*it >= 0x30 && *it <= 0x39)
|
||||
temp |= *it + 0x04;
|
||||
else if (*it == 0x2B)
|
||||
temp |= 0x3E;
|
||||
else if (*it == 0x2F)
|
||||
temp |= 0x3F;
|
||||
else if (*it == kPadCharacter) {
|
||||
switch (input.end() - it) {
|
||||
case 1:
|
||||
decoded.push_back((temp >> 16) & 0x000000FF);
|
||||
decoded.push_back((temp >> 8) & 0x000000FF);
|
||||
return decoded;
|
||||
case 2:
|
||||
decoded.push_back((temp >> 10) & 0x000000FF);
|
||||
return decoded;
|
||||
default:
|
||||
throw std::runtime_error("Invalid padding in base64!");
|
||||
}
|
||||
} else
|
||||
throw std::runtime_error("Invalid character in base64!");
|
||||
|
||||
++it;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
|
||||
decoded.push_back((temp >> 16) & 0x000000FF);
|
||||
decoded.push_back((temp >> 8 ) & 0x000000FF);
|
||||
decoded.push_back((temp ) & 0x000000FF);
|
||||
}
|
||||
decoded.push_back((temp >> 16) & 0x000000FF);
|
||||
decoded.push_back((temp >> 8) & 0x000000FF);
|
||||
decoded.push_back((temp)&0x000000FF);
|
||||
}
|
||||
|
||||
return decoded;
|
||||
return decoded;
|
||||
}
|
||||
|
||||
} // namespace base64
|
||||
|
|
|
@ -5,37 +5,34 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <villas/compat.hpp>
|
||||
#include <villas/buffer.hpp>
|
||||
#include <villas/compat.hpp>
|
||||
|
||||
using namespace villas;
|
||||
|
||||
json_t * Buffer::decode()
|
||||
{
|
||||
json_t *j;
|
||||
json_error_t err;
|
||||
json_t *Buffer::decode() {
|
||||
json_t *j;
|
||||
json_error_t err;
|
||||
|
||||
j = json_loadb(data(), size(), JSON_DISABLE_EOF_CHECK, &err);
|
||||
if (!j)
|
||||
return nullptr;
|
||||
j = json_loadb(data(), size(), JSON_DISABLE_EOF_CHECK, &err);
|
||||
if (!j)
|
||||
return nullptr;
|
||||
|
||||
// Remove decoded JSON document from beginning
|
||||
erase(begin(), begin() + err.position);
|
||||
// Remove decoded JSON document from beginning
|
||||
erase(begin(), begin() + err.position);
|
||||
|
||||
return j;
|
||||
return j;
|
||||
}
|
||||
|
||||
int Buffer::encode(json_t *j, int flags)
|
||||
{
|
||||
return json_dump_callback(j, callback, this, flags);
|
||||
int Buffer::encode(json_t *j, int flags) {
|
||||
return json_dump_callback(j, callback, this, flags);
|
||||
}
|
||||
|
||||
int Buffer::callback(const char *data, size_t len, void *ctx)
|
||||
{
|
||||
Buffer *b = static_cast<Buffer *>(ctx);
|
||||
int Buffer::callback(const char *data, size_t len, void *ctx) {
|
||||
Buffer *b = static_cast<Buffer *>(ctx);
|
||||
|
||||
// Append junk of JSON to buffer
|
||||
b->insert(b->end(), &data[0], &data[len]);
|
||||
// Append junk of JSON to buffer
|
||||
b->insert(b->end(), &data[0], &data[len]);
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -9,37 +9,36 @@
|
|||
|
||||
#include <cstdlib>
|
||||
|
||||
std::string stateToString(enum State s)
|
||||
{
|
||||
switch (s) {
|
||||
case State::DESTROYED:
|
||||
return "destroyed";
|
||||
std::string stateToString(enum State s) {
|
||||
switch (s) {
|
||||
case State::DESTROYED:
|
||||
return "destroyed";
|
||||
|
||||
case State::INITIALIZED:
|
||||
return "initialized";
|
||||
case State::INITIALIZED:
|
||||
return "initialized";
|
||||
|
||||
case State::PARSED:
|
||||
return "parsed";
|
||||
case State::PARSED:
|
||||
return "parsed";
|
||||
|
||||
case State::CHECKED:
|
||||
return "checked";
|
||||
case State::CHECKED:
|
||||
return "checked";
|
||||
|
||||
case State::STARTED:
|
||||
return "running";
|
||||
case State::STARTED:
|
||||
return "running";
|
||||
|
||||
case State::STOPPED:
|
||||
return "stopped";
|
||||
case State::STOPPED:
|
||||
return "stopped";
|
||||
|
||||
case State::PENDING_CONNECT:
|
||||
return "pending-connect";
|
||||
case State::PENDING_CONNECT:
|
||||
return "pending-connect";
|
||||
|
||||
case State::CONNECTED:
|
||||
return "connected";
|
||||
case State::CONNECTED:
|
||||
return "connected";
|
||||
|
||||
case State::PAUSED:
|
||||
return "paused";
|
||||
case State::PAUSED:
|
||||
return "paused";
|
||||
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,56 +12,49 @@
|
|||
#include <villas/compat.hpp>
|
||||
|
||||
#if JANSSON_VERSION_HEX < 0x020A00
|
||||
size_t json_dumpb(const json_t *json, char *buffer, size_t size, size_t flags)
|
||||
{
|
||||
char *str;
|
||||
size_t len;
|
||||
size_t json_dumpb(const json_t *json, char *buffer, size_t size, size_t flags) {
|
||||
char *str;
|
||||
size_t len;
|
||||
|
||||
str = json_dumps(json, flags);
|
||||
if (!str)
|
||||
return 0;
|
||||
str = json_dumps(json, flags);
|
||||
if (!str)
|
||||
return 0;
|
||||
|
||||
len = strlen(str); // Not \0 terminated
|
||||
if (buffer && len <= size)
|
||||
memcpy(buffer, str, len);
|
||||
len = strlen(str); // Not \0 terminated
|
||||
if (buffer && len <= size)
|
||||
memcpy(buffer, str, len);
|
||||
|
||||
free(str);
|
||||
free(str);
|
||||
|
||||
return len;
|
||||
return len;
|
||||
}
|
||||
|
||||
static
|
||||
size_t json_loadfd_callback(void *buffer, size_t buflen, void *data)
|
||||
{
|
||||
int *fd = (int *) data;
|
||||
static size_t json_loadfd_callback(void *buffer, size_t buflen, void *data) {
|
||||
int *fd = (int *)data;
|
||||
|
||||
return (size_t) read(*fd, buffer, buflen);
|
||||
return (size_t)read(*fd, buffer, buflen);
|
||||
}
|
||||
|
||||
json_t *json_loadfd(int input, size_t flags, json_error_t *error)
|
||||
{
|
||||
return json_load_callback(json_loadfd_callback, (void *) &input, flags, error);
|
||||
json_t *json_loadfd(int input, size_t flags, json_error_t *error) {
|
||||
return json_load_callback(json_loadfd_callback, (void *)&input, flags, error);
|
||||
}
|
||||
|
||||
static
|
||||
int json_dumpfd_callback(const char *buffer, size_t size, void *data)
|
||||
{
|
||||
static int json_dumpfd_callback(const char *buffer, size_t size, void *data) {
|
||||
#ifdef HAVE_UNISTD_H
|
||||
int *dest = (int *)data;
|
||||
int *dest = (int *)data;
|
||||
|
||||
if (write(*dest, buffer, size) == (ssize_t)size)
|
||||
return 0;
|
||||
if (write(*dest, buffer, size) == (ssize_t)size)
|
||||
return 0;
|
||||
#else
|
||||
(void)buffer;
|
||||
(void)size;
|
||||
(void)data;
|
||||
(void)buffer;
|
||||
(void)size;
|
||||
(void)data;
|
||||
#endif // HAVE_UNISTD_H
|
||||
|
||||
return -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int json_dumpfd(const json_t *json, int output, size_t flags)
|
||||
{
|
||||
return json_dump_callback(json, json_dumpfd_callback, (void *) &output, flags);
|
||||
int json_dumpfd(const json_t *json, int output, size_t flags) {
|
||||
return json_dump_callback(json, json_dumpfd_callback, (void *)&output, flags);
|
||||
}
|
||||
#endif // JANSSON_VERSION_HEX < 0x020A00
|
||||
|
|
|
@ -12,104 +12,94 @@ using namespace villas::utils;
|
|||
|
||||
#ifdef __linux__
|
||||
|
||||
CpuSet::CpuSet(uintmax_t iset) :
|
||||
CpuSet()
|
||||
{
|
||||
zero();
|
||||
CpuSet::CpuSet(uintmax_t iset) : CpuSet() {
|
||||
zero();
|
||||
|
||||
for (size_t i = 0; i < num_cpus; i++) {
|
||||
if (iset & (1L << i))
|
||||
set(i);
|
||||
}
|
||||
for (size_t i = 0; i < num_cpus; i++) {
|
||||
if (iset & (1L << i))
|
||||
set(i);
|
||||
}
|
||||
}
|
||||
|
||||
CpuSet::CpuSet(const std::string &str) :
|
||||
CpuSet()
|
||||
{
|
||||
size_t endpos, start, end;
|
||||
CpuSet::CpuSet(const std::string &str) : CpuSet() {
|
||||
size_t endpos, start, end;
|
||||
|
||||
for (auto token : tokenize(str, ",")) {
|
||||
auto sep = token.find('-');
|
||||
for (auto token : tokenize(str, ",")) {
|
||||
auto sep = token.find('-');
|
||||
|
||||
if (sep == std::string::npos) {
|
||||
start = std::stoi(token, &endpos);
|
||||
if (sep == std::string::npos) {
|
||||
start = std::stoi(token, &endpos);
|
||||
|
||||
if (token.begin() + endpos != token.end())
|
||||
throw std::invalid_argument("Not a valid CPU set");
|
||||
if (token.begin() + endpos != token.end())
|
||||
throw std::invalid_argument("Not a valid CPU set");
|
||||
|
||||
if (start < num_cpus)
|
||||
set(start);
|
||||
}
|
||||
else {
|
||||
start = std::stoi(token, &endpos);
|
||||
if (start < num_cpus)
|
||||
set(start);
|
||||
} else {
|
||||
start = std::stoi(token, &endpos);
|
||||
|
||||
if (token.begin() + endpos != token.begin() + sep)
|
||||
throw std::invalid_argument("Not a valid CPU set");
|
||||
if (token.begin() + endpos != token.begin() + sep)
|
||||
throw std::invalid_argument("Not a valid CPU set");
|
||||
|
||||
auto token2 = token.substr(endpos + 1);
|
||||
auto token2 = token.substr(endpos + 1);
|
||||
|
||||
end = std::stoi(token2, &endpos);
|
||||
end = std::stoi(token2, &endpos);
|
||||
|
||||
if (token2.begin() + endpos != token2.end())
|
||||
throw std::invalid_argument("Not a valid CPU set");
|
||||
if (token2.begin() + endpos != token2.end())
|
||||
throw std::invalid_argument("Not a valid CPU set");
|
||||
|
||||
for (size_t i = start; i <= end && i < num_cpus; i++)
|
||||
set(i);
|
||||
}
|
||||
}
|
||||
for (size_t i = start; i <= end && i < num_cpus; i++)
|
||||
set(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CpuSet::CpuSet(const char *str)
|
||||
: CpuSet(std::string(str))
|
||||
{ }
|
||||
CpuSet::CpuSet(const char *str) : CpuSet(std::string(str)) {}
|
||||
|
||||
CpuSet::operator uintmax_t()
|
||||
{
|
||||
uintmax_t iset = 0;
|
||||
CpuSet::operator uintmax_t() {
|
||||
uintmax_t iset = 0;
|
||||
|
||||
for (size_t i = 0; i < num_cpus; i++) {
|
||||
if (isSet(i))
|
||||
iset |= 1ULL << i;
|
||||
}
|
||||
for (size_t i = 0; i < num_cpus; i++) {
|
||||
if (isSet(i))
|
||||
iset |= 1ULL << i;
|
||||
}
|
||||
|
||||
return iset;
|
||||
return iset;
|
||||
}
|
||||
|
||||
CpuSet::operator std::string ()
|
||||
{
|
||||
std::stringstream ss;
|
||||
CpuSet::operator std::string() {
|
||||
std::stringstream ss;
|
||||
|
||||
bool first = true;
|
||||
bool first = true;
|
||||
|
||||
for (size_t i = 0; i < num_cpus; i++) {
|
||||
if (isSet(i)) {
|
||||
size_t run = 0;
|
||||
for (size_t j = i + 1; j < num_cpus; j++) {
|
||||
if (!isSet(j))
|
||||
break;
|
||||
for (size_t i = 0; i < num_cpus; i++) {
|
||||
if (isSet(i)) {
|
||||
size_t run = 0;
|
||||
for (size_t j = i + 1; j < num_cpus; j++) {
|
||||
if (!isSet(j))
|
||||
break;
|
||||
|
||||
run++;
|
||||
}
|
||||
run++;
|
||||
}
|
||||
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
ss << ",";
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
ss << ",";
|
||||
|
||||
ss << i;
|
||||
ss << i;
|
||||
|
||||
if (run == 1) {
|
||||
ss << "," << (i + 1);
|
||||
i++;
|
||||
}
|
||||
else if (run > 1) {
|
||||
ss << "-" << (i + run);
|
||||
i += run;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (run == 1) {
|
||||
ss << "," << (i + 1);
|
||||
i++;
|
||||
} else if (run > 1) {
|
||||
ss << "-" << (i + run);
|
||||
i += run;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
#endif // __linux__
|
||||
|
|
|
@ -5,51 +5,44 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include <villas/dsp/pid.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace villas::dsp;
|
||||
|
||||
PID::PID(double _dt, double _max, double _min, double _Kp, double _Kd, double _Ki) :
|
||||
dt(_dt),
|
||||
max(_max),
|
||||
min(_min),
|
||||
Kp(_Kp),
|
||||
Kd(_Kd),
|
||||
Ki(_Ki),
|
||||
pre_error(0),
|
||||
integral(0)
|
||||
{ }
|
||||
PID::PID(double _dt, double _max, double _min, double _Kp, double _Kd,
|
||||
double _Ki)
|
||||
: dt(_dt), max(_max), min(_min), Kp(_Kp), Kd(_Kd), Ki(_Ki), pre_error(0),
|
||||
integral(0) {}
|
||||
|
||||
double PID::calculate(double setpoint, double pv)
|
||||
{
|
||||
// Calculate error
|
||||
double error = setpoint - pv;
|
||||
double PID::calculate(double setpoint, double pv) {
|
||||
// Calculate error
|
||||
double error = setpoint - pv;
|
||||
|
||||
// Proportional term
|
||||
double Pout = Kp * error;
|
||||
// Proportional term
|
||||
double Pout = Kp * error;
|
||||
|
||||
// Integral term
|
||||
integral += error * dt;
|
||||
double Iout = Ki * integral;
|
||||
// Integral term
|
||||
integral += error * dt;
|
||||
double Iout = Ki * integral;
|
||||
|
||||
// Derivative term
|
||||
double derivative = (error - pre_error) / dt;
|
||||
double Dout = Kd * derivative;
|
||||
// Derivative term
|
||||
double derivative = (error - pre_error) / dt;
|
||||
double Dout = Kd * derivative;
|
||||
|
||||
// Calculate total output
|
||||
double output = Pout + Iout + Dout;
|
||||
// Calculate total output
|
||||
double output = Pout + Iout + Dout;
|
||||
|
||||
// Restrict to max/min
|
||||
if (output > max)
|
||||
output = max;
|
||||
else if (output < min)
|
||||
output = min;
|
||||
// Restrict to max/min
|
||||
if (output > max)
|
||||
output = max;
|
||||
else if (output < min)
|
||||
output = min;
|
||||
|
||||
// Save error to previous error
|
||||
pre_error = error;
|
||||
// Save error to previous error
|
||||
pre_error = error;
|
||||
|
||||
return output;
|
||||
return output;
|
||||
}
|
||||
|
|
|
@ -5,262 +5,227 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#include <villas/utils.hpp>
|
||||
#include <villas/hist.hpp>
|
||||
#include <villas/config.hpp>
|
||||
#include <villas/table.hpp>
|
||||
#include <villas/exceptions.hpp>
|
||||
#include <villas/hist.hpp>
|
||||
#include <villas/table.hpp>
|
||||
#include <villas/utils.hpp>
|
||||
|
||||
using namespace villas;
|
||||
using namespace villas::utils;
|
||||
|
||||
namespace villas {
|
||||
|
||||
Hist::Hist(int buckets, Hist::cnt_t wu) :
|
||||
resolution(0),
|
||||
high(0),
|
||||
low(0),
|
||||
highest(std::numeric_limits<double>::min()),
|
||||
lowest(std::numeric_limits<double>::max()),
|
||||
last(0),
|
||||
total(0),
|
||||
warmup(wu),
|
||||
higher(0),
|
||||
lower(0),
|
||||
data(buckets, 0),
|
||||
_m{0, 0},
|
||||
_s{0, 0}
|
||||
{ }
|
||||
Hist::Hist(int buckets, Hist::cnt_t wu)
|
||||
: resolution(0), high(0), low(0),
|
||||
highest(std::numeric_limits<double>::min()),
|
||||
lowest(std::numeric_limits<double>::max()), last(0), total(0), warmup(wu),
|
||||
higher(0), lower(0), data(buckets, 0), _m{0, 0}, _s{0, 0} {}
|
||||
|
||||
void Hist::put(double value)
|
||||
{
|
||||
last = value;
|
||||
void Hist::put(double value) {
|
||||
last = value;
|
||||
|
||||
// Update min/max
|
||||
if (value > highest)
|
||||
highest = value;
|
||||
if (value < lowest)
|
||||
lowest = value;
|
||||
// Update min/max
|
||||
if (value > highest)
|
||||
highest = value;
|
||||
if (value < lowest)
|
||||
lowest = value;
|
||||
|
||||
if (data.size()) {
|
||||
if (total < warmup) {
|
||||
// We are still in warmup phase... Waiting for more samples...
|
||||
}
|
||||
else if (data.size() && total == warmup && warmup != 0) {
|
||||
low = getMean() - 3 * getStddev();
|
||||
high = getMean() + 3 * getStddev();
|
||||
resolution = (high - low) / data.size();
|
||||
}
|
||||
else if (data.size() && (total == warmup) && (warmup == 0)) {
|
||||
// There is no warmup phase
|
||||
// TODO resolution = ?
|
||||
}
|
||||
else {
|
||||
idx_t idx = std::round((value - low) / resolution);
|
||||
if (data.size()) {
|
||||
if (total < warmup) {
|
||||
// We are still in warmup phase... Waiting for more samples...
|
||||
} else if (data.size() && total == warmup && warmup != 0) {
|
||||
low = getMean() - 3 * getStddev();
|
||||
high = getMean() + 3 * getStddev();
|
||||
resolution = (high - low) / data.size();
|
||||
} else if (data.size() && (total == warmup) && (warmup == 0)) {
|
||||
// There is no warmup phase
|
||||
// TODO resolution = ?
|
||||
} else {
|
||||
idx_t idx = std::round((value - low) / resolution);
|
||||
|
||||
// Check bounds and increment
|
||||
if (idx >= (idx_t) data.size())
|
||||
higher++;
|
||||
else if (idx < 0)
|
||||
lower++;
|
||||
else
|
||||
data[idx]++;
|
||||
}
|
||||
}
|
||||
// Check bounds and increment
|
||||
if (idx >= (idx_t)data.size())
|
||||
higher++;
|
||||
else if (idx < 0)
|
||||
lower++;
|
||||
else
|
||||
data[idx]++;
|
||||
}
|
||||
}
|
||||
|
||||
total++;
|
||||
total++;
|
||||
|
||||
// Online / running calculation of variance and mean
|
||||
// by Donald Knuth’s Art of Computer Programming, Vol 2, page 232, 3rd edition
|
||||
if (total == 1) {
|
||||
_m[1] = _m[0] = value;
|
||||
_s[1] = 0.0;
|
||||
}
|
||||
else {
|
||||
_m[0] = _m[1] + (value - _m[1]) / total;
|
||||
_s[0] = _s[1] + (value - _m[1]) * (value - _m[0]);
|
||||
// Online / running calculation of variance and mean
|
||||
// by Donald Knuth’s Art of Computer Programming, Vol 2, page 232, 3rd edition
|
||||
if (total == 1) {
|
||||
_m[1] = _m[0] = value;
|
||||
_s[1] = 0.0;
|
||||
} else {
|
||||
_m[0] = _m[1] + (value - _m[1]) / total;
|
||||
_s[0] = _s[1] + (value - _m[1]) * (value - _m[0]);
|
||||
|
||||
// Set up for next iteration
|
||||
_m[1] = _m[0];
|
||||
_s[1] = _s[0];
|
||||
}
|
||||
// Set up for next iteration
|
||||
_m[1] = _m[0];
|
||||
_s[1] = _s[0];
|
||||
}
|
||||
}
|
||||
|
||||
void Hist::reset()
|
||||
{
|
||||
total = 0;
|
||||
higher = 0;
|
||||
lower = 0;
|
||||
void Hist::reset() {
|
||||
total = 0;
|
||||
higher = 0;
|
||||
lower = 0;
|
||||
|
||||
highest = std::numeric_limits<double>::min();
|
||||
lowest = std::numeric_limits<double>::max();
|
||||
highest = std::numeric_limits<double>::min();
|
||||
lowest = std::numeric_limits<double>::max();
|
||||
|
||||
for (auto &elm : data)
|
||||
elm = 0;
|
||||
for (auto &elm : data)
|
||||
elm = 0;
|
||||
}
|
||||
|
||||
double Hist::getMean() const
|
||||
{
|
||||
return total > 0 ? _m[0] : std::numeric_limits<double>::quiet_NaN();
|
||||
double Hist::getMean() const {
|
||||
return total > 0 ? _m[0] : std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
|
||||
double Hist::getVar() const
|
||||
{
|
||||
return total > 1 ? _s[0] / (total - 1) : std::numeric_limits<double>::quiet_NaN();
|
||||
double Hist::getVar() const {
|
||||
return total > 1 ? _s[0] / (total - 1)
|
||||
: std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
|
||||
double Hist::getStddev() const
|
||||
{
|
||||
return sqrt(getVar());
|
||||
double Hist::getStddev() const { return sqrt(getVar()); }
|
||||
|
||||
void Hist::print(Logger logger, bool details) const {
|
||||
if (total > 0) {
|
||||
Hist::cnt_t missed = total - higher - lower;
|
||||
|
||||
logger->info("Counted values: {} ({} between {} and {})", total, missed,
|
||||
low, high);
|
||||
logger->info("Highest: {:g}", highest);
|
||||
logger->info("Lowest: {:g}", lowest);
|
||||
logger->info("Mu: {:g}", getMean());
|
||||
logger->info("1/Mu: {:g}", 1.0 / getMean());
|
||||
logger->info("Variance: {:g}", getVar());
|
||||
logger->info("Stddev: {:g}", getStddev());
|
||||
|
||||
if (details && total - higher - lower > 0) {
|
||||
char *buf = dump();
|
||||
logger->info("Matlab: {}", buf);
|
||||
free(buf);
|
||||
|
||||
plot(logger);
|
||||
}
|
||||
} else
|
||||
logger->info("Counted values: {}", total);
|
||||
}
|
||||
|
||||
void Hist::print(Logger logger, bool details) const
|
||||
{
|
||||
if (total > 0) {
|
||||
Hist::cnt_t missed = total - higher - lower;
|
||||
void Hist::plot(Logger logger) const {
|
||||
// Get highest bar
|
||||
Hist::cnt_t max = *std::max_element(data.begin(), data.end());
|
||||
|
||||
logger->info("Counted values: {} ({} between {} and {})", total, missed, low, high);
|
||||
logger->info("Highest: {:g}", highest);
|
||||
logger->info("Lowest: {:g}", lowest);
|
||||
logger->info("Mu: {:g}", getMean());
|
||||
logger->info("1/Mu: {:g}", 1.0 / getMean());
|
||||
logger->info("Variance: {:g}", getVar());
|
||||
logger->info("Stddev: {:g}", getStddev());
|
||||
std::vector<TableColumn> cols = {
|
||||
{-9, TableColumn::Alignment::RIGHT, "Value", "%+9.3g"},
|
||||
{-6, TableColumn::Alignment::RIGHT, "Count", "%6ju"},
|
||||
{0, TableColumn::Alignment::LEFT, "Plot", "%s", "occurences"}};
|
||||
|
||||
if (details && total - higher - lower > 0) {
|
||||
char *buf = dump();
|
||||
logger->info("Matlab: {}", buf);
|
||||
free(buf);
|
||||
Table table = Table(logger, cols);
|
||||
|
||||
plot(logger);
|
||||
}
|
||||
}
|
||||
else
|
||||
logger->info("Counted values: {}", total);
|
||||
// Print plot
|
||||
table.header();
|
||||
|
||||
for (size_t i = 0; i < data.size(); i++) {
|
||||
double value = low + (i)*resolution;
|
||||
Hist::cnt_t cnt = data[i];
|
||||
int bar = cols[2].getWidth() * ((double)cnt / max);
|
||||
|
||||
char *buf = strf("%s", "");
|
||||
for (int i = 0; i < bar; i++)
|
||||
buf = strcatf(&buf, "\u2588");
|
||||
|
||||
table.row(3, value, cnt, buf);
|
||||
|
||||
free(buf);
|
||||
}
|
||||
}
|
||||
|
||||
void Hist::plot(Logger logger) const
|
||||
{
|
||||
// Get highest bar
|
||||
Hist::cnt_t max = *std::max_element(data.begin(), data.end());
|
||||
char *Hist::dump() const {
|
||||
char *buf = new char[128];
|
||||
if (!buf)
|
||||
throw MemoryAllocationError();
|
||||
|
||||
std::vector<TableColumn> cols = {
|
||||
{ -9, TableColumn::Alignment::RIGHT, "Value", "%+9.3g" },
|
||||
{ -6, TableColumn::Alignment::RIGHT, "Count", "%6ju" },
|
||||
{ 0, TableColumn::Alignment::LEFT, "Plot", "%s", "occurences" }
|
||||
};
|
||||
memset(buf, 0, 128);
|
||||
|
||||
Table table = Table(logger, cols);
|
||||
strcatf(&buf, "[ ");
|
||||
|
||||
// Print plot
|
||||
table.header();
|
||||
for (auto elm : data)
|
||||
strcatf(&buf, "%ju ", elm);
|
||||
|
||||
for (size_t i = 0; i < data.size(); i++) {
|
||||
double value = low + (i) * resolution;
|
||||
Hist::cnt_t cnt = data[i];
|
||||
int bar = cols[2].getWidth() * ((double) cnt / max);
|
||||
strcatf(&buf, "]");
|
||||
|
||||
char *buf = strf("%s", "");
|
||||
for (int i = 0; i < bar; i++)
|
||||
buf = strcatf(&buf, "\u2588");
|
||||
|
||||
table.row(3, value, cnt, buf);
|
||||
|
||||
free(buf);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
char * Hist::dump() const
|
||||
{
|
||||
char *buf = new char[128];
|
||||
if (!buf)
|
||||
throw MemoryAllocationError();
|
||||
json_t *Hist::toJson() const {
|
||||
json_t *json_buckets, *json_hist;
|
||||
|
||||
memset(buf, 0, 128);
|
||||
json_hist = json_pack("{ s: f, s: f, s: i }", "low", low, "high", high,
|
||||
"total", total);
|
||||
|
||||
strcatf(&buf, "[ ");
|
||||
if (total > 0) {
|
||||
json_object_update(json_hist,
|
||||
json_pack("{ s: i, s: i, s: f, s: f, s: f, s: f, s: f }",
|
||||
"higher", higher, "lower", lower, "highest",
|
||||
highest, "lowest", lowest, "mean", getMean(),
|
||||
"variance", getVar(), "stddev", getStddev()));
|
||||
}
|
||||
|
||||
for (auto elm : data)
|
||||
strcatf(&buf, "%ju ", elm);
|
||||
if (total - lower - higher > 0) {
|
||||
json_buckets = json_array();
|
||||
|
||||
strcatf(&buf, "]");
|
||||
for (auto elm : data)
|
||||
json_array_append(json_buckets, json_integer(elm));
|
||||
|
||||
return buf;
|
||||
json_object_set(json_hist, "buckets", json_buckets);
|
||||
}
|
||||
|
||||
return json_hist;
|
||||
}
|
||||
|
||||
json_t * Hist::toJson() const
|
||||
{
|
||||
json_t *json_buckets, *json_hist;
|
||||
int Hist::dumpJson(FILE *f) const {
|
||||
json_t *j = Hist::toJson();
|
||||
|
||||
json_hist = json_pack("{ s: f, s: f, s: i }",
|
||||
"low", low,
|
||||
"high", high,
|
||||
"total", total
|
||||
);
|
||||
int ret = json_dumpf(j, f, 0);
|
||||
|
||||
if (total > 0) {
|
||||
json_object_update(json_hist, json_pack("{ s: i, s: i, s: f, s: f, s: f, s: f, s: f }",
|
||||
"higher", higher,
|
||||
"lower", lower,
|
||||
"highest", highest,
|
||||
"lowest", lowest,
|
||||
"mean", getMean(),
|
||||
"variance", getVar(),
|
||||
"stddev", getStddev()
|
||||
));
|
||||
}
|
||||
json_decref(j);
|
||||
|
||||
if (total - lower - higher > 0) {
|
||||
json_buckets = json_array();
|
||||
|
||||
for (auto elm : data)
|
||||
json_array_append(json_buckets, json_integer(elm));
|
||||
|
||||
json_object_set(json_hist, "buckets", json_buckets);
|
||||
}
|
||||
|
||||
return json_hist;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int Hist::dumpJson(FILE *f) const
|
||||
{
|
||||
json_t *j = Hist::toJson();
|
||||
int Hist::dumpMatlab(FILE *f) const {
|
||||
fprintf(f, "struct(");
|
||||
fprintf(f, "'low', %f, ", low);
|
||||
fprintf(f, "'high', %f, ", high);
|
||||
fprintf(f, "'total', %ju, ", total);
|
||||
fprintf(f, "'higher', %ju, ", higher);
|
||||
fprintf(f, "'lower', %ju, ", lower);
|
||||
fprintf(f, "'highest', %f, ", highest);
|
||||
fprintf(f, "'lowest', %f, ", lowest);
|
||||
fprintf(f, "'mean', %f, ", getMean());
|
||||
fprintf(f, "'variance', %f, ", getVar());
|
||||
fprintf(f, "'stddev', %f, ", getStddev());
|
||||
|
||||
int ret = json_dumpf(j, f, 0);
|
||||
if (total - lower - higher > 0) {
|
||||
char *buf = dump();
|
||||
fprintf(f, "'buckets', %s", buf);
|
||||
free(buf);
|
||||
} else
|
||||
fprintf(f, "'buckets', zeros(1, %zu)", data.size());
|
||||
|
||||
json_decref(j);
|
||||
fprintf(f, ")");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int Hist::dumpMatlab(FILE *f) const
|
||||
{
|
||||
fprintf(f, "struct(");
|
||||
fprintf(f, "'low', %f, ", low);
|
||||
fprintf(f, "'high', %f, ", high);
|
||||
fprintf(f, "'total', %ju, ", total);
|
||||
fprintf(f, "'higher', %ju, ", higher);
|
||||
fprintf(f, "'lower', %ju, ", lower);
|
||||
fprintf(f, "'highest', %f, ", highest);
|
||||
fprintf(f, "'lowest', %f, ", lowest);
|
||||
fprintf(f, "'mean', %f, ", getMean());
|
||||
fprintf(f, "'variance', %f, ", getVar());
|
||||
fprintf(f, "'stddev', %f, ", getStddev());
|
||||
|
||||
if (total - lower - higher > 0) {
|
||||
char *buf = dump();
|
||||
fprintf(f, "'buckets', %s", buf);
|
||||
free(buf);
|
||||
}
|
||||
else
|
||||
fprintf(f, "'buckets', zeros(1, %zu)", data.size());
|
||||
|
||||
fprintf(f, ")");
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace villas
|
||||
|
|
|
@ -5,336 +5,329 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <sys/utsname.h>
|
||||
#include <cinttypes>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <cinttypes>
|
||||
#include <sys/utsname.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <villas/log.hpp>
|
||||
#include <villas/utils.hpp>
|
||||
#include <villas/config.hpp>
|
||||
#include <villas/kernel/kernel.hpp>
|
||||
#include <villas/log.hpp>
|
||||
#include <villas/utils.hpp>
|
||||
|
||||
#include <villas/kernel/kernel.hpp>
|
||||
#include <villas/exceptions.hpp>
|
||||
#include <villas/kernel/kernel.hpp>
|
||||
|
||||
using namespace villas;
|
||||
using namespace villas::utils;
|
||||
|
||||
Version villas::kernel::getVersion()
|
||||
{
|
||||
struct utsname uts;
|
||||
Version villas::kernel::getVersion() {
|
||||
struct utsname uts;
|
||||
|
||||
if (uname(&uts) < 0)
|
||||
throw SystemError("Failed to retrieve system identification");
|
||||
if (uname(&uts) < 0)
|
||||
throw SystemError("Failed to retrieve system identification");
|
||||
|
||||
std::string rel = uts.release;
|
||||
std::string rel = uts.release;
|
||||
|
||||
// Remove release part. E.g. 4.9.93-linuxkit-aufs
|
||||
auto sep = rel.find('-');
|
||||
auto ver = rel.substr(0, sep - 1);
|
||||
// Remove release part. E.g. 4.9.93-linuxkit-aufs
|
||||
auto sep = rel.find('-');
|
||||
auto ver = rel.substr(0, sep - 1);
|
||||
|
||||
return Version(ver);
|
||||
return Version(ver);
|
||||
}
|
||||
|
||||
int villas::kernel::getCachelineSize()
|
||||
{
|
||||
int villas::kernel::getCachelineSize() {
|
||||
#if defined(__linux__) && defined(__x86_64__) && defined(__GLIBC__)
|
||||
return sysconf(_SC_LEVEL1_ICACHE_LINESIZE);
|
||||
return sysconf(_SC_LEVEL1_ICACHE_LINESIZE);
|
||||
#elif defined(__MACH__)
|
||||
// Open the command for reading.
|
||||
FILE *fp = popen("sysctl -n machdep.cpu.cache.linesize", "r");
|
||||
if (fp == nullptr)
|
||||
return -1;
|
||||
// Open the command for reading.
|
||||
FILE *fp = popen("sysctl -n machdep.cpu.cache.linesize", "r");
|
||||
if (fp == nullptr)
|
||||
return -1;
|
||||
|
||||
int ret, size;
|
||||
int ret, size;
|
||||
|
||||
ret = fscanf(fp, "%d", &size);
|
||||
ret = fscanf(fp, "%d", &size);
|
||||
|
||||
pclose(fp);
|
||||
pclose(fp);
|
||||
|
||||
return ret == 1 ? size : -1;
|
||||
return ret == 1 ? size : -1;
|
||||
#else
|
||||
return CACHELINE_SIZE;
|
||||
return CACHELINE_SIZE;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(__linux__)
|
||||
int villas::kernel::getPageSize()
|
||||
{
|
||||
return sysconf(_SC_PAGESIZE);
|
||||
}
|
||||
int villas::kernel::getPageSize() { return sysconf(_SC_PAGESIZE); }
|
||||
#else
|
||||
#error "Unsupported platform"
|
||||
#error "Unsupported platform"
|
||||
#endif
|
||||
|
||||
// There is no sysconf interface to get the hugepage size
|
||||
int villas::kernel::getHugePageSize()
|
||||
{
|
||||
int villas::kernel::getHugePageSize() {
|
||||
#ifdef __linux__
|
||||
char *key, *value, *unit, *line = nullptr, *lasts;
|
||||
int sz = -1;
|
||||
size_t len = 0;
|
||||
FILE *f;
|
||||
char *key, *value, *unit, *line = nullptr, *lasts;
|
||||
int sz = -1;
|
||||
size_t len = 0;
|
||||
FILE *f;
|
||||
|
||||
f = fopen(PROCFS_PATH "/meminfo", "r");
|
||||
if (!f)
|
||||
return -1;
|
||||
f = fopen(PROCFS_PATH "/meminfo", "r");
|
||||
if (!f)
|
||||
return -1;
|
||||
|
||||
while (getline(&line, &len, f) != -1) {
|
||||
key = strtok_r(line, ": ", &lasts);
|
||||
value = strtok_r(nullptr, " ", &lasts);
|
||||
unit = strtok_r(nullptr, "\n", &lasts);
|
||||
while (getline(&line, &len, f) != -1) {
|
||||
key = strtok_r(line, ": ", &lasts);
|
||||
value = strtok_r(nullptr, " ", &lasts);
|
||||
unit = strtok_r(nullptr, "\n", &lasts);
|
||||
|
||||
if (!strcmp(key, "Hugepagesize") && !strcmp(unit, "kB")) {
|
||||
sz = strtoul(value, nullptr, 10) * 1024;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!strcmp(key, "Hugepagesize") && !strcmp(unit, "kB")) {
|
||||
sz = strtoul(value, nullptr, 10) * 1024;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(line);
|
||||
fclose(f);
|
||||
free(line);
|
||||
fclose(f);
|
||||
|
||||
return sz;
|
||||
return sz;
|
||||
#elif defined(__x86_64__)
|
||||
return 1 << 21;
|
||||
return 1 << 21;
|
||||
#elif defined(__i386__)
|
||||
return 1 << 22;
|
||||
return 1 << 22;
|
||||
#else
|
||||
#error "Unsupported architecture"
|
||||
#error "Unsupported architecture"
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
int villas::kernel::setModuleParam(const char *module, const char *param, const char *value)
|
||||
{
|
||||
FILE *f;
|
||||
char fn[256];
|
||||
int villas::kernel::setModuleParam(const char *module, const char *param,
|
||||
const char *value) {
|
||||
FILE *f;
|
||||
char fn[256];
|
||||
|
||||
snprintf(fn, sizeof(fn), "%s/module/%s/parameters/%s", SYSFS_PATH, module, param);
|
||||
f = fopen(fn, "w");
|
||||
if (!f)
|
||||
throw RuntimeError("Failed set parameter {} for kernel module {} to {}", module, param, value);
|
||||
snprintf(fn, sizeof(fn), "%s/module/%s/parameters/%s", SYSFS_PATH, module,
|
||||
param);
|
||||
f = fopen(fn, "w");
|
||||
if (!f)
|
||||
throw RuntimeError("Failed set parameter {} for kernel module {} to {}",
|
||||
module, param, value);
|
||||
|
||||
auto logger = logging.get("kernel");
|
||||
logger->debug("Set parameter {} of kernel module {} to {}", module, param, value);
|
||||
auto logger = logging.get("kernel");
|
||||
logger->debug("Set parameter {} of kernel module {} to {}", module, param,
|
||||
value);
|
||||
|
||||
fprintf(f, "%s", value);
|
||||
fclose(f);
|
||||
fprintf(f, "%s", value);
|
||||
fclose(f);
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int villas::kernel::loadModule(const char *module)
|
||||
{
|
||||
int ret;
|
||||
int villas::kernel::loadModule(const char *module) {
|
||||
int ret;
|
||||
|
||||
ret = isModuleLoaded(module);
|
||||
if (!ret) {
|
||||
auto logger = logging.get("kernel");
|
||||
logger->debug("Kernel module {} already loaded...", module);
|
||||
return 0;
|
||||
}
|
||||
ret = isModuleLoaded(module);
|
||||
if (!ret) {
|
||||
auto logger = logging.get("kernel");
|
||||
logger->debug("Kernel module {} already loaded...", module);
|
||||
return 0;
|
||||
}
|
||||
|
||||
pid_t pid = fork();
|
||||
switch (pid) {
|
||||
case -1: // Error
|
||||
return -1;
|
||||
pid_t pid = fork();
|
||||
switch (pid) {
|
||||
case -1: // Error
|
||||
return -1;
|
||||
|
||||
case 0: // Child
|
||||
execlp("modprobe", "modprobe", module, (char *) 0);
|
||||
exit(EXIT_FAILURE); // exec() never returns
|
||||
case 0: // Child
|
||||
execlp("modprobe", "modprobe", module, (char *)0);
|
||||
exit(EXIT_FAILURE); // exec() never returns
|
||||
|
||||
default:
|
||||
wait(&ret);
|
||||
default:
|
||||
wait(&ret);
|
||||
|
||||
return isModuleLoaded(module);
|
||||
}
|
||||
return isModuleLoaded(module);
|
||||
}
|
||||
}
|
||||
|
||||
int villas::kernel::isModuleLoaded(const char *module)
|
||||
{
|
||||
FILE *f;
|
||||
int ret = -1;
|
||||
char *line = nullptr;
|
||||
size_t len = 0;
|
||||
int villas::kernel::isModuleLoaded(const char *module) {
|
||||
FILE *f;
|
||||
int ret = -1;
|
||||
char *line = nullptr;
|
||||
size_t len = 0;
|
||||
|
||||
f = fopen(PROCFS_PATH "/modules", "r");
|
||||
if (!f)
|
||||
return -1;
|
||||
f = fopen(PROCFS_PATH "/modules", "r");
|
||||
if (!f)
|
||||
return -1;
|
||||
|
||||
while (getline(&line, &len, f) >= 0) {
|
||||
if (strstr(line, module) == line) {
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (getline(&line, &len, f) >= 0) {
|
||||
if (strstr(line, module) == line) {
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(line);
|
||||
fclose(f);
|
||||
free(line);
|
||||
fclose(f);
|
||||
|
||||
return ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int villas::kernel::getCmdlineParam(const char *param, char *buf, size_t len)
|
||||
{
|
||||
int ret;
|
||||
char cmdline[512], key[128], value[128], *lasts, *tok;
|
||||
int villas::kernel::getCmdlineParam(const char *param, char *buf, size_t len) {
|
||||
int ret;
|
||||
char cmdline[512], key[128], value[128], *lasts, *tok;
|
||||
|
||||
FILE *f = fopen(PROCFS_PATH "/cmdline", "r");
|
||||
if (!f)
|
||||
return -1;
|
||||
FILE *f = fopen(PROCFS_PATH "/cmdline", "r");
|
||||
if (!f)
|
||||
return -1;
|
||||
|
||||
if (!fgets(cmdline, sizeof(cmdline), f))
|
||||
goto out;
|
||||
if (!fgets(cmdline, sizeof(cmdline), f))
|
||||
goto out;
|
||||
|
||||
tok = strtok_r(cmdline, " \t", &lasts);
|
||||
do {
|
||||
ret = sscanf(tok, "%127[^=]=%127s", key, value);
|
||||
if (ret >= 1) {
|
||||
auto logger = logging.get("kernel");
|
||||
if (ret >= 2)
|
||||
logger->debug("Found kernel param: {}={}", key, value);
|
||||
else
|
||||
logger->debug("Found kernel param: {}", key);
|
||||
tok = strtok_r(cmdline, " \t", &lasts);
|
||||
do {
|
||||
ret = sscanf(tok, "%127[^=]=%127s", key, value);
|
||||
if (ret >= 1) {
|
||||
auto logger = logging.get("kernel");
|
||||
if (ret >= 2)
|
||||
logger->debug("Found kernel param: {}={}", key, value);
|
||||
else
|
||||
logger->debug("Found kernel param: {}", key);
|
||||
|
||||
if (strcmp(param, key) == 0) {
|
||||
if (ret >= 2 && buf)
|
||||
snprintf(buf, len, "%s", value);
|
||||
if (strcmp(param, key) == 0) {
|
||||
if (ret >= 2 && buf)
|
||||
snprintf(buf, len, "%s", value);
|
||||
|
||||
return 0; // Found
|
||||
}
|
||||
}
|
||||
} while ((tok = strtok_r(nullptr, " \t", &lasts)));
|
||||
return 0; // Found
|
||||
}
|
||||
}
|
||||
} while ((tok = strtok_r(nullptr, " \t", &lasts)));
|
||||
|
||||
out:
|
||||
fclose(f);
|
||||
fclose(f);
|
||||
|
||||
return -1; // Not found or error
|
||||
return -1; // Not found or error
|
||||
}
|
||||
|
||||
int villas::kernel::getNrHugepages()
|
||||
{
|
||||
FILE *f;
|
||||
int nr, ret;
|
||||
int villas::kernel::getNrHugepages() {
|
||||
FILE *f;
|
||||
int nr, ret;
|
||||
|
||||
f = fopen(PROCFS_PATH "/sys/vm/nr_hugepages", "r");
|
||||
if (!f) {
|
||||
auto logger = logging.get("kernel");
|
||||
logger->error("Failed to open {}: {}", PROCFS_PATH "/sys/vm/nr_hugepages", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
f = fopen(PROCFS_PATH "/sys/vm/nr_hugepages", "r");
|
||||
if (!f) {
|
||||
auto logger = logging.get("kernel");
|
||||
logger->error("Failed to open {}: {}", PROCFS_PATH "/sys/vm/nr_hugepages",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = fscanf(f, "%d", &nr);
|
||||
if (ret != 1)
|
||||
nr = -1;
|
||||
ret = fscanf(f, "%d", &nr);
|
||||
if (ret != 1)
|
||||
nr = -1;
|
||||
|
||||
fclose(f);
|
||||
fclose(f);
|
||||
|
||||
return nr;
|
||||
return nr;
|
||||
}
|
||||
|
||||
int villas::kernel::setNrHugepages(int nr)
|
||||
{
|
||||
FILE *f;
|
||||
int villas::kernel::setNrHugepages(int nr) {
|
||||
FILE *f;
|
||||
|
||||
f = fopen(PROCFS_PATH "/sys/vm/nr_hugepages", "w");
|
||||
if (!f)
|
||||
return -1;
|
||||
f = fopen(PROCFS_PATH "/sys/vm/nr_hugepages", "w");
|
||||
if (!f)
|
||||
return -1;
|
||||
|
||||
fprintf(f, "%d\n", nr);
|
||||
fclose(f);
|
||||
fprintf(f, "%d\n", nr);
|
||||
fclose(f);
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int villas::kernel::setIRQAffinity(unsigned irq, uintmax_t aff, uintmax_t *old)
|
||||
{
|
||||
char fn[64];
|
||||
FILE *f;
|
||||
int ret = 0;
|
||||
int villas::kernel::setIRQAffinity(unsigned irq, uintmax_t aff,
|
||||
uintmax_t *old) {
|
||||
char fn[64];
|
||||
FILE *f;
|
||||
int ret = 0;
|
||||
|
||||
snprintf(fn, sizeof(fn), "/proc/irq/%u/smp_affinity", irq);
|
||||
snprintf(fn, sizeof(fn), "/proc/irq/%u/smp_affinity", irq);
|
||||
|
||||
f = fopen(fn, "w+");
|
||||
if (!f)
|
||||
return -1; // IRQ does not exist
|
||||
f = fopen(fn, "w+");
|
||||
if (!f)
|
||||
return -1; // IRQ does not exist
|
||||
|
||||
if (old)
|
||||
ret = fscanf(f, "%jx", old);
|
||||
if (old)
|
||||
ret = fscanf(f, "%jx", old);
|
||||
|
||||
fprintf(f, "%jx", aff);
|
||||
fclose(f);
|
||||
fprintf(f, "%jx", aff);
|
||||
fclose(f);
|
||||
|
||||
return ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int villas::kernel::get_cpu_frequency(uint64_t *freq)
|
||||
{
|
||||
char *line = nullptr, *sep, *end;
|
||||
size_t len = 0;
|
||||
double dfreq;
|
||||
int ret;
|
||||
FILE *f;
|
||||
int villas::kernel::get_cpu_frequency(uint64_t *freq) {
|
||||
char *line = nullptr, *sep, *end;
|
||||
size_t len = 0;
|
||||
double dfreq;
|
||||
int ret;
|
||||
FILE *f;
|
||||
|
||||
// Try to get CPU frequency from cpufreq module
|
||||
f = fopen(SYSFS_PATH "/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq", "r");
|
||||
if (!f)
|
||||
goto cpuinfo;
|
||||
// Try to get CPU frequency from cpufreq module
|
||||
f = fopen(SYSFS_PATH "/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq",
|
||||
"r");
|
||||
if (!f)
|
||||
goto cpuinfo;
|
||||
|
||||
ret = fscanf(f, "%" PRIu64, freq);
|
||||
fclose(f);
|
||||
if (ret != 1)
|
||||
return -1;
|
||||
ret = fscanf(f, "%" PRIu64, freq);
|
||||
fclose(f);
|
||||
if (ret != 1)
|
||||
return -1;
|
||||
|
||||
// cpufreq reports kHz
|
||||
*freq = *freq * 1000;
|
||||
// cpufreq reports kHz
|
||||
*freq = *freq * 1000;
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
|
||||
cpuinfo:
|
||||
// Try to read CPU frequency from /proc/cpuinfo
|
||||
f = fopen(PROCFS_PATH "/cpuinfo", "r");
|
||||
if (!f)
|
||||
return -1; // We give up here
|
||||
// Try to read CPU frequency from /proc/cpuinfo
|
||||
f = fopen(PROCFS_PATH "/cpuinfo", "r");
|
||||
if (!f)
|
||||
return -1; // We give up here
|
||||
|
||||
ret = -1;
|
||||
while (getline(&line, &len, f) >= 0) {
|
||||
if (strstr(line, "cpu MHz") == line) {
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ret)
|
||||
goto out;
|
||||
ret = -1;
|
||||
while (getline(&line, &len, f) >= 0) {
|
||||
if (strstr(line, "cpu MHz") == line) {
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
sep = strchr(line, ':');
|
||||
if (!sep) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
sep = strchr(line, ':');
|
||||
if (!sep) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
dfreq = strtod(sep+1, &end);
|
||||
dfreq = strtod(sep + 1, &end);
|
||||
|
||||
if (end == sep+1) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
if (end == sep + 1) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
// Frequency is given in MHz
|
||||
*freq = dfreq * 1e6;
|
||||
// Frequency is given in MHz
|
||||
*freq = dfreq * 1e6;
|
||||
|
||||
out: fclose(f);
|
||||
free(line);
|
||||
out:
|
||||
fclose(f);
|
||||
free(line);
|
||||
|
||||
return ret;
|
||||
return ret;
|
||||
}
|
||||
#endif // __linux__
|
||||
|
|
|
@ -5,456 +5,441 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <dirent.h>
|
||||
#include <libgen.h>
|
||||
#include <cstring>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <libgen.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/pci_regs.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <villas/utils.hpp>
|
||||
#include <villas/exceptions.hpp>
|
||||
#include <villas/config.hpp>
|
||||
#include <villas/exceptions.hpp>
|
||||
#include <villas/kernel/pci.hpp>
|
||||
#include <villas/utils.hpp>
|
||||
|
||||
using namespace villas::kernel::pci;
|
||||
|
||||
#define PCI_BASE_ADDRESS_N(n) (PCI_BASE_ADDRESS_0 + sizeof(uint32_t) * (n))
|
||||
|
||||
DeviceList::DeviceList()
|
||||
{
|
||||
struct dirent *e;
|
||||
DIR *dp;
|
||||
FILE *f;
|
||||
char path[PATH_MAX];
|
||||
int ret;
|
||||
DeviceList::DeviceList() {
|
||||
struct dirent *e;
|
||||
DIR *dp;
|
||||
FILE *f;
|
||||
char path[PATH_MAX];
|
||||
int ret;
|
||||
|
||||
snprintf(path, sizeof(path), "%s/bus/pci/devices", SYSFS_PATH);
|
||||
snprintf(path, sizeof(path), "%s/bus/pci/devices", SYSFS_PATH);
|
||||
|
||||
dp = opendir(path);
|
||||
if (!dp)
|
||||
throw SystemError("Failed to detect PCI devices");
|
||||
dp = opendir(path);
|
||||
if (!dp)
|
||||
throw SystemError("Failed to detect PCI devices");
|
||||
|
||||
while ((e = readdir(dp))) {
|
||||
// Ignore special entries
|
||||
if ((strcmp(e->d_name, ".") == 0) ||
|
||||
(strcmp(e->d_name, "..") == 0))
|
||||
continue;
|
||||
while ((e = readdir(dp))) {
|
||||
// Ignore special entries
|
||||
if ((strcmp(e->d_name, ".") == 0) || (strcmp(e->d_name, "..") == 0))
|
||||
continue;
|
||||
|
||||
Id id;
|
||||
Slot slot;
|
||||
Id id;
|
||||
Slot slot;
|
||||
|
||||
struct {
|
||||
const char *s;
|
||||
unsigned int *p;
|
||||
} map[] = {
|
||||
{"vendor", &id.vendor},
|
||||
{"device", &id.device}};
|
||||
struct {
|
||||
const char *s;
|
||||
unsigned int *p;
|
||||
} map[] = {{"vendor", &id.vendor}, {"device", &id.device}};
|
||||
|
||||
// Read vendor & device id
|
||||
for (int i = 0; i < 2; i++) {
|
||||
snprintf(path, sizeof(path), "%s/bus/pci/devices/%s/%s", SYSFS_PATH, e->d_name, map[i].s);
|
||||
// Read vendor & device id
|
||||
for (int i = 0; i < 2; i++) {
|
||||
snprintf(path, sizeof(path), "%s/bus/pci/devices/%s/%s", SYSFS_PATH,
|
||||
e->d_name, map[i].s);
|
||||
|
||||
f = fopen(path, "r");
|
||||
if (!f)
|
||||
throw SystemError("Failed to open '{}'", path);
|
||||
f = fopen(path, "r");
|
||||
if (!f)
|
||||
throw SystemError("Failed to open '{}'", path);
|
||||
|
||||
ret = fscanf(f, "%x", map[i].p);
|
||||
if (ret != 1)
|
||||
throw RuntimeError("Failed to parse {} ID from: {}", map[i].s, path);
|
||||
ret = fscanf(f, "%x", map[i].p);
|
||||
if (ret != 1)
|
||||
throw RuntimeError("Failed to parse {} ID from: {}", map[i].s, path);
|
||||
|
||||
fclose(f);
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
// Get slot id
|
||||
ret = sscanf(e->d_name, "%4x:%2x:%2x.%u", &slot.domain, &slot.bus, &slot.device, &slot.function);
|
||||
if (ret != 4)
|
||||
throw RuntimeError("Failed to parse PCI slot number: {}", e->d_name);
|
||||
// Get slot id
|
||||
ret = sscanf(e->d_name, "%4x:%2x:%2x.%u", &slot.domain, &slot.bus,
|
||||
&slot.device, &slot.function);
|
||||
if (ret != 4)
|
||||
throw RuntimeError("Failed to parse PCI slot number: {}", e->d_name);
|
||||
|
||||
emplace_back(std::make_shared<Device>(id, slot));
|
||||
}
|
||||
emplace_back(std::make_shared<Device>(id, slot));
|
||||
}
|
||||
|
||||
closedir(dp);
|
||||
closedir(dp);
|
||||
}
|
||||
|
||||
DeviceList::value_type DeviceList::lookupDevice(const Slot &s)
|
||||
{
|
||||
return *std::find_if(begin(), end(), [s](const DeviceList::value_type &d)
|
||||
{ return d->slot == s; });
|
||||
DeviceList::value_type DeviceList::lookupDevice(const Slot &s) {
|
||||
return *std::find_if(begin(), end(), [s](const DeviceList::value_type &d) {
|
||||
return d->slot == s;
|
||||
});
|
||||
}
|
||||
|
||||
DeviceList::value_type DeviceList::lookupDevice(const Id &i)
|
||||
{
|
||||
return *std::find_if(begin(), end(), [i](const DeviceList::value_type &d)
|
||||
{ return d->id == i; });
|
||||
DeviceList::value_type DeviceList::lookupDevice(const Id &i) {
|
||||
return *std::find_if(begin(), end(), [i](const DeviceList::value_type &d) {
|
||||
return d->id == i;
|
||||
});
|
||||
}
|
||||
|
||||
DeviceList::value_type DeviceList::lookupDevice(const Device &d)
|
||||
{
|
||||
auto dev = std::find_if(begin(), end(), [d](const DeviceList::value_type &e)
|
||||
{ return *e == d; });
|
||||
DeviceList::value_type DeviceList::lookupDevice(const Device &d) {
|
||||
auto dev = std::find_if(
|
||||
begin(), end(), [d](const DeviceList::value_type &e) { return *e == d; });
|
||||
|
||||
return dev == end() ? value_type() : *dev;
|
||||
return dev == end() ? value_type() : *dev;
|
||||
}
|
||||
|
||||
Id::Id(const std::string &str) :
|
||||
vendor(0),
|
||||
device(0),
|
||||
class_code(0)
|
||||
{
|
||||
char *s, *c, *e;
|
||||
char *tmp = strdup(str.c_str());
|
||||
Id::Id(const std::string &str) : vendor(0), device(0), class_code(0) {
|
||||
char *s, *c, *e;
|
||||
char *tmp = strdup(str.c_str());
|
||||
|
||||
if (!*tmp)
|
||||
return;
|
||||
if (!*tmp)
|
||||
return;
|
||||
|
||||
s = strchr(tmp, ':');
|
||||
if (!s) {
|
||||
free(tmp);
|
||||
throw RuntimeError("Failed to parse PCI id: ':' expected", str);
|
||||
}
|
||||
s = strchr(tmp, ':');
|
||||
if (!s) {
|
||||
free(tmp);
|
||||
throw RuntimeError("Failed to parse PCI id: ':' expected", str);
|
||||
}
|
||||
|
||||
*s++ = 0;
|
||||
if (tmp[0] && strcmp(tmp, "*")) {
|
||||
long int x = strtol(tmp, &e, 16);
|
||||
*s++ = 0;
|
||||
if (tmp[0] && strcmp(tmp, "*")) {
|
||||
long int x = strtol(tmp, &e, 16);
|
||||
|
||||
if ((e && *e) || (x < 0 || x > 0xffff)) {
|
||||
free(tmp);
|
||||
throw RuntimeError("Failed to parse PCI id: {}: Invalid vendor id", str);
|
||||
}
|
||||
if ((e && *e) || (x < 0 || x > 0xffff)) {
|
||||
free(tmp);
|
||||
throw RuntimeError("Failed to parse PCI id: {}: Invalid vendor id", str);
|
||||
}
|
||||
|
||||
vendor = x;
|
||||
}
|
||||
vendor = x;
|
||||
}
|
||||
|
||||
c = strchr(s, ':');
|
||||
if (c)
|
||||
*c++ = 0;
|
||||
c = strchr(s, ':');
|
||||
if (c)
|
||||
*c++ = 0;
|
||||
|
||||
if (s[0] && strcmp(s, "*")) {
|
||||
long int x = strtol(s, &e, 16);
|
||||
if ((e && *e) || (x < 0 || x > 0xffff)) {
|
||||
free(tmp);
|
||||
throw RuntimeError("Failed to parse PCI id: {}: Invalid device id", str);
|
||||
}
|
||||
if (s[0] && strcmp(s, "*")) {
|
||||
long int x = strtol(s, &e, 16);
|
||||
if ((e && *e) || (x < 0 || x > 0xffff)) {
|
||||
free(tmp);
|
||||
throw RuntimeError("Failed to parse PCI id: {}: Invalid device id", str);
|
||||
}
|
||||
|
||||
device = x;
|
||||
}
|
||||
device = x;
|
||||
}
|
||||
|
||||
if (c && c[0] && strcmp(s, "*")) {
|
||||
long int x = strtol(c, &e, 16);
|
||||
if (c && c[0] && strcmp(s, "*")) {
|
||||
long int x = strtol(c, &e, 16);
|
||||
|
||||
if ((e && *e) || (x < 0 || x > 0xffff)) {
|
||||
free(tmp);
|
||||
throw RuntimeError("Failed to parse PCI id: {}: Invalid class code", str);
|
||||
}
|
||||
if ((e && *e) || (x < 0 || x > 0xffff)) {
|
||||
free(tmp);
|
||||
throw RuntimeError("Failed to parse PCI id: {}: Invalid class code", str);
|
||||
}
|
||||
|
||||
class_code = x;
|
||||
}
|
||||
class_code = x;
|
||||
}
|
||||
}
|
||||
|
||||
bool Id::operator==(const Id &i)
|
||||
{
|
||||
if ((i.device != 0 && i.device != device) ||
|
||||
(i.vendor != 0 && i.vendor != vendor))
|
||||
return false;
|
||||
bool Id::operator==(const Id &i) {
|
||||
if ((i.device != 0 && i.device != device) ||
|
||||
(i.vendor != 0 && i.vendor != vendor))
|
||||
return false;
|
||||
|
||||
if ((i.class_code != 0) || (i.class_code != class_code))
|
||||
return false;
|
||||
if ((i.class_code != 0) || (i.class_code != class_code))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
Slot::Slot(const std::string &str) :
|
||||
domain(0),
|
||||
bus(0),
|
||||
device(0),
|
||||
function(0)
|
||||
{
|
||||
char *tmp = strdup(str.c_str());
|
||||
char *colon = strrchr(tmp, ':');
|
||||
char *dot = strchr((colon ? colon + 1 : tmp), '.');
|
||||
char *mid = tmp;
|
||||
char *e, *buss, *colon2;
|
||||
Slot::Slot(const std::string &str) : domain(0), bus(0), device(0), function(0) {
|
||||
char *tmp = strdup(str.c_str());
|
||||
char *colon = strrchr(tmp, ':');
|
||||
char *dot = strchr((colon ? colon + 1 : tmp), '.');
|
||||
char *mid = tmp;
|
||||
char *e, *buss, *colon2;
|
||||
|
||||
if (colon) {
|
||||
*colon++ = 0;
|
||||
mid = colon;
|
||||
if (colon) {
|
||||
*colon++ = 0;
|
||||
mid = colon;
|
||||
|
||||
colon2 = strchr(tmp, ':');
|
||||
if (colon2) {
|
||||
*colon2++ = 0;
|
||||
buss = colon2;
|
||||
colon2 = strchr(tmp, ':');
|
||||
if (colon2) {
|
||||
*colon2++ = 0;
|
||||
buss = colon2;
|
||||
|
||||
if (tmp[0] && strcmp(tmp, "*")) {
|
||||
long int x = strtol(tmp, &e, 16);
|
||||
if ((e && *e) || (x < 0 || x > 0x7fffffff)) {
|
||||
free(tmp);
|
||||
throw RuntimeError("Failed to parse PCI slot: {}: invalid domain", str);
|
||||
}
|
||||
if (tmp[0] && strcmp(tmp, "*")) {
|
||||
long int x = strtol(tmp, &e, 16);
|
||||
if ((e && *e) || (x < 0 || x > 0x7fffffff)) {
|
||||
free(tmp);
|
||||
throw RuntimeError("Failed to parse PCI slot: {}: invalid domain",
|
||||
str);
|
||||
}
|
||||
|
||||
domain = x;
|
||||
}
|
||||
}
|
||||
else
|
||||
buss = tmp;
|
||||
domain = x;
|
||||
}
|
||||
} else
|
||||
buss = tmp;
|
||||
|
||||
if (buss[0] && strcmp(buss, "*")) {
|
||||
long int x = strtol(buss, &e, 16);
|
||||
if ((e && *e) || (x < 0 || x > 0xff)) {
|
||||
free(tmp);
|
||||
throw RuntimeError("Failed to parse PCI slot: {}: invalid bus", str);
|
||||
}
|
||||
if (buss[0] && strcmp(buss, "*")) {
|
||||
long int x = strtol(buss, &e, 16);
|
||||
if ((e && *e) || (x < 0 || x > 0xff)) {
|
||||
free(tmp);
|
||||
throw RuntimeError("Failed to parse PCI slot: {}: invalid bus", str);
|
||||
}
|
||||
|
||||
bus = x;
|
||||
}
|
||||
}
|
||||
bus = x;
|
||||
}
|
||||
}
|
||||
|
||||
if (dot)
|
||||
*dot++ = 0;
|
||||
if (dot)
|
||||
*dot++ = 0;
|
||||
|
||||
if (mid[0] && strcmp(mid, "*")) {
|
||||
long int x = strtol(mid, &e, 16);
|
||||
if (mid[0] && strcmp(mid, "*")) {
|
||||
long int x = strtol(mid, &e, 16);
|
||||
|
||||
if ((e && *e) || (x < 0 || x > 0x1f)) {
|
||||
free(tmp);
|
||||
throw RuntimeError("Failed to parse PCI slot: {}: invalid slot", str);
|
||||
}
|
||||
if ((e && *e) || (x < 0 || x > 0x1f)) {
|
||||
free(tmp);
|
||||
throw RuntimeError("Failed to parse PCI slot: {}: invalid slot", str);
|
||||
}
|
||||
|
||||
device = x;
|
||||
}
|
||||
device = x;
|
||||
}
|
||||
|
||||
if (dot && dot[0] && strcmp(dot, "*")) {
|
||||
long int x = strtol(dot, &e, 16);
|
||||
if (dot && dot[0] && strcmp(dot, "*")) {
|
||||
long int x = strtol(dot, &e, 16);
|
||||
|
||||
if ((e && *e) || (x < 0 || x > 7)) {
|
||||
free(tmp);
|
||||
throw RuntimeError("Failed to parse PCI slot: {}: invalid function", str);
|
||||
}
|
||||
if ((e && *e) || (x < 0 || x > 7)) {
|
||||
free(tmp);
|
||||
throw RuntimeError("Failed to parse PCI slot: {}: invalid function", str);
|
||||
}
|
||||
|
||||
function = x;
|
||||
}
|
||||
function = x;
|
||||
}
|
||||
|
||||
free(tmp);
|
||||
free(tmp);
|
||||
}
|
||||
|
||||
bool Slot::operator==(const Slot &s)
|
||||
{
|
||||
if ((s.domain != 0 && s.domain != domain) ||
|
||||
(s.bus != 0 && s.bus != bus) ||
|
||||
(s.device != 0 && s.device != device) ||
|
||||
(s.function != 0 && s.function != function))
|
||||
return false;
|
||||
bool Slot::operator==(const Slot &s) {
|
||||
if ((s.domain != 0 && s.domain != domain) || (s.bus != 0 && s.bus != bus) ||
|
||||
(s.device != 0 && s.device != device) ||
|
||||
(s.function != 0 && s.function != function))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Device::operator==(const Device &f)
|
||||
{
|
||||
return id == f.id && slot == f.slot;
|
||||
bool Device::operator==(const Device &f) {
|
||||
return id == f.id && slot == f.slot;
|
||||
}
|
||||
|
||||
std::list<Region> Device::getRegions() const
|
||||
{
|
||||
FILE *f;
|
||||
char sysfs[1024];
|
||||
std::list<Region> Device::getRegions() const {
|
||||
FILE *f;
|
||||
char sysfs[1024];
|
||||
|
||||
snprintf(sysfs, sizeof(sysfs), "%s/bus/pci/devices/%04x:%02x:%02x.%x/resource",
|
||||
SYSFS_PATH, slot.domain, slot.bus, slot.device, slot.function);
|
||||
snprintf(sysfs, sizeof(sysfs),
|
||||
"%s/bus/pci/devices/%04x:%02x:%02x.%x/resource", SYSFS_PATH,
|
||||
slot.domain, slot.bus, slot.device, slot.function);
|
||||
|
||||
f = fopen(sysfs, "r");
|
||||
if (!f)
|
||||
throw SystemError("Failed to open resource mapping {}", sysfs);
|
||||
f = fopen(sysfs, "r");
|
||||
if (!f)
|
||||
throw SystemError("Failed to open resource mapping {}", sysfs);
|
||||
|
||||
std::list<Region> regions;
|
||||
std::list<Region> regions;
|
||||
|
||||
ssize_t bytesRead;
|
||||
char *line = nullptr;
|
||||
size_t len = 0;
|
||||
ssize_t bytesRead;
|
||||
char *line = nullptr;
|
||||
size_t len = 0;
|
||||
|
||||
int reg_num = 0;
|
||||
int reg_num = 0;
|
||||
|
||||
// Cap to 8 regions, just because we don't know how many may exist.
|
||||
while (reg_num < 8 && (bytesRead = getline(&line, &len, f)) != -1) {
|
||||
unsigned long long tokens[3];
|
||||
char *s = line;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
char *end;
|
||||
tokens[i] = strtoull(s, &end, 16);
|
||||
if (s == end) {
|
||||
log->debug("Error parsing line {} of {}", reg_num + 1, sysfs);
|
||||
tokens[0] = tokens[1] = 0; // Mark invalid
|
||||
break;
|
||||
}
|
||||
s = end;
|
||||
}
|
||||
// Cap to 8 regions, just because we don't know how many may exist.
|
||||
while (reg_num < 8 && (bytesRead = getline(&line, &len, f)) != -1) {
|
||||
unsigned long long tokens[3];
|
||||
char *s = line;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
char *end;
|
||||
tokens[i] = strtoull(s, &end, 16);
|
||||
if (s == end) {
|
||||
log->debug("Error parsing line {} of {}", reg_num + 1, sysfs);
|
||||
tokens[0] = tokens[1] = 0; // Mark invalid
|
||||
break;
|
||||
}
|
||||
s = end;
|
||||
}
|
||||
|
||||
free(line);
|
||||
free(line);
|
||||
|
||||
// Required for getline() to allocate a new buffer on the next iteration.
|
||||
line = nullptr;
|
||||
len = 0;
|
||||
// Required for getline() to allocate a new buffer on the next iteration.
|
||||
line = nullptr;
|
||||
len = 0;
|
||||
|
||||
if (tokens[0] != tokens[1]) { // This is a valid region
|
||||
Region region;
|
||||
if (tokens[0] != tokens[1]) { // This is a valid region
|
||||
Region region;
|
||||
|
||||
region.num = reg_num;
|
||||
region.start = tokens[0];
|
||||
region.end = tokens[1];
|
||||
region.flags = tokens[2];
|
||||
region.num = reg_num;
|
||||
region.start = tokens[0];
|
||||
region.end = tokens[1];
|
||||
region.flags = tokens[2];
|
||||
|
||||
regions.push_back(region);
|
||||
}
|
||||
regions.push_back(region);
|
||||
}
|
||||
|
||||
reg_num++;
|
||||
}
|
||||
reg_num++;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
fclose(f);
|
||||
|
||||
return regions;
|
||||
return regions;
|
||||
}
|
||||
|
||||
std::string Device::getDriver() const
|
||||
{
|
||||
int ret;
|
||||
char sysfs[1024], syml[1024];
|
||||
memset(syml, 0, sizeof(syml));
|
||||
std::string Device::getDriver() const {
|
||||
int ret;
|
||||
char sysfs[1024], syml[1024];
|
||||
memset(syml, 0, sizeof(syml));
|
||||
|
||||
snprintf(sysfs, sizeof(sysfs), "%s/bus/pci/devices/%04x:%02x:%02x.%x/driver", SYSFS_PATH,
|
||||
slot.domain, slot.bus, slot.device, slot.function);
|
||||
snprintf(sysfs, sizeof(sysfs), "%s/bus/pci/devices/%04x:%02x:%02x.%x/driver",
|
||||
SYSFS_PATH, slot.domain, slot.bus, slot.device, slot.function);
|
||||
|
||||
struct stat st;
|
||||
ret = stat(sysfs, &st);
|
||||
if (ret)
|
||||
return "";
|
||||
struct stat st;
|
||||
ret = stat(sysfs, &st);
|
||||
if (ret)
|
||||
return "";
|
||||
|
||||
ret = readlink(sysfs, syml, sizeof(syml));
|
||||
if (ret < 0)
|
||||
throw SystemError("Failed to follow link: {}", sysfs);
|
||||
ret = readlink(sysfs, syml, sizeof(syml));
|
||||
if (ret < 0)
|
||||
throw SystemError("Failed to follow link: {}", sysfs);
|
||||
|
||||
return basename(syml);
|
||||
return basename(syml);
|
||||
}
|
||||
|
||||
bool Device::attachDriver(const std::string &driver) const
|
||||
{
|
||||
FILE *f;
|
||||
char fn[1024];
|
||||
bool Device::attachDriver(const std::string &driver) const {
|
||||
FILE *f;
|
||||
char fn[1024];
|
||||
|
||||
// Add new ID to driver
|
||||
snprintf(fn, sizeof(fn), "%s/bus/pci/drivers/%s/new_id", SYSFS_PATH, driver.c_str());
|
||||
f = fopen(fn, "w");
|
||||
if (!f)
|
||||
throw SystemError("Failed to add PCI id to {} driver ({})", driver, fn);
|
||||
// Add new ID to driver
|
||||
snprintf(fn, sizeof(fn), "%s/bus/pci/drivers/%s/new_id", SYSFS_PATH,
|
||||
driver.c_str());
|
||||
f = fopen(fn, "w");
|
||||
if (!f)
|
||||
throw SystemError("Failed to add PCI id to {} driver ({})", driver, fn);
|
||||
|
||||
log->info("Adding ID to {} module: {:04x} {:04x}", driver, id.vendor, id.device);
|
||||
fprintf(f, "%04x %04x", id.vendor, id.device);
|
||||
fclose(f);
|
||||
log->info("Adding ID to {} module: {:04x} {:04x}", driver, id.vendor,
|
||||
id.device);
|
||||
fprintf(f, "%04x %04x", id.vendor, id.device);
|
||||
fclose(f);
|
||||
|
||||
// Bind to driver
|
||||
snprintf(fn, sizeof(fn), "%s/bus/pci/drivers/%s/bind", SYSFS_PATH, driver.c_str());
|
||||
f = fopen(fn, "w");
|
||||
if (!f)
|
||||
throw SystemError("Failed to bind PCI device to {} driver ({})", driver, fn);
|
||||
// Bind to driver
|
||||
snprintf(fn, sizeof(fn), "%s/bus/pci/drivers/%s/bind", SYSFS_PATH,
|
||||
driver.c_str());
|
||||
f = fopen(fn, "w");
|
||||
if (!f)
|
||||
throw SystemError("Failed to bind PCI device to {} driver ({})", driver,
|
||||
fn);
|
||||
|
||||
log->info("Bind device to {} driver", driver);
|
||||
fprintf(f, "%04x:%02x:%02x.%x\n", slot.domain, slot.bus, slot.device, slot.function);
|
||||
fclose(f);
|
||||
log->info("Bind device to {} driver", driver);
|
||||
fprintf(f, "%04x:%02x:%02x.%x\n", slot.domain, slot.bus, slot.device,
|
||||
slot.function);
|
||||
fclose(f);
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t Device::readHostBar(unsigned barNum) const
|
||||
{
|
||||
auto file = openSysFs("resource", std::ios_base::in);
|
||||
uint32_t Device::readHostBar(unsigned barNum) const {
|
||||
auto file = openSysFs("resource", std::ios_base::in);
|
||||
|
||||
std::string line;
|
||||
std::string line;
|
||||
|
||||
unsigned i;
|
||||
for (i = 0; i <= barNum && !file.eof(); i++)
|
||||
std::getline(file, line);
|
||||
unsigned i;
|
||||
for (i = 0; i <= barNum && !file.eof(); i++)
|
||||
std::getline(file, line);
|
||||
|
||||
if (i != barNum && file.eof())
|
||||
throw RuntimeError("Failed to read resource file");
|
||||
if (i != barNum && file.eof())
|
||||
throw RuntimeError("Failed to read resource file");
|
||||
|
||||
unsigned long long start, end, flags;
|
||||
if (std::sscanf(line.c_str(), "%llx %llx %llx", &start, &end, &flags) != 3)
|
||||
throw SystemError("Failed to parse BAR line");
|
||||
unsigned long long start, end, flags;
|
||||
if (std::sscanf(line.c_str(), "%llx %llx %llx", &start, &end, &flags) != 3)
|
||||
throw SystemError("Failed to parse BAR line");
|
||||
|
||||
if (end > start)
|
||||
throw SystemError("Invalid BAR: start={}, end={}", start, end);
|
||||
if (end > start)
|
||||
throw SystemError("Invalid BAR: start={}, end={}", start, end);
|
||||
|
||||
log->debug("Host BAR: start={:#x}, end={:#x}, size={:#x}, flags={:#x}", start, end, end - start, flags);
|
||||
log->debug("Host BAR: start={:#x}, end={:#x}, size={:#x}, flags={:#x}", start,
|
||||
end, end - start, flags);
|
||||
|
||||
return start;
|
||||
return start;
|
||||
}
|
||||
|
||||
void Device::rewriteBar(unsigned barNum)
|
||||
{
|
||||
auto hostBar = readHostBar(barNum);
|
||||
auto configBar = readBar(barNum);
|
||||
void Device::rewriteBar(unsigned barNum) {
|
||||
auto hostBar = readHostBar(barNum);
|
||||
auto configBar = readBar(barNum);
|
||||
|
||||
log->debug("Host BAR: {:#x}, configbar: {:#x}", hostBar, configBar);
|
||||
log->debug("Host BAR: {:#x}, configbar: {:#x}", hostBar, configBar);
|
||||
|
||||
if (hostBar == configBar) {
|
||||
log->debug("BAR is already correct");
|
||||
return;
|
||||
}
|
||||
if (hostBar == configBar) {
|
||||
log->debug("BAR is already correct");
|
||||
return;
|
||||
}
|
||||
|
||||
log->debug("BAR is incorrect, rewriting");
|
||||
log->debug("BAR is incorrect, rewriting");
|
||||
|
||||
writeBar(hostBar, barNum);
|
||||
writeBar(hostBar, barNum);
|
||||
}
|
||||
|
||||
uint32_t Device::readBar(unsigned barNum) const
|
||||
{
|
||||
uint32_t addr;
|
||||
auto file = openSysFs("config", std::ios_base::in);
|
||||
uint32_t Device::readBar(unsigned barNum) const {
|
||||
uint32_t addr;
|
||||
auto file = openSysFs("config", std::ios_base::in);
|
||||
|
||||
file.seekg(PCI_BASE_ADDRESS_N(barNum));
|
||||
file.read(reinterpret_cast<char *>(&addr), sizeof(addr));
|
||||
file.seekg(PCI_BASE_ADDRESS_N(barNum));
|
||||
file.read(reinterpret_cast<char *>(&addr), sizeof(addr));
|
||||
|
||||
return addr;
|
||||
return addr;
|
||||
}
|
||||
|
||||
void Device::writeBar(uint32_t addr, unsigned barNum)
|
||||
{
|
||||
auto file = openSysFs("config", std::ios_base::out);
|
||||
void Device::writeBar(uint32_t addr, unsigned barNum) {
|
||||
auto file = openSysFs("config", std::ios_base::out);
|
||||
|
||||
file.seekp(PCI_BASE_ADDRESS_N(barNum));
|
||||
file.write(reinterpret_cast<char *>(&addr), sizeof(addr));
|
||||
file.seekp(PCI_BASE_ADDRESS_N(barNum));
|
||||
file.write(reinterpret_cast<char *>(&addr), sizeof(addr));
|
||||
}
|
||||
|
||||
int Device::getIommuGroup() const
|
||||
{
|
||||
int ret;
|
||||
char *group;
|
||||
int Device::getIommuGroup() const {
|
||||
int ret;
|
||||
char *group;
|
||||
|
||||
// readlink() does not add a null terminator!
|
||||
char link[1024] = {0};
|
||||
char sysfs[1024];
|
||||
// readlink() does not add a null terminator!
|
||||
char link[1024] = {0};
|
||||
char sysfs[1024];
|
||||
|
||||
snprintf(sysfs, sizeof(sysfs), "%s/bus/pci/devices/%04x:%02x:%02x.%x/iommu_group", SYSFS_PATH,
|
||||
slot.domain, slot.bus, slot.device, slot.function);
|
||||
snprintf(sysfs, sizeof(sysfs),
|
||||
"%s/bus/pci/devices/%04x:%02x:%02x.%x/iommu_group", SYSFS_PATH,
|
||||
slot.domain, slot.bus, slot.device, slot.function);
|
||||
|
||||
ret = readlink(sysfs, link, sizeof(link));
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
ret = readlink(sysfs, link, sizeof(link));
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
group = basename(link);
|
||||
group = basename(link);
|
||||
|
||||
return atoi(group);
|
||||
return atoi(group);
|
||||
}
|
||||
|
||||
std::fstream Device::openSysFs(const std::string &subPath, std::ios_base::openmode mode) const
|
||||
{
|
||||
std::fstream file;
|
||||
std::fstream Device::openSysFs(const std::string &subPath,
|
||||
std::ios_base::openmode mode) const {
|
||||
std::fstream file;
|
||||
|
||||
auto sysFsFilename = fmt::format("{}/bus/pci/devices/{:04x}:{:02x}:{:02x}.{:x}/{}", SYSFS_PATH,
|
||||
slot.domain, slot.bus, slot.device, slot.function, subPath);
|
||||
auto sysFsFilename =
|
||||
fmt::format("{}/bus/pci/devices/{:04x}:{:02x}:{:02x}.{:x}/{}", SYSFS_PATH,
|
||||
slot.domain, slot.bus, slot.device, slot.function, subPath);
|
||||
|
||||
file.exceptions(std::ifstream::failbit | std::ifstream::badbit);
|
||||
file.open(sysFsFilename, mode);
|
||||
file.exceptions(std::ifstream::failbit | std::ifstream::badbit);
|
||||
file.open(sysFsFilename, mode);
|
||||
|
||||
return file;
|
||||
return file;
|
||||
}
|
||||
|
|
|
@ -8,127 +8,132 @@
|
|||
#include <sched.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <villas/log.hpp>
|
||||
#include <villas/cpuset.hpp>
|
||||
#include <villas/config.hpp>
|
||||
#include <villas/utils.hpp>
|
||||
#include <villas/cpuset.hpp>
|
||||
#include <villas/exceptions.hpp>
|
||||
#include <villas/log.hpp>
|
||||
#include <villas/utils.hpp>
|
||||
|
||||
#include <villas/kernel/kernel.hpp>
|
||||
#include <villas/kernel/kernel.hpp>
|
||||
#include <villas/kernel/rt.hpp>
|
||||
|
||||
#ifdef __linux__
|
||||
using villas::utils::CpuSet;
|
||||
using villas::utils::CpuSet;
|
||||
#endif // __linux__
|
||||
|
||||
namespace villas {
|
||||
namespace kernel {
|
||||
namespace rt {
|
||||
|
||||
void init(int priority, int affinity)
|
||||
{
|
||||
Logger logger = logging.get("kernel:rt");
|
||||
void init(int priority, int affinity) {
|
||||
Logger logger = logging.get("kernel:rt");
|
||||
|
||||
logger->info("Initialize sub-system");
|
||||
logger->info("Initialize sub-system");
|
||||
|
||||
#ifdef __linux__
|
||||
int is_rt, is_isol;
|
||||
char isolcpus[255];
|
||||
int is_rt, is_isol;
|
||||
char isolcpus[255];
|
||||
|
||||
// Use FIFO scheduler with real time priority
|
||||
is_rt = isPreemptible();
|
||||
if (!is_rt)
|
||||
logger->warn("We recommend to use an PREEMPT_RT patched kernel!");
|
||||
// Use FIFO scheduler with real time priority
|
||||
is_rt = isPreemptible();
|
||||
if (!is_rt)
|
||||
logger->warn("We recommend to use an PREEMPT_RT patched kernel!");
|
||||
|
||||
if (priority)
|
||||
setPriority(priority);
|
||||
else
|
||||
logger->warn("You might want to use the 'priority' setting to increase " PROJECT_NAME "'s process priority");
|
||||
if (priority)
|
||||
setPriority(priority);
|
||||
else
|
||||
logger->warn(
|
||||
"You might want to use the 'priority' setting to increase " PROJECT_NAME
|
||||
"'s process priority");
|
||||
|
||||
if (affinity) {
|
||||
is_isol = getCmdlineParam("isolcpus", isolcpus, sizeof(isolcpus));
|
||||
if (is_isol)
|
||||
logger->warn("You should reserve some cores for " PROJECT_NAME " (see 'isolcpus')");
|
||||
else {
|
||||
CpuSet cset_pin(affinity);
|
||||
CpuSet cset_isol(isolcpus);
|
||||
CpuSet cset_non_isol = ~cset_isol & cset_pin;
|
||||
if (affinity) {
|
||||
is_isol = getCmdlineParam("isolcpus", isolcpus, sizeof(isolcpus));
|
||||
if (is_isol)
|
||||
logger->warn("You should reserve some cores for " PROJECT_NAME
|
||||
" (see 'isolcpus')");
|
||||
else {
|
||||
CpuSet cset_pin(affinity);
|
||||
CpuSet cset_isol(isolcpus);
|
||||
CpuSet cset_non_isol = ~cset_isol & cset_pin;
|
||||
|
||||
if (cset_non_isol.count() > 0)
|
||||
logger->warn("Affinity setting includes cores which are not isolated: affinity={}, isolcpus={}, non_isolated={}", (std::string) cset_pin, (std::string) cset_isol, (std::string) cset_non_isol);
|
||||
}
|
||||
if (cset_non_isol.count() > 0)
|
||||
logger->warn("Affinity setting includes cores which are not isolated: "
|
||||
"affinity={}, isolcpus={}, non_isolated={}",
|
||||
(std::string)cset_pin, (std::string)cset_isol,
|
||||
(std::string)cset_non_isol);
|
||||
}
|
||||
|
||||
setProcessAffinity(affinity);
|
||||
}
|
||||
else
|
||||
logger->warn("You might want to use the 'affinity' setting to pin " PROJECT_NAME " to dedicate CPU cores");
|
||||
setProcessAffinity(affinity);
|
||||
} else
|
||||
logger->warn(
|
||||
"You might want to use the 'affinity' setting to pin " PROJECT_NAME
|
||||
" to dedicate CPU cores");
|
||||
#else
|
||||
logger->warn("This platform is not optimized for real-time execution");
|
||||
logger->warn("This platform is not optimized for real-time execution");
|
||||
|
||||
(void) affinity;
|
||||
(void) priority;
|
||||
(void)affinity;
|
||||
(void)priority;
|
||||
#endif // __linux__
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
void setProcessAffinity(int affinity)
|
||||
{
|
||||
int ret;
|
||||
void setProcessAffinity(int affinity) {
|
||||
int ret;
|
||||
|
||||
assert(affinity != 0);
|
||||
assert(affinity != 0);
|
||||
|
||||
Logger logger = logging.get("kernel:rt");
|
||||
Logger logger = logging.get("kernel:rt");
|
||||
|
||||
// Pin threads to CPUs by setting the affinity
|
||||
CpuSet cset_pin(affinity);
|
||||
// Pin threads to CPUs by setting the affinity
|
||||
CpuSet cset_pin(affinity);
|
||||
|
||||
ret = sched_setaffinity(0, cset_pin.size(), cset_pin);
|
||||
if (ret)
|
||||
throw SystemError("Failed to set CPU affinity of process");
|
||||
ret = sched_setaffinity(0, cset_pin.size(), cset_pin);
|
||||
if (ret)
|
||||
throw SystemError("Failed to set CPU affinity of process");
|
||||
|
||||
logger->debug("Set affinity to {} {}", cset_pin.count() == 1 ? "core" : "cores", (std::string) cset_pin);
|
||||
logger->debug("Set affinity to {} {}",
|
||||
cset_pin.count() == 1 ? "core" : "cores",
|
||||
(std::string)cset_pin);
|
||||
}
|
||||
|
||||
void setThreadAffinity(pthread_t thread, int affinity)
|
||||
{
|
||||
int ret;
|
||||
void setThreadAffinity(pthread_t thread, int affinity) {
|
||||
int ret;
|
||||
|
||||
assert(affinity != 0);
|
||||
assert(affinity != 0);
|
||||
|
||||
Logger logger = logging.get("kernel:rt");
|
||||
Logger logger = logging.get("kernel:rt");
|
||||
|
||||
CpuSet cset_pin(affinity);
|
||||
CpuSet cset_pin(affinity);
|
||||
|
||||
ret = pthread_setaffinity_np(thread, cset_pin.size(), cset_pin);
|
||||
if (ret)
|
||||
throw SystemError("Failed to set CPU affinity of thread");
|
||||
ret = pthread_setaffinity_np(thread, cset_pin.size(), cset_pin);
|
||||
if (ret)
|
||||
throw SystemError("Failed to set CPU affinity of thread");
|
||||
|
||||
logger->debug("Set affinity of thread {} to {} {}", (long unsigned) thread, cset_pin.count() == 1 ? "core" : "cores", (std::string) cset_pin);
|
||||
logger->debug("Set affinity of thread {} to {} {}", (long unsigned)thread,
|
||||
cset_pin.count() == 1 ? "core" : "cores",
|
||||
(std::string)cset_pin);
|
||||
}
|
||||
|
||||
void setPriority(int priority)
|
||||
{
|
||||
int ret;
|
||||
struct sched_param param;
|
||||
param.sched_priority = priority;
|
||||
void setPriority(int priority) {
|
||||
int ret;
|
||||
struct sched_param param;
|
||||
param.sched_priority = priority;
|
||||
|
||||
Logger logger = logging.get("kernel:rt");
|
||||
Logger logger = logging.get("kernel:rt");
|
||||
|
||||
ret = sched_setscheduler(0, SCHED_FIFO, ¶m);
|
||||
if (ret)
|
||||
throw SystemError("Failed to set real time priority");
|
||||
ret = sched_setscheduler(0, SCHED_FIFO, ¶m);
|
||||
if (ret)
|
||||
throw SystemError("Failed to set real time priority");
|
||||
|
||||
logger->debug("Task priority set to {}", priority);
|
||||
logger->debug("Task priority set to {}", priority);
|
||||
}
|
||||
|
||||
bool isPreemptible()
|
||||
{
|
||||
return access(SYSFS_PATH "/kernel/realtime", R_OK) == 0;
|
||||
bool isPreemptible() {
|
||||
return access(SYSFS_PATH "/kernel/realtime", R_OK) == 0;
|
||||
}
|
||||
|
||||
#endif // __linux__
|
||||
|
||||
} // namespace villas
|
||||
} // namespace kernel
|
||||
} // namespace rt
|
||||
} // namespace kernel
|
||||
} // namespace villas
|
||||
|
|
|
@ -10,61 +10,62 @@
|
|||
#define _DEFAULT_SOURCE
|
||||
|
||||
#if defined(__arm__) || defined(__aarch64__)
|
||||
#define _LARGEFILE64_SOURCE 1
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
#define _LARGEFILE64_SOURCE 1
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <limits>
|
||||
#include <array>
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include <cstdint>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/eventfd.h>
|
||||
#include <linux/pci_regs.h>
|
||||
#include <sys/eventfd.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <villas/exceptions.hpp>
|
||||
#include <villas/log.hpp>
|
||||
#include <villas/kernel/vfio.hpp>
|
||||
#include <villas/kernel/vfio_container.hpp>
|
||||
#include <villas/kernel/kernel.hpp>
|
||||
#include <villas/kernel/vfio_container.hpp>
|
||||
#include <villas/log.hpp>
|
||||
|
||||
using namespace villas::kernel::vfio;
|
||||
|
||||
#ifndef VFIO_NOIOMMU_IOMMU
|
||||
#define VFIO_NOIOMMU_IOMMU 8
|
||||
#define VFIO_NOIOMMU_IOMMU 8
|
||||
#endif
|
||||
|
||||
static
|
||||
std::array<std::string, EXTENSION_SIZE> construct_vfio_extension_str() {
|
||||
std::array<std::string, EXTENSION_SIZE> ret;
|
||||
ret[VFIO_TYPE1_IOMMU] = "Type 1";
|
||||
ret[VFIO_SPAPR_TCE_IOMMU] = "SPAPR TCE";
|
||||
ret[VFIO_TYPE1v2_IOMMU] = "Type 1 v2";
|
||||
ret[VFIO_DMA_CC_IOMMU] = "DMA CC";
|
||||
ret[VFIO_EEH] = "EEH";
|
||||
ret[VFIO_TYPE1_NESTING_IOMMU] = "Type 1 Nesting";
|
||||
ret[VFIO_SPAPR_TCE_v2_IOMMU] = "SPAPR TCE v2";
|
||||
ret[VFIO_NOIOMMU_IOMMU] = "No IOMMU";
|
||||
static std::array<std::string, EXTENSION_SIZE> construct_vfio_extension_str() {
|
||||
std::array<std::string, EXTENSION_SIZE> ret;
|
||||
ret[VFIO_TYPE1_IOMMU] = "Type 1";
|
||||
ret[VFIO_SPAPR_TCE_IOMMU] = "SPAPR TCE";
|
||||
ret[VFIO_TYPE1v2_IOMMU] = "Type 1 v2";
|
||||
ret[VFIO_DMA_CC_IOMMU] = "DMA CC";
|
||||
ret[VFIO_EEH] = "EEH";
|
||||
ret[VFIO_TYPE1_NESTING_IOMMU] = "Type 1 Nesting";
|
||||
ret[VFIO_SPAPR_TCE_v2_IOMMU] = "SPAPR TCE v2";
|
||||
ret[VFIO_NOIOMMU_IOMMU] = "No IOMMU";
|
||||
// Backwards compatability with older kernels
|
||||
#ifdef VFIO_UNMAP_ALL
|
||||
ret[VFIO_UNMAP_ALL] = "Unmap all";
|
||||
ret[VFIO_UNMAP_ALL] = "Unmap all";
|
||||
#endif
|
||||
#ifdef VFIO_UPDATE_VADDR
|
||||
ret[VFIO_UPDATE_VADDR] = "Update vaddr";
|
||||
ret[VFIO_UPDATE_VADDR] = "Update vaddr";
|
||||
#endif
|
||||
return ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static
|
||||
std::array<std::string, EXTENSION_SIZE> VFIO_EXTENSION_STR = construct_vfio_extension_str();
|
||||
static std::array<std::string, EXTENSION_SIZE> VFIO_EXTENSION_STR =
|
||||
construct_vfio_extension_str();
|
||||
|
||||
Container::Container() :
|
||||
fd(-1),
|
||||
|
@ -82,231 +83,232 @@ Container::Container() :
|
|||
}
|
||||
}
|
||||
|
||||
// Create a VFIO Container
|
||||
fd = open(VFIO_DEV, O_RDWR);
|
||||
if (fd < 0)
|
||||
throw RuntimeError("Failed to open VFIO container");
|
||||
// Create a VFIO Container
|
||||
fd = open(VFIO_DEV, O_RDWR);
|
||||
if (fd < 0)
|
||||
throw RuntimeError("Failed to open VFIO container");
|
||||
|
||||
// Check VFIO API version
|
||||
version = ioctl(fd, VFIO_GET_API_VERSION);
|
||||
if (version < 0 || version != VFIO_API_VERSION)
|
||||
throw RuntimeError("Unknown API version: {}", version);
|
||||
// Check VFIO API version
|
||||
version = ioctl(fd, VFIO_GET_API_VERSION);
|
||||
if (version < 0 || version != VFIO_API_VERSION)
|
||||
throw RuntimeError("Unknown API version: {}", version);
|
||||
|
||||
// Check available VFIO extensions (IOMMU types)
|
||||
for (unsigned int i = VFIO_TYPE1_IOMMU; i < EXTENSION_SIZE; i++) {
|
||||
int ret = ioctl(fd, VFIO_CHECK_EXTENSION, i);
|
||||
// Check available VFIO extensions (IOMMU types)
|
||||
for (unsigned int i = VFIO_TYPE1_IOMMU; i < EXTENSION_SIZE; i++) {
|
||||
int ret = ioctl(fd, VFIO_CHECK_EXTENSION, i);
|
||||
|
||||
extensions[i] = ret != 0;
|
||||
extensions[i] = ret != 0;
|
||||
|
||||
log->debug("VFIO extension {} is {} ({})", i, extensions[i] ? "available" : "not available", VFIO_EXTENSION_STR[i]);
|
||||
}
|
||||
log->debug("VFIO extension {} is {} ({})", i,
|
||||
extensions[i] ? "available" : "not available",
|
||||
VFIO_EXTENSION_STR[i]);
|
||||
}
|
||||
|
||||
if (extensions[VFIO_TYPE1_IOMMU]) {
|
||||
log->debug("Using VFIO type {} ({})", VFIO_TYPE1_IOMMU, VFIO_EXTENSION_STR[VFIO_TYPE1_IOMMU]);
|
||||
hasIommu = true;
|
||||
} else if (extensions[VFIO_NOIOMMU_IOMMU]) {
|
||||
log->debug("Using VFIO type {} ({})", VFIO_NOIOMMU_IOMMU, VFIO_EXTENSION_STR[VFIO_NOIOMMU_IOMMU]);
|
||||
hasIommu = false;
|
||||
} else
|
||||
throw RuntimeError("No supported IOMMU type available");
|
||||
if (extensions[VFIO_TYPE1_IOMMU]) {
|
||||
log->debug("Using VFIO type {} ({})", VFIO_TYPE1_IOMMU,
|
||||
VFIO_EXTENSION_STR[VFIO_TYPE1_IOMMU]);
|
||||
hasIommu = true;
|
||||
} else if (extensions[VFIO_NOIOMMU_IOMMU]) {
|
||||
log->debug("Using VFIO type {} ({})", VFIO_NOIOMMU_IOMMU,
|
||||
VFIO_EXTENSION_STR[VFIO_NOIOMMU_IOMMU]);
|
||||
hasIommu = false;
|
||||
} else
|
||||
throw RuntimeError("No supported IOMMU type available");
|
||||
|
||||
log->debug("Version: {:#x}", version);
|
||||
log->debug("IOMMU: {}", hasIommu ? "yes" : "no");
|
||||
log->debug("Version: {:#x}", version);
|
||||
log->debug("IOMMU: {}", hasIommu ? "yes" : "no");
|
||||
}
|
||||
|
||||
Container::~Container()
|
||||
{
|
||||
// Release memory and close fds
|
||||
groups.clear();
|
||||
Container::~Container() {
|
||||
// Release memory and close fds
|
||||
groups.clear();
|
||||
|
||||
log->debug("Cleaning up container with fd {}", fd);
|
||||
log->debug("Cleaning up container with fd {}", fd);
|
||||
|
||||
// Close container
|
||||
int ret = close(fd);
|
||||
if (ret < 0)
|
||||
log->error("Error closing vfio container fd {}: {}", fd, ret);
|
||||
// Close container
|
||||
int ret = close(fd);
|
||||
if (ret < 0)
|
||||
log->error("Error closing vfio container fd {}: {}", fd, ret);
|
||||
}
|
||||
|
||||
void Container::attachGroup(std::shared_ptr<Group> group)
|
||||
{
|
||||
if (group->isAttachedToContainer())
|
||||
throw RuntimeError("Group is already attached to a container");
|
||||
void Container::attachGroup(std::shared_ptr<Group> group) {
|
||||
if (group->isAttachedToContainer())
|
||||
throw RuntimeError("Group is already attached to a container");
|
||||
|
||||
// Claim group ownership
|
||||
int ret = ioctl(group->getFileDescriptor(), VFIO_GROUP_SET_CONTAINER, &fd);
|
||||
if (ret < 0)
|
||||
throw SystemError("Failed to attach VFIO group {} to container fd {}",
|
||||
group->getIndex(), fd);
|
||||
// Claim group ownership
|
||||
int ret = ioctl(group->getFileDescriptor(), VFIO_GROUP_SET_CONTAINER, &fd);
|
||||
if (ret < 0)
|
||||
throw SystemError("Failed to attach VFIO group {} to container fd {}",
|
||||
group->getIndex(), fd);
|
||||
|
||||
// Set IOMMU type
|
||||
int iommu_type = isIommuEnabled() ? VFIO_TYPE1_IOMMU : VFIO_NOIOMMU_IOMMU;
|
||||
// Set IOMMU type
|
||||
int iommu_type = isIommuEnabled() ? VFIO_TYPE1_IOMMU : VFIO_NOIOMMU_IOMMU;
|
||||
|
||||
ret = ioctl(fd, VFIO_SET_IOMMU, iommu_type);
|
||||
if (ret < 0)
|
||||
throw SystemError("Failed to set IOMMU type of container");
|
||||
ret = ioctl(fd, VFIO_SET_IOMMU, iommu_type);
|
||||
if (ret < 0)
|
||||
throw SystemError("Failed to set IOMMU type of container");
|
||||
|
||||
group->setAttachedToContainer();
|
||||
group->setAttachedToContainer();
|
||||
|
||||
log->debug("Attached new group {} to VFIO container with fd {}", group->getIndex(), fd);
|
||||
log->debug("Attached new group {} to VFIO container with fd {}",
|
||||
group->getIndex(), fd);
|
||||
|
||||
// Push to our list
|
||||
groups.push_back(std::move(group));
|
||||
// Push to our list
|
||||
groups.push_back(std::move(group));
|
||||
}
|
||||
|
||||
std::shared_ptr<Group> Container::getOrAttachGroup(int index)
|
||||
{
|
||||
// Search if group with index already exists
|
||||
for (auto &group : groups) {
|
||||
if (group->getIndex() == index)
|
||||
return group;
|
||||
}
|
||||
std::shared_ptr<Group> Container::getOrAttachGroup(int index) {
|
||||
// Search if group with index already exists
|
||||
for (auto &group : groups) {
|
||||
if (group->getIndex() == index)
|
||||
return group;
|
||||
}
|
||||
|
||||
// Group not yet part of this container, so acquire ownership
|
||||
auto group = std::make_shared<Group>(index, isIommuEnabled());
|
||||
attachGroup(group);
|
||||
// Group not yet part of this container, so acquire ownership
|
||||
auto group = std::make_shared<Group>(index, isIommuEnabled());
|
||||
attachGroup(group);
|
||||
|
||||
return group;
|
||||
return group;
|
||||
}
|
||||
|
||||
void Container::dump()
|
||||
{
|
||||
log->info("File descriptor: {}", fd);
|
||||
log->info("Version: {}", version);
|
||||
void Container::dump() {
|
||||
log->info("File descriptor: {}", fd);
|
||||
log->info("Version: {}", version);
|
||||
|
||||
// Check available VFIO extensions (IOMMU types)
|
||||
for (size_t i = 0; i < extensions.size(); i++)
|
||||
log->debug("VFIO extension {} ({}) is {}", extensions[i], VFIO_EXTENSION_STR[i], extensions[i] ? "available" : "not available");
|
||||
// Check available VFIO extensions (IOMMU types)
|
||||
for (size_t i = 0; i < extensions.size(); i++)
|
||||
log->debug("VFIO extension {} ({}) is {}", extensions[i],
|
||||
VFIO_EXTENSION_STR[i],
|
||||
extensions[i] ? "available" : "not available");
|
||||
|
||||
for (auto &group : groups)
|
||||
group->dump();
|
||||
for (auto &group : groups)
|
||||
group->dump();
|
||||
}
|
||||
|
||||
std::shared_ptr<Device> Container::attachDevice(const std::string& name, int index)
|
||||
{
|
||||
auto group = getOrAttachGroup(index);
|
||||
auto device = group->attachDevice(name);
|
||||
std::shared_ptr<Device> Container::attachDevice(const std::string &name,
|
||||
int index) {
|
||||
auto group = getOrAttachGroup(index);
|
||||
auto device = group->attachDevice(name);
|
||||
|
||||
return device;
|
||||
return device;
|
||||
}
|
||||
|
||||
std::shared_ptr<Device> Container::attachDevice(pci::Device &pdev)
|
||||
{
|
||||
int ret;
|
||||
char name[32], iommu_state[4];
|
||||
static constexpr
|
||||
const char* kernelDriver = "vfio-pci";
|
||||
std::shared_ptr<Device> Container::attachDevice(pci::Device &pdev) {
|
||||
int ret;
|
||||
char name[32], iommu_state[4];
|
||||
static constexpr const char *kernelDriver = "vfio-pci";
|
||||
|
||||
// Load PCI bus driver for VFIO
|
||||
if (kernel::loadModule("vfio_pci"))
|
||||
throw RuntimeError("Failed to load kernel driver: vfio_pci");
|
||||
// Load PCI bus driver for VFIO
|
||||
if (kernel::loadModule("vfio_pci"))
|
||||
throw RuntimeError("Failed to load kernel driver: vfio_pci");
|
||||
|
||||
// Bind PCI card to vfio-pci driver if not already bound
|
||||
if (pdev.getDriver() != kernelDriver) {
|
||||
log->debug("Bind PCI card to kernel driver '{}'", kernelDriver);
|
||||
pdev.attachDriver(kernelDriver);
|
||||
}
|
||||
// Bind PCI card to vfio-pci driver if not already bound
|
||||
if (pdev.getDriver() != kernelDriver) {
|
||||
log->debug("Bind PCI card to kernel driver '{}'", kernelDriver);
|
||||
pdev.attachDriver(kernelDriver);
|
||||
}
|
||||
|
||||
try {
|
||||
pdev.rewriteBar();
|
||||
} catch (std::exception &e) {
|
||||
throw RuntimeError("BAR of device is in inconsistent state. Rewriting the BAR "
|
||||
"failed. Please remove, rescan and reset the device and try again.");
|
||||
}
|
||||
try {
|
||||
pdev.rewriteBar();
|
||||
} catch (std::exception &e) {
|
||||
throw RuntimeError(
|
||||
"BAR of device is in inconsistent state. Rewriting the BAR "
|
||||
"failed. Please remove, rescan and reset the device and try again.");
|
||||
}
|
||||
|
||||
// Get IOMMU group of device
|
||||
int index = isIommuEnabled() ? pdev.getIommuGroup() : 0;
|
||||
if (index < 0) {
|
||||
ret = kernel::getCmdlineParam("intel_iommu", iommu_state, sizeof(iommu_state));
|
||||
if (ret != 0 || strcmp("on", iommu_state) != 0)
|
||||
log->warn("Kernel booted without command line parameter "
|
||||
"'intel_iommu' set to 'on'. Please check documentation "
|
||||
"(https://villas.fein-aachen.org/doc/fpga-setup.html) "
|
||||
"for help with troubleshooting.");
|
||||
// Get IOMMU group of device
|
||||
int index = isIommuEnabled() ? pdev.getIommuGroup() : 0;
|
||||
if (index < 0) {
|
||||
ret = kernel::getCmdlineParam("intel_iommu", iommu_state,
|
||||
sizeof(iommu_state));
|
||||
if (ret != 0 || strcmp("on", iommu_state) != 0)
|
||||
log->warn("Kernel booted without command line parameter "
|
||||
"'intel_iommu' set to 'on'. Please check documentation "
|
||||
"(https://villas.fein-aachen.org/doc/fpga-setup.html) "
|
||||
"for help with troubleshooting.");
|
||||
|
||||
throw RuntimeError("Failed to get IOMMU group of device");
|
||||
}
|
||||
throw RuntimeError("Failed to get IOMMU group of device");
|
||||
}
|
||||
|
||||
// VFIO device name consists of PCI BDF
|
||||
snprintf(name, sizeof(name), "%04x:%02x:%02x.%x", pdev.slot.domain,
|
||||
pdev.slot.bus, pdev.slot.device, pdev.slot.function);
|
||||
// VFIO device name consists of PCI BDF
|
||||
snprintf(name, sizeof(name), "%04x:%02x:%02x.%x", pdev.slot.domain,
|
||||
pdev.slot.bus, pdev.slot.device, pdev.slot.function);
|
||||
|
||||
log->info("Attach to device {} with index {}", std::string(name), index);
|
||||
auto group = getOrAttachGroup(index);
|
||||
auto device = group->attachDevice(name, &pdev);
|
||||
log->info("Attach to device {} with index {}", std::string(name), index);
|
||||
auto group = getOrAttachGroup(index);
|
||||
auto device = group->attachDevice(name, &pdev);
|
||||
|
||||
// Check if this is really a vfio-pci device
|
||||
if (!device->isVfioPciDevice())
|
||||
throw RuntimeError("Device is not a vfio-pci device");
|
||||
// Check if this is really a vfio-pci device
|
||||
if (!device->isVfioPciDevice())
|
||||
throw RuntimeError("Device is not a vfio-pci device");
|
||||
|
||||
return device;
|
||||
return device;
|
||||
}
|
||||
|
||||
uintptr_t Container::memoryMap(uintptr_t virt, uintptr_t phys, size_t length)
|
||||
{
|
||||
int ret;
|
||||
uintptr_t Container::memoryMap(uintptr_t virt, uintptr_t phys, size_t length) {
|
||||
int ret;
|
||||
|
||||
if (not hasIommu) {
|
||||
log->error("DMA mapping not supported without IOMMU");
|
||||
return UINTPTR_MAX;
|
||||
}
|
||||
if (not hasIommu) {
|
||||
log->error("DMA mapping not supported without IOMMU");
|
||||
return UINTPTR_MAX;
|
||||
}
|
||||
|
||||
if (length & 0xFFF) {
|
||||
length += 0x1000;
|
||||
length &= ~0xFFF;
|
||||
}
|
||||
if (length & 0xFFF) {
|
||||
length += 0x1000;
|
||||
length &= ~0xFFF;
|
||||
}
|
||||
|
||||
// Super stupid allocator
|
||||
size_t iovaIncrement = 0;
|
||||
if (phys == UINTPTR_MAX) {
|
||||
phys = this->iova_next;
|
||||
iovaIncrement = length;
|
||||
}
|
||||
// Super stupid allocator
|
||||
size_t iovaIncrement = 0;
|
||||
if (phys == UINTPTR_MAX) {
|
||||
phys = this->iova_next;
|
||||
iovaIncrement = length;
|
||||
}
|
||||
|
||||
struct vfio_iommu_type1_dma_map dmaMap;
|
||||
memset(&dmaMap, 0, sizeof(dmaMap));
|
||||
struct vfio_iommu_type1_dma_map dmaMap;
|
||||
memset(&dmaMap, 0, sizeof(dmaMap));
|
||||
|
||||
dmaMap.argsz = sizeof(dmaMap);
|
||||
dmaMap.vaddr = virt;
|
||||
dmaMap.iova = phys;
|
||||
dmaMap.size = length;
|
||||
dmaMap.flags = VFIO_DMA_MAP_FLAG_READ | VFIO_DMA_MAP_FLAG_WRITE;
|
||||
dmaMap.argsz = sizeof(dmaMap);
|
||||
dmaMap.vaddr = virt;
|
||||
dmaMap.iova = phys;
|
||||
dmaMap.size = length;
|
||||
dmaMap.flags = VFIO_DMA_MAP_FLAG_READ | VFIO_DMA_MAP_FLAG_WRITE;
|
||||
|
||||
ret = ioctl(this->fd, VFIO_IOMMU_MAP_DMA, &dmaMap);
|
||||
if (ret) {
|
||||
log->error("Failed to create DMA mapping: {}", ret);
|
||||
return UINTPTR_MAX;
|
||||
}
|
||||
ret = ioctl(this->fd, VFIO_IOMMU_MAP_DMA, &dmaMap);
|
||||
if (ret) {
|
||||
log->error("Failed to create DMA mapping: {}", ret);
|
||||
return UINTPTR_MAX;
|
||||
}
|
||||
|
||||
log->debug("DMA map size={:#x}, iova={:#x}, vaddr={:#x}",
|
||||
dmaMap.size, dmaMap.iova, dmaMap.vaddr);
|
||||
log->debug("DMA map size={:#x}, iova={:#x}, vaddr={:#x}", dmaMap.size,
|
||||
dmaMap.iova, dmaMap.vaddr);
|
||||
|
||||
// Mapping successful, advance IOVA allocator
|
||||
this->iova_next += iovaIncrement;
|
||||
// Mapping successful, advance IOVA allocator
|
||||
this->iova_next += iovaIncrement;
|
||||
|
||||
// We intentionally don't return the actual mapped length, the users are
|
||||
// only guaranteed to have their demanded memory mapped correctly
|
||||
return dmaMap.iova;
|
||||
// We intentionally don't return the actual mapped length, the users are
|
||||
// only guaranteed to have their demanded memory mapped correctly
|
||||
return dmaMap.iova;
|
||||
}
|
||||
|
||||
bool Container::memoryUnmap(uintptr_t phys, size_t length)
|
||||
{
|
||||
int ret;
|
||||
bool Container::memoryUnmap(uintptr_t phys, size_t length) {
|
||||
int ret;
|
||||
|
||||
if (not hasIommu)
|
||||
return true;
|
||||
if (not hasIommu)
|
||||
return true;
|
||||
|
||||
struct vfio_iommu_type1_dma_unmap dmaUnmap;
|
||||
dmaUnmap.argsz = sizeof(struct vfio_iommu_type1_dma_unmap);
|
||||
dmaUnmap.flags = 0;
|
||||
dmaUnmap.iova = phys;
|
||||
dmaUnmap.size = length;
|
||||
struct vfio_iommu_type1_dma_unmap dmaUnmap;
|
||||
dmaUnmap.argsz = sizeof(struct vfio_iommu_type1_dma_unmap);
|
||||
dmaUnmap.flags = 0;
|
||||
dmaUnmap.iova = phys;
|
||||
dmaUnmap.size = length;
|
||||
|
||||
ret = ioctl(this->fd, VFIO_IOMMU_UNMAP_DMA, &dmaUnmap);
|
||||
if (ret) {
|
||||
log->error("Failed to unmap DMA mapping");
|
||||
return false;
|
||||
}
|
||||
log->debug("DMA unmap size={:#x}, iova={:#x}", dmaUnmap.size, dmaUnmap.iova);
|
||||
ret = ioctl(this->fd, VFIO_IOMMU_UNMAP_DMA, &dmaUnmap);
|
||||
if (ret) {
|
||||
log->error("Failed to unmap DMA mapping");
|
||||
return false;
|
||||
}
|
||||
log->debug("DMA unmap size={:#x}, iova={:#x}", dmaUnmap.size, dmaUnmap.iova);
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -12,77 +12,68 @@
|
|||
#define _DEFAULT_SOURCE
|
||||
|
||||
#if defined(__arm__) || defined(__aarch64__)
|
||||
#define _LARGEFILE64_SOURCE 1
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
#define _LARGEFILE64_SOURCE 1
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/eventfd.h>
|
||||
#include <linux/pci_regs.h>
|
||||
#include <sys/eventfd.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <villas/exceptions.hpp>
|
||||
#include <villas/kernel/vfio_device.hpp>
|
||||
|
||||
using namespace villas::kernel::vfio;
|
||||
|
||||
static
|
||||
const char *vfio_pci_region_names[] = {
|
||||
"PCI_BAR0", // VFIO_PCI_BAR0_REGION_INDEX
|
||||
"PCI_BAR1", // VFIO_PCI_BAR1_REGION_INDEX
|
||||
"PCI_BAR2", // VFIO_PCI_BAR2_REGION_INDEX
|
||||
"PCI_BAR3", // VFIO_PCI_BAR3_REGION_INDEX
|
||||
"PCI_BAR4", // VFIO_PCI_BAR4_REGION_INDEX
|
||||
"PCI_BAR5", // VFIO_PCI_BAR5_REGION_INDEX
|
||||
"PCI_ROM", // VFIO_PCI_ROM_REGION_INDEX
|
||||
"PCI_CONFIG", // VFIO_PCI_CONFIG_REGION_INDEX
|
||||
"PCI_VGA" // VFIO_PCI_INTX_IRQ_INDEX
|
||||
static const char *vfio_pci_region_names[] = {
|
||||
"PCI_BAR0", // VFIO_PCI_BAR0_REGION_INDEX
|
||||
"PCI_BAR1", // VFIO_PCI_BAR1_REGION_INDEX
|
||||
"PCI_BAR2", // VFIO_PCI_BAR2_REGION_INDEX
|
||||
"PCI_BAR3", // VFIO_PCI_BAR3_REGION_INDEX
|
||||
"PCI_BAR4", // VFIO_PCI_BAR4_REGION_INDEX
|
||||
"PCI_BAR5", // VFIO_PCI_BAR5_REGION_INDEX
|
||||
"PCI_ROM", // VFIO_PCI_ROM_REGION_INDEX
|
||||
"PCI_CONFIG", // VFIO_PCI_CONFIG_REGION_INDEX
|
||||
"PCI_VGA" // VFIO_PCI_INTX_IRQ_INDEX
|
||||
};
|
||||
|
||||
static
|
||||
const char *vfio_pci_irq_names[] = {
|
||||
"PCI_INTX", // VFIO_PCI_INTX_IRQ_INDEX
|
||||
"PCI_MSI", // VFIO_PCI_MSI_IRQ_INDEX
|
||||
"PCI_MSIX", // VFIO_PCI_MSIX_IRQ_INDEX
|
||||
"PCI_ERR", // VFIO_PCI_ERR_IRQ_INDEX
|
||||
"PCI_REQ" // VFIO_PCI_REQ_IRQ_INDEX
|
||||
static const char *vfio_pci_irq_names[] = {
|
||||
"PCI_INTX", // VFIO_PCI_INTX_IRQ_INDEX
|
||||
"PCI_MSI", // VFIO_PCI_MSI_IRQ_INDEX
|
||||
"PCI_MSIX", // VFIO_PCI_MSIX_IRQ_INDEX
|
||||
"PCI_ERR", // VFIO_PCI_ERR_IRQ_INDEX
|
||||
"PCI_REQ" // VFIO_PCI_REQ_IRQ_INDEX
|
||||
};
|
||||
|
||||
Device::Device(const std::string &name, int groupFileDescriptor, const kernel::pci::Device *pci_device) :
|
||||
name(name),
|
||||
fd(-1),
|
||||
attachedToGroup(false),
|
||||
groupFd(groupFileDescriptor),
|
||||
info(),
|
||||
irqs(),
|
||||
regions(),
|
||||
mappings(),
|
||||
pci_device(pci_device),
|
||||
log(logging.get("kernel:vfio::Device"))
|
||||
{
|
||||
if (groupFileDescriptor < 0)
|
||||
throw RuntimeError("Invalid group file descriptor");
|
||||
Device::Device(const std::string &name, int groupFileDescriptor,
|
||||
const kernel::pci::Device *pci_device)
|
||||
: name(name), fd(-1), attachedToGroup(false), groupFd(groupFileDescriptor),
|
||||
info(), irqs(), regions(), mappings(), pci_device(pci_device),
|
||||
log(logging.get("kernel:vfio::Device")) {
|
||||
if (groupFileDescriptor < 0)
|
||||
throw RuntimeError("Invalid group file descriptor");
|
||||
|
||||
// Open device fd
|
||||
fd = ioctl(groupFileDescriptor, VFIO_GROUP_GET_DEVICE_FD, name.c_str());
|
||||
if (fd < 0)
|
||||
throw RuntimeError("Failed to open VFIO device: {}", name.c_str());
|
||||
// Open device fd
|
||||
fd = ioctl(groupFileDescriptor, VFIO_GROUP_GET_DEVICE_FD, name.c_str());
|
||||
if (fd < 0)
|
||||
throw RuntimeError("Failed to open VFIO device: {}", name.c_str());
|
||||
|
||||
// Get device info
|
||||
info.argsz = sizeof(info);
|
||||
// Get device info
|
||||
info.argsz = sizeof(info);
|
||||
|
||||
int ret = ioctl(fd, VFIO_DEVICE_GET_INFO, &info);
|
||||
if (ret < 0)
|
||||
throw RuntimeError("Failed to get VFIO device info for: {}", name);
|
||||
int ret = ioctl(fd, VFIO_DEVICE_GET_INFO, &info);
|
||||
if (ret < 0)
|
||||
throw RuntimeError("Failed to get VFIO device info for: {}", name);
|
||||
|
||||
log->debug("device info: flags: 0x{:x}, num_regions: {}, num_irqs: {}",
|
||||
info.flags, info.num_regions, info.num_irqs);
|
||||
log->debug("device info: flags: 0x{:x}, num_regions: {}, num_irqs: {}",
|
||||
info.flags, info.num_regions, info.num_irqs);
|
||||
|
||||
if (pci_device != 0) {
|
||||
// device_info.num_region reports always 9 and includes a VGA region, which is only supported on
|
||||
|
@ -91,372 +82,348 @@ Device::Device(const std::string &name, int groupFileDescriptor, const kernel::p
|
|||
info.num_regions = VFIO_PCI_CONFIG_REGION_INDEX + 1;
|
||||
} else { info.num_regions = 1; }
|
||||
|
||||
// Reserve slots already so that we can use the []-operator for access
|
||||
irqs.resize(info.num_irqs);
|
||||
regions.resize(info.num_regions);
|
||||
mappings.resize(info.num_regions);
|
||||
// Reserve slots already so that we can use the []-operator for access
|
||||
irqs.resize(info.num_irqs);
|
||||
regions.resize(info.num_regions);
|
||||
mappings.resize(info.num_regions);
|
||||
|
||||
// Get device regions
|
||||
for (size_t i = 0; i < info.num_regions; i++) {
|
||||
struct vfio_region_info region;
|
||||
memset(®ion, 0, sizeof (region));
|
||||
|
||||
region.argsz = sizeof(region);
|
||||
region.index = i;
|
||||
region.argsz = sizeof(region);
|
||||
region.index = i;
|
||||
|
||||
ret = ioctl(fd, VFIO_DEVICE_GET_REGION_INFO, ®ion);
|
||||
if (ret < 0)
|
||||
throw RuntimeError("Failed to get region {} of VFIO device: {}", i, name);
|
||||
ret = ioctl(fd, VFIO_DEVICE_GET_REGION_INFO, ®ion);
|
||||
if (ret < 0)
|
||||
throw RuntimeError("Failed to get region {} of VFIO device: {}", i, name);
|
||||
|
||||
log->debug("region {} info: flags: 0x{:x}, cap_offset: 0x{:x}, size: 0x{:x}, offset: 0x{:x}",
|
||||
region.index, region.flags, region.cap_offset, region.size, region.offset);
|
||||
|
||||
regions[i] = region;
|
||||
}
|
||||
regions[i] = region;
|
||||
}
|
||||
|
||||
// Get device IRQs
|
||||
for (size_t i = 0; i < info.num_irqs; i++) {
|
||||
struct vfio_irq_info irq;
|
||||
memset(&irq, 0, sizeof (irq));
|
||||
// Get device IRQs
|
||||
for (size_t i = 0; i < info.num_irqs; i++) {
|
||||
struct vfio_irq_info irq;
|
||||
memset(&irq, 0, sizeof(irq));
|
||||
|
||||
irq.argsz = sizeof(irq);
|
||||
irq.index = i;
|
||||
irq.argsz = sizeof(irq);
|
||||
irq.index = i;
|
||||
|
||||
ret = ioctl(fd, VFIO_DEVICE_GET_IRQ_INFO, &irq);
|
||||
if (ret < 0)
|
||||
throw RuntimeError("Failed to get IRQ {} of VFIO device: {}", i, name);
|
||||
ret = ioctl(fd, VFIO_DEVICE_GET_IRQ_INFO, &irq);
|
||||
if (ret < 0)
|
||||
throw RuntimeError("Failed to get IRQ {} of VFIO device: {}", i, name);
|
||||
|
||||
log->debug("irq {} info: flags: 0x{:x}, count: {}",
|
||||
irq.index, irq.flags, irq.count);
|
||||
|
||||
irqs[i] = irq;
|
||||
}
|
||||
log->debug("irq {} info: flags: 0x{:x}, count: {}", irq.index, irq.flags,
|
||||
irq.count);
|
||||
|
||||
irqs[i] = irq;
|
||||
}
|
||||
}
|
||||
|
||||
Device::~Device()
|
||||
{
|
||||
log->debug("Cleaning up device {} with fd {}", this->name, this->fd);
|
||||
Device::~Device() {
|
||||
log->debug("Cleaning up device {} with fd {}", this->name, this->fd);
|
||||
|
||||
for (auto ®ion : regions) {
|
||||
regionUnmap(region.index);
|
||||
}
|
||||
if (isVfioPciDevice()) {
|
||||
pciHotReset();
|
||||
}
|
||||
reset();
|
||||
for (auto ®ion : regions) {
|
||||
regionUnmap(region.index);
|
||||
}
|
||||
if (isVfioPciDevice()) {
|
||||
pciHotReset();
|
||||
}
|
||||
reset();
|
||||
|
||||
int ret = close(fd);
|
||||
if (ret != 0) {
|
||||
log->error("Closing device fd {} failed", fd);
|
||||
}
|
||||
int ret = close(fd);
|
||||
if (ret != 0) {
|
||||
log->error("Closing device fd {} failed", fd);
|
||||
}
|
||||
}
|
||||
|
||||
bool Device::reset()
|
||||
{
|
||||
log->debug("Resetting device.");
|
||||
if (this->info.flags & VFIO_DEVICE_FLAGS_RESET)
|
||||
return ioctl(this->fd, VFIO_DEVICE_RESET) == 0;
|
||||
else
|
||||
return false; // Not supported by this device
|
||||
bool Device::reset() {
|
||||
log->debug("Resetting device.");
|
||||
if (this->info.flags & VFIO_DEVICE_FLAGS_RESET)
|
||||
return ioctl(this->fd, VFIO_DEVICE_RESET) == 0;
|
||||
else
|
||||
return false; // Not supported by this device
|
||||
}
|
||||
|
||||
void * Device::regionMap(size_t index)
|
||||
{
|
||||
struct vfio_region_info *r = ®ions[index];
|
||||
void *Device::regionMap(size_t index) {
|
||||
struct vfio_region_info *r = ®ions[index];
|
||||
|
||||
if (!(r->flags & VFIO_REGION_INFO_FLAG_MMAP))
|
||||
return MAP_FAILED;
|
||||
if (!(r->flags & VFIO_REGION_INFO_FLAG_MMAP))
|
||||
return MAP_FAILED;
|
||||
|
||||
int flags = MAP_SHARED;
|
||||
int flags = MAP_SHARED;
|
||||
|
||||
#if !(defined(__arm__) || defined(__aarch64__))
|
||||
flags |= MAP_SHARED | MAP_32BIT;
|
||||
flags |= MAP_SHARED | MAP_32BIT;
|
||||
#endif
|
||||
|
||||
log->debug("Mapping region {} of size 0x{:x} with flags 0x{:x}", index, r->size, flags);
|
||||
mappings[index] = mmap(nullptr, r->size,
|
||||
PROT_READ | PROT_WRITE,
|
||||
flags, fd, r->offset);
|
||||
log->debug("Mapping region {} of size 0x{:x} with flags 0x{:x}", index,
|
||||
r->size, flags);
|
||||
mappings[index] =
|
||||
mmap(nullptr, r->size, PROT_READ | PROT_WRITE, flags, fd, r->offset);
|
||||
|
||||
return mappings[index];
|
||||
return mappings[index];
|
||||
}
|
||||
|
||||
bool Device::regionUnmap(size_t index)
|
||||
{
|
||||
int ret;
|
||||
struct vfio_region_info *r = ®ions[index];
|
||||
bool Device::regionUnmap(size_t index) {
|
||||
int ret;
|
||||
struct vfio_region_info *r = ®ions[index];
|
||||
|
||||
if (!mappings[index])
|
||||
return false; // Was not mapped
|
||||
if (!mappings[index])
|
||||
return false; // Was not mapped
|
||||
|
||||
log->debug("Unmap region {} from device {}", index, name);
|
||||
log->debug("Unmap region {} from device {}", index, name);
|
||||
|
||||
ret = munmap(mappings[index], r->size);
|
||||
if (ret)
|
||||
return false;
|
||||
ret = munmap(mappings[index], r->size);
|
||||
if (ret)
|
||||
return false;
|
||||
|
||||
mappings[index] = nullptr;
|
||||
mappings[index] = nullptr;
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t Device::regionGetSize(size_t index)
|
||||
{
|
||||
if (index >= regions.size()) {
|
||||
log->error("Index out of range: {} >= {}", index, regions.size());
|
||||
throw std::out_of_range("Index out of range");
|
||||
}
|
||||
size_t Device::regionGetSize(size_t index) {
|
||||
if (index >= regions.size()) {
|
||||
log->error("Index out of range: {} >= {}", index, regions.size());
|
||||
throw std::out_of_range("Index out of range");
|
||||
}
|
||||
|
||||
return regions[index].size;
|
||||
return regions[index].size;
|
||||
}
|
||||
|
||||
void Device::dump()
|
||||
{
|
||||
log->info("Device {}: regions={}, irqs={}, flags={}",
|
||||
name,
|
||||
info.num_regions,
|
||||
info.num_irqs,
|
||||
info.flags
|
||||
);
|
||||
void Device::dump() {
|
||||
log->info("Device {}: regions={}, irqs={}, flags={}", name, info.num_regions,
|
||||
info.num_irqs, info.flags);
|
||||
|
||||
for (size_t i = 0; i < info.num_regions && i < 8; i++) {
|
||||
struct vfio_region_info *region = ®ions[i];
|
||||
for (size_t i = 0; i < info.num_regions && i < 8; i++) {
|
||||
struct vfio_region_info *region = ®ions[i];
|
||||
|
||||
if (region->size > 0) {
|
||||
log->info("Region {} {}: size={}, offset={}, flags={}",
|
||||
region->index,
|
||||
(info.flags & VFIO_DEVICE_FLAGS_PCI) ?
|
||||
vfio_pci_region_names[i] : "",
|
||||
region->size,
|
||||
region->offset,
|
||||
region->flags
|
||||
);
|
||||
}
|
||||
}
|
||||
if (region->size > 0) {
|
||||
log->info("Region {} {}: size={}, offset={}, flags={}", region->index,
|
||||
(info.flags & VFIO_DEVICE_FLAGS_PCI) ? vfio_pci_region_names[i]
|
||||
: "",
|
||||
region->size, region->offset, region->flags);
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < info.num_irqs; i++) {
|
||||
struct vfio_irq_info *irq = &irqs[i];
|
||||
for (size_t i = 0; i < info.num_irqs; i++) {
|
||||
struct vfio_irq_info *irq = &irqs[i];
|
||||
|
||||
if (irq->count > 0) {
|
||||
log->info("IRQ {} {}: count={}, flags={}",
|
||||
irq->index,
|
||||
(info.flags & VFIO_DEVICE_FLAGS_PCI ) ?
|
||||
vfio_pci_irq_names[i] : "",
|
||||
irq->count,
|
||||
irq->flags
|
||||
);
|
||||
}
|
||||
}
|
||||
if (irq->count > 0) {
|
||||
log->info("IRQ {} {}: count={}, flags={}", irq->index,
|
||||
(info.flags & VFIO_DEVICE_FLAGS_PCI) ? vfio_pci_irq_names[i]
|
||||
: "",
|
||||
irq->count, irq->flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Device::pciEnable()
|
||||
{
|
||||
int ret;
|
||||
uint32_t reg;
|
||||
const off64_t offset = PCI_COMMAND +
|
||||
(static_cast<off64_t>(VFIO_PCI_CONFIG_REGION_INDEX) << 40);
|
||||
bool Device::pciEnable() {
|
||||
int ret;
|
||||
uint32_t reg;
|
||||
const off64_t offset =
|
||||
PCI_COMMAND + (static_cast<off64_t>(VFIO_PCI_CONFIG_REGION_INDEX) << 40);
|
||||
|
||||
// Check if this is really a vfio-pci device
|
||||
if (!(this->info.flags & VFIO_DEVICE_FLAGS_PCI))
|
||||
return false;
|
||||
// Check if this is really a vfio-pci device
|
||||
if (!(this->info.flags & VFIO_DEVICE_FLAGS_PCI))
|
||||
return false;
|
||||
|
||||
ret = pread64(this->fd, ®, sizeof(reg), offset);
|
||||
if (ret != sizeof(reg))
|
||||
return false;
|
||||
ret = pread64(this->fd, ®, sizeof(reg), offset);
|
||||
if (ret != sizeof(reg))
|
||||
return false;
|
||||
|
||||
// Enable memory access and PCI bus mastering which is required for DMA
|
||||
reg |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
|
||||
// Enable memory access and PCI bus mastering which is required for DMA
|
||||
reg |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
|
||||
|
||||
ret = pwrite64(this->fd, ®, sizeof(reg), offset);
|
||||
if (ret != sizeof(reg))
|
||||
return false;
|
||||
ret = pwrite64(this->fd, ®, sizeof(reg), offset);
|
||||
if (ret != sizeof(reg))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
int Device::pciMsiInit(int efds[])
|
||||
{
|
||||
// Check if this is really a vfio-pci device
|
||||
if (not isVfioPciDevice())
|
||||
return -1;
|
||||
int Device::pciMsiInit(int efds[]) {
|
||||
// Check if this is really a vfio-pci device
|
||||
if (not isVfioPciDevice())
|
||||
return -1;
|
||||
|
||||
const size_t irqCount = irqs[VFIO_PCI_MSI_IRQ_INDEX].count;
|
||||
const size_t irqSetSize = sizeof(struct vfio_irq_set) +
|
||||
sizeof(int) * irqCount;
|
||||
const size_t irqCount = irqs[VFIO_PCI_MSI_IRQ_INDEX].count;
|
||||
const size_t irqSetSize =
|
||||
sizeof(struct vfio_irq_set) + sizeof(int) * irqCount;
|
||||
|
||||
auto *irqSetBuf = new char[irqSetSize];
|
||||
if (!irqSetBuf)
|
||||
throw MemoryAllocationError();
|
||||
auto *irqSetBuf = new char[irqSetSize];
|
||||
if (!irqSetBuf)
|
||||
throw MemoryAllocationError();
|
||||
|
||||
auto *irqSet = reinterpret_cast<struct vfio_irq_set*>(irqSetBuf);
|
||||
auto *irqSet = reinterpret_cast<struct vfio_irq_set *>(irqSetBuf);
|
||||
|
||||
irqSet->argsz = irqSetSize;
|
||||
// DATA_EVENTFD binds the interrupt to the provided eventfd.
|
||||
// SET_ACTION_TRIGGER enables kernel->userspace signalling.
|
||||
irqSet->flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER;
|
||||
irqSet->index = VFIO_PCI_MSI_IRQ_INDEX;
|
||||
irqSet->start = 0;
|
||||
irqSet->count = irqCount;
|
||||
irqSet->argsz = irqSetSize;
|
||||
// DATA_EVENTFD binds the interrupt to the provided eventfd.
|
||||
// SET_ACTION_TRIGGER enables kernel->userspace signalling.
|
||||
irqSet->flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER;
|
||||
irqSet->index = VFIO_PCI_MSI_IRQ_INDEX;
|
||||
irqSet->start = 0;
|
||||
irqSet->count = irqCount;
|
||||
|
||||
// Now set the new eventfds
|
||||
for (size_t i = 0; i < irqCount; i++) {
|
||||
efds[i] = eventfd(0, 0);
|
||||
if (efds[i] < 0) {
|
||||
delete[] irqSetBuf;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
// Now set the new eventfds
|
||||
for (size_t i = 0; i < irqCount; i++) {
|
||||
efds[i] = eventfd(0, 0);
|
||||
if (efds[i] < 0) {
|
||||
delete[] irqSetBuf;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(irqSet->data, efds, sizeof(int) * irqCount);
|
||||
memcpy(irqSet->data, efds, sizeof(int) * irqCount);
|
||||
|
||||
if (ioctl(fd, VFIO_DEVICE_SET_IRQS, irqSet) != 0) {
|
||||
delete[] irqSetBuf;
|
||||
return -1;
|
||||
}
|
||||
if (ioctl(fd, VFIO_DEVICE_SET_IRQS, irqSet) != 0) {
|
||||
delete[] irqSetBuf;
|
||||
return -1;
|
||||
}
|
||||
|
||||
delete[] irqSetBuf;
|
||||
delete[] irqSetBuf;
|
||||
|
||||
return irqCount;
|
||||
return irqCount;
|
||||
}
|
||||
|
||||
int Device::pciMsiDeinit(int efds[])
|
||||
{
|
||||
// Check if this is really a vfio-pci device
|
||||
if (not isVfioPciDevice())
|
||||
return -1;
|
||||
int Device::pciMsiDeinit(int efds[]) {
|
||||
// Check if this is really a vfio-pci device
|
||||
if (not isVfioPciDevice())
|
||||
return -1;
|
||||
|
||||
const size_t irqCount = irqs[VFIO_PCI_MSI_IRQ_INDEX].count;
|
||||
const size_t irqSetSize = sizeof(struct vfio_irq_set) +
|
||||
sizeof(int) * irqCount;
|
||||
const size_t irqCount = irqs[VFIO_PCI_MSI_IRQ_INDEX].count;
|
||||
const size_t irqSetSize =
|
||||
sizeof(struct vfio_irq_set) + sizeof(int) * irqCount;
|
||||
|
||||
auto *irqSetBuf = new char[irqSetSize];
|
||||
if (!irqSetBuf)
|
||||
throw MemoryAllocationError();
|
||||
auto *irqSetBuf = new char[irqSetSize];
|
||||
if (!irqSetBuf)
|
||||
throw MemoryAllocationError();
|
||||
|
||||
auto *irqSet = reinterpret_cast<struct vfio_irq_set*>(irqSetBuf);
|
||||
auto *irqSet = reinterpret_cast<struct vfio_irq_set *>(irqSetBuf);
|
||||
|
||||
irqSet->argsz = irqSetSize;
|
||||
irqSet->flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER;
|
||||
irqSet->index = VFIO_PCI_MSI_IRQ_INDEX;
|
||||
irqSet->count = irqCount;
|
||||
irqSet->start = 0;
|
||||
irqSet->argsz = irqSetSize;
|
||||
irqSet->flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER;
|
||||
irqSet->index = VFIO_PCI_MSI_IRQ_INDEX;
|
||||
irqSet->count = irqCount;
|
||||
irqSet->start = 0;
|
||||
|
||||
for (size_t i = 0; i < irqCount; i++) {
|
||||
close(efds[i]);
|
||||
efds[i] = -1;
|
||||
}
|
||||
for (size_t i = 0; i < irqCount; i++) {
|
||||
close(efds[i]);
|
||||
efds[i] = -1;
|
||||
}
|
||||
|
||||
memcpy(irqSet->data, efds, sizeof(int) * irqCount);
|
||||
memcpy(irqSet->data, efds, sizeof(int) * irqCount);
|
||||
|
||||
if (ioctl(fd, VFIO_DEVICE_SET_IRQS, irqSet) != 0) {
|
||||
delete[] irqSetBuf;
|
||||
return -1;
|
||||
}
|
||||
if (ioctl(fd, VFIO_DEVICE_SET_IRQS, irqSet) != 0) {
|
||||
delete[] irqSetBuf;
|
||||
return -1;
|
||||
}
|
||||
|
||||
delete[] irqSetBuf;
|
||||
delete[] irqSetBuf;
|
||||
|
||||
return irqCount;
|
||||
return irqCount;
|
||||
}
|
||||
|
||||
bool Device::pciMsiFind(int nos[])
|
||||
{
|
||||
int ret, idx, irq;
|
||||
char *end, *col, *last, line[1024], name[13];
|
||||
FILE *f;
|
||||
bool Device::pciMsiFind(int nos[]) {
|
||||
int ret, idx, irq;
|
||||
char *end, *col, *last, line[1024], name[13];
|
||||
FILE *f;
|
||||
|
||||
f = fopen("/proc/interrupts", "r");
|
||||
if (!f)
|
||||
return false;
|
||||
f = fopen("/proc/interrupts", "r");
|
||||
if (!f)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < 32; i++)
|
||||
nos[i] = -1;
|
||||
for (int i = 0; i < 32; i++)
|
||||
nos[i] = -1;
|
||||
|
||||
// For each line in /proc/interrupts
|
||||
while (fgets(line, sizeof(line), f)) {
|
||||
col = strtok(line, " ");
|
||||
// For each line in /proc/interrupts
|
||||
while (fgets(line, sizeof(line), f)) {
|
||||
col = strtok(line, " ");
|
||||
|
||||
// IRQ number is in first column
|
||||
irq = strtol(col, &end, 10);
|
||||
if (col == end)
|
||||
continue;
|
||||
// IRQ number is in first column
|
||||
irq = strtol(col, &end, 10);
|
||||
if (col == end)
|
||||
continue;
|
||||
|
||||
// Find last column of line
|
||||
do {
|
||||
last = col;
|
||||
} while ((col = strtok(nullptr, " ")));
|
||||
// Find last column of line
|
||||
do {
|
||||
last = col;
|
||||
} while ((col = strtok(nullptr, " ")));
|
||||
|
||||
ret = sscanf(last, "vfio-msi[%d](%12[0-9:])", &idx, name);
|
||||
if (ret == 2) {
|
||||
if (strstr(this->name.c_str(), name) == this->name.c_str())
|
||||
nos[idx] = irq;
|
||||
}
|
||||
}
|
||||
ret = sscanf(last, "vfio-msi[%d](%12[0-9:])", &idx, name);
|
||||
if (ret == 2) {
|
||||
if (strstr(this->name.c_str(), name) == this->name.c_str())
|
||||
nos[idx] = irq;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
fclose(f);
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Device::isVfioPciDevice() const
|
||||
{
|
||||
return info.flags & VFIO_DEVICE_FLAGS_PCI;
|
||||
bool Device::isVfioPciDevice() const {
|
||||
return info.flags & VFIO_DEVICE_FLAGS_PCI;
|
||||
}
|
||||
|
||||
bool Device::pciHotReset()
|
||||
{
|
||||
// Check if this is really a vfio-pci device
|
||||
if (!isVfioPciDevice())
|
||||
return false;
|
||||
bool Device::pciHotReset() {
|
||||
// Check if this is really a vfio-pci device
|
||||
if (!isVfioPciDevice())
|
||||
return false;
|
||||
|
||||
log->debug("Performing hot reset.");
|
||||
const size_t reset_info_len = sizeof(struct vfio_pci_hot_reset_info) +
|
||||
sizeof(struct vfio_pci_dependent_device) * 64;
|
||||
log->debug("Performing hot reset.");
|
||||
const size_t reset_info_len = sizeof(struct vfio_pci_hot_reset_info) +
|
||||
sizeof(struct vfio_pci_dependent_device) * 64;
|
||||
|
||||
auto *reset_info_buf = new char[reset_info_len];
|
||||
if (!reset_info_buf)
|
||||
throw MemoryAllocationError();
|
||||
auto *reset_info_buf = new char[reset_info_len];
|
||||
if (!reset_info_buf)
|
||||
throw MemoryAllocationError();
|
||||
|
||||
auto *reset_info = reinterpret_cast<struct vfio_pci_hot_reset_info *>(reset_info_buf);
|
||||
auto *reset_info =
|
||||
reinterpret_cast<struct vfio_pci_hot_reset_info *>(reset_info_buf);
|
||||
|
||||
reset_info->argsz = reset_info_len;
|
||||
reset_info->argsz = reset_info_len;
|
||||
|
||||
if (ioctl(fd, VFIO_DEVICE_GET_PCI_HOT_RESET_INFO, reset_info) != 0) {
|
||||
delete[] reset_info_buf;
|
||||
return false;
|
||||
}
|
||||
if (ioctl(fd, VFIO_DEVICE_GET_PCI_HOT_RESET_INFO, reset_info) != 0) {
|
||||
delete[] reset_info_buf;
|
||||
return false;
|
||||
}
|
||||
|
||||
log->debug("Dependent devices for hot-reset:");
|
||||
for (size_t i = 0; i < reset_info->count; i++) {
|
||||
struct vfio_pci_dependent_device *dd = &reset_info->devices[i];
|
||||
log->debug(" {:04x}:{:02x}:{:02x}.{:01x}: iommu_group={}",
|
||||
dd->segment, dd->bus,
|
||||
PCI_SLOT(dd->devfn), PCI_FUNC(dd->devfn), dd->group_id);
|
||||
}
|
||||
log->debug("Dependent devices for hot-reset:");
|
||||
for (size_t i = 0; i < reset_info->count; i++) {
|
||||
struct vfio_pci_dependent_device *dd = &reset_info->devices[i];
|
||||
log->debug(" {:04x}:{:02x}:{:02x}.{:01x}: iommu_group={}", dd->segment,
|
||||
dd->bus, PCI_SLOT(dd->devfn), PCI_FUNC(dd->devfn), dd->group_id);
|
||||
}
|
||||
|
||||
delete[] reset_info_buf;
|
||||
delete[] reset_info_buf;
|
||||
|
||||
const size_t reset_len = sizeof(struct vfio_pci_hot_reset) +
|
||||
sizeof(int32_t) * 1;
|
||||
auto *reset_buf = new char[reset_len];
|
||||
if (!reset_buf)
|
||||
throw MemoryAllocationError();
|
||||
const size_t reset_len =
|
||||
sizeof(struct vfio_pci_hot_reset) + sizeof(int32_t) * 1;
|
||||
auto *reset_buf = new char[reset_len];
|
||||
if (!reset_buf)
|
||||
throw MemoryAllocationError();
|
||||
|
||||
auto *reset = reinterpret_cast<struct vfio_pci_hot_reset*>(reset_buf);
|
||||
auto *reset = reinterpret_cast<struct vfio_pci_hot_reset *>(reset_buf);
|
||||
|
||||
reset->argsz = reset_len;
|
||||
reset->count = 1;
|
||||
reset->group_fds[0] = groupFd;
|
||||
reset->argsz = reset_len;
|
||||
reset->count = 1;
|
||||
reset->group_fds[0] = groupFd;
|
||||
|
||||
int ret = ioctl(fd, VFIO_DEVICE_PCI_HOT_RESET, reset);
|
||||
const bool success = (ret == 0);
|
||||
int ret = ioctl(fd, VFIO_DEVICE_PCI_HOT_RESET, reset);
|
||||
const bool success = (ret == 0);
|
||||
|
||||
delete[] reset_buf;
|
||||
delete[] reset_buf;
|
||||
|
||||
if (!success) {
|
||||
log->warn("PCI hot reset failed, maybe no IOMMU available?");
|
||||
return true;
|
||||
}
|
||||
if (!success) {
|
||||
log->warn("PCI hot reset failed, maybe no IOMMU available?");
|
||||
return true;
|
||||
}
|
||||
|
||||
return success;
|
||||
return success;
|
||||
}
|
||||
|
|
|
@ -10,127 +10,114 @@
|
|||
#define _DEFAULT_SOURCE
|
||||
|
||||
#if defined(__arm__) || defined(__aarch64__)
|
||||
#define _LARGEFILE64_SOURCE 1
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
#define _LARGEFILE64_SOURCE 1
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include <cstdint>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/eventfd.h>
|
||||
#include <linux/pci_regs.h>
|
||||
#include <sys/eventfd.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <villas/exceptions.hpp>
|
||||
#include <villas/kernel/vfio_group.hpp>
|
||||
|
||||
using namespace villas::kernel::vfio;
|
||||
|
||||
Group::Group(int index, bool iommuEnabled) :
|
||||
fd(-1),
|
||||
index(index),
|
||||
attachedToContainer(false),
|
||||
status(),
|
||||
devices(),
|
||||
log(logging.get("kernel:vfio::Group"))
|
||||
{
|
||||
// Open group fd
|
||||
std::stringstream groupPath;
|
||||
groupPath << VFIO_PATH
|
||||
<< (iommuEnabled ? "" : "noiommu-")
|
||||
<< index;
|
||||
Group::Group(int index, bool iommuEnabled)
|
||||
: fd(-1), index(index), attachedToContainer(false), status(), devices(),
|
||||
log(logging.get("kernel:vfio::Group")) {
|
||||
// Open group fd
|
||||
std::stringstream groupPath;
|
||||
groupPath << VFIO_PATH << (iommuEnabled ? "" : "noiommu-") << index;
|
||||
|
||||
log->debug("path: {}", groupPath.str().c_str());
|
||||
fd = open(groupPath.str().c_str(), O_RDWR);
|
||||
if (fd < 0) {
|
||||
log->error("Failed to open VFIO group {}", index);
|
||||
throw RuntimeError("Failed to open VFIO group");
|
||||
}
|
||||
log->debug("path: {}", groupPath.str().c_str());
|
||||
fd = open(groupPath.str().c_str(), O_RDWR);
|
||||
if (fd < 0) {
|
||||
log->error("Failed to open VFIO group {}", index);
|
||||
throw RuntimeError("Failed to open VFIO group");
|
||||
}
|
||||
|
||||
log->debug("VFIO group {} (fd {}) has path {}",
|
||||
index, fd, groupPath.str());
|
||||
|
||||
checkStatus();
|
||||
log->debug("VFIO group {} (fd {}) has path {}", index, fd, groupPath.str());
|
||||
|
||||
checkStatus();
|
||||
}
|
||||
|
||||
std::shared_ptr<Device> Group::attachDevice(std::shared_ptr<Device> device)
|
||||
{
|
||||
if (device->isAttachedToGroup())
|
||||
throw RuntimeError("Device is already attached to a group");
|
||||
std::shared_ptr<Device> Group::attachDevice(std::shared_ptr<Device> device) {
|
||||
if (device->isAttachedToGroup())
|
||||
throw RuntimeError("Device is already attached to a group");
|
||||
|
||||
devices.push_back(device);
|
||||
devices.push_back(device);
|
||||
|
||||
device->setAttachedToGroup();
|
||||
device->setAttachedToGroup();
|
||||
|
||||
return device;
|
||||
return device;
|
||||
}
|
||||
|
||||
std::shared_ptr<Device> Group::attachDevice(const std::string& name, const kernel::pci::Device *pci_device)
|
||||
{
|
||||
auto device = std::make_shared<Device>(name, fd, pci_device);
|
||||
return attachDevice(device);
|
||||
std::shared_ptr<Device>
|
||||
Group::attachDevice(const std::string &name,
|
||||
const kernel::pci::Device *pci_device) {
|
||||
auto device = std::make_shared<Device>(name, fd, pci_device);
|
||||
return attachDevice(device);
|
||||
}
|
||||
|
||||
bool Group::checkStatus()
|
||||
{
|
||||
int ret;
|
||||
bool Group::checkStatus() {
|
||||
int ret;
|
||||
|
||||
// Check group viability and features
|
||||
status.argsz = sizeof(status);
|
||||
// Check group viability and features
|
||||
status.argsz = sizeof(status);
|
||||
|
||||
ret = ioctl(fd, VFIO_GROUP_GET_STATUS, &status);
|
||||
if (ret < 0) {
|
||||
log->error("Failed to get VFIO group status");
|
||||
return false;
|
||||
}
|
||||
ret = ioctl(fd, VFIO_GROUP_GET_STATUS, &status);
|
||||
if (ret < 0) {
|
||||
log->error("Failed to get VFIO group status");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(status.flags & VFIO_GROUP_FLAGS_VIABLE)) {
|
||||
log->error("VFIO group is not available: bind all devices to the VFIO driver!");
|
||||
return false;
|
||||
}
|
||||
log->debug("VFIO group is {} viable", index);
|
||||
return true;
|
||||
if (!(status.flags & VFIO_GROUP_FLAGS_VIABLE)) {
|
||||
log->error(
|
||||
"VFIO group is not available: bind all devices to the VFIO driver!");
|
||||
return false;
|
||||
}
|
||||
log->debug("VFIO group is {} viable", index);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Group::dump()
|
||||
{
|
||||
log->info("VFIO Group {}, viable={}, container={}",
|
||||
index,
|
||||
(status.flags & VFIO_GROUP_FLAGS_VIABLE) > 0,
|
||||
(status.flags & VFIO_GROUP_FLAGS_CONTAINER_SET) > 0
|
||||
);
|
||||
void Group::dump() {
|
||||
log->info("VFIO Group {}, viable={}, container={}", index,
|
||||
(status.flags & VFIO_GROUP_FLAGS_VIABLE) > 0,
|
||||
(status.flags & VFIO_GROUP_FLAGS_CONTAINER_SET) > 0);
|
||||
|
||||
for (auto& device : devices) {
|
||||
device->dump();
|
||||
}
|
||||
for (auto &device : devices) {
|
||||
device->dump();
|
||||
}
|
||||
}
|
||||
|
||||
Group::~Group()
|
||||
{
|
||||
// Release memory and close fds
|
||||
devices.clear();
|
||||
Group::~Group() {
|
||||
// Release memory and close fds
|
||||
devices.clear();
|
||||
|
||||
log->debug("Cleaning up group {} with fd {}", index, fd);
|
||||
log->debug("Cleaning up group {} with fd {}", index, fd);
|
||||
|
||||
if (fd < 0)
|
||||
log->debug("Destructing group that has not been attached");
|
||||
else {
|
||||
log->debug("unsetting group container");
|
||||
int ret = ioctl(fd, VFIO_GROUP_UNSET_CONTAINER);
|
||||
if (ret != 0)
|
||||
log->error("Cannot unset container for group fd {}", fd);
|
||||
if (fd < 0)
|
||||
log->debug("Destructing group that has not been attached");
|
||||
else {
|
||||
log->debug("unsetting group container");
|
||||
int ret = ioctl(fd, VFIO_GROUP_UNSET_CONTAINER);
|
||||
if (ret != 0)
|
||||
log->error("Cannot unset container for group fd {}", fd);
|
||||
|
||||
ret = close(fd);
|
||||
if (ret != 0)
|
||||
log->error("Cannot close group fd {}", fd);
|
||||
}
|
||||
ret = close(fd);
|
||||
if (ret != 0)
|
||||
log->error("Cannot close group fd {}", fd);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <array>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
@ -18,263 +18,248 @@
|
|||
|
||||
using namespace villas;
|
||||
|
||||
int villas::list_init(struct List *l)
|
||||
{
|
||||
pthread_mutex_init(&l->lock, nullptr);
|
||||
int villas::list_init(struct List *l) {
|
||||
pthread_mutex_init(&l->lock, nullptr);
|
||||
|
||||
l->length = 0;
|
||||
l->capacity = 0;
|
||||
l->array = nullptr;
|
||||
l->state = State::INITIALIZED;
|
||||
l->length = 0;
|
||||
l->capacity = 0;
|
||||
l->array = nullptr;
|
||||
l->state = State::INITIALIZED;
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int villas::list_destroy(struct List *l, dtor_cb_t destructor, bool release)
|
||||
{
|
||||
pthread_mutex_lock(&l->lock);
|
||||
int villas::list_destroy(struct List *l, dtor_cb_t destructor, bool release) {
|
||||
pthread_mutex_lock(&l->lock);
|
||||
|
||||
assert(l->state != State::DESTROYED);
|
||||
assert(l->state != State::DESTROYED);
|
||||
|
||||
for (size_t i = 0; i < list_length(l); i++) {
|
||||
void *e = list_at(l, i);
|
||||
for (size_t i = 0; i < list_length(l); i++) {
|
||||
void *e = list_at(l, i);
|
||||
|
||||
if (destructor)
|
||||
destructor(e);
|
||||
if (release)
|
||||
free(e);
|
||||
}
|
||||
if (destructor)
|
||||
destructor(e);
|
||||
if (release)
|
||||
free(e);
|
||||
}
|
||||
|
||||
free(l->array);
|
||||
free(l->array);
|
||||
|
||||
l->length = -1;
|
||||
l->capacity = 0;
|
||||
l->array = nullptr;
|
||||
l->state = State::DESTROYED;
|
||||
l->length = -1;
|
||||
l->capacity = 0;
|
||||
l->array = nullptr;
|
||||
l->state = State::DESTROYED;
|
||||
|
||||
pthread_mutex_unlock(&l->lock);
|
||||
pthread_mutex_destroy(&l->lock);
|
||||
pthread_mutex_unlock(&l->lock);
|
||||
pthread_mutex_destroy(&l->lock);
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void villas::list_push(struct List *l, void *p)
|
||||
{
|
||||
pthread_mutex_lock(&l->lock);
|
||||
void villas::list_push(struct List *l, void *p) {
|
||||
pthread_mutex_lock(&l->lock);
|
||||
|
||||
assert(l->state == State::INITIALIZED);
|
||||
assert(l->state == State::INITIALIZED);
|
||||
|
||||
// Resize array if out of capacity
|
||||
if (l->length >= l->capacity) {
|
||||
l->capacity += LIST_CHUNKSIZE;
|
||||
l->array = (void **) realloc(l->array, l->capacity * sizeof(void *));
|
||||
}
|
||||
// Resize array if out of capacity
|
||||
if (l->length >= l->capacity) {
|
||||
l->capacity += LIST_CHUNKSIZE;
|
||||
l->array = (void **)realloc(l->array, l->capacity * sizeof(void *));
|
||||
}
|
||||
|
||||
l->array[l->length] = p;
|
||||
l->length++;
|
||||
l->array[l->length] = p;
|
||||
l->length++;
|
||||
|
||||
pthread_mutex_unlock(&l->lock);
|
||||
pthread_mutex_unlock(&l->lock);
|
||||
}
|
||||
|
||||
void villas::list_clear(struct List *l)
|
||||
{
|
||||
pthread_mutex_lock(&l->lock);
|
||||
void villas::list_clear(struct List *l) {
|
||||
pthread_mutex_lock(&l->lock);
|
||||
|
||||
l->length = 0;
|
||||
l->length = 0;
|
||||
|
||||
pthread_mutex_unlock(&l->lock);
|
||||
pthread_mutex_unlock(&l->lock);
|
||||
}
|
||||
|
||||
int villas::list_remove(struct List *l, size_t idx)
|
||||
{
|
||||
pthread_mutex_lock(&l->lock);
|
||||
int villas::list_remove(struct List *l, size_t idx) {
|
||||
pthread_mutex_lock(&l->lock);
|
||||
|
||||
assert(l->state == State::INITIALIZED);
|
||||
assert(l->state == State::INITIALIZED);
|
||||
|
||||
if (idx >= l->length)
|
||||
return -1;
|
||||
if (idx >= l->length)
|
||||
return -1;
|
||||
|
||||
for (size_t i = idx; i < l->length - 1; i++)
|
||||
l->array[i] = l->array[i+1];
|
||||
for (size_t i = idx; i < l->length - 1; i++)
|
||||
l->array[i] = l->array[i + 1];
|
||||
|
||||
l->length--;
|
||||
l->length--;
|
||||
|
||||
pthread_mutex_unlock(&l->lock);
|
||||
pthread_mutex_unlock(&l->lock);
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int villas::list_insert(struct List *l, size_t idx, void *p)
|
||||
{
|
||||
size_t i;
|
||||
void *t, *o;
|
||||
int villas::list_insert(struct List *l, size_t idx, void *p) {
|
||||
size_t i;
|
||||
void *t, *o;
|
||||
|
||||
pthread_mutex_lock(&l->lock);
|
||||
pthread_mutex_lock(&l->lock);
|
||||
|
||||
assert(l->state == State::INITIALIZED);
|
||||
assert(l->state == State::INITIALIZED);
|
||||
|
||||
if (idx >= l->length)
|
||||
return -1;
|
||||
if (idx >= l->length)
|
||||
return -1;
|
||||
|
||||
// Resize array if out of capacity
|
||||
if (l->length + 1 > l->capacity) {
|
||||
l->capacity += LIST_CHUNKSIZE;
|
||||
l->array = (void **) realloc(l->array, l->capacity * sizeof(void *));
|
||||
}
|
||||
// Resize array if out of capacity
|
||||
if (l->length + 1 > l->capacity) {
|
||||
l->capacity += LIST_CHUNKSIZE;
|
||||
l->array = (void **)realloc(l->array, l->capacity * sizeof(void *));
|
||||
}
|
||||
|
||||
o = p;
|
||||
for (i = idx; i < l->length; i++) {
|
||||
t = l->array[i];
|
||||
l->array[i] = o;
|
||||
o = t;
|
||||
}
|
||||
o = p;
|
||||
for (i = idx; i < l->length; i++) {
|
||||
t = l->array[i];
|
||||
l->array[i] = o;
|
||||
o = t;
|
||||
}
|
||||
|
||||
l->array[l->length] = o;
|
||||
l->length++;
|
||||
l->array[l->length] = o;
|
||||
l->length++;
|
||||
|
||||
pthread_mutex_unlock(&l->lock);
|
||||
pthread_mutex_unlock(&l->lock);
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void villas::list_remove_all(struct List *l, void *p)
|
||||
{
|
||||
int removed = 0;
|
||||
void villas::list_remove_all(struct List *l, void *p) {
|
||||
int removed = 0;
|
||||
|
||||
pthread_mutex_lock(&l->lock);
|
||||
pthread_mutex_lock(&l->lock);
|
||||
|
||||
assert(l->state == State::INITIALIZED);
|
||||
assert(l->state == State::INITIALIZED);
|
||||
|
||||
for (size_t i = 0; i < list_length(l); i++) {
|
||||
if (list_at(l, i) == p)
|
||||
removed++;
|
||||
else
|
||||
l->array[i - removed] = list_at(l, i);
|
||||
}
|
||||
for (size_t i = 0; i < list_length(l); i++) {
|
||||
if (list_at(l, i) == p)
|
||||
removed++;
|
||||
else
|
||||
l->array[i - removed] = list_at(l, i);
|
||||
}
|
||||
|
||||
l->length -= removed;
|
||||
l->length -= removed;
|
||||
|
||||
pthread_mutex_unlock(&l->lock);
|
||||
pthread_mutex_unlock(&l->lock);
|
||||
}
|
||||
|
||||
int villas::list_contains(struct List *l, void *p)
|
||||
{
|
||||
return list_count(l, [](const void *a, const void *b) {
|
||||
return a == b ? 0 : 1;
|
||||
}, p);
|
||||
int villas::list_contains(struct List *l, void *p) {
|
||||
return list_count(
|
||||
l, [](const void *a, const void *b) { return a == b ? 0 : 1; }, p);
|
||||
}
|
||||
|
||||
int villas::list_count(struct List *l, cmp_cb_t cmp, void *ctx)
|
||||
{
|
||||
int c = 0;
|
||||
void *e;
|
||||
int villas::list_count(struct List *l, cmp_cb_t cmp, void *ctx) {
|
||||
int c = 0;
|
||||
void *e;
|
||||
|
||||
pthread_mutex_lock(&l->lock);
|
||||
pthread_mutex_lock(&l->lock);
|
||||
|
||||
assert(l->state == State::INITIALIZED);
|
||||
assert(l->state == State::INITIALIZED);
|
||||
|
||||
for (size_t i = 0; i < list_length(l); i++) {
|
||||
e = list_at(l, i);
|
||||
if (cmp(e, ctx) == 0)
|
||||
c++;
|
||||
}
|
||||
for (size_t i = 0; i < list_length(l); i++) {
|
||||
e = list_at(l, i);
|
||||
if (cmp(e, ctx) == 0)
|
||||
c++;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&l->lock);
|
||||
pthread_mutex_unlock(&l->lock);
|
||||
|
||||
return c;
|
||||
return c;
|
||||
}
|
||||
|
||||
void * villas::list_search(struct List *l, cmp_cb_t cmp, const void *ctx)
|
||||
{
|
||||
void *e;
|
||||
void *villas::list_search(struct List *l, cmp_cb_t cmp, const void *ctx) {
|
||||
void *e;
|
||||
|
||||
pthread_mutex_lock(&l->lock);
|
||||
pthread_mutex_lock(&l->lock);
|
||||
|
||||
assert(l->state == State::INITIALIZED);
|
||||
assert(l->state == State::INITIALIZED);
|
||||
|
||||
for (size_t i = 0; i < list_length(l); i++) {
|
||||
e = list_at(l, i);
|
||||
if (cmp(e, ctx) == 0)
|
||||
goto out;
|
||||
}
|
||||
for (size_t i = 0; i < list_length(l); i++) {
|
||||
e = list_at(l, i);
|
||||
if (cmp(e, ctx) == 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
e = nullptr; // Not found
|
||||
e = nullptr; // Not found
|
||||
|
||||
out: pthread_mutex_unlock(&l->lock);
|
||||
out:
|
||||
pthread_mutex_unlock(&l->lock);
|
||||
|
||||
return e;
|
||||
return e;
|
||||
}
|
||||
|
||||
void villas::list_sort(struct List *l, cmp_cb_t cmp)
|
||||
{
|
||||
pthread_mutex_lock(&l->lock);
|
||||
void villas::list_sort(struct List *l, cmp_cb_t cmp) {
|
||||
pthread_mutex_lock(&l->lock);
|
||||
|
||||
assert(l->state == State::INITIALIZED);
|
||||
assert(l->state == State::INITIALIZED);
|
||||
|
||||
auto array = std::vector<void *>(l->array, l->array + l->length);
|
||||
auto array = std::vector<void *>(l->array, l->array + l->length);
|
||||
|
||||
std::sort(array.begin(), array.end(), [cmp](void *&a, void *&b) -> bool {
|
||||
return cmp(a, b) < 0;
|
||||
});
|
||||
std::sort(array.begin(), array.end(),
|
||||
[cmp](void *&a, void *&b) -> bool { return cmp(a, b) < 0; });
|
||||
|
||||
std::copy(array.begin(), array.end(), l->array);
|
||||
std::copy(array.begin(), array.end(), l->array);
|
||||
|
||||
pthread_mutex_unlock(&l->lock);
|
||||
pthread_mutex_unlock(&l->lock);
|
||||
}
|
||||
|
||||
int villas::list_set(struct List *l, unsigned index, void *value)
|
||||
{
|
||||
if (index >= l->length)
|
||||
return -1;
|
||||
int villas::list_set(struct List *l, unsigned index, void *value) {
|
||||
if (index >= l->length)
|
||||
return -1;
|
||||
|
||||
l->array[index] = value;
|
||||
l->array[index] = value;
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t villas::list_index(struct List *l, void *p)
|
||||
{
|
||||
void *e;
|
||||
ssize_t f;
|
||||
ssize_t villas::list_index(struct List *l, void *p) {
|
||||
void *e;
|
||||
ssize_t f;
|
||||
|
||||
pthread_mutex_lock(&l->lock);
|
||||
pthread_mutex_lock(&l->lock);
|
||||
|
||||
assert(l->state == State::INITIALIZED);
|
||||
assert(l->state == State::INITIALIZED);
|
||||
|
||||
for (size_t i = 0; i < list_length(l); i++) {
|
||||
e = list_at(l, i);
|
||||
if (e == p) {
|
||||
f = i;
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < list_length(l); i++) {
|
||||
e = list_at(l, i);
|
||||
if (e == p) {
|
||||
f = i;
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
|
||||
f = -1;
|
||||
f = -1;
|
||||
|
||||
found: pthread_mutex_unlock(&l->lock);
|
||||
found:
|
||||
pthread_mutex_unlock(&l->lock);
|
||||
|
||||
return f;
|
||||
return f;
|
||||
}
|
||||
|
||||
void villas::list_extend(struct List *l, size_t len, void *val)
|
||||
{
|
||||
while (list_length(l) < len)
|
||||
list_push(l, val);
|
||||
void villas::list_extend(struct List *l, size_t len, void *val) {
|
||||
while (list_length(l) < len)
|
||||
list_push(l, val);
|
||||
}
|
||||
|
||||
void villas::list_filter(struct List *l, dtor_cb_t cb)
|
||||
{
|
||||
size_t i, j;
|
||||
pthread_mutex_lock(&l->lock);
|
||||
void villas::list_filter(struct List *l, dtor_cb_t cb) {
|
||||
size_t i, j;
|
||||
pthread_mutex_lock(&l->lock);
|
||||
|
||||
for (i = 0, j = 0; i < list_length(l); i++) {
|
||||
void *e = list_at(l, i);
|
||||
for (i = 0, j = 0; i < list_length(l); i++) {
|
||||
void *e = list_at(l, i);
|
||||
|
||||
if (!cb(e))
|
||||
l->array[j++] = e;
|
||||
}
|
||||
if (!cb(e))
|
||||
l->array[j++] = e;
|
||||
}
|
||||
|
||||
l->length = j;
|
||||
l->length = j;
|
||||
|
||||
pthread_mutex_unlock(&l->lock);
|
||||
pthread_mutex_unlock(&l->lock);
|
||||
}
|
||||
|
|
|
@ -5,219 +5,195 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <list>
|
||||
#include <algorithm>
|
||||
#include <list>
|
||||
#include <map>
|
||||
|
||||
#include <fnmatch.h>
|
||||
|
||||
#include <spdlog/sinks/syslog_sink.h>
|
||||
#include <spdlog/sinks/basic_file_sink.h>
|
||||
#include <spdlog/sinks/syslog_sink.h>
|
||||
|
||||
#include <villas/exceptions.hpp>
|
||||
#include <villas/log.hpp>
|
||||
#include <villas/terminal.hpp>
|
||||
#include <villas/exceptions.hpp>
|
||||
|
||||
using namespace villas;
|
||||
|
||||
// The global log instance
|
||||
Log villas::logging;
|
||||
|
||||
static
|
||||
std::map<spdlog::level::level_enum, std::string> levelNames = {
|
||||
{ spdlog::level::trace, "trc" },
|
||||
{ spdlog::level::debug, "dbg" },
|
||||
{ spdlog::level::info, "info" },
|
||||
{ spdlog::level::warn, "warn" },
|
||||
{ spdlog::level::err, "err" },
|
||||
{ spdlog::level::critical, "crit" },
|
||||
{ spdlog::level::off, "off" }
|
||||
};
|
||||
static std::map<spdlog::level::level_enum, std::string> levelNames = {
|
||||
{spdlog::level::trace, "trc"}, {spdlog::level::debug, "dbg"},
|
||||
{spdlog::level::info, "info"}, {spdlog::level::warn, "warn"},
|
||||
{spdlog::level::err, "err"}, {spdlog::level::critical, "crit"},
|
||||
{spdlog::level::off, "off"}};
|
||||
|
||||
class CustomLevelFlag : public spdlog::custom_flag_formatter {
|
||||
|
||||
public:
|
||||
void format(const spdlog::details::log_msg &msg, const std::tm &, spdlog::memory_buf_t &dest) override
|
||||
{
|
||||
auto lvl = levelNames[msg.level];
|
||||
auto lvlpad = std::string(padinfo_.width_ - lvl.size(), ' ') + lvl;
|
||||
dest.append(lvlpad.data(), lvlpad.data() + lvlpad.size());
|
||||
}
|
||||
void format(const spdlog::details::log_msg &msg, const std::tm &,
|
||||
spdlog::memory_buf_t &dest) override {
|
||||
auto lvl = levelNames[msg.level];
|
||||
auto lvlpad = std::string(padinfo_.width_ - lvl.size(), ' ') + lvl;
|
||||
dest.append(lvlpad.data(), lvlpad.data() + lvlpad.size());
|
||||
}
|
||||
|
||||
spdlog::details::padding_info get_padding_info()
|
||||
{
|
||||
return padinfo_;
|
||||
}
|
||||
spdlog::details::padding_info get_padding_info() { return padinfo_; }
|
||||
|
||||
std::unique_ptr<custom_flag_formatter> clone() const override
|
||||
{
|
||||
return spdlog::details::make_unique<CustomLevelFlag>();
|
||||
}
|
||||
std::unique_ptr<custom_flag_formatter> clone() const override {
|
||||
return spdlog::details::make_unique<CustomLevelFlag>();
|
||||
}
|
||||
};
|
||||
|
||||
Log::Log(Level lvl) :
|
||||
level(lvl),
|
||||
pattern("%H:%M:%S %^%-4t%$ %-16n %v")
|
||||
{
|
||||
char *p = getenv("VILLAS_LOG_PREFIX");
|
||||
Log::Log(Level lvl) : level(lvl), pattern("%H:%M:%S %^%-4t%$ %-16n %v") {
|
||||
char *p = getenv("VILLAS_LOG_PREFIX");
|
||||
|
||||
sinks = std::make_shared<DistSink::element_type>();
|
||||
sinks = std::make_shared<DistSink::element_type>();
|
||||
|
||||
setLevel(level);
|
||||
setFormatter(pattern, p ? p : "");
|
||||
setLevel(level);
|
||||
setFormatter(pattern, p ? p : "");
|
||||
|
||||
// Default sink
|
||||
sink = std::make_shared<spdlog::sinks::stderr_color_sink_mt>();
|
||||
// Default sink
|
||||
sink = std::make_shared<spdlog::sinks::stderr_color_sink_mt>();
|
||||
|
||||
sinks->add_sink(sink);
|
||||
sinks->add_sink(sink);
|
||||
}
|
||||
|
||||
int Log::getWidth()
|
||||
{
|
||||
int width = Terminal::getCols() - 50;
|
||||
int Log::getWidth() {
|
||||
int width = Terminal::getCols() - 50;
|
||||
|
||||
if (!prefix.empty())
|
||||
width -= prefix.length();
|
||||
if (!prefix.empty())
|
||||
width -= prefix.length();
|
||||
|
||||
return width;
|
||||
return width;
|
||||
}
|
||||
|
||||
Logger Log::get(const std::string &name)
|
||||
{
|
||||
Logger logger = spdlog::get(name);
|
||||
Logger Log::get(const std::string &name) {
|
||||
Logger logger = spdlog::get(name);
|
||||
|
||||
if (not logger) {
|
||||
logger = std::make_shared<Logger::element_type>(name, sinks);
|
||||
if (not logger) {
|
||||
logger = std::make_shared<Logger::element_type>(name, sinks);
|
||||
|
||||
logger->set_level(level);
|
||||
logger->set_formatter(formatter->clone());
|
||||
logger->set_level(level);
|
||||
logger->set_formatter(formatter->clone());
|
||||
|
||||
for (auto &expr : expressions) {
|
||||
int flags = 0;
|
||||
for (auto &expr : expressions) {
|
||||
int flags = 0;
|
||||
#ifdef FNM_EXTMATCH
|
||||
// musl-libc doesnt support this flag yet
|
||||
flags |= FNM_EXTMATCH;
|
||||
// musl-libc doesnt support this flag yet
|
||||
flags |= FNM_EXTMATCH;
|
||||
#endif
|
||||
if (!fnmatch(expr.name.c_str(), name.c_str(), flags))
|
||||
logger->set_level(expr.level);
|
||||
}
|
||||
if (!fnmatch(expr.name.c_str(), name.c_str(), flags))
|
||||
logger->set_level(expr.level);
|
||||
}
|
||||
|
||||
spdlog::register_logger(logger);
|
||||
}
|
||||
spdlog::register_logger(logger);
|
||||
}
|
||||
|
||||
return logger;
|
||||
return logger;
|
||||
}
|
||||
|
||||
void Log::parse(json_t *json)
|
||||
{
|
||||
const char *level = nullptr;
|
||||
const char *path = nullptr;
|
||||
const char *pattern = nullptr;
|
||||
void Log::parse(json_t *json) {
|
||||
const char *level = nullptr;
|
||||
const char *path = nullptr;
|
||||
const char *pattern = nullptr;
|
||||
|
||||
int ret, syslog = 0;
|
||||
int ret, syslog = 0;
|
||||
|
||||
json_error_t err;
|
||||
json_t *json_expressions = nullptr;
|
||||
json_error_t err;
|
||||
json_t *json_expressions = nullptr;
|
||||
|
||||
ret = json_unpack_ex(json, &err, JSON_STRICT, "{ s?: s, s?: s, s?: o, s?: b, s?: s }",
|
||||
"level", &level,
|
||||
"file", &path,
|
||||
"expressions", &json_expressions,
|
||||
"syslog", &syslog,
|
||||
"pattern", &pattern
|
||||
);
|
||||
if (ret)
|
||||
throw ConfigError(json, err, "node-config-logging");
|
||||
ret = json_unpack_ex(json, &err, JSON_STRICT,
|
||||
"{ s?: s, s?: s, s?: o, s?: b, s?: s }", "level", &level,
|
||||
"file", &path, "expressions", &json_expressions,
|
||||
"syslog", &syslog, "pattern", &pattern);
|
||||
if (ret)
|
||||
throw ConfigError(json, err, "node-config-logging");
|
||||
|
||||
if (level)
|
||||
setLevel(level);
|
||||
if (level)
|
||||
setLevel(level);
|
||||
|
||||
if (path) {
|
||||
auto sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(path);
|
||||
if (path) {
|
||||
auto sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(path);
|
||||
|
||||
sinks->add_sink(sink);
|
||||
}
|
||||
sinks->add_sink(sink);
|
||||
}
|
||||
|
||||
if (syslog) {
|
||||
if (syslog) {
|
||||
#if SPDLOG_VERSION >= 10400
|
||||
auto sink = std::make_shared<spdlog::sinks::syslog_sink_mt>("villas", LOG_PID, LOG_DAEMON, true);
|
||||
auto sink = std::make_shared<spdlog::sinks::syslog_sink_mt>(
|
||||
"villas", LOG_PID, LOG_DAEMON, true);
|
||||
#else
|
||||
auto sink = std::make_shared<spdlog::sinks::syslog_sink_mt>("villas", LOG_PID, LOG_DAEMON);
|
||||
auto sink = std::make_shared<spdlog::sinks::syslog_sink_mt>(
|
||||
"villas", LOG_PID, LOG_DAEMON);
|
||||
#endif
|
||||
sinks->add_sink(sink);
|
||||
}
|
||||
sinks->add_sink(sink);
|
||||
}
|
||||
|
||||
if (json_expressions) {
|
||||
if (!json_is_array(json_expressions))
|
||||
throw ConfigError(json_expressions, "node-config-logging-expressions", "The 'expressions' setting must be a list of objects.");
|
||||
if (json_expressions) {
|
||||
if (!json_is_array(json_expressions))
|
||||
throw ConfigError(json_expressions, "node-config-logging-expressions",
|
||||
"The 'expressions' setting must be a list of objects.");
|
||||
|
||||
size_t i;
|
||||
json_t *json_expression;
|
||||
size_t i;
|
||||
json_t *json_expression;
|
||||
|
||||
// cppcheck-suppress unknownMacro
|
||||
json_array_foreach(json_expressions, i, json_expression)
|
||||
expressions.emplace_back(json_expression);
|
||||
}
|
||||
// cppcheck-suppress unknownMacro
|
||||
json_array_foreach(json_expressions, i, json_expression)
|
||||
expressions.emplace_back(json_expression);
|
||||
}
|
||||
}
|
||||
|
||||
void Log::setFormatter(const std::string &pat, const std::string &pfx)
|
||||
{
|
||||
pattern = pat;
|
||||
prefix = pfx;
|
||||
void Log::setFormatter(const std::string &pat, const std::string &pfx) {
|
||||
pattern = pat;
|
||||
prefix = pfx;
|
||||
|
||||
formatter = std::make_shared<spdlog::pattern_formatter>(spdlog::pattern_time_type::utc);
|
||||
formatter->add_flag<CustomLevelFlag>('t');
|
||||
formatter->set_pattern(prefix + pattern);
|
||||
formatter = std::make_shared<spdlog::pattern_formatter>(
|
||||
spdlog::pattern_time_type::utc);
|
||||
formatter->add_flag<CustomLevelFlag>('t');
|
||||
formatter->set_pattern(prefix + pattern);
|
||||
|
||||
sinks->set_formatter(formatter->clone());
|
||||
sinks->set_formatter(formatter->clone());
|
||||
}
|
||||
|
||||
void Log::setLevel(Level lvl)
|
||||
{
|
||||
level = lvl;
|
||||
void Log::setLevel(Level lvl) {
|
||||
level = lvl;
|
||||
|
||||
sinks->set_level(lvl);
|
||||
sinks->set_level(lvl);
|
||||
}
|
||||
|
||||
void Log::setLevel(const std::string &lvl)
|
||||
{
|
||||
auto l = SPDLOG_LEVEL_NAMES;
|
||||
void Log::setLevel(const std::string &lvl) {
|
||||
auto l = SPDLOG_LEVEL_NAMES;
|
||||
|
||||
auto it = std::find(l.begin(), l.end(), lvl);
|
||||
if (it == l.end())
|
||||
throw RuntimeError("Invalid log level {}", lvl);
|
||||
auto it = std::find(l.begin(), l.end(), lvl);
|
||||
if (it == l.end())
|
||||
throw RuntimeError("Invalid log level {}", lvl);
|
||||
|
||||
setLevel(spdlog::level::from_str(lvl));
|
||||
setLevel(spdlog::level::from_str(lvl));
|
||||
}
|
||||
|
||||
Log::Level Log::getLevel() const
|
||||
{
|
||||
return level;
|
||||
Log::Level Log::getLevel() const { return level; }
|
||||
|
||||
std::string Log::getLevelName() const {
|
||||
auto sv = spdlog::level::to_string_view(level);
|
||||
|
||||
return std::string(sv.data());
|
||||
}
|
||||
|
||||
std::string Log::getLevelName() const
|
||||
{
|
||||
auto sv = spdlog::level::to_string_view(level);
|
||||
Log::Expression::Expression(json_t *json) {
|
||||
int ret;
|
||||
|
||||
return std::string(sv.data());
|
||||
}
|
||||
|
||||
Log::Expression::Expression(json_t *json)
|
||||
{
|
||||
int ret;
|
||||
|
||||
const char *nme;
|
||||
const char *lvl;
|
||||
|
||||
json_error_t err;
|
||||
|
||||
ret = json_unpack_ex(json, &err, JSON_STRICT, "{ s: s, s: s }",
|
||||
"name", &nme,
|
||||
"level", &lvl
|
||||
);
|
||||
if (ret)
|
||||
throw ConfigError(json, err, "node-config-logging-expressions");
|
||||
|
||||
level = spdlog::level::from_str(lvl);
|
||||
name = nme;
|
||||
const char *nme;
|
||||
const char *lvl;
|
||||
|
||||
json_error_t err;
|
||||
|
||||
ret = json_unpack_ex(json, &err, JSON_STRICT, "{ s: s, s: s }", "name", &nme,
|
||||
"level", &lvl);
|
||||
if (ret)
|
||||
throw ConfigError(json, err, "node-config-logging-expressions");
|
||||
|
||||
level = spdlog::level::from_str(lvl);
|
||||
name = nme;
|
||||
}
|
||||
|
|
|
@ -10,276 +10,260 @@
|
|||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
#include <villas/memory.hpp>
|
||||
|
||||
namespace villas {
|
||||
|
||||
std::unique_ptr<MemoryBlock, MemoryBlock::deallocator_fn>
|
||||
HostRam::HostRamAllocator::allocateBlock(size_t size)
|
||||
{
|
||||
// Align to next bigger page size chunk
|
||||
if (size & size_t(0xFFF)) {
|
||||
size += size_t(0x1000);
|
||||
size &= size_t(~0xFFF);
|
||||
}
|
||||
HostRam::HostRamAllocator::allocateBlock(size_t size) {
|
||||
// Align to next bigger page size chunk
|
||||
if (size & size_t(0xFFF)) {
|
||||
size += size_t(0x1000);
|
||||
size &= size_t(~0xFFF);
|
||||
}
|
||||
|
||||
#if defined(__linux__) && !(defined(__arm__) || defined(__aarch64__))
|
||||
const int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT;
|
||||
const int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT;
|
||||
#else
|
||||
const int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS;
|
||||
const int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS;
|
||||
#endif
|
||||
const int mmap_protection = PROT_READ | PROT_WRITE;
|
||||
const int mmap_protection = PROT_READ | PROT_WRITE;
|
||||
|
||||
const void* addr = mmap(nullptr, size, mmap_protection, mmap_flags, 0, 0);
|
||||
if (addr == nullptr) {
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
const void *addr = mmap(nullptr, size, mmap_protection, mmap_flags, 0, 0);
|
||||
if (addr == nullptr) {
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
auto &mm = MemoryManager::get();
|
||||
auto &mm = MemoryManager::get();
|
||||
|
||||
// Assemble name for this block
|
||||
std::stringstream name;
|
||||
name << std::showbase << std::hex << reinterpret_cast<uintptr_t>(addr);
|
||||
// Assemble name for this block
|
||||
std::stringstream name;
|
||||
name << std::showbase << std::hex << reinterpret_cast<uintptr_t>(addr);
|
||||
|
||||
auto blockAddrSpaceId = mm.getProcessAddressSpaceMemoryBlock(name.str());
|
||||
auto blockAddrSpaceId = mm.getProcessAddressSpaceMemoryBlock(name.str());
|
||||
|
||||
const auto localAddr = reinterpret_cast<uintptr_t>(addr);
|
||||
std::unique_ptr<MemoryBlock, MemoryBlock::deallocator_fn>
|
||||
mem(new MemoryBlock(localAddr, size, blockAddrSpaceId), this->free);
|
||||
const auto localAddr = reinterpret_cast<uintptr_t>(addr);
|
||||
std::unique_ptr<MemoryBlock, MemoryBlock::deallocator_fn> mem(
|
||||
new MemoryBlock(localAddr, size, blockAddrSpaceId), this->free);
|
||||
|
||||
insertMemoryBlock(*mem);
|
||||
insertMemoryBlock(*mem);
|
||||
|
||||
return mem;
|
||||
return mem;
|
||||
}
|
||||
|
||||
// cppcheck-suppress passedByValue
|
||||
LinearAllocator::LinearAllocator(MemoryManager::AddressSpaceId memoryAddrSpaceId,
|
||||
size_t memorySize,
|
||||
size_t internalOffset) :
|
||||
BaseAllocator(memoryAddrSpaceId),
|
||||
nextFreeAddress(0),
|
||||
memorySize(memorySize),
|
||||
internalOffset(internalOffset),
|
||||
allocationCount(0)
|
||||
{
|
||||
// Make sure to start at aligned offset, reduce size in case we need padding
|
||||
if (const size_t paddingBytes = getAlignmentPadding(internalOffset)) {
|
||||
assert(paddingBytes < memorySize);
|
||||
LinearAllocator::LinearAllocator(
|
||||
MemoryManager::AddressSpaceId memoryAddrSpaceId, size_t memorySize,
|
||||
size_t internalOffset)
|
||||
: BaseAllocator(memoryAddrSpaceId), nextFreeAddress(0),
|
||||
memorySize(memorySize), internalOffset(internalOffset),
|
||||
allocationCount(0) {
|
||||
// Make sure to start at aligned offset, reduce size in case we need padding
|
||||
if (const size_t paddingBytes = getAlignmentPadding(internalOffset)) {
|
||||
assert(paddingBytes < memorySize);
|
||||
|
||||
internalOffset += paddingBytes;
|
||||
memorySize -= paddingBytes;
|
||||
}
|
||||
internalOffset += paddingBytes;
|
||||
memorySize -= paddingBytes;
|
||||
}
|
||||
|
||||
// Deallocation callback
|
||||
free = [&](MemoryBlock* mem) {
|
||||
logger->debug("Deallocating memory block at local addr {:#x} (addr space {})",
|
||||
mem->getOffset(), mem->getAddrSpaceId());
|
||||
// Deallocation callback
|
||||
free = [&](MemoryBlock *mem) {
|
||||
logger->debug(
|
||||
"Deallocating memory block at local addr {:#x} (addr space {})",
|
||||
mem->getOffset(), mem->getAddrSpaceId());
|
||||
|
||||
removeMemoryBlock(*mem);
|
||||
removeMemoryBlock(*mem);
|
||||
|
||||
allocationCount--;
|
||||
if (allocationCount == 0) {
|
||||
logger->debug("All allocations are deallocated now, freeing memory");
|
||||
allocationCount--;
|
||||
if (allocationCount == 0) {
|
||||
logger->debug("All allocations are deallocated now, freeing memory");
|
||||
|
||||
// All allocations have been deallocated, free all memory
|
||||
nextFreeAddress = 0;
|
||||
}
|
||||
// All allocations have been deallocated, free all memory
|
||||
nextFreeAddress = 0;
|
||||
}
|
||||
|
||||
logger->debug("Available memory: {:#x} bytes", getAvailableMemory());
|
||||
};
|
||||
logger->debug("Available memory: {:#x} bytes", getAvailableMemory());
|
||||
};
|
||||
}
|
||||
|
||||
std::string
|
||||
LinearAllocator::getName() const
|
||||
{
|
||||
std::stringstream name;
|
||||
name << "LinearAlloc" << getAddrSpaceId();
|
||||
if (internalOffset != 0) {
|
||||
name << "@0x" << std::hex << internalOffset;
|
||||
}
|
||||
std::string LinearAllocator::getName() const {
|
||||
std::stringstream name;
|
||||
name << "LinearAlloc" << getAddrSpaceId();
|
||||
if (internalOffset != 0) {
|
||||
name << "@0x" << std::hex << internalOffset;
|
||||
}
|
||||
|
||||
return name.str();
|
||||
return name.str();
|
||||
}
|
||||
|
||||
std::unique_ptr<MemoryBlock, MemoryBlock::deallocator_fn>
|
||||
LinearAllocator::allocateBlock(size_t size)
|
||||
{
|
||||
if (size > getAvailableMemory()) {
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
LinearAllocator::allocateBlock(size_t size) {
|
||||
if (size > getAvailableMemory()) {
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
// Assign address
|
||||
const uintptr_t localAddr = nextFreeAddress + internalOffset;
|
||||
// Assign address
|
||||
const uintptr_t localAddr = nextFreeAddress + internalOffset;
|
||||
|
||||
// Reserve memory
|
||||
nextFreeAddress += size;
|
||||
// Reserve memory
|
||||
nextFreeAddress += size;
|
||||
|
||||
// Make sure it is aligned
|
||||
if (const size_t paddingBytes = getAlignmentPadding(nextFreeAddress)) {
|
||||
nextFreeAddress += paddingBytes;
|
||||
// Make sure it is aligned
|
||||
if (const size_t paddingBytes = getAlignmentPadding(nextFreeAddress)) {
|
||||
nextFreeAddress += paddingBytes;
|
||||
|
||||
// If next free address is outside this block due to padding, cap it
|
||||
nextFreeAddress = std::min(nextFreeAddress, memorySize);
|
||||
}
|
||||
// If next free address is outside this block due to padding, cap it
|
||||
nextFreeAddress = std::min(nextFreeAddress, memorySize);
|
||||
}
|
||||
|
||||
auto &mm = MemoryManager::get();
|
||||
auto &mm = MemoryManager::get();
|
||||
|
||||
// Assemble name for this block
|
||||
std::stringstream blockName;
|
||||
blockName << std::showbase << std::hex << localAddr;
|
||||
// Assemble name for this block
|
||||
std::stringstream blockName;
|
||||
blockName << std::showbase << std::hex << localAddr;
|
||||
|
||||
// Create address space
|
||||
auto addrSpaceName = mm.getSlaveAddrSpaceName(getName(), blockName.str());
|
||||
auto addrSpaceId = mm.getOrCreateAddressSpace(addrSpaceName);
|
||||
// Create address space
|
||||
auto addrSpaceName = mm.getSlaveAddrSpaceName(getName(), blockName.str());
|
||||
auto addrSpaceId = mm.getOrCreateAddressSpace(addrSpaceName);
|
||||
|
||||
logger->debug("Allocated {:#x} bytes for {}, {:#x} bytes remaining",
|
||||
size, addrSpaceId, getAvailableMemory());
|
||||
logger->debug("Allocated {:#x} bytes for {}, {:#x} bytes remaining", size,
|
||||
addrSpaceId, getAvailableMemory());
|
||||
|
||||
std::unique_ptr<MemoryBlock, MemoryBlock::deallocator_fn>
|
||||
mem(new MemoryBlock(localAddr, size, addrSpaceId), this->free);
|
||||
std::unique_ptr<MemoryBlock, MemoryBlock::deallocator_fn> mem(
|
||||
new MemoryBlock(localAddr, size, addrSpaceId), this->free);
|
||||
|
||||
// Mount block into the memory graph
|
||||
insertMemoryBlock(*mem);
|
||||
// Mount block into the memory graph
|
||||
insertMemoryBlock(*mem);
|
||||
|
||||
// Increase the allocation count
|
||||
allocationCount++;
|
||||
// Increase the allocation count
|
||||
allocationCount++;
|
||||
|
||||
return mem;
|
||||
return mem;
|
||||
}
|
||||
|
||||
HostRam::HostRamAllocator
|
||||
HostRam::allocator;
|
||||
HostRam::HostRamAllocator HostRam::allocator;
|
||||
|
||||
HostRam::HostRamAllocator::HostRamAllocator() :
|
||||
BaseAllocator(MemoryManager::get().getProcessAddressSpace())
|
||||
{
|
||||
free = [&](MemoryBlock* mem) {
|
||||
if (::munmap(reinterpret_cast<void*>(mem->getOffset()), mem->getSize()) != 0) {
|
||||
logger->warn("munmap() failed for {:#x} of size {:#x}",
|
||||
mem->getOffset(), mem->getSize());
|
||||
}
|
||||
HostRam::HostRamAllocator::HostRamAllocator()
|
||||
: BaseAllocator(MemoryManager::get().getProcessAddressSpace()) {
|
||||
free = [&](MemoryBlock *mem) {
|
||||
if (::munmap(reinterpret_cast<void *>(mem->getOffset()), mem->getSize()) !=
|
||||
0) {
|
||||
logger->warn("munmap() failed for {:#x} of size {:#x}", mem->getOffset(),
|
||||
mem->getSize());
|
||||
}
|
||||
|
||||
removeMemoryBlock(*mem);
|
||||
};
|
||||
removeMemoryBlock(*mem);
|
||||
};
|
||||
}
|
||||
|
||||
std::map<int, std::unique_ptr<HostDmaRam::HostDmaRamAllocator>>
|
||||
HostDmaRam::allocators;
|
||||
HostDmaRam::allocators;
|
||||
|
||||
HostDmaRam::HostDmaRamAllocator::HostDmaRamAllocator(int num) :
|
||||
LinearAllocator(MemoryManager::get().getOrCreateAddressSpace(getUdmaBufName(num)), getUdmaBufBufSize(num)),
|
||||
num(num)
|
||||
{
|
||||
auto &mm = MemoryManager::get();
|
||||
logger = logging.get(getName());
|
||||
HostDmaRam::HostDmaRamAllocator::HostDmaRamAllocator(int num)
|
||||
: LinearAllocator(
|
||||
MemoryManager::get().getOrCreateAddressSpace(getUdmaBufName(num)),
|
||||
getUdmaBufBufSize(num)),
|
||||
num(num) {
|
||||
auto &mm = MemoryManager::get();
|
||||
logger = logging.get(getName());
|
||||
|
||||
if (getSize() == 0) {
|
||||
logger->error("Zero-sized DMA buffer not supported, is the kernel module loaded?");
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
if (getSize() == 0) {
|
||||
logger->error(
|
||||
"Zero-sized DMA buffer not supported, is the kernel module loaded?");
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
const uintptr_t base = getUdmaBufPhysAddr(num);
|
||||
const uintptr_t base = getUdmaBufPhysAddr(num);
|
||||
|
||||
mm.createMapping(base, 0, getSize(), getName() + "-PCI",
|
||||
mm.getPciAddressSpace(), getAddrSpaceId());
|
||||
mm.createMapping(base, 0, getSize(), getName() + "-PCI",
|
||||
mm.getPciAddressSpace(), getAddrSpaceId());
|
||||
|
||||
const auto bufPath = std::string("/dev/") + getUdmaBufName(num);
|
||||
const int bufFd = open(bufPath.c_str(), O_RDWR | O_SYNC);
|
||||
if (bufFd != -1) {
|
||||
void* buf = mmap(nullptr, getSize(), PROT_READ|PROT_WRITE, MAP_SHARED, bufFd, 0);
|
||||
close(bufFd);
|
||||
const auto bufPath = std::string("/dev/") + getUdmaBufName(num);
|
||||
const int bufFd = open(bufPath.c_str(), O_RDWR | O_SYNC);
|
||||
if (bufFd != -1) {
|
||||
void *buf =
|
||||
mmap(nullptr, getSize(), PROT_READ | PROT_WRITE, MAP_SHARED, bufFd, 0);
|
||||
close(bufFd);
|
||||
|
||||
if (buf != MAP_FAILED)
|
||||
mm.createMapping(reinterpret_cast<uintptr_t>(buf), 0, getSize(),
|
||||
getName() + "-VA",
|
||||
mm.getProcessAddressSpace(), getAddrSpaceId());
|
||||
else
|
||||
logger->warn("Cannot map {}", bufPath);
|
||||
}
|
||||
else
|
||||
logger->warn("Cannot open {}", bufPath);
|
||||
if (buf != MAP_FAILED)
|
||||
mm.createMapping(reinterpret_cast<uintptr_t>(buf), 0, getSize(),
|
||||
getName() + "-VA", mm.getProcessAddressSpace(),
|
||||
getAddrSpaceId());
|
||||
else
|
||||
logger->warn("Cannot map {}", bufPath);
|
||||
} else
|
||||
logger->warn("Cannot open {}", bufPath);
|
||||
|
||||
logger->info("Mapped {} of size {} bytes", bufPath, getSize());
|
||||
logger->info("Mapped {} of size {} bytes", bufPath, getSize());
|
||||
}
|
||||
|
||||
HostDmaRam::HostDmaRamAllocator::~HostDmaRamAllocator()
|
||||
{
|
||||
auto &mm = MemoryManager::get();
|
||||
HostDmaRam::HostDmaRamAllocator::~HostDmaRamAllocator() {
|
||||
auto &mm = MemoryManager::get();
|
||||
|
||||
void* baseVirt;
|
||||
try {
|
||||
auto translation = mm.getTranslationFromProcess(getAddrSpaceId());
|
||||
baseVirt = reinterpret_cast<void*>(translation.getLocalAddr(0));
|
||||
} catch (const std::out_of_range&) {
|
||||
// Not mapped, nothing to do
|
||||
return;
|
||||
}
|
||||
void *baseVirt;
|
||||
try {
|
||||
auto translation = mm.getTranslationFromProcess(getAddrSpaceId());
|
||||
baseVirt = reinterpret_cast<void *>(translation.getLocalAddr(0));
|
||||
} catch (const std::out_of_range &) {
|
||||
// Not mapped, nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
logger->debug("Unmapping {}", getName());
|
||||
logger->debug("Unmapping {}", getName());
|
||||
|
||||
// Try to unmap it
|
||||
if (::munmap(baseVirt, getSize()) != 0) {
|
||||
logger->warn("munmap() failed for {:p} of size {:#x}",
|
||||
baseVirt, getSize());
|
||||
}
|
||||
// Try to unmap it
|
||||
if (::munmap(baseVirt, getSize()) != 0) {
|
||||
logger->warn("munmap() failed for {:p} of size {:#x}", baseVirt, getSize());
|
||||
}
|
||||
}
|
||||
|
||||
std::string
|
||||
HostDmaRam::getUdmaBufName(int num)
|
||||
{
|
||||
std::stringstream name;
|
||||
name << "udmabuf" << num;
|
||||
std::string HostDmaRam::getUdmaBufName(int num) {
|
||||
std::stringstream name;
|
||||
name << "udmabuf" << num;
|
||||
|
||||
return name.str();
|
||||
return name.str();
|
||||
}
|
||||
|
||||
std::string
|
||||
HostDmaRam::getUdmaBufBasePath(int num)
|
||||
{
|
||||
std::stringstream path;
|
||||
path << "/sys/class/udmabuf/udmabuf" << num << "/";
|
||||
return path.str();
|
||||
std::string HostDmaRam::getUdmaBufBasePath(int num) {
|
||||
std::stringstream path;
|
||||
path << "/sys/class/udmabuf/udmabuf" << num << "/";
|
||||
return path.str();
|
||||
}
|
||||
|
||||
size_t
|
||||
HostDmaRam::getUdmaBufBufSize(int num)
|
||||
{
|
||||
std::fstream s(getUdmaBufBasePath(num) + "size", s.in);
|
||||
if (s.is_open()) {
|
||||
std::string line;
|
||||
if (std::getline(s, line)) {
|
||||
return std::strtoul(line.c_str(), nullptr, 10);
|
||||
}
|
||||
}
|
||||
size_t HostDmaRam::getUdmaBufBufSize(int num) {
|
||||
std::fstream s(getUdmaBufBasePath(num) + "size", s.in);
|
||||
if (s.is_open()) {
|
||||
std::string line;
|
||||
if (std::getline(s, line)) {
|
||||
return std::strtoul(line.c_str(), nullptr, 10);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uintptr_t
|
||||
HostDmaRam::getUdmaBufPhysAddr(int num)
|
||||
{
|
||||
std::fstream s(getUdmaBufBasePath(num) + "phys_addr", s.in);
|
||||
if (s.is_open()) {
|
||||
std::string line;
|
||||
if (std::getline(s, line)) {
|
||||
return std::strtoul(line.c_str(), nullptr, 16);
|
||||
}
|
||||
}
|
||||
uintptr_t HostDmaRam::getUdmaBufPhysAddr(int num) {
|
||||
std::fstream s(getUdmaBufBasePath(num) + "phys_addr", s.in);
|
||||
if (s.is_open()) {
|
||||
std::string line;
|
||||
if (std::getline(s, line)) {
|
||||
return std::strtoul(line.c_str(), nullptr, 16);
|
||||
}
|
||||
}
|
||||
|
||||
return UINTPTR_MAX;
|
||||
return UINTPTR_MAX;
|
||||
}
|
||||
|
||||
HostDmaRam::HostDmaRamAllocator&HostDmaRam::getAllocator(int num)
|
||||
{
|
||||
auto &allocator = allocators[num];
|
||||
if (not allocator) {
|
||||
allocator = std::make_unique<HostDmaRamAllocator>(num);
|
||||
}
|
||||
HostDmaRam::HostDmaRamAllocator &HostDmaRam::getAllocator(int num) {
|
||||
auto &allocator = allocators[num];
|
||||
if (not allocator) {
|
||||
allocator = std::make_unique<HostDmaRamAllocator>(num);
|
||||
}
|
||||
|
||||
return *allocator;
|
||||
return *allocator;
|
||||
}
|
||||
|
||||
} // namespace villas
|
||||
|
|
|
@ -5,241 +5,226 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
#include <limits>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
|
||||
#include <villas/exceptions.hpp>
|
||||
#include <villas/utils.hpp>
|
||||
#include <villas/memory_manager.hpp>
|
||||
#include <villas/utils.hpp>
|
||||
|
||||
using namespace villas;
|
||||
using namespace villas::utils;
|
||||
|
||||
namespace villas {
|
||||
|
||||
MemoryManager*
|
||||
MemoryManager::instance = nullptr;
|
||||
MemoryManager *MemoryManager::instance = nullptr;
|
||||
|
||||
MemoryManager&
|
||||
MemoryManager::get()
|
||||
{
|
||||
if (instance == nullptr) {
|
||||
instance = new MemoryManager;
|
||||
if (!instance)
|
||||
throw MemoryAllocationError();
|
||||
}
|
||||
MemoryManager &MemoryManager::get() {
|
||||
if (instance == nullptr) {
|
||||
instance = new MemoryManager;
|
||||
if (!instance)
|
||||
throw MemoryAllocationError();
|
||||
}
|
||||
|
||||
return *instance;
|
||||
return *instance;
|
||||
}
|
||||
|
||||
MemoryManager::AddressSpaceId
|
||||
MemoryManager::getOrCreateAddressSpace(std::string name)
|
||||
{
|
||||
try {
|
||||
// Try fast lookup
|
||||
return addrSpaceLookup.at(name);
|
||||
} catch (const std::out_of_range&) {
|
||||
// Does not yet exist, create
|
||||
std::shared_ptr<AddressSpace> addrSpace(new AddressSpace);
|
||||
addrSpace->name = name;
|
||||
MemoryManager::getOrCreateAddressSpace(std::string name) {
|
||||
try {
|
||||
// Try fast lookup
|
||||
return addrSpaceLookup.at(name);
|
||||
} catch (const std::out_of_range &) {
|
||||
// Does not yet exist, create
|
||||
std::shared_ptr<AddressSpace> addrSpace(new AddressSpace);
|
||||
addrSpace->name = name;
|
||||
|
||||
// Cache it for the next access
|
||||
addrSpaceLookup[name] = memoryGraph.addVertex(addrSpace);
|
||||
// Cache it for the next access
|
||||
addrSpaceLookup[name] = memoryGraph.addVertex(addrSpace);
|
||||
|
||||
return addrSpaceLookup[name];
|
||||
}
|
||||
return addrSpaceLookup[name];
|
||||
}
|
||||
}
|
||||
|
||||
MemoryManager::MappingId
|
||||
MemoryManager::createMapping(uintptr_t src, uintptr_t dest, size_t size,
|
||||
const std::string &name,
|
||||
MemoryManager::AddressSpaceId fromAddrSpace,
|
||||
MemoryManager::AddressSpaceId toAddrSpace)
|
||||
{
|
||||
std::shared_ptr<Mapping> mapping(new Mapping);
|
||||
MemoryManager::AddressSpaceId toAddrSpace) {
|
||||
std::shared_ptr<Mapping> mapping(new Mapping);
|
||||
|
||||
mapping->name = name;
|
||||
mapping->src = src;
|
||||
mapping->dest = dest;
|
||||
mapping->size = size;
|
||||
mapping->name = name;
|
||||
mapping->src = src;
|
||||
mapping->dest = dest;
|
||||
mapping->size = size;
|
||||
|
||||
return addMapping(mapping, fromAddrSpace, toAddrSpace);
|
||||
return addMapping(mapping, fromAddrSpace, toAddrSpace);
|
||||
}
|
||||
|
||||
MemoryManager::MappingId
|
||||
MemoryManager::addMapping(std::shared_ptr<Mapping> mapping,
|
||||
MemoryManager::AddressSpaceId fromAddrSpace,
|
||||
MemoryManager::AddressSpaceId toAddrSpace)
|
||||
{
|
||||
return memoryGraph.addEdge(mapping, fromAddrSpace, toAddrSpace);
|
||||
MemoryManager::AddressSpaceId toAddrSpace) {
|
||||
return memoryGraph.addEdge(mapping, fromAddrSpace, toAddrSpace);
|
||||
}
|
||||
|
||||
MemoryManager::AddressSpaceId
|
||||
MemoryManager::findAddressSpace(const std::string &name)
|
||||
{
|
||||
return memoryGraph.findVertex(
|
||||
[&](const std::shared_ptr<AddressSpace>& v) {
|
||||
return v->name == name;
|
||||
});
|
||||
MemoryManager::findAddressSpace(const std::string &name) {
|
||||
return memoryGraph.findVertex(
|
||||
[&](const std::shared_ptr<AddressSpace> &v) { return v->name == name; });
|
||||
}
|
||||
|
||||
std::list<MemoryManager::AddressSpaceId>
|
||||
MemoryManager::findPath(const MemoryManager::AddressSpaceId &fromAddrSpaceId,
|
||||
const MemoryManager::AddressSpaceId &toAddrSpaceId)
|
||||
{
|
||||
std::list<AddressSpaceId> path;
|
||||
const MemoryManager::AddressSpaceId &toAddrSpaceId) {
|
||||
std::list<AddressSpaceId> path;
|
||||
|
||||
auto fromAddrSpace = memoryGraph.getVertex(fromAddrSpaceId);
|
||||
auto toAddrSpace = memoryGraph.getVertex(toAddrSpaceId);
|
||||
auto fromAddrSpace = memoryGraph.getVertex(fromAddrSpaceId);
|
||||
auto toAddrSpace = memoryGraph.getVertex(toAddrSpaceId);
|
||||
|
||||
// Find a path through the memory graph
|
||||
MemoryGraph::Path pathGraph;
|
||||
if (not memoryGraph.getPath(fromAddrSpaceId, toAddrSpaceId, pathGraph, pathCheckFunc)) {
|
||||
// Find a path through the memory graph
|
||||
MemoryGraph::Path pathGraph;
|
||||
if (not memoryGraph.getPath(fromAddrSpaceId, toAddrSpaceId, pathGraph,
|
||||
pathCheckFunc)) {
|
||||
|
||||
logger->debug("No translation found from ({}) to ({})",
|
||||
fromAddrSpace->toString(), toAddrSpace->toString());
|
||||
logger->debug("No translation found from ({}) to ({})",
|
||||
fromAddrSpace->toString(), toAddrSpace->toString());
|
||||
|
||||
throw std::out_of_range("no translation found");
|
||||
}
|
||||
throw std::out_of_range("no translation found");
|
||||
}
|
||||
|
||||
for (auto &mappingId : pathGraph) {
|
||||
auto mapping = memoryGraph.getEdge(mappingId);
|
||||
path.push_back(mapping->getVertexTo());
|
||||
}
|
||||
for (auto &mappingId : pathGraph) {
|
||||
auto mapping = memoryGraph.getEdge(mappingId);
|
||||
path.push_back(mapping->getVertexTo());
|
||||
}
|
||||
|
||||
return path;
|
||||
return path;
|
||||
}
|
||||
|
||||
MemoryTranslation
|
||||
MemoryManager::getTranslation(const MemoryManager::AddressSpaceId &fromAddrSpaceId,
|
||||
const MemoryManager::AddressSpaceId &toAddrSpaceId)
|
||||
{
|
||||
// Find a path through the memory graph
|
||||
MemoryGraph::Path path;
|
||||
if (not memoryGraph.getPath(fromAddrSpaceId, toAddrSpaceId, path, pathCheckFunc)) {
|
||||
MemoryTranslation MemoryManager::getTranslation(
|
||||
const MemoryManager::AddressSpaceId &fromAddrSpaceId,
|
||||
const MemoryManager::AddressSpaceId &toAddrSpaceId) {
|
||||
// Find a path through the memory graph
|
||||
MemoryGraph::Path path;
|
||||
if (not memoryGraph.getPath(fromAddrSpaceId, toAddrSpaceId, path,
|
||||
pathCheckFunc)) {
|
||||
|
||||
auto fromAddrSpace = memoryGraph.getVertex(fromAddrSpaceId);
|
||||
auto toAddrSpace = memoryGraph.getVertex(toAddrSpaceId);
|
||||
logger->debug("No translation found from ({}) to ({})",
|
||||
fromAddrSpace->toString(), toAddrSpace->toString());
|
||||
auto fromAddrSpace = memoryGraph.getVertex(fromAddrSpaceId);
|
||||
auto toAddrSpace = memoryGraph.getVertex(toAddrSpaceId);
|
||||
logger->debug("No translation found from ({}) to ({})",
|
||||
fromAddrSpace->toString(), toAddrSpace->toString());
|
||||
|
||||
throw std::out_of_range("no translation found");
|
||||
}
|
||||
throw std::out_of_range("no translation found");
|
||||
}
|
||||
|
||||
// Start with an identity mapping
|
||||
MemoryTranslation translation(0, 0, SIZE_MAX);
|
||||
// Start with an identity mapping
|
||||
MemoryTranslation translation(0, 0, SIZE_MAX);
|
||||
|
||||
// Iterate through path and merge all mappings into a single translation
|
||||
for (auto &mappingId : path) {
|
||||
auto mapping = memoryGraph.getEdge(mappingId);
|
||||
translation += getTranslationFromMapping(*mapping);
|
||||
}
|
||||
// Iterate through path and merge all mappings into a single translation
|
||||
for (auto &mappingId : path) {
|
||||
auto mapping = memoryGraph.getEdge(mappingId);
|
||||
translation += getTranslationFromMapping(*mapping);
|
||||
}
|
||||
|
||||
return translation;
|
||||
return translation;
|
||||
}
|
||||
|
||||
bool
|
||||
MemoryManager::pathCheck(const MemoryGraph::Path &path)
|
||||
{
|
||||
// Start with an identity mapping
|
||||
MemoryTranslation translation(0, 0, SIZE_MAX);
|
||||
bool MemoryManager::pathCheck(const MemoryGraph::Path &path) {
|
||||
// Start with an identity mapping
|
||||
MemoryTranslation translation(0, 0, SIZE_MAX);
|
||||
|
||||
// Try to add all mappings together to a common translation. If this fails
|
||||
// there is a non-overlapping window.
|
||||
for (auto &mappingId : path) {
|
||||
auto mapping = memoryGraph.getEdge(mappingId);
|
||||
try {
|
||||
translation += getTranslationFromMapping(*mapping);
|
||||
} catch (const InvalidTranslation&) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Try to add all mappings together to a common translation. If this fails
|
||||
// there is a non-overlapping window.
|
||||
for (auto &mappingId : path) {
|
||||
auto mapping = memoryGraph.getEdge(mappingId);
|
||||
try {
|
||||
translation += getTranslationFromMapping(*mapping);
|
||||
} catch (const InvalidTranslation &) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
uintptr_t
|
||||
MemoryTranslation::getLocalAddr(uintptr_t addrInForeignAddrSpace) const
|
||||
{
|
||||
assert(addrInForeignAddrSpace >= dst);
|
||||
assert(addrInForeignAddrSpace < (dst + size));
|
||||
return src + addrInForeignAddrSpace - dst;
|
||||
MemoryTranslation::getLocalAddr(uintptr_t addrInForeignAddrSpace) const {
|
||||
assert(addrInForeignAddrSpace >= dst);
|
||||
assert(addrInForeignAddrSpace < (dst + size));
|
||||
return src + addrInForeignAddrSpace - dst;
|
||||
}
|
||||
|
||||
uintptr_t
|
||||
MemoryTranslation::getForeignAddr(uintptr_t addrInLocalAddrSpace) const
|
||||
{
|
||||
assert(addrInLocalAddrSpace >= src);
|
||||
assert(addrInLocalAddrSpace < (src + size));
|
||||
return dst + addrInLocalAddrSpace - src;
|
||||
MemoryTranslation::getForeignAddr(uintptr_t addrInLocalAddrSpace) const {
|
||||
assert(addrInLocalAddrSpace >= src);
|
||||
assert(addrInLocalAddrSpace < (src + size));
|
||||
return dst + addrInLocalAddrSpace - src;
|
||||
}
|
||||
|
||||
MemoryTranslation&
|
||||
MemoryTranslation::operator+=(const MemoryTranslation &other)
|
||||
{
|
||||
Logger logger = logging.get("MemoryTranslation");
|
||||
MemoryTranslation &
|
||||
MemoryTranslation::operator+=(const MemoryTranslation &other) {
|
||||
Logger logger = logging.get("MemoryTranslation");
|
||||
|
||||
// Set level to debug to enable debug output
|
||||
logger->set_level(spdlog::level::info);
|
||||
// Set level to debug to enable debug output
|
||||
logger->set_level(spdlog::level::info);
|
||||
|
||||
const uintptr_t this_dst_high = this->dst + this->size;
|
||||
const uintptr_t other_src_high = other.src + other.size;
|
||||
const uintptr_t this_dst_high = this->dst + this->size;
|
||||
const uintptr_t other_src_high = other.src + other.size;
|
||||
|
||||
logger->debug("this->src: {:#x}", this->src);
|
||||
logger->debug("this->dst: {:#x}", this->dst);
|
||||
logger->debug("this->size: {:#x}", this->size);
|
||||
logger->debug("other.src: {:#x}", other.src);
|
||||
logger->debug("other.dst: {:#x}", other.dst);
|
||||
logger->debug("other.size: {:#x}", other.size);
|
||||
logger->debug("this_dst_high: {:#x}", this_dst_high);
|
||||
logger->debug("other_src_high: {:#x}", other_src_high);
|
||||
logger->debug("this->src: {:#x}", this->src);
|
||||
logger->debug("this->dst: {:#x}", this->dst);
|
||||
logger->debug("this->size: {:#x}", this->size);
|
||||
logger->debug("other.src: {:#x}", other.src);
|
||||
logger->debug("other.dst: {:#x}", other.dst);
|
||||
logger->debug("other.size: {:#x}", other.size);
|
||||
logger->debug("this_dst_high: {:#x}", this_dst_high);
|
||||
logger->debug("other_src_high: {:#x}", other_src_high);
|
||||
|
||||
// Make sure there is a common memory area
|
||||
assertExcept(other.src < this_dst_high, MemoryManager::InvalidTranslation());
|
||||
assertExcept(this->dst < other_src_high, MemoryManager::InvalidTranslation());
|
||||
// Make sure there is a common memory area
|
||||
assertExcept(other.src < this_dst_high, MemoryManager::InvalidTranslation());
|
||||
assertExcept(this->dst < other_src_high, MemoryManager::InvalidTranslation());
|
||||
|
||||
const uintptr_t hi = std::max(this_dst_high, other_src_high);
|
||||
const uintptr_t lo = std::min(this->dst, other.src);
|
||||
const uintptr_t hi = std::max(this_dst_high, other_src_high);
|
||||
const uintptr_t lo = std::min(this->dst, other.src);
|
||||
|
||||
const uintptr_t diff_hi = (this_dst_high > other_src_high)
|
||||
? (this_dst_high - other_src_high)
|
||||
: (other_src_high - this_dst_high);
|
||||
const uintptr_t diff_hi = (this_dst_high > other_src_high)
|
||||
? (this_dst_high - other_src_high)
|
||||
: (other_src_high - this_dst_high);
|
||||
|
||||
const bool otherSrcIsSmaller = this->dst > other.src;
|
||||
const uintptr_t diff_lo = (otherSrcIsSmaller)
|
||||
? (this->dst - other.src)
|
||||
: (other.src - this->dst);
|
||||
const bool otherSrcIsSmaller = this->dst > other.src;
|
||||
const uintptr_t diff_lo =
|
||||
(otherSrcIsSmaller) ? (this->dst - other.src) : (other.src - this->dst);
|
||||
|
||||
logger->debug("hi: {:#x}", hi);
|
||||
logger->debug("lo: {:#x}", lo);
|
||||
logger->debug("diff_hi: {:#x}", diff_hi);
|
||||
logger->debug("diff_lo: {:#x}", diff_lo);
|
||||
logger->debug("hi: {:#x}", hi);
|
||||
logger->debug("lo: {:#x}", lo);
|
||||
logger->debug("diff_hi: {:#x}", diff_hi);
|
||||
logger->debug("diff_lo: {:#x}", diff_lo);
|
||||
|
||||
// New size of aperture, can only stay or shrink
|
||||
this->size = (hi - lo) - diff_hi - diff_lo;
|
||||
// New size of aperture, can only stay or shrink
|
||||
this->size = (hi - lo) - diff_hi - diff_lo;
|
||||
|
||||
// New translation will come out other's destination (by default)
|
||||
this->dst = other.dst;
|
||||
// New translation will come out other's destination (by default)
|
||||
this->dst = other.dst;
|
||||
|
||||
// The source stays the same and can only increase with merged translations
|
||||
//this->src = this->src;
|
||||
// The source stays the same and can only increase with merged translations
|
||||
//this->src = this->src;
|
||||
|
||||
if (otherSrcIsSmaller)
|
||||
// Other mapping starts at lower addresses, so we actually arrive at
|
||||
// higher addresses
|
||||
this->dst += diff_lo;
|
||||
else
|
||||
// Other mapping starts at higher addresses than this, so we have to
|
||||
// increase the start
|
||||
// NOTE: for addresses equality, this just adds 0
|
||||
this->src += diff_lo;
|
||||
if (otherSrcIsSmaller)
|
||||
// Other mapping starts at lower addresses, so we actually arrive at
|
||||
// higher addresses
|
||||
this->dst += diff_lo;
|
||||
else
|
||||
// Other mapping starts at higher addresses than this, so we have to
|
||||
// increase the start
|
||||
// NOTE: for addresses equality, this just adds 0
|
||||
this->src += diff_lo;
|
||||
|
||||
logger->debug("result src: {:#x}", this->src);
|
||||
logger->debug("result dst: {:#x}", this->dst);
|
||||
logger->debug("result size: {:#x}", this->size);
|
||||
logger->debug("result src: {:#x}", this->src);
|
||||
logger->debug("result dst: {:#x}", this->dst);
|
||||
logger->debug("result size: {:#x}", this->size);
|
||||
|
||||
return *this;
|
||||
return *this;
|
||||
}
|
||||
|
||||
} // namespace villas
|
||||
|
|
|
@ -5,33 +5,28 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <new>
|
||||
#include <type_traits>
|
||||
#include <dlfcn.h>
|
||||
#include <iostream>
|
||||
#include <new>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
#include <villas/plugin.hpp>
|
||||
|
||||
using namespace villas::plugin;
|
||||
|
||||
Registry * villas::plugin::registry = nullptr;
|
||||
Registry *villas::plugin::registry = nullptr;
|
||||
|
||||
Plugin::Plugin()
|
||||
{
|
||||
if (registry == nullptr)
|
||||
registry = new Registry();
|
||||
Plugin::Plugin() {
|
||||
if (registry == nullptr)
|
||||
registry = new Registry();
|
||||
|
||||
registry->add(this);
|
||||
registry->add(this);
|
||||
}
|
||||
|
||||
Plugin::~Plugin()
|
||||
{
|
||||
registry->remove(this);
|
||||
}
|
||||
Plugin::~Plugin() { registry->remove(this); }
|
||||
|
||||
void
|
||||
Plugin::dump()
|
||||
{
|
||||
getLogger()->info("Name: '{}' Description: '{}'", getName(), getDescription());
|
||||
void Plugin::dump() {
|
||||
getLogger()->info("Name: '{}' Description: '{}'", getName(),
|
||||
getDescription());
|
||||
}
|
||||
|
|
|
@ -5,203 +5,179 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <signal.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <paths.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <paths.h>
|
||||
#include <signal.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <cerrno>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <fmt/core.h>
|
||||
#include <villas/utils.hpp>
|
||||
#include <villas/exceptions.hpp>
|
||||
#include <villas/popen.hpp>
|
||||
#include <villas/utils.hpp>
|
||||
|
||||
namespace villas {
|
||||
namespace utils {
|
||||
|
||||
Popen::Popen(const std::string &cmd,
|
||||
const arg_list &args,
|
||||
const env_map &env,
|
||||
const std::string &wd,
|
||||
bool sh) :
|
||||
command(cmd),
|
||||
working_dir(wd),
|
||||
arguments(args),
|
||||
environment(env),
|
||||
shell(sh)
|
||||
{
|
||||
open();
|
||||
Popen::Popen(const std::string &cmd, const arg_list &args, const env_map &env,
|
||||
const std::string &wd, bool sh)
|
||||
: command(cmd), working_dir(wd), arguments(args), environment(env),
|
||||
shell(sh) {
|
||||
open();
|
||||
}
|
||||
|
||||
Popen::~Popen()
|
||||
{
|
||||
close();
|
||||
}
|
||||
Popen::~Popen() { close(); }
|
||||
|
||||
void Popen::kill(int signal)
|
||||
{
|
||||
::kill(pid, signal);
|
||||
}
|
||||
void Popen::kill(int signal) { ::kill(pid, signal); }
|
||||
|
||||
void Popen::open()
|
||||
{
|
||||
std::vector<char *> argv, envp;
|
||||
void Popen::open() {
|
||||
std::vector<char *> argv, envp;
|
||||
|
||||
const int READ = 0;
|
||||
const int WRITE = 1;
|
||||
const int READ = 0;
|
||||
const int WRITE = 1;
|
||||
|
||||
int ret;
|
||||
int inpipe[2];
|
||||
int outpipe[2];
|
||||
int ret;
|
||||
int inpipe[2];
|
||||
int outpipe[2];
|
||||
|
||||
ret = pipe(inpipe);
|
||||
if (ret)
|
||||
goto error_out;
|
||||
ret = pipe(inpipe);
|
||||
if (ret)
|
||||
goto error_out;
|
||||
|
||||
ret = pipe(outpipe);
|
||||
if (ret)
|
||||
goto clean_inpipe_out;
|
||||
ret = pipe(outpipe);
|
||||
if (ret)
|
||||
goto clean_inpipe_out;
|
||||
|
||||
fd_in = outpipe[READ];
|
||||
fd_out = inpipe[WRITE];
|
||||
fd_in = outpipe[READ];
|
||||
fd_out = inpipe[WRITE];
|
||||
|
||||
pid = fork();
|
||||
if (pid == -1)
|
||||
goto clean_outpipe_out;
|
||||
pid = fork();
|
||||
if (pid == -1)
|
||||
goto clean_outpipe_out;
|
||||
|
||||
if (pid == 0) {
|
||||
// Prepare arguments
|
||||
if (shell) {
|
||||
argv.push_back((char *) "sh");
|
||||
argv.push_back((char *) "-c");
|
||||
argv.push_back((char *) command.c_str());
|
||||
}
|
||||
else {
|
||||
for (auto arg: arguments) {
|
||||
auto s = strdup(arg.c_str());
|
||||
if (pid == 0) {
|
||||
// Prepare arguments
|
||||
if (shell) {
|
||||
argv.push_back((char *)"sh");
|
||||
argv.push_back((char *)"-c");
|
||||
argv.push_back((char *)command.c_str());
|
||||
} else {
|
||||
for (auto arg : arguments) {
|
||||
auto s = strdup(arg.c_str());
|
||||
|
||||
argv.push_back(s);
|
||||
}
|
||||
}
|
||||
argv.push_back(s);
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare environment
|
||||
for (char **p = environ; *p; p++)
|
||||
envp.push_back(*p);
|
||||
// Prepare environment
|
||||
for (char **p = environ; *p; p++)
|
||||
envp.push_back(*p);
|
||||
|
||||
for (auto env: environment) {
|
||||
auto e = fmt::format("{}={}", env.first, env.second);
|
||||
auto s = strdup(e.c_str());
|
||||
for (auto env : environment) {
|
||||
auto e = fmt::format("{}={}", env.first, env.second);
|
||||
auto s = strdup(e.c_str());
|
||||
|
||||
envp.push_back(s);
|
||||
}
|
||||
envp.push_back(s);
|
||||
}
|
||||
|
||||
argv.push_back(nullptr);
|
||||
envp.push_back(nullptr);
|
||||
argv.push_back(nullptr);
|
||||
envp.push_back(nullptr);
|
||||
|
||||
// Redirect IO
|
||||
::close(outpipe[READ]);
|
||||
::close(inpipe[WRITE]);
|
||||
// Redirect IO
|
||||
::close(outpipe[READ]);
|
||||
::close(inpipe[WRITE]);
|
||||
|
||||
if (inpipe[READ] != STDIN_FILENO) {
|
||||
dup2(inpipe[READ], STDIN_FILENO);
|
||||
::close(inpipe[READ]);
|
||||
}
|
||||
if (inpipe[READ] != STDIN_FILENO) {
|
||||
dup2(inpipe[READ], STDIN_FILENO);
|
||||
::close(inpipe[READ]);
|
||||
}
|
||||
|
||||
if (outpipe[WRITE] != STDOUT_FILENO) {
|
||||
dup2(outpipe[WRITE], STDOUT_FILENO);
|
||||
::close(outpipe[WRITE]);
|
||||
}
|
||||
if (outpipe[WRITE] != STDOUT_FILENO) {
|
||||
dup2(outpipe[WRITE], STDOUT_FILENO);
|
||||
::close(outpipe[WRITE]);
|
||||
}
|
||||
|
||||
// Change working directory
|
||||
if (!working_dir.empty()) {
|
||||
int ret;
|
||||
// Change working directory
|
||||
if (!working_dir.empty()) {
|
||||
int ret;
|
||||
|
||||
ret = chdir(working_dir.c_str());
|
||||
if (ret)
|
||||
exit(127);
|
||||
}
|
||||
ret = chdir(working_dir.c_str());
|
||||
if (ret)
|
||||
exit(127);
|
||||
}
|
||||
|
||||
execvpe(shell ? _PATH_BSHELL : command.c_str(), (char * const *) argv.data(), (char * const *) envp.data());
|
||||
exit(127);
|
||||
}
|
||||
execvpe(shell ? _PATH_BSHELL : command.c_str(), (char *const *)argv.data(),
|
||||
(char *const *)envp.data());
|
||||
exit(127);
|
||||
}
|
||||
|
||||
::close(outpipe[WRITE]);
|
||||
::close(inpipe[READ]);
|
||||
::close(outpipe[WRITE]);
|
||||
::close(inpipe[READ]);
|
||||
|
||||
return;
|
||||
return;
|
||||
|
||||
clean_outpipe_out:
|
||||
::close(outpipe[READ]);
|
||||
::close(outpipe[WRITE]);
|
||||
::close(outpipe[READ]);
|
||||
::close(outpipe[WRITE]);
|
||||
|
||||
clean_inpipe_out:
|
||||
::close(inpipe[READ]);
|
||||
::close(inpipe[WRITE]);
|
||||
::close(inpipe[READ]);
|
||||
::close(inpipe[WRITE]);
|
||||
|
||||
error_out:
|
||||
throw SystemError("Failed to start subprocess");
|
||||
throw SystemError("Failed to start subprocess");
|
||||
}
|
||||
|
||||
int Popen::close()
|
||||
{
|
||||
int pstat;
|
||||
int Popen::close() {
|
||||
int pstat;
|
||||
|
||||
if (pid != -1) {
|
||||
do {
|
||||
pid = waitpid(pid, &pstat, 0);
|
||||
} while (pid == -1 && errno == EINTR);
|
||||
}
|
||||
if (pid != -1) {
|
||||
do {
|
||||
pid = waitpid(pid, &pstat, 0);
|
||||
} while (pid == -1 && errno == EINTR);
|
||||
}
|
||||
|
||||
return pid == -1 ? -1 : pstat;
|
||||
return pid == -1 ? -1 : pstat;
|
||||
}
|
||||
|
||||
PopenStream::PopenStream(const std::string &cmd,
|
||||
const arg_list &args,
|
||||
const env_map &env,
|
||||
const std::string &wd,
|
||||
bool sh) :
|
||||
Popen(cmd, args, env, wd, sh)
|
||||
{
|
||||
open();
|
||||
PopenStream::PopenStream(const std::string &cmd, const arg_list &args,
|
||||
const env_map &env, const std::string &wd, bool sh)
|
||||
: Popen(cmd, args, env, wd, sh) {
|
||||
open();
|
||||
}
|
||||
|
||||
PopenStream::~PopenStream()
|
||||
{
|
||||
close();
|
||||
PopenStream::~PopenStream() { close(); }
|
||||
|
||||
void PopenStream::open() {
|
||||
Popen::open();
|
||||
|
||||
input.buffer = std::make_unique<stdio_buf>(fd_in, std::ios_base::in);
|
||||
output.buffer = std::make_unique<stdio_buf>(fd_out, std::ios_base::out);
|
||||
|
||||
input.stream = std::make_unique<std::istream>(input.buffer.get());
|
||||
output.stream = std::make_unique<std::ostream>(output.buffer.get());
|
||||
}
|
||||
|
||||
void PopenStream::open()
|
||||
{
|
||||
Popen::open();
|
||||
int PopenStream::close() {
|
||||
int ret = Popen::close();
|
||||
|
||||
input.buffer = std::make_unique<stdio_buf>(fd_in, std::ios_base::in);
|
||||
output.buffer = std::make_unique<stdio_buf>(fd_out, std::ios_base::out);
|
||||
input.stream.reset();
|
||||
output.stream.reset();
|
||||
|
||||
input.stream = std::make_unique<std::istream>(input.buffer.get());
|
||||
output.stream = std::make_unique<std::ostream>(output.buffer.get());
|
||||
}
|
||||
input.buffer.reset();
|
||||
output.buffer.reset();
|
||||
|
||||
int PopenStream::close()
|
||||
{
|
||||
int ret = Popen::close();
|
||||
|
||||
input.stream.reset();
|
||||
output.stream.reset();
|
||||
|
||||
input.buffer.reset();
|
||||
output.buffer.reset();
|
||||
|
||||
return ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
|
|
|
@ -8,138 +8,135 @@
|
|||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include <villas/config.hpp>
|
||||
#include <villas/utils.hpp>
|
||||
#include <villas/table.hpp>
|
||||
#include <villas/colors.hpp>
|
||||
#include <villas/boxes.hpp>
|
||||
#include <villas/colors.hpp>
|
||||
#include <villas/config.hpp>
|
||||
#include <villas/log.hpp>
|
||||
#include <villas/table.hpp>
|
||||
#include <villas/utils.hpp>
|
||||
|
||||
using namespace villas;
|
||||
using namespace villas::utils;
|
||||
|
||||
#if !defined(LOG_COLOR_DISABLE)
|
||||
#define ANSI_RESET "\e[0m"
|
||||
#define ANSI_RESET "\e[0m"
|
||||
#else
|
||||
#define ANSI_RESET
|
||||
#define ANSI_RESET
|
||||
#endif
|
||||
|
||||
int Table::resize(int w)
|
||||
{
|
||||
int norm, flex, fixed, total;
|
||||
int Table::resize(int w) {
|
||||
int norm, flex, fixed, total;
|
||||
|
||||
width = w;
|
||||
width = w;
|
||||
|
||||
norm = 0;
|
||||
flex = 0;
|
||||
fixed = 0;
|
||||
total = width - columns.size() * 2;
|
||||
norm = 0;
|
||||
flex = 0;
|
||||
fixed = 0;
|
||||
total = width - columns.size() * 2;
|
||||
|
||||
// Normalize width
|
||||
for (unsigned i = 0; i < columns.size(); i++) {
|
||||
if (columns[i].width > 0)
|
||||
norm += columns[i].width;
|
||||
if (columns[i].width == 0)
|
||||
flex++;
|
||||
if (columns[i].width < 0)
|
||||
fixed += -1 * columns[i].width;
|
||||
}
|
||||
// Normalize width
|
||||
for (unsigned i = 0; i < columns.size(); i++) {
|
||||
if (columns[i].width > 0)
|
||||
norm += columns[i].width;
|
||||
if (columns[i].width == 0)
|
||||
flex++;
|
||||
if (columns[i].width < 0)
|
||||
fixed += -1 * columns[i].width;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < columns.size(); i++) {
|
||||
if (columns[i].width > 0)
|
||||
columns[i]._width = columns[i].width * (float) (total - fixed) / norm;
|
||||
if (columns[i].width == 0)
|
||||
columns[i]._width = (float) (total - fixed) / flex;
|
||||
if (columns[i].width < 0)
|
||||
columns[i]._width = -1 * columns[i].width;
|
||||
}
|
||||
for (unsigned i = 0; i < columns.size(); i++) {
|
||||
if (columns[i].width > 0)
|
||||
columns[i]._width = columns[i].width * (float)(total - fixed) / norm;
|
||||
if (columns[i].width == 0)
|
||||
columns[i]._width = (float)(total - fixed) / flex;
|
||||
if (columns[i].width < 0)
|
||||
columns[i]._width = -1 * columns[i].width;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Table::header()
|
||||
{
|
||||
if (width != logging.getWidth())
|
||||
resize(logging.getWidth());
|
||||
void Table::header() {
|
||||
if (width != logging.getWidth())
|
||||
resize(logging.getWidth());
|
||||
|
||||
char *line1 = nullptr;
|
||||
char *line2 = nullptr;
|
||||
char *line3 = nullptr;
|
||||
char *line1 = nullptr;
|
||||
char *line2 = nullptr;
|
||||
char *line3 = nullptr;
|
||||
|
||||
for (unsigned i = 0; i < columns.size(); i++) {
|
||||
int w, u;
|
||||
char *col, *unit;
|
||||
for (unsigned i = 0; i < columns.size(); i++) {
|
||||
int w, u;
|
||||
char *col, *unit;
|
||||
|
||||
col = strf(CLR_BLD("%s"), columns[i].title.c_str());
|
||||
unit = columns[i].unit.size() ? strf(CLR_YEL("%s"), columns[i].unit.c_str()) : strf("");
|
||||
col = strf(CLR_BLD("%s"), columns[i].title.c_str());
|
||||
unit = columns[i].unit.size() ? strf(CLR_YEL("%s"), columns[i].unit.c_str())
|
||||
: strf("");
|
||||
|
||||
w = columns[i]._width + strlen(col) - strlenp(col);
|
||||
u = columns[i]._width + strlen(unit) - strlenp(unit);
|
||||
w = columns[i]._width + strlen(col) - strlenp(col);
|
||||
u = columns[i]._width + strlen(unit) - strlenp(unit);
|
||||
|
||||
if (columns[i].align == TableColumn::Alignment::LEFT) {
|
||||
strcatf(&line1, " %-*.*s" ANSI_RESET, w, w, col);
|
||||
strcatf(&line2, " %-*.*s" ANSI_RESET, u, u, unit);
|
||||
}
|
||||
else {
|
||||
strcatf(&line1, " %*.*s" ANSI_RESET, w, w, col);
|
||||
strcatf(&line2, " %*.*s" ANSI_RESET, u, u, unit);
|
||||
}
|
||||
if (columns[i].align == TableColumn::Alignment::LEFT) {
|
||||
strcatf(&line1, " %-*.*s" ANSI_RESET, w, w, col);
|
||||
strcatf(&line2, " %-*.*s" ANSI_RESET, u, u, unit);
|
||||
} else {
|
||||
strcatf(&line1, " %*.*s" ANSI_RESET, w, w, col);
|
||||
strcatf(&line2, " %*.*s" ANSI_RESET, u, u, unit);
|
||||
}
|
||||
|
||||
for (int j = 0; j < columns[i]._width + 2; j++) {
|
||||
strcatf(&line3, "%s", BOX_LR);
|
||||
}
|
||||
for (int j = 0; j < columns[i]._width + 2; j++) {
|
||||
strcatf(&line3, "%s", BOX_LR);
|
||||
}
|
||||
|
||||
if (i != columns.size() - 1) {
|
||||
strcatf(&line1, " %s", BOX_UD);
|
||||
strcatf(&line2, " %s", BOX_UD);
|
||||
strcatf(&line3, "%s", BOX_UDLR);
|
||||
}
|
||||
if (i != columns.size() - 1) {
|
||||
strcatf(&line1, " %s", BOX_UD);
|
||||
strcatf(&line2, " %s", BOX_UD);
|
||||
strcatf(&line3, "%s", BOX_UDLR);
|
||||
}
|
||||
|
||||
free(col);
|
||||
free(unit);
|
||||
}
|
||||
free(col);
|
||||
free(unit);
|
||||
}
|
||||
|
||||
logger->info("{}", line1);
|
||||
logger->info("{}", line2);
|
||||
logger->info("{}", line3);
|
||||
logger->info("{}", line1);
|
||||
logger->info("{}", line2);
|
||||
logger->info("{}", line3);
|
||||
|
||||
free(line1);
|
||||
free(line2);
|
||||
free(line3);
|
||||
free(line1);
|
||||
free(line2);
|
||||
free(line3);
|
||||
}
|
||||
|
||||
void Table::row(int count, ...)
|
||||
{
|
||||
if (width != logging.getWidth()) {
|
||||
resize(logging.getWidth());
|
||||
header();
|
||||
}
|
||||
void Table::row(int count, ...) {
|
||||
if (width != logging.getWidth()) {
|
||||
resize(logging.getWidth());
|
||||
header();
|
||||
}
|
||||
|
||||
va_list args;
|
||||
va_start(args, count);
|
||||
va_list args;
|
||||
va_start(args, count);
|
||||
|
||||
char *line = nullptr;
|
||||
char *line = nullptr;
|
||||
|
||||
for (unsigned i = 0; i < columns.size(); ++i) {
|
||||
char *col = vstrf(columns[i].format.c_str(), args);
|
||||
for (unsigned i = 0; i < columns.size(); ++i) {
|
||||
char *col = vstrf(columns[i].format.c_str(), args);
|
||||
|
||||
int l = strlenp(col);
|
||||
int r = strlen(col);
|
||||
int w = columns[i]._width + r - l;
|
||||
int l = strlenp(col);
|
||||
int r = strlen(col);
|
||||
int w = columns[i]._width + r - l;
|
||||
|
||||
if (columns[i].align == TableColumn::Alignment::LEFT)
|
||||
strcatf(&line, " %-*.*s " ANSI_RESET, w, w, col);
|
||||
else
|
||||
strcatf(&line, " %*.*s " ANSI_RESET, w, w, col);
|
||||
if (columns[i].align == TableColumn::Alignment::LEFT)
|
||||
strcatf(&line, " %-*.*s " ANSI_RESET, w, w, col);
|
||||
else
|
||||
strcatf(&line, " %*.*s " ANSI_RESET, w, w, col);
|
||||
|
||||
if (i != columns.size() - 1)
|
||||
strcatf(&line, BOX_UD);
|
||||
if (i != columns.size() - 1)
|
||||
strcatf(&line, BOX_UD);
|
||||
|
||||
free(col);
|
||||
}
|
||||
free(col);
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
va_end(args);
|
||||
|
||||
logger->info("{}", line);
|
||||
free(line);
|
||||
logger->info("{}", line);
|
||||
free(line);
|
||||
}
|
||||
|
|
|
@ -5,183 +5,167 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <ctime>
|
||||
#include <cerrno>
|
||||
#include <ctime>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <villas/utils.hpp>
|
||||
#include <villas/exceptions.hpp>
|
||||
#include <villas/task.hpp>
|
||||
#include <villas/timing.hpp>
|
||||
#include <villas/exceptions.hpp>
|
||||
#include <villas/utils.hpp>
|
||||
|
||||
using namespace villas;
|
||||
|
||||
#if PERIODIC_TASK_IMPL == TIMERFD
|
||||
#include <sys/timerfd.h>
|
||||
#include <sys/timerfd.h>
|
||||
#endif // PERIODIC_TASK_IMPL
|
||||
|
||||
Task::Task(int clk) :
|
||||
clock(clk)
|
||||
{
|
||||
Task::Task(int clk) : clock(clk) {
|
||||
#if PERIODIC_TASK_IMPL == TIMERFD
|
||||
fd = timerfd_create(clock, 0);
|
||||
if (fd < 0)
|
||||
throw SystemError("Failed to create timerfd");
|
||||
fd = timerfd_create(clock, 0);
|
||||
if (fd < 0)
|
||||
throw SystemError("Failed to create timerfd");
|
||||
#elif PERIODIC_TASK_IMPL == RDTSC
|
||||
int ret = tsc_init(&tsc);
|
||||
if (ret)
|
||||
return ret;
|
||||
int ret = tsc_init(&tsc);
|
||||
if (ret)
|
||||
return ret;
|
||||
#endif // PERIODIC_TASK_IMPL
|
||||
}
|
||||
|
||||
void Task::setTimeout(double to)
|
||||
{
|
||||
struct timespec now;
|
||||
void Task::setTimeout(double to) {
|
||||
struct timespec now;
|
||||
|
||||
clock_gettime(clock, &now);
|
||||
clock_gettime(clock, &now);
|
||||
|
||||
struct timespec timeout = time_from_double(to);
|
||||
struct timespec next = time_add(&now, &timeout);
|
||||
struct timespec timeout = time_from_double(to);
|
||||
struct timespec next = time_add(&now, &timeout);
|
||||
|
||||
setNext(&next);
|
||||
setNext(&next);
|
||||
}
|
||||
|
||||
void Task::setNext(const struct timespec *nxt)
|
||||
{
|
||||
void Task::setNext(const struct timespec *nxt) {
|
||||
|
||||
#if PERIODIC_TASK_IMPL != RDTSC
|
||||
next = *nxt;
|
||||
next = *nxt;
|
||||
|
||||
#if PERIODIC_TASK_IMPL == TIMERFD
|
||||
int ret;
|
||||
struct itimerspec its = {
|
||||
.it_interval = (struct timespec) { 0, 0 },
|
||||
.it_value = next
|
||||
};
|
||||
#if PERIODIC_TASK_IMPL == TIMERFD
|
||||
int ret;
|
||||
struct itimerspec its = {.it_interval = (struct timespec){0, 0},
|
||||
.it_value = next};
|
||||
|
||||
ret = timerfd_settime(fd, TFD_TIMER_ABSTIME, &its, nullptr);
|
||||
if (ret)
|
||||
throw SystemError("Failed to set timerfd");
|
||||
#endif // PERIODIC_TASK_IMPL == TIMERFD
|
||||
ret = timerfd_settime(fd, TFD_TIMER_ABSTIME, &its, nullptr);
|
||||
if (ret)
|
||||
throw SystemError("Failed to set timerfd");
|
||||
#endif // PERIODIC_TASK_IMPL == TIMERFD
|
||||
#endif // PERIODIC_TASK_IMPL != RDTSC
|
||||
}
|
||||
|
||||
void Task::setRate(double rate)
|
||||
{
|
||||
void Task::setRate(double rate) {
|
||||
#if PERIODIC_TASK_IMPL == RDTSC
|
||||
period = tsc_rate_to_cycles(&tsc, rate);
|
||||
next = tsc_now(&tsc) + period;
|
||||
period = tsc_rate_to_cycles(&tsc, rate);
|
||||
next = tsc_now(&tsc) + period;
|
||||
#else
|
||||
// A rate of 0 will disarm the timer
|
||||
period = rate ? time_from_double(1.0 / rate) : (struct timespec) { 0, 0 };
|
||||
// A rate of 0 will disarm the timer
|
||||
period = rate ? time_from_double(1.0 / rate) : (struct timespec){0, 0};
|
||||
|
||||
#if PERIODIC_TASK_IMPL == CLOCK_NANOSLEEP || PERIODIC_TASK_IMPL == NANOSLEEP
|
||||
struct timespec now, next;
|
||||
#if PERIODIC_TASK_IMPL == CLOCK_NANOSLEEP || PERIODIC_TASK_IMPL == NANOSLEEP
|
||||
struct timespec now, next;
|
||||
|
||||
clock_gettime(clock, &now);
|
||||
clock_gettime(clock, &now);
|
||||
|
||||
next = time_add(&now, &period);
|
||||
next = time_add(&now, &period);
|
||||
|
||||
return setNext(&next);
|
||||
#elif PERIODIC_TASK_IMPL == TIMERFD
|
||||
int ret;
|
||||
struct itimerspec its = {
|
||||
.it_interval = period,
|
||||
.it_value = period
|
||||
};
|
||||
return setNext(&next);
|
||||
#elif PERIODIC_TASK_IMPL == TIMERFD
|
||||
int ret;
|
||||
struct itimerspec its = {.it_interval = period, .it_value = period};
|
||||
|
||||
ret = timerfd_settime(fd, 0, &its, nullptr);
|
||||
if (ret)
|
||||
throw SystemError("Failed to set timerfd");
|
||||
#endif // PERIODIC_TASK_IMPL
|
||||
ret = timerfd_settime(fd, 0, &its, nullptr);
|
||||
if (ret)
|
||||
throw SystemError("Failed to set timerfd");
|
||||
#endif // PERIODIC_TASK_IMPL
|
||||
#endif // PERIODIC_TASK_IMPL == RDTSC
|
||||
}
|
||||
|
||||
Task::~Task()
|
||||
{
|
||||
Task::~Task() {
|
||||
#if PERIODIC_TASK_IMPL == TIMERFD
|
||||
close(fd);
|
||||
close(fd);
|
||||
#endif
|
||||
}
|
||||
|
||||
uint64_t Task::wait()
|
||||
{
|
||||
uint64_t runs;
|
||||
uint64_t Task::wait() {
|
||||
uint64_t runs;
|
||||
|
||||
#if PERIODIC_TASK_IMPL == CLOCK_NANOSLEEP || PERIODIC_TASK_IMPL == NANOSLEEP
|
||||
int ret;
|
||||
struct timespec now;
|
||||
int ret;
|
||||
struct timespec now;
|
||||
|
||||
ret = clock_gettime(clock, &now);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = clock_gettime(clock, &now);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (runs = 0; time_cmp(&next, &now) < 0; runs++)
|
||||
next = time_add(&next, &period);
|
||||
for (runs = 0; time_cmp(&next, &now) < 0; runs++)
|
||||
next = time_add(&next, &period);
|
||||
|
||||
#if PERIODIC_TASK_IMPL == CLOCK_NANOSLEEP
|
||||
do {
|
||||
ret = clock_nanosleep(clock, TIMER_ABSTIME, &next, nullptr);
|
||||
} while (ret == EINTR);
|
||||
#elif PERIODIC_TASK_IMPL == NANOSLEEP
|
||||
struct timespec req, rem = time_diff(&now, &next);
|
||||
#if PERIODIC_TASK_IMPL == CLOCK_NANOSLEEP
|
||||
do {
|
||||
ret = clock_nanosleep(clock, TIMER_ABSTIME, &next, nullptr);
|
||||
} while (ret == EINTR);
|
||||
#elif PERIODIC_TASK_IMPL == NANOSLEEP
|
||||
struct timespec req, rem = time_diff(&now, &next);
|
||||
|
||||
do {
|
||||
req = rem;
|
||||
ret = nanosleep(&req, &rem);
|
||||
} while (ret < 0 && errno == EINTR);
|
||||
#endif
|
||||
if (ret)
|
||||
return 0;
|
||||
do {
|
||||
req = rem;
|
||||
ret = nanosleep(&req, &rem);
|
||||
} while (ret < 0 && errno == EINTR);
|
||||
#endif
|
||||
if (ret)
|
||||
return 0;
|
||||
|
||||
ret = clock_gettime(clock, &now);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = clock_gettime(clock, &now);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (; time_cmp(&next, &now) < 0; runs++)
|
||||
next = time_add(&next, &period);
|
||||
for (; time_cmp(&next, &now) < 0; runs++)
|
||||
next = time_add(&next, &period);
|
||||
#elif PERIODIC_TASK_IMPL == TIMERFD
|
||||
int ret;
|
||||
int ret;
|
||||
|
||||
ret = read(fd, &runs, sizeof(runs));
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
ret = read(fd, &runs, sizeof(runs));
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
#elif PERIODIC_TASK_IMPL == RDTSC
|
||||
uint64_t now;
|
||||
uint64_t now;
|
||||
|
||||
do {
|
||||
now = tsc_now(&tsc);
|
||||
} while (now < next);
|
||||
do {
|
||||
now = tsc_now(&tsc);
|
||||
} while (now < next);
|
||||
|
||||
for (runs = 0; next < now; runs++)
|
||||
next += period;
|
||||
for (runs = 0; next < now; runs++)
|
||||
next += period;
|
||||
#else
|
||||
#error "Invalid period task implementation"
|
||||
#error "Invalid period task implementation"
|
||||
#endif
|
||||
|
||||
return runs;
|
||||
return runs;
|
||||
}
|
||||
|
||||
void Task::stop()
|
||||
{
|
||||
void Task::stop() {
|
||||
#if PERIODIC_TASK_IMPL == TIMERFD
|
||||
int ret;
|
||||
struct itimerspec its = {
|
||||
.it_interval = (struct timespec) { 0, 0 },
|
||||
.it_value = (struct timespec) { 0, 0 }
|
||||
};
|
||||
int ret;
|
||||
struct itimerspec its = {.it_interval = (struct timespec){0, 0},
|
||||
.it_value = (struct timespec){0, 0}};
|
||||
|
||||
ret = timerfd_settime(fd, 0, &its, nullptr);
|
||||
if (ret)
|
||||
throw SystemError("Failed to disarm timerfd");
|
||||
ret = timerfd_settime(fd, 0, &its, nullptr);
|
||||
if (ret)
|
||||
throw SystemError("Failed to disarm timerfd");
|
||||
#endif // PERIODIC_TASK_IMPL == TIMERFD
|
||||
}
|
||||
|
||||
int Task::getFD() const
|
||||
{
|
||||
int Task::getFD() const {
|
||||
#if PERIODIC_TASK_IMPL == TIMERFD
|
||||
return fd;
|
||||
return fd;
|
||||
#else
|
||||
return -1;
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -7,64 +7,64 @@
|
|||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <villas/terminal.hpp>
|
||||
#include <villas/exceptions.hpp>
|
||||
#include <villas/log.hpp>
|
||||
#include <villas/terminal.hpp>
|
||||
|
||||
using namespace villas;
|
||||
|
||||
class Terminal * Terminal::current = nullptr;
|
||||
class Terminal *Terminal::current = nullptr;
|
||||
|
||||
Terminal::Terminal()
|
||||
{
|
||||
int ret;
|
||||
Terminal::Terminal() {
|
||||
int ret;
|
||||
|
||||
window.ws_row = 0;
|
||||
window.ws_col = 0;
|
||||
window.ws_row = 0;
|
||||
window.ws_col = 0;
|
||||
|
||||
isTty = isatty(STDERR_FILENO);
|
||||
isTty = isatty(STDERR_FILENO);
|
||||
|
||||
Logger logger = logging.get("terminal");
|
||||
Logger logger = logging.get("terminal");
|
||||
|
||||
if (isTty) {
|
||||
struct sigaction sa_resize;
|
||||
sa_resize.sa_flags = SA_SIGINFO;
|
||||
sa_resize.sa_sigaction = resize;
|
||||
if (isTty) {
|
||||
struct sigaction sa_resize;
|
||||
sa_resize.sa_flags = SA_SIGINFO;
|
||||
sa_resize.sa_sigaction = resize;
|
||||
|
||||
sigemptyset(&sa_resize.sa_mask);
|
||||
sigemptyset(&sa_resize.sa_mask);
|
||||
|
||||
ret = sigaction(SIGWINCH, &sa_resize, nullptr);
|
||||
if (ret)
|
||||
throw SystemError("Failed to register signal handler");
|
||||
ret = sigaction(SIGWINCH, &sa_resize, nullptr);
|
||||
if (ret)
|
||||
throw SystemError("Failed to register signal handler");
|
||||
|
||||
// Try to get initial terminal dimensions
|
||||
ret = ioctl(STDERR_FILENO, TIOCGWINSZ, &window);
|
||||
if (ret)
|
||||
logger->warn("Failed to get terminal dimensions");
|
||||
} else {
|
||||
logger->info("stderr is not associated with a terminal! Using fallback values for window size...");
|
||||
}
|
||||
// Try to get initial terminal dimensions
|
||||
ret = ioctl(STDERR_FILENO, TIOCGWINSZ, &window);
|
||||
if (ret)
|
||||
logger->warn("Failed to get terminal dimensions");
|
||||
} else {
|
||||
logger->info("stderr is not associated with a terminal! Using fallback "
|
||||
"values for window size...");
|
||||
}
|
||||
|
||||
// Fallback if for some reason we can not determine a prober window size
|
||||
if (window.ws_col == 0)
|
||||
window.ws_col = 150;
|
||||
// Fallback if for some reason we can not determine a prober window size
|
||||
if (window.ws_col == 0)
|
||||
window.ws_col = 150;
|
||||
|
||||
if (window.ws_row == 0)
|
||||
window.ws_row = 50;
|
||||
if (window.ws_row == 0)
|
||||
window.ws_row = 50;
|
||||
}
|
||||
|
||||
void Terminal::resize(int, siginfo_t *, void *)
|
||||
{
|
||||
if (!current)
|
||||
current = new Terminal();
|
||||
void Terminal::resize(int, siginfo_t *, void *) {
|
||||
if (!current)
|
||||
current = new Terminal();
|
||||
|
||||
Logger logger = logging.get("terminal");
|
||||
Logger logger = logging.get("terminal");
|
||||
|
||||
int ret;
|
||||
int ret;
|
||||
|
||||
ret = ioctl(STDERR_FILENO, TIOCGWINSZ, ¤t->window);
|
||||
if (ret)
|
||||
throw SystemError("Failed to get terminal dimensions");
|
||||
ret = ioctl(STDERR_FILENO, TIOCGWINSZ, ¤t->window);
|
||||
if (ret)
|
||||
throw SystemError("Failed to get terminal dimensions");
|
||||
|
||||
logger->debug("New terminal size: {}x{}", current->window.ws_row, current->window.ws_col);
|
||||
logger->debug("New terminal size: {}x{}", current->window.ws_row,
|
||||
current->window.ws_col);
|
||||
};
|
||||
|
|
|
@ -9,71 +9,62 @@
|
|||
|
||||
#include <villas/timing.hpp>
|
||||
|
||||
struct timespec time_now()
|
||||
{
|
||||
struct timespec ts;
|
||||
struct timespec time_now() {
|
||||
struct timespec ts;
|
||||
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
|
||||
return ts;
|
||||
return ts;
|
||||
}
|
||||
|
||||
struct timespec time_add(const struct timespec *start, const struct timespec *end)
|
||||
{
|
||||
struct timespec sum = {
|
||||
.tv_sec = end->tv_sec + start->tv_sec,
|
||||
.tv_nsec = end->tv_nsec + start->tv_nsec
|
||||
};
|
||||
struct timespec time_add(const struct timespec *start,
|
||||
const struct timespec *end) {
|
||||
struct timespec sum = {.tv_sec = end->tv_sec + start->tv_sec,
|
||||
.tv_nsec = end->tv_nsec + start->tv_nsec};
|
||||
|
||||
if (sum.tv_nsec >= 1000000000) {
|
||||
sum.tv_sec += 1;
|
||||
sum.tv_nsec -= 1000000000;
|
||||
}
|
||||
if (sum.tv_nsec >= 1000000000) {
|
||||
sum.tv_sec += 1;
|
||||
sum.tv_nsec -= 1000000000;
|
||||
}
|
||||
|
||||
return sum;
|
||||
return sum;
|
||||
}
|
||||
|
||||
struct timespec time_diff(const struct timespec *start, const struct timespec *end)
|
||||
{
|
||||
struct timespec diff = {
|
||||
.tv_sec = end->tv_sec - start->tv_sec,
|
||||
.tv_nsec = end->tv_nsec - start->tv_nsec
|
||||
};
|
||||
struct timespec time_diff(const struct timespec *start,
|
||||
const struct timespec *end) {
|
||||
struct timespec diff = {.tv_sec = end->tv_sec - start->tv_sec,
|
||||
.tv_nsec = end->tv_nsec - start->tv_nsec};
|
||||
|
||||
if (diff.tv_nsec < 0) {
|
||||
diff.tv_sec -= 1;
|
||||
diff.tv_nsec += 1000000000;
|
||||
}
|
||||
if (diff.tv_nsec < 0) {
|
||||
diff.tv_sec -= 1;
|
||||
diff.tv_nsec += 1000000000;
|
||||
}
|
||||
|
||||
return diff;
|
||||
return diff;
|
||||
}
|
||||
|
||||
struct timespec time_from_double(double secs)
|
||||
{
|
||||
struct timespec ts;
|
||||
struct timespec time_from_double(double secs) {
|
||||
struct timespec ts;
|
||||
|
||||
ts.tv_sec = secs;
|
||||
ts.tv_nsec = 1.0e9 * (secs - ts.tv_sec);
|
||||
ts.tv_sec = secs;
|
||||
ts.tv_nsec = 1.0e9 * (secs - ts.tv_sec);
|
||||
|
||||
return ts;
|
||||
return ts;
|
||||
}
|
||||
|
||||
double time_to_double(const struct timespec *ts)
|
||||
{
|
||||
return ts->tv_sec + ts->tv_nsec * 1e-9;
|
||||
double time_to_double(const struct timespec *ts) {
|
||||
return ts->tv_sec + ts->tv_nsec * 1e-9;
|
||||
}
|
||||
|
||||
double time_delta(const struct timespec *start, const struct timespec *end)
|
||||
{
|
||||
struct timespec diff = time_diff(start, end);
|
||||
double time_delta(const struct timespec *start, const struct timespec *end) {
|
||||
struct timespec diff = time_diff(start, end);
|
||||
|
||||
return time_to_double(&diff);
|
||||
return time_to_double(&diff);
|
||||
}
|
||||
|
||||
ssize_t time_cmp(const struct timespec *a, const struct timespec *b)
|
||||
{
|
||||
ssize_t sd = a->tv_sec - b->tv_sec;
|
||||
ssize_t nsd = a->tv_nsec - b->tv_nsec;
|
||||
ssize_t time_cmp(const struct timespec *a, const struct timespec *b) {
|
||||
ssize_t sd = a->tv_sec - b->tv_sec;
|
||||
ssize_t nsd = a->tv_nsec - b->tv_nsec;
|
||||
|
||||
return sd != 0 ? sd : nsd;
|
||||
return sd != 0 ? sd : nsd;
|
||||
}
|
||||
|
|
|
@ -11,63 +11,57 @@
|
|||
|
||||
using namespace villas;
|
||||
|
||||
void Tool::staticHandler(int signal, siginfo_t *sinfo, void *ctx)
|
||||
{
|
||||
if (current_tool)
|
||||
current_tool->handler(signal, sinfo, ctx);
|
||||
void Tool::staticHandler(int signal, siginfo_t *sinfo, void *ctx) {
|
||||
if (current_tool)
|
||||
current_tool->handler(signal, sinfo, ctx);
|
||||
}
|
||||
|
||||
void Tool::printCopyright()
|
||||
{
|
||||
std::cout << PROJECT_NAME " " << CLR_BLU(PROJECT_BUILD_ID)
|
||||
<< " (built on " CLR_MAG(__DATE__) " " CLR_MAG(__TIME__) ")" << std::endl
|
||||
<< " Copyright 2014-2021, Institute for Automation of Complex Power Systems, RWTH Aachen University" << std::endl
|
||||
<< " Steffen Vogel <post@steffenvogel.de>" << std::endl;
|
||||
void Tool::printCopyright() {
|
||||
std::cout << PROJECT_NAME " " << CLR_BLU(PROJECT_BUILD_ID)
|
||||
<< " (built on " CLR_MAG(__DATE__) " " CLR_MAG(__TIME__) ")"
|
||||
<< std::endl
|
||||
<< " Copyright 2014-2021, Institute for Automation of Complex "
|
||||
"Power Systems, RWTH Aachen University"
|
||||
<< std::endl
|
||||
<< " Steffen Vogel <post@steffenvogel.de>" << std::endl;
|
||||
}
|
||||
|
||||
void Tool::printVersion()
|
||||
{
|
||||
std::cout << PROJECT_BUILD_ID << std::endl;
|
||||
void Tool::printVersion() { std::cout << PROJECT_BUILD_ID << std::endl; }
|
||||
|
||||
Tool::Tool(int ac, char *av[], const std::string &nme,
|
||||
const std::list<int> &sigs)
|
||||
: argc(ac), argv(av), name(nme), handlerSignals(sigs) {
|
||||
current_tool = this;
|
||||
|
||||
logger = logging.get(name);
|
||||
}
|
||||
|
||||
Tool::Tool(int ac, char *av[], const std::string &nme, const std::list<int> &sigs) :
|
||||
argc(ac),
|
||||
argv(av),
|
||||
name(nme),
|
||||
handlerSignals(sigs)
|
||||
{
|
||||
current_tool = this;
|
||||
int Tool::run() {
|
||||
try {
|
||||
int ret;
|
||||
|
||||
logger = logging.get(name);
|
||||
}
|
||||
logger->info("This is VILLASnode {} (built on {}, {})",
|
||||
CLR_BLD(CLR_YEL(PROJECT_BUILD_ID)), CLR_BLD(CLR_MAG(__DATE__)),
|
||||
CLR_BLD(CLR_MAG(__TIME__)));
|
||||
|
||||
int Tool::run()
|
||||
{
|
||||
try {
|
||||
int ret;
|
||||
ret = utils::signalsInit(staticHandler, handlerSignals);
|
||||
if (ret)
|
||||
throw RuntimeError("Failed to initialize signal subsystem");
|
||||
|
||||
logger->info("This is VILLASnode {} (built on {}, {})",
|
||||
CLR_BLD(CLR_YEL(PROJECT_BUILD_ID)),
|
||||
CLR_BLD(CLR_MAG(__DATE__)), CLR_BLD(CLR_MAG(__TIME__)));
|
||||
// Parse command line arguments
|
||||
parse();
|
||||
|
||||
ret = utils::signalsInit(staticHandler, handlerSignals);
|
||||
if (ret)
|
||||
throw RuntimeError("Failed to initialize signal subsystem");
|
||||
// Run tool
|
||||
ret = main();
|
||||
|
||||
// Parse command line arguments
|
||||
parse();
|
||||
logger->info(CLR_GRN("Goodbye!"));
|
||||
|
||||
// Run tool
|
||||
ret = main();
|
||||
return ret;
|
||||
} catch (const std::runtime_error &e) {
|
||||
logger->error("{}", e.what());
|
||||
|
||||
logger->info(CLR_GRN("Goodbye!"));
|
||||
|
||||
return ret;
|
||||
} catch (const std::runtime_error &e) {
|
||||
logger->error("{}", e.what());
|
||||
|
||||
return -1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
Tool *Tool::current_tool = nullptr;
|
||||
|
|
|
@ -9,42 +9,40 @@
|
|||
|
||||
using namespace villas;
|
||||
|
||||
int tsc_init(struct Tsc *t)
|
||||
{
|
||||
uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0;
|
||||
int tsc_init(struct Tsc *t) {
|
||||
uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0;
|
||||
|
||||
// Check if TSC is supported
|
||||
__get_cpuid(0x1, &eax, &ebx, &ecx, &edx);
|
||||
if (!(edx & bit_TSC))
|
||||
return -2;
|
||||
// Check if TSC is supported
|
||||
__get_cpuid(0x1, &eax, &ebx, &ecx, &edx);
|
||||
if (!(edx & bit_TSC))
|
||||
return -2;
|
||||
|
||||
// Check if RDTSCP instruction is supported
|
||||
__get_cpuid(0x80000001, &eax, &ebx, &ecx, &edx);
|
||||
t->rdtscp_supported = edx & bit_RDTSCP;
|
||||
// Check if RDTSCP instruction is supported
|
||||
__get_cpuid(0x80000001, &eax, &ebx, &ecx, &edx);
|
||||
t->rdtscp_supported = edx & bit_RDTSCP;
|
||||
|
||||
// Check if TSC is invariant
|
||||
__get_cpuid(0x80000007, &eax, &ebx, &ecx, &edx);
|
||||
t->is_invariant = edx & bit_TSC_INVARIANT;
|
||||
// Check if TSC is invariant
|
||||
__get_cpuid(0x80000007, &eax, &ebx, &ecx, &edx);
|
||||
t->is_invariant = edx & bit_TSC_INVARIANT;
|
||||
|
||||
// Intel SDM Vol 3, Section 18.7.3:
|
||||
// Nominal TSC frequency = CPUID.15H.ECX[31:0] * CPUID.15H.EBX[31:0] ) ÷ CPUID.15H.EAX[31:0]
|
||||
__get_cpuid(0x15, &eax, &ebx, &ecx, &edx);
|
||||
// Intel SDM Vol 3, Section 18.7.3:
|
||||
// Nominal TSC frequency = CPUID.15H.ECX[31:0] * CPUID.15H.EBX[31:0] ) ÷ CPUID.15H.EAX[31:0]
|
||||
__get_cpuid(0x15, &eax, &ebx, &ecx, &edx);
|
||||
|
||||
if (ecx != 0)
|
||||
t->frequency = ecx * ebx / eax;
|
||||
else {
|
||||
int ret;
|
||||
if (ecx != 0)
|
||||
t->frequency = ecx * ebx / eax;
|
||||
else {
|
||||
int ret;
|
||||
#ifdef __linux__
|
||||
ret = kernel::get_cpu_frequency(&t->frequency);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = kernel::get_cpu_frequency(&t->frequency);
|
||||
if (ret)
|
||||
return ret;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t tsc_rate_to_cycles(struct Tsc *t, double rate)
|
||||
{
|
||||
return t->frequency / rate;
|
||||
uint64_t tsc_rate_to_cycles(struct Tsc *t, double rate) {
|
||||
return t->frequency / rate;
|
||||
}
|
||||
|
|
|
@ -6,374 +6,349 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <cstdint>
|
||||
#include <cstdarg>
|
||||
#include <cstring>
|
||||
#include <unistd.h>
|
||||
#include <cmath>
|
||||
#include <pthread.h>
|
||||
#include <cctype>
|
||||
#include <cmath>
|
||||
#include <cstdarg>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <jansson.h>
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/buffer.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <jansson.h>
|
||||
|
||||
#include <villas/config.hpp>
|
||||
#include <villas/utils.hpp>
|
||||
#include <villas/colors.hpp>
|
||||
#include <villas/config.hpp>
|
||||
#include <villas/exceptions.hpp>
|
||||
#include <villas/log.hpp>
|
||||
#include <villas/utils.hpp>
|
||||
|
||||
static
|
||||
pthread_t main_thread;
|
||||
static pthread_t main_thread;
|
||||
|
||||
namespace villas {
|
||||
namespace utils {
|
||||
|
||||
std::vector<std::string> tokenize(std::string s, const std::string &delimiter)
|
||||
{
|
||||
std::vector<std::string> tokens;
|
||||
std::vector<std::string> tokenize(std::string s, const std::string &delimiter) {
|
||||
std::vector<std::string> tokens;
|
||||
|
||||
size_t lastPos = 0;
|
||||
size_t curentPos;
|
||||
size_t lastPos = 0;
|
||||
size_t curentPos;
|
||||
|
||||
while ((curentPos = s.find(delimiter, lastPos)) != std::string::npos) {
|
||||
const size_t tokenLength = curentPos - lastPos;
|
||||
tokens.push_back(s.substr(lastPos, tokenLength));
|
||||
while ((curentPos = s.find(delimiter, lastPos)) != std::string::npos) {
|
||||
const size_t tokenLength = curentPos - lastPos;
|
||||
tokens.push_back(s.substr(lastPos, tokenLength));
|
||||
|
||||
// Advance in string
|
||||
lastPos = curentPos + delimiter.length();
|
||||
}
|
||||
// Advance in string
|
||||
lastPos = curentPos + delimiter.length();
|
||||
}
|
||||
|
||||
// Check if there's a last token behind the last delimiter.
|
||||
if (lastPos != s.length()) {
|
||||
const size_t lastTokenLength = s.length() - lastPos;
|
||||
tokens.push_back(s.substr(lastPos, lastTokenLength));
|
||||
}
|
||||
// Check if there's a last token behind the last delimiter.
|
||||
if (lastPos != s.length()) {
|
||||
const size_t lastTokenLength = s.length() - lastPos;
|
||||
tokens.push_back(s.substr(lastPos, lastTokenLength));
|
||||
}
|
||||
|
||||
return tokens;
|
||||
return tokens;
|
||||
}
|
||||
|
||||
ssize_t readRandom(char *buf, size_t len)
|
||||
{
|
||||
int fd;
|
||||
ssize_t bytes = -1;
|
||||
ssize_t readRandom(char *buf, size_t len) {
|
||||
int fd;
|
||||
ssize_t bytes = -1;
|
||||
|
||||
fd = open("/dev/urandom", O_RDONLY);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
fd = open("/dev/urandom", O_RDONLY);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
while (len) {
|
||||
bytes = read(fd, buf, len);
|
||||
if (bytes < 0)
|
||||
break;
|
||||
while (len) {
|
||||
bytes = read(fd, buf, len);
|
||||
if (bytes < 0)
|
||||
break;
|
||||
|
||||
len -= bytes;
|
||||
buf += bytes;
|
||||
}
|
||||
len -= bytes;
|
||||
buf += bytes;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
close(fd);
|
||||
|
||||
return bytes;
|
||||
return bytes;
|
||||
}
|
||||
|
||||
// Setup exit handler
|
||||
int signalsInit(void (*cb)(int signal, siginfo_t *sinfo, void *ctx), std::list<int> cbSignals, std::list<int> ignoreSignals)
|
||||
{
|
||||
int ret;
|
||||
int signalsInit(void (*cb)(int signal, siginfo_t *sinfo, void *ctx),
|
||||
std::list<int> cbSignals, std::list<int> ignoreSignals) {
|
||||
int ret;
|
||||
|
||||
Logger logger = logging.get("signals");
|
||||
Logger logger = logging.get("signals");
|
||||
|
||||
logger->info("Initialize subsystem");
|
||||
logger->info("Initialize subsystem");
|
||||
|
||||
struct sigaction sa_cb;
|
||||
sa_cb.sa_flags = SA_SIGINFO | SA_NODEFER;
|
||||
sa_cb.sa_sigaction = cb;
|
||||
struct sigaction sa_cb;
|
||||
sa_cb.sa_flags = SA_SIGINFO | SA_NODEFER;
|
||||
sa_cb.sa_sigaction = cb;
|
||||
|
||||
struct sigaction sa_ign;
|
||||
sa_ign.sa_flags = 0;
|
||||
sa_ign.sa_handler = SIG_IGN;
|
||||
struct sigaction sa_ign;
|
||||
sa_ign.sa_flags = 0;
|
||||
sa_ign.sa_handler = SIG_IGN;
|
||||
|
||||
main_thread = pthread_self();
|
||||
main_thread = pthread_self();
|
||||
|
||||
sigemptyset(&sa_cb.sa_mask);
|
||||
sigemptyset(&sa_ign.sa_mask);
|
||||
sigemptyset(&sa_cb.sa_mask);
|
||||
sigemptyset(&sa_ign.sa_mask);
|
||||
|
||||
cbSignals.insert(cbSignals.begin(), { SIGINT, SIGTERM, SIGUSR1, SIGUSR2, SIGALRM });
|
||||
cbSignals.sort();
|
||||
cbSignals.unique();
|
||||
cbSignals.insert(cbSignals.begin(),
|
||||
{SIGINT, SIGTERM, SIGUSR1, SIGUSR2, SIGALRM});
|
||||
cbSignals.sort();
|
||||
cbSignals.unique();
|
||||
|
||||
for (auto signal : cbSignals) {
|
||||
ret = sigaction(signal, &sa_cb, nullptr);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
for (auto signal : cbSignals) {
|
||||
ret = sigaction(signal, &sa_cb, nullptr);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (auto signal : ignoreSignals) {
|
||||
ret = sigaction(signal, &sa_ign, nullptr);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
for (auto signal : ignoreSignals) {
|
||||
ret = sigaction(signal, &sa_ign, nullptr);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
char * decolor(char *str)
|
||||
{
|
||||
char *p, *q;
|
||||
bool inseq = false;
|
||||
char *decolor(char *str) {
|
||||
char *p, *q;
|
||||
bool inseq = false;
|
||||
|
||||
for (p = q = str; *p; p++) {
|
||||
switch (*p) {
|
||||
case 0x1b:
|
||||
if (*(++p) == '[') {
|
||||
inseq = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
for (p = q = str; *p; p++) {
|
||||
switch (*p) {
|
||||
case 0x1b:
|
||||
if (*(++p) == '[') {
|
||||
inseq = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'm':
|
||||
if (inseq) {
|
||||
inseq = false;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'm':
|
||||
if (inseq) {
|
||||
inseq = false;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!inseq) {
|
||||
*q = *p;
|
||||
q++;
|
||||
}
|
||||
}
|
||||
if (!inseq) {
|
||||
*q = *p;
|
||||
q++;
|
||||
}
|
||||
}
|
||||
|
||||
*q = '\0';
|
||||
*q = '\0';
|
||||
|
||||
return str;
|
||||
return str;
|
||||
}
|
||||
|
||||
void killme(int sig)
|
||||
{
|
||||
// Send only to main thread in case the ID was initilized by signalsInit()
|
||||
if (main_thread)
|
||||
pthread_kill(main_thread, sig);
|
||||
else
|
||||
kill(0, sig);
|
||||
void killme(int sig) {
|
||||
// Send only to main thread in case the ID was initilized by signalsInit()
|
||||
if (main_thread)
|
||||
pthread_kill(main_thread, sig);
|
||||
else
|
||||
kill(0, sig);
|
||||
}
|
||||
|
||||
double boxMuller(float m, float s)
|
||||
{
|
||||
double x1, x2, y1;
|
||||
static
|
||||
double y2;
|
||||
static
|
||||
int use_last = 0;
|
||||
double boxMuller(float m, float s) {
|
||||
double x1, x2, y1;
|
||||
static double y2;
|
||||
static int use_last = 0;
|
||||
|
||||
if (use_last) { // Use value from previous call
|
||||
y1 = y2;
|
||||
use_last = 0;
|
||||
}
|
||||
else {
|
||||
double w;
|
||||
do {
|
||||
x1 = 2.0 * randf() - 1.0;
|
||||
x2 = 2.0 * randf() - 1.0;
|
||||
w = x1*x1 + x2*x2;
|
||||
} while (w >= 1.0);
|
||||
if (use_last) { // Use value from previous call
|
||||
y1 = y2;
|
||||
use_last = 0;
|
||||
} else {
|
||||
double w;
|
||||
do {
|
||||
x1 = 2.0 * randf() - 1.0;
|
||||
x2 = 2.0 * randf() - 1.0;
|
||||
w = x1 * x1 + x2 * x2;
|
||||
} while (w >= 1.0);
|
||||
|
||||
w = sqrt(-2.0 * log(w) / w);
|
||||
y1 = x1 * w;
|
||||
y2 = x2 * w;
|
||||
use_last = 1;
|
||||
}
|
||||
w = sqrt(-2.0 * log(w) / w);
|
||||
y1 = x1 * w;
|
||||
y2 = x2 * w;
|
||||
use_last = 1;
|
||||
}
|
||||
|
||||
return m + y1 * s;
|
||||
return m + y1 * s;
|
||||
}
|
||||
|
||||
double randf()
|
||||
{
|
||||
return (double) random() / RAND_MAX;
|
||||
double randf() { return (double)random() / RAND_MAX; }
|
||||
|
||||
char *vstrcatf(char **dest, const char *fmt, va_list ap) {
|
||||
char *tmp;
|
||||
int n = *dest ? strlen(*dest) : 0;
|
||||
int i = vasprintf(&tmp, fmt, ap);
|
||||
|
||||
*dest = (char *)(realloc(*dest, n + i + 1));
|
||||
if (*dest != nullptr)
|
||||
strncpy(*dest + n, tmp, i + 1);
|
||||
|
||||
free(tmp);
|
||||
|
||||
return *dest;
|
||||
}
|
||||
|
||||
char * vstrcatf(char **dest, const char *fmt, va_list ap)
|
||||
{
|
||||
char *tmp;
|
||||
int n = *dest ? strlen(*dest) : 0;
|
||||
int i = vasprintf(&tmp, fmt, ap);
|
||||
char *strcatf(char **dest, const char *fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vstrcatf(dest, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
*dest = (char *)(realloc(*dest, n + i + 1));
|
||||
if (*dest != nullptr)
|
||||
strncpy(*dest+n, tmp, i + 1);
|
||||
|
||||
free(tmp);
|
||||
|
||||
return *dest;
|
||||
return *dest;
|
||||
}
|
||||
|
||||
char * strcatf(char **dest, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vstrcatf(dest, fmt, ap);
|
||||
va_end(ap);
|
||||
char *strf(const char *fmt, ...) {
|
||||
char *buf = nullptr;
|
||||
|
||||
return *dest;
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vstrcatf(&buf, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
char * strf(const char *fmt, ...)
|
||||
{
|
||||
char *buf = nullptr;
|
||||
char *vstrf(const char *fmt, va_list va) {
|
||||
char *buf = nullptr;
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vstrcatf(&buf, fmt, ap);
|
||||
va_end(ap);
|
||||
vstrcatf(&buf, fmt, va);
|
||||
|
||||
return buf;
|
||||
return buf;
|
||||
}
|
||||
|
||||
char * vstrf(const char *fmt, va_list va)
|
||||
{
|
||||
char *buf = nullptr;
|
||||
void *memdup(const void *src, size_t bytes) {
|
||||
void *dst = new char[bytes];
|
||||
if (!dst)
|
||||
throw MemoryAllocationError();
|
||||
|
||||
vstrcatf(&buf, fmt, va);
|
||||
memcpy(dst, src, bytes);
|
||||
|
||||
return buf;
|
||||
return dst;
|
||||
}
|
||||
|
||||
void * memdup(const void *src, size_t bytes)
|
||||
{
|
||||
void *dst = new char[bytes];
|
||||
if (!dst)
|
||||
throw MemoryAllocationError();
|
||||
pid_t spawn(const char *name, char *const argv[]) {
|
||||
pid_t pid;
|
||||
|
||||
memcpy(dst, src, bytes);
|
||||
pid = fork();
|
||||
switch (pid) {
|
||||
case -1:
|
||||
return -1;
|
||||
case 0:
|
||||
return execvp(name, (char *const *)argv);
|
||||
}
|
||||
|
||||
return dst;
|
||||
return pid;
|
||||
}
|
||||
|
||||
pid_t spawn(const char* name, char *const argv[])
|
||||
{
|
||||
pid_t pid;
|
||||
size_t strlenp(const char *str) {
|
||||
size_t sz = 0;
|
||||
|
||||
pid = fork();
|
||||
switch (pid) {
|
||||
case -1: return -1;
|
||||
case 0: return execvp(name, (char * const*) argv);
|
||||
}
|
||||
for (const char *d = str; *d; d++) {
|
||||
const unsigned char *c = (const unsigned char *)d;
|
||||
|
||||
return pid;
|
||||
}
|
||||
if (isprint(*c))
|
||||
sz++;
|
||||
else if (c[0] == '\b')
|
||||
sz--;
|
||||
else if (c[0] == '\t')
|
||||
sz += 4; // Tab width == 4
|
||||
// CSI sequence
|
||||
else if (c[0] == '\e' && c[1] == '[') {
|
||||
c += 2;
|
||||
while (*c && *c != 'm')
|
||||
c++;
|
||||
}
|
||||
// UTF-8
|
||||
else if (c[0] >= 0xc2 && c[0] <= 0xdf) {
|
||||
sz++;
|
||||
c += 1;
|
||||
} else if (c[0] >= 0xe0 && c[0] <= 0xef) {
|
||||
sz++;
|
||||
c += 2;
|
||||
} else if (c[0] >= 0xf0 && c[0] <= 0xf4) {
|
||||
sz++;
|
||||
c += 3;
|
||||
}
|
||||
|
||||
size_t strlenp(const char *str)
|
||||
{
|
||||
size_t sz = 0;
|
||||
d = (const char *)c;
|
||||
}
|
||||
|
||||
for (const char *d = str; *d; d++) {
|
||||
const unsigned char *c = (const unsigned char *) d;
|
||||
|
||||
if (isprint(*c))
|
||||
sz++;
|
||||
else if (c[0] == '\b')
|
||||
sz--;
|
||||
else if (c[0] == '\t')
|
||||
sz += 4; // Tab width == 4
|
||||
// CSI sequence
|
||||
else if (c[0] == '\e' && c[1] == '[') {
|
||||
c += 2;
|
||||
while (*c && *c != 'm')
|
||||
c++;
|
||||
}
|
||||
// UTF-8
|
||||
else if (c[0] >= 0xc2 && c[0] <= 0xdf) {
|
||||
sz++;
|
||||
c += 1;
|
||||
}
|
||||
else if (c[0] >= 0xe0 && c[0] <= 0xef) {
|
||||
sz++;
|
||||
c += 2;
|
||||
}
|
||||
else if (c[0] >= 0xf0 && c[0] <= 0xf4) {
|
||||
sz++;
|
||||
c += 3;
|
||||
}
|
||||
|
||||
d = (const char *) c;
|
||||
}
|
||||
|
||||
return sz;
|
||||
return sz;
|
||||
}
|
||||
|
||||
int log2i(long long x) {
|
||||
if (x == 0)
|
||||
return 1;
|
||||
if (x == 0)
|
||||
return 1;
|
||||
|
||||
return sizeof(x) * 8 - __builtin_clzll(x) - 1;
|
||||
return sizeof(x) * 8 - __builtin_clzll(x) - 1;
|
||||
}
|
||||
|
||||
int sha1sum(FILE *f, unsigned char *sha1)
|
||||
{
|
||||
int ret;
|
||||
char buf[512];
|
||||
ssize_t bytes;
|
||||
long seek;
|
||||
int sha1sum(FILE *f, unsigned char *sha1) {
|
||||
int ret;
|
||||
char buf[512];
|
||||
ssize_t bytes;
|
||||
long seek;
|
||||
|
||||
seek = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
seek = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
|
||||
EVP_MD_CTX *c = EVP_MD_CTX_new();
|
||||
EVP_MD_CTX *c = EVP_MD_CTX_new();
|
||||
|
||||
ret = EVP_DigestInit(c, EVP_sha1());
|
||||
if (!ret)
|
||||
return -1;
|
||||
ret = EVP_DigestInit(c, EVP_sha1());
|
||||
if (!ret)
|
||||
return -1;
|
||||
|
||||
bytes = fread(buf, 1, 512, f);
|
||||
while (bytes > 0) {
|
||||
EVP_DigestUpdate(c, buf, bytes);
|
||||
bytes = fread(buf, 1, 512, f);
|
||||
}
|
||||
bytes = fread(buf, 1, 512, f);
|
||||
while (bytes > 0) {
|
||||
EVP_DigestUpdate(c, buf, bytes);
|
||||
bytes = fread(buf, 1, 512, f);
|
||||
}
|
||||
|
||||
EVP_DigestFinal(c, sha1, nullptr);
|
||||
EVP_DigestFinal(c, sha1, nullptr);
|
||||
|
||||
fseek(f, seek, SEEK_SET);
|
||||
fseek(f, seek, SEEK_SET);
|
||||
|
||||
EVP_MD_CTX_free(c);
|
||||
EVP_MD_CTX_free(c);
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool isDocker()
|
||||
{
|
||||
return access("/.dockerenv", F_OK) != -1;
|
||||
bool isDocker() { return access("/.dockerenv", F_OK) != -1; }
|
||||
|
||||
bool isKubernetes() {
|
||||
return access("/var/run/secrets/kubernetes.io", F_OK) != -1 ||
|
||||
getenv("KUBERNETES_SERVICE_HOST") != nullptr;
|
||||
}
|
||||
|
||||
bool isKubernetes()
|
||||
{
|
||||
return access("/var/run/secrets/kubernetes.io", F_OK) != -1 ||
|
||||
getenv("KUBERNETES_SERVICE_HOST") != nullptr;
|
||||
}
|
||||
|
||||
bool isContainer() {
|
||||
return isDocker() || isKubernetes();
|
||||
}
|
||||
bool isContainer() { return isDocker() || isKubernetes(); }
|
||||
|
||||
bool isPrivileged() {
|
||||
// TODO: a cleaner way would be to use libcap here and check for the
|
||||
// SYS_ADMIN capability.
|
||||
auto *f = fopen(PROCFS_PATH "/sys/vm/nr_hugepages", "w");
|
||||
if (!f)
|
||||
return false;
|
||||
// TODO: a cleaner way would be to use libcap here and check for the
|
||||
// SYS_ADMIN capability.
|
||||
auto *f = fopen(PROCFS_PATH "/sys/vm/nr_hugepages", "w");
|
||||
if (!f)
|
||||
return false;
|
||||
|
||||
fclose(f);
|
||||
fclose(f);
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
|
|
|
@ -11,76 +11,74 @@
|
|||
|
||||
using namespace villas::uuid;
|
||||
|
||||
std::string villas::uuid::toString(const uuid_t in)
|
||||
{
|
||||
uuid_string_t str;
|
||||
uuid_unparse_lower(in, str);
|
||||
return str;
|
||||
std::string villas::uuid::toString(const uuid_t in) {
|
||||
uuid_string_t str;
|
||||
uuid_unparse_lower(in, str);
|
||||
return str;
|
||||
}
|
||||
|
||||
int villas::uuid::generateFromString(uuid_t out, const std::string &data, const std::string &ns)
|
||||
{
|
||||
int ret;
|
||||
EVP_MD_CTX *c = EVP_MD_CTX_new();
|
||||
int villas::uuid::generateFromString(uuid_t out, const std::string &data,
|
||||
const std::string &ns) {
|
||||
int ret;
|
||||
EVP_MD_CTX *c = EVP_MD_CTX_new();
|
||||
|
||||
ret = EVP_DigestInit(c, EVP_md5());
|
||||
if (!ret)
|
||||
return -1;
|
||||
ret = EVP_DigestInit(c, EVP_md5());
|
||||
if (!ret)
|
||||
return -1;
|
||||
|
||||
// Namespace
|
||||
ret = EVP_DigestUpdate(c, (unsigned char *) ns.c_str(), ns.size());
|
||||
if (!ret)
|
||||
return -1;
|
||||
// Namespace
|
||||
ret = EVP_DigestUpdate(c, (unsigned char *)ns.c_str(), ns.size());
|
||||
if (!ret)
|
||||
return -1;
|
||||
|
||||
// Data
|
||||
ret = EVP_DigestUpdate(c, (unsigned char *) data.c_str(), data.size());
|
||||
if (!ret)
|
||||
return -1;
|
||||
// Data
|
||||
ret = EVP_DigestUpdate(c, (unsigned char *)data.c_str(), data.size());
|
||||
if (!ret)
|
||||
return -1;
|
||||
|
||||
ret = EVP_DigestFinal(c, (unsigned char *) out, nullptr);
|
||||
if (!ret)
|
||||
return -1;
|
||||
ret = EVP_DigestFinal(c, (unsigned char *)out, nullptr);
|
||||
if (!ret)
|
||||
return -1;
|
||||
|
||||
EVP_MD_CTX_free(c);
|
||||
EVP_MD_CTX_free(c);
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int villas::uuid::generateFromString(uuid_t out, const std::string &data, const uuid_t ns)
|
||||
{
|
||||
int ret;
|
||||
EVP_MD_CTX *c = EVP_MD_CTX_new();
|
||||
int villas::uuid::generateFromString(uuid_t out, const std::string &data,
|
||||
const uuid_t ns) {
|
||||
int ret;
|
||||
EVP_MD_CTX *c = EVP_MD_CTX_new();
|
||||
|
||||
ret = EVP_DigestInit(c, EVP_md5());
|
||||
if (!ret)
|
||||
return -1;
|
||||
ret = EVP_DigestInit(c, EVP_md5());
|
||||
if (!ret)
|
||||
return -1;
|
||||
|
||||
// Namespace
|
||||
ret = EVP_DigestUpdate(c, (unsigned char *) ns, 16);
|
||||
if (!ret)
|
||||
return -1;
|
||||
// Namespace
|
||||
ret = EVP_DigestUpdate(c, (unsigned char *)ns, 16);
|
||||
if (!ret)
|
||||
return -1;
|
||||
|
||||
// Data
|
||||
ret = EVP_DigestUpdate(c, (unsigned char *) data.c_str(), data.size());
|
||||
if (!ret)
|
||||
return -1;
|
||||
// Data
|
||||
ret = EVP_DigestUpdate(c, (unsigned char *)data.c_str(), data.size());
|
||||
if (!ret)
|
||||
return -1;
|
||||
|
||||
ret = EVP_DigestFinal(c, (unsigned char *) out, nullptr);
|
||||
if (!ret)
|
||||
return -1;
|
||||
ret = EVP_DigestFinal(c, (unsigned char *)out, nullptr);
|
||||
if (!ret)
|
||||
return -1;
|
||||
|
||||
EVP_MD_CTX_free(c);
|
||||
EVP_MD_CTX_free(c);
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int villas::uuid::generateFromJson(uuid_t out, json_t *json, const uuid_t ns)
|
||||
{
|
||||
char *str = json_dumps(json, JSON_COMPACT | JSON_SORT_KEYS);
|
||||
int villas::uuid::generateFromJson(uuid_t out, json_t *json, const uuid_t ns) {
|
||||
char *str = json_dumps(json, JSON_COMPACT | JSON_SORT_KEYS);
|
||||
|
||||
int ret = generateFromString(out, str, ns);
|
||||
int ret = generateFromString(out, str, ns);
|
||||
|
||||
free(str);
|
||||
free(str);
|
||||
|
||||
return ret;
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -14,42 +14,35 @@
|
|||
|
||||
using namespace villas::utils;
|
||||
|
||||
Version::Version(const std::string &str)
|
||||
{
|
||||
size_t endpos;
|
||||
Version::Version(const std::string &str) {
|
||||
size_t endpos;
|
||||
|
||||
auto comp = tokenize(str, ".");
|
||||
auto comp = tokenize(str, ".");
|
||||
|
||||
if (comp.size() > 3)
|
||||
throw std::invalid_argument("Not a valid version string");
|
||||
if (comp.size() > 3)
|
||||
throw std::invalid_argument("Not a valid version string");
|
||||
|
||||
for (unsigned i = 0; i < 3; i++) {
|
||||
if (i < comp.size()) {
|
||||
components[i] = std::stoi(comp[i], &endpos, 10);
|
||||
for (unsigned i = 0; i < 3; i++) {
|
||||
if (i < comp.size()) {
|
||||
components[i] = std::stoi(comp[i], &endpos, 10);
|
||||
|
||||
if (comp[i].begin() + endpos != comp[i].end())
|
||||
throw std::invalid_argument("Not a valid version string");
|
||||
}
|
||||
else
|
||||
components[i] = 0;
|
||||
}
|
||||
if (comp[i].begin() + endpos != comp[i].end())
|
||||
throw std::invalid_argument("Not a valid version string");
|
||||
} else
|
||||
components[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Version::Version(int maj, int min, int pat) :
|
||||
components{maj, min, pat}
|
||||
{
|
||||
Version::Version(int maj, int min, int pat) : components{maj, min, pat} {}
|
||||
|
||||
}
|
||||
|
||||
int Version::cmp(const Version &lhs, const Version &rhs)
|
||||
{
|
||||
int d;
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
d = lhs.components[i] - rhs.components[i];
|
||||
if (d)
|
||||
return d;
|
||||
}
|
||||
|
||||
return 0;
|
||||
int Version::cmp(const Version &lhs, const Version &rhs) {
|
||||
int d;
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
d = lhs.components[i] - rhs.components[i];
|
||||
if (d)
|
||||
return d;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -16,18 +16,16 @@ using namespace villas::utils::base64;
|
|||
// cppcheck-suppress unknownMacro
|
||||
TestSuite(base64, .description = "Base64 En/decoder");
|
||||
|
||||
static
|
||||
std::vector<byte> vec(const char *str)
|
||||
{
|
||||
return std::vector<byte>((byte *) str, (byte *) str + strlen(str));
|
||||
static std::vector<byte> vec(const char *str) {
|
||||
return std::vector<byte>((byte *)str, (byte *)str + strlen(str));
|
||||
}
|
||||
|
||||
Test(base64, encoding)
|
||||
{
|
||||
cr_assert(encode(vec("pohy0Aiy1ZaVa5aik2yaiy3ifoh3oole")) == "cG9oeTBBaXkxWmFWYTVhaWsyeWFpeTNpZm9oM29vbGU=");
|
||||
Test(base64, encoding) {
|
||||
cr_assert(encode(vec("pohy0Aiy1ZaVa5aik2yaiy3ifoh3oole")) ==
|
||||
"cG9oeTBBaXkxWmFWYTVhaWsyeWFpeTNpZm9oM29vbGU=");
|
||||
}
|
||||
|
||||
Test(base64, decoding)
|
||||
{
|
||||
cr_assert(decode("cG9oeTBBaXkxWmFWYTVhaWsyeWFpeTNpZm9oM29vbGU=") == vec("pohy0Aiy1ZaVa5aik2yaiy3ifoh3oole"));
|
||||
Test(base64, decoding) {
|
||||
cr_assert(decode("cG9oeTBBaXkxWmFWYTVhaWsyeWFpeTNpZm9oM29vbGU=") ==
|
||||
vec("pohy0Aiy1ZaVa5aik2yaiy3ifoh3oole"));
|
||||
}
|
||||
|
|
|
@ -18,97 +18,93 @@ using namespace villas;
|
|||
// cppcheck-suppress unknownMacro
|
||||
TestSuite(buffer, .description = "Buffer datastructure");
|
||||
|
||||
Test(buffer, decode)
|
||||
{
|
||||
Buffer buf;
|
||||
json_t *j;
|
||||
json_t *k;
|
||||
Test(buffer, decode) {
|
||||
Buffer buf;
|
||||
json_t *j;
|
||||
json_t *k;
|
||||
|
||||
const char *e = "{\"id\": \"5a786626-fbc6-4c04-98c2-48027e68c2fa\"}";
|
||||
size_t len = strlen(e);
|
||||
const char *e = "{\"id\": \"5a786626-fbc6-4c04-98c2-48027e68c2fa\"}";
|
||||
size_t len = strlen(e);
|
||||
|
||||
buf.insert(buf.begin(), e, e+len);
|
||||
buf.insert(buf.begin(), e, e + len);
|
||||
|
||||
k = json_loads(e, 0, nullptr);
|
||||
cr_assert_not_null(k);
|
||||
k = json_loads(e, 0, nullptr);
|
||||
cr_assert_not_null(k);
|
||||
|
||||
j = buf.decode();
|
||||
cr_assert_not_null(j);
|
||||
cr_assert(json_equal(j, k));
|
||||
j = buf.decode();
|
||||
cr_assert_not_null(j);
|
||||
cr_assert(json_equal(j, k));
|
||||
}
|
||||
|
||||
Test(buffer, encode)
|
||||
{
|
||||
int ret;
|
||||
Buffer buf;
|
||||
json_t *k;
|
||||
Test(buffer, encode) {
|
||||
int ret;
|
||||
Buffer buf;
|
||||
json_t *k;
|
||||
|
||||
const char *e = "{\"id\": \"5a786626-fbc6-4c04-98c2-48027e68c2fa\"}";
|
||||
const char *e = "{\"id\": \"5a786626-fbc6-4c04-98c2-48027e68c2fa\"}";
|
||||
|
||||
k = json_loads(e, 0, nullptr);
|
||||
cr_assert_not_null(k);
|
||||
k = json_loads(e, 0, nullptr);
|
||||
cr_assert_not_null(k);
|
||||
|
||||
ret = buf.encode(k);
|
||||
cr_assert_eq(ret, 0);
|
||||
ret = buf.encode(k);
|
||||
cr_assert_eq(ret, 0);
|
||||
|
||||
char *f = buf.data();
|
||||
cr_assert_not_null(f);
|
||||
char *f = buf.data();
|
||||
cr_assert_not_null(f);
|
||||
|
||||
cr_assert_str_eq(e, f);
|
||||
cr_assert_str_eq(e, f);
|
||||
|
||||
json_decref(k);
|
||||
json_decref(k);
|
||||
}
|
||||
|
||||
Test(buffer, encode_decode)
|
||||
{
|
||||
int ret;
|
||||
Buffer buf;
|
||||
json_t *k;
|
||||
json_t *j;
|
||||
Test(buffer, encode_decode) {
|
||||
int ret;
|
||||
Buffer buf;
|
||||
json_t *k;
|
||||
json_t *j;
|
||||
|
||||
const char *e = "{\"id\": \"5a786626-fbc6-4c04-98c2-48027e68c2fa\"}";
|
||||
const char *e = "{\"id\": \"5a786626-fbc6-4c04-98c2-48027e68c2fa\"}";
|
||||
|
||||
k = json_loads(e, 0, nullptr);
|
||||
cr_assert_not_null(k);
|
||||
k = json_loads(e, 0, nullptr);
|
||||
cr_assert_not_null(k);
|
||||
|
||||
ret = buf.encode(k);
|
||||
cr_assert_eq(ret, 0);
|
||||
ret = buf.encode(k);
|
||||
cr_assert_eq(ret, 0);
|
||||
|
||||
j = buf.decode();
|
||||
cr_assert_not_null(j);
|
||||
j = buf.decode();
|
||||
cr_assert_not_null(j);
|
||||
|
||||
cr_assert(json_equal(j, k));
|
||||
cr_assert(json_equal(j, k));
|
||||
|
||||
json_decref(j);
|
||||
json_decref(k);
|
||||
json_decref(j);
|
||||
json_decref(k);
|
||||
}
|
||||
|
||||
Test(buffer, multiple)
|
||||
{
|
||||
int ret;
|
||||
const int N = 100;
|
||||
Test(buffer, multiple) {
|
||||
int ret;
|
||||
const int N = 100;
|
||||
|
||||
Buffer buf;
|
||||
json_t *k[N];
|
||||
json_t *j[N];
|
||||
Buffer buf;
|
||||
json_t *k[N];
|
||||
json_t *j[N];
|
||||
|
||||
std::srand(std::time(nullptr));
|
||||
std::srand(std::time(nullptr));
|
||||
|
||||
for (int i = 0; i < N; i++) {
|
||||
k[i] = json_pack("{ s: i }", "id", std::rand());
|
||||
cr_assert_not_null(k[i]);
|
||||
for (int i = 0; i < N; i++) {
|
||||
k[i] = json_pack("{ s: i }", "id", std::rand());
|
||||
cr_assert_not_null(k[i]);
|
||||
|
||||
ret = buf.encode(k[i]);
|
||||
cr_assert_eq(ret, 0);
|
||||
}
|
||||
ret = buf.encode(k[i]);
|
||||
cr_assert_eq(ret, 0);
|
||||
}
|
||||
|
||||
for (int i = 0; i < N; i++) {
|
||||
j[i] = buf.decode();
|
||||
cr_assert_not_null(j[i]);
|
||||
for (int i = 0; i < N; i++) {
|
||||
j[i] = buf.decode();
|
||||
cr_assert_not_null(j[i]);
|
||||
|
||||
cr_assert(json_equal(k[i], j[i]));
|
||||
cr_assert(json_equal(k[i], j[i]));
|
||||
|
||||
json_decref(k[i]);
|
||||
json_decref(j[i]);
|
||||
}
|
||||
json_decref(k[i]);
|
||||
json_decref(j[i]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,114 +17,111 @@ using namespace villas;
|
|||
// cppcheck-suppress unknownMacro
|
||||
TestSuite(graph, .description = "Graph library");
|
||||
|
||||
Test(graph, basic, .description = "DirectedGraph")
|
||||
{
|
||||
Logger logger = logging.get("test:graph:basic");
|
||||
villas::graph::DirectedGraph<> g("test:graph:basic");
|
||||
Test(graph, basic, .description = "DirectedGraph") {
|
||||
Logger logger = logging.get("test:graph:basic");
|
||||
villas::graph::DirectedGraph<> g("test:graph:basic");
|
||||
|
||||
logger->info("Testing basic graph construction and modification");
|
||||
logger->info("Testing basic graph construction and modification");
|
||||
|
||||
std::shared_ptr<villas::graph::Vertex> v1(new villas::graph::Vertex);
|
||||
std::shared_ptr<villas::graph::Vertex> v2(new villas::graph::Vertex);
|
||||
std::shared_ptr<villas::graph::Vertex> v3(new villas::graph::Vertex);
|
||||
std::shared_ptr<villas::graph::Vertex> v1(new villas::graph::Vertex);
|
||||
std::shared_ptr<villas::graph::Vertex> v2(new villas::graph::Vertex);
|
||||
std::shared_ptr<villas::graph::Vertex> v3(new villas::graph::Vertex);
|
||||
|
||||
auto v1id = g.addVertex(v1);
|
||||
auto v2id = g.addVertex(v2);
|
||||
auto v3id = g.addVertex(v3);
|
||||
cr_assert(g.getVertexCount() == 3);
|
||||
auto v1id = g.addVertex(v1);
|
||||
auto v2id = g.addVertex(v2);
|
||||
auto v3id = g.addVertex(v3);
|
||||
cr_assert(g.getVertexCount() == 3);
|
||||
|
||||
g.addDefaultEdge(v1id, v2id);
|
||||
g.addDefaultEdge(v3id, v2id);
|
||||
g.addDefaultEdge(v1id, v3id);
|
||||
g.addDefaultEdge(v2id, v1id);
|
||||
cr_assert(g.getEdgeCount() == 4);
|
||||
cr_assert(g.vertexGetEdges(v1id).size() == 2);
|
||||
cr_assert(g.vertexGetEdges(v2id).size() == 1);
|
||||
cr_assert(g.vertexGetEdges(v3id).size() == 1);
|
||||
g.addDefaultEdge(v1id, v2id);
|
||||
g.addDefaultEdge(v3id, v2id);
|
||||
g.addDefaultEdge(v1id, v3id);
|
||||
g.addDefaultEdge(v2id, v1id);
|
||||
cr_assert(g.getEdgeCount() == 4);
|
||||
cr_assert(g.vertexGetEdges(v1id).size() == 2);
|
||||
cr_assert(g.vertexGetEdges(v2id).size() == 1);
|
||||
cr_assert(g.vertexGetEdges(v3id).size() == 1);
|
||||
|
||||
g.removeVertex(v1id);
|
||||
g.dump();
|
||||
cr_assert(g.getVertexCount() == 2);
|
||||
cr_assert(g.vertexGetEdges(v2id).size() == 0);
|
||||
g.removeVertex(v1id);
|
||||
g.dump();
|
||||
cr_assert(g.getVertexCount() == 2);
|
||||
cr_assert(g.vertexGetEdges(v2id).size() == 0);
|
||||
}
|
||||
|
||||
Test(graph, path, .description = "Find path")
|
||||
{
|
||||
Logger logger = logging.get("test:graph:path");
|
||||
logger->info("Testing path finding algorithm");
|
||||
Test(graph, path, .description = "Find path") {
|
||||
Logger logger = logging.get("test:graph:path");
|
||||
logger->info("Testing path finding algorithm");
|
||||
|
||||
using Graph = villas::graph::DirectedGraph<>;
|
||||
Graph g("test:graph:path");
|
||||
using Graph = villas::graph::DirectedGraph<>;
|
||||
Graph g("test:graph:path");
|
||||
|
||||
std::shared_ptr<villas::graph::Vertex> v1(new villas::graph::Vertex);
|
||||
std::shared_ptr<villas::graph::Vertex> v2(new villas::graph::Vertex);
|
||||
std::shared_ptr<villas::graph::Vertex> v3(new villas::graph::Vertex);
|
||||
std::shared_ptr<villas::graph::Vertex> v4(new villas::graph::Vertex);
|
||||
std::shared_ptr<villas::graph::Vertex> v5(new villas::graph::Vertex);
|
||||
std::shared_ptr<villas::graph::Vertex> v6(new villas::graph::Vertex);
|
||||
std::shared_ptr<villas::graph::Vertex> v1(new villas::graph::Vertex);
|
||||
std::shared_ptr<villas::graph::Vertex> v2(new villas::graph::Vertex);
|
||||
std::shared_ptr<villas::graph::Vertex> v3(new villas::graph::Vertex);
|
||||
std::shared_ptr<villas::graph::Vertex> v4(new villas::graph::Vertex);
|
||||
std::shared_ptr<villas::graph::Vertex> v5(new villas::graph::Vertex);
|
||||
std::shared_ptr<villas::graph::Vertex> v6(new villas::graph::Vertex);
|
||||
|
||||
auto v1id = g.addVertex(v1);
|
||||
auto v2id = g.addVertex(v2);
|
||||
auto v3id = g.addVertex(v3);
|
||||
auto v1id = g.addVertex(v1);
|
||||
auto v2id = g.addVertex(v2);
|
||||
auto v3id = g.addVertex(v3);
|
||||
|
||||
auto v4id = g.addVertex(v4);
|
||||
auto v5id = g.addVertex(v5);
|
||||
auto v6id = g.addVertex(v6);
|
||||
auto v4id = g.addVertex(v4);
|
||||
auto v5id = g.addVertex(v5);
|
||||
auto v6id = g.addVertex(v6);
|
||||
|
||||
g.addDefaultEdge(v1id, v2id);
|
||||
g.addDefaultEdge(v2id, v3id);
|
||||
g.addDefaultEdge(v1id, v2id);
|
||||
g.addDefaultEdge(v2id, v3id);
|
||||
|
||||
// Create circular subgraph
|
||||
g.addDefaultEdge(v4id, v5id);
|
||||
g.addDefaultEdge(v5id, v4id);
|
||||
g.addDefaultEdge(v5id, v6id);
|
||||
// Create circular subgraph
|
||||
g.addDefaultEdge(v4id, v5id);
|
||||
g.addDefaultEdge(v5id, v4id);
|
||||
g.addDefaultEdge(v5id, v6id);
|
||||
|
||||
g.dump();
|
||||
g.dump();
|
||||
|
||||
logger->info("Find simple path via two edges");
|
||||
std::list<Graph::EdgeIdentifier> path1;
|
||||
cr_assert(g.getPath(v1id, v3id, path1));
|
||||
logger->info("Find simple path via two edges");
|
||||
std::list<Graph::EdgeIdentifier> path1;
|
||||
cr_assert(g.getPath(v1id, v3id, path1));
|
||||
|
||||
logger->info(" Path from {} to {} via:", v1id, v3id);
|
||||
for(auto &edge : path1) {
|
||||
logger->info(" -> edge {}", edge);
|
||||
}
|
||||
logger->info(" Path from {} to {} via:", v1id, v3id);
|
||||
for (auto &edge : path1) {
|
||||
logger->info(" -> edge {}", edge);
|
||||
}
|
||||
|
||||
logger->info("Find path between two unconnected sub-graphs");
|
||||
std::list<Graph::EdgeIdentifier> path2;
|
||||
cr_assert(not g.getPath(v1id, v4id, path2));
|
||||
logger->info(" no path found -> ok");
|
||||
logger->info("Find path between two unconnected sub-graphs");
|
||||
std::list<Graph::EdgeIdentifier> path2;
|
||||
cr_assert(not g.getPath(v1id, v4id, path2));
|
||||
logger->info(" no path found -> ok");
|
||||
|
||||
logger->info("Find non-existing path in circular sub-graph");
|
||||
std::list<Graph::EdgeIdentifier> path3;
|
||||
cr_assert(not g.getPath(v4id, v2id, path3));
|
||||
logger->info(" no path found -> ok");
|
||||
logger->info("Find non-existing path in circular sub-graph");
|
||||
std::list<Graph::EdgeIdentifier> path3;
|
||||
cr_assert(not g.getPath(v4id, v2id, path3));
|
||||
logger->info(" no path found -> ok");
|
||||
|
||||
logger->info("Find path in circular graph");
|
||||
std::list<Graph::EdgeIdentifier> path4;
|
||||
cr_assert(g.getPath(v4id, v6id, path4));
|
||||
logger->info("Find path in circular graph");
|
||||
std::list<Graph::EdgeIdentifier> path4;
|
||||
cr_assert(g.getPath(v4id, v6id, path4));
|
||||
|
||||
logger->info(" Path from {} to {} via:", v4id, v6id);
|
||||
for(auto &edge : path4) {
|
||||
logger->info(" -> edge {}", edge);
|
||||
}
|
||||
logger->info(" Path from {} to {} via:", v4id, v6id);
|
||||
for (auto &edge : path4) {
|
||||
logger->info(" -> edge {}", edge);
|
||||
}
|
||||
}
|
||||
|
||||
Test(graph, memory_manager, .description = "Global Memory Manager")
|
||||
{
|
||||
Logger logger = logging.get("test:graph:mm");
|
||||
auto &mm = villas::MemoryManager::get();
|
||||
Test(graph, memory_manager, .description = "Global Memory Manager") {
|
||||
Logger logger = logging.get("test:graph:mm");
|
||||
auto &mm = villas::MemoryManager::get();
|
||||
|
||||
logger->info("Create address spaces");
|
||||
auto dmaRegs = mm.getOrCreateAddressSpace("DMA Registers");
|
||||
auto pcieBridge = mm.getOrCreateAddressSpace("PCIe Bridge");
|
||||
logger->info("Create address spaces");
|
||||
auto dmaRegs = mm.getOrCreateAddressSpace("DMA Registers");
|
||||
auto pcieBridge = mm.getOrCreateAddressSpace("PCIe Bridge");
|
||||
|
||||
logger->info("Create a mapping");
|
||||
mm.createMapping(0x1000, 0, 0x1000, "Testmapping", dmaRegs, pcieBridge);
|
||||
logger->info("Create a mapping");
|
||||
mm.createMapping(0x1000, 0, 0x1000, "Testmapping", dmaRegs, pcieBridge);
|
||||
|
||||
logger->info("Find address space by name");
|
||||
auto vertex = mm.findAddressSpace("PCIe Bridge");
|
||||
logger->info(" found: {}", vertex);
|
||||
logger->info("Find address space by name");
|
||||
auto vertex = mm.findAddressSpace("PCIe Bridge");
|
||||
logger->info(" found: {}", vertex);
|
||||
|
||||
mm.getGraph().dump();
|
||||
mm.getGraph().dump();
|
||||
}
|
||||
|
|
|
@ -21,12 +21,12 @@ TestSuite(hist, .description = "Histogram");
|
|||
|
||||
Test(hist, simple) {
|
||||
|
||||
Hist h(10,2);
|
||||
Hist h(10, 2);
|
||||
|
||||
for (auto td : test_data)
|
||||
h.put(td);
|
||||
for (auto td : test_data)
|
||||
h.put(td);
|
||||
|
||||
cr_assert_float_eq(h.getMean(), 5.5, 1e-6, "Mean is %lf", h.getMean());
|
||||
cr_assert_float_eq(h.getVar(), 9.1666, 1e-3);
|
||||
cr_assert_float_eq(h.getStddev(), 3.027650, 1e-6);
|
||||
cr_assert_float_eq(h.getMean(), 5.5, 1e-6, "Mean is %lf", h.getMean());
|
||||
cr_assert_float_eq(h.getVar(), 9.1666, 1e-3);
|
||||
cr_assert_float_eq(h.getStddev(), 3.027650, 1e-6);
|
||||
}
|
||||
|
|
|
@ -15,83 +15,78 @@ using namespace villas::kernel;
|
|||
TestSuite(kernel, .description = "Kernel features");
|
||||
|
||||
#if defined(__x86_64__) || defined(__i386__)
|
||||
#define PAGESIZE (1 << 12)
|
||||
#define CACHELINESIZE 64
|
||||
#define PAGESIZE (1 << 12)
|
||||
#define CACHELINESIZE 64
|
||||
|
||||
#if defined(__x86_64__)
|
||||
#define HUGEPAGESIZE (1 << 21)
|
||||
#elif defined(__i386__)
|
||||
#define HUGEPAGESIZE (1 << 22)
|
||||
#endif
|
||||
#if defined(__x86_64__)
|
||||
#define HUGEPAGESIZE (1 << 21)
|
||||
#elif defined(__i386__)
|
||||
#define HUGEPAGESIZE (1 << 22)
|
||||
#endif
|
||||
#else
|
||||
#error "Unsupported architecture"
|
||||
#error "Unsupported architecture"
|
||||
#endif
|
||||
|
||||
// This test is not portable, but we currently support x86 only
|
||||
Test(kernel, sizes)
|
||||
{
|
||||
int sz;
|
||||
Test(kernel, sizes) {
|
||||
int sz;
|
||||
|
||||
sz = getPageSize();
|
||||
cr_assert_eq(sz, PAGESIZE);
|
||||
sz = getPageSize();
|
||||
cr_assert_eq(sz, PAGESIZE);
|
||||
|
||||
sz = getHugePageSize();
|
||||
cr_assert(sz == HUGEPAGESIZE);
|
||||
sz = getHugePageSize();
|
||||
cr_assert(sz == HUGEPAGESIZE);
|
||||
|
||||
sz = getCachelineSize();
|
||||
cr_assert_eq(sz, CACHELINESIZE);
|
||||
sz = getCachelineSize();
|
||||
cr_assert_eq(sz, CACHELINESIZE);
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
Test(kernel, hugepages)
|
||||
{
|
||||
int ret;
|
||||
Test(kernel, hugepages) {
|
||||
int ret;
|
||||
|
||||
ret = setNrHugepages(25);
|
||||
cr_assert_eq(ret, 0);
|
||||
ret = setNrHugepages(25);
|
||||
cr_assert_eq(ret, 0);
|
||||
|
||||
ret = getNrHugepages();
|
||||
cr_assert_eq(ret, 25);
|
||||
ret = getNrHugepages();
|
||||
cr_assert_eq(ret, 25);
|
||||
|
||||
ret = setNrHugepages(10);
|
||||
cr_assert_eq(ret, 0);
|
||||
ret = setNrHugepages(10);
|
||||
cr_assert_eq(ret, 0);
|
||||
|
||||
ret = getNrHugepages();
|
||||
cr_assert_eq(ret, 10);
|
||||
ret = getNrHugepages();
|
||||
cr_assert_eq(ret, 10);
|
||||
}
|
||||
|
||||
Test(kernel, version)
|
||||
{
|
||||
using villas::utils::Version;
|
||||
Test(kernel, version) {
|
||||
using villas::utils::Version;
|
||||
|
||||
Version ver = villas::kernel::getVersion();
|
||||
Version ver1 = { 100, 5 };
|
||||
Version ver2 = { 2, 6 };
|
||||
Version ver = villas::kernel::getVersion();
|
||||
Version ver1 = {100, 5};
|
||||
Version ver2 = {2, 6};
|
||||
|
||||
cr_assert_lt(ver, ver1);
|
||||
cr_assert_gt(ver, ver2);
|
||||
cr_assert_lt(ver, ver1);
|
||||
cr_assert_gt(ver, ver2);
|
||||
}
|
||||
|
||||
Test(kernel, module, .disabled = true)
|
||||
{
|
||||
int ret;
|
||||
Test(kernel, module, .disabled = true) {
|
||||
int ret;
|
||||
|
||||
ret = isModuleLoaded("nf_nat");
|
||||
cr_assert_eq(ret, 0);
|
||||
ret = isModuleLoaded("nf_nat");
|
||||
cr_assert_eq(ret, 0);
|
||||
|
||||
ret = isModuleLoaded("does_not_exist");
|
||||
cr_assert_neq(ret, 0);
|
||||
ret = isModuleLoaded("does_not_exist");
|
||||
cr_assert_neq(ret, 0);
|
||||
}
|
||||
|
||||
Test(kernel, frequency)
|
||||
{
|
||||
int ret;
|
||||
uint64_t freq;
|
||||
Test(kernel, frequency) {
|
||||
int ret;
|
||||
uint64_t freq;
|
||||
|
||||
ret = get_cpu_frequency(&freq);
|
||||
cr_assert_eq(ret, 0);
|
||||
ret = get_cpu_frequency(&freq);
|
||||
cr_assert_eq(ret, 0);
|
||||
|
||||
// Check for plausability only
|
||||
cr_assert(freq > 1e9 && freq < 5e9);
|
||||
// Check for plausability only
|
||||
cr_assert(freq > 1e9 && freq < 5e9);
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -5,154 +5,155 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <criterion/criterion.h>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <criterion/criterion.h>
|
||||
|
||||
#include <villas/exceptions.hpp>
|
||||
#include <villas/utils.hpp>
|
||||
#include <villas/list.hpp>
|
||||
#include <villas/utils.hpp>
|
||||
|
||||
using namespace villas;
|
||||
|
||||
static
|
||||
const char *nouns[] = { "time", "person", "year", "way", "day", "thing", "man", "world", "life", "hand", "part", "child", "eye", "woman", "place", "work", "week", "case", "point", "government", "company", "number", "group", "problem", "fact" };
|
||||
static const char *nouns[] = {
|
||||
"time", "person", "year", "way", "day", "thing", "man",
|
||||
"world", "life", "hand", "part", "child", "eye", "woman",
|
||||
"place", "work", "week", "case", "point", "government", "company",
|
||||
"number", "group", "problem", "fact"};
|
||||
|
||||
struct data {
|
||||
const char *name;
|
||||
int data;
|
||||
const char *name;
|
||||
int data;
|
||||
};
|
||||
|
||||
// cppcheck-suppress unknownMacro
|
||||
TestSuite(list, .description = "List datastructure");
|
||||
|
||||
Test(list, list_search)
|
||||
{
|
||||
int ret;
|
||||
struct List l;
|
||||
Test(list, list_search) {
|
||||
int ret;
|
||||
struct List l;
|
||||
|
||||
ret = list_init(&l);
|
||||
cr_assert_eq(ret, 0);
|
||||
ret = list_init(&l);
|
||||
cr_assert_eq(ret, 0);
|
||||
|
||||
// Fill list
|
||||
for (unsigned i = 0; i < ARRAY_LEN(nouns); i++)
|
||||
list_push(&l, (void *) nouns[i]);
|
||||
// Fill list
|
||||
for (unsigned i = 0; i < ARRAY_LEN(nouns); i++)
|
||||
list_push(&l, (void *)nouns[i]);
|
||||
|
||||
cr_assert_eq(list_length(&l), ARRAY_LEN(nouns));
|
||||
cr_assert_eq(list_length(&l), ARRAY_LEN(nouns));
|
||||
|
||||
// Declare on stack!
|
||||
char positive[] = "woman";
|
||||
char negative[] = "dinosaurrier";
|
||||
// Declare on stack!
|
||||
char positive[] = "woman";
|
||||
char negative[] = "dinosaurrier";
|
||||
|
||||
char *found = (char *) list_search(&l, (cmp_cb_t) strcmp, positive);
|
||||
cr_assert_not_null(found);
|
||||
cr_assert_eq(found, nouns[13], "found = %p, nouns[13] = %p", found, nouns[13]);
|
||||
cr_assert_str_eq(found, positive);
|
||||
char *found = (char *)list_search(&l, (cmp_cb_t)strcmp, positive);
|
||||
cr_assert_not_null(found);
|
||||
cr_assert_eq(found, nouns[13], "found = %p, nouns[13] = %p", found,
|
||||
nouns[13]);
|
||||
cr_assert_str_eq(found, positive);
|
||||
|
||||
char *not_found = (char *) list_search(&l, (cmp_cb_t) strcmp, negative);
|
||||
cr_assert_null(not_found);
|
||||
char *not_found = (char *)list_search(&l, (cmp_cb_t)strcmp, negative);
|
||||
cr_assert_null(not_found);
|
||||
|
||||
ret = list_destroy(&l, nullptr, false);
|
||||
cr_assert_eq(ret, 0);
|
||||
ret = list_destroy(&l, nullptr, false);
|
||||
cr_assert_eq(ret, 0);
|
||||
}
|
||||
|
||||
struct content {
|
||||
int destroyed;
|
||||
int destroyed;
|
||||
};
|
||||
|
||||
static
|
||||
int dtor(void *ptr)
|
||||
{
|
||||
struct content *elm = (struct content *) ptr;
|
||||
static int dtor(void *ptr) {
|
||||
struct content *elm = (struct content *)ptr;
|
||||
|
||||
elm->destroyed = 1;
|
||||
elm->destroyed = 1;
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
Test(list, destructor)
|
||||
{
|
||||
int ret;
|
||||
struct List l;
|
||||
Test(list, destructor) {
|
||||
int ret;
|
||||
struct List l;
|
||||
|
||||
struct content elm;
|
||||
elm.destroyed = 0;
|
||||
struct content elm;
|
||||
elm.destroyed = 0;
|
||||
|
||||
ret = list_init(&l);
|
||||
cr_assert_eq(ret, 0);
|
||||
ret = list_init(&l);
|
||||
cr_assert_eq(ret, 0);
|
||||
|
||||
list_push(&l, &elm);
|
||||
list_push(&l, &elm);
|
||||
|
||||
cr_assert_eq(list_length(&l), 1);
|
||||
cr_assert_eq(list_length(&l), 1);
|
||||
|
||||
ret = list_destroy(&l, dtor, false);
|
||||
cr_assert_eq(ret, 0);
|
||||
ret = list_destroy(&l, dtor, false);
|
||||
cr_assert_eq(ret, 0);
|
||||
|
||||
cr_assert_eq(elm.destroyed, 1);
|
||||
cr_assert_eq(elm.destroyed, 1);
|
||||
}
|
||||
|
||||
Test(list, basics)
|
||||
{
|
||||
uintptr_t i;
|
||||
int ret;
|
||||
struct List l;
|
||||
Test(list, basics) {
|
||||
uintptr_t i;
|
||||
int ret;
|
||||
struct List l;
|
||||
|
||||
ret = list_init(&l);
|
||||
cr_assert_eq(ret, 0);
|
||||
ret = list_init(&l);
|
||||
cr_assert_eq(ret, 0);
|
||||
|
||||
for (i = 0; i < 100; i++) {
|
||||
cr_assert_eq(list_length(&l), i);
|
||||
for (i = 0; i < 100; i++) {
|
||||
cr_assert_eq(list_length(&l), i);
|
||||
|
||||
list_push(&l, (void *) i);
|
||||
}
|
||||
list_push(&l, (void *)i);
|
||||
}
|
||||
|
||||
cr_assert_eq(list_at_safe(&l, 555), nullptr);
|
||||
cr_assert_eq(list_last(&l), (void *) 99);
|
||||
cr_assert_eq(list_first(&l), (void *) 0);
|
||||
cr_assert_eq(list_at_safe(&l, 555), nullptr);
|
||||
cr_assert_eq(list_last(&l), (void *)99);
|
||||
cr_assert_eq(list_first(&l), (void *)0);
|
||||
|
||||
for (size_t j = 0, i = 0; j < list_length(&l); j++) {
|
||||
void *k = list_at(&l, j);
|
||||
for (size_t j = 0, i = 0; j < list_length(&l); j++) {
|
||||
void *k = list_at(&l, j);
|
||||
|
||||
cr_assert_eq(k, (void *) i++);
|
||||
}
|
||||
cr_assert_eq(k, (void *)i++);
|
||||
}
|
||||
|
||||
list_sort(&l, (cmp_cb_t) [](const void *a, const void *b) -> int {
|
||||
return (intptr_t) b - (intptr_t) a;
|
||||
});
|
||||
list_sort(
|
||||
&l, (cmp_cb_t)[](const void *a, const void *b)->int {
|
||||
return (intptr_t)b - (intptr_t)a;
|
||||
});
|
||||
|
||||
for (size_t j = 0, i = 99; j <= 99; j++, i--) {
|
||||
uintptr_t k = (uintptr_t) list_at(&l, j);
|
||||
cr_assert_eq(k, i, "Is %zu, expected %zu", k, i);
|
||||
}
|
||||
for (size_t j = 0, i = 99; j <= 99; j++, i--) {
|
||||
uintptr_t k = (uintptr_t)list_at(&l, j);
|
||||
cr_assert_eq(k, i, "Is %zu, expected %zu", k, i);
|
||||
}
|
||||
|
||||
ret = list_contains(&l, (void *) 55);
|
||||
cr_assert(ret);
|
||||
ret = list_contains(&l, (void *)55);
|
||||
cr_assert(ret);
|
||||
|
||||
void *before_ptr = list_at(&l, 12);
|
||||
void *before_ptr = list_at(&l, 12);
|
||||
|
||||
ret = list_insert(&l, 12, (void *) 123);
|
||||
cr_assert_eq(ret, 0);
|
||||
cr_assert_eq(list_at(&l, 12), (void *) 123, "Is: %p", list_at(&l, 12));
|
||||
ret = list_insert(&l, 12, (void *)123);
|
||||
cr_assert_eq(ret, 0);
|
||||
cr_assert_eq(list_at(&l, 12), (void *)123, "Is: %p", list_at(&l, 12));
|
||||
|
||||
ret = list_remove(&l, 12);
|
||||
cr_assert_eq(ret, 0);
|
||||
cr_assert_eq(list_at(&l, 12), before_ptr);
|
||||
ret = list_remove(&l, 12);
|
||||
cr_assert_eq(ret, 0);
|
||||
cr_assert_eq(list_at(&l, 12), before_ptr);
|
||||
|
||||
int counts, before_len;
|
||||
int counts, before_len;
|
||||
|
||||
before_len = list_length(&l);
|
||||
counts = list_contains(&l, (void *) 55);
|
||||
cr_assert_gt(counts, 0);
|
||||
before_len = list_length(&l);
|
||||
counts = list_contains(&l, (void *)55);
|
||||
cr_assert_gt(counts, 0);
|
||||
|
||||
list_remove_all(&l, (void *) 55);
|
||||
cr_assert_eq(list_length(&l), (size_t) (before_len - counts));
|
||||
list_remove_all(&l, (void *)55);
|
||||
cr_assert_eq(list_length(&l), (size_t)(before_len - counts));
|
||||
|
||||
ret = list_contains(&l, (void *) 55);
|
||||
cr_assert(!ret);
|
||||
ret = list_contains(&l, (void *)55);
|
||||
cr_assert(!ret);
|
||||
|
||||
ret = list_destroy(&l, nullptr, false);
|
||||
cr_assert(!ret);
|
||||
ret = list_destroy(&l, nullptr, false);
|
||||
cr_assert(!ret);
|
||||
|
||||
ret = list_length(&l);
|
||||
cr_assert_eq(ret, -1, "List not properly destroyed: l.length = %zd", l.length);
|
||||
ret = list_length(&l);
|
||||
cr_assert_eq(ret, -1, "List not properly destroyed: l.length = %zd",
|
||||
l.length);
|
||||
}
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <criterion/criterion.h>
|
||||
#include <iostream>
|
||||
|
||||
#include <villas/popen.hpp>
|
||||
|
||||
|
@ -15,65 +15,62 @@ using namespace villas::utils;
|
|||
// cppcheck-suppress unknownMacro
|
||||
TestSuite(popen, .description = "Bi-directional popen");
|
||||
|
||||
Test(popen, no_shell)
|
||||
{
|
||||
PopenStream proc("/usr/bin/tee", {"tee", "test"});
|
||||
Test(popen, no_shell) {
|
||||
PopenStream proc("/usr/bin/tee", {"tee", "test"});
|
||||
|
||||
proc.cout() << "Hello World" << std::endl;
|
||||
proc.cout().flush();
|
||||
proc.cout() << "Hello World" << std::endl;
|
||||
proc.cout().flush();
|
||||
|
||||
std::string str, str2;
|
||||
std::string str, str2;
|
||||
|
||||
proc.cin() >> str >> str2;
|
||||
proc.cin() >> str >> str2;
|
||||
|
||||
std::cout << str << str2 << std::endl;
|
||||
std::cout << str << str2 << std::endl;
|
||||
|
||||
cr_assert_eq(str, "Hello");
|
||||
cr_assert_eq(str2, "World");
|
||||
cr_assert_eq(str, "Hello");
|
||||
cr_assert_eq(str2, "World");
|
||||
|
||||
proc.kill();
|
||||
proc.close();
|
||||
proc.kill();
|
||||
proc.close();
|
||||
}
|
||||
|
||||
Test(popen, shell)
|
||||
{
|
||||
PopenStream proc("echo \"Hello World\"", {}, {}, std::string(), true);
|
||||
Test(popen, shell) {
|
||||
PopenStream proc("echo \"Hello World\"", {}, {}, std::string(), true);
|
||||
|
||||
std::string str, str2;
|
||||
std::string str, str2;
|
||||
|
||||
proc.cin() >> str >> str2;
|
||||
proc.cin() >> str >> str2;
|
||||
|
||||
cr_assert_eq(str, "Hello");
|
||||
cr_assert_eq(str2, "World");
|
||||
cr_assert_eq(str, "Hello");
|
||||
cr_assert_eq(str2, "World");
|
||||
|
||||
proc.kill();
|
||||
proc.close();
|
||||
proc.kill();
|
||||
proc.close();
|
||||
}
|
||||
|
||||
Test(popen, wd)
|
||||
{
|
||||
PopenStream proc("/usr/bin/pwd", {"pwd"}, {}, "/usr/lib");
|
||||
Test(popen, wd) {
|
||||
PopenStream proc("/usr/bin/pwd", {"pwd"}, {}, "/usr/lib");
|
||||
|
||||
std::string wd;
|
||||
std::string wd;
|
||||
|
||||
proc.cin() >> wd;
|
||||
proc.cin() >> wd;
|
||||
|
||||
cr_assert_eq(wd, "/usr/lib");
|
||||
cr_assert_eq(wd, "/usr/lib");
|
||||
|
||||
proc.kill();
|
||||
proc.close();
|
||||
proc.kill();
|
||||
proc.close();
|
||||
}
|
||||
|
||||
Test(popen, env)
|
||||
{
|
||||
PopenStream proc("echo $MYVAR", {}, {{"MYVAR", "TESTVAL"}}, std::string(), true);
|
||||
Test(popen, env) {
|
||||
PopenStream proc("echo $MYVAR", {}, {{"MYVAR", "TESTVAL"}}, std::string(),
|
||||
true);
|
||||
|
||||
std::string var;
|
||||
std::string var;
|
||||
|
||||
proc.cin() >> var;
|
||||
proc.cin() >> var;
|
||||
|
||||
cr_assert_eq(var, "TESTVAL");
|
||||
cr_assert_eq(var, "TESTVAL");
|
||||
|
||||
proc.kill();
|
||||
proc.close();
|
||||
proc.kill();
|
||||
proc.close();
|
||||
}
|
||||
|
|
|
@ -15,55 +15,56 @@
|
|||
// cppcheck-suppress unknownMacro
|
||||
TestSuite(task, .description = "Periodic timer tasks");
|
||||
|
||||
Test(task, rate, .timeout = 10)
|
||||
{
|
||||
int runs = 10;
|
||||
double rate = 5, waited;
|
||||
struct timespec start, end;
|
||||
Task task(CLOCK_MONOTONIC);
|
||||
Test(task, rate, .timeout = 10) {
|
||||
int runs = 10;
|
||||
double rate = 5, waited;
|
||||
struct timespec start, end;
|
||||
Task task(CLOCK_MONOTONIC);
|
||||
|
||||
task.setRate(rate);
|
||||
task.setRate(rate);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < runs; i++) {
|
||||
clock_gettime(CLOCK_MONOTONIC, &start);
|
||||
int i;
|
||||
for (i = 0; i < runs; i++) {
|
||||
clock_gettime(CLOCK_MONOTONIC, &start);
|
||||
|
||||
task.wait();
|
||||
task.wait();
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &end);
|
||||
clock_gettime(CLOCK_MONOTONIC, &end);
|
||||
|
||||
waited = time_delta(&start, &end);
|
||||
waited = time_delta(&start, &end);
|
||||
|
||||
if (fabs(waited - 1.0 / rate) > 10e-3)
|
||||
break;
|
||||
}
|
||||
if (fabs(waited - 1.0 / rate) > 10e-3)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i < runs)
|
||||
cr_assert_float_eq(waited, 1.0 / rate, 1e-2, "We slept for %f instead of %f secs in round %d", waited, 1.0 / rate, i);
|
||||
if (i < runs)
|
||||
cr_assert_float_eq(waited, 1.0 / rate, 1e-2,
|
||||
"We slept for %f instead of %f secs in round %d", waited,
|
||||
1.0 / rate, i);
|
||||
}
|
||||
|
||||
Test(task, wait_until, .timeout = 5)
|
||||
{
|
||||
int ret;
|
||||
struct timespec start, end, diff, future;
|
||||
Test(task, wait_until, .timeout = 5) {
|
||||
int ret;
|
||||
struct timespec start, end, diff, future;
|
||||
|
||||
Task task(CLOCK_REALTIME);
|
||||
Task task(CLOCK_REALTIME);
|
||||
|
||||
double waitfor = 3.423456789;
|
||||
double waitfor = 3.423456789;
|
||||
|
||||
start = time_now();
|
||||
diff = time_from_double(waitfor);
|
||||
future = time_add(&start, &diff);
|
||||
start = time_now();
|
||||
diff = time_from_double(waitfor);
|
||||
future = time_add(&start, &diff);
|
||||
|
||||
task.setNext(&future);
|
||||
task.setNext(&future);
|
||||
|
||||
ret = task.wait();
|
||||
ret = task.wait();
|
||||
|
||||
end = time_now();
|
||||
end = time_now();
|
||||
|
||||
cr_assert_eq(ret, 1);
|
||||
cr_assert_eq(ret, 1);
|
||||
|
||||
double waited = time_delta(&start, &end);
|
||||
double waited = time_delta(&start, &end);
|
||||
|
||||
cr_assert_float_eq(waited, waitfor, 1e-2, "We slept for %f instead of %f secs", waited, waitfor);
|
||||
cr_assert_float_eq(waited, waitfor, 1e-2,
|
||||
"We slept for %f instead of %f secs", waited, waitfor);
|
||||
}
|
||||
|
|
|
@ -5,73 +5,71 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <math.h>
|
||||
#include <criterion/criterion.h>
|
||||
#include <math.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <villas/timing.hpp>
|
||||
|
||||
// cppcheck-suppress unknownMacro
|
||||
TestSuite(timing, .description = "Time measurements");
|
||||
|
||||
Test(timing, time_now)
|
||||
{
|
||||
struct timespec now1 = time_now();
|
||||
struct timespec now2 = time_now();
|
||||
Test(timing, time_now) {
|
||||
struct timespec now1 = time_now();
|
||||
struct timespec now2 = time_now();
|
||||
|
||||
cr_assert(time_cmp(&now1, &now2) <= 0, "time_now() was reordered!");
|
||||
cr_assert(time_cmp(&now1, &now2) <= 0, "time_now() was reordered!");
|
||||
}
|
||||
|
||||
Test(timing, time_diff)
|
||||
{
|
||||
struct timespec ts1 = { .tv_sec = 0, .tv_nsec = 1}; // Value doesnt matter
|
||||
struct timespec ts2 = { .tv_sec = 1, .tv_nsec = 0}; // Overflow in nano seconds!
|
||||
Test(timing, time_diff) {
|
||||
struct timespec ts1 = {.tv_sec = 0, .tv_nsec = 1}; // Value doesnt matter
|
||||
struct timespec ts2 = {.tv_sec = 1,
|
||||
.tv_nsec = 0}; // Overflow in nano seconds!
|
||||
|
||||
struct timespec ts3 = time_diff(&ts1, &ts2);
|
||||
struct timespec ts3 = time_diff(&ts1, &ts2);
|
||||
|
||||
// ts4 == ts2?
|
||||
cr_assert_eq(ts3.tv_sec, 0);
|
||||
cr_assert_eq(ts3.tv_nsec, 999999999);
|
||||
// ts4 == ts2?
|
||||
cr_assert_eq(ts3.tv_sec, 0);
|
||||
cr_assert_eq(ts3.tv_nsec, 999999999);
|
||||
}
|
||||
|
||||
Test(timing, time_add)
|
||||
{
|
||||
struct timespec ts1 = { .tv_sec = 1, .tv_nsec = 999999999}; // Value doesnt matter
|
||||
struct timespec ts2 = { .tv_sec = 1, .tv_nsec = 1}; // Overflow in nano seconds!
|
||||
Test(timing, time_add) {
|
||||
struct timespec ts1 = {.tv_sec = 1,
|
||||
.tv_nsec = 999999999}; // Value doesnt matter
|
||||
struct timespec ts2 = {.tv_sec = 1,
|
||||
.tv_nsec = 1}; // Overflow in nano seconds!
|
||||
|
||||
struct timespec ts3 = time_add(&ts1, &ts2);
|
||||
struct timespec ts3 = time_add(&ts1, &ts2);
|
||||
|
||||
// ts4 == ts2?
|
||||
cr_assert_eq(ts3.tv_sec, 3);
|
||||
cr_assert_eq(ts3.tv_nsec, 0);
|
||||
// ts4 == ts2?
|
||||
cr_assert_eq(ts3.tv_sec, 3);
|
||||
cr_assert_eq(ts3.tv_nsec, 0);
|
||||
}
|
||||
|
||||
Test(timing, time_delta)
|
||||
{
|
||||
struct timespec ts1 = { .tv_sec = 1, .tv_nsec = 123}; // Value doesnt matter
|
||||
struct timespec ts2 = { .tv_sec = 5, .tv_nsec = 246}; // Overflow in nano seconds!
|
||||
Test(timing, time_delta) {
|
||||
struct timespec ts1 = {.tv_sec = 1, .tv_nsec = 123}; // Value doesnt matter
|
||||
struct timespec ts2 = {.tv_sec = 5,
|
||||
.tv_nsec = 246}; // Overflow in nano seconds!
|
||||
|
||||
double delta = time_delta(&ts1, &ts2);
|
||||
double delta = time_delta(&ts1, &ts2);
|
||||
|
||||
cr_assert_float_eq(delta, 4 + 123e-9, 1e-9);
|
||||
cr_assert_float_eq(delta, 4 + 123e-9, 1e-9);
|
||||
}
|
||||
|
||||
Test(timing, time_from_double)
|
||||
{
|
||||
double ref = 1234.56789;
|
||||
Test(timing, time_from_double) {
|
||||
double ref = 1234.56789;
|
||||
|
||||
struct timespec ts = time_from_double(ref);
|
||||
struct timespec ts = time_from_double(ref);
|
||||
|
||||
cr_assert_eq(ts.tv_sec, 1234);
|
||||
cr_assert_eq(ts.tv_nsec, 567890000);
|
||||
cr_assert_eq(ts.tv_sec, 1234);
|
||||
cr_assert_eq(ts.tv_nsec, 567890000);
|
||||
}
|
||||
|
||||
Test(timing, time_to_from_double)
|
||||
{
|
||||
double ref = 1234.56789;
|
||||
Test(timing, time_to_from_double) {
|
||||
double ref = 1234.56789;
|
||||
|
||||
struct timespec ts = time_from_double(ref);
|
||||
double dbl = time_to_double(&ts);
|
||||
struct timespec ts = time_from_double(ref);
|
||||
double dbl = time_to_double(&ts);
|
||||
|
||||
cr_assert_float_eq(dbl, ref, 1e-9);
|
||||
cr_assert_float_eq(dbl, ref, 1e-9);
|
||||
}
|
||||
|
|
|
@ -7,56 +7,56 @@
|
|||
|
||||
#include <criterion/criterion.h>
|
||||
|
||||
#include <villas/utils.hpp>
|
||||
#include <villas/tsc.h>
|
||||
#include <villas/timing.hpp>
|
||||
#include <villas/tsc.h>
|
||||
#include <villas/utils.hpp>
|
||||
|
||||
#define CNT (1 << 18)
|
||||
|
||||
// cppcheck-suppress unknownMacro
|
||||
TestSuite(tsc, .description = "Timestamp counters");
|
||||
|
||||
Test(tsc, increasing)
|
||||
{
|
||||
int ret;
|
||||
struct Tsc tsc;
|
||||
uint64_t *cntrs;
|
||||
Test(tsc, increasing) {
|
||||
int ret;
|
||||
struct Tsc tsc;
|
||||
uint64_t *cntrs;
|
||||
|
||||
ret = tsc_init(&tsc);
|
||||
cr_assert_eq(ret, 0);
|
||||
ret = tsc_init(&tsc);
|
||||
cr_assert_eq(ret, 0);
|
||||
|
||||
cntrs = new uint64_t[CNT];
|
||||
cr_assert_not_null(cntrs);
|
||||
cntrs = new uint64_t[CNT];
|
||||
cr_assert_not_null(cntrs);
|
||||
|
||||
for (unsigned i = 0; i < CNT; i++)
|
||||
cntrs[i] = tsc_now(&tsc);
|
||||
for (unsigned i = 0; i < CNT; i++)
|
||||
cntrs[i] = tsc_now(&tsc);
|
||||
|
||||
for (unsigned i = 1; i < CNT; i++)
|
||||
cr_assert_lt(cntrs[i-1], cntrs[i]);
|
||||
for (unsigned i = 1; i < CNT; i++)
|
||||
cr_assert_lt(cntrs[i - 1], cntrs[i]);
|
||||
|
||||
delete cntrs;
|
||||
delete cntrs;
|
||||
}
|
||||
|
||||
Test(tsc, sleep)
|
||||
{
|
||||
int ret;
|
||||
double delta, duration = 1;
|
||||
struct timespec start, stop;
|
||||
struct Tsc tsc;
|
||||
uint64_t start_cycles, end_cycles;
|
||||
Test(tsc, sleep) {
|
||||
int ret;
|
||||
double delta, duration = 1;
|
||||
struct timespec start, stop;
|
||||
struct Tsc tsc;
|
||||
uint64_t start_cycles, end_cycles;
|
||||
|
||||
ret = tsc_init(&tsc);
|
||||
cr_assert_eq(ret, 0);
|
||||
ret = tsc_init(&tsc);
|
||||
cr_assert_eq(ret, 0);
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &start);
|
||||
clock_gettime(CLOCK_MONOTONIC, &start);
|
||||
|
||||
start_cycles = tsc_now(&tsc);
|
||||
end_cycles = start_cycles + duration * tsc.frequency;
|
||||
start_cycles = tsc_now(&tsc);
|
||||
end_cycles = start_cycles + duration * tsc.frequency;
|
||||
|
||||
while (tsc_now(&tsc) < end_cycles);
|
||||
while (tsc_now(&tsc) < end_cycles)
|
||||
;
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &stop);
|
||||
delta = time_delta(&start, &stop);
|
||||
clock_gettime(CLOCK_MONOTONIC, &stop);
|
||||
delta = time_delta(&start, &stop);
|
||||
|
||||
cr_assert_float_eq(delta, duration, 1e-4, "Error: %f, Delta: %lf, Freq: %llu", delta - duration, delta, tsc.frequency);
|
||||
cr_assert_float_eq(delta, duration, 1e-4, "Error: %f, Delta: %lf, Freq: %llu",
|
||||
delta - duration, delta, tsc.frequency);
|
||||
}
|
||||
|
|
|
@ -8,10 +8,10 @@
|
|||
#include <criterion/criterion.h>
|
||||
|
||||
#include <villas/colors.hpp>
|
||||
#include <villas/utils.hpp>
|
||||
#include <villas/version.hpp>
|
||||
#include <villas/cpuset.hpp>
|
||||
#include <villas/log.hpp>
|
||||
#include <villas/utils.hpp>
|
||||
#include <villas/version.hpp>
|
||||
|
||||
using namespace villas::utils;
|
||||
|
||||
|
@ -19,19 +19,21 @@ using namespace villas::utils;
|
|||
TestSuite(utils, .description = "Utilities");
|
||||
|
||||
// Simple normality test for 1,2,3s intervals
|
||||
Test(utils, box_muller)
|
||||
{
|
||||
double n;
|
||||
unsigned sigma[3] = { 0 };
|
||||
unsigned iter = 1000000;
|
||||
Test(utils, box_muller) {
|
||||
double n;
|
||||
unsigned sigma[3] = {0};
|
||||
unsigned iter = 1000000;
|
||||
|
||||
for (unsigned i = 0; i < iter; i++) {
|
||||
n = boxMuller(0, 1);
|
||||
for (unsigned i = 0; i < iter; i++) {
|
||||
n = boxMuller(0, 1);
|
||||
|
||||
if (n > 2 || n < -2) sigma[2]++;
|
||||
else if (n > 1 || n < -1) sigma[1]++;
|
||||
else sigma[0]++;
|
||||
}
|
||||
if (n > 2 || n < -2)
|
||||
sigma[2]++;
|
||||
else if (n > 1 || n < -1)
|
||||
sigma[1]++;
|
||||
else
|
||||
sigma[0]++;
|
||||
}
|
||||
|
||||
#if 0
|
||||
printf("%f %f %f\n",
|
||||
|
@ -40,179 +42,173 @@ Test(utils, box_muller)
|
|||
(double) sigma[0] / iter);
|
||||
#endif
|
||||
|
||||
// The random variable generated by the Box Muller transform is
|
||||
// not an ideal normal distributed variable.
|
||||
// The numbers from below are empirically measured.
|
||||
cr_assert_float_eq((double) sigma[2] / iter, 0.045527, 1e-2);
|
||||
cr_assert_float_eq((double) sigma[1] / iter, 0.271644, 1e-2);
|
||||
cr_assert_float_eq((double) sigma[0] / iter, 0.682829, 1e-2);
|
||||
// The random variable generated by the Box Muller transform is
|
||||
// not an ideal normal distributed variable.
|
||||
// The numbers from below are empirically measured.
|
||||
cr_assert_float_eq((double)sigma[2] / iter, 0.045527, 1e-2);
|
||||
cr_assert_float_eq((double)sigma[1] / iter, 0.271644, 1e-2);
|
||||
cr_assert_float_eq((double)sigma[0] / iter, 0.682829, 1e-2);
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
Test(utils, cpuset)
|
||||
{
|
||||
using villas::utils::CpuSet;
|
||||
Test(utils, cpuset) {
|
||||
using villas::utils::CpuSet;
|
||||
|
||||
uintmax_t int1 = 0x1234567890ABCDEFULL;
|
||||
uintmax_t int1 = 0x1234567890ABCDEFULL;
|
||||
|
||||
CpuSet cset1(int1);
|
||||
CpuSet cset1(int1);
|
||||
|
||||
std::string cset1_str = cset1;
|
||||
std::string cset1_str = cset1;
|
||||
|
||||
CpuSet cset2(cset1_str);
|
||||
CpuSet cset2(cset1_str);
|
||||
|
||||
cr_assert_eq(cset1, cset2);
|
||||
cr_assert_eq(cset1, cset2);
|
||||
|
||||
uintmax_t int2 = cset2;
|
||||
uintmax_t int2 = cset2;
|
||||
|
||||
cr_assert_eq(int1, int2);
|
||||
cr_assert_eq(int1, int2);
|
||||
|
||||
CpuSet cset3("1-5");
|
||||
CpuSet cset4("1,2,3,4,5");
|
||||
CpuSet cset3("1-5");
|
||||
CpuSet cset4("1,2,3,4,5");
|
||||
|
||||
cr_assert_eq(cset3, cset4);
|
||||
cr_assert_eq(cset3.count(), 5);
|
||||
cr_assert_eq(cset3, cset4);
|
||||
cr_assert_eq(cset3.count(), 5);
|
||||
|
||||
cr_assert(cset3.isSet(3));
|
||||
cr_assert_not(cset3.isSet(6));
|
||||
cr_assert(cset3.isSet(3));
|
||||
cr_assert_not(cset3.isSet(6));
|
||||
|
||||
cr_assert(cset3[3]);
|
||||
cr_assert_not(cset3[6]);
|
||||
cr_assert(cset3[3]);
|
||||
cr_assert_not(cset3[6]);
|
||||
|
||||
cset4.set(6);
|
||||
cr_assert(cset4[6]);
|
||||
cset4.set(6);
|
||||
cr_assert(cset4[6]);
|
||||
|
||||
cset4.clear(6);
|
||||
cr_assert_not(cset4[6]);
|
||||
cset4.clear(6);
|
||||
cr_assert_not(cset4[6]);
|
||||
|
||||
cr_assert_str_eq(static_cast<std::string>(cset4).c_str(), "1-5");
|
||||
cr_assert_str_eq(static_cast<std::string>(cset4).c_str(), "1-5");
|
||||
|
||||
cr_assert_any_throw(CpuSet cset5("0-"));
|
||||
cr_assert_any_throw(CpuSet cset5("0-"));
|
||||
|
||||
CpuSet cset6;
|
||||
cr_assert(cset6.empty());
|
||||
cr_assert_eq(cset6.count(), 0);
|
||||
CpuSet cset6;
|
||||
cr_assert(cset6.empty());
|
||||
cr_assert_eq(cset6.count(), 0);
|
||||
|
||||
cr_assert((~cset6).full());
|
||||
cr_assert((cset1 | ~cset1).full());
|
||||
cr_assert((cset1 ^ cset1).empty());
|
||||
cr_assert((cset1 & cset6).empty());
|
||||
cr_assert((~cset6).full());
|
||||
cr_assert((cset1 | ~cset1).full());
|
||||
cr_assert((cset1 ^ cset1).empty());
|
||||
cr_assert((cset1 & cset6).empty());
|
||||
|
||||
cset1.zero();
|
||||
cr_assert(cset1.empty());
|
||||
cset1.zero();
|
||||
cr_assert(cset1.empty());
|
||||
}
|
||||
#endif // __linux__
|
||||
|
||||
Test(utils, memdup)
|
||||
{
|
||||
char orig[1024], *copy;
|
||||
size_t len;
|
||||
Test(utils, memdup) {
|
||||
char orig[1024], *copy;
|
||||
size_t len;
|
||||
|
||||
len = readRandom(orig, sizeof(orig));
|
||||
cr_assert_eq(len, sizeof(orig));
|
||||
len = readRandom(orig, sizeof(orig));
|
||||
cr_assert_eq(len, sizeof(orig));
|
||||
|
||||
copy = (char *) memdup(orig, sizeof(orig));
|
||||
cr_assert_not_null(copy);
|
||||
cr_assert_arr_eq(copy, orig, sizeof(orig));
|
||||
copy = (char *)memdup(orig, sizeof(orig));
|
||||
cr_assert_not_null(copy);
|
||||
cr_assert_arr_eq(copy, orig, sizeof(orig));
|
||||
|
||||
free(copy);
|
||||
free(copy);
|
||||
}
|
||||
|
||||
Test(utils, is_aligned)
|
||||
{
|
||||
// Positive
|
||||
cr_assert(IS_ALIGNED(1, 1));
|
||||
cr_assert(IS_ALIGNED(128, 64));
|
||||
Test(utils, is_aligned) {
|
||||
// Positive
|
||||
cr_assert(IS_ALIGNED(1, 1));
|
||||
cr_assert(IS_ALIGNED(128, 64));
|
||||
|
||||
// Negative
|
||||
cr_assert(!IS_ALIGNED(55, 16));
|
||||
cr_assert(!IS_ALIGNED(55, 55));
|
||||
cr_assert(!IS_ALIGNED(1128, 256));
|
||||
// Negative
|
||||
cr_assert(!IS_ALIGNED(55, 16));
|
||||
cr_assert(!IS_ALIGNED(55, 55));
|
||||
cr_assert(!IS_ALIGNED(1128, 256));
|
||||
}
|
||||
|
||||
Test(utils, ceil)
|
||||
{
|
||||
cr_assert_eq(CEIL(10, 3), 4);
|
||||
cr_assert_eq(CEIL(10, 5), 2);
|
||||
cr_assert_eq(CEIL(4, 3), 2);
|
||||
Test(utils, ceil) {
|
||||
cr_assert_eq(CEIL(10, 3), 4);
|
||||
cr_assert_eq(CEIL(10, 5), 2);
|
||||
cr_assert_eq(CEIL(4, 3), 2);
|
||||
}
|
||||
|
||||
Test(utils, is_pow2)
|
||||
{
|
||||
// Positive
|
||||
cr_assert(IS_POW2(1));
|
||||
cr_assert(IS_POW2(2));
|
||||
cr_assert(IS_POW2(64));
|
||||
Test(utils, is_pow2) {
|
||||
// Positive
|
||||
cr_assert(IS_POW2(1));
|
||||
cr_assert(IS_POW2(2));
|
||||
cr_assert(IS_POW2(64));
|
||||
|
||||
// Negative
|
||||
cr_assert(!IS_POW2(0));
|
||||
cr_assert(!IS_POW2(3));
|
||||
cr_assert(!IS_POW2(11111));
|
||||
cr_assert(!IS_POW2(-1));
|
||||
// Negative
|
||||
cr_assert(!IS_POW2(0));
|
||||
cr_assert(!IS_POW2(3));
|
||||
cr_assert(!IS_POW2(11111));
|
||||
cr_assert(!IS_POW2(-1));
|
||||
}
|
||||
|
||||
Test(utils, strf)
|
||||
{
|
||||
char *buf = nullptr;
|
||||
Test(utils, strf) {
|
||||
char *buf = nullptr;
|
||||
|
||||
buf = strcatf(&buf, "Hallo %s", "Steffen.");
|
||||
cr_assert_str_eq(buf, "Hallo Steffen.");
|
||||
buf = strcatf(&buf, "Hallo %s", "Steffen.");
|
||||
cr_assert_str_eq(buf, "Hallo Steffen.");
|
||||
|
||||
strcatf(&buf, " Its Monday %uth %s %u.", 13, "August", 2018);
|
||||
cr_assert_str_eq(buf, "Hallo Steffen. Its Monday 13th August 2018.");
|
||||
strcatf(&buf, " Its Monday %uth %s %u.", 13, "August", 2018);
|
||||
cr_assert_str_eq(buf, "Hallo Steffen. Its Monday 13th August 2018.");
|
||||
|
||||
free(buf);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
Test(utils, version)
|
||||
{
|
||||
using villas::utils::Version;
|
||||
Test(utils, version) {
|
||||
using villas::utils::Version;
|
||||
|
||||
Version v1 = Version("1.2");
|
||||
Version v2 = Version("1.3");
|
||||
Version v3 = Version("55");
|
||||
Version v4 = Version("66");
|
||||
Version v5 = Version(66);
|
||||
Version v6 = Version(1, 2, 5);
|
||||
Version v7 = Version("1.2.5");
|
||||
Version v1 = Version("1.2");
|
||||
Version v2 = Version("1.3");
|
||||
Version v3 = Version("55");
|
||||
Version v4 = Version("66");
|
||||
Version v5 = Version(66);
|
||||
Version v6 = Version(1, 2, 5);
|
||||
Version v7 = Version("1.2.5");
|
||||
|
||||
cr_assert_lt(v1, v2);
|
||||
cr_assert_eq(v1, v1);
|
||||
cr_assert_gt(v2, v1);
|
||||
cr_assert_lt(v3, v4);
|
||||
cr_assert_eq(v4, v5);
|
||||
cr_assert_eq(v6, v7);
|
||||
cr_assert_lt(v1, v2);
|
||||
cr_assert_eq(v1, v1);
|
||||
cr_assert_gt(v2, v1);
|
||||
cr_assert_lt(v3, v4);
|
||||
cr_assert_eq(v4, v5);
|
||||
cr_assert_eq(v6, v7);
|
||||
}
|
||||
|
||||
Test(utils, sha1sum)
|
||||
{
|
||||
int ret;
|
||||
FILE *f = tmpfile();
|
||||
Test(utils, sha1sum) {
|
||||
int ret;
|
||||
FILE *f = tmpfile();
|
||||
|
||||
unsigned char hash[SHA_DIGEST_LENGTH];
|
||||
unsigned char expected[SHA_DIGEST_LENGTH] = { 0x69, 0xdf, 0x29, 0xdf, 0x1f, 0xf2, 0xd2, 0x5d, 0xb8, 0x68, 0x6c, 0x02, 0x8d, 0xdf, 0x40, 0xaf, 0xb3, 0xc1, 0xc9, 0x4d };
|
||||
unsigned char hash[SHA_DIGEST_LENGTH];
|
||||
unsigned char expected[SHA_DIGEST_LENGTH] = {
|
||||
0x69, 0xdf, 0x29, 0xdf, 0x1f, 0xf2, 0xd2, 0x5d, 0xb8, 0x68,
|
||||
0x6c, 0x02, 0x8d, 0xdf, 0x40, 0xaf, 0xb3, 0xc1, 0xc9, 0x4d};
|
||||
|
||||
// Write the first 512 fibonaccia numbers to the file
|
||||
for (int i = 0, a = 0, b = 1, c; i < 512; i++, a = b, b = c) {
|
||||
c = a + b;
|
||||
// Write the first 512 fibonaccia numbers to the file
|
||||
for (int i = 0, a = 0, b = 1, c; i < 512; i++, a = b, b = c) {
|
||||
c = a + b;
|
||||
|
||||
fwrite((void *) &c, sizeof(c), 1, f);
|
||||
}
|
||||
fwrite((void *)&c, sizeof(c), 1, f);
|
||||
}
|
||||
|
||||
ret = sha1sum(f, hash);
|
||||
ret = sha1sum(f, hash);
|
||||
|
||||
cr_assert_eq(ret, 0);
|
||||
cr_assert_arr_eq(hash, expected, SHA_DIGEST_LENGTH);
|
||||
cr_assert_eq(ret, 0);
|
||||
cr_assert_arr_eq(hash, expected, SHA_DIGEST_LENGTH);
|
||||
|
||||
fclose(f);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
Test(utils, decolor)
|
||||
{
|
||||
char str[] = "This " CLR_RED("is") " a " CLR_BLU("colored") " " CLR_BLD("text!");
|
||||
char expect[] = "This is a colored text!";
|
||||
Test(utils, decolor) {
|
||||
char str[] =
|
||||
"This " CLR_RED("is") " a " CLR_BLU("colored") " " CLR_BLD("text!");
|
||||
char expect[] = "This is a colored text!";
|
||||
|
||||
decolor(str);
|
||||
decolor(str);
|
||||
|
||||
cr_assert_str_eq(str, expect);
|
||||
cr_assert_str_eq(str, expect);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue