diff --git a/framework/CMakeLists.txt b/framework/CMakeLists.txt index fa6d26060c41bd20110f8b7ae808f650518e78b8..98b1ce815769ae226ed70775ae4f0cab1f5225ef 100644 --- a/framework/CMakeLists.txt +++ b/framework/CMakeLists.txt @@ -37,6 +37,7 @@ target_sources( "${CMAKE_CURRENT_SOURCE_DIR}/src/fwk_string.c" "${CMAKE_CURRENT_SOURCE_DIR}/src/fwk_delayed_resp.c" "${CMAKE_CURRENT_SOURCE_DIR}/src/fwk_time.c" + "${CMAKE_CURRENT_SOURCE_DIR}/src/fwk_trace.c" "${CMAKE_CURRENT_SOURCE_DIR}/src/stdlib.c" "${CMAKE_CURRENT_SOURCE_DIR}/src/fwk_core.c") diff --git a/framework/include/fwk_trace.h b/framework/include/fwk_trace.h new file mode 100644 index 0000000000000000000000000000000000000000..4c65775a85e272bc0c3cb42576705219a559a473 --- /dev/null +++ b/framework/include/fwk_trace.h @@ -0,0 +1,152 @@ +/* + * Arm SCP/MCP Software + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef FWK_TRACE_H +#define FWK_TRACE_H + +#include +#include + +/*! + * \addtogroup GroupLibFramework + * \defgroup GroupTime Trace operations. + * + * \details This component adds support for various trace operations. + * + * \{ + */ + +/*! + * \brief Start trace of an event. + * + * \param[in] ID Event ID. + * \return Status code representing the result of the operation. + */ +#define FWK_TRACE_START(ID) fwk_trace_start(ID) + +/*! + * \brief Finish trace of an event. + * + * \param[in] ID Event ID. + * \return Status code representing the result of the operation. + */ +#define FWK_TRACE_FINISH(ID, MSG) \ + fwk_trace_finish(__FILE__, __func__, __LINE__, ID, MSG) + +/*! + * \brief Calculate the trace overhead. + * + * \return Tracing overhead. + */ +#define FWK_TRACE_CALC_OVERHEAD() fwk_trace_calc_overhead() + +typedef uint64_t fwk_trace_count_t; +typedef uint32_t fwk_trace_id_t; +#define PRItraceid PRIu32 +#define PRItracecount PRIu64 + +/*! + * \brief Trace driver + */ +struct fwk_trace_driver { + /*! The number of the trace entries. */ + unsigned int trace_entry_count; + + /*! + * \brief Get current cycle/timestamp count + * + * \return trace count + */ + fwk_trace_count_t (*get_trace_count)(void); + + /*! + * \brief Report trace entry + * + * \param[in] filename File name where end of trace occured. + * \param[in] func Function where end of trace occured. + * \param[in] line Line number where end of trace occured. + * \param[in] id ID of the event which trace ended. + * \param[in] trace_count Count of how many time units passed along the + * event. + * \param[in] msg optional message to add to the trace entry. + * + */ + void (*report_trace_entry)( + const char *filename, + const char *func, + const unsigned int line, + const fwk_trace_id_t id, + const fwk_trace_count_t trace_count, + const char *msg); +}; + +/*! + * \brief Initialize tracing + * Note: This function has Constructor attribute which adds it to the list of + * function executes before `main`. + */ +void fwk_trace_init(void); + +/*! + * \brief Calculate the trace overhead. + * + * \return Tracing overhead. + */ +fwk_trace_count_t fwk_trace_calc_overhead(void); + +/*! + * \brief Start trace of an event. + * + * \param[in] id Event ID. + * \return Status code representing the result of the operation. + */ +int fwk_trace_start(const fwk_trace_id_t id); + +/*! + * \brief Finish trace of an event. + * + * \param[in] filename Name of the file in which the tracing finished. + * use __FILE__ macro. + * \param[in] line Line number in which the tracing finished. + * use __LINE__ macro. + * \param[in] func Name of the function in which the fwk_trace_finish is called. + * use __func__ macro. + * \param[in] id Event ID. + * \param[in] msg An optional message. + * + * \note id must match a started tracing point to report the count spent between + * the tracing points. The rest of the params helps in reporting, and it + * is left on the driver how it uses them. + * + * \return Status code representing the result of the operation. + */ +int fwk_trace_finish( + const char *filename, + const char *func, + const unsigned int line, + const fwk_trace_id_t id, + const char *msg); + +/*! + * \brief Register a framework trace driver. + * + * \details + * + * This is a weak function provided by the framework that, by default, does + * not register a driver, and should be overridden by the firmware if you + * wish to provide one. + * + * + * \return Framework trace driver. + */ +struct fwk_trace_driver fmw_trace_driver(void); + +/*! + * \} + */ + +#endif /* FWK_TRACE_H */ diff --git a/framework/src/fwk_trace.c b/framework/src/fwk_trace.c new file mode 100644 index 0000000000000000000000000000000000000000..67b59bd216703fdd175945e2ce08a0713a113a59 --- /dev/null +++ b/framework/src/fwk_trace.c @@ -0,0 +1,114 @@ +/* + * Arm SCP/MCP Software + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include + +#include + +static struct { + struct fwk_trace_driver driver; /* Trace driver */ + fwk_trace_count_t *start_timestamp; + bool *entry_pending; +} fwk_trace_ctx; + +FWK_CONSTRUCTOR void fwk_trace_init(void) +{ + struct fwk_trace_driver driver = fmw_trace_driver(); + (void)fwk_str_memcpy(&fwk_trace_ctx.driver, &driver, sizeof(driver)); + if (fwk_trace_ctx.driver.trace_entry_count != 0) { + fwk_trace_ctx.start_timestamp = fwk_mm_calloc( + fwk_trace_ctx.driver.trace_entry_count + 1, + sizeof(fwk_trace_count_t)); + fwk_trace_ctx.entry_pending = fwk_mm_calloc( + fwk_trace_ctx.driver.trace_entry_count + 1, + sizeof(fwk_trace_count_t)); + } +} + +static inline fwk_trace_count_t calc_delta( + fwk_trace_count_t start, + fwk_trace_count_t end) +{ + return (end >= start) ? (end - start) : + ((fwk_trace_count_t)(-1) - start + end); +} + +fwk_trace_count_t fwk_trace_calc_overhead(void) +{ + fwk_trace_count_t start = fwk_trace_ctx.driver.get_trace_count(); + (void)FWK_TRACE_START(fwk_trace_ctx.driver.trace_entry_count); + (void)FWK_TRACE_FINISH(fwk_trace_ctx.driver.trace_entry_count, ""); + return calc_delta(start, fwk_trace_ctx.driver.get_trace_count()); +} + +int fwk_trace_start(fwk_trace_id_t id) +{ + if (id >= fwk_trace_ctx.driver.trace_entry_count) { + FWK_LOG_ERR("id is not valid"); + return FWK_E_PARAM; + } + if (fwk_trace_ctx.entry_pending[id] == true) { + FWK_LOG_ERR("tracing id 0x%" PRItraceid " has already started", id); + return FWK_E_STATE; + } + if (fwk_trace_ctx.driver.get_trace_count == NULL) { + FWK_LOG_ERR("start trace driver is not set!"); + return FWK_E_DEVICE; + } + fwk_trace_ctx.entry_pending[id] = true; + fwk_trace_ctx.start_timestamp[id] = fwk_trace_ctx.driver.get_trace_count(); + + return FWK_SUCCESS; +} + +int fwk_trace_finish( + const char *filename, + const char *func, + const unsigned int line, + fwk_trace_id_t id, + const char *msg) +{ + if (id >= fwk_trace_ctx.driver.trace_entry_count) { + FWK_LOG_ERR("id is not valid"); + return FWK_E_PARAM; + } + if (fwk_trace_ctx.entry_pending[id] == false) { + FWK_LOG_ERR( + "%s:%u: tracing id 0x%" PRItraceid " has not been started", + func, + line, + id); + return FWK_E_STATE; + } + fwk_trace_ctx.entry_pending[id] = false; + if (fwk_trace_ctx.driver.get_trace_count == NULL || + fwk_trace_ctx.driver.report_trace_entry == NULL) { + FWK_LOG_ERR("finish trace driver is not set!"); + return FWK_E_DEVICE; + } + fwk_trace_count_t trace_count = calc_delta( + fwk_trace_ctx.start_timestamp[id], + fwk_trace_ctx.driver.get_trace_count()); + fwk_trace_ctx.start_timestamp[id] = 0; + fwk_trace_ctx.driver.report_trace_entry( + filename, func, line, id, trace_count, msg); + + return FWK_SUCCESS; +} + +FWK_WEAK struct fwk_trace_driver fmw_trace_driver(void) +{ + return (struct fwk_trace_driver){ + .trace_entry_count = 0, + .get_trace_count = NULL, + .report_trace_entry = NULL, + }; +} diff --git a/framework/test/CMakeLists.txt b/framework/test/CMakeLists.txt index fbd206972e0514706b4858ebb5d2661e659c71e6..931889952a0e1ec4615426c1672cfd9a5178bf5b 100644 --- a/framework/test/CMakeLists.txt +++ b/framework/test/CMakeLists.txt @@ -76,6 +76,7 @@ list(APPEND SCP_FWK_TEST_TARGETS test_fwk_ring) list(APPEND SCP_FWK_TEST_TARGETS test_fwk_ring_init) list(APPEND SCP_FWK_TEST_TARGETS test_fwk_string) list(APPEND SCP_FWK_TEST_TARGETS test_fwk_core) +list(APPEND SCP_FWK_TEST_TARGETS test_fwk_trace) # Create a list of the tests that need notifications. list(APPEND NOTIFICATION_ENABLED_TEST test_fwk_module test_fwk_notification @@ -130,6 +131,7 @@ list(APPEND COMMON_SRC ${FWK_SRC_ROOT}/fwk_string.c) list(APPEND COMMON_SRC ${FWK_TEST_SRC_ROOT}/fwk_test.c) list(APPEND COMMON_SRC ${FWK_SRC_ROOT}/fwk_delayed_resp.c) list(APPEND COMMON_SRC ${FWK_SRC_ROOT}/fwk_time.c) +list(APPEND COMMON_SRC ${FWK_SRC_ROOT}/fwk_trace.c) # CMake internal function enables testing for this directory enable_testing() diff --git a/framework/test/test_fwk_trace.c b/framework/test/test_fwk_trace.c new file mode 100644 index 0000000000000000000000000000000000000000..22ea359ad4d5fb2897eb9a8fe61314fb86bb270a --- /dev/null +++ b/framework/test/test_fwk_trace.c @@ -0,0 +1,191 @@ +/* + * Arm SCP/MCP Software + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include + +#include +#include + +#define TRACE_ENTRY_COUNT (3) + +static fwk_trace_count_t current_trace_count = 0; +static fwk_trace_count_t expected_trace_entry_count = 0; +static fwk_trace_id_t expected_id = 0; + +static fwk_trace_count_t get_trace_count(void) +{ + return current_trace_count; +} + +void report_trace_entry( + const char *filename, + const char *func, + const unsigned int line, + const fwk_trace_id_t id, + const fwk_trace_count_t trace_count, + const char *msg) +{ + assert(expected_id == id); + assert(expected_trace_entry_count == trace_count); +} + +static const struct fwk_trace_driver default_driver = { + .trace_entry_count = TRACE_ENTRY_COUNT, + .get_trace_count = get_trace_count, + .report_trace_entry = report_trace_entry, +}; + +static struct fwk_trace_driver driver; + +struct fwk_trace_driver fmw_trace_driver(void) +{ + return driver; +} + +static int test_suite_setup(void) +{ + return FWK_SUCCESS; +} + +static void test_case_setup(void) +{ + driver = default_driver; + fwk_trace_init(); +} + +static void test_fwk_trace_start_invalid_id(void) +{ + int status = FWK_TRACE_START(TRACE_ENTRY_COUNT); + assert(status == FWK_E_PARAM); + status = FWK_TRACE_START(TRACE_ENTRY_COUNT + 1); + assert(status == FWK_E_PARAM); + status = FWK_TRACE_START(-1); + assert(status == FWK_E_PARAM); +} + +static void test_fwk_trace_start_twice(void) +{ + int status = FWK_TRACE_START(0); + int status2 = FWK_TRACE_START(0); + assert(status == FWK_SUCCESS); + assert(status2 == FWK_E_STATE); +} + +static void test_fwk_trace_start_no_driver(void) +{ + driver.get_trace_count = NULL; + fwk_trace_init(); + int status = FWK_TRACE_START(0); + assert(status == FWK_E_DEVICE); +} + +static void test_fwk_trace_finish_invalid_id(void) +{ + int status = FWK_TRACE_FINISH(TRACE_ENTRY_COUNT, ""); + assert(status == FWK_E_PARAM); + status = FWK_TRACE_FINISH(TRACE_ENTRY_COUNT + 1, ""); + assert(status == FWK_E_PARAM); + status = FWK_TRACE_FINISH(-1, ""); + assert(status == FWK_E_PARAM); +} + +static void test_fwk_trace_finish_with_no_start(void) +{ + int status = FWK_TRACE_FINISH(0, ""); + assert(status == FWK_E_STATE); +} + +static void test_fwk_trace_normal_usage(void) +{ + current_trace_count = 1000; + int status = FWK_TRACE_START(0); + assert(status == FWK_SUCCESS); + current_trace_count = 3000; + expected_id = 0; + expected_trace_entry_count = 2000; + status = FWK_TRACE_FINISH(0, ""); + assert(status == FWK_SUCCESS); +} + +static void test_fwk_trace_full_nesting(void) +{ + /* + * ----(1000)------(2000)----(3000)-----(4000)----------> + * ----[s:0]-------[s:1]-----[f:1]------[f:0]-----------> + */ + fwk_trace_count_t trace_counts[] = { 1000, 2000, 3000, 4000 }; + current_trace_count = trace_counts[0]; + int status = FWK_TRACE_START(0); + assert(status == FWK_SUCCESS); + + current_trace_count = trace_counts[1]; + status = FWK_TRACE_START(1); + assert(status == FWK_SUCCESS); + current_trace_count = trace_counts[2]; + expected_id = 1; + expected_trace_entry_count = trace_counts[2] - trace_counts[1]; + status = FWK_TRACE_FINISH(1, ""); + assert(status == FWK_SUCCESS); + + current_trace_count = trace_counts[3]; + expected_id = 0; + expected_trace_entry_count = trace_counts[3] - trace_counts[0]; + status = FWK_TRACE_FINISH(0, ""); + assert(status == FWK_SUCCESS); +} + +static void test_fwk_trace_partial_nesting(void) +{ + /* + * ----(1000)------(2000)----(3000)-----(4000)----------> + * ----[s:0]-------[s:1]-----[f:0]------[f:1]-----------> + */ + fwk_trace_count_t trace_counts[] = { 1000, 2000, 3000, 4000 }; + current_trace_count = trace_counts[0]; + int status = FWK_TRACE_START(0); + assert(status == FWK_SUCCESS); + + current_trace_count = trace_counts[1]; + status = FWK_TRACE_START(1); + assert(status == FWK_SUCCESS); + + current_trace_count = trace_counts[2]; + expected_id = 0; + expected_trace_entry_count = trace_counts[2] - trace_counts[0]; + status = FWK_TRACE_FINISH(0, ""); + assert(status == FWK_SUCCESS); + + current_trace_count = trace_counts[3]; + expected_id = 1; + expected_trace_entry_count = trace_counts[3] - trace_counts[1]; + status = FWK_TRACE_FINISH(1, ""); + assert(status == FWK_SUCCESS); +} + +static const struct fwk_test_case_desc test_case_table[] = { + FWK_TEST_CASE(test_fwk_trace_start_invalid_id), + FWK_TEST_CASE(test_fwk_trace_start_twice), + FWK_TEST_CASE(test_fwk_trace_start_no_driver), + FWK_TEST_CASE(test_fwk_trace_finish_invalid_id), + FWK_TEST_CASE(test_fwk_trace_finish_with_no_start), + FWK_TEST_CASE(test_fwk_trace_normal_usage), + FWK_TEST_CASE(test_fwk_trace_full_nesting), + FWK_TEST_CASE(test_fwk_trace_partial_nesting), +}; + +struct fwk_test_suite_desc test_suite = { + .name = "fwk_test", + + .test_suite_setup = test_suite_setup, + .test_case_setup = test_case_setup, + + .test_case_count = FWK_ARRAY_SIZE(test_case_table), + .test_case_table = test_case_table, +}; diff --git a/product/juno/scp_ramfw/CMakeLists.txt b/product/juno/scp_ramfw/CMakeLists.txt index 666d1dd404567b4de02077412c084a483da56be7..e84c50bc90fae9ba9f5bf949dd48935a85b4b5de 100644 --- a/product/juno/scp_ramfw/CMakeLists.txt +++ b/product/juno/scp_ramfw/CMakeLists.txt @@ -62,6 +62,7 @@ target_sources( "${CMAKE_CURRENT_SOURCE_DIR}/config_juno_cdcel937.c" "${CMAKE_CURRENT_SOURCE_DIR}/config_juno_hdlcd.c" "${CMAKE_CURRENT_SOURCE_DIR}/config_timer.c" + "${CMAKE_CURRENT_SOURCE_DIR}/config_trace.c" "${CMAKE_CURRENT_SOURCE_DIR}/config_juno_ddr_phy400.c" "${CMAKE_CURRENT_SOURCE_DIR}/config_juno_dmc400.c" "${CMAKE_CURRENT_SOURCE_DIR}/config_juno_ram.c" diff --git a/product/juno/scp_ramfw/config_trace.c b/product/juno/scp_ramfw/config_trace.c new file mode 100644 index 0000000000000000000000000000000000000000..ab7553b708931b7f18faf4367235a8fda8eda090 --- /dev/null +++ b/product/juno/scp_ramfw/config_trace.c @@ -0,0 +1,45 @@ +/* + * Arm SCP/MCP Software + * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include + +#include + +#define UNIT_STR "ns" + +fwk_trace_count_t get_trace_count(void) +{ + return FWK_NS(fwk_time_current()); +} + +void report_trace_entry( + const char *filename, + const char *func, + const unsigned int line, + const fwk_trace_id_t id, + const fwk_trace_count_t trace_count, + const char *msg) +{ + FWK_LOG_CRIT( + "%s:%d:[TRACE][%" PRItraceid "]: %" PRIu32 " " UNIT_STR, + func, + line, + id, + (uint32_t)trace_count); +} + +struct fwk_trace_driver fmw_trace_driver(void) +{ + return (struct fwk_trace_driver){ + .trace_entry_count = 5, + .get_trace_count = get_trace_count, + .report_trace_entry = report_trace_entry, + }; +} diff --git a/product/optee/sub.mk b/product/optee/sub.mk index debc30d889d3234c737b80fee6c5d921be4973d4..e02729e699fe69af8cde3f4c84259d6dec7e0313 100644 --- a/product/optee/sub.mk +++ b/product/optee/sub.mk @@ -37,6 +37,7 @@ srcs-y += $(scpfw-path)/framework/src/fwk_status.c srcs-y += $(scpfw-path)/framework/src/fwk_string.c srcs-y += $(scpfw-path)/framework/src/fwk_delayed_resp.c srcs-y += $(scpfw-path)/framework/src/fwk_time.c +srcs-y += $(scpfw-path)/framework/src/fwk_trace.c srcs-y += $(scpfw-path)/framework/src/fwk_core.c srcs-y += $(scpfw-path)/framework/src/assert.c srcs-y += $(scpfw-path)/framework/src/stdlib.c