Object Cores

Object cores are a kernel debugging tool that can be used to both identify and perform operations on registered objects.

Object Core Concepts

Each instance of an object embeds an object core field named obj_core. An object core links the object to its object type, and each object type lets debugging tools enumerate the objects of that type. Object types are linked together via a singly linked list. Together, this allows debugging tools to traverse all the objects in the system.

An object type enumerates its objects from two places:

  • Its permanent objects, which are statically defined instances such as those created with K_SEM_DEFINE. They are walked in place from their iterable section and are never registered at run time.

  • A bounded registry of the objects initialized at run time, for instance a semaphore in a driver’s data structure initialized with k_sem_init(). The registry only references the objects; it never stores anything inside them.

Because of that, an object may be initialized in any storage, initialized again in place, or discarded without any object core call: the registry cannot be corrupted by an object that no longer exists. Some rules follow from this model:

  • An object located in the running thread’s stack or in the interrupt stack is not registered: its life ends with the stack frame. The object type counts such objects in its skipped field. Other stacks are not recognized, such as the privileged stack a user thread’s system calls run on or an exception stack specific to an architecture; an object there is registered and stays reported until its storage is reused.

  • When the registry is full, further objects are not registered and the object type counts them in its dropped field. The registry size is set with CONFIG_OBJ_CORE_MAX_DYNAMIC_OBJECTS.

  • An object that was discarded without being unregistered stays reported until its storage is reused: the walk recognizes the reused storage by the missing type tag and drops the entry. The kernel unregisters objects itself when it releases them: on thread abort, in k_object_free(), k_timer_cleanup(), k_msgq_cleanup() and k_stack_cleanup(), and, with CONFIG_OBJ_CORE_EVICT_ON_FREE, when memory is returned to a heap, a memory slab or a memory blocks allocator. Code that ends an object’s life in another way may call k_obj_core_unlink() so that the object stops being reported at once; the type tag check is only a backstop, and it reads the object’s former storage, which must still be mapped.

  • Once a type’s dropped count is not zero, objects exist that no walk reports, so an inventory is complete only while it is zero.

Object cores have been integrated into the following kernel objects:

Developers are free to integrate them if desired into other objects within their projects. The Object cores sample shows the facility step by step and the Object monitor sample uses it to watch the objects of a running application.

Object Core Statistics Concepts

A variety of kernel objects allow for the gathering and reporting of statistics. Object cores provide a uniform means to retrieve that information via object core statistics. When enabled, the object type contains a pointer to a statistics descriptor that defines the various operations that have been enabled for interfacing with the object’s statistics. Additionally, the object core contains a pointer to the “raw” statistical information associated with that object. Raw data is the raw, unmanipulated data associated with the statistics. Queried data may be “raw”, but it may also have been manipulated in some way by calculation (such as determining an average).

The following table indicates both what objects have been integrated into the object core statistics as well as the structures used for both “raw” and “queried” data.

Object

Raw Data Type

Query Data Type

struct mem_slab

struct mem_slab_info

struct sys_memory_stats

struct sys_mem_blocks

struct sys_mem_blocks_info

struct sys_memory_stats

struct k_thread

struct k_cycle_stats

struct k_thread_runtime_stats

struct _cpu

struct k_cycle_stats

struct k_thread_runtime_stats

struct z_kernel

struct k_cycle_stats[num CPUs]

struct k_thread_runtime_stats

Implementation

Defining a New Object Type

An object type is a global variable of type k_obj_type. When the object struct has statically defined instances in an iterable section, the type is defined at build time with K_OBJ_TYPE_DEFINE and those instances become its permanent objects. The following code shows how a new object type can be defined for use with object cores and object core statistics.

/* Unique object type ID */

#define K_OBJ_TYPE_MY_NEW_TYPE  K_OBJ_TYPE_ID_GEN("UNIQ")

struct my_obj_type_raw_info {
    ...
};

