Pārlūkot izejas kodu

Better logging system +goodies

channel
Ludovic 'Archivist' Lagouardette pirms 3 gadiem
vecāks
revīzija
92b4b971c4
10 mainītis faili ar 555 papildinājumiem un 24 dzēšanām
  1. +1
    -1
      Makefile
  2. +13
    -0
      include/gp/containers/buffer.hpp
  3. +185
    -0
      include/gp/containers/dynarray.hpp
  4. +19
    -5
      include/gp/functional/optional.hpp
  5. +16
    -16
      include/gp/functional/variant.hpp
  6. +126
    -0
      include/gp/system/logging_segment.hpp
  7. +98
    -0
      include/gp/utils/pointers.hpp
  8. +35
    -2
      include/gp_config.hpp
  9. +32
    -0
      tests.cpp
  10. +30
    -0
      tests/logging_test.cpp

+ 1
- 1
Makefile Parādīt failu

@ -1,5 +1,5 @@
CXX= clang++
CXXFLAGS= --std=c++20 -O0 -g -pthread -DGP_TESTS -DFUZZ_STRENGTH=100000 -DNO_BENCH=0 -pedantic \
CXXFLAGS= --std=c++20 -O0 -g -pthread -DGP_TESTS -DFUZZ_STRENGTH=100 -DNO_BENCH=1 -pedantic \
-fprofile-instr-generate -fcoverage-mapping -Wno-unknown-attributes -fno-omit-frame-pointer \
# -fsanitize=address -fsanitize-blacklist=blacklist.txt

+ 13
- 0
include/gp/containers/buffer.hpp Parādīt failu

