diff --git a/src/daqd/CMakeLists.txt b/src/daqd/CMakeLists.txt index e56dc1f51cf3e14939dfe38db9af777246c0bc67..3cc06ce4bc4e87b242aab543735b9d24a7fe3515 100644 --- a/src/daqd/CMakeLists.txt +++ b/src/daqd/CMakeLists.txt @@ -157,10 +157,13 @@ add_executable(test_daqd_unit_tests tests/test_main.cc tests/test_daqd_cmask_t.cc tests/test_shmem_recv.cc tests/test_daqd_thread.cc + tests/test_daqd_channel_hashes.cc daqd_thread.cc ) target_include_directories(test_daqd_unit_tests PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ../include) -target_link_libraries(test_daqd_unit_tests PUBLIC ${CMAKE_THREAD_LIBS_INIT}) +target_link_libraries(test_daqd_unit_tests PUBLIC + ldastools::framecpp + ${CMAKE_THREAD_LIBS_INIT}) add_test(NAME test_daqd_unit_tests COMMAND test_daqd_unit_tests WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") diff --git a/src/daqd/channel.hh b/src/daqd/channel.hh index e9488d4e30e6c7b8f4a003172d3c8708ab9e70c7..5864fa2a5752f815d7505fc59e500556b5cd6360 100644 --- a/src/daqd/channel.hh +++ b/src/daqd/channel.hh @@ -3,6 +3,7 @@ #include "channel.h" #include <string.h> +#include <cstdlib> typedef struct { @@ -129,6 +130,51 @@ public: } }; +template < typename Hash > +class FrameCpp_hash_adapter +{ +public: + explicit FrameCpp_hash_adapter( Hash& hash ) : hash_{ hash } + { + } + FrameCpp_hash_adapter( const FrameCpp_hash_adapter& ) = default; + + void + add( const void* data, std::size_t length ) + { + // why is this not const void* ? + hash_.Update( const_cast< void* >( data ), length ); + } + +private: + Hash& hash_; +}; + +template < typename HashObject > +void +hash_channel_v0_broken( HashObject& hash, const channel_t& channel ) +{ + // this is broken, but is kept (for now) for compatibility + // the name and units sum the first sizeof(size_t) bytes of their values + hash.add( &( channel.chNum ), sizeof( channel.chNum ) ); + hash.add( &( channel.seq_num ), sizeof( channel.seq_num ) ); + size_t name_len = strnlen( channel.name, channel_t::channel_name_max_len ); + hash.add( channel.name, sizeof( name_len ) ); + hash.add( &( channel.sample_rate ), sizeof( channel.sample_rate ) ); + hash.add( &( channel.active ), sizeof( channel.active ) ); + hash.add( &( channel.trend ), sizeof( channel.trend ) ); + hash.add( &( channel.group_num ), sizeof( channel.group_num ) ); + hash.add( &( channel.bps ), sizeof( channel.bps ) ); + hash.add( &( channel.dcu_id ), sizeof( channel.dcu_id ) ); + hash.add( &( channel.data_type ), sizeof( channel.data_type ) ); + hash.add( &( channel.signal_gain ), sizeof( channel.signal_gain ) ); + hash.add( &( channel.signal_slope ), sizeof( channel.signal_slope ) ); + hash.add( &( channel.signal_offset ), sizeof( channel.signal_offset ) ); + size_t unit_len = + strnlen( channel.signal_units, channel_t::engr_unit_max_len ); + hash.add( channel.signal_units, sizeof( unit_len ) ); +} + template < typename HashObject > void hash_channel( HashObject& hash, const channel_t& channel ) diff --git a/src/daqd/daqd.cc b/src/daqd/daqd.cc index 34f430f33d49cc077ca1f89e609d58e76ed7aecc..395c4c13ccdf64549ccaf5c5a525a70657f49c4d 100644 --- a/src/daqd/daqd.cc +++ b/src/daqd/daqd.cc @@ -501,32 +501,27 @@ daqd_c::update_configuration_number( const char* source_address ) if ( num_channels == 0 ) return; - FrameCPP::Common::MD5Sum check_sum; + FrameCPP::Common::MD5Sum check_sum; + FrameCpp_hash_adapter< decltype( check_sum ) > hash_wrapper( check_sum ); channel_t* cur = channels; channel_t* end = channels + num_channels; - for ( ; cur < end; ++cur ) - { - - check_sum.Update( &( cur->chNum ), sizeof( cur->chNum ) ); - check_sum.Update( &( cur->seq_num ), sizeof( cur->seq_num ) ); - size_t name_len = strnlen( cur->name, channel_t::channel_name_max_len ); - check_sum.Update( cur->name, sizeof( name_len ) ); - check_sum.Update( &( cur->sample_rate ), sizeof( cur->sample_rate ) ); - check_sum.Update( &( cur->active ), sizeof( cur->active ) ); - check_sum.Update( &( cur->trend ), sizeof( cur->trend ) ); - check_sum.Update( &( cur->group_num ), sizeof( cur->group_num ) ); - check_sum.Update( &( cur->bps ), sizeof( cur->bps ) ); - check_sum.Update( &( cur->dcu_id ), sizeof( cur->dcu_id ) ); - check_sum.Update( &( cur->data_type ), sizeof( cur->data_type ) ); - check_sum.Update( &( cur->signal_gain ), sizeof( cur->signal_gain ) ); - check_sum.Update( &( cur->signal_slope ), sizeof( cur->signal_slope ) ); - check_sum.Update( &( cur->signal_offset ), - sizeof( cur->signal_offset ) ); - size_t unit_len = - strnlen( cur->signal_units, channel_t::engr_unit_max_len ); - check_sum.Update( cur->signal_units, sizeof( unit_len ) ); + + std::function< void( const channel_t& ) > hash_cb{ + [&hash_wrapper]( const channel_t& channel ) { + hash_channel( hash_wrapper, channel ); + } + }; + auto use_broken = parameters( ).get< int >( + "USE_BROKEN_CONFIGURATION_NUMBER_HASH", 0 ) == 1; + if ( use_broken ) + { + hash_cb = [&hash_wrapper]( const channel_t& channel ) { + hash_channel_v0_broken( hash_wrapper, channel ); + }; } + std::for_each( cur, end, hash_cb ); + check_sum.Finalize( ); std::ostringstream ss; diff --git a/src/daqd/tests/test_daqd_channel_hashes.cc b/src/daqd/tests/test_daqd_channel_hashes.cc new file mode 100644 index 0000000000000000000000000000000000000000..11c6bedf71874a333a724eaa21151ede094271c7 --- /dev/null +++ b/src/daqd/tests/test_daqd_channel_hashes.cc @@ -0,0 +1,166 @@ +// +// Created by jonathan.hanks on 10/2/20. +// + +#include "channel.hh" +#include <cstring> +#include <sstream> +#include <string> + +#include "framecpp/Common/MD5SumFilter.hh" +#include "catch.hpp" + +namespace +{ + channel_t + channel1( ) + { + channel_t ch{}; + + ch.chNum = 1000; + ch.seq_num = 1; + ch.id = nullptr; + std::strcpy( ch.name, "CHANNEL1_THE_REAL_DEAL" ); + ch.sample_rate = 16; + ch.active = 1; + ch.group_num = 1; + ch.bps = 16 * 4; + ch.offset = 0; + ch.bytes = 4; + ch.req_rate = 16; + ch.dcu_id = 1; + ch.ifoid = 0; + ch.tp_node = 1; + ch.rm_offset = 0; + ch.data_type = daq_data_t::_32bit_float; + ch.signal_gain = 1.0; + ch.signal_slope = 1.0; + ch.signal_offset = 0.0; + std::strcpy( ch.signal_units, "SOMETHING" ); + return ch; + } + + channel_t + channel2( ) + { + channel_t ch{}; + + ch.chNum = 1000; + ch.seq_num = 1; + ch.id = nullptr; + std::strcpy( ch.name, "CHANNEL1_IMPOSTER" ); + ch.sample_rate = 16; + ch.active = 1; + ch.group_num = 1; + ch.bps = 16 * 4; + ch.offset = 0; + ch.bytes = 4; + ch.req_rate = 16; + ch.dcu_id = 1; + ch.ifoid = 0; + ch.tp_node = 1; + ch.rm_offset = 0; + ch.data_type = daq_data_t::_32bit_float; + ch.signal_gain = 1.0; + ch.signal_slope = 1.0; + ch.signal_offset = 0.0; + std::strcpy( ch.signal_units, "SOMETHING_ELSE" ); + return ch; + } + + channel_t + channel3( ) + { + channel_t ch{}; + + ch.chNum = 1001; + ch.seq_num = 1; + ch.id = nullptr; + std::strcpy( ch.name, "NOT_LIKE_CHANNEL1_IMPOSTER" ); + ch.sample_rate = 16; + ch.active = 1; + ch.group_num = 1; + ch.bps = 16 * 4; + ch.offset = 0; + ch.bytes = 4; + ch.req_rate = 16; + ch.dcu_id = 1; + ch.ifoid = 0; + ch.tp_node = 1; + ch.rm_offset = 0; + ch.data_type = daq_data_t::_32bit_float; + ch.signal_gain = 1.0; + ch.signal_slope = 1.0; + ch.signal_offset = 0.0; + std::strcpy( ch.signal_units, "SOMETHING_ELSE_ENTIRELY" ); + return ch; + } + + std::string + test_hash_this_channel_v0( const channel_t& ch ) + { + FrameCPP::Common::MD5Sum check_sum; + FrameCpp_hash_adapter< decltype( check_sum ) > hash_wrapper( + check_sum ); + + hash_channel_v0_broken( hash_wrapper, ch ); + + check_sum.Finalize( ); + std::ostringstream os; + os << check_sum; + return os.str( ); + } + + std::string + test_hash_this_channel_v1( const channel_t& ch ) + { + FrameCPP::Common::MD5Sum check_sum; + FrameCpp_hash_adapter< decltype( check_sum ) > hash_wrapper( + check_sum ); + + hash_channel( hash_wrapper, ch ); + + check_sum.Finalize( ); + std::ostringstream os; + os << check_sum; + return os.str( ); + } +} // namespace + +TEST_CASE( + "hash_channel_v0_broken has easy collisions, reproduce buggy behavior" ) +{ + + auto hash1 = test_hash_this_channel_v0( channel1( ) ); + auto hash2 = test_hash_this_channel_v0( channel2( ) ); + + REQUIRE( hash1 == hash2 ); + + REQUIRE( hash1 == "1353dc82bb61dddaf97ffdf2400206d0" ); +} + +TEST_CASE( "hash_channel_v0_broken doesn't collide on everything" ) +{ + auto hash1 = test_hash_this_channel_v0( channel1( ) ); + auto hash3 = test_hash_this_channel_v0( channel3( ) ); + + REQUIRE( hash1 != hash3 ); + + REQUIRE( hash3 == "c45a8cc2d02de142d87c89d260e7f6d5" ); +} + +TEST_CASE( "hash_channel does not have the same collision issue as " + "hash_channel_v0_broken" ) +{ + auto hash1 = test_hash_this_channel_v1( channel1( ) ); + auto hash2 = test_hash_this_channel_v1( channel2( ) ); + auto hash3 = test_hash_this_channel_v1( channel3( ) ); + + REQUIRE( hash1 != hash2 ); + REQUIRE( hash1 != hash3 ); + REQUIRE( hash2 != hash3 ); + + REQUIRE( hash1 == "5bd1825ca5aaf1f1354ac3d39fe9a5e3" ); + REQUIRE( hash2 == "7aa4d0506697a4d7ad7d5268a8a38389" ); + REQUIRE( hash3 == "6b194dd0ec444ab919072d94ed95c130" ); +}