summaryrefslogtreecommitdiff
path: root/test/smp/regression/threadx_trace_enable_trace_test.c
blob: 687c9a09fbb44dd30e850f8a45fba5fcdd8eff16 (plain)
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
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
/***************************************************************************
 * 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 two trace inserts the enable service writes itself.

   The last thing the enable does is record two running events, so that a buffer
   handed to an analysis tool is never empty. Those two inserts are the only ones
   in the kernel that run inside the service that turns tracing on, which
   constrains them: the buffer pointer and the event enable bits are both set a
   few lines above, so neither can be found in any other state on the way in.
   What is left is the system state the enable runs in, whether the first of the
   two entries is the one that wraps the buffer, and what a registered
   buffer-full notification does when it is called from inside the first insert
   and the second has still to run.

   The enable is therefore driven with a buffer sized to hold exactly one event,
   which puts both inserts on the wrap boundary, and with three different
   notifications: one that only counts, one that filters the class the running
   event belongs to, and one that turns tracing off again - the shape an
   application that wants a single bufferful of history would use.

   The SMP insert reads the system state of the core it is running on rather
   than a single global, so the forced-state pass writes the entry of the core
   this test is running on, under the SMP protection primitive. Every thread in
   this suite is created mapped to core 0 alone, but the core is read rather
   than assumed.

   Without event tracing compiled in the enable reports the feature as disabled,
   which is all there is to check.  */


#include   <stdio.h>
#include   "tx_api.h"
#define    TX_SOURCE_CODE

/* The system state is per core on SMP, and tx_thread.h remaps the name to a
   function call returning the running core's entry unless this macro says the
   file wants the array itself. The forced-state pass below has to write it, so
   it needs the array.  */

#define    TX_THREAD_SMP_SOURCE_CODE
#include   "tx_trace.h"
#include   "tx_thread.h"
#include   "tx_initialize.h"


static unsigned long   thread_0_counter =  0;
static TX_THREAD       thread_0;

/* The trace buffer, declared as ULONGs so that the control header it starts
   with is aligned.  */

static ULONG           trace_buffer[256];

#define TRACE_REGISTRY_ENTRIES      4

static ULONG           buffer_full_count =  0;


/* 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 smallest buffer the enable accepts for the given number of registry
   entries: a control header, the registry, and room for one event. Sized from
   the kernel's own structures rather than guessed, so the buffer holds exactly
   one entry and both of the enable's inserts land on the wrap boundary.  */

static ULONG  smallest_buffer(ULONG registry_entries)
{

    return((ULONG) ((sizeof(TX_TRACE_HEADER)) +
                    ((sizeof(TX_TRACE_OBJECT_ENTRY)) * registry_entries) +
                    (sizeof(TX_TRACE_BUFFER_ENTRY))));
}


/* A notification that only counts.  */

static VOID  notify_count(void *buffer)
{

    buffer_full_count++;
}


/* A notification that filters the class the running event belongs to, so the
   second of the enable's two inserts is reached with its event filtered out.  */

static VOID  notify_filter(void *buffer)
{

    buffer_full_count++;
    tx_trace_event_filter(TX_TRACE_INTERNAL_EVENTS);
}


/* A notification that turns tracing off, which is what an application capturing
   a single bufferful of history does. The second of the enable's two inserts is
   then reached with no buffer to write into.  */

static VOID  notify_disable(void *buffer)
{

    buffer_full_count++;
    tx_trace_disable();
}


/* Enable and immediately disable, with the system state forced to the value the
   inserts have to see. One SMP protection covers the write, the two services
   and the restore, so nothing else observes the forced value; the state is
   non-zero throughout, so the preemption check the enable ends with is a
   no-op.  */

static UINT  enable_with_system_state(ULONG system_state)
{

TX_INTERRUPT_SAVE_AREA

UINT    status;
UINT    core;
ULONG   saved_system_state;


    TX_DISABLE
    core =  tx_thread_smp_core_get();
    saved_system_state =  _tx_thread_system_state[core];
    _tx_thread_system_state[core] =  system_state;

    status =  tx_trace_enable(trace_buffer, sizeof(trace_buffer), TRACE_REGISTRY_ENTRIES);

    if (status == TX_SUCCESS)
    {
        status =  tx_trace_disable();
    }

    _tx_thread_system_state[core] =  saved_system_state;
    TX_RESTORE

    return(status);
}

#endif


/* Define what the initial system looks like.  */

#ifdef CTEST
void test_application_define(void *first_unused_memory)
#else
void    threadx_trace_enable_trace_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 Enable Trace Test.................................... ERROR #1\n");
        test_control_return(1);
    }
}


/* Define the test thread.  */

