Sunteți pe pagina 1din 26

II. 3.

Faciliti pentru monitorizare i debug


Functii trace
Prin configurari adecvate in FreeRTOSconfig.h se pot activa functii de trace
acestea sunt apelate n anumite contexte, indicnd dac anumite
evenimente au avut loc
pot fi scrise se programator implementand servicii specifice.
Macro definitie

Observatii

traceTASK_INCREMENT_TICK()

Apelata din intreurperea tick kernel.

traceTASK_SWITCHED_OUT()

Apelata nainte de a preda procesorul unui task dup o replanificare.


pxCurrentTCB conine handlerul la taskul care pred procesorul.

traceTASK_SWITCHED_IN()

Apelata dupa ce procesorul este predat unui task, la replanificare.


pxCurrentTCB conine handlerul la taskul care preia procesorul.

traceBLOCKING_ON_QUEUE_
RECEIVE(pxQueue)

Indica dac taskul in curs de excutie este pe cale sa treac in starea blocat la
citirea dintr-o coad goal sau solicitarea accesului la un semafor/mutex ocupat.

traceBLOCKING_ON_QUEUE_SEND
(pxQueue)

Indica dac taskul in curs de excutie este pe cale sa treac in starea blocat la
scrierea ntr-o coad plin.

traceGIVE_MUTEX_RECURSIVE(pxMutex)

Apelata din xSemaphoreGiveRecursive().

traceQUEUE_CREATE()

Apelata din xQueueCreate() succes

traceQUEUE_CREATE_FAILED()

Apelata din xQueueCreate() esec (memorie heap insuficienta).

traceCREATE_MUTEX()

Apelata din xSemaphoreCreateMutex() caz mutexul creat cu succes.

traceCREATE_MUTEX_FAILED()

Apelata din xSemaphoreCreateMutex() la esec (memorie heap insuficienta)

traceGIVE_MUTEX_RECURSIVE(pxMutex)

Apelata din xSemaphoreGiveRecursive() pe succes

traceGIVE_MUTEX_RECURSIVE_
FAILED(pxMutex)

Apelata din xSemaphoreGiveRecursive() esec (taskul solicitanr nu a fost


proprietarul mutexului).

traceTAKE_MUTEX_RECURSIVE(pxMutex)

Apelata din xQueueTakeMutexRecursive().

traceCREATE_COUNTING_SEMAPHORE()

Apelata din xSemaphoreCreateCounting() succes

traceCREATE_COUNTING_SEMAPHORE_
FAILED()

Apelata din xSemaphoreCreateCounting() la esec (memorie heap insuficienta)

traceQUEUE_SEND(pxQueue)

Apelata din xQueueSend(), xQueueSendToFront(), xQueueSendToBack(),, etc


succes

traceQUEUE_SEND_FAILED(pxQueue)

Apelata din xQueueSend(), xQueueSendToFront(), xQueueSendToBack(), esec


(coada plina)

traceQUEUE_RECEIVE(pxQueue)

Apelata din xQueueReceive() , etc succes

traceQUEUE_RECEIVE_FAILED()

Apelata din xQueueReceive(), etc - n caz de esec coad goal.

traceQUEUE_PEEK()

Apelata din xQueuePeek()

traceQUEUE_SEND_FROM_ISR(pxQueue)

Apelata din xQueueSendFromISR() - succes.

traceQUEUE_SEND_FROM_ISR_
FAILED(pxQueue)

Apelata din xQueueSendFromISR() esec (coada plina).

traceQUEUE_RECEIVE_FROM_
ISR(pxQueue)

Apelata din xQueueReceiveFromISR() - succes.

traceQUEUE_RECEIVE_FROM_ISR_
FAILED(pxQueue)

Apelata din xQueueReceiveFromISR() n caz de esec coad goal.

traceQUEUE_DELETE(pxQueue)

Apelata din vQueueDelete().

traceTASK_CREATE(pxTask)

Apelata din xTaskCreate() daca taskul este creat cu succes.

traceTASK_CREATE_FAILED()

Apelata din xTaskCreate() dac taskul nu este creat cu success, din cauz ca nu
exista memorie heap suficient

