summaryrefslogtreecommitdiff
path: root/FreeRTOS-Plus/Source/FreeRTOS-Plus-Trace/trcBase.c
blob: c80915cc944e4c66d7afdaf9416730a6ded04e59 (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
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
/*******************************************************************************
 * FreeRTOS+Trace v2.3.0 Recorder Library
 * Percepio AB, www.percepio.com
 *
 * trcBase.c
 *
 * Core functionality of the trace recorder library.
 *
 * Terms of Use
 * This software is copyright Percepio AB. The recorder library is free for
 * use together with Percepio products. You may distribute the recorder library
 * in its original form, including modifications in trcPort.c and trcPort.h
 * given that these modification are clearly marked as your own modifications
 * and documented in the initial comment section of these source files. 
 * This software is the intellectual property of Percepio AB and may not be 
 * sold or in other ways commercially redistributed without explicit written 
 * permission by Percepio AB.
 *
 * Disclaimer 
 * The trace tool and recorder library is being delivered to you AS IS and 
 * Percepio AB makes no warranty as to its use or performance. Percepio AB does 
 * not and cannot warrant the performance or results you may obtain by using the 
 * software or documentation. Percepio AB make no warranties, express or 
 * implied, as to noninfringement of third party rights, merchantability, or 
 * fitness for any particular purpose. In no event will Percepio AB, its 
 * technology partners, or distributors be liable to you for any consequential, 
 * incidental or special damages, including any lost profits or lost savings, 
 * even if a representative of Percepio AB has been advised of the possibility 
 * of such damages, or for any claim by any third party. Some jurisdictions do 
 * not allow the exclusion or limitation of incidental, consequential or special 
 * damages, or the exclusion of implied warranties or limitations on how long an 
 * implied warranty may last, so the above limitations may not apply to you.
 *
 * FreeRTOS+Trace is available as Free Edition and in two premium editions.
 * You may use the premium features during 30 days for evaluation.
 * Download FreeRTOS+Trace at http://www.percepio.com/products/downloads/
 *
 * Copyright Percepio AB, 2012.
 * www.percepio.com
 ******************************************************************************/

#include "trcUser.h"
#include "task.h"

#if (configUSE_TRACE_FACILITY == 1)

/*******************************************************************************
 * Static data initializations
 ******************************************************************************/


/*******************************************************************************
 * RecorderData
 *
 * The main data structure. This is the data read by FreeRTOS+Trace, typically
 * through a debugger RAM dump. The recorder access this through the pointer
 * RecorderDataPtr, to allow for dynamic memory allocation as well.
 *
 * On the NXP LPC17xx you may use the secondary RAM bank (AHB RAM) for this
 * purpose. For instance, the LPC1766 has 32 KB AHB RAM which allows for 
 * allocating a buffer size of at least 7500 events without affecting the main
 * RAM. To place RecorderData in this RAM bank, use the below declaration.
 * 
 *     #pragma location="AHB_RAM_MEMORY"
 *     RecorderDataType RecorderData = ...
 * 
 * This of course works for other hardware architectures with additional RAM 
 * banks as well, just replace "AHB_RAM_MEMORY" with the name of the right 
 * address section from the linker file.
 *
 * However, to keep trcBase.c portable and still have a preconfigured IAR demo
 * using AHB RAM, we don't add the pragma directly in trcBase.c but in a header 
 * included where the pragma should go. This is used depending on the setting
 * USE_LINKER_PRAGMA, defined in trcConfig.h.
 * 
 * If using GCC, this is instead done by adding a "section" attribute:
 *
 *     RecorderDataType RecorderData __attribute__ ((section ("name"))) = ...
 *
 * Remember to replace "name" with the correct section name.
 ******************************************************************************/

#if (TRACE_DATA_ALLOCATION == TRACE_DATA_ALLOCATION_STATIC)
#if (USE_LINKER_PRAGMA == 1)
#include "recorderdata_linker_pragma.h"
#endif
RecorderDataType RecorderData =
{
    /* start marker, 12 chars */
    0x01, 0x02, 0x03, 0x04,
    0x71, 0x72, 0x73, 0x74,
    0xF1, 0xF2, 0xF3, 0xF4,

    /* version code - also used to determine endianness */
    VERSION,

    /* minor file format version */
    MINOR_VERSION,
    
    /* irq priority order */
    IRQ_PRIORITY_ORDER,

    /* file size (for control) */
    sizeof(RecorderDataType),

    /* number of events stored so far */
    0,

    /* size of events buffer (in event records, each 4 bytes) */
    EVENT_BUFFER_SIZE,

    /* next free event index (event index, not byte address) */
    0,

    /* buffer is full */ 
    0,

    /* frequency of clock user for timestamps, in Hz - should be 0 here
    as this is used to indicate "not yet initialized" - this is instead
    initialized on the first taskswitch event. */
    0,

    /* the absolute timestamp of the last stored event, modulo frequency */
    0,

    /* the number of seconds so far */
    0,

    /* is recorder active (yes = 1) - note that "close" events are always 
        stored to keep the name-handle mapping updated!*/
    0,

    /* Generated by FreeRTOS+Trace in Team Admin mode. Otherwise this should be "". */
    TEAM_LICENSE_CODE,

    /* debug marker 0 */
    0xF0F0F0F0,

    /* The Object Property Table - holds info of all active objects */ 
    {
        /* Number of object classes, also those not used */
        NCLASSES,

        /* The size in bytes of the object table byte pool */
        DynObjTableSize,

        /* The number of slots/handles available for each class */
        {
            NQueue,
            NSemaphore,
            NMutex,
            NTask,
            NISR
        },

        /* The maximum name length for each object class */
        {
            NameLenQueue,
            NameLenSemaphore,
            NameLenMutex,
            NameLenTask,
            NameLenISR
        },

        /* The total length a property table entry of the class */
        {
            PropertyTableSizeQueue,
            PropertyTableSizeSemaphore,
            PropertyTableSizeMutex,
            PropertyTableSizeTask,
            PropertyTableSizeISR
        },

        /* The start index of each class in the object property table */
        {
            StartIndexQueue,
            StartIndexSemaphore,
            StartIndexMutex,
            StartIndexTask,
            StartIndexISR
        },

        /* the object property table - encoded in a byte array using above 
        definitions */
        {0}
    },

    /* debug marker 1 */
    0xF1F1F1F1,

    /* The Symbol Table - holds all object names used since system 
       startup. Every string is unique, so objects with same name will share 
       an entry. Each name entry has four extra bytes: byte 0-1 is a link 
       reference in an internal linked list, used for fast lookups, byte 2-3 
       holds a reference to a channel label used for vTracePrintF format 
       strings, and byte 4.. holds the object name, followed by a 
       zero-termination.*/
    {
        SYMBOL_TABLE_SIZE,

        /* next free index (0 is reserved to mean NULL) */
        1,

        /* the symbol table byte pool */
        {0},

        /* this is a 64 entry array holding 16-bit references (indexes) 
           to the most recent entry of each checksum - i.e., list heads.*/
        {0},

    },

#if (INCLUDE_FLOAT_SUPPORT == 1)
    /* example float, for float endian detection */
    (float)1.0,
#else
    /* This code signals that no float support is included */
    (uint32_t)0, 
#endif

    /* internalErrorOccured */
    0,
    
    /* debug marker 2 */
    0xF2F2F2F2,
    
    /* The trace description string, can hold any information about the system, 
        e.g., version, configuration. Error messages from the recorder are
        copied to this buffer. Also used for internal error messages.*/
    TRACE_DESCRIPTION,
        
    /* debug marker 3 */
    0xF3F3F3F3,

    /* the event data buffer, size EVENT_BUFFER_SIZE*4 */
    {0},

    /* end markers, used to extract the trace from a RAM dump image */
    0x0A, 0x0B, 0x0C, 0x0D,
    0x71, 0x72, 0x73, 0x74,
    0xF1, 0xF2, 0xF3, 0xF4
};

RecorderDataType* RecorderDataPtr = &RecorderData;

/* This version of the function does nothing as the trace data is statically allocated */
RecorderDataType* xTraceInitTraceData(void)
{
    return 0;
}

#elif (TRACE_DATA_ALLOCATION == TRACE_DATA_ALLOCATION_DYNAMIC)

RecorderDataType* RecorderDataPtr = NULL;

/* This version of the function dynamically allocates the trace data */
RecorderDataType* xTraceInitTraceData(void)
{
    RecorderDataType* tmp = (RecorderDataType*)pvPortMalloc(sizeof(RecorderDataType));

    if (! tmp)
    {
        vTraceError("Malloc failed in xTraceInitTraceData! Reduce size constants in trcConfig.h");
        return NULL;
    }
    
    (void)memset(tmp, 0, sizeof(RecorderDataType));

    tmp->startmarker0 = 0x01;
    tmp->startmarker1 = 0x02;
    tmp->startmarker2 = 0x03;
    tmp->startmarker3 = 0x04;
    tmp->startmarker4 = 0x71;
    tmp->startmarker5 = 0x72;
    tmp->startmarker6 = 0x73;
    tmp->startmarker7 = 0x74;
    tmp->startmarker8 = 0xF1;
    tmp->startmarker9 = 0xF2;
    tmp->startmarker10 = 0xF3;
    tmp->startmarker11 = 0xF4;
    tmp->version = VERSION;
    tmp->minor_version = MINOR_VERSION;
    tmp->irq_priority_order = IRQ_PRIORITY_ORDER;
    tmp->filesize = sizeof(RecorderDataType);
    
    tmp->maxEvents = EVENT_BUFFER_SIZE;
    
    tmp->debugMarker0 = 0xF0F0F0F0;
    tmp->ObjectPropertyTable.NumberOfObjectClasses = NCLASSES;
    tmp->ObjectPropertyTable.ObjectPropertyTableSizeInBytes = DynObjTableSize;
    tmp->ObjectPropertyTable.NumberOfObjectsPerClass[0] = NQueue;
    tmp->ObjectPropertyTable.NumberOfObjectsPerClass[1] = NSemaphore;
    tmp->ObjectPropertyTable.NumberOfObjectsPerClass[2] = NMutex;
    tmp->ObjectPropertyTable.NumberOfObjectsPerClass[3] = NTask;
    tmp->ObjectPropertyTable.NumberOfObjectsPerClass[4] = NISR;
    tmp->ObjectPropertyTable.NameLengthPerClass[0] = NameLenQueue;
    tmp->ObjectPropertyTable.NameLengthPerClass[1] = NameLenSemaphore;
    tmp->ObjectPropertyTable.NameLengthPerClass[2] = NameLenMutex;
    tmp->ObjectPropertyTable.NameLengthPerClass[3] = NameLenTask;
    tmp->ObjectPropertyTable.NameLengthPerClass[4] = NameLenISR;
    tmp->ObjectPropertyTable.TotalPropertyBytesPerClass[0] = PropertyTableSizeQueue;
    tmp->ObjectPropertyTable.TotalPropertyBytesPerClass[1] = PropertyTableSizeSemaphore;
    tmp->ObjectPropertyTable.TotalPropertyBytesPerClass[2] = PropertyTableSizeMutex;
    tmp->ObjectPropertyTable.TotalPropertyBytesPerClass[3] = PropertyTableSizeTask;
    tmp->ObjectPropertyTable.TotalPropertyBytesPerClass[4] = PropertyTableSizeISR;
    tmp->ObjectPropertyTable.StartIndexOfClass[0] = StartIndexQueue;
    tmp->ObjectPropertyTable.StartIndexOfClass[1] = StartIndexSemaphore;
    tmp->ObjectPropertyTable.StartIndexOfClass[2] = StartIndexMutex;
    tmp->ObjectPropertyTable.StartIndexOfClass[3] = StartIndexTask;
    tmp->ObjectPropertyTable.StartIndexOfClass[4] = StartIndexISR;    
    tmp->debugMarker1 = 0xF1F1F1F1;
    tmp->SymbolTable.symTableSize = SYMBOL_TABLE_SIZE;
    tmp->SymbolTable.nextFreeSymbolIndex = 1;
#if (INCLUDE_FLOAT_SUPPORT == 1)
    tmp->exampleFloatEncoding = (float)1.0; /* otherwize already zero */
#endif
    tmp->debugMarker2 = 0xF2F2F2F2;    
    (void)strncpy(tmp->systemInfo, TRACE_DESCRIPTION, TRACE_DESCRIPTION_MAX_LENGTH);
    tmp->debugMarker3 = 0xF3F3F3F3;
    tmp->endmarker0 = 0x0A;
    tmp->endmarker1 = 0x0B;
    tmp->endmarker2 = 0x0C;
    tmp->endmarker3 = 0x0D;
    tmp->endmarker4 = 0x71;
    tmp->endmarker5 = 0x72;
    tmp->endmarker6 = 0x73;
    tmp->endmarker7 = 0x74;
    tmp->endmarker8 = 0xF1;
    tmp->endmarker9 = 0xF2;
    tmp->endmarker10 = 0xF3;
    tmp->endmarker11 = 0xF4;
    
    RecorderDataPtr = tmp;

    return (RecorderDataType*)RecorderDataPtr;
}

#endif

volatile int recorder_busy = 0;

char sprintfBuffer[150];

/* For debug printouts - the names of the object classes */
char OBJECTCLASSNAME[NCLASSES][10] =
{
        "QUEUE",
        "SEMAPHORE",
        "MUTEX",
        "TASK",
        "ISR"
};

/* Initialization of the handle mechanism, see e.g, xTraceGetObjectHandle */
objectHandleStackType objectHandleStacks =
{
        /* indexOfNextAvailableHandle */
        {
                0,
                NQueue,
                NQueue + NSemaphore,
                NQueue + NSemaphore + NMutex,
                NQueue + NSemaphore + NMutex + NTask
        },

        /* lowestIndexOfClass */
        {
                0,
                NQueue,
                NQueue + NSemaphore,
                NQueue + NSemaphore + NMutex,
                NQueue + NSemaphore + NMutex + NTask
        },

        /* highestIndexOfClass */
        {
                NQueue - 1,
                NQueue + NSemaphore - 1,
                NQueue + NSemaphore + NMutex - 1,
                NQueue + NSemaphore + NMutex + NTask - 1,
                NQueue + NSemaphore + NMutex + NTask + NISR - 1
        },
        {0},
        {0}
};


/* Used for internal state flags of objects */
uint8_t excludedFlags[(NEventCodes+NQueue+NSemaphore+NMutex+NTask) / 8 + 1];
uint8_t ifeFlags[NTask / 8 + 1];

/* Gives the last error message of the recorder. NULL if no error message. */
char* traceErrorMessage = NULL;

void* xTraceNextFreeEventBufferSlot(void)
{
    if (RecorderDataPtr->nextFreeIndex >= EVENT_BUFFER_SIZE)
    {
        vTraceError("Attempt to index outside event buffer!");
        return NULL;
    }
    return (void*)(&RecorderDataPtr->
                   eventData[RecorderDataPtr->nextFreeIndex*4]);
}

uint16_t uiIndexOfObject(objectHandleType objecthandle, uint8_t objectclass)
{
    if ((objectclass < NCLASSES) && (objecthandle > 0) && (objecthandle <= 
    RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectclass]))
    {
        return (uint16_t)(RecorderDataPtr->
            ObjectPropertyTable.StartIndexOfClass[objectclass] + 
            (RecorderDataPtr->
            ObjectPropertyTable.TotalPropertyBytesPerClass[objectclass] * 
            (objecthandle-1)));
    }
    
    vTraceError("Object table lookup with invalid object handle or object class!");    
    return 0;
}

