Overview
Object Cores is a kernel debugging tool provided by Zephyr, used to identify registered objects and perform statistical operations on them. Most RTOS kernel objects have their own lists for collecting and reporting statistics, while Zephyr abstracts Object Cores to provide a unified method for collecting and retrieving information. Various kernel objects in Zephyr allow for the collection and reporting of statistics through Object Cores. Object Cores are abstracted to identify objects and statistical operations, but are not limited to use within the kernel; developers can freely integrate them into other objects in their projects. This article focuses on how to use Object Cores, while another document explains their usage within Zephyr.
Object Cores contain a pointer to a statistical descriptor that defines various operations for interacting with object statistics. Additionally, Object Cores contain a pointer to the “raw” statistics associated with that object. The raw data is the unprocessed data related to the statistics. The queried data may be “raw”, but it may also be processed in some way (e.g., averaged).
Usage
As we learned from the overview, Object Cores are used to track a certain type of object. Here we have prepared an object called mgr
as an example, which supports the following interfaces:
-
mgr_open
Opens amgr
object -
mgr_close
Closes amgr
object -
mgr_fetch
Executes the fetch action -
mgr_get_stats_raw
Retrieves the raw data of themgr
state -
mgr_query_stats
Queries the processed data -
mgr_stats_reset
Resets themgr
state -
mgr_stats_enable
Enables the collection ofmgr
state -
mgr_stats_disable
Disables the collection ofmgr
state
Here we use Object Cores to demonstrate how to track an opened mgr
object and how to obtain the state of mgr
through Object Cores.
Configuration
To use Object Cores, you need to configure CONFIG_OBJ_CORE=y
. If you want to obtain the object state through Object Cores, you also need to configure CONFIG_OBJ_CORE_STATS=y
. Object Cores need to be embedded in the mgr
object. For convenience in distinguishing subsequent example code, the code embedding Object Cores into mgr
will be wrapped with CONFIG_OBJ_CORE
.
Data Structure
The mgr
object has the following data structure. For mgr
, each object requires a struct k_obj_core
to track:
struct my_obj_type_raw_info { uint32_t total_fetch_time; uint32_t fetch_cnt; }; struct my_obj_type_query_stats { uint32_t avg_fetch_time; }; // Used to manage the <code>mgr
object struct my_test_manage{ bool used; // Whether themgr
object is open bool statistic; // Whether to allowmgr
to be counted struct my_obj_type_raw_info my_raw_data; // Raw data counted bymgr
// Insertstruct k_obj_core
to track this object; recommended member name isobj_core
#ifdef CONFIG_OBJ_CORE struct k_obj_core obj_core; #endif };
The API of Object Core takes struct k_obj_core
as a parameter. When we insert the member name obj_core
in the structure, we can access this member through K_OBJ_CORE
. For example, accessing struct my_test_manage mgr
’s obj_core
is K_OBJ_CORE ( &mgr )
. Conversely, knowing the pointer to obj_core
, we can also obtain the address of mgr
, for example:
struct my_test_manage *mgr;
mgr = CONTAINER_OF ( obj_core, struct my_test_manage, obj_core ) ;
Initialization
The example code is as follows:
#ifdef CONFIG_OBJ_CORE #ifdef CONFIG_OBJ_CORE_STATS // Implement the callback function for raw state data static int my_obj_type_stats_raw ( struct k_obj_core *obj_core, void *stats ) { memcpy ( stats, obj_core->stats, sizeof ( struct my_obj_type_raw_info )) ; return 0; } // Implement the callback function for query data static int my_obj_type_stats_query ( struct k_obj_core *obj_core, void *stats ) { struct my_test_manage *mgr; mgr = CONTAINER_OF ( obj_core, struct my_test_manage, obj_core ) ; return mgr_query_stats ( mgr, stats ) ; } // Implement the callback function for resetting state static int my_obj_type_stats_reset ( struct k_obj_core *obj_core ) { struct my_test_manage *mgr; mgr = CONTAINER_OF ( obj_core, struct my_test_manage, obj_core ) ; mgr_stats_reset ( mgr ) ; return 0; } // Implement the callback function for disabling state collection static int my_obj_type_stats_disable ( struct k_obj_core *obj_core ) { struct my_test_manage *mgr; mgr = CONTAINER_OF ( obj_core, struct my_test_manage, obj_core ) ; mgr_stats_disable ( mgr ) ; return 0; } // Implement the callback function for enabling state collection static int my_obj_type_stats_enable ( struct k_obj_core *obj_core ) { struct my_test_manage *mgr; mgr = CONTAINER_OF ( obj_core, struct my_test_manage, obj_core ) ; mgr_stats_enable ( mgr ) ; return 0; } // Define and initialize <code>struct k_obj_core_stats_desc
, specifying the length of the data and the callback functions static struct k_obj_core_stats_desc my_obj_type_stats_desc = { .raw_size = sizeof ( struct my_obj_type_raw_info ) , // Specify the length of raw data .query_size = sizeof ( struct my_obj_type_query_stats ) , // Specify the length of query data .raw = my_obj_type_stats_raw, .query = my_obj_type_stats_query, .reset = my_obj_type_stats_reset, .disable = my_obj_type_stats_disable, .enable = my_obj_type_stats_enable, }; #endif /* CONFIG_OBJ_CORE_STATS */ // Define the type of MGR # define K_OBJ_TYPE_MY_NEW_TYPE K_OBJ_TYPE_ID_GEN ( "MGRS" ) struct k_obj_type my_obj_type; void my_obj_type_init ( void ) { // Initialize object core to managemgr
z_obj_type_init ( &my_obj_type, K_OBJ_TYPE_MY_NEW_TYPE, offsetof ( struct my_test_manage, obj_core )) ; #ifdef CONFIG_OBJ_CORE_STATS // Initialize object core stats to obtainmgr
statistics k_obj_type_stats_init ( &my_obj_type, &my_obj_type_stats_desc ) ; #endif /* CONFIG_OBJ_CORE_STATS */ } // Callmy_obj_type_init
during application initialization to complete the initialization ofmgr
’s object core SYS_INIT ( my_obj_type_init, APPLICATION, 0 ) ; #endif /*CONFIG_OBJ_CORE_STATS*/
The entire process above completes the initialization of my_obj_type
, which can be used to manage the bound mgr
object. The initialization can be viewed in two parts: one is using z_obj_type_init
to initialize the Object Core, which will create a linked list to manage the MGR objects. The other is implementing the callback functions required by struct k_obj_core_stats_desc
and initializing them through k_obj_type_stats_init
.
Binding Objects
In mgr_open
and mgr_close
, use k_obj_core_init_and_link
to link the mgr
object to my_obj_type
. After that, by operating on my_obj_type
, you can access all mgr
objects. When the mgr
object is closed, use k_obj_core_unlink
to unlink it. Similarly, if you want to obtain data from the mgr
object through the object core, you need to use k_obj_core_stats_register
to register the raw data, and when the mgr
object is closed, unregister it.
static void *mgr_open ( void )
{
for ( uint32_t i=0; i<MY_TEST_MGR_NUM; i++ ) {
if ( mgrs [i].used == false ) {
mgrs [i].used = true;
#ifdef CONFIG_OBJ_CORE
// Link the object
k_obj_core_init_and_link ( K_OBJ_CORE ( &mgrs [i] ) , &my_obj_type ) ;
#ifdef CONFIG_OBJ_CORE_STATS
// Register raw data for the object
k_obj_core_stats_register ( K_OBJ_CORE ( &mgrs [i] ) ,
&mgrs [i].my_raw_data,
sizeof ( mgrs [i].my_raw_data )) ;
#endif
#endif
return &mgrs [i];
}
}
return NULL;
}
static bool mgr_close ( void* mgr )
{
for ( uint32_t i=0; i<MY_TEST_MGR_NUM; i++ ) {
if ( &mgrs [i].used == mgr ) {
#ifdef CONFIG_OBJ_CORE
#ifdef CONFIG_OBJ_CORE_STATS
// Unlink
k_obj_core_stats_deregister ( K_OBJ_CORE ( &mgrs [i] )) ;
#endif
// Unregister
k_obj_core_unlink ( K_OBJ_CORE ( &mgrs [i] )) ;
#endif
memset ( &mgrs [i], 0, sizeof ( struct my_test_manage )) ;
return true;
}
}
return false;
}
Accessing Objects
You can access objects using k_obj_type_walk_unlocked
and k_obj_type_walk_locked
. The difference between the two is whether the access is locked. First, define the access function:
int walk_op ( struct k_obj_core *obj_core, void *data )
{
struct my_test_manage *mgr;
mgr = CONTAINER_OF ( obj_core, struct my_test_manage, obj_core ) ;
shell_print ( data, "%p fetch time %u cnt %u", mgr, mgr->my_raw_data.total_fetch_time, mgr->my_raw_data.fetch_cnt ) ;
return 0;
}
Then traverse access:
// Find the object to traverse
struct k_obj_type *obj_type;
obj_type = k_obj_type_find ( K_OBJ_TYPE_MY_NEW_TYPE ) ;
// Traverse all MGR objects in an unlocked manner; each object will call walk_op once
k_obj_type_walk_unlocked ( obj_type, walk_op, shell ) ;
// Traverse all MGR objects in a locked manner; each object will call walk_op once
k_obj_type_walk_locked ( obj_type, walk_op, shell ) ;
Statistical Query
When CONFIG_OBJ_CORE_STATS=y
is configured, the following APIs for statistical queries are effective:
// Get the raw state data of obj_core into stats, allowed data length is stats_len
int k_obj_core_stats_raw ( struct k_obj_core *obj_core, void *stats, size_t stats_len ) ;
// Get the state data of obj_core into stats, allowed data length is stats_len
int k_obj_core_stats_query ( struct k_obj_core *obj_core, void *stats, size_t stats_len ) ;
// Reset obj_core's state
int k_obj_core_stats_reset ( struct k_obj_core *obj_core ) ;
// Disable statistical state
int k_obj_core_stats_disable ( struct k_obj_core *obj_core ) ;
// Enable statistical state
int k_obj_core_stats_enable ( struct k_obj_core *obj_core ) ;
k_obj_type_stats_init
initializes by calling the callback functions required by struct k_obj_core_stats_desc
. The correspondence between the callback functions and the APIs for obtaining state is as follows:
-
raw
:k_obj_core_stats_raw
-
query
:k_obj_core_stats_query
-
reset
:k_obj_core_stats_reset
-
disable
:k_obj_core_stats_disable
-
enable
:k_obj_core_stats_enable
These callbacks are not mandatory to implement; when the callback is assigned NULL
, calling the corresponding API will return -ENOTSUP
.
Additionally, struct k_obj_core_stats_desc
also requires specifying raw_size
and query_size
. When executing k_obj_core_stats_raw
and k_obj_core_stats_query
, it will compare the passed parameter stats_len
. If they do not match, it will return -EINVAL
.
Example code for obtaining state:
int status;
// Enable statistics
k_obj_core_stats_enable ( K_OBJ_CORE (( struct my_test_manage* ) mgr_handle [i] )) ;
// Get state data
status = k_obj_core_stats_query ( K_OBJ_CORE (( struct my_test_manage* ) mgr_handle [i] ) ,
&my_stats, sizeof ( my_stats )) ;
if ( status == 0 ) {
shell_print ( shell, "avg fetch time %u", my_stats.avg_fetch_time ) ;
}
// Get raw data
status = k_obj_core_stats_raw ( K_OBJ_CORE (( struct my_test_manage* ) mgr_handle [i] ) ,
&my_raw, sizeof ( my_raw )) ;
if ( status == 0 ) {
shell_print ( shell, "total fetch time %u fetch cnt", my_raw.total_fetch_time, my_raw.fetch_cnt ) ;
}
// Reset statistical data
k_obj_core_stats_reset ( K_OBJ_CORE (( struct my_test_manage* ) mgr_handle [i] )) ;
// Disable statistics
k_obj_core_stats_disable ( K_OBJ_CORE (( struct my_test_manage* ) mgr_handle [i] )) ;
References
https://docs.zephyrproject.org/3.5.0/kernel/object_cores/index.html