Misc

Logging

PVXS internally logs warning/status messages using errlog.h from EPICS Base. User applications may control which messages are printed, and may add output.

#include <pvxs/log.h>
namespace pvxs { ... }

Control of log message output is available through named loggers. All internal logger names begin with the prefix “pvxs.”.

In addition to a name, each logger has an associated integer logging level. A message will be logged if the level of the message is less than or equal to the level of the associated logger.

To enable all logging at full detail.

export PVXS_LOG="*=DEBUG"
enum pvxs::Level

Importance of message.

Values:

enumerator Debug
enumerator Info
enumerator Warn
enumerator Err
enumerator Crit

Controlling Logging

By default, all loggers have level Err. It is recommended that user applications prefer configuration through the environment variable $PVXS_LOG by calling pvxs::logger_config_env().

void pvxs::logger_config_env()

Configure logging from environment variable $PVXS_LOG

Value of the form “key=VAL,…”

Keys may be literal logger names, or may include ‘*’ wildcards to match multiple loggers. eg. “pvxs.*=DEBUG” will enable all internal log messages.

VAL may be one of “CRIT”, “ERR”, “WARN”, “INFO”, or “DEBUG”

If this is undesireable, logger levels may be (reset) manually.

void pvxs::logger_level_set(const char *name, Level lvl)
void pvxs::logger_level_clear()

Remove any previously logger configurations. Does not change any logger::lvl Use prior to re-applying new configuration.

Logging from User applications

To emit log messages from user code, a new logger should be defined with DEFINE_LOGGER which will be usable within the current translation unit. It is allowable for multiple loggers to have the same name.

Logger names beginning with “pvxs.*” is reserved for internal usage, and must not be present in user code.

DEFINE_LOGGER(VAR, NAME)

Define a new logger global.

Parameters
  • VAR: The (static) variable name passed to log_printf() and friends.

  • NAME: A name string in “A.B.C” form.

log_crit_printf(LOGGER, FMT, ...)
log_err_printf(LOGGER, FMT, ...)
log_warn_printf(LOGGER, FMT, ...)
log_info_printf(LOGGER, FMT, ...)
log_debug_printf(LOGGER, FMT, ...)
log_printf(LOGGER, LVL, FMT, ...)

Try to log a message at the defined level.

Due to portability issues with MSVC, log formats must have at least one argument.