/*******************************************************************************
 * Object handle system 
 * This provides a mechanism to assign each kernel object (tasks, queues, etc)
 * with a 1-byte handle, that is used to identify the object in the trace.
 * This way, only one byte instead of four is necessary to identify the object.
 * This allows for maximum 255 objects, of each object class, active at any
 * moment.
 * Note that zero is reserved as an error code and is not a valid handle.
 * 
 * In order to allow for fast dynamic allocation and release of object handles, 
 * the handles of each object class (e.g., TASK) are stored in a stack. When a 
 * handle is needed, e.g., on task creation, the next free handle is popped from 
 * the stack. When an object (e.g., task) is deleted, its handle is pushed back 
 * on the stack and can thereby be reused for other objects.
 * 
 * Since this allows for reuse of object handles, a specific handle (e.g, "8") 
 * may refer to TASK_X at one point, and later mean "TASK_Y". To resolve this, 
 * the recorder uses "Close events", which are stored in the main event buffer 
 * when objects are deleted and their handles are released. The close event 
 * contains the mapping between object handle and object name which was valid up
 * to this point in time. The object name is stored as a symbol table entry. 
 ******************************************************************************/

objectHandleType xTraceGetObjectHandle(traceObjectClass objectclass)
{
    static objectHandleType handle;
    static int indexOfHandle;

    indexOfHandle = objectHandleStacks.indexOfNextAvailableHandle[objectclass];
    if (objectHandleStacks.objectHandles[indexOfHandle] == 0)
    {
        /* Zero is used to indicate a never before used handle, i.e.,
           new slots in the handle stack. The handle slot needs to 
           be initialized here (starts at 1). */
        objectHandleStacks.objectHandles[indexOfHandle] = 
            (objectHandleType)(1 + indexOfHandle - 
            objectHandleStacks.lowestIndexOfClass[objectclass]);
    }

    handle = objectHandleStacks.objectHandles[indexOfHandle];
    
    if ( objectHandleStacks.indexOfNextAvailableHandle[objectclass] 
        > objectHandleStacks.highestIndexOfClass[objectclass] )
    {
        /* ERROR */
        switch(objectclass)
        {
        case TRACE_CLASS_TASK:
            vTraceError("Not enough TASK handles - increase NTask in trcConfig.h");         
            break;
        case TRACE_CLASS_ISR:
            vTraceError("Not enough ISR handles - increase NISR in trcConfig.h");         
            break;
        case TRACE_CLASS_SEMAPHORE:
            vTraceError("Not enough SEMAPHORE handles - increase NSemaphore in trcConfig.h");         
            break;
        case TRACE_CLASS_MUTEX:
            vTraceError("Not enough MUTEX handles - increase NMutex in trcConfig.h");         
            break;
        case TRACE_CLASS_QUEUE:
            vTraceError("Not enough QUEUE handles - increase NQueue in trcConfig.h");         
            break;
        default:
            vTraceError("Invalid object class.");
            break;
        }
        
        handle = 0; /* an invalid/anonymous handle - but the recorder is stopped now... */
    }
    else
    {
        int hndCount;
        objectHandleStacks.indexOfNextAvailableHandle[objectclass]++;
        
        hndCount = objectHandleStacks.indexOfNextAvailableHandle[objectclass] - 
            objectHandleStacks.lowestIndexOfClass[objectclass];
        
        if (hndCount > 
            objectHandleStacks.handleCountWaterMarksOfClass[objectclass])
        {
            objectHandleStacks.handleCountWaterMarksOfClass[objectclass] = 
                (objectHandleType)hndCount;
        }
    }

    return handle;
}