traceTASK_DELETE(pxTask)

Apelata din vTaskDelete().

traceTASK_DELAY_UNTIL()

Apelata din vTaskDelayUntil().

traceTASK_DELAY()

Apelata din vTaskDelay().

traceTASK_PRIORITY_SET(pxTask,
uxNewPriority)

Apelata din vTaskPrioritySet().

traceTASK_SUSPEND(pxTask)

Apelata din vTaskSuspend().

traceTASK_RESUME(pxTask)

Apelata din vTaskResume().

traceTASK_RESUME_FROM_ISR(pxTask)

Apelata din xTaskResumeFromISR().

! O variant simpl de extragere informatii pentru debug, fr a folosi direct


variabilele gestionate de FreeRTOS.

Implementarea functiilor proprii de trace:


Functiile trace trebuie sa fie cu timp foarte mic de executie, fara apeluri la sistem.

Cel mai frecvent sunt folosite pentru debug:


o numrarea apelurilor pentru a indica de cate ori evenimentul semnalat apare
in STR i realizarea unor statistici - interesant in special pentru evenimente
greu de stabilit a priori cand au loc
 exemplu: traceTASK_SWITCHED_IN() poate indica numarul
de comutri spre un anumit task, ultimele 10 momente la care a
avut loc comutarea, etc.
o dac respectivul eveniment nu ar fi trebuit s apar (e un simptom de situaie
nedorit) obinerea unor informaii suplimentare despre acest caz
 exemplu traceTASK_CREATE_FAILED().

Exemple:
TraceTASK_SWITCHED_OUT() i traceTASK_SWITCHED_IN() sunt apelate de
vTaskSwitchContext.
In FreeRTOSconfig.h:
#define configUSE_TRACE_FACILITY

#define traceTASK_SWITCHED_OUT() vMainMineOut()


#define traceTASK_SWITCHED_IN() vMainMineIn()

1. numarare comutri:
In main.c:
unsigned int nr_switch_T2;
void vMainMineIn(void){
if ( handT2 == pxCurrentTCB )
}

nr_switch_T2++;

2. numarare comutari de la T2 la T1
unsigned int nr_switch_T2, ies_T2;
void vMainMineOut(void){
if ( handT2 == pxCurrentTCB) ies_T2=1;
}
void vMainMineIn(void){
if ( handT1 == pxCurrentTCB && ies_T2==1 )
ies_T2=0;
}

nr_switch_T2++;

3. numarare comutari de la T2 la T2
unsigned int nr_switch_T2, ies_T2;
void vMainMineOut(void){
if ( handT2 == pxCurrentTCB) ies_T2=1;
}
void vMainMineIn(void){
if ( handT2 == pxCurrentTCB && ies_T2==1 )
ies_T2=0;
}

nr_switch_T2++;

4. semnalare cazuri cu mai multe de 2 comutari in cadrul aceluiasi ciclu for


Modificare:
void Task2(void *params) {
portTickType xLastTime = xTaskGetTickCount();//la primul frame valoarea este 0
for (;;){
flagT2++;
no_ticks=xTaskGetTickCount();
if (xLastTime!=no_ticks){varaux2=varaux2+1;xLastTime=no_ticks;}
if (no_ticks <50) { vParTestSetLED( 14, 0 );}
if (no_ticks ==50) {
if (uxCurrentNumberOfTasks==2)
xTaskCreate(Task1, (signed portCHAR *) "T1",
configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY+1, &handT1);
vParTestSetLED( 14, 1 );
}
if (no_ticks >50){
vParTestToggleLED(14);
vTaskDelayUntil(&xLastTime,35);
vParTestToggleLED(14);
vTaskDelayUntil(&xLastTime,35);
vParTestToggleLED(14);
vTaskDelayUntil(&xLastTime,35);
vParTestToggleLED(14);
vTaskDelayUntil(&xLastTime,35);
}
}}

unsigned int nr_cazuri, com_in_for_T2=1, flagT2=0;


void vMainMineOut(void){
static unsigned int urmflagT2=-1;
if ( handT2 == pxCurrentTCB) {
urmflag++;
if(urmflagT2!=flagT2) com_in_for_T2++;
}
}