static void    thread_0_entry(ULONG thread_input)
{

UINT    status;


    /* Increment thread 0 counter.  */
    thread_0_counter++;

    /* Inform user of success getting to this test.  */
    printf("Running Trace Enable Trace Test.................................... ");

    /* A buffer with room for many events: neither insert wraps it.  */
    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);
    }

    status =  tx_trace_disable();

    if (status != TX_SUCCESS)
    {

        printf("ERROR #4\n");
        test_control_return(1);
    }

    /* The state an interrupt presents: the inserts record the interrupted
       thread instead of a priority.  */
    if (enable_with_system_state((ULONG) 1) != TX_SUCCESS)
    {

        printf("ERROR #5\n");
        test_control_return(1);
    }

    /* Initialization context: the inserts have no thread to record at all.  */
    if (enable_with_system_state(TX_INITIALIZE_IN_PROGRESS) != TX_SUCCESS)
    {

        printf("ERROR #6\n");
        test_control_return(1);
    }

    /* A buffer with room for exactly one event, and no notification
       registered: both inserts wrap it.  */
    status =  tx_trace_enable(trace_buffer, smallest_buffer(TRACE_REGISTRY_ENTRIES), TRACE_REGISTRY_ENTRIES);

    if (status != TX_SUCCESS)
    {

        printf("ERROR #7\n");
        test_control_return(1);
    }

    status =  tx_trace_disable();

    if (status != TX_SUCCESS)
    {

        printf("ERROR #8\n");
        test_control_return(1);
    }

    /* The same, with a notification registered. Both of the enable's inserts
       wrap, so it is called at least twice.  */
    buffer_full_count =  0;

    status =  tx_trace_buffer_full_notify(notify_count);

    if (status != TX_SUCCESS)
    {

        printf("ERROR #9\n");
        test_control_return(1);
    }

    status =  tx_trace_enable(trace_buffer, smallest_buffer(TRACE_REGISTRY_ENTRIES), TRACE_REGISTRY_ENTRIES);

    if (status != TX_SUCCESS)
    {

        printf("ERROR #10\n");
        test_control_return(1);
    }

    if (buffer_full_count < 2)
    {

        printf("ERROR #11\n");
        test_control_return(1);
    }

    status =  tx_trace_disable();

    if (status != TX_SUCCESS)
    {

        printf("ERROR #12\n");
        test_control_return(1);
    }

    /* A notification that filters the running event's own class. It is called
       from inside the first insert, so the second one is reached with its event
       filtered out and records nothing.  */
    buffer_full_count =  0;

    status =  tx_trace_buffer_full_notify(notify_filter);

    if (status != TX_SUCCESS)
    {

        printf("ERROR #13\n");
        test_control_return(1);
    }

    status =  tx_trace_enable(trace_buffer, smallest_buffer(TRACE_REGISTRY_ENTRIES), TRACE_REGISTRY_ENTRIES);

    if (status != TX_SUCCESS)
    {

        printf("ERROR #14\n");
        test_control_return(1);
    }

    /* The second insert took the filtered arm, so it never wrapped and the
       notification was called exactly once.  */
    if (buffer_full_count != 1)
    {

        printf("ERROR #15\n");
        test_control_return(1);
    }

    status =  tx_trace_disable();

    if (status != TX_SUCCESS)
    {

        printf("ERROR #16\n");
        test_control_return(1);
    }

    status =  tx_trace_event_unfilter(TX_TRACE_INTERNAL_EVENTS);

    if (status != TX_SUCCESS)
    {

        printf("ERROR #17\n");
        test_control_return(1);
    }

    /* A notification that turns tracing off. The second insert is reached with
       no buffer to write into, and the enable still reports success because it
       did everything it was asked to.  */
    buffer_full_count =  0;

    status =  tx_trace_buffer_full_notify(notify_disable);

    if (status != TX_SUCCESS)
    {

        printf("ERROR #18\n");
        test_control_return(1);
    }

    status =  tx_trace_enable(trace_buffer, smallest_buffer(TRACE_REGISTRY_ENTRIES), TRACE_REGISTRY_ENTRIES);

    if (status != TX_SUCCESS)
    {

        printf("ERROR #19\n");
        test_control_return(1);
    }

    if (buffer_full_count != 1)
    {

        printf("ERROR #20\n");
        test_control_return(1);
    }

    /* Tracing is already off, so there is nothing left for the disable to
       do.  */
    status =  tx_trace_disable();

    if (status != TX_NOT_DONE)
    {

        printf("ERROR #21\n");
        test_control_return(1);
    }

    status =  tx_trace_buffer_full_notify(TX_NULL);

    if (status != TX_SUCCESS)
    {

        printf("ERROR #22\n");
        test_control_return(1);
    }

    printf("SUCCESS!\n");
    test_control_return(0);

#endif
}