@ -17,9 +17,22 @@ namespace gp{
private:
pointer_iterator<T> begin_elem;
pointer_iterator<T> end_elem;
static constexpr size_t local_strlen(const char* str) {
auto ptr = str;
do{}while(*(ptr++));
return ptr-str;
}
public:
using associated_iterator = pointer_iterator<T>;
constexpr buffer(const char* val) requires std::is_same_v<T, char>
: begin_elem{const_cast<char*>(val)}
, end_elem{const_cast<char*>(val)+local_strlen(val)}
{}
constexpr buffer(T* beg_ptr, T* end_ptr)
: begin_elem{beg_ptr}
, end_elem{end_ptr}

+ 185
- 0
include/gp/containers/dynarray.hpp Parādīt failu

@ -0,0 +1,185 @@
#pragma once
#include "gp/containers/array.hpp"
namespace gp {
template<typename T, size_t cap>
class dynarray {
struct build_order{};
struct v_element{
char data[sizeof(T)];
operator T&() {
return *(T*)data;
}
v_element()
: data{}
{}
template<typename ...Args>
v_element(build_order, Args&&... argv) {
new(data) T(gp::forward<Args>(argv)...);
}
template<typename arg>
T& operator=(arg& argv) {
return *(T*)data = argv;
}
template<typename arg>
T& operator=(arg&& argv) {
return *(T*)data = gp::forward<arg>(argv);
}
template<typename ...Args>
void build(Args&&... argv) {
new(data) T(gp::forward<Args>(argv)...);
}
T& value() {
return *(T*)data;
}
T& value() const {
return *(T*)data;
}
void clear(){
((T*)data)->~T();
}
};
using associated_iterator = pointer_iterator<T, 1>;
using associated_const_iterator = const_pointer_iterator<T, 1>;
using associated_riterator = pointer_iterator<T, -1>;
using associated_const_riterator = const_pointer_iterator<T, -1>;
v_element data[cap];
size_t sz = 0;
public:
dynarray() = default;
dynarray(dynarray& oth)
{
for(auto& ref : oth) {
push_back(ref);
}
}
/*dynarray(dynarray&& oth)
{
for(auto& ref : oth) {
emplace_back(gp::forward<T>(ref));
}
}*/
constexpr associated_iterator begin()
{
return associated_iterator((T*)&data[0]);
}
constexpr associated_iterator end()
{
return associated_iterator((T*)&data[sz]);
}
constexpr associated_const_iterator cbegin() const
{
return associated_const_iterator((T*)&data[0]);
}
constexpr associated_const_iterator cend() const
{
return associated_const_iterator((T*)&data[sz]);
}
constexpr associated_riterator rbegin()
{
return associated_riterator((T*)&data[sz-1]);
}
constexpr associated_riterator rend()
{
return associated_riterator((T*)data-1);
}
constexpr associated_const_riterator crbegin() const
{
return associated_const_riterator((T*)&data[sz-1]);
}
constexpr associated_const_riterator crend() const
{
return associated_const_riterator((T*)data-1);
}
constexpr bool operator==(const dynarray& oth) const
{
if(size() != oth.size()) return false;
for(size_t idx = 0; idx<sz; idx++)
{
if(data[idx].value() != oth.data[idx].value())
{
return false;
}
}
return true;
}
constexpr bool operator!=(const dynarray& oth) const
{
return !(*this == oth);
}
size_t size() const {
return sz;
}
constexpr size_t capacity() const {
return cap;
}
T& operator[] (size_t idx) {
return data[idx];
}
const T& operator[] (size_t idx) const {
return data[idx];
}
void push_back(T& value) {
data[sz].build(value);
++sz;
}
void push_back(T&& value) {
data[sz].build(value);
++sz;
}
template<typename ...Args>
void emplace_back(Args&&... argv) {
data[sz].build(gp::forward<Args>(argv)...);
++sz;
}
void remove_back() {
--sz;
data[sz].clear();
}
~dynarray() {
auto it = data;
auto end = data + size();
while(it != end) {
(*it).clear();
++it;
}
}
};
}

+ 19
- 5
include/gp/functional/optional.hpp Parādīt failu

@ -17,7 +17,7 @@ namespace gp{
template<typename T>
class optional final{
bool ready = false;
char buffer[sizeof(T)];
">alignas(T) char buffer[sizeof(T)];
public:
constexpr optional()
: ready{false}
@ -36,7 +36,7 @@ namespace gp{
constexpr optional(T&& value)
: ready{true}
{
new(buffer) T(gp::move(value));
new(buffer) T(gp::forward<T>(value));
}
~optional() {
@ -65,16 +65,16 @@ namespace gp{
optional& operator=(T&& value) {
if(ready) {
*(T*)buffer = gp::move(value);
*(T*)buffer = gp::forward<T>(value);
} else {
ready = true;
new(buffer) T(gp::move(value));
new(buffer) T(gp::forward<T>(value));
}
return *this;
}
constexpr bool has_value()
constexpr bool has_value() const
{
return ready;
}
@ -92,5 +92,19 @@ namespace gp{
}
return *reinterpret_cast<T*>(buffer);
}
constexpr const T& value() const
{
if constexpr (gp_config::has_exceptions)
{
if(!ready)
{
throw bad_optional{};
}
} else {
gp_config::assertion(ready, "bad optional access");
}
return *reinterpret_cast<const T*>(buffer);
}
};
}

+ 16
- 16
include/gp/functional/variant.hpp Parādīt failu

@ -23,7 +23,7 @@ namespace gp{
template<typename ...T>
class fixed_variant final {
std::size_t index = std::numeric_limits<std::size_t>::max();
t">char buffer[max_size<T...>()];
">alignas(gp::max(alignof(T)...)) char data[max_size<T...>()];
gp::function<void(void*, void*)> cpytor = {[](void*, void*){}, nullopt};
gp::function<void(void*, void*)> mvtor = {[](void*, void*){}, nullopt};
gp::function<void(void*)> dtor = {[](void*){}, nullopt};
@ -54,7 +54,7 @@ namespace gp{
mvtor = gp::function<void(void*, void*)>([](void* src, void* dest){
new(dest) actual(gp::move(*(actual*)src));
}, nullopt);
cpytor((char*)&value, buffer);
cpytor((char*)&value, data);
}
static_assert(list_contains_class<int, int>::value, "list_contains_class doesn't work properly");
@ -83,7 +83,7 @@ namespace gp{
mvtor = gp::function<void(void*, void*)>([](void* src, void* dest){
new(dest) actual(gp::move(*(actual*)src));
}, nullopt);
mvtor((char*)&value, buffer);
mvtor((char*)&value, data);
}
fixed_variant(const fixed_variant& oth)
@ -92,7 +92,7 @@ namespace gp{
, cpytor{oth.cpytor}
, mvtor{oth.mvtor}
{
cpytor((char*)oth.buffer, (char*)buffer);
cpytor((char*)oth.data, (char*)data);
}
fixed_variant(fixed_variant& oth)
@ -101,7 +101,7 @@ namespace gp{
, cpytor{oth.cpytor}
, mvtor{oth.mvtor}
{
cpytor(oth.buffer, buffer);
cpytor(oth.data, data);
}
fixed_variant(fixed_variant&& oth)
@ -111,7 +111,7 @@ namespace gp{
, mvtor{oth.mvtor}
{
oth.index = std::numeric_limits<std::size_t>::max();
mvtor(oth.buffer, buffer);
mvtor(oth.data, data);
}
/**
@ -140,27 +140,27 @@ namespace gp{
{
if(index != std::numeric_limits<std::size_t>::max())
{
dtor((void*)buffer);
dtor((void*)data);
}
index = value.index;
cpytor = value.cpytor;
dtor = value.dtor;
mvtor = value.mvtor;
cpytor(value.buffer, buffer);
cpytor(value.data, data);
}
void operator=(fixed_variant&& value)
{
if(index != std::numeric_limits<std::size_t>::max())
{
dtor((void*)buffer);
dtor((void*)data);
}
dtor = value.dtor;
cpytor = value.cpytor;
mvtor = value.mvtor;
index = value.index;
value.index = std::numeric_limits<std::size_t>::max();
mvtor(value.buffer, buffer);
mvtor(value.data, data);
}
template<typename U, typename std::enable_if<list_contains_class<U,T...>::value,int>::type>
@ -168,10 +168,10 @@ namespace gp{
{
if(index != std::numeric_limits<std::size_t>::max())
{
dtor((void*)buffer);
dtor((void*)data);
}
index = r_index_of<gp::remove_cvref_t<U>, T...>::value;
new(buffer) U(value);
new(data) U(value);
dtor = gp::function([](void* thing){((U*)thing)->~U();}, nullopt);
cpytor = gp::function<void(void*, void*)>([](void* src, void* dest){new(dest) U(*(U*)src);}, nullopt);
mvtor = gp::function<void(void*, void*)>([](void* src, void* dest){new(dest) U(gp::move(*(U*)src));}, nullopt);
@ -182,9 +182,9 @@ namespace gp{
{
if(index != std::numeric_limits<std::size_t>::max())
{
dtor((void*)buffer);
dtor((void*)data);
}
new(buffer) U(gp::move(value));
new(data) U(gp::move(value));
index = r_index_of<gp::remove_cvref_t<U>, T...>::value;
dtor = gp::function([](void* thing){((U*)thing)->~U();}, nullopt);
cpytor = gp::function<void(void*, void*)>([](void* src, void* dest){new(dest) U(*(U*)src);}, nullopt);
@ -195,7 +195,7 @@ namespace gp{
{
if(index != std::numeric_limits<std::size_t>::max() && dtor.ready())
{
dtor((void*)buffer);
dtor((void*)data);
index = std::numeric_limits<std::size_t>::max();
}
}
@ -217,7 +217,7 @@ namespace gp{
throw bad_variant_access<U>{};
}
}
return *reinterpret_cast<U*>(buffer);
return *reinterpret_cast<U*>(data);
}
/**

+ 126
- 0
include/gp/system/logging_segment.hpp Parādīt failu

@ -0,0 +1,126 @@
#pragma once
#include "gp/algorithms/min_of.hpp"
#include "gp/containers/dynarray.hpp"
#include "gp/containers/vector.hpp"
#include "gp/functional/optional.hpp"
#include <stdint.h>
struct segment{
int16_t priority = 0;
gp::dynarray<char, 32> name;
gp::dynarray<char, gp_config::limits::loggers_segment_size> text;
};
struct virtual_logging_segment{
virtual size_t push_segment(gp::buffer<char> name, gp::buffer<char> text, int16_t prio = 0) = 0;
virtual void set_segment(gp::buffer<char> name, gp::buffer<char> text, int16_t prio = 0) = 0;
virtual gp::optional<gp::reference_wrapper<segment>> get_segment(gp::buffer<char> name) = 0;
virtual size_t size() = 0;
virtual segment& get_segment_by_idx(size_t) = 0;
virtual void clear() = 0;
};
template<size_t capacity>
struct static_logging_segment : virtual_logging_segment{
gp::dynarray<segment, capacity> data;
gp::optional<size_t> wrap_around;
gp::function<void(virtual_logging_segment&)> on_destruct = +[](virtual_logging_segment&){};
~static_logging_segment() {
on_destruct(*this);
}
segment prepare_segment(gp::buffer<char> name, gp::buffer<char> text, int prio = 0) {
segment prepared;
prepared.priority = prio;
{
auto it = name.begin();
auto end = name.begin()+gp::min(name.size(), prepared.name.capacity());
while(it != end) {
prepared.name.push_back(*it);
++it;
}
}
{
auto it = text.begin();
auto end = text.begin()+gp::min(text.size(), prepared.text.capacity());
while(it != end) {
prepared.text.push_back(*it);
++it;
}
if(text.size()>prepared.text.capacity()) {
auto it = prepared.text.rbegin();
*it = '.'; ++it;
*it = '.'; ++it;
*it = '.';
}
}
return prepared;
}
virtual size_t push_segment(gp::buffer<char> name, gp::buffer<char> text, int16_t prio = 0) {
auto prepared = prepare_segment(name, text, prio);
if(wrap_around.has_value()) {
size_t idx = wrap_around.value();
size_t override_prio = gp::min_of(data.begin(), data.end(), [](auto v){ return v.priority; });
while(true) {
if(data[idx].priority == override_prio) {
data[idx] = prepared;
break;
}
idx = (++idx) % data.size();
}
idx = (++idx) % data.size();
wrap_around = idx;
return idx-1;
} else {
data.push_back(prepared);
return data.size()-1;
}
}
virtual void set_segment(gp::buffer<char> name, gp::buffer<char> text, int16_t prio = 0) {
auto prepared = prepare_segment(name, text, prio);
if(auto seg = get_segment(name); seg.has_value()) {
seg.value() = prepared;
} else {
data.push_back(prepared);
}
}
virtual gp::optional<gp::reference_wrapper<segment>> get_segment(gp::buffer<char> name) {
decltype(segment{}.name) tname;
auto it = name.begin();
auto end = name.begin()+gp::min(name.size(), tname.capacity());
while(it != end) {
tname.push_back(*it);
++it;
}
for(auto& elem : data) {
if(elem.name == tname) {
return gp::reference_wrapper<segment>(elem);
}
}
return gp::nullopt;
}
virtual size_t size() {
return data.size();
}
virtual segment& get_segment_by_idx(size_t idx) {
return data[idx];
}
virtual void clear() {
data.~dynarray();
new(&data) decltype(data){};
}
};
/*struct dynamic_logging_segment{
};*/

+ 98
- 0
include/gp/utils/pointers.hpp Parādīt failu

@ -6,6 +6,7 @@
#include "gp/functional/function.hpp"
#include <atomic>
#include <concepts>
namespace gp {
@ -165,4 +166,101 @@ namespace gp {
}
};
template<std::copyable T>
struct resource_traits {
using identifier = uint64_t;
static constexpr bool create_if_not_exist = false;
static T* create(const identifier&, gp::allocator&);
static T* retrieve(const identifier&, gp::allocator&);
static bool imprint(const identifier&, const T& value);
static bool annihilate(const identifier&);
};
template<std::copyable T>
requires std::copyable<typename resource_traits<T>::identifier>
class resource_ptr
{
typename resource_traits<T>::identifier _identifier;
gp::allocator& _alloc;
T* value = nullptr;
operator T&() {
if(!value) {
value = resource_traits<T>::retrieve(_identifier, _alloc);
}
if(value) return *value;
else if constexpr (resource_traits<T>::create_if_not_exist) {
value = resource_traits<T>::create(_identifier, _alloc);
if(value) return *value;
}
gp_config::assertion(false, "unavailable resource lead to crash");
}
gp::optional<T&> read() {
if(!value) {
value = resource_traits<T>::retrieve(_identifier, _alloc);
}
if(value) return *value;
else if constexpr (resource_traits<T>::create_if_not_exist) {
value = resource_traits<T>::create(_identifier, _alloc);
if(value) return *value;
}
return gp::nullopt;
}
gp::optional<T&> force_read() {
value = resource_traits<T>::retrieve(_identifier, _alloc);
if(value) return *value;
return gp::nullopt;
}
template<typename U>
requires std::is_base_of_v<T, U>
gp::optional<T&> operator=(U& new_value) {
if(resource_traits<T>::imprint(_identifier, new_value)) {
if(value) {
value->~T();
gp_config::assertion(_alloc.deallocate((void*)value), "deallocation failure during exchange");
}
value = _alloc.allocate(sizeof(U));
gp_config::assertion(value != nullptr, "could not allocate during resource transcription");
new(value) U(new_value);
return *value;
}
return gp::nullopt;
}
template<typename U>
requires std::is_base_of_v<T, U>
gp::optional<T&> operator=(const U& new_value) {
if(resource_traits<T>::imprint(_identifier, new_value)) {
if(value) {
value->~T();
gp_config::assertion(_alloc.deallocate((void*)value), "deallocation failure during exchange");
}
value = _alloc.allocate(sizeof(U));
gp_config::assertion(value != nullptr, "could not allocate during resource transcription");
new(value) U(new_value);
return *value;
}
return gp::nullopt;
}
template<>
gp::optional<T&> operator=<gp::nullopt_t>(const gp::nullopt_t) {
if(resource_traits<T>::annihilate(_identifier)) {
if(value) {
value->~T();
gp_config::assertion(_alloc.deallocate((void*)value), "deallocation failure during exchange");
}
value = nullptr;
}
return gp::nullopt;
}
};
}

+ 35
- 2
include/gp_config.hpp Parādīt failu

@ -15,6 +15,9 @@ namespace gp {
}
#endif
extern void log_failure(const char* failure);
extern void log_segment(const char* name, const char* text, int16_t prio = 0);
/**
* @brief This namespace contains the configuration.
*
@ -90,6 +93,8 @@ namespace gp_config{
constexpr size_t hardware_constructive_interference_size = 128;
constexpr size_t hardware_destructive_interference_size = 128;
#endif
constexpr size_t loggers_segment_size = 128;
}
namespace memory_module{
@ -140,8 +145,36 @@ namespace gp_config{
* @brief an assertion function
*/
constexpr auto assertion = [](bool pred, const char* reason) -> void{
if constexpr (has_exceptions)
if(!pred) throw assert_failure(reason);
if(!pred) {
log_failure(reason);
if constexpr (has_exceptions)
throw assert_failure(reason);
}
};
enum class cbor_tag {
datetime = 0,
unix_time = 1,
ubignum = 2,
nbignum = 3,
decimal = 4,
bigfloat = 5,
cose_encrypt0 = 16,
cose_mac0 = 17,
cose_sign1 = 18,
expected_base64url = 21,
expected_base64 = 22,
expected_base16 = 23,
encoded_cbor = 24,
url = 32,
base64url = 33,
base64 = 34,
regexp = 35,
mime = 36,
cose_encrypt = 96,
cose_mac = 97,
cose_sign = 98,
signature = 55799
};
}

+ 32
- 0
tests.cpp Parādīt failu

@ -1,5 +1,7 @@
#include "gp_config.hpp"
#include "gp/system/logging_segment.hpp"
#include "allocator.hpp"
#include "test_scaffold.h"
@ -8,6 +10,32 @@
alignas(2048) gp::array<char, 4096> static_mapper::store;
gp::buddy<> static_mapper::impl = gp::buddy<>{store.begin().data, store.size()};
static_logging_segment<32> logger;
void log_failure(const char* failure) {
log_segment("FAILURE", failure, std::numeric_limits<int16_t>::max());
}
void log_segment(const char* name, const char* text, int16_t prio) {
logger.push_segment(name, text, prio);
}
void print_logs() {
auto end = logger.size();
for(size_t idx = 0; idx < end; idx++) {
auto seg = logger.get_segment_by_idx(idx);
std::cout << "\t";
for(char c : seg.name) {
std::cout << c;
}
std::cout << ":\t";
for(char c : seg.text) {
std::cout << c;
}
std::cout << "\n";
}
}
std::vector<std::unique_ptr<test_scaffold>> tests;
int main()
@ -26,14 +54,18 @@ int main()
}
} catch (gp::runtime_error err) {
std::cout << test->name << " failed with an exception: " << err.what() << std::endl;
print_logs();
value = -1;
} catch (gp_config::assert_failure err) {
std::cout << test->name << " failed with an assertion failure: " << err.what() << std::endl;
print_logs();
value = -1;
} catch (...) {
std::cout << test->name << " failed with an exception" << std::endl;
print_logs();
value = -1;
}
logger.clear();
failed += (value != 0);
}
std::cout << std::dec << "Runned "<<runned<<" tests with "<<failed<<" failures" << std::endl;

+ 30
- 0
tests/logging_test.cpp Parādīt failu

@ -0,0 +1,30 @@
#include "test_scaffold.h"
#include "gp/containers/array.hpp"
#include "gp/system/logging_segment.hpp"
#include <chrono>
#include <cmath>
#include <fstream>
#include <iomanip>
#include <iostream>
struct logging_test : public test_scaffold {
logging_test() {
name = __FILE__ ":1";
}
virtual int run() {
int res = 0;
const gp::buffer<char> name{"FIRST TEST"};
const gp::buffer<char> text{"FIRST TEST TEXT"};
static_logging_segment<4> logger;
return res;
}
};
append_test dummy_rduhk786f(new logging_test{});

Notiek ielāde…
Atcelt
Saglabāt