DEFINE_LOGGER(blah, "myapp.blah");
void blahfn(int x) {
    log_info_printf(blah, "blah happened with %d\n", x);

struct pvxs::logger

A logger.

Public Functions

bool test(Level lvl)

Return

true if the logger currently allows a message at level LVL.

Public Members

const char *const name

global name of this logger. Need not be unique

std::atomic<Level> lvl

Current logging level. See logger_level_set().

Identification

Compile time access to PVXS library version information.

#include <pvxs/util.h>
namespace pvxs { ... }
PVXS_VERSION

Current library version.

PVXS_ABI_VERSION

Current library ABI version

Since

0.1.1

VERSION_INT(V, R, M, P)

Construct version number constant.

eg. to conditionally compile based on library version.

#if PVXS_VERSION < VERSION_INT(1,2,3,4)
// enable some compatibility code
#endif
unsigned long pvxs::version_int()

Return

PVXS_VERSION captured at library compile time

const char *pvxs::version_str()

Library version as a string. eg. “PVXS 1.2.3”.

unsigned long pvxs::version_abi_int()

Return

PVXS_ABI_VERSION captured at library compile time

Since

0.1.1

bool pvxs::version_abi_check()

Runtime ABI check.

This test is only meaningful if it is preformed prior to any other library calls.

It is guaranteed that the library has no global constructors.

Return

true if the header and library ABI versions match, and if the header version is not newer than the library version.

Since

0.1.1

Unit-test helpers

Extensions to epicsUnitTest.h

#include <pvxs/unittest.h>
namespace pvxs { ... }
testTrue(EXPR)

Macro which assert that an expression evaluate to ‘true’. Evaluates to a pvxs::testCase

testFalse(EXPR)

Macro which assert that an expression evaluate to ‘true’. Evaluates to a pvxs::testCase

testEq(LHS, RHS)

Macro which asserts equality between LHS and RHS. Evaluates to a pvxs::testCase Roughly equivalent to

testOk((LHS)==(RHS), "..."); 

testNotEq(LHS, RHS)

Macro which asserts in-equality between LHS and RHS. Evaluates to a pvxs::testCase Roughly equivalent to

testOk((LHS)!=(RHS), "..."); 

testStrEq(LHS, RHS)

Macro which asserts equality between LHS and RHS. Evaluates to a pvxs::testCase Functionally equivalent to testEq() with two std::string instances. Prints diff-like output which is friendlier to multi-line strings.

testStrMatch(EXPR, STR)

Macro which asserts that STR matches the regular expression EXPR Evaluates to a pvxs::testCase

Since

0.2.1 Expression syntax is POSIX extended.

Since

0.1.1

testArrEq(LHS, RHS)

Macro which asserts equality between LHS and RHS. Evaluates to a pvxs::testCase Functionally equivalent to testEq() for objects with .size() and operator[]. Prints element by element differences

testShow()

Macro which prints diagnostic (non-test) lines. Evaluates to a pvxs::testCase Roughly equivalent to

testDiag("..."); 

The testEq() macro and friends expand to a function which returns a pvxs::testCase instance which may be used as a std::ostream to append text describing a test. eg.

testEq(1, 1)<<"We really hope this is true.";
if(testNotEq(1, 2)<<"shouldn't be true") {
    // further conditional tests if 1!=2
}
template<class Exception, typename FN>
testCase pvxs::testThrows(FN fn)

Assert that an exception is thrown.

testThrows<std::runtime_error>([]() {
     testShow()<<"Now you see me";
     throw std::runtime_error("I happened");
     testShow()<<"Now you don't";
})<<"some message";
Return

A testCase which passes if an Exception instance was caught, and false otherwise (wrong type, or no exception).

Template Parameters
  • Exception: The exception type which should be thrown

Parameters
  • fn: A callable

template<class Exception, typename FN>
testCase pvxs::testThrowsMatch(const std::string &expr, FN fn)

Assert that an exception is throw with a certain message.

testThrowsMatch<std::runtime_error>("happened", []() {
     testShow()<<"Now you see me";
     throw std::runtime_error("I happened");
     testShow()<<"Now you don't";
})<<"some message";
Return

A testCase which passes if an Exception instance was caught and std::exception::what() matched the provided regular expression.

Template Parameters
  • Exception: The exception type which should be thrown

Parameters
  • expr: A regular expression

  • fn: A callable

Since

0.1.1

class pvxs::testCase

A single test case (or diagnostic line).

Acts as an output string to accumulate test comment. Multi-line output results in one test line, and subsequent diagnostic lines.

Test line is printed when an active (non-moved) testCase is destroyed.

Public Functions

testCase()

new diag message

testCase(bool result)

new test case

operator bool() const

true when passing

testCase &setPass(bool v)

Override current pass/fail result.

testCase &setPassMatch(const std::string &expr, const std::string &inp)

Override current pass/fail result if input matches a regular expression

Since

0.2.1 Expression syntax is POSIX extended.

Since

0.1.1 Added

template<typename T>
testCase &operator<<(const T &v)

Append to message.

Utilities

Misc. utility code.

#include <pvxs/util.h>
namespace pvxs { ... }
detail::Escaper pvxs::escape(const std::string &s)

Print string to output stream with non-printable characters escaped.

Outputs (almost) C-style escapes. Prefers short escapes for newline, tab, quote, etc (“\n”). Falls back to hex escape (eg. “\xab”).

Unlike C, hex escapes are always 2 chars. eg. the output “\xabcase” would need to be manually changed to “\xab””case” to be used as C source.

std::string blah("this \"is a test\"");
std::cout<<pvxs::escape(blah);

detail::Escaper pvxs::escape(const char *s)

Print nil terminated char array to output stream with non-printable characters escaped.

std::cout<<pvxs::escape("this \"is a test\"");

detail::Escaper pvxs::escape(const char *s, size_t n)

Print fixed length char array to output stream with non-printable characters escaped.

std::cout<<pvxs::escape("this \"is a test\"", 6);
// prints 'this \"'

void pvxs::cleanup_for_valgrind()

Free some internal global allocations to avoid false positives in valgrind (or similar) tools looking for memory leaks.

Calls libevent_global_shutdown() when available (libevent >=2.1).

Warning

This function is optional. If you don’t understand the intended use case, then do not call it!

Pre

Caller must release all resources explicitly allocated through PVXS (on all threads).

Post

Invalidates internal state. Use of any API functions afterwards is undefined!

class SigInt

Portable process signal handling in CLI tools.

epicsEvent evt;
SigInt handle([&evt]() {
     evt.trigger();
});
... setup network operations
evt.wait();
// completion, or SIGINT

Saves existing handler, which are restored by dtor.


Since

1.1.0 “handler” action runs in thread context. Safe to take locks etc.

std::ostream &pvxs::target_information(std::ostream&)

Describe build and runtime configuration of current system.

Print information which may be using for when troubleshooting, or creating a bug report.

Printed by CLI “pvxinfo -D” and iocsh “pvxs_target_information”.

Return

The same ostream passed as argument.

template<typename T>
class pvxs::MPMCFIFO

Thread-safe, bounded, multi-producer, multi-consumer FIFO queue.

Queue value_type must be movable. If T is also copy constructable, then push(const T&) may be used.

As an exception, the destructor is not re-entrant. Concurrent calls to methods during destruction will result in undefined behavior.

MPMCFIFO<std::function<void()>> Q;
...
while(auto work = Q.pop()) { // Q.push(nullptr) to break loop
    work();
}

Since

0.2.0

Public Types

typedef T value_type

Template parameter.

Public Functions

MPMCFIFO(size_t limit = 0u)

Construct a new queue

Parameters
  • limit: If non-zero, then emplace()/push() will block while while queue size is greater than or equal to this limit.

~MPMCFIFO()

Destructor is not re-entrant.

size_t size() const

Poll number of elements in the work queue at this moment.

template<typename ...Args>
void emplace(Args&&... args)

Construct a new element into the queue.

Will block while full.

void push(T &&ent)

Move a new element to the queue.

void push(const T &ent)

Copy a new element to the queue.

T pop()

Remove an element from the queue.

Blocks while queue is empty.