Monitorizarea executiei folosind vTaskSetApplicationTaskTag


Anumite servicii pot fi simplu particularizate pentru fiecare taskurile aplicatiei,
folosind taguri:
void vTaskSetApplicationTaskTag( xTaskHandle xTask,
pdTASK_HOOK_CODE pxTagValue );
- asociaza tagul de valoare pxTagValue taskului cu handler xTask
- daca pxTagValue este numele unei functii, aceasta va fi interpretata ca un
hook ce va putea fi apelat prin xTaskCallApplicationTaskHook()
portBASE_TYPE xTaskCallApplicationTaskHook( xTaskHandle xTask,
void * pvParameter );
- apeleaz hook-ul asociat taskului de handler xTask
xTask
Handler la taskul caruia ii corespunde hookul.
NULL pentru taskul in curs de executie.
pvParameter
Valoarea pasata functiei hook .

Cerinta: configUSE_APPLICATION_TASK_TAG = 1

Exemplu de folosire:
#define traceTASK_SWITCHED_OUT() xTaskCallApplicationTaskHook( pxCurrentTCB, 0 )
static portBASE_TYPE prvExampleTaskHook( void * pvParameter) {
..
return 0;
}

void vATask( void *pvParameters )


{
vTaskSetApplicationTaskTag( NULL, ( void * ) 1 ); // Asigneaza valoarea tag 1
for( ;; ) {
// corpul taskului
}}

void vAnotherTask( void *pvParameters ) {


vTaskSetApplicationTaskTag( NULL, prvExampleTaskHook );
// Asociaz prvExampleTaskHook ca hook
for( ;; ){
/* corpul taskului. */
}}

Exemplu
In FreeRTOSConfig.h:
#define configUSE_APPLICATION_TASK_TAG 1
#define traceTASK_SWITCHED_IN() xTaskCallApplicationTaskHook( pxCurrentTCB, 0 )

In main:
unsigned int nr_cazuri, com_in_for_T2=1, flagT2=0;
static portBASE_TYPE HookT2_In( void * pvParameter) {
static unsigned int urmflagT2=-1;
if(urmflagT2!=flagT2) com_in_for_T2++;
return 0;
}
void Task1(void *params) {
portTickType xLastTime = xTaskGetTickCount()-1;
TaskSetApplicationTaskTag( NULL, 0 );
for (;;){
no_ticks=xTaskGetTickCount();
if (xLastTime!=no_ticks){varaux1=varaux1+1;xLastTime=no_ticks;}
vParTestToggleLED(15); vTaskDelayUntil(&xLastTime,15);
}}

void Task2(void *params) {


portTickType xLastTime = xTaskGetTickCount();//la primul frame valoarea este 0
vTaskSetApplicationTaskTag( NULL, HookT2_In );
for (;;){
flagT2++;
no_ticks=xTaskGetTickCount();
if (xLastTime!=no_ticks){varaux2=varaux2+1;xLastTime=no_ticks;}
if (no_ticks <50) { vParTestSetLED( 14, 0 );}
if (no_ticks ==50) {
if (uxCurrentNumberOfTasks==2)
xTaskCreate(Task1, (signed portCHAR *)
"T1",configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY+1,
&handT1);
vParTestSetLED( 14, 1 );}
if (no_ticks >50){
vParTestToggleLED(14);vTaskDelayUntil(&xLastTime,35);
vParTestToggleLED(14);vTaskDelayUntil(&xLastTime,35);
vParTestToggleLED(14);vTaskDelayUntil(&xLastTime,35);
}}
}

Informatii despre starea planficatorului:


portBASE_TYPE xTaskGetSchedulerState( void );
- returneaz starea planificatorului
valori posibile:

taskSCHEDULER_NOT_STARTED,
taskSCHEDULER_RUNNING,
taskSCHEDULER_SUSPENDED

sau direct cu variabilele gestionate de FreeRTOS.


static volatile signed portBASE_TYPE xSchedulerRunning
static volatile unsigned portBASE_TYPE uxSchedulerSuspended

Determinarea timpilor de executie a taskurilor