void vTraceFreeObjectHandle(traceObjectClass objectclass, objectHandleType handle)
{
    int indexOfHandle;

    /* Check that there is room to push the handle on the stack */
    if ( (objectHandleStacks.indexOfNextAvailableHandle[objectclass] - 1) < 
        objectHandleStacks.lowestIndexOfClass[objectclass] )
    {
        /* Error */
        vTraceError("Attempt to free more handles than allocated! (duplicate xTaskDelete or xQueueDelete?)");
    }
    else
    {
        objectHandleStacks.indexOfNextAvailableHandle[objectclass]--;
        indexOfHandle = objectHandleStacks.indexOfNextAvailableHandle[objectclass];
        objectHandleStacks.objectHandles[indexOfHandle] = handle;
    }

}

/*******************************************************************************
 * Objects Property Table 
 *
 * This holds the names and properties of the currently active objects, such as
 * tasks and queues. This is developed to support "dynamic" objects which might
 * be deleted during runtime. Their handles are only valid during their
 * lifetime, i.e., from create to delete, as they might be reused on later
 * create operations. When an object is deleted from the OPT, its data is moved
 * to the trace buffer and/or the symbol table.
 * When an object (task, queue, etc.) is created, it receives a handle, which
 * together with the object class specifies its location in the OPT. Thus,
 * objects of different types may share the same name and/or handle, but still
 * be independent objects.
 ******************************************************************************/