struct my_obj_type_query_stats {
    ...
};

struct my_new_obj {
    ...
    struct k_obj_core obj_core;
    struct my_obj_type_raw_info  info;
};

struct k_obj_core_stats_desc my_obj_type_stats_desc = {
    .raw_size = sizeof(struct my_obj_type_raw_stats),
    .query_size = sizeof(struct my_obj_type_query_stats),
    .raw = my_obj_type_stats_raw,
    .query = my_obj_type_stats_query,
    .reset = my_obj_type_stats_reset,
    .disable = NULL,    /* Stats gathering is always on */
    .enable = NULL,     /* Stats gathering is always on */
};

K_OBJ_TYPE_DEFINE_STATS(my_obj_type, my_new_obj, K_OBJ_TYPE_MY_NEW_TYPE,
                        &my_obj_type_stats_desc, info);

A type whose objects are not in an iterable section is initialized at run time instead, before any of its objects. A permanent array of objects can be registered as the type’s range so that its objects need no registry entry.

struct k_obj_type  my_obj_type;
struct my_new_obj  my_objects[8];

void my_obj_type_init(void)
{
    z_obj_type_init(&my_obj_type, K_OBJ_TYPE_MY_NEW_TYPE,
                    offsetof(struct my_new_obj, obj_core));
    k_obj_type_init_range(&my_obj_type, my_objects,
                          &my_objects[ARRAY_SIZE(my_objects)],
                          sizeof(struct my_new_obj), false);
    k_obj_type_stats_init(&my_obj_type, &my_obj_type_stats_desc);
}

Initializing a New Object Core

Kernel objects that have already been integrated into the object core framework automatically have their object cores initialized when the object is initialized. However, developers that wish to add their own objects into the framework need to both initialize the object core and register it. Registering an object that belongs to the type’s permanent range has no effect, as it is reported from the range. The following code builds on the example above and initializes the object core.

void my_new_obj_init(struct my_new_obj *new_obj)
{
    ...
    k_obj_core_init(K_OBJ_CORE(new_obj), &my_obj_type);
    k_obj_core_link(K_OBJ_CORE(new_obj));
    k_obj_core_stats_register(K_OBJ_CORE(new_obj), &new_obj->raw_stats,
                              sizeof(struct my_obj_type_raw_info));
}

Walking a List of Object Cores

Two routines exist for walking the object cores of an object type. These are k_obj_type_walk_locked() and k_obj_type_walk_unlocked(). Both visit the permanent objects of the type first and the registered objects next. The following code builds upon the example above and prints the addresses of all the objects of that new object type.

int walk_op(struct k_obj_core *obj_core, void *data)
{
    uint8_t *ptr;

    ptr = obj_core;
    ptr -= obj_core->type->obj_core_offset;

    printk("%p\n", ptr);

    return 0;
}

void print_object_addresses(void)
{
    struct k_obj_type *obj_type;

    /* Find the object type */

    obj_type = k_obj_type_find(K_OBJ_TYPE_MY_NEW_TYPE);

    /* Walk the list of objects */

    k_obj_type_walk_unlocked(obj_type, walk_op, NULL);
}

Object Core Statistics Querying

The following code builds on the examples above and shows how an object integrated into the object core statistics framework can both retrieve queried data and reset the stats associated with the object.

struct my_new_obj my_obj;

...

void my_func(void)
{
    struct my_obj_type_query_stats  my_stats;
    int  status;

    my_obj_type_init(&my_obj);

    ...

    status = k_obj_core_stats_query(K_OBJ_CORE(&my_obj),
                                    &my_stats, sizeof(my_stats));
    if (status != 0) {
        /* Failed to get stats */
        ...
    } else {
        k_obj_core_stats_reset(K_OBJ_CORE(&my_obj));
    }

    ...
}

Configuration Options

Related configuration options:

API Reference

Object Core APIs
Object Core Statistics APIs