]> git.sur5r.net Git - freertos/blob - FreeRTOS-Plus/FreeRTOS-Plus-Trace/Include/trcHooks.h
Update PIC32 demo application to remove reliance on PLIB functions.
[freertos] / FreeRTOS-Plus / FreeRTOS-Plus-Trace / Include / trcHooks.h
1 /*******************************************************************************\r
2  * FreeRTOS+Trace v2.3.0 Recorder Library\r
3  * Percepio AB, www.percepio.com\r
4  *\r
5  * trcHooks.h\r
6  *\r
7  * The kernel integration hooks for FreeRTOS (v7.1.0 or later). This file should\r
8  * be included in the end of FreeRTOSConfig.h, together with:\r
9  *\r
10  * #define configUSE_TRACE_FACILITY 1\r
11  *\r
12  * NOTE: \r
13  * For IAR Embedded Workbench for ARM, you need to have a preprocessor condition\r
14  * on the include, to except it from the assembler step which otherwise give\r
15  * compile-time errors.\r
16  *\r
17  * #ifdef __ICCARM__\r
18  *       #include "percepio/Include/trcHooks.h"\r
19  * #endif\r
20  * \r
21  * Terms of Use\r
22  * This software is copyright Percepio AB. The recorder library is free for\r
23  * use together with Percepio products. You may distribute the recorder library\r
24  * in its original form, including modifications in trcPort.c and trcPort.h\r
25  * given that these modification are clearly marked as your own modifications\r
26  * and documented in the initial comment section of these source files. \r
27  * This software is the intellectual property of Percepio AB and may not be \r
28  * sold or in other ways commercially redistributed without explicit written \r
29  * permission by Percepio AB.\r
30  *\r
31  * Disclaimer \r
32  * The trace tool and recorder library is being delivered to you AS IS and \r
33  * Percepio AB makes no warranty as to its use or performance. Percepio AB does \r
34  * not and cannot warrant the performance or results you may obtain by using the \r
35  * software or documentation. Percepio AB make no warranties, express or \r
36  * implied, as to noninfringement of third party rights, merchantability, or \r
37  * fitness for any particular purpose. In no event will Percepio AB, its \r
38  * technology partners, or distributors be liable to you for any consequential, \r
39  * incidental or special damages, including any lost profits or lost savings, \r
40  * even if a representative of Percepio AB has been advised of the possibility \r
41  * of such damages, or for any claim by any third party. Some jurisdictions do \r
42  * not allow the exclusion or limitation of incidental, consequential or special \r
43  * damages, or the exclusion of implied warranties or limitations on how long an \r
44  * implied warranty may last, so the above limitations may not apply to you.\r
45  *\r
46  * FreeRTOS+Trace is available as Free Edition and in two premium editions.\r
47  * You may use the premium features during 30 days for evaluation.\r
48  * Download FreeRTOS+Trace at http://www.percepio.com/products/downloads/\r
49  *\r
50  * Copyright Percepio AB, 2012.\r
51  * www.percepio.com\r
52  ******************************************************************************/\r
53 \r
54 #ifndef TRCHOOKS_H\r
55 #define TRCHOOKS_H\r
56 \r
57 #if (configUSE_TRACE_FACILITY == 1)\r
58 \r
59     #include "trcUser.h"\r
60         \r
61     #undef INCLUDE_xTaskGetSchedulerState\r
62     #define INCLUDE_xTaskGetSchedulerState 1\r
63         \r
64     #undef INCLUDE_xTaskGetCurrentTaskHandle\r
65     #define INCLUDE_xTaskGetCurrentTaskHandle 1\r
66         \r
67 #if !defined INCLUDE_READY_EVENTS || INCLUDE_READY_EVENTS == 1\r
68     /* Called for each task that becomes ready */\r
69     #undef traceMOVED_TASK_TO_READY_STATE\r
70     #define traceMOVED_TASK_TO_READY_STATE( pxTCB ) \\r
71     vTraceStoreTaskReady((unsigned char)pxTCB->uxTaskNumber);\r
72 #endif\r
73         \r
74     /* Called on each OS tick. Will call uiPortGetTimestamp to make sure it is called at least once every OS tick. */\r
75     #undef traceTASK_INCREMENT_TICK\r
76     #define traceTASK_INCREMENT_TICK( xTickCount ) \\r
77       if (uxSchedulerSuspended == ( unsigned portBASE_TYPE ) pdTRUE || uxMissedTicks == 0) {extern uint32_t uiTraceTickCount; uiTraceTickCount++; uiTracePortGetTimeStamp(0);}\r
78 \r
79     /* Called on each task-switch */\r
80     #undef traceTASK_SWITCHED_IN\r
81     #define traceTASK_SWITCHED_IN() \\r
82       vTraceStoreTaskswitch();\r
83 \r
84     /* Called on vTaskSuspend */\r
85     #undef traceTASK_SUSPEND\r
86     #define traceTASK_SUSPEND( pxTaskToSuspend ) \\r
87       vTraceStoreKernelCall(TASK_SUSPEND, TRACE_CLASS_TASK, pxTaskToSuspend->uxTaskNumber); \\r
88       vTraceSetTaskInstanceFinished((uint8_t)pxTaskToSuspend->uxTaskNumber);\r
89 \r
90     /* Called on vTaskDelay - note the use of FreeRTOS variable xTicksToDelay */\r
91     #undef traceTASK_DELAY\r
92     #define traceTASK_DELAY() \\r
93       portENTER_CRITICAL(); \\r
94       vTraceStoreKernelCallWithNumericParamOnly(TASK_DELAY, (uint16_t)xTicksToDelay);\\r
95       vTraceSetTaskInstanceFinished((uint8_t)pxCurrentTCB->uxTaskNumber);\\r
96       portEXIT_CRITICAL();\r
97 \r
98     /* Called on vTaskDelayUntil - note the use of FreeRTOS variable xTimeToWake */\r
99     #undef traceTASK_DELAY_UNTIL\r
100     #define traceTASK_DELAY_UNTIL() \\r
101       portENTER_CRITICAL(); \\r
102       vTraceStoreKernelCallWithNumericParamOnly(TASK_DELAY_UNTIL, (uint16_t)xTimeToWake); \\r
103       vTraceSetTaskInstanceFinished((uint8_t)pxCurrentTCB->uxTaskNumber); \\r
104       portEXIT_CRITICAL();\r
105 \r
106 #ifndef INCLUDE_OBJECT_DELETE\r
107 #define INCLUDE_OBJECT_DELETE 1\r
108 #endif\r
109 \r
110 #if (INCLUDE_OBJECT_DELETE == 1)\r
111     /* Called on vTaskDelete */\r
112     #undef traceTASK_DELETE\r
113     #define traceTASK_DELETE( pxTaskToDelete ) \\r
114       trcCRITICAL_SECTION_BEGIN(); \\r
115       vTraceStoreKernelCall(EVENTGROUP_DELETE + TRACE_CLASS_TASK, TRACE_CLASS_TASK, pxTaskToDelete->uxTaskNumber); \\r
116       vTraceStoreObjectNameOnCloseEvent((objectHandleType)pxTaskToDelete->uxTaskNumber, TRACE_CLASS_TASK); \\r
117       vTraceStoreObjectPropertiesOnCloseEvent((objectHandleType)pxTaskToDelete->uxTaskNumber, TRACE_CLASS_TASK); \\r
118       vTraceSetPriorityProperty(TRACE_CLASS_TASK, (objectHandleType)pxTaskToDelete->uxTaskNumber, (uint8_t)pxTaskToDelete->uxPriority); \\r
119       vTraceSetObjectState(TRACE_CLASS_TASK, (objectHandleType)pxTaskToDelete->uxTaskNumber, TASK_STATE_INSTANCE_NOT_ACTIVE); \\r
120       vTraceFreeObjectHandle(TRACE_CLASS_TASK, (objectHandleType)pxTaskToDelete->uxTaskNumber); \\r
121       trcCRITICAL_SECTION_END();\r
122 #endif\r
123 \r
124     /* Called on vTaskCreate */\r
125     #undef traceTASK_CREATE\r
126     #define traceTASK_CREATE( pxNewTCB ) \\r
127       if (pxNewTCB != NULL){ \\r
128           pxNewTCB->uxTaskNumber = xTraceGetObjectHandle(TRACE_CLASS_TASK); \\r
129           vTraceSetObjectName(TRACE_CLASS_TASK, (objectHandleType)pxNewTCB->uxTaskNumber, (char*)pxNewTCB->pcTaskName); \\r
130           vTraceSetPriorityProperty(TRACE_CLASS_TASK, (objectHandleType)pxNewTCB->uxTaskNumber, (uint8_t)pxNewTCB->uxPriority); \\r
131           vTraceStoreKernelCall(EVENTGROUP_CREATE + TRACE_CLASS_TASK, TRACE_CLASS_TASK, pxNewTCB->uxTaskNumber);\\r
132       }\r
133 \r
134     /* Called in vTaskCreate, if it fails (typically if the stack can not be allocated) */\r
135     #undef traceTASK_CREATE_FAILED\r
136     #define traceTASK_CREATE_FAILED() \\r
137       portENTER_CRITICAL();\\r
138       vTraceStoreKernelCall(EVENTGROUP_FAILED_CREATE + TRACE_CLASS_TASK, TRACE_CLASS_TASK, 0); \\r
139       portEXIT_CRITICAL();\r
140 \r
141     /* Called in xQueueCreate, and thereby for all other object based on queues, such as semaphores. */\r
142     #undef traceQUEUE_CREATE\r
143     #define traceQUEUE_CREATE( pxNewQueue )\\r
144         portENTER_CRITICAL(); \\r
145         pxNewQueue->ucQueueNumber = xTraceGetObjectHandle(TraceObjectClassTable[pxNewQueue->ucQueueType]);\\r
146         vTraceStoreKernelCall(EVENTGROUP_CREATE + TraceObjectClassTable[pxNewQueue->ucQueueType], TraceObjectClassTable[pxNewQueue->ucQueueType], pxNewQueue->ucQueueNumber); \\r
147         vTraceSetObjectState(TraceObjectClassTable[pxNewQueue->ucQueueType], pxNewQueue->ucQueueNumber, 0); \\r
148         portEXIT_CRITICAL();\r
149 \r
150     /* Called in xQueueCreate, if the queue creation fails */\r
151     #undef traceQUEUE_CREATE_FAILED\r
152     #define traceQUEUE_CREATE_FAILED( queueType ) \\r
153         portENTER_CRITICAL();\\r
154         vTraceStoreKernelCall((uint8_t)(EVENTGROUP_FAILED_CREATE + TraceObjectClassTable[queueType]), TraceObjectClassTable[queueType], 0); \\r
155         portEXIT_CRITICAL();\r
156     \r
157     /* Called in xQueueCreateMutex, and thereby also from xSemaphoreCreateMutex and xSemaphoreCreateRecursiveMutex */\r
158     #undef traceCREATE_MUTEX\r
159     #define traceCREATE_MUTEX( pxNewQueue ) \\r
160       portENTER_CRITICAL();\\r
161       pxNewQueue->ucQueueNumber = xTraceGetObjectHandle(TRACE_CLASS_MUTEX); \\r
162       vTraceStoreKernelCall(EVENTGROUP_CREATE + TraceObjectClassTable[pxNewQueue->ucQueueType], TraceObjectClassTable[pxNewQueue->ucQueueType], pxNewQueue->ucQueueNumber); \\r
163       vTraceSetObjectState(TraceObjectClassTable[pxNewQueue->ucQueueType], pxNewQueue->ucQueueNumber, 0); \\r
164       portEXIT_CRITICAL();\r
165 \r
166     /* Called in xQueueCreateMutex when the operation fails (when memory allocation fails) */\r
167     #undef traceCREATE_MUTEX_FAILED\r
168     #define traceCREATE_MUTEX_FAILED() \\r
169         portENTER_CRITICAL();\\r
170         vTraceStoreKernelCall(EVENTGROUP_FAILED_CREATE + TRACE_CLASS_MUTEX, TRACE_CLASS_MUTEX, 0);\\r
171         portEXIT_CRITICAL();\r
172 \r
173     /* Called when the Mutex can not be given, since not holder */\r
174     #undef traceGIVE_MUTEX_RECURSIVE_FAILED\r
175     #define traceGIVE_MUTEX_RECURSIVE_FAILED( pxMutex ) \\r
176         portENTER_CRITICAL();\\r
177         vTraceStoreKernelCall(EVENTGROUP_FAILED_SEND + TRACE_CLASS_MUTEX, TRACE_CLASS_MUTEX, pxMutex->ucQueueNumber); \\r
178         portEXIT_CRITICAL();\r
179 \r
180     /* Called when a message is sent to a queue */\r
181     #undef traceQUEUE_SEND\r
182     #define traceQUEUE_SEND( pxQueue ) \\r
183       vTraceStoreKernelCall(EVENTGROUP_SEND + TraceObjectClassTable[pxQueue->ucQueueType], TraceObjectClassTable[pxQueue->ucQueueType], pxQueue->ucQueueNumber); \\r
184       if (TraceObjectClassTable[pxQueue->ucQueueType] == TRACE_CLASS_MUTEX){\\r
185           vTraceSetObjectState(TraceObjectClassTable[pxQueue->ucQueueType], (uint8_t)pxQueue->ucQueueNumber, (uint8_t)0); \\r
186       }else{\\r
187           vTraceSetObjectState(TraceObjectClassTable[pxQueue->ucQueueType], (uint8_t)pxQueue->ucQueueNumber, (uint8_t)(pxQueue->uxMessagesWaiting + 1)); \\r
188       }\r
189 \r
190     /* Called when a message failed to be sent to a queue (timeout) */\r
191     #undef traceQUEUE_SEND_FAILED\r
192     #define traceQUEUE_SEND_FAILED( pxQueue ) \\r
193       portENTER_CRITICAL();\\r
194       vTraceStoreKernelCall(EVENTGROUP_FAILED_SEND + TraceObjectClassTable[pxQueue->ucQueueType], TraceObjectClassTable[pxQueue->ucQueueType], pxQueue->ucQueueNumber); \\r
195       portEXIT_CRITICAL();\r
196 \r
197     /* Called when the task is blocked due to a send operation on a full queue */\r
198     #undef traceBLOCKING_ON_QUEUE_SEND\r
199     #define traceBLOCKING_ON_QUEUE_SEND( pxQueue ) \\r
200       portENTER_CRITICAL();\\r
201       vTraceStoreKernelCall(EVENTGROUP_BLOCK_ON_SEND + TraceObjectClassTable[pxQueue->ucQueueType], TraceObjectClassTable[pxQueue->ucQueueType], pxQueue->ucQueueNumber); \\r
202       portEXIT_CRITICAL();\r
203                 \r
204     /* Called when a message is received from a queue */\r
205     #undef traceQUEUE_RECEIVE\r
206     #define traceQUEUE_RECEIVE( pxQueue ) \\r
207       vTraceStoreKernelCall(EVENTGROUP_RECEIVE + TraceObjectClassTable[pxQueue->ucQueueType], TraceObjectClassTable[pxQueue->ucQueueType], pxQueue->ucQueueNumber); \\r
208       if (TraceObjectClassTable[pxQueue->ucQueueType] == TRACE_CLASS_MUTEX){\\r
209           extern volatile void * volatile pxCurrentTCB; \\r
210           vTraceSetObjectState(TraceObjectClassTable[pxQueue->ucQueueType], (objectHandleType)pxQueue->ucQueueNumber, (objectHandleType)uxTaskGetTaskNumber((xTaskHandle)pxCurrentTCB)); /*For mutex, store the new owner rather than queue length */ \\r
211       }else{\\r
212           vTraceSetObjectState(TraceObjectClassTable[pxQueue->ucQueueType], (objectHandleType)pxQueue->ucQueueNumber, (uint8_t)(pxQueue->uxMessagesWaiting - 1)); \\r
213       }\r
214         \r
215     /* Called when the task is blocked due to a receive operation on an empty queue */\r
216     #undef traceBLOCKING_ON_QUEUE_RECEIVE\r
217     #define traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue ) \\r
218       portENTER_CRITICAL(); \\r
219       vTraceStoreKernelCall(EVENTGROUP_BLOCK_ON_RECEIVE + TraceObjectClassTable[pxQueue->ucQueueType], TraceObjectClassTable[pxQueue->ucQueueType], pxQueue->ucQueueNumber); \\r
220       if (TraceObjectClassTable[pxQueue->ucQueueType] != TRACE_CLASS_MUTEX){\\r
221           extern volatile void * volatile pxCurrentTCB; \\r
222           vTraceSetTaskInstanceFinished((objectHandleType)uxTaskGetTaskNumber((xTaskHandle)pxCurrentTCB)); \\r
223       }\\r
224       portEXIT_CRITICAL();\r
225 \r
226     /* Called on xQueuePeek */\r
227     #undef traceQUEUE_PEEK\r
228     #define traceQUEUE_PEEK( pxQueue ) \\r
229         vTraceStoreKernelCall(EVENTGROUP_PEEK + TraceObjectClassTable[pxQueue->ucQueueType], TraceObjectClassTable[pxQueue->ucQueueType], pxQueue->ucQueueNumber);\r
230 \r
231     /* Called when a receive operation on a queue fails (timeout) */\r
232     #undef traceQUEUE_RECEIVE_FAILED\r
233     #define traceQUEUE_RECEIVE_FAILED( pxQueue ) \\r
234       portENTER_CRITICAL(); \\r
235       vTraceStoreKernelCall(EVENTGROUP_FAILED_RECEIVE + TraceObjectClassTable[pxQueue->ucQueueType],  TraceObjectClassTable[pxQueue->ucQueueType], pxQueue->ucQueueNumber); \\r
236       portEXIT_CRITICAL();\r
237         \r
238     /* Called when a message is sent from interrupt context, e.g., using xQueueSendFromISR */\r
239     #undef traceQUEUE_SEND_FROM_ISR\r
240     #define traceQUEUE_SEND_FROM_ISR( pxQueue ) \\r
241       vTraceStoreKernelCall(EVENTGROUP_SEND_FROM_ISR + TraceObjectClassTable[pxQueue->ucQueueType], TraceObjectClassTable[pxQueue->ucQueueType], pxQueue->ucQueueNumber); \\r
242       vTraceSetObjectState(TraceObjectClassTable[pxQueue->ucQueueType], (objectHandleType)pxQueue->ucQueueNumber, (uint8_t)(pxQueue->uxMessagesWaiting + 1));\r
243 \r
244     /* Called when a message send from interrupt context fails (since the queue was full) */\r
245     #undef traceQUEUE_SEND_FROM_ISR_FAILED\r
246     #define traceQUEUE_SEND_FROM_ISR_FAILED( pxQueue ) \\r
247       vTraceStoreKernelCall(EVENTGROUP_FAILED_SEND_FROM_ISR + TraceObjectClassTable[pxQueue->ucQueueType], TraceObjectClassTable[pxQueue->ucQueueType], pxQueue->ucQueueNumber);\r
248 \r
249     /* Called when a message is received in interrupt context, e.g., using xQueueReceiveFromISR */\r
250     #undef traceQUEUE_RECEIVE_FROM_ISR\r
251     #define traceQUEUE_RECEIVE_FROM_ISR( pxQueue ) \\r
252       vTraceStoreKernelCall(EVENTGROUP_RECEIVE_FROM_ISR + TraceObjectClassTable[pxQueue->ucQueueType], TraceObjectClassTable[pxQueue->ucQueueType], pxQueue->ucQueueNumber); \\r
253       vTraceSetObjectState(TraceObjectClassTable[pxQueue->ucQueueType], (objectHandleType)pxQueue->ucQueueNumber, (uint8_t)(pxQueue->uxMessagesWaiting - 1));\r
254     \r
255     /* Called when a message receive from interrupt context fails (since the queue was empty) */\r
256     #undef traceQUEUE_RECEIVE_FROM_ISR_FAILED\r
257     #define traceQUEUE_RECEIVE_FROM_ISR_FAILED( pxQueue ) \\r
258       vTraceStoreKernelCall(EVENTGROUP_FAILED_RECEIVE_FROM_ISR + TraceObjectClassTable[pxQueue->ucQueueType], TraceObjectClassTable[pxQueue->ucQueueType], pxQueue->ucQueueNumber);\r
259 \r
260 #if (INCLUDE_OBJECT_DELETE == 1)\r
261     /* Called on vQueueDelete */\r
262     #undef traceQUEUE_DELETE\r
263     #define traceQUEUE_DELETE( pxQueue ) \\r
264     { \\r
265         portENTER_CRITICAL();\\r
266         vTraceStoreKernelCall(EVENTGROUP_DELETE + TraceObjectClassTable[pxQueue->ucQueueType], TraceObjectClassTable[pxQueue->ucQueueType], pxQueue->ucQueueNumber); \\r
267         vTraceStoreObjectNameOnCloseEvent((objectHandleType)pxQueue->ucQueueNumber, TraceObjectClassTable[pxQueue->ucQueueType]); \\r
268         vTraceStoreObjectPropertiesOnCloseEvent((objectHandleType)pxQueue->ucQueueNumber, TraceObjectClassTable[pxQueue->ucQueueType]); \\r
269         if (TraceObjectClassTable[pxQueue->ucQueueType] == TRACE_CLASS_MUTEX){ \\r
270             vTraceSetObjectState(TraceObjectClassTable[pxQueue->ucQueueType], (objectHandleType)pxQueue->ucQueueNumber, (objectHandleType)uxTaskGetTaskNumber((xTaskHandle)pxQueue->pxMutexHolder)); \\r
271         }else{ \\r
272             vTraceSetObjectState(TraceObjectClassTable[pxQueue->ucQueueType], (objectHandleType)pxQueue->ucQueueNumber, (uint8_t)uxQueueMessagesWaiting(pxQueue)); \\r
273         } \\r
274         vTraceFreeObjectHandle(TraceObjectClassTable[pxQueue->ucQueueType], (objectHandleType)pxQueue->ucQueueNumber); \\r
275         portEXIT_CRITICAL();\\r
276     }\r
277 #endif\r
278     \r
279     /* Called in vTaskPrioritySet */\r
280     #undef traceTASK_PRIORITY_SET\r
281     #define traceTASK_PRIORITY_SET( pxTask, uxNewPriority ) \\r
282       vTraceStoreKernelCallWithParam(TASK_PRIORITY_SET, TRACE_CLASS_TASK, pxTask->uxTaskNumber, uiTraceGetPriorityProperty(TRACE_CLASS_TASK, (uint8_t)pxTask->uxTaskNumber));\\r
283       vTraceSetPriorityProperty( TRACE_CLASS_TASK, (uint8_t)pxTask->uxTaskNumber, (uint8_t)uxNewPriority);\r
284 \r
285     /* Called in vTaskPriorityInherit, which is called by Mutex operations */\r
286     #undef traceTASK_PRIORITY_INHERIT\r
287     #define traceTASK_PRIORITY_INHERIT( pxTask, uxNewPriority ) \\r
288       vTraceStoreKernelCallWithParam(TASK_PRIORITY_INHERIT, TRACE_CLASS_TASK, pxTask->uxTaskNumber, uiTraceGetPriorityProperty(TRACE_CLASS_TASK, (uint8_t)pxTask->uxTaskNumber));\\r
289       vTraceSetPriorityProperty( TRACE_CLASS_TASK, (uint8_t)pxTask->uxTaskNumber, (uint8_t)uxNewPriority );\r
290 \r
291     /* Called in vTaskPriorityDisinherit, which is called by Mutex operations */\r
292     #undef traceTASK_PRIORITY_DISINHERIT\r
293     #define traceTASK_PRIORITY_DISINHERIT( pxTask, uxNewPriority ) \\r
294       vTraceStoreKernelCallWithParam(TASK_PRIORITY_DISINHERIT, TRACE_CLASS_TASK, pxTask->uxTaskNumber, uiTraceGetPriorityProperty(TRACE_CLASS_TASK, (uint8_t)pxTask->uxTaskNumber));\\r
295       vTraceSetPriorityProperty( TRACE_CLASS_TASK, (uint8_t)pxTask->uxTaskNumber, (uint8_t)uxNewPriority );\r
296 \r
297     /* Called in vTaskResume */\r
298     #undef traceTASK_RESUME\r
299     #define traceTASK_RESUME( pxTaskToResume ) \\r
300       vTraceStoreKernelCall(TASK_RESUME, TRACE_CLASS_TASK, pxTaskToResume->uxTaskNumber);\r
301 \r
302     /* Called in vTaskResumeFromISR */\r
303     #undef traceTASK_RESUME_FROM_ISR\r
304     #define traceTASK_RESUME_FROM_ISR( pxTaskToResume )\\r
305       vTraceStoreKernelCall(TASK_RESUME_FROM_ISR, TRACE_CLASS_TASK, pxTaskToResume->uxTaskNumber);\r
306 \r
307 #endif\r
308 #endif\r