/*******************************************************************************
 * vTraceSetObjectName
 *
 * Registers the names of queues, semaphores and other kernel objects in the
 * recorder's Object Property Table, at the given handle and object class.
 ******************************************************************************/
void vTraceSetObjectName(traceObjectClass objectclass, 
                         objectHandleType handle, 
                         const char* name)
{
    static uint16_t idx;

    if (handle == 0)
    {
        vTraceError("Illegal handle (0) in vTraceSetObjectName.");   
        return;
    }
    
    switch(objectclass)
    {
        case TRACE_CLASS_TASK:
        case TRACE_CLASS_ISR:
        case TRACE_CLASS_SEMAPHORE:
        case TRACE_CLASS_MUTEX:
        case TRACE_CLASS_QUEUE:
        break;
    default:
        vTraceError("Illegal object class in vTraceSetObjectName");   
        break;            
    }

    if (handle > 
        RecorderDataPtr->ObjectPropertyTable.NumberOfObjectsPerClass[objectclass])
    {
        switch(objectclass)
        {
            case TRACE_CLASS_TASK:
            vTraceError("Not enough TASK handles - increase NTask in trcConfig.h");         
            break;
            case TRACE_CLASS_ISR:
            vTraceError("Not enough ISR handles - increase NISR in trcConfig.h");       
            break;
            case TRACE_CLASS_SEMAPHORE:
            vTraceError("Not enough SEMAPHORE handles - increase NSemaphore in trcConfig.h");
            break;
            case TRACE_CLASS_MUTEX:
            vTraceError("Not enough MUTEX handles - increase NMutex in trcConfig.h");
            break;
            case TRACE_CLASS_QUEUE:
            vTraceError("Not enough QUEUE handles - increase NQueue in trcConfig.h");
            break;
        }
    }
    else
    {
        idx = uiIndexOfObject(handle, objectclass);

        if (traceErrorMessage == NULL)
        {
            (void)strncpy((char*)&(RecorderDataPtr->ObjectPropertyTable.objbytes[idx]),
                    name,
                    RecorderDataPtr->ObjectPropertyTable.NameLengthPerClass[ objectclass ] );
#ifdef WIN32
            printf("vTraceSetObjectName(%d, %d, %s)\n", objectclass, handle, name);
#endif
        }
    }
}

