From ef3620529c936a8790d6970c57198b4caee09c0d Mon Sep 17 00:00:00 2001 From: "E. Scott Daniels" Date: Wed, 22 Jul 2020 15:49:54 -0400 Subject: [PATCH] Add metrics to the framework This change adds support for a metrics class which provides the API allowing an xAPP to easily create and send a set of measurements to a central collector for forwarding. Issue-ID: RIC381 Signed-off-by: E. Scott Daniels Change-Id: I0926b26d4862df308ab0863260805fe057785bdc --- CHANGES | 3 + CMakeLists.txt | 15 +-- doc/src/.gitignore | 10 ++ doc/src/rtd/rel-notes.rst | 57 --------- doc/src/user/Makefile | 2 +- doc/src/user/example2.im | 6 + doc/src/user/metrics.im | 181 +++++++++++++++++++++++++++ doc/src/user/user_guide.xfm | 1 + docs/rel-notes.rst | 5 + docs/user-guide.rst | 187 +++++++++++++++++++++++++++- examples/Makefile | 3 +- examples/xapp_t1.cpp | 18 ++- src/json/jwrapper.c | 2 +- src/messaging/CMakeLists.txt | 10 +- src/messaging/message.cpp | 11 +- src/messaging/messenger.cpp | 23 ++++ src/messaging/messenger.hpp | 5 + src/metrics/CMakeLists.txt | 40 ++++++ src/metrics/metrics.cpp | 284 +++++++++++++++++++++++++++++++++++++++++++ src/metrics/metrics.hpp | 102 ++++++++++++++++ test/Makefile | 14 ++- test/metrics_test.cpp | 109 +++++++++++++++++ test/unit_test.cpp | 26 +++- test/unit_test.sh | 32 ++++- 24 files changed, 1047 insertions(+), 99 deletions(-) create mode 100644 doc/src/.gitignore delete mode 100644 doc/src/rtd/rel-notes.rst create mode 100644 doc/src/user/metrics.im create mode 100644 src/metrics/CMakeLists.txt create mode 100644 src/metrics/metrics.cpp create mode 100644 src/metrics/metrics.hpp create mode 100644 test/metrics_test.cpp diff --git a/CHANGES b/CHANGES index b856218..8663e5a 100644 --- a/CHANGES +++ b/CHANGES @@ -6,6 +6,9 @@ # squished to one. release = Cherry +2020 July 22; version 2.1.0 + Added metrics support (RIC-381). + 2020 July 17; version 2.0.0 Add alarm support. diff --git a/CMakeLists.txt b/CMakeLists.txt index 949b158..308c524 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,7 +29,7 @@ project( ricxfcpp ) cmake_minimum_required( VERSION 3.5 ) set( major_version "2" ) # should be automatically populated from git tag later, but until CI process sets a tag we use this -set( minor_version "0" ) +set( minor_version "1" ) set( patch_level "0" ) set( install_root "${CMAKE_INSTALL_PREFIX}" ) @@ -156,7 +156,7 @@ set ( srcd "${CMAKE_CURRENT_SOURCE_DIR}" ) # but Cmake insists on having these exist when we add them to include directories to # enable code to find them after we build them. # -include_directories( "${srcd}/src/messaging;${srcd}/src/json;${srcd}/src/alarm;${srcd}/ext/jsmn" ) +include_directories( "${srcd}/src/messaging;${srcd}/src/json;${srcd}/src/alarm;${srcd}/src/metrics;${srcd}/ext/jsmn" ) # Compiler flags # @@ -186,12 +186,13 @@ add_subdirectory( src/json ) add_subdirectory( src/messaging ) add_subdirectory( src/xapp ) add_subdirectory( src/alarm ) +add_subdirectory( src/metrics ) #add_subdirectory( doc ) # this will auto skip if {X}fm is not available # shared and static libraries are built from the same object files. # add_library( ricxfcpp_shared SHARED - "$;$;$;$" + "$;$;$;$;$" ) set_target_properties( ricxfcpp_shared PROPERTIES @@ -199,12 +200,12 @@ set_target_properties( ricxfcpp_shared SOVERSION ${major_version} VERSION ${major_version}.${minor_version}.${patch_level} ) -target_include_directories( ricxfcpp_shared PUBLIC "src/messenger" "src/xapp" ) +#target_include_directories( ricxfcpp_shared PUBLIC "src/alarm" "src/metrics" "src/messenger" "src/xapp" ) # we only build/export the static archive (.a) if generating a dev package if( DEV_PKG ) add_library( ricxfcpp_static STATIC - "$;$;$" + "$;$;$;$;$" ) set_target_properties( ricxfcpp_static PROPERTIES @@ -212,14 +213,14 @@ if( DEV_PKG ) SOVERSION ${major_version} VERSION ${major_version}.${minor_version}.${patch_level} ) - target_include_directories( ricxfcpp_static PUBLIC "src/messenger" "src/xapp" ) +# target_include_directories( ricxfcpp_static PUBLIC "src/alarm" "src/metrics" "src/messenger" "src/xapp" ) endif() # -------- unit testing ------------------------------------------------------- enable_testing() add_test( NAME drive_unit_tests - COMMAND bash ../test/unit_test.sh -q + COMMAND bash ../test/unit_test.sh -q CMBUILD=${CMAKE_CURRENT_BINARY_DIR} WORKING_DIRECTORY ../test ) diff --git a/doc/src/.gitignore b/doc/src/.gitignore new file mode 100644 index 0000000..2313f4d --- /dev/null +++ b/doc/src/.gitignore @@ -0,0 +1,10 @@ +# don't stash generated things +*.md +*.rst +*.ps +*.pdf + +# don't stash intermediates +*.sp +*.ca +*.bncfile diff --git a/doc/src/rtd/rel-notes.rst b/doc/src/rtd/rel-notes.rst deleted file mode 100644 index 935e799..0000000 --- a/doc/src/rtd/rel-notes.rst +++ /dev/null @@ -1,57 +0,0 @@ -============= -RELEASE NOTES -============= -.. This work is licensed under a Creative Commons Attribution 4.0 International License. -.. SPDX-License-Identifier: CC-BY-4.0 -.. -.. CAUTION: this document is generated from source in doc/src/* -.. To make changes edit the source and recompile the document. -.. Do NOT make changes directly to .rst or .md files. - - - -C++ Framework Release Notes -=========================== -The following is a list of release highlights for the C++ -xAPP Framework. - -Cherry Release -============== - -2020 July 17; version 2.0.0 ---------------------------- -Add alarm support. - -Add xapp namespace for Message, Msg_component, Alarm and Jhah -objects. This is a BREAKING change and thus the major version -number was bumped to 2. - - -2020 June 29; version 1.2.0 ---------------------------- -Add support for json parsing - - -2020 June 26; version 1.1.0 ---------------------------- -Change the port type in constructors to indicate "const" - -Version bump to 1.1.0 to allow patches to bronze code to -continue to be done on the 1.0.* level. - - - -Bronze Release -============== - -2020 April 28; version 1.0.0 ----------------------------- -Bump version to force package build (old CI version added -incorrect install prefix). Bump to 1.0.0 for release. - - -2020 March 27; version 0.1.2 ----------------------------- -Changes identified by sonar (missing copy/move builders) -rmr_dump example programme Improvements to code for better -test coverage diff --git a/doc/src/user/Makefile b/doc/src/user/Makefile index 303cc20..a300d6c 100644 --- a/doc/src/user/Makefile +++ b/doc/src/user/Makefile @@ -19,7 +19,7 @@ docs = user_guide src = user_guide.xfm -imbed_src = cpp_frame.im example1.im example2.im example3.im jhash.im example4.im +imbed_src = cpp_frame.im example1.im example2.im example3.im jhash.im example4.im metrics.im desired_out = rst ps md include ../master.mk diff --git a/doc/src/user/example2.im b/doc/src/user/example2.im index bc1a9fc..b78c6db 100644 --- a/doc/src/user/example2.im +++ b/doc/src/user/example2.im @@ -34,6 +34,12 @@ used, but the programme is &bold(not) thread safe; to keep this example as simple as possible, the counters are not locked when incremented. +&h2(Metrics Generation) +The example also illustrates how a metrics object instance can be created +and used to send appliction metrics to the collector. +In this example the primary callback function will genereate metrics with +the receipt of each 1000th message. + &space &indent .** pull in the code from the example directory diff --git a/doc/src/user/metrics.im b/doc/src/user/metrics.im new file mode 100644 index 0000000..9d13587 --- /dev/null +++ b/doc/src/user/metrics.im @@ -0,0 +1,181 @@ +.** vim: sw=4 ts=4 et : +.if false +================================================================================== + Copyright (c) 2020 AT&T Intellectual Property. + Copyright (c) 2020 Nokia + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +================================================================================== +.fi + +.if false + This imbed file contains the description of the metrics API. + It should be included by the main user.xfm source. +.fi + + +&h1(Metrics Support) +The C++ xAPP framework provides a lightweight interface to the metrics +gateway allowing the xAPP to create and send metrics updates without +needing to understand the underlying message format. +From the xAPP's perspective, the metrics object is created with one or +more key/value measurement pairs and then is sent to the process +responsible for forwarding them to the various collection processes. +The following sections describe the Metrics object and the API associated +with it. + + +&h2(Creating The Metrics Object) +The &cw(xapp:Metrics) object can be created directly, or via the xapp framework. +When creating directly the xAPP must supply an RMR message for the object to use; +when the framework is used to create the object, the message is created as +as part of the process. + +The framework provides three constructors for the metrics instance allowing the +xAPP to supply the measurement source, the source and reporter, or to default to +using the xAPP name as both the source and reporter (see section &ital( &snr_section ) +for a more detailed description of these). +The framework constructors are illustrated in figure &frame_builders. +&space + +.ca start frame_builder.ca +&ex_start + std::unique_ptr Alloc_metrics( ); + std::unique_ptr Alloc_metrics( std::string source ); + std::unique_ptr Alloc_metrics( std::string reporter, std::string source ); +&ex_end +&export_fig( frame_builders ) +&fig_cen(The framework constructors for creating an instance of the metrics object.) +&space +.ca end +.gv remain +&ifroom( 2 : frame_builder.ca ) + +.ca start create1.ca +&ex_start + + #include + + char* port = (char *) "4560"; + + auto x = std::unique_ptr( new Xapp( port ) ); + auto reading = std::shared_ptr( x->Alloc_metric( ) ); +&ex_end +&export_fig( metric_create_1 ) +&fig_cen(Metrics instance creation using the framework. ) +&space +.ca end +.gv remain +&ifroom( 2 : create1.ca ) + +Figures &metric_create_1 illustrates how the framework constructor can be used to +create a metrics instance. +While it is unlikely that an xAPP will create a metrics instance directly, there +are three similar constructors available. +These are prototypes are shown in figure &metrics_builders and their use is illustrated +in figure &metrics_create_2. + +.ca start met_builder.ca +&ex_start + Metrics( std::shared_ptr msg ); + Metrics( std::shared_ptr msg, std::string msource ); + Metrics( std::shared_ptr msg, std::string reporter, std::string msource ); +&ex_end +&export_fig( metrics_builders ) +&fig_cen(Metrics object constructors.) +&space +.ca end +.gv remain +&ifroom( 1 : met_builder.ca ) + +.ca start create2.ca +&ex_start + #include + + char* port = (char *) "4560"; + + auto x = std::unique_ptr( new Xapp( port ) ); + auto msg = std::shared_ptr( x->Alloc_msg( 4096 ) ); + auto reading = std::shared_ptr( new Metrics( msg ) ); +&ex_end +&export_fig( metrics_create_2 ) +&fig_cen(Direct creation of a metrics instance.) +&space +.ca end +.gv remain +&ifroom( 2 : create2.ca ) + +&h2(Adding Values) +Once an instance of the metrics object is created, the xAPP may push values in +preparation to sending the measurement(s) to the collector. +The &cw(Push_data()) function is used to push each key/value pair and is illustrated +in figure &push_fig. + +.ca start push.ca +&ex_start + reading->Push_data( "normal_count", (double) norm_count ); + reading->Push_data( "high_count", (double) hi_count ); + reading->Push_data( "excessive_count", (double) ex_count ); +&ex_end +&export_fig( push_fig ) +&fig_cen(Pushing key/value pairs into a metrics instance.) +&space +.ca end +.gv remain +&ifroom( 1 : push.ca ) + +&h2(Sending A Measurement Set) +After all of the measurement key/value pairs have been added to the instance, the +&cw(Send()) function can be invoked to create the necessary RMR message and send that +to the collection application. +Following the send, the key/value pairs are cleared from the instance and the +xAPP is free to start pushing values into the instance again. +The send function has the following prototype and returns &cw(true) on success and +&cw(false) if the measurements could not be sent. + +&h2(Source and Reporter) +&export_var( snr_section : Source and Reporter ) +The alarm collector has the understanding that a measurement might be &ital(sourced) from one +piece of equipment, or software component, but reported by another. +For auditing purposes it makes sense to distinguish these, and as such the metrics object +allows the xAPP to identify the case when the source and reporter are something other than +the xAPP which is generating the metrics message(s). + +&space +The &ital(source) is the component to which the measurement applies. +This could be a network interface card counting packets, a temperature sensor, or the xAPP +itself reporting xAPP related metrics. +The &ital(reporter) is the application that is reporting the measurement(s) to the collector. + +&space +By default, both reporter and source are assumed to be the xAPP, and the name is automatically +determined using the run-time supplied programme name. +Should the xAPP need to report measurements for more than one source it has the option to +create an instance for every reporter source combination, or to set the reporter and/or +source with the generation of each measurement set. +To facilitate the ability to change the source and/or the reporter without the need to create +a new metrics instance, two &ital(setter) functions are provided. +The prototypes for these are shown in figure &setter_fig. +&space + +.ca start setter.ca +&ex_start + void Set_source( std::string new_source ); + void Set_reporter( std::string new_reporter ); +&ex_end +&export_fig( setter_fig ) +&fig_cen( Setter functions allowing the reporter and/or source to be set after construction. ) +&space +.ca end +.gv remain +&ifroom( 1 : setter.ca ) diff --git a/doc/src/user/user_guide.xfm b/doc/src/user/user_guide.xfm index e6e47f1..9ba1d4a 100644 --- a/doc/src/user/user_guide.xfm +++ b/doc/src/user/user_guide.xfm @@ -56,6 +56,7 @@ .im cpp_frame.im .im jhash.im .im alarm.im +.im metrics.im &h1(Example Programmes) The following sections contain several example programmes which are written on diff --git a/docs/rel-notes.rst b/docs/rel-notes.rst index 935e799..4841bcb 100644 --- a/docs/rel-notes.rst +++ b/docs/rel-notes.rst @@ -18,6 +18,11 @@ xAPP Framework. Cherry Release ============== +2020 July 22; version 2.1.0 +--------------------------- +Added metrics support (RIC-381). + + 2020 July 17; version 2.0.0 --------------------------- Add alarm support. diff --git a/docs/user-guide.rst b/docs/user-guide.rst index 17f741d..f5df1b8 100644 --- a/docs/user-guide.rst +++ b/docs/user-guide.rst @@ -931,6 +931,158 @@ Figure 16: Alarm Setters +METRICS SUPPORT +=============== + +The C++ xAPP framework provides a lightweight interface to +the metrics gateway allowing the xAPP to create and send +metrics updates without needing to understand the underlying +message format. From the xAPP's perspective, the metrics +object is created with one or more key/value measurement +pairs and then is sent to the process responsible for +forwarding them to the various collection processes. The +following sections describe the Metrics object and the API +associated with it. + + +Creating The Metrics Object +--------------------------- + +The ``xapp`` object can be created directly, or via the xapp +framework. When creating directly the xAPP must supply an RMR +message for the object to use; when the framework is used to +create the object, the message is created as as part of the +process. The framework provides three constructors for the +metrics instance allowing the xAPP to supply the measurement +source, the source and reporter, or to default to using the +xAPP name as both the source and reporter (see section +*Source and Reporter* for a more detailed description of +these). The framework constructors are illustrated in figure +17. + + +:: + + std::unique_ptr Alloc_metrics( ); + std::unique_ptr Alloc_metrics( std::string source ); + std::unique_ptr Alloc_metrics( std::string reporter, std::string source ); + +Figure 17: The framework constructors for creating an +instance of the metrics object. + + +:: + + + #include + + char* port = (char *) "4560"; + + auto x = std::unique_ptr( new Xapp( port ) ); + auto reading = std::shared_ptr( x->Alloc_metric( ) ); + +Figure 18: Metrics instance creation using the framework. + +Figures 18 illustrates how the framework constructor can be +used to create a metrics instance. While it is unlikely that +an xAPP will create a metrics instance directly, there are +three similar constructors available. These are prototypes +are shown in figure 19 and their use is illustrated in figure +20. + +:: + + Metrics( std::shared_ptr msg ); + Metrics( std::shared_ptr msg, std::string msource ); + Metrics( std::shared_ptr msg, std::string reporter, std::string msource ); + +Figure 19: Metrics object constructors. + + +:: + + #include + + char* port = (char *) "4560"; + + auto x = std::unique_ptr( new Xapp( port ) ); + auto msg = std::shared_ptr( x->Alloc_msg( 4096 ) ); + auto reading = std::shared_ptr( new Metrics( msg ) ); + +Figure 20: Direct creation of a metrics instance. + + + +Adding Values +------------- + +Once an instance of the metrics object is created, the xAPP +may push values in preparation to sending the measurement(s) +to the collector. The ``Push_data()`` function is used to +push each key/value pair and is illustrated in figure 21. + +:: + + reading->Push_data( "normal_count", (double) norm_count ); + reading->Push_data( "high_count", (double) hi_count ); + reading->Push_data( "excessive_count", (double) ex_count ); + +Figure 21: Pushing key/value pairs into a metrics instance. + + + +Sending A Measurement Set +------------------------- + +After all of the measurement key/value pairs have been added +to the instance, the ``Send()`` function can be invoked to +create the necessary RMR message and send that to the +collection application. Following the send, the key/value +pairs are cleared from the instance and the xAPP is free to +start pushing values into the instance again. The send +function has the following prototype and returns ``true`` on +success and ``false`` if the measurements could not be sent. + + +Source and Reporter +------------------- + +The alarm collector has the understanding that a measurement +might be *sourced* from one piece of equipment, or software +component, but reported by another. For auditing purposes it +makes sense to distinguish these, and as such the metrics +object allows the xAPP to identify the case when the source +and reporter are something other than the xAPP which is +generating the metrics message(s). + +The *source* is the component to which the measurement +applies. This could be a network interface card counting +packets, a temperature sensor, or the xAPP itself reporting +xAPP related metrics. The *reporter* is the application that +is reporting the measurement(s) to the collector. + +By default, both reporter and source are assumed to be the +xAPP, and the name is automatically determined using the +run-time supplied programme name. Should the xAPP need to +report measurements for more than one source it has the +option to create an instance for every reporter source +combination, or to set the reporter and/or source with the +generation of each measurement set. To facilitate the ability +to change the source and/or the reporter without the need to +create a new metrics instance, two *setter* functions are +provided. The prototypes for these are shown in figure 22. + + +:: + + void Set_source( std::string new_source ); + void Set_reporter( std::string new_reporter ); + +Figure 22: Setter functions allowing the reporter and/or +source to be set after construction. + + + EXAMPLE PROGRAMMES ================== @@ -1200,7 +1352,7 @@ omitted. The full code is in the framework repository. return 0; } - Figure 17: Simple callback application. + Figure 23: Simple callback application. Callback Receiver @@ -1222,12 +1374,22 @@ this example as simple as possible, the counters are not locked when incremented. +Metrics Generation +------------------ + +The example also illustrates how a metrics object instance +can be created and used to send appliction metrics to the +collector. In this example the primary callback function will +genereate metrics with the receipt of each 1000th message. + + :: #include #include "ricxfcpp/message.hpp" #include "ricxfcpp/msg_component.hpp" + #include #include "ricxfcpp/xapp.hpp" // counts; not thread safe @@ -1238,7 +1400,10 @@ locked when incremented. long cb1_lastts = 0; long cb1_lastc = 0; - // respond with 2 messages for each type 1 received + /* + Respond with 2 messages for each type 1 received + Send metrics every 1000 messages. + */ void cb1( xapp::Message& mbuf, int mtype, int subid, int len, xapp::Msg_component payload, void* data ) { long now; @@ -1249,6 +1414,16 @@ locked when incremented. mbuf.Send_response( 101, -1, 5, (unsigned char *) "OK2\\n" ); cb1_count++; + + if( cb1_count % 1000 == 0 && data != NULL ) { // send metrics every 1000 messages + auto x = (Xapp *) data; + auto msgm = std::shared_ptr( x->Alloc_msg( 4096 ) ); + + auto m = std::unique_ptr( new xapp::Metrics( msgm ) ); + m->Push_data( "tst_cb1", (double) cb1_count ); + m->Push_data( "tst_cb2", (double) cb2_count ); + m->Send(); + } } // just count messages @@ -1294,7 +1469,7 @@ locked when incremented. fprintf( stderr, " starting %d threads\\n", nthreads ); x = new Xapp( port, true ); - x->Add_msg_cb( 1, cb1, NULL ); // register callbacks + x->Add_msg_cb( 1, cb1, x ); // register callbacks x->Add_msg_cb( 2, cb2, NULL ); x->Add_msg_cb( x->DEFAULT_CALLBACK, cbd, NULL ); @@ -1302,7 +1477,7 @@ locked when incremented. // control should not return } - Figure 18: Simple callback application. + Figure 24: Simple callback application. @@ -1414,7 +1589,7 @@ to keep the example small and to the point. } } - Figure 19: Simple looping sender application. + Figure 25: Simple looping sender application. @@ -1539,6 +1714,6 @@ Alarm Example } } - Figure 20: Simple looping sender application with alarm + Figure 26: Simple looping sender application with alarm generation. diff --git a/examples/Makefile b/examples/Makefile index a56760a..2c4ded5 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -22,9 +22,10 @@ # variables are set to reference the needed files. %.o:: %.cpp %.hpp - g++ -g ${prereq%% *} -c + g++ -g ${prereq%% *} -c % :: %.cpp + #C_INCLUDE_PATH=/tmp/usr/include:/usr/local/include g++ $< -g -o $@ -lricxfcpp -lrmr_si -lpthread -lm g++ $< -g -o $@ -lricxfcpp -lrmr_si -lpthread -lm all:: xapp_t1 xapp_t2 rmr_dump diff --git a/examples/xapp_t1.cpp b/examples/xapp_t1.cpp index 5ec8536..0de4e11 100644 --- a/examples/xapp_t1.cpp +++ b/examples/xapp_t1.cpp @@ -45,6 +45,7 @@ #include "ricxfcpp/message.hpp" #include "ricxfcpp/msg_component.hpp" +#include #include "ricxfcpp/xapp.hpp" // counts; not thread safe @@ -55,7 +56,10 @@ long cbd_count = 0; long cb1_lastts = 0; long cb1_lastc = 0; -// respond with 2 messages for each type 1 received +/* + Respond with 2 messages for each type 1 received + Send metrics every 1000 messages. +*/ void cb1( xapp::Message& mbuf, int mtype, int subid, int len, xapp::Msg_component payload, void* data ) { long now; @@ -66,6 +70,16 @@ void cb1( xapp::Message& mbuf, int mtype, int subid, int len, mbuf.Send_response( 101, -1, 5, (unsigned char *) "OK2\n" ); cb1_count++; + + if( cb1_count % 1000 == 0 && data != NULL ) { // send metrics every 1000 messages + auto x = (Xapp *) data; + auto msgm = std::shared_ptr( x->Alloc_msg( 4096 ) ); + + auto m = std::unique_ptr( new xapp::Metrics( msgm ) ); + m->Push_data( "tst_cb1", (double) cb1_count ); + m->Push_data( "tst_cb2", (double) cb2_count ); + m->Send(); + } } // just count messages @@ -111,7 +125,7 @@ int main( int argc, char** argv ) { fprintf( stderr, " starting %d threads\n", nthreads ); x = new Xapp( port, true ); - x->Add_msg_cb( 1, cb1, NULL ); // register callbacks + x->Add_msg_cb( 1, cb1, x ); // register callbacks x->Add_msg_cb( 2, cb2, NULL ); x->Add_msg_cb( x->DEFAULT_CALLBACK, cbd, NULL ); diff --git a/src/json/jwrapper.c b/src/json/jwrapper.c index 3f85a59..f6ce014 100644 --- a/src/json/jwrapper.c +++ b/src/json/jwrapper.c @@ -490,7 +490,7 @@ extern void* jw_new( const char* json ) { char* djson; // dup so we can save it void* rp = NULL; // return value - if( json != NULL && (st = rmr_sym_alloc( MAX_THINGS )) != NULL ) { + if( json != NULL && (st = rmr_sym_alloc( MAX_THINGS/4 )) != NULL ) { djson = strdup( json ); // allows user to free/overlay their buffer as needed rp = parse_jobject( st, djson, "" ); // empty prefix for the root object; parse_jo will clean up and free st if( rp == NULL ) { diff --git a/src/messaging/CMakeLists.txt b/src/messaging/CMakeLists.txt index 507da84..c112a6b 100644 --- a/src/messaging/CMakeLists.txt +++ b/src/messaging/CMakeLists.txt @@ -22,7 +22,7 @@ # For clarity: this generates object, not a lib as the CM command implies. # add_library( message_objects OBJECT - callback.cpp + callback.cpp default_cb.cpp message.cpp messenger.cpp @@ -36,12 +36,12 @@ target_include_directories (message_objects PUBLIC # header files should go into .../include/xfcpp/ if( DEV_PKG ) install( FILES - callback.hpp - default_cb.hpp - message.hpp + callback.hpp + default_cb.hpp + message.hpp messenger.hpp msg_component.hpp DESTINATION ${install_inc} - ) + ) endif() diff --git a/src/messaging/message.cpp b/src/messaging/message.cpp index 41f5a78..a8b8089 100644 --- a/src/messaging/message.cpp +++ b/src/messaging/message.cpp @@ -152,14 +152,14 @@ xapp::Message::~Message() { */ //char* Message::Copy_payload( ){ std::unique_ptr xapp::Message::Copy_payload( ){ - unsigned char* new_payload = NULL; if( mbuf != NULL ) { - new_payload = (unsigned char *) malloc( sizeof( unsigned char ) * mbuf->len ); + unsigned char* new_payload = new unsigned char[mbuf->len]; memcpy( new_payload, mbuf->payload, mbuf->len ); + return std::unique_ptr( new_payload ); } - return std::unique_ptr( new_payload ); + return NULL; } /* @@ -198,10 +198,7 @@ int xapp::Message::Get_mtype(){ Makes a copy of the source field and returns a smart pointer to it. */ std::unique_ptr xapp::Message::Get_src(){ - unsigned char* m = NULL; - - m = (unsigned char *) malloc( sizeof( unsigned char ) * RMR_MAX_SRC ); - memset( m, 0, sizeof( unsigned char ) * RMR_MAX_SRC ); + unsigned char* m = new unsigned char[RMR_MAX_SRC]; if( m != NULL ) { rmr_get_src( mbuf, m ); diff --git a/src/messaging/messenger.cpp b/src/messaging/messenger.cpp index 67d5e2d..ede76d3 100644 --- a/src/messaging/messenger.cpp +++ b/src/messaging/messenger.cpp @@ -44,6 +44,7 @@ #include "message.hpp" #include "messenger.hpp" #include "alarm.hpp" +#include "metrics.hpp" namespace xapp { @@ -200,6 +201,28 @@ std::unique_ptr xapp::Messenger::Alloc_alarm( ) { } +// ------------------ metrics support -------------------------------------------------- +std::unique_ptr xapp::Messenger::Alloc_metrics( ) { + std::shared_ptr m; + + m = Alloc_msg( 4096 ); + return std::unique_ptr( new xapp::Metrics( m ) ); +} + +std::unique_ptr xapp::Messenger::Alloc_metrics( std::string source ) { + std::shared_ptr m; + + m = Alloc_msg( 4096 ); + return std::unique_ptr( new xapp::Metrics( m, source ) ); +} + +std::unique_ptr xapp::Messenger::Alloc_metrics( std::string reporter, std::string source ) { + std::shared_ptr m; + + m = Alloc_msg( 4096 ); + return std::unique_ptr( new xapp::Metrics( m, reporter, source ) ); +} + // ------------------- listening support ----------------------------------------------- /* diff --git a/src/messaging/messenger.hpp b/src/messaging/messenger.hpp index 173426c..82d8a49 100644 --- a/src/messaging/messenger.hpp +++ b/src/messaging/messenger.hpp @@ -41,6 +41,7 @@ #include "message.hpp" #include "alarm.hpp" +#include "metrics.hpp" #ifndef RMR_FALSE #define RMR_FALSE 0 @@ -82,6 +83,10 @@ class Messenger { std::unique_ptr Alloc_alarm( std::string meid ); std::unique_ptr Alloc_alarm( int prob_id, std::string meid ); + std::unique_ptr Alloc_metrics( ); // metrics allocation + std::unique_ptr Alloc_metrics( std::string source ); + std::unique_ptr Alloc_metrics( std::string reporter, std::string source ); + void Listen( ); // lisen driver std::unique_ptr Receive( int timeout ); // receive 1 message void Stop( ); // force to stop diff --git a/src/metrics/CMakeLists.txt b/src/metrics/CMakeLists.txt new file mode 100644 index 0000000..7930533 --- /dev/null +++ b/src/metrics/CMakeLists.txt @@ -0,0 +1,40 @@ +# vim: sw=4 ts=4 noet: +# +#================================================================================== +# Copyright (c) 2020 Nokia +# Copyright (c) 2020 AT&T Intellectual Property. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#================================================================================== +# + + +# For clarity: this generates object, not a lib as the CM command implies. +# +add_library( metrics_objects OBJECT + metrics.cpp +) + +target_include_directories (metrics_objects PUBLIC + $ + $ + PRIVATE src) + +# header files should go into .../include/xfcpp/ +if( DEV_PKG ) + install( FILES + metrics.hpp + DESTINATION ${install_inc} + ) +endif() + diff --git a/src/metrics/metrics.cpp b/src/metrics/metrics.cpp new file mode 100644 index 0000000..1c27355 --- /dev/null +++ b/src/metrics/metrics.cpp @@ -0,0 +1,284 @@ +// vi: ts=4 sw=4 noet: +/* +================================================================================== + Copyright (c) 2020 Nokia + Copyright (c) 2020 AT&T Intellectual Property. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +================================================================================== +*/ + +/* + Mnemonic: metrics.cpp + Abstract: Impementation of the metrics functions. (see metrics.hpp) + + + void Set_source( std::string new_source ); + void Set_reporter( std::string new_reporter ); + void Push_data( std::string key, double value ); + + Date: 20 July 2020 + Author: E. Scott Daniels +*/ + +#include +#include +#include + +#include +#ifndef RIC_METRICS + #define RIC_METRICS 120 +#endif + +#include + +#include "msg_component.hpp" +#include "message.hpp" +#include "metrics.hpp" + +extern char* __progname; // runtime lib supplied since we don't get argv[0] + + + +namespace xapp { + + + +// ------ private ---------------------------------------------- + + +/* + Return the current time in milliseconds past the UNIX epoch. +*/ +static long long now( void ) { + struct timespec ts; + long long now = 0; + + clock_gettime( CLOCK_REALTIME, &ts ); + now = (ts.tv_sec * 1000000) + (ts.tv_nsec/1000000); // convert nano to milli and bang onto seconds + + return now; +} + + +/* + Build the payload that we'll send. + Accepts a pointer to the payload message component in the RMR message that we are + to fill in, along with the maxmimum length of the payload. Returns the length of + the payload that was acutally used. + + Currently, the munchkin expects: + { + "reporter": "", + "generator" "", + "timestamp": double, + "data": [ + { + "id": "", + "type": "", + "value": double + },... + ] + } +*/ +int xapp::Metrics::build_payload( xapp::Msg_component payload, int max_len ) { + int used; // actual size of payload created + std::string generator; + + + if( data.compare( "" ) == 0 ) { // xAPP never pushed any data + return 0; // don't build, just bail + } + + generator = source.compare( "" ) == 0 ? reporter : source; + + used = snprintf( (char *) payload.get(), max_len, + "{ " + "\"reporter\": \"%s\", " + "\"generator\": \"%s\", " + "\"timestamp\": %lld, " + "\"data\": [ %s ] " + " }", + reporter.c_str(), + generator.c_str(), + now(), + data.c_str() + ); + + return used; +} + + +// --------------- builders/operators ------------------------------------- + +/* + Create a new metrics "message" with a provided (empty) RMR message. +*/ +xapp::Metrics::Metrics( std::shared_ptr msg ) : + msg( msg ), + reporter( __progname ), + source( "" ), + data( "" ) +{ /* empty body */ } + +/* + Build a metrics for a value source other than the calling xAPP +*/ +xapp::Metrics::Metrics( std::shared_ptr msg, std::string msource ) : + msg( msg ), + reporter( __progname ), + source( msource ), + data( "" ) +{ /* empty body */ } + +/* + Build a metrics object that allows the xAPP to set it's reporter name + rather than assuming the programme name and the source. + + The xAPP can pass "" as the msource if the intent is just to supply + an alternate programme name which is also the source. +*/ +xapp::Metrics::Metrics( std::shared_ptr msg, std::string reporter, std::string msource ) : + msg( msg ), + reporter( reporter ), + source( msource ), + data( "" ) +{ /* empty body */ } + +// ------------------ copy and move support --------------------------------- + +/* + Copy builder. Given a source object instance (soi), create a copy. + Creating a copy should be avoided as it can be SLOW! +*/ +xapp::Metrics::Metrics( const Metrics& soi ) { + msg = soi.msg; + data = soi.data; + source = soi.source; + reporter = soi.reporter; +} + +/* + Assignment operator. Simiolar to the copycat, but "this" object exists and + may have data that needs to be released prior to making a copy of the soi. +*/ +Metrics& xapp::Metrics::operator=( const Metrics& soi ) { + if( this != &soi ) { // cannot do self assignment + // anything that must be freed from 'this' must be done here + + msg = soi.msg; + data = soi.data; + source = soi.source; + reporter = soi.reporter; + } + + return *this; +} + +/* + Move builder. Given a source object instance (soi), move the information from + the soi ensuring that the destriction of the soi doesn't trash things from + under us. +*/ +xapp::Metrics::Metrics( Metrics&& soi ) { + msg = soi.msg; // capture pointers and copy data before setting soruce things to nil + data = soi.data; + source = soi.source; + reporter = soi.reporter; + + soi.msg = NULL; // prevent closing of RMR stuff on soi destroy +} + +/* + Move Assignment operator. Move the message data to the existing object + ensure the object reference is cleaned up, and ensuring that the source + object references are removed. +*/ +Metrics& xapp::Metrics::operator=( Metrics&& soi ) { + if( this != &soi ) { // cannot do self assignment + // anything that needs to be freed/delted from 'this', must be done here + + msg = soi.msg; // move pointers and values + data = soi.data; + source = soi.source; + reporter = soi.reporter; + + soi.msg = NULL; // prevent bad things when source is destroyed + } + + return *this; +} + +/* + Destroyer. +*/ +xapp::Metrics::~Metrics() { + msg = NULL; +} + + +// ---- setters ------------------------------------------------- + +void xapp::Metrics::Set_source( std::string new_source ) { + source = new_source; +} + +void xapp::Metrics::Set_reporter( std::string new_reporter ) { + reporter = new_reporter; +} + +/* + Pushes the key/value pair onto the current data list. + This could be more efficent, but for now it is simple. +*/ +void xapp::Metrics::Push_data( std::string key, double value ) { + char wbuf[1024]; + char* sep = (char *) " "; + + if( data.compare( "" ) != 0 ) { // first on the list, no need for preceeding comma + sep = (char *) ", "; + } + + snprintf( wbuf, sizeof( wbuf ), "%s{ \"id\": \"%s\", \"value\": %.5f }", sep, key.c_str(), value ); + + data += std::string( wbuf ); +} + +// ------------------- getters ------------------------------------ + + +// ------- message sending --------------------------------------- + +/* + Send the message by building the payload and passing to RMR. + Returns the state of the send (true == good). If the xapp did not + push any data, no send is actually invoked, and true is reported. +*/ +bool xapp::Metrics::Send( ) { + int used = 0; // actual num bytes used in the payload + bool state = true; + + used = build_payload( msg->Get_payload(), msg->Get_available_size() - 1 ); + if( used > 0 ) { + //fprintf( stderr, ">>>> sending payload: %s\n", (char *) msg->Get_payload().get() ); + state = msg->Send_msg( RIC_METRICS, RMR_VOID_SUBID, used+1, NULL ); + } + + data = ""; // clear data after the send + + return state; +} + + + +} // namespace diff --git a/src/metrics/metrics.hpp b/src/metrics/metrics.hpp new file mode 100644 index 0000000..77f258c --- /dev/null +++ b/src/metrics/metrics.hpp @@ -0,0 +1,102 @@ +// vi: ts=4 sw=4 noet: +/* +================================================================================== + Copyright (c) 2020 Nokia + Copyright (c) 2020 AT&T Intellectual Property. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +================================================================================== +*/ + +/* + Mnemonic: metrics.hpp + Abstract: Headers for the metrics class. + This class provides a simple interface for an xAPP to compose and + send metrics. The class allows the xAPP to name the metric and to + provide one or more key/value pairs which are sent to the metrics + gateway (munchkin) when Send() is invoked. The class hides the + true message structure that the munchkin desires from the xAPP. + + The munchkin endpoint is assumed to be a routable target and thus + normal RMR messages (with appropriate type) are sent. + + Date: 20 July 2020 + Author: E. Scott Daniels +*/ + +#ifndef _XAPP_METRICS_HPP +#define _XAPP_METRICS_HPP + + +#include +#include + +#include "msg_component.hpp" + +namespace xapp { + +// ------------------------------------------------------------------------ + +/* +This class is loosely tied to the json expected by the munchkin. See the munchkin +documentation for explicit information, but this will help to understand a bit: + { + "reporter": "", + "generator" "", + "timestamp": double, + "data": [ + { + "id": "", + "type": "", + "value": double + },... + ] + } +*/ + +class Metrics { + private: + std::shared_ptr msg; // message to send + + // data for the payload + std::string reporter; // this application as it's reporting the metrics + std::string source; // source of the measurement if not the reporter + std::string data; // key/value/type tuples "pushed" waiting for send + + + int build_payload( xapp::Msg_component payload, int payload_len ); + + public: + + Metrics( std::shared_ptr msg ); // builders + Metrics( std::shared_ptr msg, std::string msource ); // allow xapp to supply metric source + Metrics( std::shared_ptr msg, std::string reporter, std::string msource ); // allow xapp to supply all + + Metrics( const Metrics& soi ); // copy to newly created instance + Metrics& operator=( const Metrics& soi ); // copy operator + Metrics( Metrics&& soi ); // mover + Metrics& operator=( Metrics&& soi ); // move operator + ~Metrics(); // destroyer + + + void Set_source( std::string new_source ); + void Set_reporter( std::string new_reporter ); + void Push_data( std::string key, double value ); + + + bool Send( ); +}; + +} // namespace + +#endif diff --git a/test/Makefile b/test/Makefile index 691f365..b7c843b 100644 --- a/test/Makefile +++ b/test/Makefile @@ -3,15 +3,19 @@ coverage_opts = -ftest-coverage -fprofile-arcs binaries = unit_test -tests:: unit_test jhash_test +tests:: unit_test jhash_test metrics_test + +include_list = -I ../src/metrics -I ../src/alarm -I ../src/messaging # RMR emulation rmr_em.o:: rmr_em.c cc -g rmr_em.c -c +# include the emulated rmr stuff, but we still need the rmr lib for symtab things which we cannot hope to +# emulate (and don't need to) +# unit_test:: unit_test.cpp rmr_em.o - # do NOT link the xapp lib; we include all modules in the test programme - g++ -g $(coverage_opts) -I ../src/alarm -I ../src/messaging unit_test.cpp -o unit_test rmr_em.o -lpthread + g++ -g $(coverage_opts) $(include_list) unit_test.cpp -o unit_test -L../.build -lricxfcpp rmr_em.o -lrmr_si -lpthread # build a special jwrapper object with coverage settings jwrapper_test.o:: ../src/json/jwrapper.c ../src/json/jwrapper.h @@ -21,6 +25,10 @@ jhash_test:: jwrapper_test.o jhash_test.cpp # do NOT link the xapp lib; we include all modules in the test programme g++ -g $(coverage_opts) -I ../src/json -I ../ext/jsmn jhash_test.cpp -o jhash_test jwrapper_test.o -lrmr_si -lpthread +metrics_test:: metrics_test.cpp rmr_em.o + # do NOT link the xapp lib; we include all modules in the test programme + g++ -g $(coverage_opts) $(include_list) metrics_test.cpp -o metrics_test -L../.build -lricxfcpp rmr_em.o -l rmr_si -lpthread + # prune gcov files generated by system include files clean:: rm -f *.h.gcov *.c.gcov diff --git a/test/metrics_test.cpp b/test/metrics_test.cpp new file mode 100644 index 0000000..3b939e3 --- /dev/null +++ b/test/metrics_test.cpp @@ -0,0 +1,109 @@ +// vim: ts=4 sw=4 noet : +/* +================================================================================== + Copyright (c) 2020 Nokia + Copyright (c) 2020 AT&T Intellectual Property. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +================================================================================== +*/ + +/* + Mnemonic: metric_test.cpp + Abstract: This is the unit test driver for the metrics class. + + Date: 20 July 2020 + Author: E. Scott Daniels +*/ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../src/messaging/callback.hpp" +#include "../src/messaging/default_cb.hpp" +#include "../src/messaging/message.hpp" +#include "../src/messaging/messenger.hpp" +#include "../src/messaging/msg_component.hpp" +#include "../src/alarm/alarm.hpp" +#include "../src/xapp/xapp.hpp" + +#include "../src/metrics/metrics.hpp" // overtly pull the code under test to get coverage opts +#include "../src/metrics/metrics.cpp" + +#include "ut_support.cpp" + +int main( int argc, char** argv ) { + int errors = 0; + std::shared_ptr x; + std::shared_ptr m; + + set_test_name( "jhash_test" ); + + x = std::shared_ptr( new Xapp( "4560", true ) ); + if( x == NULL ) { + fprintf( stderr, " unable to allocate xapp object\n" ); + announce_results( 1 ); + return 1; + } + + m = x->Alloc_metrics( ); + m->Push_data( "barney_balance", 216.49 ); + m->Push_data( "fred_balance", 760.88 ); + m->Send( ); + + // ensure data is cleared after first send + m->Push_data( "barney_balance", 216.49 ); + m->Push_data( "fred_balance", 760.88 ); + m->Push_data( "wilma_balance", 1986.0430 ); + m->Send(); + + m->Send(); // shouldn't really send + + + // drive alternate builders + m = x->Alloc_metrics( "different-source" ); + m->Push_data( "wilma_balance", 1986.0430 ); + m->Send(); + + m = x->Alloc_metrics( "different-app", "different-source" ); + m->Push_data( "wilma_balance", 1986.0430 ); + m->Push_data( "pebbles_balance", 1982.0614 ); + m->Send(); + + + m->Set_reporter( "set-reporter" ); + m->Set_source( "set-source" ); + + + // drive move/copy adjunct functions + + xapp::Metrics b = *m.get(); // force the move/copy operator functions to trigger + xapp::Metrics c( NULL ); // a useless metric without a message + xapp::Metrics f( NULL, "source" ); // a useless metric to drive direct construction + c = *m.get(); // drive copy = operator + + b = std::move( c ); // move = operator + xapp::Metrics d = std::move( b ); // move constructor + + + // ---------------------------- end housekeeping --------------------------- + announce_results( errors ); + return !!errors; +} diff --git a/test/unit_test.cpp b/test/unit_test.cpp index 56f40b8..3e3c5bc 100644 --- a/test/unit_test.cpp +++ b/test/unit_test.cpp @@ -40,6 +40,7 @@ #include "../src/messaging/messenger.hpp" #include "../src/messaging/msg_component.hpp" #include "../src/alarm/alarm.hpp" +#include "../src/metrics/metrics.hpp" #include "../src/xapp/xapp.hpp" #include "../src/messaging/callback.cpp" @@ -107,6 +108,19 @@ void killer( std::shared_ptr x ) { x->Halt(); } +/* + Drive the constructors so that we actually see the coverage + in the .gc* files. The metrics tests will actually verify that + the various underlying functions work. +*/ +static int metrics( std::shared_ptr x ) { + std::shared_ptr m; + + m = x->Alloc_metrics( ); // basic construction + m = x->Alloc_metrics( "different-source" ); // drive alternate builders + m = x->Alloc_metrics( "different-app", "different-source" ); +} + int main( int argc, char** argv ) { std::thread* tinfo; // we'll start a thread that will shut things down after a few seconds std::unique_ptr msg; @@ -321,12 +335,6 @@ int main( int argc, char** argv ) { a = x->Alloc_alarm( 13, "meid-abc" ); errors += fail_if( a == NULL, "unable to allcoate an alarm with meid and problem id" ); - - //#####: 210:xapp::Alarm::Alarm( const Alarm& soi ) { - //#####: 227:Alarm& xapp::Alarm::operator=( const Alarm& soi ) { - //#####: 249:xapp::Alarm::Alarm( Alarm&& soi ) { - //#####: 269:Alarm& xapp::Alarm::operator=( Alarm&& soi ) { - a->Set_meid( "changed_meid" ); for( i = 0; i < 6; i++ ) { a->Set_severity( i ); // drive all switch possibilities @@ -357,6 +365,12 @@ int main( int argc, char** argv ) { b = std::move( c ); // move = operator xapp::Alarm d = std::move( b ); // move constructor + + // ------ minimal metrics to prove coverage ------------------------------------------------------------ + metrics( x ); + + // ------------------------------------------------------------------------------------------------------ + announce_results( errors ); return errors > 0; } diff --git a/test/unit_test.sh b/test/unit_test.sh index 2f30686..3e06a53 100755 --- a/test/unit_test.sh +++ b/test/unit_test.sh @@ -60,6 +60,16 @@ function abort_if_error { spew="cat" # default to dumping all make output on failure (-q turns it to ~40 lines) +if [[ -d ../.build ]] +then + build_dir="../.build" +else + if [[ -d ../build ]] + then + build_dir="../build" + fi +fi + while [[ $1 == "-"* ]] do case $1 in @@ -70,8 +80,24 @@ do shift done -export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH -export LIBRARY_PATH=/usr/local/lib:$LIBRARY_PATH +while [[ $1 == *"="* ]] +do + case ${1%%=*} in + CMBUILD) + export build_dir=${1##*=} + ;; + esac + + shift +done + +echo "## INFO ##" +echo "build dir=$build_dir" +find $build_dir -name "libricxfcpp.*" +echo "## INFO ##" + +export LD_LIBRARY_PATH=$build_dir:/usr/local/lib:$LD_LIBRARY_PATH +export LIBRARY_PATH=$build_dir:/usr/local/lib:$LIBRARY_PATH make nuke >/dev/null make tests >/tmp/PID$$.log 2>&1 @@ -80,7 +106,7 @@ echo "tests successfully built" >&2 spew="cat" -for x in unit_test jhash_test +for x in unit_test jhash_test metrics_test do ./$x >/tmp/PID$$.log 2>&1 abort_if_error $? "test failed: $x" -- 2.16.6