diff --git a/common/output-model.cpp b/common/output-model.cpp index 2c471a7700..8554f82cd7 100644 --- a/common/output-model.cpp +++ b/common/output-model.cpp @@ -53,9 +53,11 @@ void output_model::thread_loop() rsutils::string::from() << "Invalid Hardware Logger XML at '" << hwlogger_xml << "': " << ex.what() << "\nEither configure valid XML or remove it" ); + continue; // Don't try to get log entries for this device } } + fwlogger.start_collecting(); auto message = fwlogger.create_message(); while (fwlogger.get_firmware_log(message)) { @@ -68,11 +70,15 @@ void output_model::thread_loop() { parsed_ok = true; + std::string module_print = "[" + parsed.module_name() + "]"; + if( module_print == "[Unknown]" ) + module_print.clear(); // Some devices don't support FW log modules + add_log( message.get_severity(), parsed.file_name(), parsed.line(), - rsutils::string::from() - << "FW-LOG [" << parsed.thread_name() << "] " << parsed.message() ); + rsutils::string::from() << "FW-LOG [" << parsed.thread_name() << "]" + << module_print << parsed.message() ); } } @@ -83,8 +89,11 @@ void output_model::thread_loop() ss << std::setfill('0') << std::setw(2) << std::hex << static_cast(elem) << " "; add_log(message.get_severity(), __FILE__, 0, ss.str()); } - if (!enable_firmware_logs && fwlogger.get_number_of_fw_logs() == 0) + if( ! enable_firmware_logs && fwlogger.get_number_of_fw_logs() == 0 ) + { + fwlogger.stop_collecting(); break; + } } } } diff --git a/common/viewer.cpp b/common/viewer.cpp index 51ed761295..b6571c3514 100644 --- a/common/viewer.cpp +++ b/common/viewer.cpp @@ -2709,7 +2709,7 @@ namespace rs2 } { - ImGui::Text("HWLoggerEvents.xml Path:"); + ImGui::Text("FW logs XML file:"); ImGui::SameLine(); static char logpath[256]; memset(logpath, 0, 256); @@ -2721,6 +2721,19 @@ namespace rs2 path_str = logpath; temp_cfg.set(configurations::viewer::hwlogger_xml, path_str); } + + ImGui::SameLine(); + if( ImGui::Button( "FW logs XML" ) ) + { + auto ret = file_dialog_open(open_file, "XML file\0*.xml\0", NULL, NULL); + if( ret ) + { + memset( logpath, 0, 256 ); + memcpy( logpath, ret, std::min( 255, static_cast< int >( strlen( ret ) ) ) ); + path_str = logpath; + temp_cfg.set( configurations::viewer::hwlogger_xml, path_str ); + } + } } ImGui::Separator(); diff --git a/include/librealsense2/h/rs_internal.h b/include/librealsense2/h/rs_internal.h index fdf5d4b583..d26a3a2a60 100644 --- a/include/librealsense2/h/rs_internal.h +++ b/include/librealsense2/h/rs_internal.h @@ -359,6 +359,20 @@ void rs2_software_sensor_add_option(rs2_sensor* sensor, rs2_option option, float void rs2_software_sensor_detach(rs2_sensor* sensor, rs2_error** error); +/** + * \brief Starts collecting FW log messages in the device. + * \param[in] dev Device that will start collecting log messages. + * \param[out] error If non-null, receives any error that occurs during this call, otherwise, errors are + */ +void rs2_start_collecting_fw_logs( rs2_device * dev, rs2_error ** error ); + +/** + * \brief Stops collecting FW log messages in the device. + * \param[in] dev Device that will stop collecting log messages. + * \param[out] error If non-null, receives any error that occurs during this call, otherwise, errors are + */ +void rs2_stop_collecting_fw_logs( rs2_device * dev, rs2_error ** error ); + /** * \brief Creates RealSense firmware log message. * \param[in] dev Device from which the FW log will be taken using the created message @@ -483,13 +497,23 @@ const char* rs2_get_fw_log_parsed_message(rs2_firmware_log_parsed_message* fw_lo const char* rs2_get_fw_log_parsed_file_name(rs2_firmware_log_parsed_message* fw_log_parsed_msg, rs2_error** error); /** -* \brief Gets RealSense firmware log parsed message thread name. +* \brief Gets RealSense firmware log parsed message source (SoC) or thread name. * \param[in] fw_log_parsed_msg firmware log parsed message object * \param[out] error If non-null, receives any error that occurs during this call, otherwise, errors are ignored. -* \return thread name of the firmware log parsed message +* \return source (SoC) or thread name of the firmware log parsed message */ const char* rs2_get_fw_log_parsed_thread_name(rs2_firmware_log_parsed_message* fw_log_parsed_msg, rs2_error** error); +/** + * \brief Gets RealSense firmware log parsed message module name. + * \param[in] fw_log_parsed_msg firmware log parsed message object + * \param[out] error If non-null, receives any error that occurs during this call, otherwise, errors are + * ignored. \return module name of the firmware log parsed message + */ +const char * rs2_get_fw_log_parsed_module_name( rs2_firmware_log_parsed_message * fw_log_parsed_msg, + rs2_error ** error ); + + /** * \brief Gets RealSense firmware log parsed message severity. * \param[in] fw_log_parsed_msg firmware log parsed message object diff --git a/include/librealsense2/hpp/rs_internal.hpp b/include/librealsense2/hpp/rs_internal.hpp index f902362476..9936abeb98 100644 --- a/include/librealsense2/hpp/rs_internal.hpp +++ b/include/librealsense2/hpp/rs_internal.hpp @@ -396,9 +396,16 @@ namespace rs2 std::string thread_name() const { rs2_error* e = nullptr; - std::string thread_name(rs2_get_fw_log_parsed_thread_name(_parsed_fw_log.get(), &e)); + std::string name(rs2_get_fw_log_parsed_thread_name(_parsed_fw_log.get(), &e)); error::handle(e); - return thread_name; + return name; + } + std::string module_name() const + { + rs2_error * e = nullptr; + std::string name( rs2_get_fw_log_parsed_module_name( _parsed_fw_log.get(), &e ) ); + error::handle( e ); + return name; } std::string severity() const { @@ -450,6 +457,20 @@ namespace rs2 error::handle(e); } + void start_collecting() + { + rs2_error * e = nullptr; + rs2_start_collecting_fw_logs( _dev.get(), &e ); + error::handle( e ); + } + + void stop_collecting() + { + rs2_error * e = nullptr; + rs2_start_collecting_fw_logs( _dev.get(), &e ); + error::handle( e ); + } + rs2::firmware_log_message create_message() { rs2_error* e = nullptr; diff --git a/src/ds/d500/d500-device.cpp b/src/ds/d500/d500-device.cpp index cefe8d46bb..2c12a9b11b 100644 --- a/src/ds/d500/d500-device.cpp +++ b/src/ds/d500/d500-device.cpp @@ -652,7 +652,7 @@ namespace librealsense register_info(RS2_CAMERA_INFO_FIRMWARE_UPDATE_ID, gvd_parsed_fields.optical_module_sn); register_info(RS2_CAMERA_INFO_FIRMWARE_VERSION, gvd_parsed_fields.fw_version); register_info(RS2_CAMERA_INFO_PHYSICAL_PORT, group.uvc_devices.front().device_path); - register_info(RS2_CAMERA_INFO_DEBUG_OP_CODE, std::to_string(static_cast(fw_cmd::GLD))); + register_info(RS2_CAMERA_INFO_DEBUG_OP_CODE, std::to_string(static_cast(fw_cmd::GET_FW_LOGS))); register_info(RS2_CAMERA_INFO_ADVANCED_MODE, ((advanced_mode) ? "YES" : "NO")); register_info(RS2_CAMERA_INFO_PRODUCT_ID, pid_hex_str); register_info(RS2_CAMERA_INFO_PRODUCT_LINE, "D500"); @@ -713,12 +713,7 @@ namespace librealsense command d500_device::get_firmware_logs_command() const { - return command{ ds::GLD, 0x1f4 }; - } - - command d500_device::get_flash_logs_command() const - { - return command{ ds::FRB, 0x17a000, 0x3f8 }; + return command{ ds::GET_FW_LOGS }; } bool d500_device::check_symmetrization_enabled() const diff --git a/src/ds/d500/d500-device.h b/src/ds/d500/d500-device.h index f73d7fac4e..540df641af 100644 --- a/src/ds/d500/d500-device.h +++ b/src/ds/d500/d500-device.h @@ -88,9 +88,8 @@ namespace librealsense void get_gvd_details(const std::vector& gvd_buff, ds::d500_gvd_parsed_fields* parsed_fields) const; bool check_symmetrization_enabled() const; - //TODO - add these to device class as pure virtual methods + command get_firmware_logs_command() const; - command get_flash_logs_command() const; void init(std::shared_ptr ctx, const platform::backend_device_group& group); void register_features(); diff --git a/src/ds/d500/d500-factory.cpp b/src/ds/d500/d500-factory.cpp index 46f01f27ba..194628a8cb 100644 --- a/src/ds/d500/d500-factory.cpp +++ b/src/ds/d500/d500-factory.cpp @@ -41,7 +41,7 @@ class d555e_device , public d500_color , public d500_motion , public ds_advanced_mode_base - , public firmware_logger_device + , public extended_firmware_logger_device { public: d555e_device( std::shared_ptr< const d500_info > dev_info ) @@ -52,8 +52,9 @@ class d555e_device , d500_color( dev_info, RS2_FORMAT_YUYV ) , d500_motion( dev_info ) , ds_advanced_mode_base( d500_device::_hw_monitor, get_depth_sensor() ) - , firmware_logger_device( - dev_info, d500_device::_hw_monitor, get_firmware_logs_command(), get_flash_logs_command() ) + , extended_firmware_logger_device( dev_info, + d500_device::_hw_monitor, + get_firmware_logs_command() ) { } diff --git a/src/ds/ds-private.h b/src/ds/ds-private.h index 144e761771..18bfe812cd 100644 --- a/src/ds/ds-private.h +++ b/src/ds/ds-private.h @@ -89,7 +89,7 @@ namespace librealsense FEF = 0x0c, // Erase flash full FSRU = 0x0d, // Flash status register unlock FPLOCK = 0x0e, // Permanent lock on lower Quarter region of the flash - GLD = 0x0f, // FW logs + GLD = 0x0f, // Legacy get FW logs command GVD = 0x10, // camera details GETINTCAL = 0x15, // Read calibration table SETINTCAL = 0x16, // Set Internal sub calibration table @@ -129,7 +129,8 @@ namespace librealsense SAFETY_PRESET_WRITE = 0x95, // Write safety preset to given index SET_HKR_CONFIG_TABLE = 0xA6, // HKR Set Internal sub calibration table GET_HKR_CONFIG_TABLE = 0xA7, // HKR Get Internal sub calibration table - CALIBRESTOREEPROM = 0xA8 // HKR Store EEPROM Calibration + CALIBRESTOREEPROM = 0xA8, // HKR Store EEPROM Calibration + GET_FW_LOGS = 0xB4 // Get FW logs extended format }; #define TOSTRING(arg) #arg @@ -161,6 +162,7 @@ namespace librealsense ENUM2STR(SETSUBPRESET); ENUM2STR(GETSUBPRESET); ENUM2STR(GETSUBPRESETID); + ENUM2STR(GET_FW_LOGS); default: return ( rsutils::string::from() << "Unrecognized FW command " << state ); } diff --git a/src/firmware_logger_device.cpp b/src/firmware_logger_device.cpp index 8167c7ea14..71da2916f7 100644 --- a/src/firmware_logger_device.cpp +++ b/src/firmware_logger_device.cpp @@ -2,133 +2,221 @@ // Copyright(c) 2020 Intel Corporation. All Rights Reserved. #include "firmware_logger_device.h" -#include namespace librealsense { firmware_logger_device::firmware_logger_device( std::shared_ptr< const device_info > const & dev_info, - std::shared_ptr hardware_monitor, - const command& fw_logs_command, const command& flash_logs_command) : - device(dev_info), - _hw_monitor(hardware_monitor), - _fw_logs(), - _flash_logs(), - _flash_logs_initialized(false), - _parser(nullptr), - _fw_logs_command(fw_logs_command), - _flash_logs_command(flash_logs_command) { } - - bool firmware_logger_device::get_fw_log(fw_logs::fw_logs_binary_data& binary_data) + std::shared_ptr< hw_monitor > hardware_monitor, + const command & fw_logs_command, + const command & flash_logs_command ) + : device( dev_info ) + , _fw_logs_command( fw_logs_command ) + , _hw_monitor( hardware_monitor ) + , _fw_logs() + , _parser( nullptr ) + , _flash_logs_command( flash_logs_command ) + , _flash_logs() + , _flash_logs_initialized( false ) + { + if( ! _hw_monitor ) + throw librealsense::invalid_value_exception( "HW monitor is empty" ); + } + + bool firmware_logger_device::get_fw_log( fw_logs::fw_logs_binary_data & binary_data ) { bool result = false; - if (_fw_logs.empty()) + + if( _fw_logs.empty() ) { get_fw_logs_from_hw_monitor(); } - if (!_fw_logs.empty()) + if( ! _fw_logs.empty() ) { - fw_logs::fw_logs_binary_data data; - data = _fw_logs.front(); + binary_data = std::move( _fw_logs.front() ); _fw_logs.pop(); - binary_data = data; result = true; } + return result; } + void firmware_logger_device::get_fw_logs_from_hw_monitor() + { + command update_command = get_update_command(); + if( update_command.cmd == 0 ) + return; + + auto res = _hw_monitor->send( update_command ); + if( res.empty() ) + { + return; + } + + // Convert bytes to fw_logs_binary_data + auto beginOfLogIterator = res.data(); + size_t size_left = res.size(); + while( size_left > 0 ) + { + size_t log_size = get_log_size( beginOfLogIterator ); + if( log_size > size_left ) + { + LOG_WARNING( "Received an incomplete FW log" ); // TODO - remove after debugging, or decrease to debug level. + break; + } + auto endOfLogIterator = beginOfLogIterator + log_size; + fw_logs::fw_logs_binary_data binary_data; + binary_data.logs_buffer.assign( beginOfLogIterator, endOfLogIterator ); + _fw_logs.push( std::move( binary_data ) ); + beginOfLogIterator = endOfLogIterator; + size_left -= log_size; + } + } + + command firmware_logger_device::get_update_command() + { + return _fw_logs_command; + } + unsigned int firmware_logger_device::get_number_of_fw_logs() const { return (unsigned int)_fw_logs.size(); } - void firmware_logger_device::get_fw_logs_from_hw_monitor() + size_t firmware_logger_device::get_log_size( const uint8_t * buff ) const + { + // Log size constant, no need to call parser like firmware_logger_device::get_log_size. + return sizeof( fw_logs::fw_log_binary ); + } + + bool firmware_logger_device::get_flash_log( fw_logs::fw_logs_binary_data & binary_data ) { - auto res = _hw_monitor->send(_fw_logs_command); - if (res.empty()) + bool result = false; + + if( ! _flash_logs_initialized ) { - return; + get_flash_logs_from_hw_monitor(); } - auto beginOfLogIterator = res.begin(); - // convert bytes to fw_logs_binary_data - for (int i = 0; i < res.size() / fw_logs::BINARY_DATA_SIZE; ++i) + if( ! _flash_logs.empty() ) { - auto endOfLogIterator = beginOfLogIterator + fw_logs::BINARY_DATA_SIZE; - std::vector resultsForOneLog; - resultsForOneLog.insert(resultsForOneLog.begin(), beginOfLogIterator, endOfLogIterator); - fw_logs::fw_logs_binary_data binary_data{ resultsForOneLog }; - _fw_logs.push(binary_data); - beginOfLogIterator = endOfLogIterator; + binary_data = std::move( _flash_logs.front() ); + _flash_logs.pop(); + result = true; } + + return result; } void firmware_logger_device::get_flash_logs_from_hw_monitor() { - auto res = _hw_monitor->send(_flash_logs_command); + auto res = _hw_monitor->send( _flash_logs_command ); - if (res.empty()) + if( res.empty() ) { - LOG_INFO("Getting Flash logs failed!"); + LOG_INFO( "Getting Flash logs failed!" ); return; } - //erasing header - int size_of_flash_logs_header = 27; - res.erase(res.begin(), res.begin() + size_of_flash_logs_header); + // Erasing header + constexpr const uint8_t size_of_flash_logs_header = 27; + res.erase( res.begin(), res.begin() + size_of_flash_logs_header ); + // Convert bytes to fw_logs_binary_data. + constexpr const uint8_t flash_logs_magic_number = 0xA0; auto beginOfLogIterator = res.begin(); - // convert bytes to flash_logs_binary_data - for (int i = 0; i < res.size() / fw_logs::BINARY_DATA_SIZE && *beginOfLogIterator == 160; ++i) + for( int i = 0; + i < res.size() / sizeof( fw_logs::fw_log_binary ) && *beginOfLogIterator == flash_logs_magic_number; + ++i ) { - auto endOfLogIterator = beginOfLogIterator + fw_logs::BINARY_DATA_SIZE; - std::vector resultsForOneLog; - resultsForOneLog.insert(resultsForOneLog.begin(), beginOfLogIterator, endOfLogIterator); + auto endOfLogIterator = beginOfLogIterator + sizeof( fw_logs::fw_log_binary ); + std::vector< uint8_t > resultsForOneLog; + resultsForOneLog.insert( resultsForOneLog.begin(), beginOfLogIterator, endOfLogIterator ); fw_logs::fw_logs_binary_data binary_data{ resultsForOneLog }; - _flash_logs.push(binary_data); + _flash_logs.push( binary_data ); beginOfLogIterator = endOfLogIterator; } _flash_logs_initialized = true; } - bool firmware_logger_device::get_flash_log(fw_logs::fw_logs_binary_data& binary_data) + bool firmware_logger_device::init_parser( const std::string & xml_content ) { - bool result = false; - if (!_flash_logs_initialized) - { - get_flash_logs_from_hw_monitor(); - } + _parser = std::make_unique< fw_logs::fw_logs_parser >( xml_content ); + + return ( _parser != nullptr ); + } - if (!_flash_logs.empty()) + bool firmware_logger_device::parse_log( const fw_logs::fw_logs_binary_data * fw_log_msg, + fw_logs::fw_log_data * parsed_msg ) + { + bool result = false; + if( _parser && parsed_msg && fw_log_msg ) { - fw_logs::fw_logs_binary_data data; - data = _flash_logs.front(); - _flash_logs.pop(); - binary_data = data; + *parsed_msg = _parser->parse_fw_log( fw_log_msg ); result = true; } + return result; } - bool firmware_logger_device::init_parser(std::string xml_content) + extended_firmware_logger_device::extended_firmware_logger_device( std::shared_ptr< const device_info > const & dev_info, + std::shared_ptr< hw_monitor > hardware_monitor, + const command & fw_logs_command ) + : firmware_logger_device( dev_info, hardware_monitor, fw_logs_command, command( 0 ) ) { - _parser = new fw_logs::fw_logs_parser(xml_content); + } - return (_parser != nullptr); + void extended_firmware_logger_device::start() + { + fw_logs::extended_fw_logs_parser * parser; + if( ! _parser || !( parser = dynamic_cast< fw_logs::extended_fw_logs_parser * >( _parser.get() ) ) ) + throw librealsense::wrong_api_call_sequence_exception( "FW log parser is not initialized" ); + + command start_command = parser->get_start_command(); + start_command.cmd = _fw_logs_command.cmd; // Opcode comes from the device, may be different between devices + if( start_command.cmd != 0 ) + _hw_monitor->send( start_command ); } - bool firmware_logger_device::parse_log(const fw_logs::fw_logs_binary_data* fw_log_msg, - fw_logs::fw_log_data* parsed_msg) + void extended_firmware_logger_device::stop() { - bool result = false; - if (_parser && parsed_msg && fw_log_msg) - { - *parsed_msg = _parser->parse_fw_log(fw_log_msg); - result = true; - } + fw_logs::extended_fw_logs_parser * parser; + if( ! _parser || ! ( parser = dynamic_cast< fw_logs::extended_fw_logs_parser * >( _parser.get() ) ) ) + throw librealsense::wrong_api_call_sequence_exception( "FW log parser is not initialized" ); + + command stop_command = parser->get_stop_command(); + stop_command.cmd = _fw_logs_command.cmd; // Opcode comes from the device, may be different between devices + if( stop_command.cmd != 0 ) + _hw_monitor->send( stop_command ); + } - return result; + size_t extended_firmware_logger_device::get_log_size( const uint8_t * buff ) const + { + // Log size is dynamic. Parser must be initialized to examine log buffer. + if( ! _parser ) + throw librealsense::wrong_api_call_sequence_exception( "FW log parser is not initialized" ); + + return _parser->get_log_size( buff ); + } + + + command extended_firmware_logger_device::get_update_command() + { + fw_logs::extended_fw_logs_parser * parser; + if( ! _parser || ! ( parser = dynamic_cast< fw_logs::extended_fw_logs_parser * >( _parser.get() ) ) ) + throw librealsense::wrong_api_call_sequence_exception( "FW log parser is not initialized" ); + + command update_command = parser->get_update_command(); + update_command.cmd = _fw_logs_command.cmd; // Opcode comes from the device, may be different between devices + + return update_command; } + bool extended_firmware_logger_device::init_parser( const std::string & xml_content ) + { + _parser = std::make_unique< fw_logs::extended_fw_logs_parser >( xml_content, _source_to_expected_version ); + + return ( _parser != nullptr ); + } } diff --git a/src/firmware_logger_device.h b/src/firmware_logger_device.h index cd77dcf319..547eb9c48a 100644 --- a/src/firmware_logger_device.h +++ b/src/firmware_logger_device.h @@ -1,66 +1,91 @@ // License: Apache 2.0. See LICENSE file in root directory. -// Copyright(c) 2020 Intel Corporation. All Rights Reserved. +// Copyright(c) 2020-2024 Intel Corporation. All Rights Reserved. #pragma once #include "core/extension.h" #include "device.h" #include "hw-monitor.h" -#include #include "fw-logs/fw-log-data.h" #include "fw-logs/fw-logs-parser.h" +#include +#include + namespace librealsense { class firmware_logger_extensions { public: - virtual bool get_fw_log(fw_logs::fw_logs_binary_data& binary_data) = 0; - virtual bool get_flash_log(fw_logs::fw_logs_binary_data& binary_data) = 0; + virtual void start() = 0; + virtual void stop() = 0; + virtual bool get_fw_log( fw_logs::fw_logs_binary_data & binary_data ) = 0; + virtual bool get_flash_log( fw_logs::fw_logs_binary_data & binary_data ) = 0; virtual unsigned int get_number_of_fw_logs() const = 0; - virtual bool init_parser(std::string xml_content) = 0; - virtual bool parse_log(const fw_logs::fw_logs_binary_data* fw_log_msg, fw_logs::fw_log_data* parsed_msg) = 0; + virtual bool init_parser( const std::string & xml_content ) = 0; + virtual bool parse_log( const fw_logs::fw_logs_binary_data * fw_log_msg, fw_logs::fw_log_data * parsed_msg ) = 0; virtual ~firmware_logger_extensions() = default; }; - MAP_EXTENSION(RS2_EXTENSION_FW_LOGGER, librealsense::firmware_logger_extensions); + MAP_EXTENSION( RS2_EXTENSION_FW_LOGGER, librealsense::firmware_logger_extensions ); class firmware_logger_device : public virtual device, public firmware_logger_extensions { public: firmware_logger_device( std::shared_ptr< const device_info > const & dev_info, - std::shared_ptr hardware_monitor, - const command& fw_logs_command, const command& flash_logs_command); - - bool get_fw_log(fw_logs::fw_logs_binary_data& binary_data) override; - bool get_flash_log(fw_logs::fw_logs_binary_data& binary_data) override; - - unsigned int get_number_of_fw_logs() const override; + std::shared_ptr< hw_monitor > hardware_monitor, + const command & fw_logs_command, + const command & flash_logs_command ); - bool init_parser(std::string xml_content) override; - bool parse_log(const fw_logs::fw_logs_binary_data* fw_log_msg, fw_logs::fw_log_data* parsed_msg) override; + // Legacy devices always collecting, no need to start/stop + void start() override { return; } + void stop() override { return; } - // Temporal solution for HW_Monitor injection - void assign_hw_monitor(std::shared_ptr hardware_monitor) - { _hw_monitor = hardware_monitor; } + bool get_fw_log( fw_logs::fw_logs_binary_data & binary_data ) override; + bool get_flash_log( fw_logs::fw_logs_binary_data & binary_data ) override; + unsigned int get_number_of_fw_logs() const override; - private: + bool init_parser( const std::string & xml_content ) override; + bool parse_log( const fw_logs::fw_logs_binary_data * fw_log_msg, fw_logs::fw_log_data * parsed_msg ) override; + protected: void get_fw_logs_from_hw_monitor(); void get_flash_logs_from_hw_monitor(); - + virtual command get_update_command(); + virtual size_t get_log_size( const uint8_t * buff ) const; + command _fw_logs_command; + std::shared_ptr< hw_monitor > _hw_monitor; + std::queue< fw_logs::fw_logs_binary_data > _fw_logs; + std::unique_ptr< fw_logs::fw_logs_parser > _parser; + command _flash_logs_command; + std::queue< fw_logs::fw_logs_binary_data > _flash_logs; + bool _flash_logs_initialized; + }; + + class extended_firmware_logger_device : public firmware_logger_device + { + public: + extended_firmware_logger_device( std::shared_ptr< const device_info > const & dev_info, + std::shared_ptr< hw_monitor > hardware_monitor, + const command & fw_logs_command ); - std::shared_ptr _hw_monitor; + void start() override; + void stop() override; - std::queue _fw_logs; - std::queue _flash_logs; + bool get_flash_log( fw_logs::fw_logs_binary_data & binary_data ) override { return false; } // Not supported - bool _flash_logs_initialized; + bool init_parser( const std::string & xml_content ) override; - fw_logs::fw_logs_parser* _parser; - uint16_t _device_pid; + void set_expected_source_versions( std::map< int, std::string > && expected_versions ) + { + _source_to_expected_version = std::move( expected_versions ); + } - }; + protected: + command get_update_command() override; + size_t get_log_size( const uint8_t * buff ) const override; + std::map< int, std::string > _source_to_expected_version; + }; } diff --git a/src/fw-logs/CMakeLists.txt b/src/fw-logs/CMakeLists.txt index 87b93913e4..9c0b4e6c21 100644 --- a/src/fw-logs/CMakeLists.txt +++ b/src/fw-logs/CMakeLists.txt @@ -4,8 +4,8 @@ target_sources(${LRS_TARGET} PRIVATE "${CMAKE_CURRENT_LIST_DIR}/fw-log-data.cpp" "${CMAKE_CURRENT_LIST_DIR}/fw-log-data.h" - "${CMAKE_CURRENT_LIST_DIR}/fw-logs-formating-options.cpp" - "${CMAKE_CURRENT_LIST_DIR}/fw-logs-formating-options.h" + "${CMAKE_CURRENT_LIST_DIR}/fw-logs-formatting-options.cpp" + "${CMAKE_CURRENT_LIST_DIR}/fw-logs-formatting-options.h" "${CMAKE_CURRENT_LIST_DIR}/fw-logs-parser.cpp" "${CMAKE_CURRENT_LIST_DIR}/fw-logs-parser.h" "${CMAKE_CURRENT_LIST_DIR}/fw-logs-xml-helper.cpp" diff --git a/src/fw-logs/fw-log-data.cpp b/src/fw-logs/fw-log-data.cpp index a250d07ad5..5fc4eeedfe 100644 --- a/src/fw-logs/fw-log-data.cpp +++ b/src/fw-logs/fw-log-data.cpp @@ -1,70 +1,93 @@ // License: Apache 2.0. See LICENSE file in root directory. -// Copyright(c) 2019 Intel Corporation. All Rights Reserved. +// Copyright(c) 2019-2024 Intel Corporation. All Rights Reserved. + #include "fw-log-data.h" -#include -#include -#include -#include -using namespace std; +#include -# define SET_WIDTH_AND_FILL(num, element) \ -setfill(' ') << setw(num) << left << element \ +#include namespace librealsense { namespace fw_logs { - rs2_log_severity fw_log_data::get_severity() const - { - return fw_logs_severity_to_log_severity(_severity); - } + constexpr const size_t min_binary_size = std::min( sizeof( fw_log_binary ), sizeof( extended_fw_log_binary ) ); + constexpr const uint8_t fw_logs_magic_number = 0xA0; + constexpr const uint8_t extended_fw_logs_magic_number = 0xA5; - const std::string& fw_log_data::get_message() const + rs2_log_severity fw_logs_binary_data::get_severity() const { - return _message; - } + if( logs_buffer.size() < min_binary_size ) + throw librealsense::invalid_value_exception( rsutils::string::from() + << "FW log data size is too small " + << logs_buffer.size() ); - const std::string& fw_log_data::get_file_name() const - { - return _file_name; - } + const auto log_binary = reinterpret_cast< const fw_log_binary_common * >( logs_buffer.data() ); + if( log_binary->magic_number == extended_fw_logs_magic_number ) + return extended_fw_logs_severity_to_rs2_log_severity( log_binary->severity ); + if( log_binary->magic_number == fw_logs_magic_number ) + return fw_logs_severity_to_rs2_log_severity( log_binary->severity ); - const std::string& fw_log_data::get_thread_name() const - { - return _thread_name; + throw librealsense::invalid_value_exception( rsutils::string::from() + << "Received unfamiliar FW log 'magic number' " + << log_binary->magic_number ); } - uint32_t fw_log_data::get_line() const + uint32_t fw_logs_binary_data::get_timestamp() const { - return _line; - } + if( logs_buffer.size() < min_binary_size ) + throw librealsense::invalid_value_exception( rsutils::string::from() + << "FW log data size is too small " + << logs_buffer.size() ); - uint32_t fw_log_data::get_timestamp() const - { - return (uint32_t)_timestamp; - } + const auto log_binary = reinterpret_cast< const fw_log_binary_common * >( logs_buffer.data() ); + if( log_binary->magic_number == extended_fw_logs_magic_number ) + { + auto timestamp = reinterpret_cast< const extended_fw_log_binary * >( this )->soc_timestamp; + return static_cast< uint32_t >( timestamp ); + } + if( log_binary->magic_number == fw_logs_magic_number ) + return reinterpret_cast< const fw_log_binary * >( this )->timestamp; - uint32_t fw_log_data::get_sequence_id() const - { - return _sequence; + throw librealsense::invalid_value_exception( rsutils::string::from() + << "Received unfamiliar FW log 'magic number' " + << log_binary->magic_number ); } - rs2_log_severity fw_logs_binary_data::get_severity() const + rs2_log_severity extended_fw_logs_severity_to_rs2_log_severity( int32_t severity ) { - const fw_log_binary* log_binary = reinterpret_cast(logs_buffer.data()); - return fw_logs_severity_to_log_severity(static_cast(log_binary->dword1.bits.severity)); - } + rs2_log_severity result = RS2_LOG_SEVERITY_NONE; - uint32_t fw_logs_binary_data::get_timestamp() const - { - const fw_log_binary* log_binary = reinterpret_cast(logs_buffer.data()); - return static_cast(log_binary->dword5.timestamp); + switch( severity ) + { + case 1: // Verbose level. Fall through, debug is LibRS most verbose level + case 2: + result = RS2_LOG_SEVERITY_DEBUG; + break; + case 4: + result = RS2_LOG_SEVERITY_INFO; + break; + case 8: + result = RS2_LOG_SEVERITY_WARN; + break; + case 16: + result = RS2_LOG_SEVERITY_ERROR; + break; + case 32: + result = RS2_LOG_SEVERITY_FATAL; + break; + case 0: // No logging. Fall through to keep level None set above. + default: + break; + } + + return result; } - rs2_log_severity fw_logs_severity_to_log_severity(int32_t severity) + rs2_log_severity fw_logs_severity_to_rs2_log_severity(int32_t severity) { rs2_log_severity result = RS2_LOG_SEVERITY_NONE; + switch (severity) { case 1: @@ -82,7 +105,8 @@ namespace librealsense default: break; } + return result; } - } -} + } // namespace fw_logs +} // namespace librealsense diff --git a/src/fw-logs/fw-log-data.h b/src/fw-logs/fw-log-data.h index 183a4d56bb..16b1390352 100644 --- a/src/fw-logs/fw-log-data.h +++ b/src/fw-logs/fw-log-data.h @@ -17,89 +17,63 @@ namespace librealsense uint32_t get_timestamp() const; }; - rs2_log_severity fw_logs_severity_to_log_severity(int32_t severity); + rs2_log_severity extended_fw_logs_severity_to_rs2_log_severity( int32_t severity ); + rs2_log_severity fw_logs_severity_to_rs2_log_severity( int32_t severity ); - static const int BINARY_DATA_SIZE = 20; - - typedef union - { - uint32_t value; - struct - { - uint32_t magic_number : 8; - uint32_t severity : 5; - uint32_t thread_id : 3; - uint32_t file_id : 11; - uint32_t group_id : 5; - } bits; - } fw_log_header_dword1; - - typedef union +#pragma pack( push, 1 ) + struct fw_log_binary_common { - uint32_t value; - struct - { - uint32_t event_id : 16; - uint32_t line_id : 12; - uint32_t seq_id : 4; - } bits; - } fw_log_header_dword2; + uint32_t magic_number : 8; + uint32_t severity : 5; + uint32_t source_id : 3; // SoC ID in D500, thread ID in D400 + uint32_t file_id : 11; + uint32_t module_id : 5; + uint32_t event_id : 16; + uint32_t line_id : 12; + uint32_t seq_id : 4; // Rolling counter 0-15 per module + }; - struct fw_log_header_dword3 + struct fw_log_binary : public fw_log_binary_common { uint16_t p1; uint16_t p2; - }; - - struct fw_log_header_dword4 - { uint32_t p3; - }; - - struct fw_log_header_dword5 - { uint32_t timestamp; }; - struct fw_log_binary + struct extended_fw_log_binary : public fw_log_binary_common { - fw_log_header_dword1 dword1; - fw_log_header_dword2 dword2; - fw_log_header_dword3 dword3; - fw_log_header_dword4 dword4; - fw_log_header_dword5 dword5; - }; + uint16_t number_of_params; // Max 32 params + uint16_t total_params_size_bytes; // Max 848 bytes, number of bytes after hkr_timestamp field + uint64_t soc_timestamp; + uint64_t hkr_timestamp; + // Also additinal parameters data, but size differs based on number and type of params so not defined here. + }; +#pragma pack( pop ) - class fw_log_data + struct fw_log_data { - public: - uint32_t _magic_number = 0; - uint32_t _severity = 0; - uint32_t _file_id = 0; - uint32_t _group_id = 0; - uint32_t _event_id = 0; - uint32_t _line = 0; - uint32_t _sequence = 0; - uint32_t _p1 = 0; - uint32_t _p2 = 0; - uint32_t _p3 = 0; - uint64_t _timestamp = 0; - double _delta = 0.0; + rs2_log_severity severity = RS2_LOG_SEVERITY_NONE; + uint32_t line = 0; + uint32_t sequence = 0; + uint64_t timestamp = 0; - uint32_t _thread_id = 0; + std::string message = ""; + std::string source_name = ""; + std::string file_name = ""; + std::string module_name = ""; + }; - std::string _message = ""; - std::string _file_name = ""; - std::string _thread_name = ""; + constexpr const size_t max_sources = 3; + constexpr const size_t max_modules = 32; - rs2_log_severity get_severity() const; - const std::string& get_message() const; - const std::string& get_file_name() const; - const std::string& get_thread_name() const; - uint32_t get_line() const; - uint32_t get_timestamp() const; - uint32_t get_sequence_id() const; + struct extended_log_request + { + uint32_t opcode = 0; + uint32_t update = 0; // Indication if FW log settings need to be updated. Ignore the following fields if 0. + uint32_t module_filter[max_sources] = {}; // Bits mapped to source modules. 1 should log, 0 should not. + uint8_t severity_level[max_sources][max_modules] = {}; // Maps to modules. Zero disables module logging. }; } } diff --git a/src/fw-logs/fw-logs-formating-options.cpp b/src/fw-logs/fw-logs-formating-options.cpp deleted file mode 100644 index 0b8a947ac1..0000000000 --- a/src/fw-logs/fw-logs-formating-options.cpp +++ /dev/null @@ -1,95 +0,0 @@ -// License: Apache 2.0. See LICENSE file in root directory. -// Copyright(c) 2019 Intel Corporation. All Rights Reserved. -#include "fw-logs-formating-options.h" -#include "fw-logs-xml-helper.h" -#include - - -using namespace std; -namespace librealsense -{ - namespace fw_logs - { - fw_log_event::fw_log_event() - : num_of_params(0), - line("") - {} - - fw_log_event::fw_log_event(int input_num_of_params, const string& input_line) - : num_of_params(input_num_of_params), - line(input_line) - {} - - - fw_logs_formating_options::fw_logs_formating_options(const string& xml_content) - : _xml_content(xml_content) - {} - - - fw_logs_formating_options::~fw_logs_formating_options(void) - { - } - - bool fw_logs_formating_options::get_event_data(int id, fw_log_event* log_event_data) const - { - auto event_it = _fw_logs_event_list.find(id); - if (event_it != _fw_logs_event_list.end()) - { - *log_event_data = event_it->second; - return true; - } - else - { - stringstream ss; - ss << "*** Unrecognized Log Id: "; - ss << id; - ss << "! P1 = 0x{0:x}, P2 = 0x{1:x}, P3 = 0x{2:x}"; - log_event_data->line = ss.str(); - log_event_data->num_of_params = 3; - return false; - } - } - - bool fw_logs_formating_options::get_file_name(int id, string* file_name) const - { - auto file_it = _fw_logs_file_names_list.find(id); - if (file_it != _fw_logs_file_names_list.end()) - { - *file_name = file_it->second; - return true; - } - else - { - *file_name = "Unknown"; - return false; - } - } - - bool fw_logs_formating_options::get_thread_name(uint32_t thread_id, string* thread_name) const - { - auto file_it = _fw_logs_thread_names_list.find(thread_id); - if (file_it != _fw_logs_thread_names_list.end()) - { - *thread_name = file_it->second; - return true; - } - else - { - *thread_name = "Unknown"; - return false; - } - } - - std::unordered_map> fw_logs_formating_options::get_enums() const - { - return _fw_logs_enum_names_list; - } - - bool fw_logs_formating_options::initialize_from_xml() - { - fw_logs_xml_helper fw_logs_xml(_xml_content); - return fw_logs_xml.build_log_meta_data(this); - } - } -} - diff --git a/src/fw-logs/fw-logs-formating-options.h b/src/fw-logs/fw-logs-formating-options.h deleted file mode 100644 index 3578fcbee0..0000000000 --- a/src/fw-logs/fw-logs-formating-options.h +++ /dev/null @@ -1,54 +0,0 @@ -/* License: Apache 2.0. See LICENSE file in root directory. */ -/* Copyright(c) 2019 Intel Corporation. All Rights Reserved. */ -#pragma once -#include -#include -#include -#include - -#ifdef ANDROID -#include "../../common/android_helpers.h" -#endif - - -namespace librealsense -{ - namespace fw_logs - { - struct fw_log_event - { - size_t num_of_params; - std::string line; - - fw_log_event(); - fw_log_event(int input_num_of_params, const std::string& input_line); - }; - - typedef std::pair kvp; // XML key/value pair - - class fw_logs_xml_helper; - - class fw_logs_formating_options - { - public: - fw_logs_formating_options(const std::string& xml_content); - ~fw_logs_formating_options(void); - - - bool get_event_data(int id, fw_log_event* log_event_data) const; - bool get_file_name(int id, std::string* file_name) const; - bool get_thread_name(uint32_t thread_id, std::string* thread_name) const; - std::unordered_map> get_enums() const; - bool initialize_from_xml(); - - private: - friend fw_logs_xml_helper; - std::unordered_map _fw_logs_event_list; - std::unordered_map _fw_logs_file_names_list; - std::unordered_map _fw_logs_thread_names_list; - std::unordered_map>> _fw_logs_enum_names_list; - - std::string _xml_content; - }; - } -} diff --git a/src/fw-logs/fw-logs-formatting-options.cpp b/src/fw-logs/fw-logs-formatting-options.cpp new file mode 100644 index 0000000000..c7472f0714 --- /dev/null +++ b/src/fw-logs/fw-logs-formatting-options.cpp @@ -0,0 +1,77 @@ +// License: Apache 2.0. See LICENSE file in root directory. +// Copyright(c) 2024 Intel Corporation. All Rights Reserved. + +#include +#include + +#include +#include + +#include + +namespace librealsense +{ + namespace fw_logs + { + fw_logs_formatting_options::fw_logs_formatting_options( std::string && xml_content ) + : _xml_content( std::move( xml_content ) ) + { + initialize_from_xml(); + } + + kvp fw_logs_formatting_options::get_event_data( int id ) const + { + auto event_it = _fw_logs_event_list.find( id ); + if( event_it != _fw_logs_event_list.end() ) + { + return event_it->second; + } + + throw librealsense::invalid_value_exception( rsutils::string::from() << "Unrecognized Log Id: " << id ); + } + + std::string fw_logs_formatting_options::get_file_name( int id ) const + { + auto file_it = _fw_logs_file_names_list.find( id ); + if( file_it != _fw_logs_file_names_list.end() ) + return file_it->second; + + return "Unknown"; + } + + std::string fw_logs_formatting_options::get_thread_name( uint32_t thread_id ) const + { + auto file_it = _fw_logs_thread_names_list.find( thread_id ); + if( file_it != _fw_logs_thread_names_list.end() ) + return file_it->second; + + return "Unknown"; + } + + std::string fw_logs_formatting_options::get_module_name( uint32_t module_id ) const + { + auto file_it = _fw_logs_module_names_list.find( module_id ); + if( file_it != _fw_logs_module_names_list.end() ) + return file_it->second; + + return "Unknown"; + } + + std::unordered_map< std::string, std::vector< kvp > > fw_logs_formatting_options::get_enums() const + { + return _fw_logs_enums_list; + } + + void fw_logs_formatting_options::initialize_from_xml() + { + if( _xml_content.empty() ) + throw librealsense::invalid_value_exception( "Trying to initialize from empty xml content" ); + + _fw_logs_event_list = fw_logs_xml_helper::get_events( _xml_content ); + _fw_logs_file_names_list = fw_logs_xml_helper::get_files( _xml_content ); + _fw_logs_thread_names_list = fw_logs_xml_helper::get_threads( _xml_content ); + _fw_logs_module_names_list = fw_logs_xml_helper::get_modules( _xml_content ); + _fw_logs_enums_list = fw_logs_xml_helper::get_enums( _xml_content ); + } + } // namespace fw_logs +} // namespace librealsense diff --git a/src/fw-logs/fw-logs-formatting-options.h b/src/fw-logs/fw-logs-formatting-options.h new file mode 100644 index 0000000000..b9787926a9 --- /dev/null +++ b/src/fw-logs/fw-logs-formatting-options.h @@ -0,0 +1,47 @@ +/* License: Apache 2.0. See LICENSE file in root directory. */ +/* Copyright(c) 2024 Intel Corporation. All Rights Reserved. */ + +#pragma once + +#include + +#include +#include +#include + +#ifdef ANDROID +#include "../../common/android_helpers.h" +#endif + + +namespace librealsense +{ + namespace fw_logs + { + typedef std::pair< int, std::string > kvp; // XML key/value pair + + class fw_logs_formatting_options + { + public: + fw_logs_formatting_options() = default; + fw_logs_formatting_options( std::string && xml_content ); + + kvp get_event_data( int id ) const; + std::string get_file_name( int id ) const; + std::string get_thread_name( uint32_t thread_id ) const; + std::string get_module_name( uint32_t module_id ) const; + std::unordered_map< std::string, std::vector< kvp > > get_enums() const; + + private: + void initialize_from_xml(); + + std::string _xml_content; + + std::unordered_map< int, kvp > _fw_logs_event_list; + std::unordered_map< int, std::string > _fw_logs_file_names_list; + std::unordered_map< int, std::string > _fw_logs_thread_names_list; + std::unordered_map< int, std::string > _fw_logs_module_names_list; + std::unordered_map< std::string, std::vector< std::pair< int, std::string > > > _fw_logs_enums_list; + }; + } +} diff --git a/src/fw-logs/fw-logs-parser.cpp b/src/fw-logs/fw-logs-parser.cpp index d801603a55..8f5b3ee24b 100644 --- a/src/fw-logs/fw-logs-parser.cpp +++ b/src/fw-logs/fw-logs-parser.cpp @@ -1,90 +1,343 @@ // License: Apache 2.0. See LICENSE file in root directory. -// Copyright(c) 2019 Intel Corporation. All Rights Reserved. -#include "fw-logs-parser.h" +// Copyright(c) 2024 Intel Corporation. All Rights Reserved. + +#include +#include + +#include + #include -#include -#include "fw-string-formatter.h" -#include "stdint.h" +#include +#include -using namespace std; namespace librealsense { namespace fw_logs { - fw_logs_parser::fw_logs_parser(string xml_content) - : _fw_logs_formating_options(xml_content), - _last_timestamp(0), - _timestamp_factor(0.00001) + + fw_logs_parser::fw_logs_parser( const std::string & definitions_xml ) + : _source_to_formatting_options() + { + // The definitions XML should contain entries for all log sources. + // For each source it lists parser options file path and (optional) module verbosity level. + _source_id_to_name = fw_logs_xml_helper::get_listed_sources( definitions_xml ); + for( const auto & source : _source_id_to_name ) + { + if( source.first >= fw_logs::max_sources ) + throw librealsense::invalid_value_exception( rsutils::string::from() << "Supporting source id 0 to " + << fw_logs::max_sources << ". Found source (" + << source.first << ", " << source.second << ")" ); + + initialize_source_formatting_options( source, definitions_xml ); + } + + // HACK - legacy format did not have multiple sources and did not use definitions XML to define them. + // If no sources found in definitions XML, assume it is legacy format and use the passed data to + // initialize formatting options. It also had no vebosity settings. + if( _source_id_to_name.empty() ) + { + _source_id_to_name[0] = ""; + std::string xml_contents( definitions_xml ); + fw_logs_formatting_options format_options( std::move( xml_contents ) ); + _source_to_formatting_options[0] = format_options; + } + } + + void fw_logs_parser::initialize_source_formatting_options( const std::pair< const int, std::string > & source, + const std::string & definitions_xml ) + { + std::string path = fw_logs_xml_helper::get_source_parser_file_path( source.first, definitions_xml ); + std::ifstream f( path.c_str() ); + if( f.good() ) + { + std::string xml_contents; + xml_contents.append( std::istreambuf_iterator< char >( f ), std::istreambuf_iterator< char >() ); + fw_logs_formatting_options format_options( std::move( xml_contents ) ); + _source_to_formatting_options[source.first] = format_options; + } + else + throw librealsense::invalid_value_exception( rsutils::string::from() << "Can't open file " << path ); + } + + fw_log_data fw_logs_parser::parse_fw_log( const fw_logs_binary_data * fw_log_msg ) + { + fw_log_data parsed_data = fw_log_data(); + + if( ! fw_log_msg || fw_log_msg->logs_buffer.size() == 0 ) + return parsed_data; + + // Struct data in a common format + structured_binary_data structured = structure_common_data( fw_log_msg ); + structure_timestamp( fw_log_msg, &structured ); + + const fw_logs_formatting_options & formatting = get_format_options_ref( structured.source_id ); + + kvp event_data = formatting.get_event_data( structured.event_id ); + structure_params( fw_log_msg, event_data.first, &structured ); + + // Parse data + fw_string_formatter reg_exp( formatting.get_enums() ); + parsed_data.message = reg_exp.generate_message( event_data.second, + structured.params_info, + structured.params_blob ); + + parsed_data.line = structured.line; + parsed_data.sequence = structured.sequence; + parsed_data.timestamp = structured.timestamp; + parsed_data.severity = parse_severity( structured.severity ); + parsed_data.source_name = get_source_name( structured.source_id ); + parsed_data.file_name = formatting.get_file_name( structured.file_id ); + parsed_data.module_name = formatting.get_module_name( structured.module_id ); + + return parsed_data; + } + + size_t fw_logs_parser::get_log_size( const uint8_t * ) const + { + return sizeof( fw_log_binary ); + } + + fw_logs_parser::structured_binary_data + fw_logs_parser::structure_common_data( const fw_logs_binary_data * fw_log_msg ) const + { + structured_binary_data structured_data; + + auto log_binary = reinterpret_cast< const fw_log_binary_common * >( fw_log_msg->logs_buffer.data() ); + + structured_data.severity = static_cast< uint32_t >( log_binary->severity ); + structured_data.source_id = static_cast< uint32_t >( log_binary->source_id ); + structured_data.file_id = static_cast< uint32_t >( log_binary->file_id ); + structured_data.module_id = static_cast< uint32_t >( log_binary->module_id ); + structured_data.event_id = static_cast< uint32_t >( log_binary->event_id ); + structured_data.line = static_cast< uint32_t >( log_binary->line_id ); + structured_data.sequence = static_cast< uint32_t >( log_binary->seq_id ); + + return structured_data; + } + + void fw_logs_parser::structure_timestamp( const fw_logs_binary_data * raw, + structured_binary_data * structured ) const + { + const auto actual_struct = reinterpret_cast< const fw_log_binary * >( raw->logs_buffer.data() ); + structured->timestamp = actual_struct->timestamp; + } + + void fw_logs_parser::structure_params( const fw_logs_binary_data * raw, + size_t num_of_params, + structured_binary_data * structured ) const + { + const auto actual_struct = reinterpret_cast< const fw_log_binary * >( raw->logs_buffer.data() ); + uint16_t params_size_bytes = 0; + if( num_of_params > 0 ) + { + structured->params_info.push_back( { params_size_bytes, param_type::UINT16, 2 } ); // P1, uint16_t + params_size_bytes += 2; + } + if( num_of_params > 1 ) + { + structured->params_info.push_back( { params_size_bytes, param_type::UINT16, 2 } ); // P2, uint16_t + params_size_bytes += 2; + } + if( num_of_params > 2 ) + { + structured->params_info.push_back( { params_size_bytes, param_type::UINT32, 4 } ); // P3, uint32_t + params_size_bytes += 4; + } + if( num_of_params > 3 ) + throw librealsense::invalid_value_exception( rsutils::string::from() << + "Expecting max 3 parameters, received " << num_of_params ); + + const uint8_t * blob_start = reinterpret_cast< const uint8_t * >( &actual_struct->p1 ); + structured->params_blob.insert( structured->params_blob.end(), blob_start, blob_start + params_size_bytes ); + } + + const fw_logs_formatting_options & fw_logs_parser::get_format_options_ref( int source_id ) const + { + if( _source_to_formatting_options.size() != 1 ) + throw librealsense::invalid_value_exception( rsutils::string::from() + << "FW logs parser expect one formating options, have " + << _source_to_formatting_options.size() ); + return _source_to_formatting_options.begin()->second; + } + + std::string fw_logs_parser::get_source_name( int source_id ) const { - _fw_logs_formating_options.initialize_from_xml(); + if( _source_to_formatting_options.size() != 1 ) + throw librealsense::invalid_value_exception( rsutils::string::from() + << "FW logs parser expect one formating options, have " + << _source_to_formatting_options.size() ); + // FW logs had threads, only extended format have source + return _source_to_formatting_options.begin()->second.get_thread_name( source_id ); } + rs2_log_severity fw_logs_parser::parse_severity( uint32_t severity ) const + { + return fw_logs::fw_logs_severity_to_rs2_log_severity( severity ); + } + + extended_fw_logs_parser::extended_fw_logs_parser( const std::string & definitions_xml, + const std::map< int, std::string > & expected_versions ) + : fw_logs_parser( definitions_xml ) + { + for( const auto & source : _source_id_to_name ) + initialize_source_verbosity_settings( source, definitions_xml ); - fw_logs_parser::~fw_logs_parser(void) + for( const auto & expected : expected_versions ) + validate_source_version( expected.first, expected.second, definitions_xml ); + } + + void extended_fw_logs_parser::initialize_source_verbosity_settings( const std::pair< const int, std::string > & source, + const std::string & definitions_xml ) { + auto verbosity = fw_logs_xml_helper::get_source_module_verbosity( source.first, definitions_xml ); + if( ! verbosity.empty() && verbosity.rbegin()->first >= fw_logs::max_modules ) + throw librealsense::invalid_value_exception( rsutils::string::from() << "Supporting module id 0 to " + << fw_logs::max_modules << ". Found module " + << verbosity.rbegin()->first << " in source (" + << source.first << ", " << source.second << ")" ); + + _verbosity_settings.module_filter[source.first] = 0; + for( const auto & module : verbosity ) + { + // Each bit maps to one module. If the bit is 1 logs from this module shall be collected. + _verbosity_settings.module_filter[source.first] |= module.second ? 1 << module.first : 0; + // Each item maps to one SW module. Only logs of that severity level will be collected. + _verbosity_settings.severity_level[source.first][module.first] = module.second; + } } - fw_log_data fw_logs_parser::parse_fw_log(const fw_logs_binary_data* fw_log_msg) + size_t extended_fw_logs_parser::get_log_size( const uint8_t * log ) const { - fw_log_data log_data = fw_log_data(); + const auto extended = reinterpret_cast< const extended_fw_log_binary * >( log ); + + // If there are parameters total_params_size_bytes includes the information + return sizeof( extended_fw_log_binary ) + extended->total_params_size_bytes; + } - if (!fw_log_msg || fw_log_msg->logs_buffer.size() == 0) - return log_data; + constexpr const uint32_t keep_settings = 0; + constexpr const uint32_t update_settings = 1; - log_data = fill_log_data(fw_log_msg); + command extended_fw_logs_parser::get_start_command() const + { + command activate_command( 0 ); // Opcode would be overriden by the device + activate_command.param1 = update_settings; + // extended_log_request struct was designed to match the HWM command struct, copy remaining data + activate_command.param2 = _verbosity_settings.module_filter[0]; + activate_command.param3 = _verbosity_settings.module_filter[1]; + activate_command.param4 = _verbosity_settings.module_filter[2]; + activate_command.data.assign( &_verbosity_settings.severity_level[0][0], + &_verbosity_settings.severity_level[max_sources][0] ); - //message - fw_string_formatter reg_exp(_fw_logs_formating_options.get_enums()); - fw_log_event log_event_data; - _fw_logs_formating_options.get_event_data(log_data._event_id, &log_event_data); + return activate_command; + } - uint32_t params[3] = { log_data._p1, log_data._p2, log_data._p3 }; - reg_exp.generate_message(log_event_data.line, log_event_data.num_of_params, params, &log_data._message); + command extended_fw_logs_parser::get_update_command() const + { + command update_command( 0 ); // Opcode would be overriden by the device + update_command.param1 = keep_settings; + // All other fields are ignored - //file_name - _fw_logs_formating_options.get_file_name(log_data._file_id, &log_data._file_name); + return update_command; + } - //thread_name - _fw_logs_formating_options.get_thread_name(log_data._thread_id, &log_data._thread_name); + command extended_fw_logs_parser::get_stop_command() const + { + command stop_command( 0 ); // Opcode would be overriden by the device + stop_command.param1 = update_settings; + // All other fields should remain 0 to clear settings - return log_data; + return stop_command; } - fw_log_data fw_logs_parser::fill_log_data(const fw_logs_binary_data* fw_log_msg) + void extended_fw_logs_parser::structure_timestamp( const fw_logs_binary_data * raw, + structured_binary_data * structured ) const { - fw_log_data log_data; + const auto actual_struct = reinterpret_cast< const extended_fw_log_binary * >( raw->logs_buffer.data() ); + structured->timestamp = actual_struct->soc_timestamp; + } - auto* log_binary = reinterpret_cast(fw_log_msg->logs_buffer.data()); + void extended_fw_logs_parser::structure_params( const fw_logs_binary_data * raw, + size_t num_of_params, + structured_binary_data * structured ) const + { + const auto actual_struct = reinterpret_cast< const extended_fw_log_binary * >( raw->logs_buffer.data() ); + if( num_of_params != actual_struct->number_of_params ) + throw librealsense::invalid_value_exception( rsutils::string::from() << "Expecting " << num_of_params + << " parameters, received " << actual_struct->number_of_params ); - //parse first DWORD - log_data._magic_number = static_cast(log_binary->dword1.bits.magic_number); - log_data._severity = static_cast(log_binary->dword1.bits.severity); - log_data._thread_id = static_cast(log_binary->dword1.bits.thread_id); - log_data._file_id = static_cast(log_binary->dword1.bits.file_id); - log_data._group_id = static_cast(log_binary->dword1.bits.group_id); + if( num_of_params > 0 ) + { +#pragma pack( push, 1 ) + struct extended_fw_log_params_binary : public extended_fw_log_binary + { + // param_info array size namber_of_params, 1 is just a placeholder. Need to use an array not a + // pointer because pointer can be 8 bytes of size and than we won't parse the data correctly + param_info info[1]; + }; +#pragma pack( pop ) - //parse second DWORD - log_data._event_id = static_cast(log_binary->dword2.bits.event_id); - log_data._line = static_cast(log_binary->dword2.bits.line_id); - log_data._sequence = static_cast(log_binary->dword2.bits.seq_id); + const auto with_params = reinterpret_cast< const extended_fw_log_params_binary * >( raw->logs_buffer.data() ); + const uint8_t * blob_start = reinterpret_cast< const uint8_t * >( with_params->info + + actual_struct->number_of_params ); + size_t blob_size = actual_struct->total_params_size_bytes - num_of_params * sizeof( fw_logs::param_info ); + for( size_t i = 0; i < num_of_params; ++i ) + { + // Raw message offset is start of message, structured data offset is start of blob + size_t blob_offset = blob_start - raw->logs_buffer.data(); + param_info adjusted_info = { static_cast< uint16_t >( with_params->info[i].offset - blob_offset ), + with_params->info[i].type, + with_params->info[i].size }; + structured->params_info.push_back( adjusted_info ); + } + structured->params_blob.assign( blob_start, blob_start + blob_size ); + } + } - //parse third DWORD - log_data._p1 = static_cast(log_binary->dword3.p1); - log_data._p2 = static_cast(log_binary->dword3.p2); + const fw_logs_formatting_options & extended_fw_logs_parser::get_format_options_ref( int source_id ) const + { + auto iter = _source_to_formatting_options.find( source_id ); + if( iter != _source_to_formatting_options.end() ) + return iter->second; - //parse forth DWORD - log_data._p3 = static_cast(log_binary->dword4.p3); + throw librealsense::invalid_value_exception( rsutils::string::from() + << "Invalid source ID received " << source_id ); + } - //parse fifth DWORD - log_data._timestamp = log_binary->dword5.timestamp; + std::string extended_fw_logs_parser::get_source_name( int source_id ) const + { + auto iter = _source_id_to_name.find( source_id ); + if( iter != _source_id_to_name.end() ) + return iter->second; - log_data._delta = (_last_timestamp == 0) ? - 0 :(log_data._timestamp - _last_timestamp) * _timestamp_factor; + throw librealsense::invalid_value_exception( rsutils::string::from() + << "Invalid source ID received " << source_id ); + } - _last_timestamp = log_data._timestamp; + rs2_log_severity extended_fw_logs_parser::parse_severity( uint32_t severity ) const + { + return fw_logs::extended_fw_logs_severity_to_rs2_log_severity( severity ); + } - return log_data; + void extended_fw_logs_parser::validate_source_version( int source_id, + const std::string & expected_version, + const std::string & definitions_xml ) + { + std::string path = fw_logs_xml_helper::get_source_parser_file_path( source_id, definitions_xml ); + std::ifstream f( path.c_str() ); + if( f.good() ) + { + std::string xml_contents; + xml_contents.append( std::istreambuf_iterator< char >( f ), std::istreambuf_iterator< char >() ); + std::string file_version = fw_logs_xml_helper::get_file_version( xml_contents ); + if( expected_version != file_version ) + throw librealsense::invalid_value_exception( rsutils::string::from() + << "Source " << _source_id_to_name[source_id] + << " expected version " << expected_version + << " but xml file version is " << file_version ); + } + else + throw librealsense::invalid_value_exception( rsutils::string::from() << "Can't open file " << path ); } } } diff --git a/src/fw-logs/fw-logs-parser.h b/src/fw-logs/fw-logs-parser.h index e27b9f3e30..d70f38df03 100644 --- a/src/fw-logs/fw-logs-parser.h +++ b/src/fw-logs/fw-logs-parser.h @@ -1,31 +1,96 @@ /* License: Apache 2.0. See LICENSE file in root directory. */ -/* Copyright(c) 2019 Intel Corporation. All Rights Reserved. */ +/* Copyright(c) 2024 Intel Corporation. All Rights Reserved. */ + #pragma once + +#include +#include +#include + +#include + #include #include #include -#include "fw-logs-formating-options.h" -#include "fw-log-data.h" namespace librealsense { namespace fw_logs { - class fw_logs_parser : public std::enable_shared_from_this + class fw_logs_parser : public std::enable_shared_from_this< fw_logs_parser > { public: - explicit fw_logs_parser(std::string xml_content); - ~fw_logs_parser(void); + explicit fw_logs_parser( const std::string & definitions_xml ); + virtual ~fw_logs_parser() = default; + + fw_log_data parse_fw_log( const fw_logs_binary_data * fw_log_msg ); + virtual size_t get_log_size( const uint8_t * log ) const; + + protected: + struct structured_binary_data // Common format for legacy and extended binary data formats + { + uint32_t severity = 0; + uint32_t source_id = 0; + uint32_t file_id = 0; + uint32_t module_id = 0; + uint32_t event_id = 0; + uint32_t line = 0; + uint32_t sequence = 0; + uint64_t timestamp = 0; + + std::vector< fw_logs::param_info > params_info; + std::vector< uint8_t > params_blob; + }; + + void initialize_source_formatting_options( const std::pair< const int, std::string > & source, + const std::string & definitions_xml ); + + structured_binary_data structure_common_data( const fw_logs_binary_data * fw_log_msg ) const; + + virtual void structure_timestamp( const fw_logs_binary_data * raw, + structured_binary_data * structured ) const; + virtual void structure_params( const fw_logs_binary_data * raw, + size_t num_of_params, + structured_binary_data * structured ) const; + + virtual const fw_logs_formatting_options & get_format_options_ref( int source_id ) const; + virtual std::string get_source_name( int source_id ) const; + virtual rs2_log_severity parse_severity( uint32_t severity ) const; + + std::map< int, fw_logs_formatting_options > _source_to_formatting_options; + std::map< int, std::string > _source_id_to_name; + }; + + class extended_fw_logs_parser: public fw_logs_parser + { + public: + explicit extended_fw_logs_parser( const std::string & definitions_xml, + const std::map< int, std::string > & expected_versions = {} ); + + size_t get_log_size( const uint8_t * log ) const override; + + command get_start_command() const; + command get_update_command() const; + command get_stop_command() const; - fw_log_data parse_fw_log(const fw_logs_binary_data* fw_log_msg); + protected: + void initialize_source_verbosity_settings( const std::pair< const int, std::string > & source, + const std::string & definitions_xml ); + void validate_source_version( int source_id, + const std::string & expected_version, // Throws if not as expected + const std::string & definitions_xml ); + void structure_timestamp( const fw_logs_binary_data * raw, + structured_binary_data * structured ) const override; + void structure_params( const fw_logs_binary_data * raw, + size_t num_of_params, + structured_binary_data * structured ) const override; - private: - fw_log_data fill_log_data(const fw_logs_binary_data* fw_log_msg); + const fw_logs_formatting_options & get_format_options_ref( int source_id ) const override; + std::string get_source_name( int source_id ) const override; + rs2_log_severity parse_severity( uint32_t severity ) const override; - fw_logs_formating_options _fw_logs_formating_options; - uint64_t _last_timestamp; - const double _timestamp_factor; + fw_logs::extended_log_request _verbosity_settings; }; } } diff --git a/src/fw-logs/fw-logs-xml-helper.cpp b/src/fw-logs/fw-logs-xml-helper.cpp index e5f19f1b37..ecb5cc3a75 100644 --- a/src/fw-logs/fw-logs-xml-helper.cpp +++ b/src/fw-logs/fw-logs-xml-helper.cpp @@ -1,309 +1,411 @@ // License: Apache 2.0. See LICENSE file in root directory. -// Copyright(c) 2019 Intel Corporation. All Rights Reserved. -#include "fw-logs-xml-helper.h" -#include -#include -#include -#include +// Copyright(c) 2024 Intel Corporation. All Rights Reserved. -using namespace std; +#include +#include -namespace librealsense +#include + + +#include "../../third-party/rapidxml/rapidxml_utils.hpp" +using namespace rapidxml; + +namespace librealsense { +namespace fw_logs { + +std::vector< char > string_to_char_buffer( const std::string & xml_content ) { - namespace fw_logs - { - fw_logs_xml_helper::fw_logs_xml_helper(string xml_content) - : _init_done(false), - _xml_content(xml_content) - {} + std::vector< char > buffer; + buffer.resize( xml_content.size() + 2 ); + memcpy( buffer.data(), xml_content.data(), xml_content.size() ); + buffer[xml_content.size()] = '\0'; + buffer[xml_content.size() + 1] = '\0'; - fw_logs_xml_helper::~fw_logs_xml_helper(void) - { - // TODO: Add cleanup code - } + return buffer; +} - bool fw_logs_xml_helper::get_root_node(xml_node<>** node) - { - if (_init_done) - { - *node = _xml_doc.first_node(); - return true; - } +void load_external_xml( xml_document<> * document, std::vector< char > & buffer ) +{ + if( buffer.empty() ) + throw librealsense::invalid_value_exception( "Empty XML content" ); - return false; - } + document->parse< 0 >( buffer.data() ); // Might throw +} - bool fw_logs_xml_helper::try_load_external_xml() - { - try - { - if (_xml_content.empty()) - return false; +xml_node<> * get_first_node( const xml_document<> * document ) +{ + xml_node<> * root = document->first_node(); - _document_buffer.resize(_xml_content.size() + 2); - memcpy(_document_buffer.data(), _xml_content.data(), _xml_content.size()); - _document_buffer[_xml_content.size()] = '\0'; - _document_buffer[_xml_content.size() + 1] = '\0'; - _xml_doc.parse<0>(_document_buffer.data()); + std::string root_name( root->name(), root->name() + root->name_size() ); + if( root_name.compare( "Format" ) != 0 ) + throw librealsense::invalid_value_exception( "XML root should be 'Format'" ); - return true; - } - catch (...) - { - _document_buffer.clear(); - throw; - } + return root->first_node(); +} - return false; +int get_id_attribute( const xml_node<> * node ) +{ + for( xml_attribute<> * attribute = node->first_attribute(); attribute; attribute = attribute->next_attribute() ) + { + std::string attr( attribute->name(), attribute->name() + attribute->name_size() ); + if( attr.compare( "id" ) == 0 ) + { + std::string id_as_str( attribute->value(), attribute->value() + attribute->value_size() ); + return stoi( id_as_str ); } + } + + std::string tag( node->name(), node->name() + node->name_size() ); + throw librealsense::invalid_value_exception( rsutils::string::from() + << "Can't find attribute 'id' in node " << tag ); +} - bool fw_logs_xml_helper::init() +std::string get_name_attribute( const xml_node<> * node ) +{ + for( xml_attribute<> * attribute = node->first_attribute(); attribute; attribute = attribute->next_attribute() ) + { + std::string attr( attribute->name(), attribute->name() + attribute->name_size() ); + if( attr.compare( "Name" ) == 0 ) { - _init_done = try_load_external_xml(); - return _init_done; + std::string attr_value( attribute->value(), attribute->value() + attribute->value_size() ); + return attr_value; } + } + + std::string tag( node->name(), node->name() + node->name_size() ); + throw librealsense::invalid_value_exception( rsutils::string::from() + << "Can't find attribute 'Name' in node " << tag ); +} - bool fw_logs_xml_helper::build_log_meta_data(fw_logs_formating_options* log_meta_data) +xml_node<> * get_source_node( int source_id, const xml_document<> * document ) +{ + // Loop through all elements to find 'Source' nodes, when found look for id attribure and compare to requested id. + for( xml_node<> * node = get_first_node( document ); node; node = node->next_sibling() ) + { + std::string tag( node->name(), node->name() + node->name_size() ); + if( tag.compare( "Source" ) == 0 ) { - xml_node<>* xml_root_node_list; + if( source_id == get_id_attribute( node ) ) + return node; + } + } - if (!init()) - return false; + throw librealsense::invalid_value_exception( rsutils::string::from() + << "Did not find 'Source' node with id " << source_id ); +} - if (!get_root_node(&xml_root_node_list)) +std::string get_file_path( const xml_node<> * source_node ) +{ + // Loop through all elements to find 'File' node, when found look for 'Path' attribure. + for( xml_node<> * node = source_node->first_node(); node; node = node->next_sibling() ) + { + std::string tag( node->name(), node->name() + node->name_size() ); + if( tag.compare( "File" ) == 0 ) + { + for( xml_attribute<> * attribute = node->first_attribute(); attribute; attribute = attribute->next_attribute() ) { - return false; + std::string attr( attribute->name(), attribute->name() + attribute->name_size() ); + if( attr.compare( "Path" ) == 0 ) + { + std::string path( attribute->value(), attribute->value() + attribute->value_size() ); + return path; + } } + } + } - string root_name(xml_root_node_list->name(), xml_root_node_list->name() + xml_root_node_list->name_size()); - - // check if Format is the first root name. - if (root_name.compare("Format") != 0) - return false; - - xml_node<>* events_node = xml_root_node_list->first_node(); + return {}; +} +std::map< int, std::string > fw_logs_xml_helper::get_listed_sources( const std::string & definitions_xml ) +{ + std::map< int, std::string > source_ids_to_names; - if (!build_meta_data_structure(events_node, log_meta_data)) - return false; + std::vector< char > buffer = string_to_char_buffer( definitions_xml ); + xml_document<> document; + load_external_xml( &document, buffer ); - return true; + // Loop through all elements to find 'Source' nodes, when found look for id attribure and compare to requested id. + for( xml_node<> * node = get_first_node( &document ); node; node = node->next_sibling() ) + { + std::string tag( node->name(), node->name() + node->name_size() ); + if( tag.compare( "Source" ) == 0 ) + { + source_ids_to_names.insert( { get_id_attribute( node ), get_name_attribute( node ) } ); } + } + return source_ids_to_names; +} - bool fw_logs_xml_helper::build_meta_data_structure(xml_node<>* xml_node_list_of_events, fw_logs_formating_options* logs_formating_options) - { - node_type res = none; - int id{}; - int num_of_params{}; - string line; - // loop through all elements in the Format. - for (xml_node<>* node = xml_node_list_of_events; node; node = node->next_sibling()) - { - line.clear(); - res = get_next_node(node, &id, &num_of_params, &line); - if (res == event) - { - fw_log_event log_event(num_of_params, line); - logs_formating_options->_fw_logs_event_list.insert(pair(id, log_event)); - } - else if (res == file) - { - logs_formating_options->_fw_logs_file_names_list.insert(kvp(id, line)); - } - else if (res == thread) - { - logs_formating_options->_fw_logs_thread_names_list.insert(kvp(id, line)); - } - else if (res == enums) - { - for (xml_node<>* enum_node = node->first_node(); enum_node; enum_node = enum_node->next_sibling()) - { - for (xml_attribute<>* attribute = enum_node->first_attribute(); attribute; attribute = attribute->next_attribute()) - { - string attr(attribute->name(), attribute->name() + attribute->name_size()); - if (attr.compare("Name") == 0) - { - string name_attr_str(attribute->value(), attribute->value() + attribute->value_size()); - vector xml_kvp; - - for (xml_node<>* enum_value_node = enum_node->first_node(); enum_value_node; enum_value_node = enum_value_node->next_sibling()) - { - int key = 0; - string value_str; - for (xml_attribute<>* attribute = enum_value_node->first_attribute(); attribute; attribute = attribute->next_attribute()) - { - string attr(attribute->name(), attribute->name() + attribute->name_size()); - if (attr.compare("Value") == 0) - { - value_str = std::string(attribute->value(), attribute->value() + attribute->value_size()); - } - if (attr.compare("Key") == 0) - { - try - { - auto key_str = std::string(attribute->value()); - key = std::stoi(key_str); - } - catch (...) {} - } - } - xml_kvp.push_back(std::make_pair(key, value_str)); - } - logs_formating_options->_fw_logs_enum_names_list.insert(pair>(name_attr_str, xml_kvp)); - } - } - } - } - else - return false; - } +std::string fw_logs_xml_helper::get_source_parser_file_path( int source_id, const std::string & definitions_xml ) +{ + std::vector< char > buffer = string_to_char_buffer( definitions_xml ); + xml_document<> document; + load_external_xml( &document, buffer ); + xml_node<> * source_node = get_source_node( source_id, &document ); - return true; - } + std::string path = get_file_path( source_node ); + if( path.empty() ) + throw librealsense::invalid_value_exception( rsutils::string::from() + << "Did not find 'File' attribute for source " << source_id ); - fw_logs_xml_helper::node_type fw_logs_xml_helper::get_next_node(xml_node<>* node, int* id, int* num_of_params, string* line) - { + return path; +} - string tag(node->name(), node->name() + node->name_size()); +int get_verbosity_attribute( const xml_node<> * node ) +{ + for( xml_attribute<> * attribute = node->first_attribute(); attribute; attribute = attribute->next_attribute() ) + { + std::string attr( attribute->name(), attribute->name() + attribute->name_size() ); + if( attr.compare( "verbosity" ) == 0 ) + { + std::string as_str( attribute->value(), attribute->value() + attribute->value_size() ); + if( as_str.empty() ) + throw librealsense::invalid_value_exception( "Verbosity level cannot be empty" ); - if (tag.compare("Event") == 0) - { - if (get_event_node(node, id, num_of_params, line)) - return event; - } - else if (tag.compare("File") == 0) - { - if (get_file_node(node, id, line)) - return file; - } - else if (tag.compare("Thread") == 0) + if( std::isdigit( as_str[0] ) ) { - if (get_thread_node(node, id, line)) - return thread; + size_t converted_chars = 0; + int as_int = stoi( as_str, &converted_chars ); + if( converted_chars == as_str.size() ) // String was a number and converted successfully + return as_int; + else + throw librealsense::invalid_value_exception( rsutils::string::from() + << "Bad verbosity level " << as_str ); } - else if (tag.compare("Enums") == 0) + + // Convert verbosity string tokens to number + // Verbosity level, treated as a bitmask: 0-NONE, 1-VERBOSE, 2-DEBUG, 4-INFO, 8-WARNING, 16-ERROR, 32-FATAL + // To request INFO + WARNING + ERROR type messages the user shall request the following INFO|WARNING|ERROR + int i = 0; + size_t sub_start = 0; + size_t delim_pos = 0; + while( delim_pos != std::string::npos ) { - return enums; + delim_pos = as_str.find( "|", sub_start ); + std::string sub = as_str.substr( sub_start, delim_pos - sub_start ); + if( sub == "NONE" ) + i |= 0; + else if( sub == "VERBOSE" ) + i |= 1; + else if( sub == "DEBUG" ) + i |= 2; + else if( sub == "INFO" ) + i |= 4; + else if( sub == "WARNING" ) + i |= 8; + else if( sub == "ERROR" ) + i |= 16; + else if( sub == "FATAL" ) + i |= 32; + else + throw librealsense::invalid_value_exception( rsutils::string::from() << "Illegal verbosity " << sub + << ". Expecting NONE, VERBOSE, DEBUG, INFO, WARNING, ERROR or FATAL" ); + sub_start = delim_pos + 1; } - return none; + + return i; } + } + + std::string tag( node->name(), node->name() + node->name_size() ); + throw librealsense::invalid_value_exception( rsutils::string::from() + << "Can't find attribute 'verbosity' in node " << tag ); +} + +std::map< int, int > +fw_logs_xml_helper::get_source_module_verbosity( int source_id, const std::string & definitions_xml ) +{ + std::vector< char > buffer = string_to_char_buffer( definitions_xml ); + xml_document<> document; + load_external_xml( &document, buffer ); + xml_node<> * source_node = get_source_node( source_id, &document ); - bool fw_logs_xml_helper::get_enum_name_node(xml_node<>* node_file, int* thread_id, string* enum_name) + std::map< int, int > module_id_verbosity; + for( xml_node<> * node = source_node->first_node(); node; node = node->next_sibling() ) + { + std::string tag( node->name(), node->name() + node->name_size() ); + if( tag.compare( "Module" ) == 0 ) { - for (xml_attribute<>* attribute = node_file->first_attribute(); attribute; attribute = attribute->next_attribute()) - { - string attr(attribute->name(), attribute->name() + attribute->name_size()); + module_id_verbosity.insert( { get_id_attribute( node ), get_verbosity_attribute( node ) } ); + } + } - if (attr.compare("Name") == 0) - { - string name_attr_str(attribute->value(), attribute->value() + attribute->value_size()); - *enum_name = name_attr_str; - continue; - } - else - return false; - } + return module_id_verbosity; +} - return true; +std::pair< int, std::string > get_event_data( xml_node<> * node ) +{ + int num_of_args = -1; + std::string format; + bool format_found = false; + for( xml_attribute<> * attribute = node->first_attribute(); attribute; attribute = attribute->next_attribute() ) + { + std::string attr( attribute->name(), attribute->name() + attribute->name_size() ); + if( attr.compare( "numberOfArguments" ) == 0 ) + { + std::string num_of_args_as_str( attribute->value(), attribute->value() + attribute->value_size() ); + num_of_args = stoi( num_of_args_as_str ); + continue; } - bool fw_logs_xml_helper::get_enum_value_node(xml_node<>* node_file, int* thread_id, string* enum_name) + else if( attr.compare( "format" ) == 0 ) { - for (xml_attribute<>* attribute = node_file->first_attribute(); attribute; attribute = attribute->next_attribute()) - { - string attr(attribute->name(), attribute->name() + attribute->name_size()); + format_found = true; // Some users prefer an empty format, so can't check based on empty string, using flag + format.append( attribute->value(), attribute->value() + attribute->value_size() ); + continue; + } + } - if (attr.compare("Value") == 0) - { - string name_attr_str(attribute->value(), attribute->value() + attribute->value_size()); - *enum_name = name_attr_str; - continue; - } - else - return false; - } + if( num_of_args < 0 || !format_found ) + throw librealsense::invalid_value_exception( rsutils::string::from() + << "Can't find event 'numberOfArguments' or 'format'" ); - return true; + return { num_of_args, format }; +} + +std::unordered_map< int, std::pair< int, std::string > > +fw_logs_xml_helper::get_events( const std::string & parser_contents ) +{ + std::vector< char > buffer = string_to_char_buffer( parser_contents ); + xml_document<> document; + load_external_xml( &document, buffer ); + + std::unordered_map< int, std::pair< int, std::string > > event_id_to_num_of_args_and_format; + for( xml_node<> * node = get_first_node( &document ); node; node = node->next_sibling() ) + { + std::string tag( node->name(), node->name() + node->name_size() ); + if( tag.compare( "Event" ) == 0 ) + event_id_to_num_of_args_and_format.insert( { get_id_attribute( node ), get_event_data( node ) } ); + } + + return event_id_to_num_of_args_and_format; +} + +std::unordered_map< int, std::string > get_id_to_names( const std::string & parser_contents, const std::string tag ) +{ + std::vector< char > buffer = string_to_char_buffer( parser_contents ); + xml_document<> document; + load_external_xml( &document, buffer ); + + std::unordered_map< int, std::string > ids_to_names; + for( xml_node<> * node = get_first_node( &document ); node; node = node->next_sibling() ) + { + std::string attr( node->name(), node->name() + node->name_size() ); + if( attr == tag ) + { + ids_to_names.insert( { get_id_attribute( node ), get_name_attribute( node ) } ); } - bool fw_logs_xml_helper::get_thread_node(xml_node<>* node_file, int* thread_id, string* thread_name) + } + + return ids_to_names; +} + +std::unordered_map< int, std::string > fw_logs_xml_helper::get_files( const std::string & parser_contents ) +{ + return get_id_to_names( parser_contents, "File" ); +} + +std::unordered_map< int, std::string > fw_logs_xml_helper::get_modules( const std::string & parser_contents ) +{ + return get_id_to_names( parser_contents, "Module" ); +} + +std::unordered_map< int, std::string > fw_logs_xml_helper::get_threads( const std::string & parser_contents ) +{ + return get_id_to_names( parser_contents, "Thread" ); +} + +std::vector< std::pair< int, std::string > > get_enum_values( xml_node<> * enum_node ) +{ + std::vector< std::pair< int, std::string > > values_to_descriptions; + + for( xml_node<> * node = enum_node->first_node(); node; node = node->next_sibling() ) + { + std::string tag( node->name(), node->name() + node->name_size() ); + if( tag.compare( "EnumValue" ) == 0 ) { - for (xml_attribute<>* attribute = node_file->first_attribute(); attribute; attribute = attribute->next_attribute()) + int key = -1; + std::string value; + for( xml_attribute<> * attribute = node->first_attribute(); attribute; attribute = attribute->next_attribute() ) { - string attr(attribute->name(), attribute->name() + attribute->name_size()); - - if (attr.compare("id") == 0) + std::string attr( attribute->name(), attribute->name() + attribute->name_size() ); + if( attr.compare( "Key" ) == 0 ) { - string id_attr_str(attribute->value(), attribute->value() + attribute->value_size()); - *thread_id = stoi(id_attr_str); - continue; + std::string key_as_str( attribute->value(), attribute->value() + attribute->value_size() ); + key = stoi( key_as_str ); } - else if (attr.compare("Name") == 0) + else if( attr.compare( "Value" ) == 0 ) { - string name_attr_str(attribute->value(), attribute->value() + attribute->value_size()); - *thread_name = name_attr_str; - continue; + value.append( attribute->value(), attribute->value() + attribute->value_size() ); } - else - return false; } - return true; + if( key < 0 || value.empty() ) + throw librealsense::invalid_value_exception( rsutils::string::from() + << "Can't find EnumValue 'Key' or 'Value' for enum " << tag ); + + values_to_descriptions.push_back( { key, value } ); } + } + + return values_to_descriptions; +} + +std::unordered_map< std::string, std::vector< std::pair< int, std::string > > > +fw_logs_xml_helper::get_enums( const std::string & parser_contents ) +{ + std::vector< char > buffer = string_to_char_buffer( parser_contents ); + xml_document<> document; + load_external_xml( &document, buffer ); - bool fw_logs_xml_helper::get_file_node(xml_node<>* node_file, int* file_id, string* file_name) + std::unordered_map< std::string, std::vector< std::pair< int, std::string > > > enum_names_to_literals; + for( xml_node<> * node = get_first_node( &document ); node; node = node->next_sibling() ) + { + std::string tag( node->name(), node->name() + node->name_size() ); + if( tag.compare( "Enums" ) == 0 ) { - for (xml_attribute<>* attribute = node_file->first_attribute(); attribute; attribute = attribute->next_attribute()) + for( xml_node<> * enum_node = node->first_node(); enum_node; enum_node = enum_node->next_sibling() ) { - string attr(attribute->name(), attribute->name() + attribute->name_size()); - - if (attr.compare("id") == 0) + for( xml_attribute<> * attribute = enum_node->first_attribute(); attribute; attribute = attribute->next_attribute() ) { - string id_attr_str(attribute->value(), attribute->value() + attribute->value_size()); - *file_id = stoi(id_attr_str); - continue; - } - else if (attr.compare("Name") == 0) - { - string name_attr_str(attribute->value(), attribute->value() + attribute->value_size()); - *file_name = name_attr_str; - continue; + std::string enum_name; + std::string attr( attribute->name(), attribute->name() + attribute->name_size() ); + if( attr.compare( "Name" ) == 0 ) + enum_name.append( attribute->value(), attribute->value() + attribute->value_size() ); + + std::vector< std::pair< int, std::string > > values = get_enum_values( enum_node ); + + enum_names_to_literals.insert( { enum_name, values } ); } - else - return false; } - return true; } + } - bool fw_logs_xml_helper::get_event_node(xml_node<>* node_event, int* event_id, int* num_of_params, string* line) - { - for (xml_attribute<>* attribute = node_event->first_attribute(); attribute; attribute = attribute->next_attribute()) - { - string attr(attribute->name(), attribute->name() + attribute->name_size()); + return enum_names_to_literals; +} - if (attr.compare("id") == 0) - { - string id_attr_str(attribute->value(), attribute->value() + attribute->value_size()); - *event_id = stoi(id_attr_str); - continue; - } - else if (attr.compare("numberOfArguments") == 0) - { - string num_of_args_attr_str(attribute->value(), attribute->value() + attribute->value_size()); - *num_of_params = stoi(num_of_args_attr_str); - continue; - } - else if (attr.compare("format") == 0) - { - string format_attr_str(attribute->value(), attribute->value() + attribute->value_size()); - *line = format_attr_str; - continue; - } - else - return false; - } - return true; +std::string fw_logs_xml_helper::get_file_version( const std::string & xml_file_contents ) +{ + std::vector< char > buffer = string_to_char_buffer( xml_file_contents ); + xml_document<> document; + load_external_xml( &document, buffer ); + xml_node<> * root = document.first_node(); + + for( xml_attribute<> * attribute = root->first_attribute(); attribute; attribute = attribute->next_attribute() ) + { + std::string attr( attribute->name(), attribute->name() + attribute->name_size() ); + if( attr.compare( "version" ) == 0 ) + { + std::string file_version( attribute->value(), attribute->value() + attribute->value_size() ); + return file_version; } } + + throw librealsense::invalid_value_exception( "Can't find file 'version' attribute" ); } + +} // namespace fw_logs +} // namespace librealsense diff --git a/src/fw-logs/fw-logs-xml-helper.h b/src/fw-logs/fw-logs-xml-helper.h index 39b6c963d5..fc02e80505 100644 --- a/src/fw-logs/fw-logs-xml-helper.h +++ b/src/fw-logs/fw-logs-xml-helper.h @@ -1,48 +1,47 @@ /* License: Apache 2.0. See LICENSE file in root directory. */ -/* Copyright(c) 2019 Intel Corporation. All Rights Reserved. */ +/* Copyright(c) 2024 Intel Corporation. All Rights Reserved. */ + #pragma once -#include "../../third-party/rapidxml/rapidxml_utils.hpp" -#include "fw-logs-formating-options.h" -using namespace rapidxml; +#include +#include +#include +#include + namespace librealsense { namespace fw_logs { + // This class encapsulates + // 1. The XML file structure + // 2. The actual XML parsing implementation (currently uses rapidxml) class fw_logs_xml_helper { public: - enum node_type - { - event, - file, - thread, - enums, - none - }; - - fw_logs_xml_helper(std::string xml_content); - ~fw_logs_xml_helper(void); - - bool build_log_meta_data(fw_logs_formating_options* logs_formating_options); - - private: - bool init(); - bool build_meta_data_structure(xml_node<>* xml_node_list_of_events, fw_logs_formating_options* logs_formating_options); - node_type get_next_node(xml_node<>* xml_node_list_of_events, int* id, int* num_of_params, std::string* line); - bool get_thread_node(xml_node<>* node_file, int* thread_id, std::string* thread_name); - bool get_event_node(xml_node<>* node_event, int* event_id, int* num_of_params, std::string* line); - bool get_enum_name_node(xml_node<>* node_file, int* thread_id, std::string* thread_name); - bool get_enum_value_node(xml_node<>* node_file, int* thread_id, std::string* enum_name); - bool get_file_node(xml_node<>* node_file, int* file_id, std::string* file_name); - bool get_root_node(xml_node<>** node); - bool try_load_external_xml(); - - bool _init_done; - std::string _xml_content; - xml_document<> _xml_doc; - std::vector _document_buffer; + // Returns a list of all sources in the definitions file, mapped by id to name. + static std::map< int, std::string > get_listed_sources( const std::string & definitions_xml ); + + // Returns parser options file path of the requested source + static std::string get_source_parser_file_path( int source_id, const std::string & definitions_xml ); + + // Returns a mapping of source module IDs to their requested verbosity level. + static std::map< int, int > get_source_module_verbosity( int source_id, const std::string & definitions_xml ); + + // Returns a mapping of event IDs to their number of arguments and format pairs. + static std::unordered_map< int, std::pair< int, std::string > > get_events( const std::string & parser_contents ); + + // The following functions return a mapping of element IDs to their names + static std::unordered_map< int, std::string > get_files( const std::string & parser_contents ); + static std::unordered_map< int, std::string > get_modules( const std::string & parser_contents ); + static std::unordered_map< int, std::string > get_threads( const std::string & parser_contents ); + + // Returns a mapping of enum names to the enum litterals value and meaning + static std::unordered_map< std::string, std::vector< std::pair< int, std::string > > > + get_enums( const std::string & parser_contents ); + + // Returns the XML file version. + static std::string get_file_version( const std::string & xml_file_contents ); }; } } diff --git a/src/fw-logs/fw-string-formatter.cpp b/src/fw-logs/fw-string-formatter.cpp index 0946ebb385..23bfb4fca3 100644 --- a/src/fw-logs/fw-string-formatter.cpp +++ b/src/fw-logs/fw-string-formatter.cpp @@ -2,13 +2,12 @@ // Copyright(c) 2024 Intel Corporation. All Rights Reserved. #include "fw-string-formatter.h" -#include "fw-logs-formating-options.h" +#include "fw-logs-formatting-options.h" + +#include #include #include -#include -#include -#include #include using namespace std; @@ -17,74 +16,72 @@ namespace librealsense { namespace fw_logs { - fw_string_formatter::fw_string_formatter(std::unordered_map> enums) - :_enums(enums) - { - } - - - fw_string_formatter::~fw_string_formatter(void) + fw_string_formatter::fw_string_formatter( std::unordered_map< std::string, std::vector< kvp > > enums ) + : _enums(enums) { } - bool fw_string_formatter::generate_message(const string& source, size_t num_of_params, const uint32_t* params, string* dest) + std::string fw_string_formatter::generate_message( const string & source, + const std::vector< param_info > & params_info, + const std::vector< uint8_t > & params_blob ) { - map exp_replace_map; - map enum_replace_map; + map< string, string > exp_replace_map; + map< string, int > enum_replace_map; - if (params == nullptr && num_of_params > 0) return false; + if( params_info.size() > 0 && params_blob.empty() ) + return source; - for (size_t i = 0; i < num_of_params; i++) + for( size_t i = 0; i < params_info.size(); i++ ) { - string regular_exp[4]; - string replacement[4]; - stringstream st_regular_exp[4]; - stringstream st_replacement[4]; - - st_regular_exp[0] << "\\{\\b(" << i << ")\\}"; - regular_exp[0] = st_regular_exp[0].str(); - - st_replacement[0] << params[i]; - replacement[0] = st_replacement[0].str(); - - exp_replace_map[regular_exp[0]] = replacement[0]; + // Parsing 4 expression types - {0} / {1:x} / {2:f} / {3,Enum} + stringstream ss_regular_exp[4]; + stringstream ss_replacement[4]; + string param_as_string = convert_param_to_string( params_info[i], + params_blob.data() + params_info[i].offset ); + ss_regular_exp[0] << "\\{\\b(" << i << ")\\}"; + exp_replace_map[ss_regular_exp[0].str()] = param_as_string; - st_regular_exp[1] << "\\{\\b(" << i << "):x\\}"; - regular_exp[1] = st_regular_exp[1].str(); - - st_replacement[1] << hex << setw(2) << setfill('0') << params[i]; - replacement[1] = st_replacement[1].str(); - - exp_replace_map[regular_exp[1]] = replacement[1]; - - st_regular_exp[2] << "\\{\\b(" << i << "):f\\}"; - regular_exp[2] = st_regular_exp[2].str(); - // Parse int32_t as 4 raw bytes of float - float tmp = *reinterpret_cast< const float * >( ¶ms[i] ); - if( std::isfinite( tmp ) ) - st_replacement[2] << tmp; - else + if( is_integral( params_info[i] ) ) { - LOG_ERROR( "Expecting a number, received infinite or NaN" ); - st_replacement[2] << "0x" << hex << setw( 2 ) << setfill( '0' ) << params[i]; - } - replacement[2] = st_replacement[2].str(); - exp_replace_map[regular_exp[2]] = replacement[2]; + // Print as hexadecimal number, assumes parameter was an integer number. + ss_regular_exp[1] << "\\{\\b(" << i << "):x\\}"; + ss_replacement[1] << hex << setw( 2 ) << setfill( '0' ) << std::stoull( param_as_string ); + exp_replace_map[ss_regular_exp[1].str()] = ss_replacement[1].str(); + // Legacy format - parse parameter as 4 raw bytes of float. Parameter can be uint16_t or uint32_t. + if( params_info[i].size <= sizeof( uint32_t ) ) + { + ss_regular_exp[2] << "\\{\\b(" << i << "):f\\}"; + uint32_t as_int32 = 0; + memcpy( &as_int32, params_blob.data() + params_info[i].offset, params_info[i].size ); + float as_float = *reinterpret_cast< const float * >( &as_int32 ); + if( std::isfinite( as_float ) ) + ss_replacement[2] << as_float; + else + { + // Values for other regular expresions can be NaN when converted to float. + // Prepare replacement as hex value (will probably not be used because format is not with :f). + ss_replacement[2] << "0x" << hex << setw( 2 ) << setfill( '0' ) << as_int32; + } + exp_replace_map[ss_regular_exp[2].str()] = ss_replacement[2].str(); + } - st_regular_exp[3] << "\\{\\b(" << i << "),[a-zA-Z]+\\}"; - regular_exp[3] = st_regular_exp[3].str(); - - enum_replace_map[regular_exp[3]] = params[i]; + // enum values are mapped by int (kvp) but we don't know if this parameter is related to enum, using + // unsigned long long because unrelated arguments can overflow int + ss_regular_exp[3] << "\\{\\b(" << i << "),[a-zA-Z]+\\}"; + enum_replace_map[ss_regular_exp[3].str()] = static_cast< int >( std::stoull( param_as_string ) ); + } } - return replace_params(source, exp_replace_map, enum_replace_map, dest); + return replace_params( source, exp_replace_map, enum_replace_map ); } - bool fw_string_formatter::replace_params(const string& source, const map& exp_replace_map, const map& enum_replace_map, string* dest) + std::string fw_string_formatter::replace_params( const string & source, + const map< string, string > & exp_replace_map, + const map< string, int > & enum_replace_map ) { - string source_temp(source); + string source_temp( source ); for (auto exp_replace_it = exp_replace_map.begin(); exp_replace_it != exp_replace_map.end(); exp_replace_it++) { @@ -101,8 +98,6 @@ namespace librealsense std::smatch m; std::regex_search(source_temp, m, std::regex(e)); - string enum_name; - string st_regular_exp = "[a-zA-Z]+"; regex e1(st_regular_exp); @@ -117,7 +112,7 @@ namespace librealsense for (size_t exp = 0; exp < m1.size(); exp++) { - enum_name = m1[exp]; + string enum_name = m1[exp]; if (_enums.size() > 0 && _enums.find(enum_name) != _enums.end()) { auto vec = _enums[enum_name]; @@ -135,7 +130,7 @@ namespace librealsense s << "Protocol Error recognized!\nImproper log message received: " << source_temp << ", invalid parameter: " << exp_replace_it->second << ".\n The range of supported values is \n"; for_each(vec.begin(), vec.end(), [&s](kvp& entry) { s << entry.first << ":" << entry.second << " ,"; }); - std::cout << s.str().c_str() << std::endl;; + LOG_WARNING( s.str() ); } source_temp = destTemp; } @@ -143,8 +138,55 @@ namespace librealsense } } - *dest = source_temp; + return source_temp; + } + + std::string fw_string_formatter::convert_param_to_string( const param_info & info, const uint8_t * param_start ) const + { + switch( info.type ) + { + case param_type::STRING: + { + // Using stringstream to remove the terminating null character. We use this as regex replacement string, + // and having '\0' here will cause a terminating character in the middle of the result string. + stringstream str; + str << param_start; + return str.str(); + } + case param_type::UINT8: + return std::to_string( *reinterpret_cast< const uint8_t * >( param_start ) ); + case param_type::SINT8: + return std::to_string( *reinterpret_cast< const int8_t * >( param_start ) ); + case param_type::UINT16: + return std::to_string( *reinterpret_cast< const uint16_t * >( param_start ) ); + case param_type::SINT16: + return std::to_string( *reinterpret_cast< const int16_t * >( param_start ) ); + case param_type::SINT32: + return std::to_string( *reinterpret_cast< const int32_t * >( param_start ) ); + case param_type::UINT32: + return std::to_string( *reinterpret_cast< const uint32_t * >( param_start ) ); + case param_type::SINT64: + return std::to_string( *reinterpret_cast< const int64_t * >( param_start ) ); + case param_type::UINT64: + return std::to_string( *reinterpret_cast< const uint64_t * >( param_start ) ); + case param_type::FLOAT: + return std::to_string( *reinterpret_cast< const float * >( param_start ) ); + case param_type::DOUBLE: + return std::to_string( *reinterpret_cast< const double * >( param_start ) ); + default: + throw librealsense::invalid_value_exception( rsutils::string::from() + << "Unsupported parameter type " + << static_cast< uint8_t >( info.type ) ); + } + } + + bool fw_string_formatter::is_integral( const param_info & info ) const + { + if( info.type == param_type::STRING || info.type == param_type::FLOAT || info.type == param_type::DOUBLE ) + return false; + return true; } + } } diff --git a/src/fw-logs/fw-string-formatter.h b/src/fw-logs/fw-string-formatter.h index 7759e5b7ea..1422739429 100644 --- a/src/fw-logs/fw-string-formatter.h +++ b/src/fw-logs/fw-string-formatter.h @@ -1,6 +1,10 @@ /* License: Apache 2.0. See LICENSE file in root directory. */ -/* Copyright(c) 2019 Intel Corporation. All Rights Reserved. */ +/* Copyright(c) 2024 Intel Corporation. All Rights Reserved. */ + #pragma once + +#include + #include #include #include @@ -11,16 +15,43 @@ namespace librealsense { namespace fw_logs { + enum class param_type : uint8_t + { + STRING = 1, + UINT8 = 2, + SINT8 = 3, + UINT16 = 4, + SINT16 = 5, + SINT32 = 6, + UINT32 = 7, + SINT64 = 8, + UINT64 = 9, + FLOAT = 10, + DOUBLE = 11 + }; + + struct param_info + { + uint16_t offset; // Offset in the params blob. Note - original raw message offset is from start of message. + param_type type; // Built in type, enumerated + uint8_t size; + }; + class fw_string_formatter { public: - fw_string_formatter(std::unordered_map>> enums); - ~fw_string_formatter(void); + fw_string_formatter( std::unordered_map< std::string, std::vector< std::pair< int, std::string > > > enums ); - bool generate_message(const std::string& source, size_t num_of_params, const uint32_t* params, std::string* dest); + std::string generate_message( const std::string & source, + const std::vector< param_info > & params_info, + const std::vector< uint8_t > & params_blob ); private: - bool replace_params(const std::string& source, const std::map& exp_replace_map, const std::map& enum_replace_map, std::string* dest); + std::string replace_params( const std::string & source, + const std::map< std::string, std::string > & exp_replace_map, + const std::map< std::string, int > & enum_replace_map ); + std::string convert_param_to_string( const param_info & info, const uint8_t * param_start ) const; + bool is_integral( const param_info & info ) const; std::unordered_map>> _enums; }; diff --git a/src/realsense.def b/src/realsense.def index 9ba6890cdb..41357ebe6d 100644 --- a/src/realsense.def +++ b/src/realsense.def @@ -396,6 +396,8 @@ EXPORTS rs2_get_calibration_table rs2_set_calibration_table + rs2_start_collecting_fw_logs + rs2_stop_collecting_fw_logs rs2_create_fw_log_message rs2_delete_fw_log_message rs2_get_number_of_fw_logs @@ -412,6 +414,7 @@ EXPORTS rs2_get_fw_log_parsed_message rs2_get_fw_log_parsed_file_name rs2_get_fw_log_parsed_thread_name + rs2_get_fw_log_parsed_module_name rs2_get_fw_log_parsed_severity rs2_get_fw_log_parsed_line rs2_get_fw_log_parsed_timestamp diff --git a/src/rs.cpp b/src/rs.cpp index 71e9ab34da..4a8421d058 100644 --- a/src/rs.cpp +++ b/src/rs.cpp @@ -3564,6 +3564,24 @@ void rs2_load_json(rs2_device* dev, const void* json_content, unsigned content_s } HANDLE_EXCEPTIONS_AND_RETURN(, dev, json_content, content_size) +void rs2_start_collecting_fw_logs( rs2_device * dev, rs2_error ** error ) BEGIN_API_CALL +{ + VALIDATE_NOT_NULL( dev ); + auto fw_logger = VALIDATE_INTERFACE( dev->device, librealsense::firmware_logger_extensions ); + + fw_logger->start(); +} +HANDLE_EXCEPTIONS_AND_RETURN(, dev ) + +void rs2_stop_collecting_fw_logs( rs2_device * dev, rs2_error ** error ) BEGIN_API_CALL +{ + VALIDATE_NOT_NULL( dev ); + auto fw_logger = VALIDATE_INTERFACE( dev->device, librealsense::firmware_logger_extensions ); + + fw_logger->stop(); +} +HANDLE_EXCEPTIONS_AND_RETURN(, dev ) + rs2_firmware_log_message* rs2_create_fw_log_message(rs2_device* dev, rs2_error** error)BEGIN_API_CALL { VALIDATE_NOT_NULL(dev); @@ -3692,49 +3710,57 @@ NOEXCEPT_RETURN(, fw_log_parsed_msg) const char* rs2_get_fw_log_parsed_message(rs2_firmware_log_parsed_message* fw_log_parsed_msg, rs2_error** error) BEGIN_API_CALL { VALIDATE_NOT_NULL(fw_log_parsed_msg); - return fw_log_parsed_msg->firmware_log_parsed->get_message().c_str(); + return fw_log_parsed_msg->firmware_log_parsed->message.c_str(); } HANDLE_EXCEPTIONS_AND_RETURN(nullptr, fw_log_parsed_msg) const char* rs2_get_fw_log_parsed_file_name(rs2_firmware_log_parsed_message* fw_log_parsed_msg, rs2_error** error) BEGIN_API_CALL { VALIDATE_NOT_NULL(fw_log_parsed_msg); - return fw_log_parsed_msg->firmware_log_parsed->get_file_name().c_str(); + return fw_log_parsed_msg->firmware_log_parsed->file_name.c_str(); } HANDLE_EXCEPTIONS_AND_RETURN(nullptr, fw_log_parsed_msg) const char* rs2_get_fw_log_parsed_thread_name(rs2_firmware_log_parsed_message* fw_log_parsed_msg, rs2_error** error) BEGIN_API_CALL { VALIDATE_NOT_NULL(fw_log_parsed_msg); - return fw_log_parsed_msg->firmware_log_parsed->get_thread_name().c_str(); + return fw_log_parsed_msg->firmware_log_parsed->source_name.c_str(); +} +HANDLE_EXCEPTIONS_AND_RETURN(nullptr, fw_log_parsed_msg) + +const char* rs2_get_fw_log_parsed_module_name(rs2_firmware_log_parsed_message* fw_log_parsed_msg, rs2_error** error) BEGIN_API_CALL +{ + VALIDATE_NOT_NULL(fw_log_parsed_msg); + return fw_log_parsed_msg->firmware_log_parsed->module_name.c_str(); } HANDLE_EXCEPTIONS_AND_RETURN(nullptr, fw_log_parsed_msg) + rs2_log_severity rs2_get_fw_log_parsed_severity(rs2_firmware_log_parsed_message* fw_log_parsed_msg, rs2_error** error) BEGIN_API_CALL { VALIDATE_NOT_NULL(fw_log_parsed_msg); - return fw_log_parsed_msg->firmware_log_parsed->get_severity(); + return fw_log_parsed_msg->firmware_log_parsed->severity; } HANDLE_EXCEPTIONS_AND_RETURN(RS2_LOG_SEVERITY_NONE, fw_log_parsed_msg) unsigned int rs2_get_fw_log_parsed_line(rs2_firmware_log_parsed_message* fw_log_parsed_msg, rs2_error** error) BEGIN_API_CALL { VALIDATE_NOT_NULL(fw_log_parsed_msg); - return fw_log_parsed_msg->firmware_log_parsed->get_line(); + return fw_log_parsed_msg->firmware_log_parsed->line; } HANDLE_EXCEPTIONS_AND_RETURN(0, fw_log_parsed_msg) unsigned int rs2_get_fw_log_parsed_timestamp(rs2_firmware_log_parsed_message* fw_log_parsed_msg, rs2_error** error) BEGIN_API_CALL { VALIDATE_NOT_NULL(fw_log_parsed_msg); - return fw_log_parsed_msg->firmware_log_parsed->get_timestamp(); + return fw_log_parsed_msg->firmware_log_parsed->timestamp; } HANDLE_EXCEPTIONS_AND_RETURN(0, fw_log_parsed_msg) unsigned int rs2_get_fw_log_parsed_sequence_id(rs2_firmware_log_parsed_message* fw_log_parsed_msg, rs2_error** error) BEGIN_API_CALL { VALIDATE_NOT_NULL(fw_log_parsed_msg); - return fw_log_parsed_msg->firmware_log_parsed->get_sequence_id(); + return fw_log_parsed_msg->firmware_log_parsed->sequence; } HANDLE_EXCEPTIONS_AND_RETURN(0, fw_log_parsed_msg) diff --git a/tools/fw-logger/rs-fw-logger.cpp b/tools/fw-logger/rs-fw-logger.cpp index 42eccb09e5..2745df77ce 100644 --- a/tools/fw-logger/rs-fw-logger.cpp +++ b/tools/fw-logger/rs-fw-logger.cpp @@ -113,6 +113,8 @@ int main(int argc, char* argv[]) } } + fw_log_device.start_collecting(); + bool are_there_remaining_flash_logs_to_pull = true; auto time_of_previous_polling_ms = std::chrono::high_resolution_clock::now(); @@ -141,11 +143,14 @@ int main(int argc, char* argv[]) auto parsed_log = fw_log_device.create_parsed_message(); bool parsing_result = fw_log_device.parse_log(log_message, parsed_log); + std::string module_print = parsed_log.module_name() + " "; + if( module_print == "Unknown " ) + module_print.clear(); // Some devices don't support FW log modules + stringstream sstr; sstr << datetime_string() << " " << parsed_log.timestamp() << " " << parsed_log.sequence_id() - << " " << parsed_log.severity() << " " << parsed_log.thread_name() - << " " << parsed_log.file_name() << " " << parsed_log.line() - << " " << parsed_log.message(); + << " " << parsed_log.severity() << " " << parsed_log.thread_name() << " " << module_print + << parsed_log.file_name() << " " << parsed_log.line() << " " << parsed_log.message(); fw_log_lines.push_back(sstr.str()); } @@ -182,6 +187,8 @@ int main(int argc, char* argv[]) time_of_previous_polling_ms = std::chrono::high_resolution_clock::now(); } } + + fw_log_device.stop_collecting(); } catch (const error & e) {