traceLabel prvTraceOpenSymbol(const char* name, traceLabel userEventChannel)
{
    static uint16_t result;
    static uint8_t len;
    static uint8_t crc;
    len = 0;
    crc = 0;
    prvTraceGetChecksum(name, &crc, &len);

    trcCRITICAL_SECTION_BEGIN();
    result = prvTraceLookupSymbolTableEntry(name, crc, len, userEventChannel);
    if (!result)
    {
        result = prvTraceCreateSymbolTableEntry(name, crc, len, userEventChannel);
    }
    trcCRITICAL_SECTION_END();

    return result;
}

/*******************************************************************************
 * Supporting functions
 ******************************************************************************/

/*******************************************************************************
 * vTraceError
 *
 * Called by various parts in the recorder. Stops the recorder and stores a 
 * pointer to an error message, which is printed by the monitor task.
 * If you are not using the monitor task, you may use xTraceGetLastError() 
 * from your application to check if the recorder is OK.
 *
 * Note: If a recorder error is registered before vTraceStart is called, the 
 * trace start will be aborted. This can occur if any of the Nxxxx constants 
 * (e.g., NTask) in trcConfig.h is too small.
 ******************************************************************************/
void vTraceError(const char* msg)
{
    vTraceStop();
    if (traceErrorMessage == NULL)
    {
      traceErrorMessage = (char*)msg;
      (void)strncpy(RecorderDataPtr->systemInfo, 
          traceErrorMessage, 
          TRACE_DESCRIPTION_MAX_LENGTH);
      RecorderDataPtr->internalErrorOccured = 1;
    }
}

