├── .gitattributes ├── .gitignore ├── CMakeLists.txt ├── LICENSE.txt ├── Makefile ├── Makefile.conan ├── README.md ├── Tuprules.lua ├── benchmarks ├── .gitignore ├── Tupfile.lua ├── benchmark_mandelbrot.cpp ├── benchmark_mandelbrot.tup ├── benchmarks.props ├── boost_log.hpp ├── call_burst.cpp ├── call_burst.vcxproj ├── find_mandelbrot.cpp ├── fstream.hpp ├── mandelbrot.cpp ├── mandelbrot.hpp ├── mandelbrot.vcxproj ├── multithreaded_suite.tup ├── nanolog_benchmark.cpp ├── nop.hpp ├── one_thread.hpp ├── periodic_calls.cpp ├── plot.py ├── plot_mandelbrot.py ├── reckless.hpp ├── run_benchmark.py ├── spdlog.hpp ├── statistics.py ├── stdio.hpp ├── suite.tup └── write_files.cpp ├── common.props ├── conanfile.py ├── doc ├── images │ ├── mandelbrot.jpeg │ ├── performance_call_burst_1.png │ ├── performance_call_burst_1_ma.png │ ├── performance_mandelbrot.png │ ├── performance_mandelbrot_difference.png │ ├── performance_periodic_calls_all.png │ ├── performance_periodic_calls_all_old_spdlog.png │ ├── performance_periodic_calls_reckless.png │ └── performance_write_files.png ├── manual.md └── performance.md ├── examples ├── .gitignore ├── CMakeLists.txt ├── Tupfile.lua ├── crash.cpp ├── crash.vcxproj ├── custom_formatter.cpp ├── custom_formatter.vcxproj ├── severity_log.cpp ├── severity_log.vcxproj ├── stderr_log.cpp ├── template.vcxproj ├── ucs2_log.cpp └── ucs2_log.vcxproj ├── executable.props ├── performance_log ├── include │ └── performance_log │ │ ├── performance_log.hpp │ │ └── trace_log.hpp ├── lib │ └── Tupfile.lua ├── performance_log.vcxproj └── src │ ├── Tupfile.lua │ └── performance_log.cpp ├── reckless.sln ├── reckless ├── Tuprules.lua ├── _Tuprules.tup ├── include │ └── reckless │ │ ├── basic_log.hpp │ │ ├── crash_handler.hpp │ │ ├── detail │ │ ├── fd_writer.hpp │ │ ├── lockless_cv.hpp │ │ ├── mpsc_ring_buffer.hpp │ │ ├── platform.hpp │ │ ├── spsc_event.hpp │ │ ├── trace_log.hpp │ │ └── utility.hpp │ │ ├── file_writer.hpp │ │ ├── ntoa.hpp │ │ ├── output_buffer.hpp │ │ ├── policy_log.hpp │ │ ├── severity_log.hpp │ │ ├── stdout_writer.hpp │ │ ├── template_formatter.hpp │ │ └── writer.hpp ├── lib │ ├── Tupfile.lua │ └── _Tupfile ├── reckless.vcxproj ├── reckless.vcxproj.filters ├── src │ ├── Tupfile.lua │ ├── _Tupfile │ ├── basic_log.cpp │ ├── crash_handler_unix.cpp │ ├── crash_handler_win32.cpp │ ├── fd_writer.cpp │ ├── file_writer.cpp │ ├── lockless_cv.cpp │ ├── mpsc_ring_buffer.cpp │ ├── ntoa.cpp │ ├── output_buffer.cpp │ ├── platform.cpp │ ├── policy_log.cpp │ ├── spsc_event_win32.cpp │ ├── template_formatter.cpp │ ├── trace_log.cpp │ ├── unit_test.hpp │ └── writer.cpp └── unittest │ ├── .gitignore │ └── _Tupfile ├── scripts ├── clean.py ├── license_header.txt └── update_license_header.py ├── tests ├── .gitignore ├── Tupfile.lua ├── consistent_address.cpp ├── crash_handler.cpp ├── crash_handler.vcxproj ├── eol.hpp ├── error_code.cpp ├── error_code.vcxproj ├── flush.cpp ├── flush.vcxproj ├── format_error.cpp ├── format_error.vcxproj ├── frame_construction_error.cpp ├── frame_construction_error.vcxproj ├── memory_writer.hpp ├── panic_flush.cpp ├── panic_flush.vcxproj ├── temporary_error.cpp ├── temporary_error.vcxproj ├── unique_ptr.cpp ├── unique_ptr.vcxproj └── unreliable_writer.hpp └── tup.config.template /.gitattributes: -------------------------------------------------------------------------------- 1 | * text eol=lf 2 | *.jpeg -text 3 | *.png -text 4 | 5 | *.sln eol=crlf 6 | *.vcxproj eol=crlf 7 | *.vcxproj.filters eol=crlf 8 | *.props eol=crlf 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.obj 3 | *.exe 4 | *.lib 5 | 6 | .tup 7 | 8 | .vs 9 | *.VC.db 10 | *.VC.opendb 11 | *.vcxproj.user 12 | /build -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if(NOT MSVC) 2 | set(CMAKE_CXX_COMPILER_NAMES g++ clang++ icpc c++ cxx) 3 | endif() 4 | project(reckless LANGUAGES CXX) 5 | CMAKE_MINIMUM_REQUIRED(VERSION 3.5) 6 | 7 | ################################################################################ 8 | # Options 9 | ################################################################################ 10 | 11 | option(RECKLESS_BUILD_EXAMPLES "Build the examples" OFF) 12 | 13 | ################################################################################ 14 | # Add Flags 15 | ################################################################################ 16 | set(CMAKE_CXX_STANDARD 11) 17 | if(MSVC) 18 | add_compile_options(/W4 /O3) 19 | else() 20 | add_compile_options(-Wall -Wextra -O3 -g) 21 | endif() 22 | 23 | ################################################################################ 24 | # Build Libraries 25 | ################################################################################ 26 | include_directories("reckless/include") 27 | include_directories("performance_log/include") 28 | 29 | 30 | set (SRC_LIST 31 | reckless/src/output_buffer.cpp 32 | reckless/src/ntoa.cpp 33 | reckless/src/template_formatter.cpp 34 | reckless/src/writer.cpp 35 | reckless/src/basic_log.cpp 36 | reckless/src/policy_log.cpp 37 | reckless/src/file_writer.cpp 38 | reckless/src/fd_writer.cpp 39 | reckless/src/mpsc_ring_buffer.cpp 40 | reckless/src/platform.cpp 41 | reckless/src/lockless_cv.cpp 42 | ) 43 | 44 | if(WIN32) 45 | set (SRC_LIST ${SRC_LIST} 46 | reckless/src/spsc_event_win32.cpp 47 | reckless/src/crash_handler_win32.cpp 48 | ) 49 | else() 50 | set (SRC_LIST ${SRC_LIST} 51 | reckless/src/crash_handler_unix.cpp 52 | ) 53 | endif() 54 | 55 | add_library(reckless STATIC ${SRC_LIST}) 56 | 57 | ################################################################################ 58 | # Build Examples 59 | ################################################################################ 60 | if (RECKLESS_BUILD_EXAMPLES) 61 | message (STATUS "Making example applications") 62 | add_subdirectory(examples) 63 | endif () 64 | 65 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | reckless logging 2 | Copyright 2015-2020 Mattias Flodin 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # NB: this Makefile is for the "I just want to build this" crowd. If you want 2 | # to actually tinker with the code or build it more than once it will be a bad 3 | # fit for you, since there is no dependency handling for header files. I just 4 | # find that part of GNU make to be a terrible kludge. Also, there are no rules 5 | # here to build performance tests and other cruft. You need to use tup for all 6 | # of that. 7 | 8 | CXXFLAGS = -std=c++11 -Wall -Wextra -O3 -DNDEBUG 9 | include Makefile.conan 10 | -------------------------------------------------------------------------------- /Makefile.conan: -------------------------------------------------------------------------------- 1 | CXXFLAGS += -Ireckless/include -g 2 | 3 | target = reckless/lib/libreckless.a 4 | srcpath = reckless/src 5 | sources := $(filter-out %_win32.cpp,$(wildcard $(srcpath)/*.cpp)) 6 | objects := $(patsubst %.cpp,%.o,$(sources)) 7 | 8 | .PHONY: clean 9 | 10 | $(target): $(objects) 11 | -$(RM) $(target) 12 | mkdir -p reckless/lib 13 | ar rs $(target) $(objects) 14 | 15 | clean: 16 | $(RM) $(target) $(objects) 17 | -------------------------------------------------------------------------------- /benchmarks/.gitignore: -------------------------------------------------------------------------------- 1 | /obj 2 | /results 3 | 4 | /call_burst-fstream-? 5 | /call_burst-nop-? 6 | /call_burst-boost_log-? 7 | /call_burst-reckless-? 8 | /call_burst-spdlog-? 9 | /call_burst-g3log-? 10 | /call_burst-stdio-? 11 | 12 | /mandelbrot-fstream-? 13 | /mandelbrot-nop-? 14 | /mandelbrot-boost_log-? 15 | /mandelbrot-reckless-? 16 | /mandelbrot-spdlog-? 17 | /mandelbrot-g3log-? 18 | /mandelbrot-stdio-? 19 | 20 | /write_files-fstream 21 | /write_files-nop 22 | /write_files-boost_log 23 | /write_files-reckless 24 | /write_files-spdlog 25 | /write_files-g3log 26 | /write_files-stdio 27 | 28 | /periodic_calls-fstream 29 | /periodic_calls-nop 30 | /periodic_calls-boost_log 31 | /periodic_calls-reckless 32 | /periodic_calls-spdlog 33 | /periodic_calls-g3log 34 | /periodic_calls-stdio 35 | -------------------------------------------------------------------------------- /benchmarks/Tupfile.lua: -------------------------------------------------------------------------------- 1 | libreckless = '../reckless/lib/' .. LIBPREFIX .. 'reckless' .. LIBSUFFIX 2 | libperformance_log = '../performance_log/lib/' .. LIBPREFIX .. 'performance_log' .. LIBSUFFIX 3 | 4 | table.insert(OPTIONS.includes, joinpath(tup.getcwd(), '../performance_log/include')) 5 | 6 | function build_suite(lib, extra_objs) 7 | push_options() 8 | table.insert(OPTIONS.define, 'LOG_INCLUDE=\\"' .. lib .. '.hpp\\"') 9 | 10 | function single_threaded(name) 11 | local objs = { 12 | compile(name .. '.cpp', name .. '-' .. lib .. OBJSUFFIX), 13 | libperformance_log 14 | } 15 | objs = table.merge(objs, extra_objs) 16 | link(name .. '-' .. lib, objs) 17 | end 18 | 19 | if tup.getconfig('TRACE_LOG') != '' and lib == 'reckless' then 20 | table.insert(OPTIONS.define, 'RECKLESS_ENABLE_TRACE_LOG') 21 | end 22 | 23 | single_threaded('periodic_calls') 24 | single_threaded('write_files') 25 | 26 | mandelbrot_obj = compile('mandelbrot.cpp', 'mandelbrot' .. '-' .. lib .. OBJSUFFIX) 27 | for threads=1,8 do 28 | push_options() 29 | table.insert(OPTIONS.define, 'THREADS=' .. threads) 30 | local exesuffix = '-' .. lib .. '-' .. threads 31 | local objsuffix = exesuffix .. OBJSUFFIX 32 | 33 | -- call_burst 34 | local objs = { 35 | compile('call_burst.cpp', 'call_burst' .. objsuffix), 36 | libperformance_log 37 | } 38 | objs = table.merge(objs, extra_objs) 39 | link('call_burst' .. exesuffix, objs) 40 | 41 | -- mandelbrot 42 | objs = { 43 | compile('benchmark_mandelbrot.cpp', 'benchmark_mandelbrot' .. objsuffix), 44 | mandelbrot_obj 45 | } 46 | objs = table.merge(objs, extra_objs) 47 | link('mandelbrot' .. exesuffix, objs) 48 | pop_options() 49 | end 50 | pop_options() 51 | end 52 | 53 | build_suite('nop', {}) 54 | build_suite('stdio', {}) 55 | build_suite('fstream', {}) 56 | 57 | push_options() 58 | table.insert(OPTIONS.includes, tup.getcwd() .. '/../reckless/include') 59 | build_suite('reckless', {libreckless}, {}, {}, {}) 60 | 61 | link('nanolog_benchmark', { 62 | compile('nanolog_benchmark.cpp', 'nanolog_benchmark' .. OBJSUFFIX), 63 | libreckless 64 | }) 65 | pop_options() 66 | 67 | SPDLOG = tup.getconfig('SPDLOG') 68 | if SPDLOG ~= '' then 69 | push_options() 70 | table.insert(OPTIONS.includes, joinpath(SPDLOG, 'include')) 71 | build_suite('spdlog', {}) 72 | pop_options() 73 | end 74 | 75 | G3LOG_INCLUDE = tup.getconfig('G3LOG_INCLUDE') 76 | G3LOG_LIB = tup.getconfig('G3LOG_LIB') 77 | if G3LOG_INCLUDE ~= '' and G3LOG_LIB ~= '' then 78 | push_options() 79 | table.insert(OPTIONS.includes, G3LOG_INCLUDE) 80 | table.insert(OPTIONS.libdirs, G3LOG_LIB) 81 | table.insert(OPTIONS.libs, 'g3logger') 82 | build_suite('g3log', {}) 83 | pop_options() 84 | end 85 | 86 | BOOST_LOG_INCLUDE = tup.getconfig('BOOST_LOG_INCLUDE') 87 | BOOST_LOG_LIB = tup.getconfig('BOOST_LOG_LIB') 88 | if BOOST_LOG_INCLUDE ~= '' and BOOST_LOG_LIB ~= '' then 89 | push_options() 90 | libs = { 91 | 'boost_log', 92 | 'boost_log_setup', 93 | 'boost_system', 94 | 'boost_thread' 95 | } 96 | 97 | table.insert(OPTIONS.define, 'BOOST_ALL_DYN_LINK') 98 | table.insert(OPTIONS.includes, BOOST_LOG_INCLUDE) 99 | table.insert(OPTIONS.libdirs, BOOST_LOG_LIB) 100 | OPTIONS.libs = table.merge(OPTIONS.libs, libs) 101 | build_suite('boost_log', {}) 102 | end 103 | -------------------------------------------------------------------------------- /benchmarks/benchmark_mandelbrot.cpp: -------------------------------------------------------------------------------- 1 | #include "mandelbrot.hpp" 2 | 3 | #ifdef RECKLESS_ENABLE_TRACE_LOG 4 | #include 5 | #endif 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include LOG_INCLUDE 14 | 15 | unsigned const SAMPLES_WIDTH = 1024; 16 | unsigned const SAMPLES_HEIGHT = 1024; 17 | unsigned const MAX_ITERATIONS = 32768; 18 | 19 | double const BOX_LEFT = -0.69897762686014175; 20 | double const BOX_TOP = 0.26043204963207245; 21 | double const BOX_WIDTH = 1.33514404296875e-05; 22 | double const BOX_HEIGHT = BOX_WIDTH*SAMPLES_HEIGHT/SAMPLES_WIDTH; 23 | 24 | int main() 25 | { 26 | std::vector sample_buffer(SAMPLES_WIDTH*SAMPLES_HEIGHT); 27 | auto start = std::chrono::steady_clock::now(); 28 | { 29 | LOG_INIT(8192); 30 | mandelbrot(&sample_buffer[0], SAMPLES_WIDTH, SAMPLES_HEIGHT, 31 | BOX_LEFT, BOX_TOP, BOX_LEFT+BOX_WIDTH, BOX_TOP-BOX_HEIGHT, 32 | MAX_ITERATIONS, THREADS); 33 | LOG_CLEANUP(); 34 | } 35 | auto end = std::chrono::steady_clock::now(); 36 | std::cout << std::chrono::duration_cast( 37 | end - start).count() << std::endl; 38 | 39 | char image[3*SAMPLES_WIDTH*SAMPLES_HEIGHT]; 40 | color_mandelbrot(image, &sample_buffer[0], SAMPLES_WIDTH, SAMPLES_HEIGHT, 41 | MAX_ITERATIONS); 42 | 43 | std::ofstream ofs("bench_mandelbrot.data", std::ios::binary); 44 | ofs.write(image, sizeof(image)); 45 | 46 | #ifdef RECKLESS_ENABLE_TRACE_LOG 47 | std::ofstream trace_log("trace_log.txt", std::ios::trunc); 48 | reckless::detail::g_trace_log.read([&](std::string const& s) { 49 | trace_log << s << std::endl; 50 | }); 51 | #endif 52 | return 0; 53 | } 54 | -------------------------------------------------------------------------------- /benchmarks/benchmark_mandelbrot.tup: -------------------------------------------------------------------------------- 1 | : foreach mandelbrot.cpp find_mandelbrot.cpp benchmark_mandelbrot.cpp |> !cxx |> 2 | : mandelbrot.o find_mandelbrot.o |> !ld |> find_mandelbrot 3 | : mandelbrot.o benchmark_mandelbrot.o |> !ld |> benchmark_mandelbrot 4 | -------------------------------------------------------------------------------- /benchmarks/benchmarks.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | reckless 6 | 7 | 8 | 9 | 10 | LOG_INCLUDE="$(SubjectLibrary).hpp";%(PreprocessorDefinitions) 11 | 12 | 13 | 14 | 15 | $(SubjectLibrary) 16 | 17 | 18 | -------------------------------------------------------------------------------- /benchmarks/boost_log.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace logging = boost::log; 13 | namespace sinks = boost::log::sinks; 14 | namespace src = boost::log::sources; 15 | namespace expr = boost::log::expressions; 16 | namespace attrs = boost::log::attributes; 17 | namespace keywords = boost::log::keywords; 18 | 19 | enum severity_level 20 | { 21 | debug, 22 | information, 23 | warning, 24 | error 25 | }; 26 | struct severity_tag; 27 | 28 | #ifdef LOG_ONLY_DECLARE 29 | extern src::severity_logger_mt g_logger; 30 | #else 31 | src::severity_logger_mt g_logger; 32 | #endif 33 | 34 | inline logging::formatting_ostream& operator<< 35 | ( 36 | logging::formatting_ostream& strm, 37 | logging::to_log_manip< severity_level, severity_tag > const& manip 38 | ) 39 | { 40 | char levels[] = { 'D', 'I', 'W', 'E' }; 41 | 42 | severity_level level = manip.get(); 43 | strm << levels[level]; 44 | 45 | return strm; 46 | } 47 | 48 | #define LOG_INIT(queue_size) \ 49 | logging::add_file_log( \ 50 | keywords::file_name = "log.txt", \ 51 | keywords::format = ( \ 52 | expr::stream \ 53 | << expr::attr("Severity") \ 54 | << ' ' \ 55 | << expr::format_date_time( \ 56 | "TimeStamp", "%Y-%m-%d %H:%M:%S.%f") \ 57 | << ' ' << expr::smessage \ 58 | ) \ 59 | ); \ 60 | logging::add_common_attributes(); 61 | 62 | #define LOG_CLEANUP() 63 | 64 | #define LOG( c, i, f ) \ 65 | logging::record rec = g_logger.open_record(keywords::severity = severity_level::information); \ 66 | if (rec) \ 67 | { \ 68 | logging::record_ostream strm(rec); \ 69 | strm << "Hello, World! " << c << ' ' << i << ' ' << f; \ 70 | strm.flush(); \ 71 | g_logger.push_record(boost::move(rec)); \ 72 | } 73 | 74 | #define LOG_FILE_WRITE(FileNumber, Percent) \ 75 | logging::record rec = g_logger.open_record(keywords::severity = severity_level::information); \ 76 | if (rec) \ 77 | { \ 78 | logging::record_ostream strm(rec); \ 79 | strm << "file " << FileNumber << " (" << Percent << "%)"; \ 80 | strm.flush(); \ 81 | g_logger.push_record(boost::move(rec)); \ 82 | } 83 | 84 | #define LOG_MANDELBROT(Thread, X, Y, FloatX, FloatY, Iterations) \ 85 | logging::record rec = g_logger.open_record(keywords::severity = severity_level::information); \ 86 | if (rec) \ 87 | { \ 88 | logging::record_ostream strm(rec); \ 89 | strm << "[T" << Thread << "] " << X << ',' << Y << '/' << FloatX << ',' << FloatY << ": " << Iterations << " iterations"; \ 90 | strm.flush(); \ 91 | g_logger.push_record(boost::move(rec)); \ 92 | } 93 | -------------------------------------------------------------------------------- /benchmarks/call_burst.cpp: -------------------------------------------------------------------------------- 1 | #ifdef RECKLESS_ENABLE_TRACE_LOG 2 | #include 3 | #include 4 | #endif 5 | 6 | #include 7 | #include 8 | 9 | #include LOG_INCLUDE 10 | 11 | #if defined(__unix__) 12 | #include 13 | void remove_file(char const* path) 14 | { 15 | unlink(path); 16 | } 17 | #elif defined(_WIN32) 18 | #include 19 | void remove_file(char const* path) 20 | { 21 | DeleteFile(path); 22 | } 23 | #endif 24 | 25 | 26 | char c = 'A'; 27 | float pi = 3.1415f; 28 | int main() 29 | { 30 | remove_file("log.txt"); 31 | performance_log::logger<100000, performance_log::rdtscp_cpuid_clock> performance_log; 32 | 33 | { 34 | LOG_INIT(128); 35 | // It's important to set our CPU affinity *after* the log is 36 | // initialized, otherwise all threads will run on the same CPU. 37 | performance_log::rdtscp_cpuid_clock::bind_cpu(0); 38 | 39 | for(int i=0; i!=100000; ++i) { 40 | LOG(c, i, pi); 41 | } 42 | 43 | for(int i=0; i!=100000; ++i) { 44 | auto start = performance_log.start(); 45 | LOG(c, i, pi); 46 | performance_log.stop(start); 47 | } 48 | 49 | performance_log::rdtscp_cpuid_clock::unbind_cpu(); 50 | LOG_CLEANUP(); 51 | } 52 | 53 | for(auto sample : performance_log) { 54 | std::cout << sample.start << ' ' << sample.stop << std::endl; 55 | } 56 | 57 | #ifdef RECKLESS_ENABLE_TRACE_LOG 58 | std::ofstream trace_log("trace_log.txt", std::ios::trunc); 59 | reckless::detail::g_trace_log.save(trace_log); 60 | #endif 61 | 62 | return 0; 63 | 64 | } 65 | -------------------------------------------------------------------------------- /benchmarks/find_mandelbrot.cpp: -------------------------------------------------------------------------------- 1 | #include "mandelbrot.hpp" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | unsigned const SAMPLES_WIDTH = 1024; 9 | unsigned const SAMPLES_HEIGHT = 1024; 10 | unsigned const SEARCH_BOX_WIDTH = 256; 11 | unsigned const SEARCH_BOX_HEIGHT = 256; 12 | unsigned const MAX_ITERATIONS = 2048; 13 | //unsigned const MAX_INSIDE = 5*SEARCH_BOX_WIDTH*SEARCH_BOX_HEIGHT/100; 14 | 15 | int main() 16 | { 17 | double x = -2.5; 18 | double w = 3.5; 19 | double y = 0.5*w*SAMPLES_HEIGHT/SAMPLES_WIDTH; 20 | double h = 2*y; 21 | 22 | std::vector sample_buffer(SAMPLES_WIDTH*SAMPLES_HEIGHT); 23 | mandelbrot(sample_buffer.data(), SAMPLES_WIDTH, SAMPLES_HEIGHT, 24 | x, y, x+w, y-h, MAX_ITERATIONS, 25 | std::thread::hardware_concurrency()); 26 | std::vector total; 27 | unsigned zoom_count = 0; 28 | while(true) { //zoom_count != 10) { 29 | std::printf("zoom level %u\n", zoom_count); 30 | unsigned best_iterations = 0; 31 | unsigned best_sample_x = 0; 32 | unsigned best_sample_y = 0; 33 | for(unsigned i=0; i!=SAMPLES_WIDTH*SAMPLES_HEIGHT; ++i) { 34 | if(sample_buffer[i] == MAX_ITERATIONS) 35 | sample_buffer[i] = 0; 36 | } 37 | 38 | for(unsigned y1=0; y1!=SAMPLES_HEIGHT-SEARCH_BOX_HEIGHT; ++y1) { 39 | for(unsigned x1=0; x1!=SAMPLES_WIDTH-SEARCH_BOX_WIDTH; ++x1) { 40 | unsigned x2 = x1 + SEARCH_BOX_WIDTH; 41 | unsigned y2 = y1 + SEARCH_BOX_HEIGHT; 42 | unsigned total_iterations = 0; 43 | //unsigned total_inside = 0; 44 | for(unsigned y=y1; y!=y2; ++y) { 45 | unsigned sample_offset = y*SAMPLES_WIDTH; 46 | for(unsigned x=x1; x!=x2; ++x) { 47 | unsigned iterations = sample_buffer[sample_offset + x]; 48 | //if(iterations == MAX_ITERATIONS) 49 | // total_inside += 1; 50 | //else 51 | total_iterations += iterations; 52 | } 53 | } 54 | //if(total_inside < MAX_INSIDE && total_iterations > best_iterations) 55 | if(total_iterations > best_iterations) 56 | { 57 | best_iterations = total_iterations; 58 | best_sample_x = x1; 59 | best_sample_y = y1; 60 | } 61 | } 62 | } 63 | 64 | x = x + best_sample_x*w/SAMPLES_WIDTH; 65 | y = y - best_sample_y*h/SAMPLES_HEIGHT; 66 | w = w*SEARCH_BOX_WIDTH/SAMPLES_WIDTH; 67 | h = h*SEARCH_BOX_HEIGHT/SAMPLES_HEIGHT; 68 | std::printf("x=%.17g\n" 69 | "y=%.17g\n" 70 | "w=%.17g\n" 71 | "h=%.17g\n" 72 | "sample_x=%u\n" 73 | "sample_y=%u\n" 74 | "\n", 75 | x, y, w, h, 76 | best_sample_x, best_sample_y); 77 | auto start = std::chrono::system_clock::now(); 78 | mandelbrot(sample_buffer.data(), SAMPLES_WIDTH, SAMPLES_HEIGHT, 79 | x, y, x+w, y-h, MAX_ITERATIONS, 80 | std::thread::hardware_concurrency()); 81 | auto end = std::chrono::system_clock::now(); 82 | auto milliseconds = std::chrono::duration_cast(end-start).count(); 83 | printf("%lu ms\n", milliseconds); 84 | if(zoom_count > 4 && milliseconds > 5000) 85 | break; 86 | 87 | ++zoom_count; 88 | } 89 | std::vector image(3*SAMPLES_WIDTH*SAMPLES_HEIGHT); 90 | color_mandelbrot(image.data(), sample_buffer.data(), 91 | SAMPLES_WIDTH, SAMPLES_HEIGHT, MAX_ITERATIONS); 92 | std::ofstream ofs("mandelbrot.data"); 93 | ofs.write(image.data(), image.size()); 94 | //std::ofstream ofs2("total.data"); 95 | //ofs2.write(reinterpret_cast(total.data()), total.size()); 96 | return 0; 97 | } 98 | -------------------------------------------------------------------------------- /benchmarks/fstream.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #ifdef LOG_ONLY_DECLARE 5 | extern std::mutex g_log_mutex; 6 | extern std::ofstream g_log; 7 | #else 8 | std::mutex g_log_mutex; 9 | std::ofstream g_log; 10 | #endif 11 | 12 | #define LOG_INIT(queue_size) \ 13 | g_log.open("log.txt") 14 | 15 | #define LOG_CLEANUP() \ 16 | g_log.close() 17 | 18 | #define LOG( c, i, f ) \ 19 | g_log_mutex.lock(); \ 20 | g_log << "Hello World! " << c << ' ' << i << ' ' << f << std::endl; \ 21 | g_log_mutex.unlock() 22 | 23 | #define LOG_FILE_WRITE(FileNumber, Percent) \ 24 | g_log << "file " << FileNumber << " (" << Percent << "%)" << '\n' << std::flush 25 | 26 | #define LOG_MANDELBROT(Thread, X, Y, FloatX, FloatY, Iterations) \ 27 | g_log_mutex.lock(); \ 28 | g_log << "[T" << Thread << "] " << X << ',' << Y << '/' << FloatX << ',' << FloatY << ": " << Iterations << " iterations" << std::endl; \ 29 | g_log_mutex.unlock() 30 | -------------------------------------------------------------------------------- /benchmarks/mandelbrot.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #define LOG_ONLY_DECLARE 13 | #include LOG_INCLUDE 14 | 15 | namespace { 16 | 17 | unsigned const THREAD_SLICE_SIZE = 1024*16; 18 | 19 | void mandelbrot_thread( 20 | unsigned thread_index, 21 | unsigned* sample_buffer, 22 | unsigned samples_width, 23 | unsigned samples_height, 24 | double x1, 25 | double y1, 26 | double x2, 27 | double y2, 28 | unsigned max_iterations, 29 | std::mutex* next_slice_mutex, 30 | unsigned* next_slice_index) 31 | { 32 | double const width = x2 - x1; 33 | double const height = y1 - y2; 34 | double const scale_x = width/samples_width; 35 | double const scale_y = height/samples_height; 36 | unsigned const total_samples = samples_width*samples_height; 37 | while(true) { 38 | std::size_t sample_index; 39 | std::size_t end_sample_index; 40 | { 41 | std::lock_guard lk(*next_slice_mutex); 42 | sample_index = *next_slice_index; 43 | if(sample_index == total_samples) 44 | return; 45 | end_sample_index = std::min(total_samples, sample_index + THREAD_SLICE_SIZE); 46 | *next_slice_index = static_cast(end_sample_index); 47 | } 48 | //printf("\r%.0f%%", 100.0*sample_index/total_samples); 49 | //fflush(stdout); 50 | 51 | while(sample_index != end_sample_index) { 52 | unsigned sample_x = sample_index % samples_width; 53 | unsigned sample_y = static_cast(sample_index / samples_width); 54 | std::complex c(x1 + sample_x*scale_x, y1 - sample_y*scale_y); 55 | std::complex z; 56 | 57 | unsigned iterations = 0; 58 | while(norm(z) < 2*2 and iterations != max_iterations) { 59 | z = z*z + c; 60 | ++iterations; 61 | } 62 | 63 | LOG_MANDELBROT(thread_index, sample_x, sample_y, c.real(), c.imag(), iterations); 64 | 65 | sample_buffer[sample_index] = iterations; 66 | ++sample_index; 67 | } 68 | } 69 | } 70 | 71 | } // anonymous namespace 72 | 73 | void mandelbrot( 74 | unsigned* sample_buffer, 75 | unsigned samples_width, 76 | unsigned samples_height, 77 | double x1, 78 | double y1, 79 | double x2, 80 | double y2, 81 | unsigned max_iterations, 82 | unsigned thread_count) 83 | { 84 | unsigned next_slice_index = 0; 85 | std::mutex next_slice_index_mutex; 86 | std::vector threads(thread_count); 87 | for(unsigned thread=0; thread!=thread_count; ++thread) { 88 | threads[thread] = std::thread(&mandelbrot_thread, thread, sample_buffer, 89 | samples_width, samples_height, x1, y1, x2, y2, max_iterations, 90 | &next_slice_index_mutex, &next_slice_index); 91 | } 92 | for(std::size_t thread=0; thread!=thread_count; ++thread) 93 | threads[thread].join(); 94 | } 95 | 96 | void color_mandelbrot(char* image, unsigned const* sample_buffer, 97 | unsigned samples_width, unsigned samples_height, 98 | unsigned max_iterations) 99 | { 100 | unsigned const PALETTE_SIZE = 256; 101 | std::uint8_t palette[3*PALETTE_SIZE]; 102 | std::fill(palette, palette + sizeof(palette), 103 | static_cast(0)); 104 | for(unsigned i=0; i!=PALETTE_SIZE; ++i) { 105 | double p=static_cast(i)/PALETTE_SIZE; 106 | p *= p; 107 | double r, g, b; 108 | if(2*p < 1) { 109 | r = 2*p; 110 | g = p; 111 | b = 0; 112 | } else { 113 | r = 1; 114 | g = p; 115 | b = 2*p-1; 116 | } 117 | palette[3*i] = static_cast(255.0*r); 118 | palette[3*i+1] = static_cast(255.0*g); 119 | palette[3*i+2] = static_cast(255.0*b); 120 | } 121 | 122 | std::vector histogram(max_iterations); 123 | for(std::size_t i=0; i!=samples_width*samples_height; ++i) { 124 | auto iterations = sample_buffer[i]; 125 | histogram[iterations == max_iterations? 0 : iterations]++; 126 | } 127 | 128 | std::vector hues(max_iterations); 129 | std::size_t hue = 0; 130 | for(std::size_t i=1; i!=max_iterations; ++i) { 131 | hue += histogram[i-1]; 132 | hues[i] = static_cast( 133 | static_cast(PALETTE_SIZE-1)*hue/ 134 | (samples_width*samples_height)); 135 | } 136 | 137 | for(std::size_t i=0; i!=samples_width*samples_height; ++i) 138 | { 139 | auto iterations = sample_buffer[i]; 140 | auto color_index = hues[iterations == max_iterations? 0 : iterations]; 141 | image[3*i] = palette[3*color_index]; 142 | image[3*i+1] = palette[3*color_index+1]; 143 | image[3*i+2] = palette[3*color_index+2]; 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /benchmarks/mandelbrot.hpp: -------------------------------------------------------------------------------- 1 | void mandelbrot( 2 | unsigned* sample_buffer, 3 | unsigned samples_width, 4 | unsigned samples_height, 5 | double x1, 6 | double y1, 7 | double x2, 8 | double y2, 9 | unsigned max_iterations, 10 | unsigned thread_count); 11 | 12 | void color_mandelbrot(char* image, unsigned const* sample_buffer, 13 | unsigned samples_width, unsigned samples_height, unsigned max_iterations); 14 | -------------------------------------------------------------------------------- /benchmarks/mandelbrot.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | reckless 1 Debug 6 | Win32 7 | 8 | 9 | reckless 1 Release 10 | Win32 11 | 12 | 13 | reckless 1 Debug 14 | x64 15 | 16 | 17 | reckless 1 Release 18 | x64 19 | 20 | 21 | reckless 2 Debug 22 | Win32 23 | 24 | 25 | reckless 2 Release 26 | Win32 27 | 28 | 29 | reckless 2 Debug 30 | x64 31 | 32 | 33 | reckless 2 Release 34 | x64 35 | 36 | 37 | reckless 3 Debug 38 | Win32 39 | 40 | 41 | reckless 3 Release 42 | Win32 43 | 44 | 45 | reckless 3 Debug 46 | x64 47 | 48 | 49 | reckless 3 Release 50 | x64 51 | 52 | 53 | reckless 4 Debug 54 | Win32 55 | 56 | 57 | reckless 4 Release 58 | Win32 59 | 60 | 61 | reckless 4 Debug 62 | x64 63 | 64 | 65 | reckless 4 Release 66 | x64 67 | 68 | 69 | 70 | {60BA1990-CECE-4F52-A3D0-7BBC727B0BE4} 71 | mandelbrot 72 | 10.0.17763.0 73 | 74 | 75 | 76 | v141 77 | 78 | 79 | true 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 8388608 94 | 95 | 96 | 97 | 98 | THREADS=1;%(PreprocessorDefinitions) 99 | 100 | 101 | 102 | 103 | THREADS=2;%(PreprocessorDefinitions) 104 | 105 | 106 | 107 | 108 | THREADS=3;%(PreprocessorDefinitions) 109 | 110 | 111 | 112 | 113 | THREADS=4;%(PreprocessorDefinitions) 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /benchmarks/multithreaded_suite.tup: -------------------------------------------------------------------------------- 1 | : call_burst.cpp |> !pcxxld |> $(LIB)_call_burst_$(THREADS) 2 | : foreach benchmark_mandelbrot.cpp |> !pcxx |> $(LIB)_%B_$(THREADS).o 3 | : $(LIB)_mandelbrot.o $(LIB)_benchmark_mandelbrot_$(THREADS).o |> !pld |> $(LIB)_mandelbrot_$(THREADS) 4 | -------------------------------------------------------------------------------- /benchmarks/nanolog_benchmark.cpp: -------------------------------------------------------------------------------- 1 | // The NanoLog documentation has a benchmark that includes reckless but shows 2 | // very poor worst-case performance: 3 | // https://github.com/Iyengar111/NanoLog/tree/40a53c36e0336af45f7664abeb939f220f78273e 4 | // So we include that benchmark here for comparison. I haven't been able to 5 | // reproduce the NanoLog author's numbers however. 6 | // 7 | // I removed the other libraries tested to make it easier to build, as I'm 8 | // currently only interested in using this to reproduce the recklesss results. 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | /* Returns microseconds since epoch */ 20 | uint64_t timestamp_now() 21 | { 22 | return std::chrono::high_resolution_clock::now().time_since_epoch() / std::chrono::microseconds(1); 23 | } 24 | 25 | template 26 | void run_log_benchmark(Function &&f, char const *const logger) 27 | { 28 | int iterations = 100000; 29 | std::vector latencies; 30 | char const *const benchmark = "benchmark"; 31 | for (int i = 0; i < iterations; ++i) 32 | { 33 | uint64_t begin = timestamp_now(); 34 | f(i, benchmark); 35 | uint64_t end = timestamp_now(); 36 | latencies.push_back(end - begin); 37 | } 38 | std::sort(latencies.begin(), latencies.end()); 39 | uint64_t sum = 0; 40 | for (auto l : latencies) 41 | { 42 | sum += l; 43 | } 44 | printf("%s percentile latency numbers in microseconds\n%9s|%9s|%9s|%9s|%9s|%9s|%9s|\n%9ld|%9ld|%9ld|%9ld|%9ld|%9ld|%9lf|\n", logger, "50th", "75th", "90th", "99th", "99.9th", "Worst", "Average", latencies[(size_t)iterations * 0.5], latencies[(size_t)iterations * 0.75], latencies[(size_t)iterations * 0.9], latencies[(size_t)iterations * 0.99], latencies[(size_t)iterations * 0.999], latencies[latencies.size() - 1], (sum * 1.0) / latencies.size()); 45 | } 46 | 47 | template 48 | void run_benchmark(Function &&f, int thread_count, char const *const logger) 49 | { 50 | printf("\nThread count: %d\n", thread_count); 51 | std::vector threads; 52 | for (int i = 0; i < thread_count; ++i) 53 | { 54 | threads.emplace_back(run_log_benchmark, std::ref(f), logger); 55 | } 56 | for (int i = 0; i < thread_count; ++i) 57 | { 58 | threads[i].join(); 59 | } 60 | } 61 | 62 | int main() 63 | { 64 | using log_t = reckless::severity_log, ' ', reckless::severity_field, reckless::timestamp_field>; 65 | 66 | reckless::file_writer writer("/tmp/reckless.txt"); 67 | log_t g_log(&writer); 68 | 69 | auto reckless_benchmark = [&g_log](int i, char const *const cstr) { g_log.info("Logging %s%d%d%c%lf", cstr, i, 0, 'K', -42.42); }; 70 | for (auto threads : {1, 2, 3, 4}) 71 | run_benchmark(reckless_benchmark, threads, "reckless"); 72 | 73 | return 0; 74 | } 75 | -------------------------------------------------------------------------------- /benchmarks/nop.hpp: -------------------------------------------------------------------------------- 1 | #define LOG_INIT(queue_size) 2 | 3 | #define LOG_CLEANUP() 4 | 5 | #define LOG( c, i, f ) 6 | 7 | #define LOG_FILE_WRITE(FileNumber, Percent) 8 | 9 | #define LOG_MANDELBROT(Thread, X, Y, FloatX, FloatY, Iterations) \ 10 | (void) Thread; (void) X; (void) Y; (void) Y; \ 11 | (void) FloatX; (void) FloatY; (void) Iterations 12 | -------------------------------------------------------------------------------- /benchmarks/one_thread.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | char c = 'A'; 6 | float pi = 3.1415; 7 | int main() 8 | { 9 | unlink("log.txt"); 10 | performance_log::rdtscp_cpuid_clock::bind_cpu(0); 11 | performance_log::logger<16384, performance_log::rdtscp_cpuid_clock, std::uint32_t> performance_log; 12 | 13 | { 14 | BENCHMARK_INIT(); 15 | 16 | for(int i=0; i!=10000; ++i) { 17 | auto start = performance_log.start(); 18 | LOG(c, i, pi); 19 | performance_log.stop(start); 20 | } 21 | 22 | BENCHMARK_CLEANUP(); 23 | } 24 | performance_log::rdtscp_cpuid_clock::unbind_cpu(); 25 | 26 | for(auto sample : performance_log) { 27 | std::cout << sample << std::endl; 28 | } 29 | 30 | return 0; 31 | 32 | } 33 | -------------------------------------------------------------------------------- /benchmarks/periodic_calls.cpp: -------------------------------------------------------------------------------- 1 | #ifdef RECKLESS_ENABLE_TRACE_LOG 2 | #include 3 | #include 4 | #endif 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include LOG_INCLUDE 11 | 12 | char c = 'A'; 13 | float pi = 3.1415; 14 | int main() 15 | { 16 | unlink("log.txt"); 17 | performance_log::logger<4096, performance_log::rdtscp_cpuid_clock> performance_log; 18 | 19 | { 20 | LOG_INIT(6000); 21 | performance_log::rdtscp_cpuid_clock::bind_cpu(0); 22 | 23 | for(int i=0; i!=2500; ++i) { 24 | usleep(1000); 25 | struct timespec busywait_start; 26 | clock_gettime(CLOCK_REALTIME, &busywait_start); 27 | struct timespec busywait_end = busywait_start; 28 | while( (busywait_end.tv_sec - busywait_start.tv_sec)*1000000 + busywait_end.tv_nsec - busywait_start.tv_nsec < 1000 ) 29 | clock_gettime(CLOCK_REALTIME, &busywait_end); 30 | 31 | auto start = performance_log.start(); 32 | LOG(c, i, pi); 33 | performance_log.stop(start); 34 | } 35 | 36 | performance_log::rdtscp_cpuid_clock::unbind_cpu(); 37 | LOG_CLEANUP(); 38 | } 39 | 40 | for(auto sample : performance_log) { 41 | std::cout << sample.start << ' ' << sample.stop << std::endl; 42 | } 43 | 44 | #ifdef RECKLESS_ENABLE_TRACE_LOG 45 | std::ofstream trace_log("trace_log.txt", std::ios::trunc); 46 | reckless::detail::g_trace_log.save(trace_log); 47 | #endif 48 | 49 | return 0; 50 | 51 | } 52 | -------------------------------------------------------------------------------- /benchmarks/plot_mandelbrot.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2 2 | from __future__ import print_function 3 | import matplotlib 4 | import matplotlib.pyplot as plt 5 | import numpy as np 6 | from sys import argv 7 | import os.path 8 | 9 | matplotlib.rc('font', size=10) 10 | 11 | # color palette from colorbrewer2.org 12 | COLORS = [ 13 | '#a6cee3', 14 | '#1f78b4', 15 | '#b2df8a', 16 | '#33a02c', 17 | '#fb9a99', 18 | '#e31a1c', 19 | '#fdbf6f' 20 | ] 21 | #COLORS = [ 22 | # '#8dd3c7', 23 | # '#fb8072', 24 | # '#80b1d3', 25 | # #'#ffffb3', # this is too bright for white background 26 | # '#bebada', 27 | # '#fdb462', 28 | # '#b3de69', 29 | # '#fccde5' 30 | #] 31 | 32 | filename = None 33 | if len(argv) > 1: 34 | filename = argv[1] 35 | width = 858 36 | height = 858 37 | 38 | fig, ax = plt.subplots() 39 | 40 | ONLY_OVERHEAD = 1 41 | SCALE = 1000000.0/(1024*1024) 42 | #LOG_LINES = 1048576 43 | MAX_CORES = 8 44 | LIBS = ['nop', 'reckless', 'spdlog', 'g3log', 'stdio', 'fstream', 'boost_log'] 45 | #LIBS = ['nop', 'reckless', 'stdio', 'fstream'] 46 | if ONLY_OVERHEAD: 47 | del COLORS[-1] 48 | del LIBS[0] 49 | ind = np.arange(MAX_CORES) 50 | WIDTH=1.0/(len(LIBS)+2) 51 | GROUP_WIDTH = WIDTH*len(LIBS) 52 | GROUP_OFFSET = (1.0 - GROUP_WIDTH)/2 53 | 54 | def read_timing(lib, cores, offset=0.0): 55 | with open(os.path.join('results', 'mandelbrot-%s-%d.txt' % (lib, cores)), 'r') as f: 56 | data = f.readlines() 57 | data = sorted([float(x)/1000.0+offset for x in data]) 58 | # Use interquartile range as measure of scale. 59 | low, high = np.percentile(data, [25, 75]) 60 | 61 | # Samples generally center around a minimum point that represents the ideal 62 | # running time. There are no outliers below the ideal time; execution does 63 | # not accidentally take some kind of short cut. At least, not for this 64 | # particular benchmark. It could be conceivable for other tests where we 65 | # are I/O-bound and we get lucky with the cache, but this test is not 66 | # I/O-bound. 67 | # 68 | # However, outliers do exist on the other end of the spectrum. Something 69 | # completely unrelated sometimes happens in the system and takes tons of 70 | # CPU cycles from our benchmark. We don't want to include these because 71 | # they make it more difficult to reproduce the same statistics on multiple 72 | # runs. Or, in statistics speak, they make our benchmark less robust and 73 | # less efficient. To counter this we get rid of the worst outliers on the 74 | # upper end, but we leave the lower end intact. 75 | outlier_index = int(len(data)*0.8) 76 | data = data[:outlier_index] 77 | mean = np.mean(data) 78 | return low, mean, high 79 | 80 | offsets = [] 81 | for cores in range(1, MAX_CORES+1): 82 | if ONLY_OVERHEAD: 83 | _, mean, _ = read_timing('nop', cores) 84 | offsets.append(-mean) 85 | else: 86 | offsets.append(0.0) 87 | 88 | rects = [] 89 | for index, lib in enumerate(LIBS): 90 | means = [] 91 | error = [[], []] 92 | 93 | for cores in range(1, MAX_CORES+1): 94 | low, mean, high = read_timing(lib, cores, offsets[cores-1]) 95 | low, mean, high = low*SCALE, mean*SCALE, high*SCALE 96 | print(lib, cores, mean, low, high) 97 | means.append(mean) 98 | error[0].append(mean - low) 99 | error[1].append(high - mean) 100 | if not ONLY_OVERHEAD: 101 | # Standard deviation is so small that it just clutters the plot in this 102 | # view 103 | error = None 104 | rects.append(ax.bar(ind+index*WIDTH + GROUP_OFFSET, means, WIDTH, 105 | color=COLORS[index], yerr=error, ecolor='black')) 106 | 107 | ax.legend( rects, LIBS ) 108 | 109 | if ONLY_OVERHEAD: 110 | ax.set_ylabel('Average call latency (microseconds)') 111 | else: 112 | ax.set_ylabel('Total seconds') 113 | ax.set_xlabel('Number of worker threads') 114 | ax.set_xticks(ind + GROUP_OFFSET + GROUP_WIDTH/2) 115 | ax.set_xticklabels([str(x) for x in range(1, MAX_CORES+1)]) 116 | #ax.set_title('1024x1024 mandelbrot set render, one log line per pixel (i.e. 1.05 million log lines)') 117 | 118 | if filename is None: 119 | plt.show() 120 | else: 121 | dpi = 96 122 | fig.set_size_inches(width/dpi, height/dpi) 123 | plt.savefig(filename, dpi=dpi) 124 | -------------------------------------------------------------------------------- /benchmarks/reckless.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #ifdef LOG_ONLY_DECLARE 5 | extern reckless::severity_log g_log; 6 | #else 7 | reckless::severity_log g_log; 8 | #endif 9 | 10 | #define LOG_INIT(queue_size) \ 11 | reckless::file_writer writer("log.txt"); \ 12 | g_log.open(&writer, 64*queue_size, 64*queue_size); 13 | 14 | #define LOG_CLEANUP() g_log.close() 15 | 16 | #define LOG( c, i, f ) g_log.info("Hello World! %s %d %f", c, i, f) 17 | 18 | #define LOG_FILE_WRITE(FileNumber, Percent) \ 19 | g_log.info("file %d (%f%%)", FileNumber, Percent) 20 | 21 | #define LOG_MANDELBROT(Thread, X, Y, FloatX, FloatY, Iterations) \ 22 | g_log.info("[T%d] %d,%d/%f,%f: %d iterations", Thread, X, Y, FloatX, FloatY, Iterations) 23 | -------------------------------------------------------------------------------- /benchmarks/run_benchmark.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import sys, os, time, subprocess, errno 4 | from sys import stdout, stderr, argv 5 | from getopt import gnu_getopt 6 | 7 | ALL_LIBS = ['nop', 'reckless', 'stdio', 'fstream', 'boost_log', 'spdlog', 'g3log'] 8 | ALL_TESTS = ['periodic_calls', 'call_burst', 'write_files', 'mandelbrot'] 9 | 10 | SINGLE_SAMPLE_TESTS = {'mandelbrot'} 11 | THREADED_TESTS = {'call_burst', 'mandelbrot'} 12 | TESTS_WITH_DRY_RUN = {'call_burst', 'periodic_calls'} 13 | MAX_THREADS = 8 14 | 15 | # /run_benchmark.py -t mandelbrot 1448.92s user 64.87s system 208% cpu 12:04.87 total 16 | # with SINGLE_SAMPLE_TEST_ITERATIONS=2 means we should have 17 | # about 80 for 8 hours runtime 18 | # 19 | # time ./run_benchmark.py -t mandelbrot 20 | # mandelbrot: fstream/1234 nop/1234 pantheios/1234 reckless/1234 spdlog/1234 stdio/1234 21 | # ./run_benchmark.py -t mandelbrot 58192.78s user 2484.45s system 208% cpu 8:04:15.16 total 22 | # 23 | # Then 100 = 10 hours runtime 24 | SINGLE_SAMPLE_TEST_ITERATIONS = 100 25 | 26 | def main(): 27 | opts, args = gnu_getopt(argv[1:], 'l:t:h', ['libs', 'tests', 'help']) 28 | libs = None 29 | tests = None 30 | show_help = len(args) != 0 31 | 32 | for option, value in opts: 33 | if option in ('-l', '--libs'): 34 | libs = [lib.strip() for lib in value.split(',')] 35 | elif option in ('-t', '--tests'): 36 | tests = [test.strip() for test in value.split(',')] 37 | elif option in ('-h', '--help'): 38 | show_help = True 39 | 40 | if show_help: 41 | stderr.write( 42 | 'usage: run_benchmark.py [OPTIONS]\n' 43 | 'where OPTIONS are:\n' 44 | '-t,--tests TESTS comma-separated list of tests to run\n' 45 | '-l,--libs LIBS comma-separated list of libs to benchmark\n' 46 | '-h,--help show this help\n' 47 | 'Available libraries: {}\n' 48 | 'Available tests: {}\n'.format( 49 | ','.join(ALL_LIBS), ','.join(ALL_TESTS))) 50 | return 1 51 | 52 | if libs is None: 53 | libs = sorted(ALL_LIBS) 54 | if tests is None: 55 | tests = sorted(ALL_TESTS) 56 | 57 | run_tests(libs, tests) 58 | return 0 59 | 60 | def run_tests(libs, tests): 61 | for test in tests: 62 | stdout.write(test + ':') 63 | stdout.flush() 64 | for lib in libs: 65 | stdout.write(' ' + lib) 66 | if test in THREADED_TESTS: 67 | stdout.write('/') 68 | for threads in range(1, MAX_THREADS+1): 69 | stdout.write(str(threads)) 70 | stdout.flush() 71 | success = run_test(lib, test, threads) 72 | else: 73 | stdout.flush() 74 | success = run_test(lib, test) 75 | 76 | if not success: 77 | stdout.write('[F]') 78 | 79 | stdout.write('\n') 80 | stdout.flush() 81 | 82 | def reset(): 83 | if os.path.exists('log.txt'): 84 | os.unlink('log.txt') 85 | for name in os.listdir('data'): 86 | os.unlink(os.path.join('data', name)) 87 | subprocess.call('sync') 88 | 89 | def run_test(lib, test, threads = None): 90 | binary_name = test + '-' + lib 91 | if threads is not None: 92 | binary_name += '-' + str(threads) 93 | 94 | def run(out_file): 95 | try: 96 | p = subprocess.Popen([binary_name], executable='./' + binary_name, stdout=out) 97 | p.wait() 98 | except OSError as e: 99 | if e.errno == errno.ENOENT: 100 | return False 101 | else: 102 | raise 103 | 104 | if test in TESTS_WITH_DRY_RUN: 105 | with open(os.devnull, 'w') as out: 106 | run(out) 107 | else: 108 | busy_wait(0.5) 109 | 110 | txt_name = binary_name + '.txt' 111 | with open('results/' + txt_name, 'w') as out: 112 | total_iterations = 1 113 | if test in SINGLE_SAMPLE_TESTS: 114 | total_iterations = SINGLE_SAMPLE_TEST_ITERATIONS 115 | for i in range(0, total_iterations): 116 | reset() 117 | run(out) 118 | return True 119 | 120 | def busy_wait(period): 121 | end = time.time() + period 122 | while time.time() < end: 123 | pass 124 | 125 | if __name__ == "__main__": 126 | sys.exit(main()) 127 | -------------------------------------------------------------------------------- /benchmarks/spdlog.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #ifdef LOG_ONLY_DECLARE 7 | extern std::shared_ptr g_logger; 8 | #else 9 | std::shared_ptr g_logger; 10 | #endif 11 | 12 | // A design constraint / prerequisite for our library is to minimize the 13 | // risk of messages getting lost. That means we want to flush on every 14 | // write, not keep stuff in the stdio buffer. Hence we set 15 | // force_flush=true here. 16 | 17 | // In the reckless input buffer, each log line in the mandelbrot 18 | // benchmark takes up 88 bytes, which rounds to 128 bytes when aligned 19 | // to a cache line. This means our 64 kb input buffer fits 512 log 20 | // messages. So in an attempt to make the benchmark fair we set the 21 | // same number of log entries for spdlog's buffer. 22 | #define LOG_INIT(queue_size) \ 23 | spdlog::init_thread_pool(queue_size, 1); \ 24 | spdlog::set_pattern("%L %Y-%m-%d %H:%M:%S.%e %v"); \ 25 | g_logger = spdlog::basic_logger_mt("log", "log.txt", true) 26 | 27 | #define LOG_CLEANUP() \ 28 | g_logger.reset() 29 | 30 | #define LOG( c, i, f ) g_logger->info("Hello World! {} {} {}", c, i, f) 31 | 32 | #define LOG_FILE_WRITE(FileNumber, Percent) \ 33 | g_logger->info("file {} ({}%)", FileNumber, Percent) 34 | 35 | #define LOG_MANDELBROT(Thread, X, Y, FloatX, FloatY, Iterations) \ 36 | g_logger->info("[T{}] {},{}/{},{}: {} iterations", Thread, X, Y, FloatX, FloatY, Iterations) 37 | -------------------------------------------------------------------------------- /benchmarks/stdio.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include // forward 4 | 5 | #include 6 | 7 | #ifdef LOG_ONLY_DECLARE 8 | extern FILE* g_log; 9 | #else 10 | FILE* g_log; 11 | #endif 12 | 13 | #define LOG_INIT(queue_size) \ 14 | g_log = std::fopen("log.txt", "w") 15 | 16 | #define LOG_CLEANUP() \ 17 | std::fclose(g_log) 18 | 19 | template 20 | void log(char const* fmt, T&&... args) 21 | { 22 | timeval tv; 23 | gettimeofday(&tv, nullptr); 24 | struct tm tm; 25 | localtime_r(&tv.tv_sec, &tm); 26 | // "YYYY-mm-dd HH:MM:SS" -> 19 chars 27 | // Need 20 chars since we need a NUL char. 28 | char buf[20]; 29 | std::strftime(buf, 20, "%Y-%m-%d %H:%M:%S.", &tm); 30 | 31 | std::fprintf(g_log, fmt, buf, 32 | static_cast(tv.tv_usec)/1000u, std::forward(args)...); 33 | std::fflush(g_log); 34 | } 35 | 36 | #define LOG(c, i, f) log("%s.%03u Hello World! %c %d %f\n", c, i, f) 37 | 38 | #define LOG_FILE_WRITE(FileNumber, Percent) \ 39 | std::fprintf(g_log, "file %u (%f%%)\n", FileNumber, Percent); \ 40 | std::fflush(g_log) 41 | 42 | #define LOG_MANDELBROT(Thread, X, Y, FloatX, FloatY, Iterations) \ 43 | log("%s.%03u [T%d] %d,%d/%f,%f: %d iterations\n", \ 44 | Thread, X, Y, FloatX, FloatY, Iterations) 45 | -------------------------------------------------------------------------------- /benchmarks/suite.tup: -------------------------------------------------------------------------------- 1 | : periodic_calls.cpp |> !pcxxld |> $(LIB)_periodic_calls 2 | : mandelbrot.cpp |> !pcxx |> $(LIB)_%B.o 3 | 4 | : write_files.cpp |> !pcxxld |> $(LIB)_write_files 5 | 6 | THREADS=1 7 | include multithreaded_suite.tup 8 | THREADS=2 9 | include multithreaded_suite.tup 10 | THREADS=3 11 | include multithreaded_suite.tup 12 | THREADS=4 13 | include multithreaded_suite.tup 14 | -------------------------------------------------------------------------------- /benchmarks/write_files.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include LOG_INCLUDE 11 | 12 | int main() 13 | { 14 | unsigned const LOG_ENTRIES = 2048; 15 | unsigned long const DATA_SIZE = 64*1024*1024*1024L; 16 | unlink("log.txt"); 17 | 18 | auto const full_access = 19 | S_IRUSR | S_IWUSR | S_IXUSR | 20 | S_IRGRP | S_IWGRP | S_IXGRP | 21 | S_IROTH | S_IWOTH | S_IXOTH; 22 | mkdir("data", full_access); 23 | char data[1024*1024]; 24 | std::memset(data, 0xcd, sizeof(data)); 25 | 26 | performance_log::logger<2*2048, performance_log::rdtscp_cpuid_clock> performance_log; 27 | 28 | { 29 | LOG_INIT(128); 30 | performance_log::rdtscp_cpuid_clock::bind_cpu(0); 31 | 32 | for(unsigned number=0; number!=LOG_ENTRIES; ++number) { 33 | std::ostringstream ostr; 34 | ostr << "data/" << number; 35 | int fd = open(ostr.str().c_str(), O_WRONLY | O_CREAT, full_access); 36 | assert(fd != -1); 37 | 38 | auto start = performance_log.start(); 39 | LOG_FILE_WRITE(number, 100.0*number/256.0); 40 | performance_log.stop(start); 41 | 42 | for(std::size_t i=0; i!=DATA_SIZE/LOG_ENTRIES/sizeof(data); ++i) { 43 | auto res = write(fd, data, sizeof(data)); 44 | assert(res == sizeof(data)); 45 | } 46 | close(fd); 47 | } 48 | 49 | performance_log::rdtscp_cpuid_clock::unbind_cpu(); 50 | LOG_CLEANUP(); 51 | } 52 | 53 | for(auto sample : performance_log) { 54 | std::cout << sample.start << ' ' << sample.stop << std::endl; 55 | } 56 | 57 | return 0; 58 | 59 | } 60 | -------------------------------------------------------------------------------- /common.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | $(PlatformShortName)-$(Configuration.ToLower()) 6 | $(PlatformShortName)-debug 7 | $(PlatformShortName)-release 8 | $(SolutionDir)build\$(BuildConfiguration)\ 9 | $(ProjectDir.SubString($(SolutionDir.Length))) 10 | $(SolutionDir)build\$(LibRecklessBuildConfiguration)\lib 11 | 12 | 13 | $(BuildDir)$(BuildSubDir)\ 14 | $(BuildDir)lib\ 15 | $(BuildDir)obj\$(BuildSubDir)$(ProjectFileName)\ 16 | $(SolutionDir)reckless\include;$(SolutionDir)performance_log\include\;$(IncludePath) 17 | $(RecklessLibDir);$(LibraryPath) 18 | 19 | 20 | true 21 | 22 | 23 | false 24 | 25 | 26 | 27 | _CRT_SECURE_NO_WARNINGS 28 | 29 | 30 | false 31 | 32 | 33 | 34 | 35 | Level4 36 | Disabled 37 | true 38 | true 39 | $(SolutionDir)performance_log\include\;%(AdditionalIncludeDirectories) 40 | 41 | 42 | 43 | 44 | Level3 45 | MaxSpeed 46 | true 47 | true 48 | true 49 | 50 | 51 | true 52 | true 53 | 54 | 55 | 56 | 57 | $(BuildConfiguration) 58 | 59 | 60 | $(BuildDir) 61 | 62 | 63 | $(BuildSubDir) 64 | 65 | 66 | -------------------------------------------------------------------------------- /conanfile.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import os, shutil 5 | from conans import ConanFile, tools, AutoToolsBuildEnvironment, MSBuild 6 | from conans.errors import ConanException 7 | 8 | MSDEV_PLATFORM_SHORT_NAMES = { 9 | 'x86': 'x86', 10 | 'x86_64': 'x64' 11 | } 12 | 13 | class RecklessConan(ConanFile): 14 | name = 'reckless' 15 | license = 'MIT' 16 | version = '3.0.1' 17 | url = 'https://github.com/mattiasflodin/reckless' 18 | description = """Reckless is an extremely low-latency, high-throughput logging library.""" 19 | settings = 'arch', 'compiler', 'build_type' 20 | exports_sources = ( 21 | "reckless/src/*", 22 | "reckless/include/*", 23 | "Makefile.conan", 24 | "reckless.sln", 25 | "common.props", 26 | "*.vcxproj" 27 | ) 28 | 29 | def configure(self): 30 | if self._gcc_compatible() and self.settings.compiler.libcxx == "libstdc++": 31 | raise ConanException("This package is only compatible with libstdc++11. " 32 | "Please run with -s compiler.libcxx=libstdc++11.") 33 | 34 | def build(self): 35 | if self.settings.compiler == "Visual Studio": 36 | env = MSBuild(self) 37 | env.build('reckless.sln', use_env=False, targets=['reckless:Rebuild']) 38 | else: 39 | env = AutoToolsBuildEnvironment(self) 40 | env.make(args=['-f', 'Makefile.conan'], target="clean") 41 | # Why doesn't Conan set CXX? 42 | vars = env.vars 43 | vars['CXX'] = str(self.settings.compiler) 44 | env.make(args=['-f', 'Makefile.conan'], vars=vars) 45 | 46 | def package(self): 47 | if self._gcc_compatible(): 48 | self.copy('*.a', src='reckless/lib', dst='lib', keep_path=False) 49 | else: 50 | platform_short_name = MSDEV_PLATFORM_SHORT_NAMES[str(self.settings.arch)] 51 | configuration_name = str(self.settings.build_type).lower() 52 | build_directory = os.path.join('build', '%s-%s' % (platform_short_name, configuration_name)) 53 | self.copy('*.lib', src=build_directory, dst='lib', keep_path=False) 54 | self.copy('*.pdb', src=build_directory, dst='lib', keep_path=False) 55 | 56 | self.copy('*.hpp', src='reckless/include', dst='include', keep_path=True) 57 | self.copy('*.cpp', src='reckless/src', dst='src', keep_path=True) 58 | self.copy('LICENSE.txt', src='src') 59 | 60 | def package_info(self): 61 | self.cpp_info.libs = ["reckless"] 62 | 63 | def _gcc_compatible(self): 64 | return str(self.settings.compiler) in ['gcc', 'clang', 'apple-clang'] 65 | -------------------------------------------------------------------------------- /doc/images/mandelbrot.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasflodin/reckless/1f0d8471cb3f75b785441ad4cea81b16f6ef8814/doc/images/mandelbrot.jpeg -------------------------------------------------------------------------------- /doc/images/performance_call_burst_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasflodin/reckless/1f0d8471cb3f75b785441ad4cea81b16f6ef8814/doc/images/performance_call_burst_1.png -------------------------------------------------------------------------------- /doc/images/performance_call_burst_1_ma.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasflodin/reckless/1f0d8471cb3f75b785441ad4cea81b16f6ef8814/doc/images/performance_call_burst_1_ma.png -------------------------------------------------------------------------------- /doc/images/performance_mandelbrot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasflodin/reckless/1f0d8471cb3f75b785441ad4cea81b16f6ef8814/doc/images/performance_mandelbrot.png -------------------------------------------------------------------------------- /doc/images/performance_mandelbrot_difference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasflodin/reckless/1f0d8471cb3f75b785441ad4cea81b16f6ef8814/doc/images/performance_mandelbrot_difference.png -------------------------------------------------------------------------------- /doc/images/performance_periodic_calls_all.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasflodin/reckless/1f0d8471cb3f75b785441ad4cea81b16f6ef8814/doc/images/performance_periodic_calls_all.png -------------------------------------------------------------------------------- /doc/images/performance_periodic_calls_all_old_spdlog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasflodin/reckless/1f0d8471cb3f75b785441ad4cea81b16f6ef8814/doc/images/performance_periodic_calls_all_old_spdlog.png -------------------------------------------------------------------------------- /doc/images/performance_periodic_calls_reckless.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasflodin/reckless/1f0d8471cb3f75b785441ad4cea81b16f6ef8814/doc/images/performance_periodic_calls_reckless.png -------------------------------------------------------------------------------- /doc/images/performance_write_files.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattiasflodin/reckless/1f0d8471cb3f75b785441ad4cea81b16f6ef8814/doc/images/performance_write_files.png -------------------------------------------------------------------------------- /examples/.gitignore: -------------------------------------------------------------------------------- 1 | /severity_log 2 | /ucs2_log 3 | /custom_formatter 4 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(reckless_examples) 2 | CMAKE_MINIMUM_REQUIRED(VERSION 3.5) 3 | 4 | ################################################################################ 5 | # Build Examples 6 | ################################################################################ 7 | add_executable(severity_log severity_log.cpp ) 8 | target_link_libraries(severity_log reckless) 9 | 10 | add_executable(stderr_log stderr_log.cpp ) 11 | target_link_libraries(stderr_log reckless) 12 | 13 | add_executable(ucs2_log ucs2_log.cpp ) 14 | target_link_libraries(ucs2_log reckless) 15 | 16 | add_executable(custom_formatter custom_formatter.cpp ) 17 | target_link_libraries(custom_formatter reckless) 18 | 19 | add_executable(crash crash.cpp ) 20 | target_link_libraries(crash reckless) 21 | 22 | if (UNIX) 23 | target_link_libraries(severity_log pthread) 24 | target_link_libraries(stderr_log pthread) 25 | target_link_libraries(ucs2_log pthread) 26 | target_link_libraries(custom_formatter pthread) 27 | target_link_libraries(crash pthread) 28 | elseif(WIN32) 29 | target_link_libraries(severity_log Synchronization) 30 | target_link_libraries(stderr_log Synchronization) 31 | target_link_libraries(ucs2_log Synchronization) 32 | target_link_libraries(custom_formatter Synchronization) 33 | target_link_libraries(crash Synchronization) 34 | endif() 35 | -------------------------------------------------------------------------------- /examples/Tupfile.lua: -------------------------------------------------------------------------------- 1 | print('examples/Tupfile.lua') 2 | table.insert(OPTIONS.includes, '../reckless/include') 3 | libreckless = '../reckless/lib/' .. LIBPREFIX .. 'reckless' .. LIBSUFFIX 4 | for i, name in ipairs(tup.glob("*.cpp")) do 5 | obj = { 6 | compile(name), 7 | libreckless 8 | } 9 | link(tup.base(name), obj) 10 | end 11 | -------------------------------------------------------------------------------- /examples/crash.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of reckless logging 2 | * Copyright 2015-2020 Mattias Flodin 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | #include 23 | #include 24 | #include 25 | 26 | using log_t = reckless::policy_log< 27 | reckless::indent<4>, // 4 spaces of indent 28 | ' ', // Field separator 29 | reckless::timestamp_field // Then timestamp field 30 | >; 31 | 32 | reckless::file_writer writer("log.txt"); 33 | log_t g_log(&writer); 34 | 35 | int main() 36 | { 37 | // This shows how to install / uninstall a crash handler and how 38 | // it is ensured that the log queue is flushed on a crash. See manual 39 | // for more information. 40 | reckless::install_crash_handler(&g_log); 41 | for(int i=0; i!=16; i++) { 42 | g_log.write("Hello World!"); 43 | } 44 | char* p = 0; 45 | *p = 'X'; 46 | reckless::uninstall_crash_handler(); 47 | return 0; 48 | } 49 | -------------------------------------------------------------------------------- /examples/crash.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 15.0 23 | {72E95B9E-5DAF-4484-B399-ADD6DFBA1580} 24 | crash 25 | 10.0.17763.0 26 | 27 | 28 | 29 | v141 30 | 31 | 32 | true 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /examples/custom_formatter.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of reckless logging 2 | * Copyright 2015-2020 Mattias Flodin 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | // Example of a formatter for a user-defined type. 23 | #include 24 | #include 25 | #include 26 | 27 | reckless::file_writer writer("log.txt"); 28 | reckless::policy_log<> g_log(&writer); 29 | 30 | struct Coordinate 31 | { 32 | int x; 33 | int y; 34 | }; 35 | 36 | char const* format(reckless::output_buffer* pbuffer, char const* fmt, Coordinate const& c) 37 | { 38 | pbuffer->write("Hello "); 39 | if(*fmt == 'd') 40 | reckless::template_formatter::format(pbuffer, "(%d, %d)", c.x, c.y); 41 | else if(*fmt == 'x') 42 | reckless::template_formatter::format(pbuffer, "(%#x, %x)", c.x, c.y); 43 | else 44 | return nullptr; 45 | return fmt+1; 46 | } 47 | 48 | int main() 49 | { 50 | Coordinate c{13, 17}; 51 | g_log.write("%d", c); 52 | g_log.write("%x", c); 53 | 54 | return 0; 55 | } 56 | 57 | -------------------------------------------------------------------------------- /examples/custom_formatter.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {739D0E22-B02F-476A-8667-432F08AFF809} 23 | custom_formatter 24 | 10.0.17763.0 25 | 26 | 27 | 28 | v141 29 | 30 | 31 | true 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /examples/severity_log.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of reckless logging 2 | * Copyright 2015-2020 Mattias Flodin 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | #include 23 | #include 24 | 25 | // It is possible to build custom loggers for various ways of formatting the 26 | // log. The severity log is a stock policy-based logger that allows you to 27 | // configure fields that should be put on each line, including a severity 28 | // marker for debug/info/warning/error. 29 | using log_t = reckless::severity_log< 30 | reckless::indent<4>, // 4 spaces of indent 31 | ' ', // Field separator 32 | reckless::severity_field, // Show severity marker (D/I/W/E) first 33 | reckless::timestamp_field // Then timestamp field 34 | >; 35 | 36 | reckless::file_writer writer("log.txt"); 37 | log_t g_log(&writer); 38 | 39 | int main() 40 | { 41 | std::string s("Hello World!"); 42 | g_log.debug("Pointer: %p", s.c_str()); 43 | g_log.info("Info line: %s", s); 44 | for(int i=0; i!=4; ++i) { 45 | reckless::scoped_indent indent; // The indent object causes the lines 46 | g_log.warn("Warning: %d", i); // within this scope to be indented 47 | } 48 | g_log.error("Error: %f", 3.14); 49 | 50 | return 0; 51 | } 52 | -------------------------------------------------------------------------------- /examples/severity_log.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {3BDD683D-3B0B-4335-AF2A-86F203546B7E} 23 | severity_log 24 | 10.0.17763.0 25 | 26 | 27 | 28 | v141 29 | 30 | 31 | true 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /examples/stderr_log.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of reckless logging 2 | * Copyright 2015-2020 Mattias Flodin 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | #include 23 | #include 24 | 25 | reckless::stderr_writer writer; 26 | reckless::policy_log<> g_log(&writer); 27 | 28 | int main() 29 | { 30 | g_log.write("Hello World!"); 31 | return 0; 32 | } 33 | -------------------------------------------------------------------------------- /examples/template.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | PROJECT_GUID 23 | PROJECT_NAME 24 | 8.1 25 | 26 | 27 | 28 | v140 29 | 30 | 31 | true 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /examples/ucs2_log.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of reckless logging 2 | * Copyright 2015-2020 Mattias Flodin 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | #include 23 | #include 24 | #include 25 | 26 | class ucs2_log : public reckless::basic_log { 27 | public: 28 | using basic_log::basic_log; 29 | 30 | void puts(std::wstring const& s) 31 | { 32 | basic_log::write(s); 33 | } 34 | void puts(std::wstring&& s) 35 | { 36 | basic_log::write(std::move(s)); 37 | } 38 | private: 39 | struct ucs2_formatter { 40 | static void format(reckless::output_buffer* pbuffer, std::wstring const& s) 41 | { 42 | pbuffer->write(s.data(), sizeof(wchar_t)*s.size()); 43 | } 44 | }; 45 | }; 46 | 47 | reckless::file_writer writer("log.txt"); 48 | ucs2_log g_log(&writer); 49 | 50 | int main() 51 | { 52 | g_log.puts(L"Hello World!\n"); 53 | return 0; 54 | } 55 | 56 | -------------------------------------------------------------------------------- /examples/ucs2_log.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {323CC550-DE87-4ABB-ABBF-9567B424012D} 23 | ucs2_log 24 | 10.0.17763.0 25 | 26 | 27 | 28 | v141 29 | 30 | 31 | true 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /executable.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | reckless.lib;performance_log.lib;Synchronization.lib;%(AdditionalDependencies) 9 | Console 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /performance_log/include/performance_log/trace_log.hpp: -------------------------------------------------------------------------------- 1 | /* This file is part of reckless logging 2 | * Copyright 2015-2020 Mattias Flodin 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | #ifndef PERFORMANCE_LOG_TRACE_LOG_HPP 23 | #define PERFORMANCE_LOG_TRACE_LOG_HPP 24 | #include // size_t, max_align_t 25 | #include 26 | #include // is_empty 27 | #include // mutex, unique_lock 28 | #include // move 29 | #include // ostringstream 30 | #include 31 | #include 32 | #include 33 | 34 | #include 35 | 36 | namespace reckless { 37 | namespace detail { 38 | 39 | class trace_log { 40 | public: 41 | trace_log(std::size_t size) : 42 | pbuffer_(new char[size]), 43 | pnext_event_(pbuffer_), 44 | pbuffer_end_(pbuffer_ + size) 45 | { 46 | std::memset(pbuffer_, 0, size); 47 | } 48 | 49 | template 50 | void read(Callback const& callback) 51 | { 52 | if (pnext_event_ > pbuffer_end_) { 53 | throw std::logic_error( 54 | "Events have been written past end of buffer"); 55 | } 56 | void const* p = pbuffer_; 57 | std::string text; 58 | while(p != pnext_event_) { 59 | event_header const* pheader = static_cast(p); 60 | auto pcevent = static_cast(p) 61 | + aligned_sizeof(); 62 | p = pheader->pconsume_event(pcevent, &text); 63 | 64 | callback(text); 65 | } 66 | } 67 | 68 | void save(std::ostream& os) 69 | { 70 | read([&](std::string const& s) { 71 | os << s << std::endl; 72 | }); 73 | } 74 | 75 | template 76 | void log_event(Args&&... args) 77 | { 78 | auto const record_size = aligned_sizeof() + (std::is_empty()? 79 | 0 : aligned_sizeof()); 80 | char* pnext_event = atomic_fetch_add_relaxed(&pnext_event_, record_size); 81 | auto pevent_header = static_cast( 82 | static_cast(pnext_event)); 83 | auto pevent = pnext_event + aligned_sizeof(); 84 | pevent_header->pconsume_event = &consume_event; 85 | new (pevent) Event(std::forward(args)...); 86 | } 87 | 88 | private: 89 | struct event_header { 90 | void const* (*pconsume_event)(void const*, std::string*); 91 | }; 92 | 93 | static std::size_t align_size(std::size_t size) 94 | { 95 | std::size_t const alignment = alignof(std::max_align_t); 96 | return ((size + alignment - 1) / alignment)*alignment; 97 | } 98 | 99 | template 100 | static std::size_t aligned_sizeof() 101 | { 102 | return align_size(sizeof(T)); 103 | } 104 | 105 | template 106 | static void const* consume_event(void const* pvevent, std::string* ptext) 107 | { 108 | auto pevent = static_cast(pvevent); 109 | *ptext = pevent->format(); 110 | pevent->~Event(); 111 | 112 | auto pcevent = static_cast(pvevent); 113 | if(std::is_empty()) 114 | return pcevent; 115 | else 116 | return pcevent + aligned_sizeof(); 117 | } 118 | 119 | char* pbuffer_; 120 | char* pnext_event_; 121 | char* pbuffer_end_; 122 | }; 123 | 124 | struct timestamped_trace_event { 125 | std::uint64_t timestamp; 126 | 127 | timestamped_trace_event() : timestamp(rdtsc()) 128 | { 129 | } 130 | 131 | std::string format() const 132 | { 133 | std::ostringstream ostr; 134 | ostr << std::hex << timestamp; 135 | return ostr.str(); 136 | } 137 | }; 138 | 139 | extern trace_log g_trace_log; 140 | #define RECKLESS_TRACE(Event, ...) reckless::detail::g_trace_log.log_event(__VA_ARGS__) 141 | 142 | } // namespace detail 143 | } // namespace reckless 144 | 145 | #endif // PERFORMANCE_LOG_TRACE_LOG_HPP 146 | -------------------------------------------------------------------------------- /performance_log/lib/Tupfile.lua: -------------------------------------------------------------------------------- 1 | sources = tup.glob("../src/*.cpp") 2 | objects = table.map(sources, function(name) 3 | return '../src/' .. tup.base(name) .. OBJSUFFIX 4 | end) 5 | library('performance_log', objects) 6 | 7 | -------------------------------------------------------------------------------- /performance_log/src/Tupfile.lua: -------------------------------------------------------------------------------- 1 | table.insert(OPTIONS.includes, joinpath(tup.getcwd(), '../include')) 2 | for i, name in ipairs(filter_platform_files(tup.glob("*.cpp"))) do 3 | compile(name) 4 | end 5 | -------------------------------------------------------------------------------- /performance_log/src/performance_log.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of reckless logging 2 | * Copyright 2015-2020 Mattias Flodin 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | #include "performance_log/performance_log.hpp" 23 | 24 | #include 25 | #include 26 | 27 | #if defined(__linux__) 28 | #include 29 | #include 30 | #include 31 | 32 | void performance_log::detail::lock_memory(void const* addr, std::size_t len) 33 | { 34 | if(0 != mlock(addr, len)) 35 | throw std::system_error(errno, std::system_category()); 36 | } 37 | void performance_log::detail::unlock_memory(void const* addr, std::size_t len) 38 | { 39 | munlock(addr, len); 40 | } 41 | 42 | void performance_log::rdtscp_cpuid_clock::bind_cpu(int cpu) 43 | { 44 | int nprocessors = static_cast(sysconf(_SC_NPROCESSORS_ONLN)); 45 | assert(cpu < nprocessors); 46 | auto const size = CPU_ALLOC_SIZE(nprocessors); 47 | cpu_set_t* pcpuset = CPU_ALLOC(nprocessors); 48 | if(not pcpuset) 49 | throw std::bad_alloc(); 50 | try { 51 | CPU_ZERO_S(size, pcpuset); 52 | CPU_SET_S(cpu, size, pcpuset); 53 | int res = pthread_setaffinity_np(pthread_self(), size, pcpuset); 54 | if(res != 0) 55 | throw std::system_error(res, std::system_category()); 56 | } catch(...) { 57 | CPU_FREE(pcpuset); 58 | throw; 59 | } 60 | } 61 | 62 | void performance_log::rdtscp_cpuid_clock::unbind_cpu() 63 | { 64 | int nprocessors = static_cast(sysconf(_SC_NPROCESSORS_ONLN)); 65 | auto const size = CPU_ALLOC_SIZE(nprocessors); 66 | cpu_set_t* pcpuset = CPU_ALLOC(nprocessors); 67 | if(not pcpuset) 68 | throw std::bad_alloc(); 69 | try { 70 | CPU_ZERO_S(size, pcpuset); 71 | for(int i=0; i!=nprocessors; ++i) 72 | CPU_SET_S(i, size, pcpuset); 73 | int res = pthread_setaffinity_np(pthread_self(), size, pcpuset); 74 | if(res != 0) 75 | throw std::system_error(res, std::system_category()); 76 | } catch(...) { 77 | CPU_FREE(pcpuset); 78 | throw; 79 | } 80 | } 81 | 82 | #elif defined(_WIN32) 83 | 84 | #include 85 | 86 | void performance_log::detail::lock_memory(void const* addr, std::size_t len) 87 | { 88 | if (!VirtualLock(const_cast(addr), len)) { 89 | auto err = GetLastError(); 90 | throw std::system_error(err, std::system_category()); 91 | } 92 | } 93 | 94 | void performance_log::detail::unlock_memory(void const* addr, std::size_t len) 95 | { 96 | VirtualUnlock(const_cast(addr), len); 97 | } 98 | 99 | void performance_log::rdtscp_cpuid_clock::bind_cpu(int cpu) 100 | { 101 | DWORD_PTR mask = 1; 102 | mask <<= cpu; 103 | if (!SetThreadAffinityMask(GetCurrentThread(), mask)) { 104 | auto err = GetLastError(); 105 | throw std::system_error(err, std::system_category()); 106 | } 107 | } 108 | 109 | void performance_log::rdtscp_cpuid_clock::unbind_cpu() 110 | { 111 | DWORD_PTR mask = 0; 112 | mask -= 1; 113 | if (!SetThreadAffinityMask(GetCurrentThread(), mask)) { 114 | auto err = GetLastError(); 115 | throw std::system_error(err, std::system_category()); 116 | } 117 | } 118 | 119 | #endif 120 | 121 | -------------------------------------------------------------------------------- /reckless/Tuprules.lua: -------------------------------------------------------------------------------- 1 | table.insert(OPTIONS.includes, tup.getcwd() .. '/include') 2 | -------------------------------------------------------------------------------- /reckless/_Tuprules.tup: -------------------------------------------------------------------------------- 1 | CXXFLAGS += $(INCLUDESWITCH)$(TUP_CWD)/include 2 | -------------------------------------------------------------------------------- /reckless/include/reckless/crash_handler.hpp: -------------------------------------------------------------------------------- 1 | /* This file is part of reckless logging 2 | * Copyright 2015-2020 Mattias Flodin 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | #include 23 | 24 | namespace reckless { 25 | class basic_log; 26 | void install_crash_handler(std::initializer_list logs); 27 | void uninstall_crash_handler(); 28 | 29 | inline void install_crash_handler(basic_log* plog) 30 | { 31 | install_crash_handler({plog}); 32 | } 33 | 34 | class scoped_crash_handler { 35 | public: 36 | scoped_crash_handler(basic_log* plog) 37 | { 38 | install_crash_handler(plog); 39 | } 40 | scoped_crash_handler(std::initializer_list logs) 41 | { 42 | install_crash_handler(logs); 43 | } 44 | ~scoped_crash_handler() 45 | { 46 | uninstall_crash_handler(); 47 | } 48 | }; 49 | } // namespace reckless 50 | -------------------------------------------------------------------------------- /reckless/include/reckless/detail/fd_writer.hpp: -------------------------------------------------------------------------------- 1 | /* This file is part of reckless logging 2 | * Copyright 2015-2020 Mattias Flodin 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | #ifndef RECKLESS_FD_WRITER_HPP 23 | #define RECKLESS_FD_WRITER_HPP 24 | 25 | #include 26 | 27 | namespace reckless { 28 | namespace detail { 29 | 30 | class fd_writer : public writer { 31 | public: 32 | #if defined(__unix__) 33 | fd_writer(int fd) : fd_(fd) {} 34 | #elif defined(_WIN32) 35 | fd_writer(void* handle) : handle_(handle) {} 36 | #endif 37 | 38 | std::size_t write(void const* pbuffer, std::size_t count, std::error_code& ec) noexcept override; 39 | 40 | #if defined(__unix__) 41 | int fd_; 42 | #elif defined(_WIN32) 43 | void* handle_; 44 | #endif 45 | }; 46 | 47 | } // namespace detail 48 | } // namespace reckless 49 | 50 | #endif // RECKLESS_FD_WRITER_HPP 51 | -------------------------------------------------------------------------------- /reckless/include/reckless/detail/lockless_cv.hpp: -------------------------------------------------------------------------------- 1 | /* This file is part of reckless logging 2 | * Copyright 2015-2020 Mattias Flodin 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | #ifndef RECKLESS_DETAIL_LOCKLESS_CV_HPP 23 | #define RECKLESS_DETAIL_LOCKLESS_CV_HPP 24 | 25 | #include 26 | 27 | namespace reckless { 28 | namespace detail { 29 | 30 | // This provides the functionality of a condition variable for lockless code. 31 | // Normally a condition variable uses a mutex to ensure that the condition 32 | // doesn't have time to change between checking the condition and waiting. This 33 | // prevents the risk of race conditions that occurs when using events, whereby 34 | // 35 | // 1. Thread 1 checks the condition and it is not in the desired state. 36 | // 2. Thread 1 is preempted by thread 2, which changes the condition and 37 | // signals the event. 38 | // 3. Thread, 3 which is waiting on the event starts executing and resets the 39 | // event. 40 | // 4. Thread 1 is scheduled again and begins to wait for the event even though 41 | // the condition is now already in the desired state. It might stall here 42 | // indefinitely waiting for the event to be signaled again. 43 | // 44 | // Since we don't want to take a lock before checking the condition, we provide 45 | // an API to read the change-notification count instead. The intended usage is 46 | // 1. Call notify_count() and save the count. 47 | // 2. Check the condition of the shared data. 48 | // 3. If the shared data does not fulfill the desired condition, call wait() 49 | // and provide the notify count. The wait will occur only if the notify 50 | // count is still holds the expected value. If not, the call completes 51 | // immediately and execution restarts at 1. 52 | // 53 | // The read in step 1 has acquire semantics, guaranteeing that any writes to the 54 | // shared data done by another thread will occur before it notifies any waiters. 55 | // The notify_all() operation increases the count using corresponding release 56 | // semantics. 57 | // 58 | // Note that, just like with ordinary condition variables, a completed wait() 59 | // does not guarantee that the desired condition is fulfilled. 60 | class lockless_cv { 61 | public: 62 | lockless_cv() : notify_count_(0) {} 63 | 64 | unsigned notify_count() const 65 | { 66 | return atomic_load_acquire(¬ify_count_); 67 | } 68 | 69 | void notify_all(); 70 | void wait(unsigned expected_notify_count); 71 | void wait(unsigned expected_notify_count, unsigned milliseconds); 72 | 73 | private: 74 | unsigned notify_count_; 75 | }; 76 | 77 | } // namespace detail 78 | } // namespace reckless 79 | 80 | #endif // RECKLESS_DETAIL_LOCKLESS_CV_HPP 81 | -------------------------------------------------------------------------------- /reckless/include/reckless/detail/trace_log.hpp: -------------------------------------------------------------------------------- 1 | #ifdef RECKLESS_ENABLE_TRACE_LOG 2 | #include 3 | #else 4 | #define RECKLESS_TRACE(Event, ...) do {} while(false) 5 | #endif // RECKLESS_ENABLE_TRACE_LOG 6 | -------------------------------------------------------------------------------- /reckless/include/reckless/detail/utility.hpp: -------------------------------------------------------------------------------- 1 | /* This file is part of reckless logging 2 | * Copyright 2015-2020 Mattias Flodin 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | #ifndef RECKLESS_DETAIL_UTILITY_HPP 23 | #define RECKLESS_DETAIL_UTILITY_HPP 24 | 25 | #include // size_t 26 | #include // uintptr_t 27 | #include // enable_if, is_pointer, remove_pointer 28 | 29 | namespace reckless { 30 | namespace detail { 31 | 32 | template 33 | struct replace_pointer_type; 34 | 35 | template 36 | struct replace_pointer_type 37 | { 38 | using type = To*; 39 | }; 40 | 41 | template 42 | struct replace_pointer_type 43 | { 44 | using type = To const*; 45 | }; 46 | 47 | template 48 | T char_cast(char* p) 49 | { 50 | return static_cast(static_cast(p)); 51 | } 52 | 53 | template 54 | T char_cast(char const* p) 55 | { 56 | return static_cast(static_cast(p)); 57 | } 58 | 59 | template 60 | typename replace_pointer_type::type 61 | char_cast(T p) 62 | { 63 | using void_ptr_t = typename replace_pointer_type::type; 64 | using char_ptr_t = typename replace_pointer_type::type; 65 | return static_cast(static_cast(p)); 66 | } 67 | 68 | inline constexpr bool is_power_of_two(std::size_t v) 69 | { 70 | return (v & (v - 1)) == 0; 71 | } 72 | 73 | template 74 | struct index_sequence 75 | { 76 | }; 77 | 78 | template 79 | struct make_index_sequence_helper 80 | { 81 | typedef typename make_index_sequence_helper::type type; 82 | }; 83 | 84 | template 85 | struct make_index_sequence_helper 86 | { 87 | typedef index_sequence type; 88 | }; 89 | 90 | template 91 | struct make_index_sequence 92 | { 93 | typedef typename make_index_sequence_helper<0, N>::type type; 94 | }; 95 | 96 | } 97 | } 98 | 99 | #endif // RECKLESS_DETAIL_UTILITY_HPP 100 | -------------------------------------------------------------------------------- /reckless/include/reckless/file_writer.hpp: -------------------------------------------------------------------------------- 1 | /* This file is part of reckless logging 2 | * Copyright 2015-2020 Mattias Flodin 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | #ifndef RECKLESS_FILE_WRITER_HPP 23 | #define RECKLESS_FILE_WRITER_HPP 24 | 25 | #include "detail/fd_writer.hpp" 26 | 27 | namespace reckless { 28 | 29 | class file_writer : public detail::fd_writer { 30 | public: 31 | file_writer(char const* path); 32 | #if defined(_WIN32) 33 | file_writer(wchar_t const* path); 34 | #endif 35 | 36 | ~file_writer(); 37 | }; 38 | 39 | } // namespace reckless 40 | 41 | #endif // RECKLESS_FILE_WRITER_HPP 42 | -------------------------------------------------------------------------------- /reckless/include/reckless/ntoa.hpp: -------------------------------------------------------------------------------- 1 | /* This file is part of reckless logging 2 | * Copyright 2015-2020 Mattias Flodin 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | #ifndef RECKLESS_DETAIL_ITOA_HPP 23 | #define RECKLESS_DETAIL_ITOA_HPP 24 | #include 25 | 26 | #include 27 | 28 | namespace reckless { 29 | 30 | unsigned const UNSPECIFIED_PRECISION = std::numeric_limits::max(); 31 | // TODO It probably doesn't hurt to document these members even though they're 32 | // in the printf docs. Also, would be nice to reduce footprint e.g. with a 33 | // bitset. 34 | struct conversion_specification { 35 | conversion_specification() : 36 | minimum_field_width(0), 37 | precision(UNSPECIFIED_PRECISION), 38 | plus_sign(0), 39 | left_justify(false), 40 | alternative_form(false), 41 | pad_with_zeroes(false), 42 | uppercase(false) 43 | { 44 | } 45 | unsigned minimum_field_width; 46 | unsigned precision; 47 | char plus_sign; 48 | bool left_justify; 49 | bool alternative_form; 50 | bool pad_with_zeroes; 51 | bool uppercase; 52 | }; 53 | 54 | void itoa_base10(output_buffer* pbuffer, int value, conversion_specification const& cs); 55 | void itoa_base10(output_buffer* pbuffer, unsigned int value, conversion_specification const& cs); 56 | void itoa_base10(output_buffer* pbuffer, long value, conversion_specification const& cs); 57 | void itoa_base10(output_buffer* pbuffer, unsigned long value, conversion_specification const& cs); 58 | void itoa_base10(output_buffer* pbuffer, long long value, conversion_specification const& cs); 59 | void itoa_base10(output_buffer* pbuffer, unsigned long long value, conversion_specification const& cs); 60 | 61 | void itoa_base16(output_buffer* pbuffer, int value, conversion_specification const& cs); 62 | void itoa_base16(output_buffer* pbuffer, unsigned int value, conversion_specification const& cs); 63 | void itoa_base16(output_buffer* pbuffer, long value, conversion_specification const& cs); 64 | void itoa_base16(output_buffer* pbuffer, unsigned long value, conversion_specification const& cs); 65 | void itoa_base16(output_buffer* pbuffer, long long value, conversion_specification const& cs); 66 | void itoa_base16(output_buffer* pbuffer, unsigned long long value, conversion_specification const& cs); 67 | 68 | void ftoa_base10_f(output_buffer* pbuffer, double value, conversion_specification const& cs); 69 | void ftoa_base10_g(output_buffer* pbuffer, double value, conversion_specification const& cs); 70 | 71 | namespace detail 72 | { 73 | extern char const decimal_digits[201]; 74 | extern std::uint64_t const power_lut[19]; 75 | } 76 | 77 | } // namespace reckless 78 | 79 | #endif // RECKLESS_DETAIL_ITOA_HPP 80 | -------------------------------------------------------------------------------- /reckless/include/reckless/severity_log.hpp: -------------------------------------------------------------------------------- 1 | /* This file is part of reckless logging 2 | * Copyright 2015-2020 Mattias Flodin 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | #ifndef RECKLESS_SEVERITY_LOG_HPP 23 | #define RECKLESS_SEVERITY_LOG_HPP 24 | 25 | #include 26 | 27 | namespace reckless { 28 | class severity_field { 29 | public: 30 | severity_field(char severity) : severity_(severity) {} 31 | 32 | void format(output_buffer* poutput_buffer) const 33 | { 34 | char* p = poutput_buffer->reserve(1); 35 | *p = severity_; 36 | poutput_buffer->commit(1); 37 | } 38 | 39 | private: 40 | char severity_; 41 | }; 42 | 43 | namespace detail { 44 | template 45 | HeaderField construct_header_field(char) 46 | { 47 | return HeaderField(); 48 | } 49 | 50 | template <> 51 | inline severity_field construct_header_field(char severity) 52 | { 53 | return severity_field(severity); 54 | } 55 | } 56 | 57 | template 58 | class severity_log : public basic_log { 59 | public: 60 | using basic_log::basic_log; 61 | 62 | template 63 | void debug(char const* fmt, Args&&... args) 64 | { 65 | write('D', fmt, std::forward(args)...); 66 | } 67 | template 68 | void info(char const* fmt, Args&&... args) 69 | { 70 | write('I', fmt, std::forward(args)...); 71 | } 72 | template 73 | void warn(char const* fmt, Args&&... args) 74 | { 75 | write('W', fmt, std::forward(args)...); 76 | } 77 | template 78 | void error(char const* fmt, Args&&... args) 79 | { 80 | write('E', fmt, std::forward(args)...); 81 | } 82 | 83 | private: 84 | template 85 | void write(char severity, char const* fmt, Args&&... args) 86 | { 87 | basic_log::write>( 88 | detail::construct_header_field(severity)..., 89 | IndentPolicy(), 90 | fmt, 91 | std::forward(args)...); 92 | } 93 | }; 94 | 95 | } // namespace reckless 96 | 97 | #endif // RECKLESS_SEVERITY_LOG_HPP 98 | -------------------------------------------------------------------------------- /reckless/include/reckless/stdout_writer.hpp: -------------------------------------------------------------------------------- 1 | /* This file is part of reckless logging 2 | * Copyright 2015-2020 Mattias Flodin 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | #ifndef RECKLESS_STDOUT_WRITER_HPP 23 | #define RECKLESS_STDOUT_WRITER_HPP 24 | 25 | #include "detail/fd_writer.hpp" 26 | 27 | namespace reckless { 28 | 29 | #if defined(_WIN32) 30 | namespace detail { 31 | unsigned long const RECKLESS_STD_OUTPUT_HANDLE = static_cast(-11); 32 | unsigned long const RECKLESS_STD_ERROR_HANDLE = static_cast(-12); 33 | extern "C" { 34 | void* __stdcall GetStdHandle(unsigned long nStdHandle); 35 | } 36 | } // namespace detail 37 | #endif 38 | 39 | class stdout_writer : public detail::fd_writer { 40 | public: 41 | #if defined(__unix__) 42 | stdout_writer() : fd_writer(1) { } 43 | #elif defined(_WIN32) 44 | stdout_writer() : fd_writer(detail::GetStdHandle(detail::RECKLESS_STD_OUTPUT_HANDLE)) { } 45 | #endif 46 | }; 47 | 48 | class stderr_writer : public detail::fd_writer { 49 | public: 50 | #if defined(__unix__) 51 | stderr_writer() : fd_writer(2) { } 52 | #elif defined(_WIN32) 53 | stderr_writer() : fd_writer(detail::GetStdHandle(detail::RECKLESS_STD_ERROR_HANDLE)) { } 54 | #endif 55 | }; 56 | 57 | } // namespace reckless 58 | 59 | #endif // RECKLESS_STDOUT_WRITER_HPP 60 | 61 | -------------------------------------------------------------------------------- /reckless/include/reckless/template_formatter.hpp: -------------------------------------------------------------------------------- 1 | /* This file is part of reckless logging 2 | * Copyright 2015-2020 Mattias Flodin 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | #ifndef RECKLESS_TEMPLATE_FORMATTER_HPP 23 | #define RECKLESS_TEMPLATE_FORMATTER_HPP 24 | 25 | #include // forward 26 | #include 27 | #include // is_convertible 28 | 29 | namespace reckless { 30 | 31 | class output_buffer; 32 | namespace detail { 33 | template 34 | char const* invoke_custom_format(output_buffer* pbuffer, 35 | char const* pformat, T&& v); 36 | } 37 | 38 | class template_formatter { 39 | public: 40 | static void format(output_buffer* pbuffer, char const* pformat); 41 | 42 | template 43 | static void format(output_buffer* pbuffer, char const* pformat, 44 | T&& value, Args&&... args) 45 | { 46 | pformat = next_specifier(pbuffer, pformat); 47 | if(!pformat) 48 | return; 49 | 50 | char const* pnext_format = detail::invoke_custom_format(pbuffer, 51 | pformat, std::forward(value)); 52 | if(pnext_format) 53 | pformat = pnext_format; 54 | else 55 | append_percent(pbuffer); 56 | return template_formatter::format(pbuffer, pformat, 57 | std::forward(args)...); 58 | } 59 | 60 | private: 61 | static void append_percent(output_buffer* pbuffer); 62 | static char const* next_specifier(output_buffer* pbuffer, 63 | char const* pformat); 64 | }; 65 | 66 | char const* format(output_buffer* pbuffer, char const* pformat, char v); 67 | char const* format(output_buffer* pbuffer, char const* pformat, signed char v); 68 | char const* format(output_buffer* pbuffer, char const* pformat, unsigned char v); 69 | char const* format(output_buffer* pbuffer, char const* pformat, wchar_t v); 70 | char const* format(output_buffer* pbuffer, char const* pformat, char16_t v); 71 | char const* format(output_buffer* pbuffer, char const* pformat, char32_t v); 72 | 73 | char const* format(output_buffer* pbuffer, char const* pformat, short v); 74 | char const* format(output_buffer* pbuffer, char const* pformat, unsigned short v); 75 | char const* format(output_buffer* pbuffer, char const* pformat, int v); 76 | char const* format(output_buffer* pbuffer, char const* pformat, unsigned int v); 77 | char const* format(output_buffer* pbuffer, char const* pformat, long v); 78 | char const* format(output_buffer* pbuffer, char const* pformat, unsigned long v); 79 | char const* format(output_buffer* pbuffer, char const* pformat, long long v); 80 | char const* format(output_buffer* pbuffer, char const* pformat, unsigned long long v); 81 | 82 | char const* format(output_buffer* pbuffer, char const* pformat, float v); 83 | char const* format(output_buffer* pbuffer, char const* pformat, double v); 84 | char const* format(output_buffer* pbuffer, char const* pformat, long double v); 85 | 86 | char const* format(output_buffer* pbuffer, char const* pformat, char const* v); 87 | char const* format(output_buffer* pbuffer, char const* pformat, std::string const& v); 88 | 89 | char const* format(output_buffer* pbuffer, char const* pformat, void const* p); 90 | 91 | namespace detail { 92 | // TODO can we invoke free format() using argument-dependent lookup without 93 | // causing infinite recursion on this member function, without this 94 | // intermediary kludge? 95 | template 96 | char const* invoke_custom_format(output_buffer* pbuffer, char const* pformat, T&& v) 97 | { 98 | typedef decltype(format(pbuffer, pformat, std::forward(v))) return_type; 99 | static_assert(std::is_convertible::value, 100 | "return type of format must be char const*"); 101 | return format(pbuffer, pformat, std::forward(v)); 102 | } 103 | } // namesapce detail 104 | 105 | } // namespace reckless 106 | 107 | #endif // RECKLESS_TEMPLATE_FORMATTER_HPP 108 | -------------------------------------------------------------------------------- /reckless/include/reckless/writer.hpp: -------------------------------------------------------------------------------- 1 | /* This file is part of reckless logging 2 | * Copyright 2015-2020 Mattias Flodin 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | #ifndef RECKLESS_WRITER_HPP 23 | #define RECKLESS_WRITER_HPP 24 | 25 | #include // size_t 26 | #include // error_code, error_condition 27 | 28 | // TODO synchronous log for wrapping a channel and calling the formatter immediately. Or, just add a bool to basic_log? 29 | 30 | namespace reckless { 31 | 32 | // TODO this is a bit vague, rename to e.g. log_target or something? 33 | class writer { 34 | public: 35 | enum errc 36 | { 37 | temporary_failure = 1, 38 | permanent_failure = 2 39 | }; 40 | static std::error_category const& error_category(); 41 | 42 | virtual ~writer() = 0; 43 | virtual std::size_t write(void const* pbuffer, std::size_t count, 44 | std::error_code& ec) noexcept = 0; 45 | }; 46 | 47 | inline std::error_condition make_error_condition(writer::errc ec) 48 | { 49 | return std::error_condition(ec, writer::error_category()); 50 | } 51 | 52 | inline std::error_code make_error_code(writer::errc ec) 53 | { 54 | return std::error_code(ec, writer::error_category()); 55 | } 56 | } // namespace reckless 57 | 58 | namespace std 59 | { 60 | template <> 61 | struct is_error_condition_enum : public true_type {}; 62 | } 63 | #endif // RECKLESS_WRITER_HPP 64 | -------------------------------------------------------------------------------- /reckless/lib/Tupfile.lua: -------------------------------------------------------------------------------- 1 | sources = filter_platform_files(tup.glob("../src/*.cpp")) 2 | objects = table.map(sources, function(name) 3 | return '../src/' .. tup.base(name) .. OBJSUFFIX 4 | end) 5 | library('reckless', objects) 6 | -------------------------------------------------------------------------------- /reckless/lib/_Tupfile: -------------------------------------------------------------------------------- 1 | include_rules 2 | : ../src/*.o |> !ar |> libreckless.a 3 | -------------------------------------------------------------------------------- /reckless/reckless.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {5ca5ad7b-c8c1-4ab1-b9ee-940ec0918c44} 6 | 7 | 8 | {44ffc4b6-2345-42e3-88e2-04ccd847f89c} 9 | 10 | 11 | {f8e60ad4-e28f-40d4-9172-19b37acada3c} 12 | 13 | 14 | 15 | 16 | src 17 | 18 | 19 | include/reckless 20 | 21 | 22 | include/reckless 23 | 24 | 25 | include/reckless 26 | 27 | 28 | include/reckless 29 | 30 | 31 | include/reckless 32 | 33 | 34 | include/reckless 35 | 36 | 37 | include/reckless 38 | 39 | 40 | include/reckless 41 | 42 | 43 | include/reckless 44 | 45 | 46 | include/reckless\detail 47 | 48 | 49 | include/reckless\detail 50 | 51 | 52 | include/reckless\detail 53 | 54 | 55 | include/reckless\detail 56 | 57 | 58 | include/reckless\detail 59 | 60 | 61 | 62 | 63 | src 64 | 65 | 66 | src 67 | 68 | 69 | src 70 | 71 | 72 | src 73 | 74 | 75 | src 76 | 77 | 78 | src 79 | 80 | 81 | src 82 | 83 | 84 | src 85 | 86 | 87 | src 88 | 89 | 90 | src 91 | 92 | 93 | src 94 | 95 | 96 | src 97 | 98 | 99 | src 100 | 101 | 102 | src 103 | 104 | 105 | src 106 | 107 | 108 | -------------------------------------------------------------------------------- /reckless/src/Tupfile.lua: -------------------------------------------------------------------------------- 1 | if tup.getconfig('TRACE_LOG') != '' then 2 | table.insert(OPTIONS.define, 'RECKLESS_ENABLE_TRACE_LOG') 3 | end 4 | table.insert(OPTIONS.includes, '../../performance_log/include') 5 | for i, name in ipairs(filter_platform_files(tup.glob("*.cpp"))) do 6 | compile(name) 7 | end 8 | -------------------------------------------------------------------------------- /reckless/src/_Tupfile: -------------------------------------------------------------------------------- 1 | include_rules 2 | : foreach *.cpp |> !cxx |> 3 | -------------------------------------------------------------------------------- /reckless/src/crash_handler_unix.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of reckless logging 2 | * Copyright 2015-2020 Mattias Flodin 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | #include 23 | #include 24 | 25 | #include // memset 26 | #include 27 | #include // sort, unique 28 | #include 29 | #include 30 | #include 31 | #include // sigaction 32 | 33 | namespace reckless { 34 | namespace { 35 | // We set handlers for all signals that have a default behavior to 36 | // terminate the process or generate a core dump (and that have not had 37 | // their handler set to something else already). 38 | std::initializer_list const signals = { 39 | // POSIX.1-1990 signals 40 | SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, SIGFPE, SIGKILL, SIGSEGV, 41 | SIGPIPE, SIGALRM, SIGTERM, SIGUSR1, SIGUSR2, 42 | // SUSv2 + POSIX.1-2001 signals 43 | SIGBUS, SIGPOLL, SIGPROF, SIGSYS, SIGTRAP, SIGVTALRM, SIGXCPU, SIGXFSZ, 44 | // Various other signals 45 | SIGIOT, SIGSTKFLT, SIGIO, SIGPWR, 46 | }; 47 | 48 | std::vector g_logs; 49 | std::vector> g_old_sigactions; 50 | 51 | void signal_handler(int) 52 | { 53 | for(basic_log* plog : g_logs) 54 | plog->start_panic_flush(); 55 | for(basic_log* plog : g_logs) 56 | plog->await_panic_flush(); 57 | } 58 | } // anonymous namespace 59 | 60 | void install_crash_handler(std::initializer_list logs) 61 | { 62 | assert(logs.size() != 0); 63 | assert(g_logs.empty()); 64 | g_logs.assign(begin(logs), end(logs)); 65 | 66 | struct sigaction act; 67 | std::memset(&act, 0, sizeof(act)); 68 | act.sa_handler = &signal_handler; 69 | sigfillset(&act.sa_mask); 70 | act.sa_flags = SA_RESETHAND; 71 | // Some signals are synonyms for each other. Some are explictly specified 72 | // as such, but others may just be implemented that way on specific 73 | // systems. So we'll remove duplicate entries here before we loop through 74 | // all the signal numbers. 75 | std::vector unique_signals(signals); 76 | sort(begin(unique_signals), end(unique_signals)); 77 | unique_signals.erase(unique(begin(unique_signals), end(unique_signals)), 78 | end(unique_signals)); 79 | try { 80 | g_old_sigactions.reserve(unique_signals.size()); 81 | for(auto signal : unique_signals) { 82 | struct sigaction oldact; 83 | if(0 != sigaction(signal, nullptr, &oldact)) 84 | throw std::system_error(errno, std::system_category()); 85 | if(oldact.sa_handler == SIG_DFL) { 86 | if(0 != sigaction(signal, &act, nullptr)) 87 | { 88 | if(errno == EINVAL) { 89 | // If we get EINVAL then we assume that the kernel 90 | // does not know about this particular signal number. 91 | continue; 92 | } 93 | throw std::system_error(errno, std::system_category()); 94 | } 95 | g_old_sigactions.push_back({signal, oldact}); 96 | } 97 | } 98 | } catch(...) { 99 | uninstall_crash_handler(); 100 | throw; 101 | } 102 | } 103 | 104 | void uninstall_crash_handler() 105 | { 106 | assert(!g_logs.empty()); 107 | while(!g_old_sigactions.empty()) { 108 | auto const& p = g_old_sigactions.back(); 109 | auto signal = p.first; 110 | auto const& oldact = p.second; 111 | if(0 != sigaction(signal, &oldact, nullptr)) 112 | throw std::system_error(errno, std::system_category()); 113 | g_old_sigactions.pop_back(); 114 | } 115 | g_logs.clear(); 116 | } 117 | 118 | } // namespace reckless 119 | -------------------------------------------------------------------------------- /reckless/src/crash_handler_win32.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of reckless logging 2 | * Copyright 2015-2020 Mattias Flodin 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | 29 | #include 30 | #include 31 | 32 | namespace reckless { 33 | 34 | namespace { 35 | std::vector g_logs; 36 | LPTOP_LEVEL_EXCEPTION_FILTER g_old_exception_filter = nullptr; 37 | 38 | LONG WINAPI exception_filter(_EXCEPTION_POINTERS *ExceptionInfo) 39 | { 40 | for(basic_log* plog : g_logs) 41 | plog->start_panic_flush(); 42 | for(basic_log* plog : g_logs) 43 | plog->await_panic_flush(); 44 | if (g_old_exception_filter == nullptr) 45 | return EXCEPTION_CONTINUE_SEARCH; 46 | else 47 | return g_old_exception_filter(ExceptionInfo); 48 | } 49 | 50 | } // anonymous namespace 51 | 52 | void install_crash_handler(std::initializer_list logs) 53 | { 54 | assert(logs.size() != 0); 55 | assert(g_logs.empty()); 56 | g_logs.assign(begin(logs), end(logs)); 57 | 58 | g_old_exception_filter = SetUnhandledExceptionFilter(&exception_filter); 59 | } 60 | 61 | void uninstall_crash_handler() 62 | { 63 | assert(!g_logs.empty()); 64 | SetUnhandledExceptionFilter(g_old_exception_filter); 65 | g_old_exception_filter = nullptr; 66 | g_logs.clear(); 67 | } 68 | 69 | } // namespace reckless 70 | -------------------------------------------------------------------------------- /reckless/src/file_writer.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of reckless logging 2 | * Copyright 2015-2020 Mattias Flodin 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | #include "reckless/file_writer.hpp" 23 | 24 | #include 25 | #include 26 | #include // numeric_limits 27 | 28 | #if defined(__unix__) 29 | #include // open 30 | #include // open, lseek 31 | #include // open 32 | #include // errno 33 | #include // lseek, close 34 | 35 | #elif defined(_WIN32) 36 | 37 | #define NOMINMAX 38 | #include 39 | 40 | #endif 41 | 42 | #if defined(__unix__) 43 | namespace { 44 | int open_file(char const* path) 45 | { 46 | auto full_access = 47 | S_IRUSR | S_IWUSR | 48 | S_IRGRP | S_IWGRP | 49 | S_IROTH | S_IWOTH; 50 | int fd = open(path, O_WRONLY | O_CREAT | O_APPEND, full_access); 51 | if(fd == -1) 52 | throw std::system_error(errno, std::system_category()); 53 | return fd; 54 | } 55 | 56 | } 57 | 58 | reckless::file_writer::file_writer(char const* path) : 59 | fd_writer(open_file(path)) 60 | { 61 | } 62 | 63 | reckless::file_writer::~file_writer() 64 | { 65 | if(fd_ != -1) { 66 | while(-1 == close(fd_)) { 67 | if(errno != EINTR) 68 | break; 69 | } 70 | } 71 | } 72 | 73 | #elif defined(_WIN32) 74 | namespace { 75 | template 76 | HANDLE createfile_generic(F CreateFileX, T const* path) 77 | { 78 | // From NtCreateFile documentation: 79 | // (https://msdn.microsoft.com/en-us/library/bb432380.aspx) 80 | // If only the FILE_APPEND_DATA and SYNCHRONIZE flags are set, the caller 81 | // can write only to the end of the file, and any offset information on 82 | // writes to the file is ignored. However, the file is automatically 83 | // extended as necessary for this type of write operation. 84 | // TODO what happens if the user deletes the file while we are writing? 85 | HANDLE h = CreateFileX(path, 86 | FILE_APPEND_DATA | SYNCHRONIZE, 87 | FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 88 | NULL, 89 | OPEN_ALWAYS, 90 | FILE_ATTRIBUTE_NORMAL, 91 | NULL); 92 | if(h == INVALID_HANDLE_VALUE) 93 | throw std::system_error(GetLastError(), std::system_category()); 94 | return h; 95 | } 96 | } 97 | reckless::file_writer::file_writer(char const* path) : 98 | fd_writer(createfile_generic(CreateFileA, path)) 99 | { 100 | } 101 | 102 | reckless::file_writer::file_writer(wchar_t const* path) : 103 | fd_writer(createfile_generic(CreateFileW, path)) 104 | { 105 | } 106 | 107 | reckless::file_writer::~file_writer() 108 | { 109 | CloseHandle(handle_); 110 | } 111 | 112 | #endif 113 | -------------------------------------------------------------------------------- /reckless/src/lockless_cv.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of reckless logging 2 | * Copyright 2015-2020 Mattias Flodin 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | #include 23 | 24 | #if defined(__linux__) 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | namespace reckless { 33 | namespace detail { 34 | 35 | namespace { 36 | 37 | int sys_futex(void *addr1, int op, int val1, struct timespec const *timeout, 38 | void *addr2, int val3) 39 | { 40 | return syscall(SYS_futex, addr1, op, val1, timeout, addr2, val3); 41 | } 42 | 43 | } // anonymous namespace 44 | 45 | void lockless_cv::notify_all() 46 | { 47 | atomic_fetch_add_release(¬ify_count_, 1); 48 | sys_futex(¬ify_count_, FUTEX_WAKE_PRIVATE, 0x7fffffff, 49 | nullptr, nullptr, 0); 50 | } 51 | 52 | void lockless_cv::wait(unsigned expected_notify_count) 53 | { 54 | sys_futex(¬ify_count_, FUTEX_WAIT_PRIVATE, expected_notify_count, 55 | nullptr, 0, 0); 56 | } 57 | 58 | void lockless_cv::wait(unsigned expected_notify_count, unsigned milliseconds) 59 | { 60 | struct timespec timeout = {0, 0}; 61 | timeout.tv_sec = milliseconds/1000; 62 | timeout.tv_nsec = static_cast(milliseconds%1000)*1000000; 63 | 64 | sys_futex(¬ify_count_, FUTEX_WAIT_PRIVATE, 65 | expected_notify_count, &timeout, 0, 0); 66 | } 67 | 68 | } // namespace detail 69 | } // namespace reckless 70 | 71 | #elif defined(_WIN32) 72 | 73 | #include 74 | 75 | namespace reckless { 76 | namespace detail { 77 | 78 | void lockless_cv::notify_all() 79 | { 80 | atomic_fetch_add_release(¬ify_count_, 1); 81 | WakeByAddressAll(¬ify_count_); 82 | } 83 | 84 | void lockless_cv::wait(unsigned expected_notify_count) 85 | { 86 | WaitOnAddress(¬ify_count_, &expected_notify_count, sizeof(unsigned), INFINITE); 87 | } 88 | 89 | void lockless_cv::wait(unsigned expected_notify_count, unsigned milliseconds) 90 | { 91 | WaitOnAddress(¬ify_count_, &expected_notify_count, sizeof(unsigned), milliseconds); 92 | } 93 | 94 | } // namespace detail 95 | } // namespace reckless 96 | 97 | #else 98 | static_assert(false, "lockless_cv is not implemented for this platform") 99 | #endif 100 | -------------------------------------------------------------------------------- /reckless/src/mpsc_ring_buffer.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of reckless logging 2 | * Copyright 2015-2020 Mattias Flodin 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | #include 23 | #include // bad_alloc 24 | 25 | #if defined(__linux__) 26 | #include // mmap 27 | #include // shmget/shmat 28 | #include // shmget/shmat 29 | #include // S_IRUSR/S_IWUSR 30 | #include // get_page_size 31 | 32 | #include 33 | #elif defined(_WIN32) 34 | #include 35 | #endif 36 | 37 | namespace { 38 | template 39 | T round_nearest_power_of_2(T v, typename std::enable_if::type* = nullptr) 40 | { 41 | v--; 42 | v |= v >> 1; 43 | v |= v >> 2; 44 | v |= v >> 4; 45 | v |= v >> 8; 46 | v |= v >> 16; 47 | v++; 48 | return v; 49 | } 50 | 51 | template 52 | T round_nearest_power_of_2(T v, typename std::enable_if::type* = nullptr) 53 | { 54 | v--; 55 | v |= v >> 1; 56 | v |= v >> 2; 57 | v |= v >> 4; 58 | v |= v >> 8; 59 | v |= v >> 16; 60 | v |= v >> 32; 61 | v++; 62 | return v; 63 | } 64 | 65 | std::size_t round_capacity(std::size_t capacity) 66 | { 67 | #if defined(__linux__) 68 | std::size_t const granularity = reckless::detail::get_page_size(); 69 | #elif defined(_WIN32) 70 | std::size_t const granularity = 64*1024; 71 | #endif 72 | auto n_segments = (capacity + granularity - 1)/granularity; 73 | n_segments = round_nearest_power_of_2(n_segments); 74 | return n_segments*granularity; 75 | } 76 | } // anonymous namespace 77 | 78 | namespace reckless { 79 | namespace detail { 80 | 81 | void mpsc_ring_buffer::init(std::size_t capacity) 82 | { 83 | if(capacity == 0) { 84 | rewind(); 85 | pbuffer_start_ = nullptr; 86 | capacity_ = 0; 87 | return; 88 | } 89 | 90 | capacity = round_capacity(capacity); 91 | 92 | #if defined(__linux__) 93 | int shm = shmget(IPC_PRIVATE, capacity, IPC_CREAT | S_IRUSR | S_IWUSR); 94 | if(shm == -1) 95 | throw std::bad_alloc(); 96 | 97 | void* pbase = nullptr; 98 | while(!pbase) { 99 | pbase = mmap(nullptr, 2*capacity, PROT_NONE, 100 | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 101 | if(MAP_FAILED == pbase) 102 | throw std::bad_alloc(); 103 | munmap(pbase, 2*capacity); 104 | pbase = shmat(shm, pbase, 0); 105 | if(pbase) { 106 | if((void*)-1 == shmat(shm, static_cast(pbase) + capacity, 0)) 107 | { 108 | shmdt(pbase); 109 | pbase = nullptr; 110 | } 111 | } 112 | } 113 | shmctl(shm, IPC_RMID, nullptr); 114 | 115 | #elif defined(_WIN32) 116 | HANDLE mapping = CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, 117 | PAGE_READWRITE, static_cast((capacity >> 31)>>1), 118 | static_cast(capacity & 0xffffffff), nullptr); 119 | if(mapping == NULL) 120 | throw std::bad_alloc(); 121 | 122 | void* pbase = nullptr; 123 | while(!pbase) { 124 | pbase = VirtualAlloc(0, 2*capacity, MEM_RESERVE, PAGE_NOACCESS); 125 | if (!pbase) { 126 | CloseHandle(mapping); 127 | throw std::bad_alloc(); 128 | } 129 | VirtualFree(pbase, 0, MEM_RELEASE); 130 | pbase = MapViewOfFileEx(mapping, FILE_MAP_ALL_ACCESS, 0, 0, capacity, 131 | pbase); 132 | if(pbase) { 133 | if(!MapViewOfFileEx(mapping, FILE_MAP_ALL_ACCESS, 0, 0, capacity, 134 | static_cast(pbase) + capacity)) 135 | { 136 | UnmapViewOfFile(pbase); 137 | pbase = nullptr; 138 | } 139 | } 140 | } 141 | CloseHandle(mapping); 142 | #endif 143 | 144 | rewind(); 145 | std::memset(pbase, 0, capacity); 146 | pbuffer_start_ = static_cast(pbase); 147 | capacity_ = capacity; 148 | } 149 | 150 | void mpsc_ring_buffer::destroy() 151 | { 152 | if(!pbuffer_start_) 153 | return; 154 | #if defined(__linux__) 155 | shmdt(pbuffer_start_ + capacity_); 156 | shmdt(pbuffer_start_); 157 | 158 | #elif defined(_WIN32) 159 | UnmapViewOfFile(pbuffer_start_ + capacity_); 160 | UnmapViewOfFile(pbuffer_start_); 161 | #endif 162 | } 163 | 164 | } // namespace detail 165 | } // namespace reckless 166 | -------------------------------------------------------------------------------- /reckless/src/platform.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of reckless logging 2 | * Copyright 2015-2020 Mattias Flodin 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | #include 23 | 24 | #if defined(__unix__) 25 | #include // pthread_setname_np, pthread_self 26 | #endif 27 | #if defined(__linux__) 28 | #include // sysconf 29 | #endif 30 | #if defined(_WIN32) 31 | #include // GetSystemInfo 32 | #endif 33 | 34 | namespace reckless { 35 | namespace detail { 36 | 37 | unsigned get_page_size() 38 | { 39 | #if defined(__linux__) 40 | long sz = sysconf(_SC_PAGESIZE); 41 | return static_cast(sz); 42 | #elif defined(_WIN32) 43 | SYSTEM_INFO si; 44 | GetSystemInfo(&si); 45 | return si.dwPageSize; 46 | #else 47 | static_assert(false, "get_page_size() is not implemented for this OS"); 48 | #endif 49 | } 50 | 51 | void set_thread_name(char const* name) 52 | { 53 | #if defined(__unix__) 54 | pthread_setname_np(pthread_self(), name); 55 | 56 | #elif defined(_WIN32) 57 | // How to: Set a Thread Name in Native Code 58 | // https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx 59 | 60 | DWORD const MS_VC_EXCEPTION = 0x406D1388; 61 | #pragma pack(push,8) 62 | struct THREADNAME_INFO 63 | { 64 | DWORD dwType; // Must be 0x1000. 65 | LPCSTR szName; // Pointer to name (in user addr space). 66 | DWORD dwThreadID; // Thread ID (-1=caller thread). 67 | DWORD dwFlags; // Reserved for future use, must be zero. 68 | }; 69 | #pragma pack(pop) 70 | 71 | THREADNAME_INFO info; 72 | info.dwType = 0x1000; 73 | info.szName = name; 74 | info.dwThreadID = static_cast(-1); 75 | info.dwFlags = 0; 76 | __try{ 77 | RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), 78 | (ULONG_PTR*)&info); 79 | } 80 | __except (EXCEPTION_EXECUTE_HANDLER){ 81 | } 82 | #endif // _WIN32 83 | } 84 | 85 | 86 | unsigned const page_size = get_page_size(); 87 | 88 | } // detail 89 | } // reckless 90 | -------------------------------------------------------------------------------- /reckless/src/policy_log.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of reckless logging 2 | * Copyright 2015-2020 Mattias Flodin 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | #include 23 | 24 | RECKLESS_TLS unsigned reckless::scoped_indent::level_ = 0; 25 | -------------------------------------------------------------------------------- /reckless/src/spsc_event_win32.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of reckless logging 2 | * Copyright 2015-2020 Mattias Flodin 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | #include 23 | #include 24 | #include 25 | 26 | namespace reckless { 27 | namespace detail { 28 | 29 | spsc_event::spsc_event() : handle_(CreateEvent(NULL, FALSE, FALSE, NULL)) 30 | { 31 | if(handle_ == NULL) 32 | throw std::bad_alloc(); 33 | } 34 | 35 | spsc_event::~spsc_event() 36 | { 37 | CloseHandle(handle_); 38 | } 39 | 40 | } 41 | } -------------------------------------------------------------------------------- /reckless/src/trace_log.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of reckless logging 2 | * Copyright 2015-2020 Mattias Flodin 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | #ifdef RECKLESS_ENABLE_TRACE_LOG 24 | #include 25 | namespace reckless { 26 | namespace detail { 27 | trace_log g_trace_log(128*1024*1024); 28 | } 29 | } 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /reckless/src/unit_test.hpp: -------------------------------------------------------------------------------- 1 | /* This file is part of reckless logging 2 | * Copyright 2015-2020 Mattias Flodin 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | #ifdef UNIT_TEST 23 | 24 | #include // size_t 25 | #include 26 | #include 27 | #include 28 | #include // cout, endl 29 | #include // logic_error 30 | #include // ostringstream 31 | 32 | namespace unit_test { 33 | 34 | extern char const* g_current_testcase; 35 | struct no_context 36 | { 37 | }; 38 | 39 | template 40 | class test { 41 | public: 42 | test(char const* name, void (Context::*ptest_function)()) : 43 | name_(name), 44 | ptest_function_(ptest_function) 45 | { 46 | } 47 | void operator()(Context& ctx) 48 | { 49 | (ctx.*ptest_function_)(); 50 | } 51 | 52 | char const* name() const 53 | { 54 | return name_; 55 | } 56 | 57 | private: 58 | char const* name_; 59 | void (Context::*ptest_function_)(); 60 | }; 61 | 62 | template <> 63 | class test { 64 | public: 65 | test(char const* name, void (*ptest_function)()) : 66 | name_(name), 67 | ptest_function_(ptest_function) 68 | { 69 | } 70 | void operator()(no_context&) 71 | { 72 | (*ptest_function_)(); 73 | } 74 | 75 | char const* name() const 76 | { 77 | return name_; 78 | } 79 | 80 | private: 81 | char const* name_; 82 | void (*ptest_function_)(); 83 | }; 84 | 85 | class suite_base { 86 | public: 87 | virtual ~suite_base() = 0; 88 | virtual void operator()() = 0; 89 | virtual std::size_t succeeded() const = 0; 90 | virtual std::size_t count() const = 0; 91 | }; 92 | 93 | inline suite_base::~suite_base() {} 94 | 95 | inline std::vector& get_test_suites() 96 | { 97 | static std::vector test_suites; 98 | return test_suites; 99 | } 100 | 101 | inline void register_suite(suite_base* psuite) 102 | { 103 | get_test_suites().push_back(psuite); 104 | } 105 | 106 | template 107 | class suite : public suite_base { 108 | public: 109 | suite(std::initializer_list> tests) : 110 | tests_(tests), 111 | succeeded_(0) 112 | { 113 | register_suite(this); 114 | } 115 | 116 | void operator()() override 117 | { 118 | Context c; 119 | for(test& t : tests_) { 120 | g_current_testcase = t.name(); 121 | std::cout << t.name() << std::endl; 122 | try { 123 | t(c); 124 | } catch(std::exception const& e) { 125 | std::cout << " " << e.what() << std::endl; 126 | continue; 127 | } 128 | ++succeeded_; 129 | } 130 | } 131 | 132 | std::size_t succeeded() const override 133 | { 134 | return succeeded_; 135 | } 136 | std::size_t count() const override 137 | { 138 | return tests_.size(); 139 | } 140 | 141 | private: 142 | std::vector> tests_; 143 | std::size_t succeeded_; 144 | }; 145 | 146 | inline int run() 147 | { 148 | std::size_t succeeded = 0; 149 | for(suite_base* psuite : get_test_suites()) { 150 | (*psuite)(); 151 | if(psuite->succeeded() == psuite->count()) 152 | ++succeeded; 153 | } 154 | return 0; 155 | } 156 | 157 | class error : public std::logic_error { 158 | public: 159 | error(std::string const& expression, char const* file, unsigned line) : 160 | logic_error(make_what(expression, file, line)), 161 | expression_(expression), 162 | file_(file), 163 | line_(line) 164 | { 165 | } 166 | 167 | private: 168 | static std::string make_what(std::string const& expression, char const* file, unsigned line) 169 | { 170 | std::ostringstream ostr; 171 | ostr << file << '(' << line << "): error in \"" << g_current_testcase << "\": test " << expression << " failed"; 172 | return ostr.str(); 173 | } 174 | 175 | std::string expression_; 176 | char const* file_; 177 | unsigned line_; 178 | }; 179 | 180 | #define UNIT_TEST_MAIN() \ 181 | namespace unit_test { \ 182 | char const* g_current_testcase; \ 183 | } \ 184 | int main() \ 185 | { \ 186 | return unit_test::run(); \ 187 | } \ 188 | int main() 189 | 190 | 191 | 192 | #define TEST(a) if(a) {} else throw unit_test::error(#a, __FILE__, __LINE__) 193 | #define TESTCASE(name) {#name, &name} 194 | 195 | } // namespace unit_test 196 | #endif 197 | -------------------------------------------------------------------------------- /reckless/src/writer.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of reckless logging 2 | * Copyright 2015-2020 Mattias Flodin 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | #include 23 | 24 | namespace reckless { 25 | 26 | namespace { 27 | class error_category_t : public std::error_category { 28 | public: 29 | char const* name() const noexcept override; 30 | std::error_condition default_error_condition(int code) const noexcept override; 31 | std::string message(int condition) const override; 32 | }; 33 | 34 | } // anonymous namespace 35 | 36 | writer::~writer() 37 | { 38 | } 39 | 40 | std::error_category const& writer::error_category() 41 | { 42 | static error_category_t ec; 43 | return ec; 44 | } 45 | 46 | char const* error_category_t::name() const noexcept 47 | { 48 | return "reckless::writer"; 49 | } 50 | 51 | std::error_condition error_category_t::default_error_condition(int code) const noexcept 52 | { 53 | return static_cast(code); 54 | } 55 | 56 | std::string error_category_t::message(int condition) const 57 | { 58 | switch(static_cast(condition)) { 59 | case writer::temporary_failure: 60 | return "temporary failure while writing log"; 61 | case writer::permanent_failure: 62 | return "permanent failure while writing log"; 63 | } 64 | throw std::invalid_argument("invalid condition code"); 65 | } 66 | 67 | } // namespace reckless 68 | -------------------------------------------------------------------------------- /reckless/unittest/.gitignore: -------------------------------------------------------------------------------- 1 | /unit_test 2 | -------------------------------------------------------------------------------- /reckless/unittest/_Tupfile: -------------------------------------------------------------------------------- 1 | include_rules 2 | CXXFLAGS += -DUNIT_TEST -Wno-strict-aliasing 3 | : foreach ../src/*.cpp |> !cxx |> %B.o 4 | : *.o | $(RECKLESS_LIB)/libreckless.a $(PERFORMANCE_LOG_LIB)/libperformance_log.a |> !ld |> unit_test 5 | -------------------------------------------------------------------------------- /scripts/clean.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | from sys import stdout 3 | import os, errno 4 | from os.path import join 5 | import shutil 6 | import subprocess 7 | 8 | for root, dirs, files in os.walk('.'): 9 | assert root.startswith('.') 10 | if root.startswith('./'): 11 | root = root[2:] 12 | else: 13 | root = root[1:] 14 | if '.gitignore' not in files: 15 | continue 16 | 17 | files_to_remove = [] 18 | preserved_lines = [] 19 | with open(join(root, '.gitignore'), 'rU') as f: 20 | live = False 21 | for line in f.readlines(): 22 | if line == "##### TUP GITIGNORE #####\n": 23 | live = True 24 | if live: 25 | if line.startswith('/') and line != '/.gitignore\n': 26 | files_to_remove.append(line[1:-1]) 27 | else: 28 | preserved_lines.append(line) 29 | 30 | if len(files_to_remove) != 0: 31 | stdout.write(root + '/\n') 32 | for name in files_to_remove: 33 | stdout.write(' ') 34 | stdout.write(name + '\n') 35 | try: 36 | os.unlink(join(root, name)) 37 | except OSError as e: 38 | if e.errno == errno.ENOENT: 39 | pass 40 | else: 41 | raise 42 | 43 | if len(preserved_lines) == 0: 44 | os.unlink(join(root, '.gitignore')) 45 | else: 46 | with open(join(root, '.gitignore'), 'w') as f: 47 | f.write(''.join(preserved_lines)) 48 | 49 | shutil.rmtree(".tup") 50 | subprocess.check_call(['tup', 'init']) 51 | -------------------------------------------------------------------------------- /scripts/license_header.txt: -------------------------------------------------------------------------------- 1 | This file is part of reckless logging 2 | Copyright 2015-2020 Mattias Flodin 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | -------------------------------------------------------------------------------- /scripts/update_license_header.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import sys, os 4 | import re 5 | 6 | SOURCE_DIRS = ['reckless', 'examples', 'tests', 'performance_log'] 7 | 8 | ROOTDIR = os.path.relpath(os.path.join(os.path.dirname(__file__), '..')) 9 | 10 | def main(): 11 | read_license() 12 | for source_dir in SOURCE_DIRS: 13 | for dirpath, dirnames, filenames in os.walk(os.path.join(ROOTDIR, source_dir)): 14 | for filename in filenames: 15 | fullpath = os.path.join(dirpath, filename) 16 | fullpath = os.path.relpath(fullpath) 17 | if filename.endswith('.cpp') or filename.endswith('.hpp'): 18 | update_license_header(fullpath) 19 | return 0 20 | 21 | def read_license(): 22 | global LICENSE 23 | 24 | license = os.path.join(ROOTDIR, 'scripts', 'license_header.txt') 25 | with open(license) as f: 26 | license = f.read() 27 | license = license.split('\n') 28 | if license[-1] == '': 29 | license = license[:-1] 30 | 31 | LICENSE = ['/* ' + license[0]] + [' * ' + x for x in license[1:]] + [' */'] 32 | LICENSE = [x.rstrip() for x in LICENSE] 33 | LICENSE = '\n'.join(LICENSE) + '\n' 34 | 35 | def update_license_header(path): 36 | with open(path) as f: 37 | original_data = f.read() 38 | data = original_data 39 | if data.startswith('/*'): 40 | i = data.find('*/\n') 41 | if i != -1: 42 | data = data[i+3:] 43 | data = LICENSE + data 44 | if original_data == data: 45 | return 46 | with open(path, 'w') as f: 47 | f.write(data) 48 | print(path) 49 | 50 | if __name__ == '__main__': 51 | sys.exit(main()) 52 | -------------------------------------------------------------------------------- /tests/.gitignore: -------------------------------------------------------------------------------- 1 | /obj 2 | /unique_ptr 3 | -------------------------------------------------------------------------------- /tests/Tupfile.lua: -------------------------------------------------------------------------------- 1 | table.insert(OPTIONS.includes, '../reckless/include') 2 | libreckless = '../reckless/lib/' .. LIBPREFIX .. 'reckless' .. LIBSUFFIX 3 | for i, name in ipairs(tup.glob("*.cpp")) do 4 | obj = { 5 | compile(name), 6 | libreckless} 7 | link(tup.base(name), obj) 8 | end 9 | -------------------------------------------------------------------------------- /tests/consistent_address.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of reckless logging 2 | * Copyright 2015-2020 Mattias Flodin 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | // There was a bug in the pointer arithmetic in basic_log that would sometimes 24 | // cause a different virtual address (but the same physical address) than was 25 | // used during construction to be used for destroying an object submitted to 26 | // the log. This test case reproduces that bug. 27 | 28 | #include 29 | #include 30 | 31 | #include 32 | #include 33 | 34 | struct holder { 35 | void* pmy_address; 36 | holder() 37 | { 38 | pmy_address = this; 39 | } 40 | holder(const holder&) 41 | { 42 | pmy_address = this; 43 | } 44 | ~holder() 45 | { 46 | if (this != pmy_address) { 47 | std::fprintf(stderr, "inconsistent object address; expected %p " 48 | "but got %p\n", pmy_address, this); 49 | std::fflush(stderr); 50 | std::abort(); 51 | } 52 | } 53 | }; 54 | 55 | char const* format(reckless::output_buffer* p, char const* fmt, holder const&) 56 | { 57 | return fmt+1; 58 | } 59 | 60 | int main() 61 | { 62 | reckless::file_writer writer("log.txt"); 63 | reckless::policy_log<> log(&writer); 64 | for(int i=0; i!=500000; i++) { 65 | log.write("%s", holder()); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /tests/crash_handler.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of reckless logging 2 | * Copyright 2015-2020 Mattias Flodin 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | 28 | reckless::policy_log<> g_log; 29 | 30 | int main() 31 | { 32 | reckless::scoped_crash_handler crash_handler({&g_log}); 33 | reckless::file_writer writer("log.txt"); 34 | g_log.open(&writer); 35 | std::this_thread::sleep_for(std::chrono::seconds(2)); 36 | g_log.write("Hello World!"); 37 | char* p = nullptr; 38 | *p = 0; 39 | return 0; 40 | } 41 | -------------------------------------------------------------------------------- /tests/crash_handler.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {654EABD2-1CD0-41D5-85B3-2C727770D0FA} 23 | crash_handler 24 | 10.0.17763.0 25 | 26 | 27 | 28 | v141 29 | 30 | 31 | true 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /tests/eol.hpp: -------------------------------------------------------------------------------- 1 | /* This file is part of reckless logging 2 | * Copyright 2015-2020 Mattias Flodin 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | #include 23 | 24 | std::string eol(std::string const& s) 25 | { 26 | #if defined(__unix__) 27 | return s; 28 | #elif defined(_WIN32) 29 | std::string res; 30 | res.reserve(s.size()); 31 | for(char c : s) { 32 | if('\n' == c) 33 | res.push_back('\r'); 34 | res.push_back(c); 35 | } 36 | return res; 37 | #endif 38 | } 39 | -------------------------------------------------------------------------------- /tests/error_code.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of reckless logging 2 | * Copyright 2015-2020 Mattias Flodin 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | #include 23 | #include 24 | 25 | int main() 26 | { 27 | std::error_code code; 28 | std::error_condition special_ec(EINTR, code.category()); 29 | std::cout << code.category().name() << std::endl; 30 | bool eq = (code == reckless::writer::temporary_failure); 31 | eq = (reckless::writer::temporary_failure == code); 32 | eq = (code == std::errc::too_many_links); 33 | eq = (code == std::errc::no_space_on_device); 34 | code.clear(); 35 | eq = (code == special_ec); 36 | std::cout << eq << std::endl; 37 | return 0; 38 | } 39 | -------------------------------------------------------------------------------- /tests/error_code.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {B270E86E-A503-4159-9620-CF95B0E2C7F4} 23 | error_code 24 | 10.0.17763.0 25 | 26 | 27 | 28 | v141 29 | 30 | 31 | true 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /tests/flush.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of reckless logging 2 | * Copyright 2015-2020 Mattias Flodin 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | #include 23 | #include // sleep_for 24 | 25 | #include "unreliable_writer.hpp" 26 | 27 | unreliable_writer writer; 28 | int main() 29 | { 30 | reckless::policy_log<> log(&writer); 31 | 32 | std::this_thread::sleep_for(std::chrono::seconds(2)); 33 | log.write("(2) Write by log"); 34 | std::cout << "(1) Write by cout " << std::endl; 35 | 36 | std::this_thread::sleep_for(std::chrono::seconds(2)); 37 | log.write("(3) Write by log, then flush"); 38 | log.flush(); 39 | std::cout << "(4) Write by cout after flush " << std::endl; 40 | 41 | std::this_thread::sleep_for(std::chrono::seconds(2)); 42 | writer.error_code.assign(static_cast(std::errc::no_space_on_device), 43 | get_error_category()); 44 | std::cout <<"Simulating disk full" << std::endl; 45 | log.write("(6) Write by log, then flush"); 46 | try { 47 | log.flush(); 48 | } catch(reckless::writer_error const& e) { 49 | std::cout << "(5) Catch writer_error " << e.code() << " on flush" 50 | << std::endl; 51 | } 52 | std::this_thread::sleep_for(std::chrono::seconds(2)); 53 | std::cout <<"Simulating disk no longer full" << std::endl; 54 | writer.error_code.clear(); 55 | log.flush(); 56 | 57 | std::this_thread::sleep_for(std::chrono::seconds(2)); 58 | std::cout <<"Simulating disk full" << std::endl; 59 | writer.error_code.assign(static_cast(std::errc::no_space_on_device), 60 | get_error_category()); 61 | log.write("(8) Write by log, then flush"); 62 | std::error_code error; 63 | log.flush(error); 64 | if(error) { 65 | std::cout << "(7) Obtain error code " << error << " on flush" 66 | << std::endl; 67 | } 68 | writer.error_code.clear(); 69 | log.flush(); 70 | 71 | return 0; 72 | } 73 | -------------------------------------------------------------------------------- /tests/flush.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {C02F7537-CB77-4646-94B6-4922E6651CA3} 23 | flush 24 | 10.0.17763.0 25 | 26 | 27 | 28 | v141 29 | 30 | 31 | true 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /tests/format_error.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of reckless logging 2 | * Copyright 2015-2020 Mattias Flodin 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | #include "memory_writer.hpp" 23 | #include "eol.hpp" 24 | #include 25 | 26 | #include 27 | #include 28 | #include 29 | #include // rethrow_exception 30 | 31 | #include 32 | 33 | memory_writer g_writer; 34 | reckless::policy_log<> g_log; 35 | 36 | struct Object { 37 | Object() : copy_count_(0) 38 | { 39 | } 40 | Object(Object const& rhs) 41 | { 42 | copy_count_ = rhs.copy_count_ + 1; 43 | if(copy_count_ == 2) 44 | throw std::runtime_error("runtime error"); 45 | } 46 | 47 | unsigned copy_count_; 48 | }; 49 | 50 | char const* format(reckless::output_buffer* poutput, char const* fmt, Object) 51 | { 52 | if(*fmt != 's') 53 | return nullptr; 54 | poutput->write('X'); 55 | return fmt+1; 56 | } 57 | 58 | void format_error(reckless::output_buffer* poutput, std::exception_ptr const& pexception, std::type_info const&) 59 | { 60 | poutput->write("write(e.what()); 65 | } catch(...) { 66 | } 67 | #if defined(__unix__) 68 | poutput->write(">\n"); 69 | #elif defined(_WIN32) 70 | poutput->write(">\r\n"); 71 | #endif 72 | } 73 | 74 | int main() 75 | { 76 | g_log.open(&g_writer); 77 | g_log.write("Hello"); 78 | Object object; 79 | g_log.write("%s", object); 80 | g_log.write("World!"); 81 | g_log.close(); 82 | std::cout << g_writer.container; 83 | assert(g_writer.container == eol("Hello\nWorld!\n")); 84 | 85 | g_writer.container.clear(); 86 | g_log.open(&g_writer); 87 | g_log.format_error_callback(format_error); 88 | g_log.write("Hello"); 89 | g_log.write("%s", object); 90 | g_log.write("World!"); 91 | g_log.close(); 92 | std::cout << std::endl << g_writer.container; 93 | assert(g_writer.container == 94 | eol("Hello\n" 95 | "\n" 96 | "World!\n")); 97 | return 0; 98 | } 99 | -------------------------------------------------------------------------------- /tests/format_error.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {9DDEFDA0-2BCD-4A45-B3F9-EC067F04590B} 23 | format_error 24 | 10.0.17763.0 25 | 26 | 27 | 28 | v141 29 | 30 | 31 | true 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /tests/frame_construction_error.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of reckless logging 2 | * Copyright 2015-2020 Mattias Flodin 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | #include "memory_writer.hpp" 23 | #include "eol.hpp" 24 | #include 25 | 26 | #include 27 | #include 28 | #include 29 | #include // sleep_for 30 | 31 | #include 32 | 33 | memory_writer g_writer; 34 | reckless::policy_log<> g_log; 35 | 36 | struct Object { 37 | Object() 38 | { 39 | } 40 | Object(Object const&) 41 | { 42 | throw std::runtime_error("runtime error"); 43 | } 44 | }; 45 | 46 | char const* format(reckless::output_buffer* poutput, char const* fmt, Object) 47 | { 48 | if(*fmt != 's') 49 | return nullptr; 50 | poutput->write('X'); 51 | return fmt+1; 52 | } 53 | 54 | int main() 55 | { 56 | g_log.open(&g_writer); 57 | g_log.write("First"); 58 | Object object; 59 | try { 60 | g_log.write("%s", object); 61 | } catch(std::runtime_error const&) { 62 | g_log.write("Second"); 63 | } 64 | std::this_thread::sleep_for(std::chrono::seconds(3)); 65 | g_log.write("Third"); 66 | g_log.close(); 67 | std::cout << g_writer.container; 68 | assert(g_writer.container == eol("First\nSecond\nThird\n")); 69 | 70 | return 0; 71 | } 72 | -------------------------------------------------------------------------------- /tests/frame_construction_error.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {6F10F46C-EA37-40F8-815C-DA28B42BEBB9} 23 | frame_construction_error 24 | 10.0.17763.0 25 | 26 | 27 | 28 | v141 29 | 30 | 31 | true 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /tests/memory_writer.hpp: -------------------------------------------------------------------------------- 1 | /* This file is part of reckless logging 2 | * Copyright 2015-2020 Mattias Flodin 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | #ifndef TESTS_MEMORY_WRITER_HPP 23 | #define TESTS_MEMORY_WRITER_HPP 24 | #include 25 | 26 | template 27 | class memory_writer : public reckless::writer { 28 | public: 29 | std::size_t write(void const* data, std::size_t size, std::error_code& ec) noexcept override 30 | { 31 | char const* p = static_cast(data); 32 | container.insert(container.end(), p, p+size); 33 | ec.clear(); 34 | return size; 35 | } 36 | Container container; 37 | }; 38 | 39 | #endif // TESTS_MEMORY_WRITER_HPP 40 | -------------------------------------------------------------------------------- /tests/panic_flush.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of reckless logging 2 | * Copyright 2015-2020 Mattias Flodin 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #ifdef RECKLESS_ENABLE_TRACE_LOG 29 | #include 30 | #endif 31 | #include // cout 32 | 33 | #if defined(__unix__) 34 | #include 35 | #elif defined(_WIN32) 36 | #include 37 | #endif 38 | 39 | reckless::policy_log<> g_log; 40 | volatile int t1_count=0; 41 | volatile int t2_count=0; 42 | 43 | #if defined(__unix__) 44 | void sigsegv_handler(int) 45 | { 46 | int t1c = t1_count; 47 | int t2c = t2_count; 48 | g_log.start_panic_flush(); 49 | std::printf("Last log entry for t1 should be >=%d. Last log entry for t2 should be >=%d\n", t1c, t2c); 50 | g_log.await_panic_flush(); 51 | } 52 | #elif defined(_WIN32) 53 | LONG WINAPI exception_filter(_EXCEPTION_POINTERS*) 54 | { 55 | int t1c = t1_count; 56 | int t2c = t2_count; 57 | g_log.start_panic_flush(); 58 | std::printf("Last log entry for t1 should be >=%d. Last log entry for t2 should be >=%d\n", t1c, t2c); 59 | g_log.await_panic_flush(); 60 | return EXCEPTION_CONTINUE_SEARCH; 61 | } 62 | #endif 63 | 64 | int main() 65 | { 66 | #if defined(__unix__) 67 | struct sigaction act; 68 | std::memset(&act, 0, sizeof(act)); 69 | act.sa_handler = &sigsegv_handler; 70 | sigfillset(&act.sa_mask); 71 | act.sa_flags = SA_RESETHAND; 72 | sigaction(SIGSEGV, &act, nullptr); 73 | #elif defined(_WIN32) 74 | SetUnhandledExceptionFilter(&exception_filter); 75 | #endif 76 | 77 | reckless::file_writer writer("log.txt"); 78 | g_log.open(&writer); 79 | 80 | std::thread thread([]() 81 | { 82 | for(int i=0;i!=1000000;++i) { 83 | g_log.write("t2: %d", i); 84 | t2_count = i; 85 | } 86 | }); 87 | 88 | for(int i=0;i!=100000;++i) { 89 | g_log.write("t1: %d", i); 90 | t1_count = i; 91 | } 92 | char* p = nullptr; 93 | *p = 0; 94 | return 0; 95 | } 96 | -------------------------------------------------------------------------------- /tests/panic_flush.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {EBB7665E-F056-4C88-BDDD-E9137C873E2E} 23 | panic_flush 24 | 10.0.17763.0 25 | 26 | 27 | 28 | v141 29 | 30 | 31 | true 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /tests/temporary_error.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {755C00E4-0AA4-4AAE-97E8-562ADF62D365} 23 | temporary_error 24 | 10.0.17763.0 25 | 26 | 27 | 28 | v141 29 | 30 | 31 | true 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /tests/unique_ptr.cpp: -------------------------------------------------------------------------------- 1 | /* This file is part of reckless logging 2 | * Copyright 2015-2020 Mattias Flodin 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | #include 23 | #include 24 | #include 25 | 26 | namespace std 27 | { 28 | char const* format(reckless::output_buffer* p, char const* fmt, 29 | std::unique_ptr pc) 30 | { 31 | if(*fmt != 'c') 32 | return nullptr; 33 | p->write(*pc); 34 | return fmt+1; 35 | } 36 | } 37 | 38 | int main() 39 | { 40 | reckless::file_writer writer("log.txt"); 41 | reckless::policy_log<> log(&writer); 42 | log.write("%c", std::unique_ptr(new char('A'))); 43 | // C++14: 44 | //log.write("%c", std::make_unique('A')); 45 | } 46 | -------------------------------------------------------------------------------- /tests/unique_ptr.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {660F9398-EEFE-4BE0-B876-45A0CA53F8D7} 23 | unique_ptr 24 | 10.0.17763.0 25 | 26 | 27 | 28 | v141 29 | 30 | 31 | true 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /tests/unreliable_writer.hpp: -------------------------------------------------------------------------------- 1 | /* This file is part of reckless logging 2 | * Copyright 2015-2020 Mattias Flodin 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | #include 23 | #include 24 | 25 | class error_category : public std::error_category { 26 | public: 27 | char const* name() const noexcept override 28 | { 29 | return "unreliable_writer"; 30 | } 31 | std::error_condition default_error_condition(int code) const noexcept override 32 | { 33 | return std::system_category().default_error_condition(code); 34 | } 35 | bool equivalent(int code, std::error_condition const& condition) const noexcept override 36 | { 37 | if(condition.category() == reckless::writer::error_category()) 38 | return errc_to_writer_category(code) == condition.value(); 39 | else 40 | return std::system_category().equivalent(code, condition); 41 | } 42 | bool equivalent(std::error_code const& code, int condition) const noexcept override 43 | { 44 | if(code.category() == reckless::writer::error_category()) 45 | return errc_to_writer_category(condition) == code.value(); 46 | else 47 | return std::system_category().equivalent(code, condition); 48 | } 49 | std::string message(int condition) const override 50 | { 51 | return std::system_category().message(condition); 52 | } 53 | private: 54 | int errc_to_writer_category(int code) const 55 | { 56 | if(code == static_cast(std::errc::no_space_on_device)) 57 | return reckless::writer::temporary_failure; 58 | else 59 | return reckless::writer::permanent_failure; 60 | } 61 | }; 62 | 63 | inline error_category const& get_error_category() 64 | { 65 | static error_category cat; 66 | return cat; 67 | } 68 | 69 | class unreliable_writer : public reckless::writer { 70 | public: 71 | std::size_t write(void const* data, std::size_t size, std::error_code& ec) noexcept override 72 | { 73 | ec = error_code; 74 | if(!ec) { 75 | std::cout.write(static_cast(data), size); 76 | return size; 77 | } 78 | return 0; 79 | } 80 | 81 | std::error_code error_code; 82 | }; 83 | 84 | -------------------------------------------------------------------------------- /tup.config.template: -------------------------------------------------------------------------------- 1 | #CONFIG_STLSOFT=/PATH/stlsoft 2 | #CONFIG_SPDLOG=/PATH/spdlog 3 | #CONFIG_BOOST_LOG_INCLUDE=/PATH/boost 4 | #CONFIG_BOOST_LOG_LIB=/PATH/boost 5 | #CONFIG_CXX=clang++ 6 | CONFIG_DEBUG=yes 7 | #CONFIG_TRACE_LOG=yes 8 | --------------------------------------------------------------------------------