Timpii de executie asociati unui task in cadrul unui frame sau pe portiuni de cod pot
fi determinati
+ folosind functiile trace cu traceTASK_SWITCHED_IN()/
traceTASK_SWITCHED_OUT()
+ gestionand un numarator hardware pentru o rezolutie bun.
+ marcand inceputul /sfarsitul secventei dorite printr-un flag

Aceste servicii sunt deja oferite foarte simplu de FreeRTOS care poate evidenia
timpii de execuie ai proceselor ca valoare absoluta i % din timp total de execuie.

Necesar: configGENERATE_RUN_TIME_STATS = 1
Atunci vTaskStartScheduler() apeleaz portCONFIGURE_TIMER_
FOR_RUN_TIME_STATS(), pentru a seta o rezolutie mai mic dect cea a
tickului la calcularea timpilor de execuie.
Setarea este vizibil prin portGET_RUN_TIME_COUNTER_VALUE()

void vTaskGetRunTimeStats( portCHAR *pcWriteBuffer );


pcWriteBuffer

Un buffer n care sunt memorati timpii de executie


(valori ASCII) spatiu rezervat anterior
(recomandare 40 B per task).

Ofer statistica realizat de FreeRTOS.


Nerecomandat pentru utilizare frecvent (ci doar pentru debug), deoarece dezactiveaz ISR
pe durata executiei sale

Exemplu
In FreeRTOSConfig.h
#define configTICK_RATE_HZ

( ( portTickType ) 100 )

#define configGENERATE_RUN_TIME_STATS
1
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() ( 0UL )
#define portGET_RUN_TIME_COUNTER_VALUE() 5000UL

In Main.c se modifica
void Task2(void *params) {
portTickType xLastTime = xTaskGetTickCount();//la primul frame valoarea este 0
vTaskSetApplicationTaskTag( NULL, HookT2_In );
for (;;){
flagT2++;
no_ticks=xTaskGetTickCount();
if (xLastTime!=no_ticks){varaux2=varaux2+1;xLastTime=no_ticks;}
if (no_ticks <50) { vParTestSetLED( 14, 0 );}
if (no_ticks ==50) {
if (uxCurrentNumberOfTasks==2)
xTaskCreate(Task1, (signed portCHAR *)
"T1",configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY+1,
&handT1);
vParTestSetLED( 14, 1 );

}
if (no_ticks >50){
uxHighWaterMark = uxTaskGetStackHighWaterMark( NULL );
vTaskGetRunTimeStats( &myBuffer[0] );
vParTestToggleLED(14);
vTaskDelayUntil(&xLastTime,35);
}
}
}

La prima oprire pe breakpoint, in buffer apare:


T2 timp exec 5000 msec, grad ocupare 100%; T1 0, 0%
(Taskul T1 dupa ce a fost creat, nu a primit inca procesorul. S-au scurs 50 tickuri)

Monitorizarea utilizrii stivei (avertizare n caz de depire)


Trebuie setat configCHECK_FOR_STACK_OVERFLOW pe o valoare nenul.

Se poate particulariza o rutin Hook apelat implicit la fiecare detectare a unei


depsiri de stiv (parametrii pasai indic taskul cu probleme):
void vApplicationStackOverflowHook( xTaskHandle *pxTask, signed
portCHAR *pcTaskName );

- programatorul poate oferi servicii specifice, in caz de eroare.

Metode prin care FreeRTOS verific depirea de stiv:


1. Verificare rapid:
(trebuie configCHECK_FOR_STACK_OVERFLOW = 1)
Depirea este verificat doar la comutarea de context din starea running (atunci se
salveaza contextual de lucru n stiv i e un caz nefavorabil). Daca pointerul de
varf de stiva este in domeniul nepermis, este apelat hook-ul de depire stiv.

2. Verificare suplimentar:
(trebuie setat configCHECK_FOR_STACK_OVERFLOW = 2)
La crearea taskului, stiva este intializat cu un marcator specific.
La comutarea din starea in curs de executie se verific dac ultimii 16 octeti
introdusi in stiv nu sunt modificati de proces. In caz de eroare, este apelat hook-ul
de depire stiv.
Nu garanteaz detectarea tuturor depirilor, dar este destul de bun.