/******************************************************************************
 * prvCheckDataToBeOverwrittenForMultiEntryUserEvents
 *
 * This checks if the next event to be overwritten is a multi-entry user event, 
 * i.e., a USER_EVENT followed by data entries.
 * Such data entries do not have an event code at byte 0, as other events.
 * All 4 bytes are user data, so the first byte of such data events must 
 * not be interpreted as type field. The number of data entries following
 * a USER_EVENT is given in the event code of the USER_EVENT.
 * Therefore, when overwriting a USER_EVENT (when using in ringbuffer mode)
 * any data entries following must be replaced with NULL events (code 0).
 *
 * This is assumed to execute within a critical section...
 *****************************************************************************/

void prvCheckDataToBeOverwrittenForMultiEntryUserEvents(
    uint8_t nofEntriesToCheck)
{
    /* Generic "int" type is desired - should be 16 bit variable on 16 bit HW */
    unsigned int i = 0; 
    unsigned int e = 0;
    while (i < nofEntriesToCheck)
    {        
        e = RecorderDataPtr->nextFreeIndex + i;
        if ((RecorderDataPtr->eventData[e*4] > USER_EVENT) && 
            (RecorderDataPtr->eventData[e*4] < USER_EVENT + 16))
        {
            uint8_t nDataEvents = (uint8_t)(RecorderDataPtr->eventData[e*4] - USER_EVENT);
            if ((e + nDataEvents) < RecorderDataPtr->maxEvents)
            {
                (void)memset(& RecorderDataPtr->eventData[e*4], 0, 4 + 4 * nDataEvents);
            }
        }
        i++;
    }
}

/*******************************************************************************
 * prvTraceUpdateCounters
 *
 * Updates the index of the event buffer.
 ******************************************************************************/
void prvTraceUpdateCounters(void)
{
    if (RecorderDataPtr->recorderActive == 0)
    {
        return;
    }
    
    RecorderDataPtr->numEvents++;

    RecorderDataPtr->nextFreeIndex++;
    
    if (RecorderDataPtr->nextFreeIndex >= EVENT_BUFFER_SIZE)
    {   
#if (RECORDER_STORE_MODE == STORE_MODE_RING_BUFFER)       
        RecorderDataPtr->bufferIsFull = 1;
        RecorderDataPtr->nextFreeIndex = 0;
#else
        vTraceStop();
#endif
    }

#if (RECORDER_STORE_MODE == STORE_MODE_RING_BUFFER)
    prvCheckDataToBeOverwrittenForMultiEntryUserEvents(1);
#endif
    
#ifdef STOP_AFTER_N_EVENTS
#if (STOP_AFTER_N_EVENTS > -1)
    if (RecorderDataPtr->numEvents >= STOP_AFTER_N_EVENTS)
    {
        vTraceStop();
    }
#endif
#endif
}
    
/******************************************************************************
 * prvTraceGetDTS
 *
 * Returns a differential timestamp (DTS), i.e., the time since
 * last event, and creates an XTS event if the DTS does not fit in the
 * number of bits given. The XTS event holds the MSB bytes of the DTS.
 *
 * The parameter param_maxDTS should be 0xFF for 8-bit dts or 0xFFFF for
 * events with 16-bit dts fields.
 *****************************************************************************/
