/*************************************************************************** * Copyright (c) 2026 Eclipse ThreadX contributors * * This program and the accompanying materials are made available under the * terms of the MIT License which is available at * https://opensource.org/licenses/MIT. * * AI Disclosure: This file was largely AI-generated by Claude Code (Opus 5). * The AI-generated portions may be considered public domain (CC0-1.0) * and not subject to the project's licence. The human contributor has * reviewed and verified that the code is correct. * * SPDX-License-Identifier: MIT and CC0-1.0 **************************************************************************/ /* This test drives the three decisions in the trace object registry that ordinary use never reaches. The first is the name copy. It stops at the string's terminator, so an object whose name is at least as long as a registry entry's name field leaves the loop by its own count instead. The second is the same copy handed no name at all, which it recognises and stops on. The third is the choice the registry search makes when it has already found one free entry and comes across a second: it prefers the later one unless the earlier one has never held a valid object, and nothing the kernel registers produces an entry that is both in use and of no valid type. The third is therefore driven by registering an object of the type the registry treats as not valid, giving the search start back to that entry, and registering again - so the search meets a free entry that has never held a valid object, and keeps it. Without event tracing compiled in the registry does not exist, so the test only checks that the trace services report the feature as disabled. */ #include #include "tx_api.h" #define TX_SOURCE_CODE #include "tx_trace.h" /* A name at least as long as a registry entry's name field, so the copy runs to its own limit rather than to a terminator. */ static CHAR long_name[] = "an object name longer than the registry keeps room for"; static unsigned long thread_0_counter = 0; static TX_THREAD thread_0; static TX_QUEUE queue_0; static TX_QUEUE queue_1; static ULONG queue_0_area[4]; static ULONG queue_1_area[4]; static UCHAR trace_buffer[2048]; #define TRACE_REGISTRY_ENTRIES 16 /* Stand-in objects, used only as registry keys. The registry stores the pointer it is given and never follows it. */ #define FILLER_OBJECTS (TRACE_REGISTRY_ENTRIES + 1) static ULONG filler_object[FILLER_OBJECTS]; static ULONG probe_object; /* Define thread prototypes. */ static void thread_0_entry(ULONG thread_input); /* Prototype for test control return. */ void test_control_return(UINT status); #ifdef TX_ENABLE_EVENT_TRACE /* The registry index holding the given object, or the total number of entries if it holds none. */ static ULONG registry_index_of(VOID *object_ptr) { ULONG index; for (index = 0; index < _tx_trace_total_registry_entries; index++) { if (_tx_trace_registry_start_ptr[index].tx_trace_object_entry_thread_pointer == (ULONG) object_ptr) { break; } } return(index); } #endif /* Define what the initial system looks like. */ #ifdef CTEST void test_application_define(void *first_unused_memory) #else void threadx_trace_object_registry_application_define(void *first_unused_memory) #endif { UINT status; CHAR *pointer; /* Put first available memory address into a character pointer. */ pointer = (CHAR *) first_unused_memory; /* Put system definition stuff in here, e.g. thread creates and other assorted create information. */ status = tx_thread_create(&thread_0, "thread 0", thread_0_entry, 1, pointer, TEST_STACK_SIZE_PRINTF, 16, 16, TX_NO_TIME_SLICE, TX_AUTO_START); pointer = pointer + TEST_STACK_SIZE_PRINTF; /* Check for status. */ if (status != TX_SUCCESS) { printf("Running Trace Object Registry Test................................. ERROR #1\n"); test_control_return(1); } } /* Define the test thread. */ static void thread_0_entry(ULONG thread_input) { UINT status; #ifdef TX_ENABLE_EVENT_TRACE TX_INTERRUPT_SAVE_AREA ULONG filler; ULONG fillers_used; ULONG not_valid_index; ULONG second_free_index; VOID *second_free_object; #endif /* Increment thread 0 counter. */ thread_0_counter++; /* Inform user of success getting to this test. */ printf("Running Trace Object Registry Test................................. "); /* Enable event tracing, so that every create below registers. */ status = tx_trace_enable(trace_buffer, sizeof(trace_buffer), TRACE_REGISTRY_ENTRIES); #ifndef TX_ENABLE_EVENT_TRACE if (status != TX_FEATURE_NOT_ENABLED) { printf("ERROR #2\n"); test_control_return(1); } printf("SUCCESS!\n"); test_control_return(0); #else if (status != TX_SUCCESS) { printf("ERROR #3\n"); test_control_return(1); } /* An object whose name is longer than a registry entry keeps room for. The copy runs out of room rather than reaching a terminator. */ status = tx_queue_create(&queue_0, long_name, TX_1_ULONG, queue_0_area, sizeof(queue_0_area)); if (status != TX_SUCCESS) { printf("ERROR #4\n"); test_control_return(1); } /* The name field is filled to its last byte and terminated there. */ if (_tx_trace_registry_start_ptr[registry_index_of(&queue_0)].tx_trace_object_entry_name[TX_TRACE_OBJECT_REGISTRY_NAME - 1] != ((UCHAR) 0)) { printf("ERROR #5\n"); test_control_return(1); } /* An object with no name at all. The copy recognises it and stops before reading anything. */ status = tx_queue_create(&queue_1, TX_NULL, TX_1_ULONG, queue_1_area, sizeof(queue_1_area)); if (status != TX_SUCCESS) { printf("ERROR #6\n"); test_control_return(1); } if (_tx_trace_registry_start_ptr[registry_index_of(&queue_1)].tx_trace_object_entry_name[0] != ((UCHAR) 0)) { printf("ERROR #7\n"); test_control_return(1); } /* Fill every remaining registry entry, the first of them with the object type the registry treats as not valid. The search below has to walk the whole list, and it stops at the first entry that has never been used at all, so no entry may be left unused. */ TX_DISABLE fillers_used = 0; while ((_tx_trace_available_registry_entries != ((ULONG) 0)) && (fillers_used < FILLER_OBJECTS)) { _tx_trace_object_register((fillers_used == ((ULONG) 0)) ? TX_TRACE_OBJECT_TYPE_NOT_VALID : TX_TRACE_OBJECT_TYPE_QUEUE, &filler_object[fillers_used], "filler", ((ULONG) 0), ((ULONG) 0)); fillers_used++; } TX_RESTORE if ((_tx_trace_available_registry_entries != ((ULONG) 0)) || (fillers_used == ((ULONG) 0))) { printf("ERROR #8\n"); test_control_return(1); } not_valid_index = registry_index_of(&filler_object[0]); if (not_valid_index >= _tx_trace_total_registry_entries) { printf("ERROR #9\n"); test_control_return(1); } /* Free two entries, the one holding no valid type last, so the search starts there. The search then finds it first and meets the other one afterwards, which is the arrangement the choice below is made in. */ second_free_index = (not_valid_index + ((ULONG) 1)) % _tx_trace_total_registry_entries; second_free_object = (VOID *) _tx_trace_registry_start_ptr[second_free_index].tx_trace_object_entry_thread_pointer; TX_DISABLE _tx_trace_object_unregister(second_free_object); _tx_trace_object_unregister(&filler_object[0]); /* The search keeps the entry that has never held a valid object rather than moving on to the later free one. */ _tx_trace_object_register(TX_TRACE_OBJECT_TYPE_QUEUE, &probe_object, "probe", ((ULONG) 0), ((ULONG) 0)); TX_RESTORE if (registry_index_of(&probe_object) != not_valid_index) { printf("ERROR #10\n"); test_control_return(1); } status = tx_queue_delete(&queue_0); if (status != TX_SUCCESS) { printf("ERROR #11\n"); test_control_return(1); } status = tx_queue_delete(&queue_1); if (status != TX_SUCCESS) { printf("ERROR #12\n"); test_control_return(1); } status = tx_trace_disable(); if (status != TX_SUCCESS) { printf("ERROR #13\n"); test_control_return(1); } printf("SUCCESS!\n"); test_control_return(0); #endif }