1
0
Fork 0
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:
Steffen Vogel 2023-09-07 13:19:19 +02:00 committed by Steffen Vogel
parent fd93e2be0a
commit b6219e9163
76 changed files with 4920 additions and 5658 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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.

View file

@ -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__

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -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;
};

View file

@ -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

View file

@ -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();

View file

@ -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

View file

@ -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);

View file

@ -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 {

View file

@ -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);

View file

@ -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

View file

@ -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

View file

@ -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;
}

View file

@ -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 "";
}
}

View file

@ -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

View file

@ -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__

View file

@ -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;
}

View file

@ -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 Knuths 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 Knuths 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

View file

@ -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__

View file

@ -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;
}

View 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, &param);
if (ret)
throw SystemError("Failed to set real time priority");
ret = sched_setscheduler(0, SCHED_FIFO, &param);
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

View file

@ -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;
}

View file

@ -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(&region, 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, &region);
if (ret < 0)
throw RuntimeError("Failed to get region {} of VFIO device: {}", i, name);
ret = ioctl(fd, VFIO_DEVICE_GET_REGION_INFO, &region);
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 &region : regions) {
regionUnmap(region.index);
}
if (isVfioPciDevice()) {
pciHotReset();
}
reset();
for (auto &region : 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 = &regions[index];
void *Device::regionMap(size_t index) {
struct vfio_region_info *r = &regions[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 = &regions[index];
bool Device::regionUnmap(size_t index) {
int ret;
struct vfio_region_info *r = &regions[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 = &regions[i];
for (size_t i = 0; i < info.num_regions && i < 8; i++) {
struct vfio_region_info *region = &regions[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, &reg, sizeof(reg), offset);
if (ret != sizeof(reg))
return false;
ret = pread64(this->fd, &reg, 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, &reg, sizeof(reg), offset);
if (ret != sizeof(reg))
return false;
ret = pwrite64(this->fd, &reg, 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;
}

View file

@ -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);
}
}

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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

View file

@ -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

View file

@ -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());
}

View file

@ -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

View file

@ -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);
}

View file

@ -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
}

View file

@ -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, &current->window);
if (ret)
throw SystemError("Failed to get terminal dimensions");
ret = ioctl(STDERR_FILENO, TIOCGWINSZ, &current->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);
};

View file

@ -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;
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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"));
}

View file

@ -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]);
}
}

View file

@ -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();
}

View file

@ -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);
}

View file

@ -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

View file

@ -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);
}

View file

@ -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();
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);
}