uint16_t prvTraceGetDTS(uint16_t param_maxDTS)
{
    static uint32_t old_timestamp = 0;
    XTSEvent* xts = 0;
    uint32_t dts = 0;
    uint32_t timestamp = 0;

    if (RecorderDataPtr->frequency == 0)
    {
        /* If HWTC_PERIOD is mapped to the timer reload register,
        such as in the Cortex M port, it is not initialized before
        FreeRTOS has been started. We therefore store the frequency
        of the timer at the first timestamped event after the 
        scheduler has started. (Note that this function is called
        also by vTraceStart and uiTraceStart, which might be
        called before the scheduler has been started.) */

#if (SELECTED_PORT == PORT_Win32)    
        RecorderDataPtr->frequency = 100000;
#elif (SELECTED_PORT == PORT_HWIndependent)    
        RecorderDataPtr->frequency = configTICK_RATE_HZ;
#else        
        if (xTaskGetSchedulerState() != 0) /* Has the scheduler started? */
        {            
            RecorderDataPtr->frequency = 
                 (uint32_t)HWTC_PERIOD * (uint32_t)configTICK_RATE_HZ / (uint32_t)HWTC_DIVISOR; 
        }
#endif
    }

    /**************************************************************************
    * The below statements read the timestamp from the timer port module.
    * If necessary, whole seconds are extracted using division while the rest
    * comes from the modulo operation.
    **************************************************************************/
    
    uiTracePortGetTimeStamp(&timestamp);

    /***************************************************************************
    * This condition is only for the Win32 port, since it does not use the tick
    * count but instead only HWTC_COUNT (from the performance counter).
    * Without this condition, you sometimes get a negative dts value (converted
    * into a very large unsiged value) when the performance counter wraps 
    * around. In other "normal" ports also using the FreeRTOS tick counter, this 
    * condition can not occur and therefore has no impact.
    ***************************************************************************/
    if (timestamp < old_timestamp)
    {
        timestamp += RecorderDataPtr->frequency;
    }


	dts = timestamp - old_timestamp;
    old_timestamp = timestamp;
    
    if (RecorderDataPtr->frequency > 0)
    {
        /* Check if dts > 1 second */
        if (dts > RecorderDataPtr->frequency)
        {
            /* More than 1 second has passed */
            RecorderDataPtr->absTimeLastEventSecond += dts / RecorderDataPtr->frequency;
            /* The part that is not an entire second is added to absTimeLastEvent */
            RecorderDataPtr->absTimeLastEvent += dts % RecorderDataPtr->frequency;
        }
        else
            RecorderDataPtr->absTimeLastEvent += dts;
        
        /* Check if absTimeLastEvent >= 1 second */
        if (RecorderDataPtr->absTimeLastEvent >= RecorderDataPtr->frequency)
        {
            /* RecorderDataPtr->absTimeLastEvent is more than or equal to 1 second, but always less than 2 seconds */
            RecorderDataPtr->absTimeLastEventSecond++;
            RecorderDataPtr->absTimeLastEvent -= RecorderDataPtr->frequency;
            /* RecorderDataPtr->absTimeLastEvent is now less than 1 second */
        }
    }
    else
    {
        /* Special case if the recorder has not yet started (frequency may be uninitialized, i.e., zero) */
        RecorderDataPtr->absTimeLastEvent = timestamp;
    }

    /* If the dts (time since last event) does not fit in event->dts (only 8 or 16 bits) */
    if (dts > param_maxDTS)
    {
        /* Create an XTS event (eXtended TimeStamp) containing the higher dts bits*/
        xts = (XTSEvent*) xTraceNextFreeEventBufferSlot();

        if (xts != NULL)
        {
            if (param_maxDTS == 0xFFFF)
            {
                xts->type = XTS16;
                xts->xts_16 = (uint16_t)((dts / 0x10000) & 0xFFFF);
                xts->xts_8 = 0;
            }
            else if (param_maxDTS == 0xFF)
            {
                xts->type = XTS8;
                xts->xts_16 = (uint16_t)((dts / 0x100) & 0xFFFF);
                xts->xts_8 = (uint8_t)((dts / 0x1000000) & 0xFF);
            }
            else
            {
                vTraceError("Bad param_maxDTS in prvTraceGetDTS");
            }
            prvTraceUpdateCounters();
        }
    }

    return (uint16_t)(dts % (param_maxDTS + 1));
}

/*******************************************************************************
 * prvTraceLookupSymbolTableEntry
 *
 * Find an entry in the symbol table, return 0 if not present.
 *
 * The strings are stored in a byte pool, with four bytes of "meta-data" for
 * every string.
 * byte 0-1: index of next entry with same checksum (for fast lookup).
 * byte 2-3: reference to a symbol table entry, a label for vTracePrintF
 * format strings only (the handle of the destination channel).
 * byte 4..(4 + length): the string (object name or user event label), with
 * zero-termination
 ******************************************************************************/