Alte servicii API utile


unsigned portBASE_TYPE uxTaskGetStackHighWaterMark( xTaskHandle
xTask );
- returneaz nr itemi liberi n stiva; 0 dac a aprut depire de stiv

Exercitii
1. Schitati in C (pentru FreeRTOS) urmatorul program:
o Sunt activate task T1 (prioritate P1), task T2 (prioritate P2), task T3 (prioritate
P3) si task T2 (prioritate 2) apoi este activat planificatorul.
o T1 suspenda T2 daca acesta nu este deja suspendat si intra in blocare pentru 2
sec.
o T2 intra in blocare pentru 2 sec
o T3 scoate T2 din suspended daca acesta este deja suspendat si intra in blocare
pentru 2 sec.
a) P1= 2, P2=1, P3=3:
b) P1= 4, P2=1, P3=2:
c) P1= 3, P2=2, P3=1:
Indicati o varianta prin care sa ilustrati experimental daca T3 este preemtat si cate
preemtari au loc pe un interval de timp specificat.
Frame-ul are lungime de 500 msec.

2. Schitati in C (pentru FreeRTOS) urmatorul program:


o Sunt activate task T1 (prioritate P1), task T2 (prioritate P2), apoi este activat
planificatorul.
A) T1 schimba la fiecare ciclu for impar prioritatea lui T2 pe 1 si la fiecare
ciclu for par pe 3, apoi trece in blocare pentru 2 sec. T2 este un task
periodic, de perioada 2sec, care citeste PORTB14.
P1=2, P2=1
B) T1 citeste la fiecare 2sec PORTB14 (setat ca intrare). Cand depisteaza la
doua citiri succesive o valoare schimbata, forteaz prioritatea lui T2 pe 3,
altfel ii foreaz prioritatea 1.
T2 este un task periodic, de perioada 2sec, care citeste PORTB14.
Presupunand ca frame-ul are lungimea 500 msec, ca secventa asigurata pe
PORTB14 este 101011100100 (valoare constanta pe durata unui frame)
indicati ordinea de executie a taskurilor pentru modul preemtiv si pentru
mod nepreemtiv.

Folosind functii trace adecvate asignati variabila nr_change_prior cu numrul


total de schimbri de prioritate solicitate de taskurile executate.
Indicati o varianta prin care sa ilustrati experimental daca T1 este preemtat si cate
preemtari au loc pe un interval de timp.
3. Folosind functii trace adecvate, asignati variabila nr_com cu numarul total de
comutri ntre taskuri realizate n interiorul frame-urilor (se vor elimina comutrile
realizate la nceput de frame, ce au loc prin activarea implicit a planificatorului pe
mod preemtiv).

4. Schitati in C (pentru FreeRTOS) urmatorul program:


o Sunt activate task T1 si task T2, apoi este activat planificatorul.
o T1 citeste la fiecare 2 sec PORTB14 (setat ca intrare), iar T2 citeste la fiecare 3
sec PORTB13 (setat ca intrare).
o Se cere validarea vApplicationIdleHook si vApplicationTickHook. Aceste
rutine trebuie doar sa indice prin incrementarea unor variabile globale de cate
ori au fost executate.
Presupunand ca frame-ul are lungimea 1 sec, indicati ordinea de executie a taskurilor
si care este numrul asteptat de executii pentru rutinele hook validate (dac acest
lucru este posibil) dup 12sec de la inceperea executiei aplicatiei, pentru modul
preemtiv (cu configIDLE_SHOULD_YIELD = 0 sau =1) si pentru mod nepreemtiv,
considernd
a) prioritatea lui T1 este 1, prioritatea lui T2 este 2;
b) prioritatea lui T1 este 2, prioritatea lui T2 este 1.
Indicati o varianta software care s permit verificarea secventei de execuie schiate
prin rularea aplicaiei (de exemplu prin folosirea unor variabile globale specifice).
Explicai procedura folosit.

5. Indicati cum poate fi evidentiat intr-o aplicatie compatibil FreeRTOS:


- numrul taskurilor active;
- dac planificatorul este activ;
- ce task este n curs de executie,

S-ar putea să vă placă și