1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
|
/***************************************************************************
* 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
**************************************************************************/
// Portions of this file were generated with AI assistance.
/* This test drives the trace entry update paths -- the blocks guarded by
TX_ENABLE_EVENT_TRACE that go back and patch a trace entry after the call
they belong to has finished its work.
Those blocks all have the same shape:
if (entry_ptr != TX_NULL)
{
if (time_stamp == entry_ptr -> tx_trace_buffer_entry_time_stamp)
{
entry_ptr -> tx_trace_buffer_entry_information_field_N = ...;
}
}
entry_ptr comes from _tx_trace_buffer_current_ptr, which stays TX_NULL until
tx_trace_enable() is called at run time. Compiling with TX_ENABLE_EVENT_TRACE
is therefore not enough to reach them, and the trace_build configuration on
its own never does: the only other test that enables tracing --
threadx_trace_basic_test -- exercises the enable API itself and never calls
tx_block_allocate or tx_byte_allocate at all. So the update blocks in
tx_block_allocate.c, tx_byte_allocate.c, tx_thread_system_suspend.c and
tx_thread_system_resume.c were absent from the coverage report's covered set
while appearing in its denominator.
Both allocators are driven twice, because they carry two of these blocks
each and only one is on the path that succeeds immediately: the second sits
after a suspension, where the allocation completes in the releasing thread's
context. Hence the second thread, which exists only to release what
thread 0 is waiting on.
The test is built in all five configurations, and four of them do not define
TX_ENABLE_EVENT_TRACE. There tx_trace_enable reports TX_FEATURE_NOT_ENABLED,
the update blocks do not exist, and the allocation sequence below is still a
valid exercise of both allocators' suspension paths -- so the test is
meaningful in every configuration rather than skipped in four. */
#include <stdio.h>
#include "tx_api.h"
#define TX_SOURCE_CODE
#include "tx_trace.h"
void test_control_return(UINT status);
static TX_THREAD thread_0;
static TX_THREAD thread_1;
static TX_BLOCK_POOL block_pool_0;
static TX_BYTE_POOL byte_pool_0;
static TX_SEMAPHORE semaphore_0;
/* The trace buffer is sized generously on purpose. The inner test of each
update block compares the saved time stamp against the one in the entry, and
a buffer small enough to wrap between the call and its completion would
overwrite the entry and take the false branch every time -- the block would
be reached and its body still never run. */
static UCHAR trace_buffer[16384];
/* Enough storage for several 20-byte blocks on both 32-bit and 64-bit builds. */
static UCHAR block_pool_area[100];
static UCHAR byte_pool_area[512];
/* Allocated by thread 0 and released by thread 1, which is what lets thread 0's
suspended allocations complete. */
static void *held_block;
static void *held_bytes;
static UINT error = 0;
static void thread_0_entry(ULONG thread_input);
static void thread_1_entry(ULONG thread_input);
/* Define what the initial system looks like. */
#ifdef CTEST
void test_application_define(void *first_unused_memory)
#else
void threadx_trace_entry_update_application_define(void *first_unused_memory)
#endif
{
UINT status;
CHAR *pointer;
/* Setup a pointer. */
pointer = (CHAR *) first_unused_memory;
/* Create the pools before tracing is enabled, so that the events this test
cares about are the allocations rather than the creates. */
status = tx_block_pool_create(&block_pool_0, "trace entry update block pool",
20, block_pool_area, sizeof(block_pool_area));
if (status != TX_SUCCESS)
{
printf("Running Trace Entry Update Test..................................... ERROR #1\n");
test_control_return(1);
}
status = tx_byte_pool_create(&byte_pool_0, "trace entry update byte pool",
byte_pool_area, sizeof(byte_pool_area));
if (status != TX_SUCCESS)
{
printf("Running Trace Entry Update Test..................................... ERROR #2\n");
test_control_return(1);
}
/* Turn tracing on. This is the whole point of the test: without it
_tx_trace_buffer_current_ptr stays TX_NULL and every update block below
takes its guarding false branch. */
_tx_trace_initialize();
status = tx_trace_enable(trace_buffer, sizeof(trace_buffer), 16);
#ifdef TX_ENABLE_EVENT_TRACE
if (status != TX_SUCCESS)
{
printf("Running Trace Entry Update Test..................................... ERROR #3\n");
test_control_return(1);
}
#else
/* Four of the five build configurations reach this branch. The rest of the
test still runs; it just is not exercising any update block. */
if (status != TX_FEATURE_NOT_ENABLED)
{
printf("Running Trace Entry Update Test..................................... ERROR #4\n");
test_control_return(1);
}
#endif
/* Register an object with no name at all, now that tracing is on. The name
copy in _tx_trace_object_register guards against a TX_NULL name and
breaks out of the copy loop, and nothing else in the suite takes that
branch with tracing enabled -- threadx_trace_basic_test makes the
equivalent call only under #ifndef TX_ENABLE_EVENT_TRACE, against the
no-op stub.
A semaphore rather than a thread, deliberately: for a thread object the
register function dereferences the pointer it is handed to read the
thread's priority, so that type needs a real TX_THREAD behind it. */
status = tx_semaphore_create(&semaphore_0, TX_NULL, 1);
if (status != TX_SUCCESS)
{
printf("Running Trace Entry Update Test..................................... ERROR #5\n");
test_control_return(1);
}
/* Thread 0 drives the test and must run first, so it takes the higher
priority -- the lower number. */
status = tx_thread_create(&thread_0, "thread 0", thread_0_entry, 0,
pointer, TEST_STACK_SIZE_PRINTF,
15, 15, TX_NO_TIME_SLICE, TX_AUTO_START);
pointer = pointer + TEST_STACK_SIZE_PRINTF;
if (status != TX_SUCCESS)
{
printf("Running Trace Entry Update Test..................................... ERROR #6\n");
test_control_return(1);
}
/* Thread 1 releases what thread 0 waits on. It runs only while thread 0 is
suspended, which is exactly when it is needed. */
status = tx_thread_create(&thread_1, "thread 1", thread_1_entry, 0,
pointer, TEST_STACK_SIZE_PRINTF,
16, 16, TX_NO_TIME_SLICE, TX_AUTO_START);
pointer = pointer + TEST_STACK_SIZE_PRINTF;
if (status != TX_SUCCESS)
{
printf("Running Trace Entry Update Test..................................... ERROR #7\n");
test_control_return(1);
}
}
static void thread_0_entry(ULONG thread_input)
{
UINT status;
void *block_ptr;
void *byte_ptr;
UINT i;
/* Empty the block pool. The first allocation takes the immediate-success
path through tx_block_allocate, which carries the first update block.
The exact capacity is port-dependent because each block has a pointer-
sized header, so allocate until the pool reports that it is empty. */
held_block = TX_NULL;
for (i = 0; i < 6u; i++)
{
status = tx_block_allocate(&block_pool_0, &block_ptr, TX_NO_WAIT);
if (status == TX_NO_MEMORY)
{
break;
}
if (status != TX_SUCCESS)
{
error++;
break;
}
/* Keep the first one for thread 1 to give back. */
if (held_block == TX_NULL)
{
held_block = block_ptr;
}
}
if ((status != TX_NO_MEMORY) || (held_block == TX_NULL))
{
error++;
}
/* The pool is empty now, so this suspends. It completes in thread 1's
context when the block comes back, which is the second update block in
tx_block_allocate -- and the suspend and resume it goes through carry the
update blocks in tx_thread_system_suspend and tx_thread_system_resume. */
status = tx_block_allocate(&block_pool_0, &block_ptr, TX_WAIT_FOREVER);
if (status != TX_SUCCESS)
{
printf("Running Trace Entry Update Test..................................... ERROR #8\n");
test_control_return(1);
}
/* Now the same shape for the byte pool. Take most of it first, on the path
that succeeds immediately. */
status = tx_byte_allocate(&byte_pool_0, &held_bytes, 300, TX_NO_WAIT);
if (status != TX_SUCCESS)
{
printf("Running Trace Entry Update Test..................................... ERROR #9\n");
test_control_return(1);
}
/* Not enough left, so this one suspends and is satisfied by thread 1's
release. */
status = tx_byte_allocate(&byte_pool_0, &byte_ptr, 300, TX_WAIT_FOREVER);
if (status != TX_SUCCESS)
{
printf("Running Trace Entry Update Test..................................... ERROR #10\n");
test_control_return(1);
}
/* Sleep, so that thread 1 gets to run out its entry function and suspend
itself. That leaves nothing ready to execute, which is a third update
block in tx_thread_system_suspend -- the one on the path that sets
_tx_thread_execute_ptr to TX_NULL. The two suspensions above never reach
it, because the other thread was always ready to take over. */
status = tx_thread_sleep(5);
if (status != TX_SUCCESS)
{
printf("Running Trace Entry Update Test..................................... ERROR #11\n");
test_control_return(1);
}
#ifdef TX_ENABLE_EVENT_TRACE
/* Every update block above compares a saved time stamp against the one in
the entry, so a port whose time source never changes makes all of them
take their true branch for the wrong reason: not because the entry
survived, but because every entry reads the same. Check the buffer holds
at least one entry with a time stamp the port actually supplied. The
events written by this point number in the dozens, so a single zero
reading from a real clock cannot fail this. */
{
TX_TRACE_BUFFER_ENTRY *entry_ptr;
UINT stamped;
stamped = TX_FALSE;
for (entry_ptr = _tx_trace_buffer_start_ptr; entry_ptr < _tx_trace_buffer_end_ptr; entry_ptr++)
{
if (entry_ptr -> tx_trace_buffer_entry_time_stamp != ((ULONG) 0))
{
stamped = TX_TRUE;
break;
}
}
if (stamped != TX_TRUE)
{
printf("Running Trace Entry Update Test..................................... ERROR #13\n");
test_control_return(1);
}
}
#endif
if (error)
{
printf("Running Trace Entry Update Test..................................... ERROR #12\n");
test_control_return(1);
}
else
{
printf("Running Trace Entry Update Test..................................... SUCCESS!\n");
test_control_return(0);
}
}
static void thread_1_entry(ULONG thread_input)
{
UINT status;
/* Reached only when thread 0 has suspended on the empty block pool. */
status = tx_block_release(held_block);
if (status != TX_SUCCESS)
{
error++;
}
/* And again when thread 0 has suspended on the byte pool. Thread 0 preempts
on the release above, runs until it suspends a second time, and control
comes back here. */
status = tx_byte_release(held_bytes);
if (status != TX_SUCCESS)
{
error++;
}
/* Thread 0 reports the result; this thread has nothing left to do. */
tx_thread_suspend(&thread_1);
}
|