traceLabel prvTraceLookupSymbolTableEntry(const char* name, 
                                          uint8_t crc6, 
                                          uint8_t len, 
                                          traceLabel chn)
{
    uint16_t i = RecorderDataPtr->SymbolTable.latestEntryOfChecksum[ crc6 ];

    while (i != 0)
    {
        if (RecorderDataPtr->SymbolTable.symbytes[i + 2] == (chn & 0x00FF))
        {
            if (RecorderDataPtr->SymbolTable.symbytes[i + 3] == (chn / 0x100))
            {
                if (RecorderDataPtr->SymbolTable.symbytes[i + 4 + len] == '\0')
                {
                    if (strncmp((char*)(& RecorderDataPtr->SymbolTable.symbytes[i + 4]), name, len) == 0)
                    {
                        break; /* found */
                    }
                }
            }
        }
        i = (uint16_t)(RecorderDataPtr->SymbolTable.symbytes[i] + (RecorderDataPtr->SymbolTable.symbytes[i + 1] * 0x100));
    }
    return i;
}

/*******************************************************************************
 * prvTraceCreateSymbolTableEntry
 *
 * Creates an entry in the symbol table, independent if it exists already.
 *
 * The strings are stored in a byte pool, with four bytes of "meta-data" for
 * every string.
 * byte 0-1: index of next entry with same checksum (for fast lookup).
 * byte 2-3: reference to a symbol table entry, a label for vTracePrintF
 * format strings only (the handle of the destination channel).
 * byte 4..(4 + length): the string (object name or user event label), with
 * zero-termination
 ******************************************************************************/
uint16_t prvTraceCreateSymbolTableEntry(const char* name, 
                                        uint8_t crc6, 
                                        uint8_t len, 
                                        traceLabel channel)
{
    uint16_t ret = 0;
    if (RecorderDataPtr->SymbolTable.nextFreeSymbolIndex + len + 4 >= SYMBOL_TABLE_SIZE)
    {
        vTraceError("Symbol table full. Increase SYMBOL_TABLE_SIZE in trcConfig.h");
        ret = 0;        
    }
    else
    {

        RecorderDataPtr->SymbolTable.symbytes
            [ RecorderDataPtr->SymbolTable.nextFreeSymbolIndex] = 
            (uint8_t)(RecorderDataPtr->SymbolTable.latestEntryOfChecksum[ crc6 ] & 0x00FF);

        RecorderDataPtr->SymbolTable.symbytes
            [ RecorderDataPtr->SymbolTable.nextFreeSymbolIndex + 1] = 
            (uint8_t)(RecorderDataPtr->SymbolTable.latestEntryOfChecksum[ crc6 ] / 0x100);

        RecorderDataPtr->SymbolTable.symbytes
            [ RecorderDataPtr->SymbolTable.nextFreeSymbolIndex + 2] = 
            (uint8_t)(channel & 0x00FF);

        RecorderDataPtr->SymbolTable.symbytes
            [ RecorderDataPtr->SymbolTable.nextFreeSymbolIndex + 3] = 
            (uint8_t)(channel / 0x100);

        /* set name (bytes 4...4+len-1) */
        (void)strncpy((char*)&( RecorderDataPtr->SymbolTable.symbytes
            [ RecorderDataPtr->SymbolTable.nextFreeSymbolIndex + 4] ), name, len);

        /* Set zero termination (at offest 4+len) */
        RecorderDataPtr->SymbolTable.symbytes
            [RecorderDataPtr->SymbolTable.nextFreeSymbolIndex + 4 + len] = '\0';

        /* store index of entry (for return value, and as head of LL[crc6]) */
        RecorderDataPtr->SymbolTable.latestEntryOfChecksum
            [ crc6 ] = (uint16_t)RecorderDataPtr->SymbolTable.nextFreeSymbolIndex;
        
        RecorderDataPtr->SymbolTable.nextFreeSymbolIndex += (len + 5);
        
        ret = (uint16_t)(RecorderDataPtr->SymbolTable.nextFreeSymbolIndex - 
            (len + 5));
    }
    
    return ret;
}


/*******************************************************************************
 * prvTraceGetChecksum
 *
 * Calculates a simple 6-bit checksum from a string, used to index the string 
 * for fast symbol table lookup.
 ******************************************************************************/
void prvTraceGetChecksum(const char *pname, uint8_t* pcrc, uint8_t* plength)
{
   unsigned char c;
   int length = 0;
   int crc = 0;
   if ( pname != (const char *) 0 )
   {
      for ( ; (c = *pname++) != '\0'; )
      {
         crc += c;
         length++;
      }
   }
   *pcrc = (uint8_t)(crc % 64);
   *plength = (uint8_t)length;
}

#endif