diff --git a/src/xrt/auxiliary/util/CMakeLists.txt b/src/xrt/auxiliary/util/CMakeLists.txt index ceb2480a4..53f4b63d5 100644 --- a/src/xrt/auxiliary/util/CMakeLists.txt +++ b/src/xrt/auxiliary/util/CMakeLists.txt @@ -61,6 +61,8 @@ add_library( u_json.hpp u_logging.c u_logging.h + u_metrics.c + u_metrics.h u_misc.c u_misc.h u_pacing.h @@ -98,7 +100,15 @@ add_library( u_worker.hpp "${CMAKE_CURRENT_BINARY_DIR}/u_git_tag.c" ) -target_link_libraries(aux_util PUBLIC aux-includes aux_generated_bindings aux_os aux_math) +target_link_libraries( + aux_util + PUBLIC + xrt-external-nanopb + aux-includes + aux_generated_bindings + aux_os + aux_math + ) # Is basically used everywhere, unavoidable. if(XRT_HAVE_SYSTEM_CJSON) diff --git a/src/xrt/auxiliary/util/u_metrics.c b/src/xrt/auxiliary/util/u_metrics.c new file mode 100644 index 000000000..d0a5163e9 --- /dev/null +++ b/src/xrt/auxiliary/util/u_metrics.c @@ -0,0 +1,230 @@ +// Copyright 2022, Collabora, Ltd. +// SPDX-License-Identifier: BSL-1.0 +/*! + * @file + * @brief Metrics saving functions. + * @author Jakob Bornecrantz + * @ingroup aux_util + */ + +#include "os/os_threading.h" + +#include "util/u_metrics.h" +#include "util/u_debug.h" + +#include "monado_metrics.pb.h" +#include "pb_encode.h" + +#include + +#define VERSION_MAJOR 1 +#define VERSION_MINOR 1 + +static FILE *g_file = NULL; +static struct os_mutex g_file_mutex; +static bool g_metrics_initialized = false; + +DEBUG_GET_ONCE_OPTION(metrics_file, "XRT_METRICS_FILE", NULL) + + + +/* + * + * Helper functions. + * + */ + +static void +write_record(monado_metrics_Record *r) +{ + uint8_t buffer[monado_metrics_Record_size + 10]; // Including submessage + + + pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer)); + bool ret = pb_encode_submessage(&stream, &monado_metrics_Record_msg, r); + if (!ret) { + U_LOG_E("Failed to encode metrics message!"); + return; + } + + os_mutex_lock(&g_file_mutex); + + fwrite(buffer, stream.bytes_written, 1, g_file); + + os_mutex_unlock(&g_file_mutex); +} + +static void +write_version(uint32_t major, uint32_t minor) +{ + if (!g_metrics_initialized) { + return; + } + + monado_metrics_Record record = monado_metrics_Record_init_default; + + // Select which filed is used. + record.which_record = monado_metrics_Record_version_tag; + record.record.version.major = major; + record.record.version.minor = minor; + + write_record(&record); +} + + +/* + * + * 'Exported' functions. + * + */ + +void +u_metrics_init() +{ + const char *str = debug_get_option_metrics_file(); + if (str == NULL) { + U_LOG_D("No metrics file!"); + return; + } + + g_file = fopen(str, "wb"); + if (g_file == NULL) { + U_LOG_E("Could not open '%s'!", str); + return; + } + + os_mutex_init(&g_file_mutex); + + g_metrics_initialized = true; + + write_version(VERSION_MAJOR, VERSION_MINOR); + + U_LOG_I("Opened metrics file: '%s'", str); +} + +void +u_metrics_close() +{ + if (!g_metrics_initialized) { + return; + } + + U_LOG_I("Closing metrics file: '%s'", debug_get_option_metrics_file()); + + // At least try to avoid races. + os_mutex_lock(&g_file_mutex); + fflush(g_file); + fclose(g_file); + g_file = NULL; + os_mutex_unlock(&g_file_mutex); + + os_mutex_destroy(&g_file_mutex); + + g_metrics_initialized = false; +} + +bool +u_metrics_is_active(void) +{ + return g_metrics_initialized; +} + +void +u_metrics_write_session_frame(struct u_metrics_session_frame *umsf) +{ + if (!g_metrics_initialized) { + return; + } + + monado_metrics_Record record = monado_metrics_Record_init_default; + + // Select which filed is used. + record.which_record = monado_metrics_Record_session_frame_tag; + +#define COPY(_0, _1, _2, _3, FIELD, _4) (record.record.session_frame.FIELD = umsf->FIELD); + monado_metrics_SessionFrame_FIELDLIST(COPY, 0); +#undef COPY + + + write_record(&record); +} + +void +u_metrics_write_used(struct u_metrics_used *umu) +{ + if (!g_metrics_initialized) { + return; + } + + monado_metrics_Record record = monado_metrics_Record_init_default; + + // Select which filed is used. + record.which_record = monado_metrics_Record_used_tag; + +#define COPY(_0, _1, _2, _3, FIELD, _4) (record.record.used.FIELD = umu->FIELD); + monado_metrics_Used_FIELDLIST(COPY, 0); +#undef COPY + + + write_record(&record); +} + +void +u_metrics_write_system_frame(struct u_metrics_system_frame *umsf) +{ + if (!g_metrics_initialized) { + return; + } + + monado_metrics_Record record = monado_metrics_Record_init_default; + + // Select which filed is used. + record.which_record = monado_metrics_Record_system_frame_tag; + +#define COPY(_0, _1, _2, _3, FIELD, _4) (record.record.system_frame.FIELD = umsf->FIELD); + monado_metrics_SystemFrame_FIELDLIST(COPY, 0); +#undef COPY + + + write_record(&record); +} + +void +u_metrics_write_system_gpu_info(struct u_metrics_system_gpu_info *umgi) +{ + if (!g_metrics_initialized) { + return; + } + + monado_metrics_Record record = monado_metrics_Record_init_default; + + // Select which filed is used. + record.which_record = monado_metrics_Record_system_gpu_info_tag; + +#define COPY(_0, _1, _2, _3, FIELD, _4) (record.record.system_gpu_info.FIELD = umgi->FIELD); + monado_metrics_SystemGpuInfo_FIELDLIST(COPY, 0); +#undef COPY + + + write_record(&record); +} + +void +u_metrics_write_system_present_info(struct u_metrics_system_present_info *umpi) +{ + if (!g_metrics_initialized) { + return; + } + + monado_metrics_Record record = monado_metrics_Record_init_default; + + // Select which filed is used. + record.which_record = monado_metrics_Record_system_present_info_tag; + +#define COPY(_0, _1, _2, _3, FIELD, _4) (record.record.system_present_info.FIELD = umpi->FIELD); + monado_metrics_SystemPresentInfo_FIELDLIST(COPY, 0); +#undef COPY + + + write_record(&record); +} diff --git a/src/xrt/auxiliary/util/u_metrics.h b/src/xrt/auxiliary/util/u_metrics.h new file mode 100644 index 000000000..c5fbfa3b3 --- /dev/null +++ b/src/xrt/auxiliary/util/u_metrics.h @@ -0,0 +1,110 @@ +// Copyright 2022, Collabora, Ltd. +// SPDX-License-Identifier: BSL-1.0 +/*! + * @file + * @brief Metrics saving functions. + * @author Jakob Bornecrantz + * @ingroup aux_util + */ + +#pragma once + +#include "xrt/xrt_compiler.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +struct u_metrics_session_frame +{ + int64_t session_id; + int64_t frame_id; + uint64_t predicted_frame_time_ns; + uint64_t predicted_wake_up_time_ns; + uint64_t predicted_gpu_done_time_ns; + uint64_t predicted_display_time_ns; + uint64_t predicted_display_period_ns; + uint64_t display_time_ns; + uint64_t when_predicted_ns; + uint64_t when_wait_woke_ns; + uint64_t when_begin_ns; + uint64_t when_delivered_ns; + uint64_t when_gpu_done_ns; + bool discarded; +}; + +struct u_metrics_used +{ + int64_t session_id; + int64_t session_frame_id; + int64_t system_frame_id; + uint64_t when_ns; +}; + +struct u_metrics_system_frame +{ + int64_t frame_id; + uint64_t predicted_display_time_ns; + uint64_t predicted_display_period_ns; + uint64_t desired_present_time_ns; + uint64_t wake_up_time_ns; + uint64_t present_slop_ns; +}; + +struct u_metrics_system_gpu_info +{ + int64_t frame_id; + uint64_t gpu_start_ns; + uint64_t gpu_end_ns; + uint64_t when_ns; +}; + +struct u_metrics_system_present_info +{ + int64_t frame_id; + uint64_t expected_comp_time_ns; + uint64_t predicted_wake_up_time_ns; + uint64_t predicted_done_time_ns; + uint64_t predicted_display_time_ns; + uint64_t when_predict_ns; + uint64_t when_woke_ns; + uint64_t when_began_ns; + uint64_t when_submitted_ns; + uint64_t when_infoed_ns; + uint64_t desired_present_time_ns; + uint64_t present_slop_ns; + uint64_t present_margin_ns; + uint64_t actual_present_time_ns; + uint64_t earliest_present_time_ns; +}; + + +void +u_metrics_init(void); + +void +u_metrics_close(void); + +bool +u_metrics_is_active(void); + +void +u_metrics_write_session_frame(struct u_metrics_session_frame *umsf); + +void +u_metrics_write_used(struct u_metrics_used *umu); + +void +u_metrics_write_system_frame(struct u_metrics_system_frame *umsf); + +void +u_metrics_write_system_gpu_info(struct u_metrics_system_gpu_info *umgi); + +void +u_metrics_write_system_present_info(struct u_metrics_system_present_info *umpi); + + +#ifdef __cplusplus +} +#endif