1 /*
2                Language Independent Runtime Library
3 
4 	Copyright 1983-2003 by Green Hills Software,Inc.
5 
6      This program is the property of Green Hills Software, Inc,
7      its contents are proprietary information and no part of it
8      is to be disclosed to anyone except employees of Green Hills
9      Software, Inc., or as agreed in writing signed by the President
10      of Green Hills Software, Inc.
11 */
12 
13 /*
14  * Target Driven Timer-Based Profiling Framework:
15  *  - This file provides sample implementations of a timer-interrupt driven
16  *  profiler using the debug server supported MANPROF system call.
17  *  - To use timer-based profiling:
18  *   1) provide implementations of the following low-level macros for your
19  *      board (see ppc_manprf.h, arm_manprf.h for examples) :
20  *       TICKS_PER_SEC
21  *       TIMER_INIT
22  *       GET_EPC
23  *       CLEAR_TIMER_INTERRUPT
24  *       ENABLE_TIMER_INTERRUPT
25  *   2) rebuild ind_manprf.o(libsys.a).
26  *   3) relink your standalone application using the -timer_profile driver
27  *      option (or "-u __ghs_manprf_timer_handler" linker option).
28  *   4) enable profiling in MULTI before running your application.
29  */
30 #if defined(EMBEDDED) && defined(__ELF)
31 
32 #include "indsyscl.h"
33 #include "indos.h"
34 #include "ind_exit.h"
35 #include <stdint.h>
36 
37 #if defined(__THUMB) && !defined(__THUMB2_AWARE)
38 /* This module should not be compiled in Thumb mode */
39 #pragma ghs nothumb
40 #endif	/* __THUMB */
41 
42 /* extern prototypes (needed by ppc_manprf.h) */
43 #if defined(__ppc)
44 #if defined(__SPE__)    || defined(__ppc440)   || defined(__ppc440ep) ||     \
45     defined(__ppc440gx) || defined(__ppc440gr)
46 /* Force the interrupt handler to be 16-byte aligned. */
47 #pragma alignfunc(16)
48 #endif
49 #endif
50 __interrupt void __ghs_manprf_timer_handler(void);
51 
52 /* Pull in the low-level (asm) macro implementations */
53 #if defined(__ppc)
54 #include "ppc_manprf.h"
55 #elif defined(__mips)
56 #include "mip_manprf.h"
57 #elif defined(__ARM)
58 #include "arm_manprf.h"
59 #elif defined(__ColdFire)
60 #include "coldfire_manprf.h"
61 #elif defined(__ADSPBLACKFIN__)
62 #include "bf_manprf.h"
63 #else
64 /* you will need to implement the following items
65  *  for your processor / board. see ppc_manprf.h
66  *  and arm_manprf.h for examples */
67 #define NO_BUILD_MANPRF 1
68 #endif
69 
70 #if !defined(NO_BUILD_MANPRF)
71 
72 static void __ghs_manprf_end(void);
73 
74 /* FREQUENCY: number of times per second the PC is sampled.
75  *  default: 60 Hz. you may adjust this as necessary. */
76 #define 		FREQUENCY 	60
77 /* PROFBUFSIZE: number of samples the static PC sample buffer holds.
78  *  default: 120 samples.  you may adjust this as necessary according
79  *  to your memory resources and/or frequency at which you want to
80  *  send the profiling information to the debug server. */
81 #define		 	PROFBUFSIZE 	120
82 
83 #if __PTR_BIT == 64
84 typedef uint64_t address;
85 #else
86 typedef uint32_t address;
87 #endif
88 /* timer_value: number of cycles between timer interrupts. */
89 static 	int 		timer_value = 1;
90 /* PC Sample Buffer:
91  *  sample_count: number of used entries in profile_buffer PC sample buffer */
92 static 	int          	sample_count = 0;
93 /*  profile_buffer: PC sample static buffer */
94 static 	address		profile_buffer[PROFBUFSIZE];
95 
96 
97 /***************************************************************************
98  * __ghs_manprf_init() is called from __ghs_ind_crt1() when this module
99  *  is linked into the program.
100  *  - registers an 'at exit' callback
101  *  - compute the value to use in the counter/compare or decrementer
102  *  - initialize the timer
103  *  - initialize profiling
104  *  - enable the timer interrupt
105  ***************************************************************************/
106 #pragma ghs startnoinline
__ghs_manprf_init(void)107 void __ghs_manprf_init(void)
108 {
109     /* Request that this variable be allocated to a register so only
110      *  the register case need be handled by asm macros */
111     register unsigned int counter;
112     /* register with low level _exit() */
113     static struct __GHS_AT_EXIT gae;
114     if (!gae.func) {
115 	gae.func = __ghs_manprf_end;
116 	__ghs_at_exit(&gae);
117     }
118     /* Calculate clock cycle delay required to produce a timer interrupt at
119      * specified frequency in Hz. */
120     counter = timer_value = TICKS_PER_SEC() / FREQUENCY;
121     /* Set initial count and compare values (or decrementer) */
122     TIMER_INIT(counter);
123     /* Call SYSCALL_MANPROF with buf=NULL, cnt=frequency of sample rate,
124      * to initialize sample frequency. */
125     __ghs_syscall(SYSCALL_MANPROF, NULL, FREQUENCY);
126     /* Enable the timer interrupt. */
127     ENABLE_TIMER_INTERRUPT();
128 }
129 #pragma ghs endnoinline
130 
131 /***************************************************************************
132  * flush_buffer() sends the PC sample buffer to the debug server using
133  *  system call emulation.
134  *  - invoke SYSCALL_MANPROF to send the PC samples to the debug server.
135  *  - clear the PC sample buffer.
136  ***************************************************************************/
flush_buffer(void)137 static void flush_buffer(void)
138 {
139     /* send profile count data to debug server using syscall emulation */
140     __ghs_syscall(SYSCALL_MANPROF, profile_buffer, sample_count);
141     /* clear the PC sample buffer */
142     sample_count = 0;
143 }
144 
145 /***************************************************************************
146  * __ghs_manprf_end() sends any remaining PC samples at program termination.
147  ***************************************************************************/
__ghs_manprf_end(void)148 static void __ghs_manprf_end(void)
149 {
150     if (sample_count > 0) flush_buffer();
151 }
152 
153 /***************************************************************************
154  * __ghs_manprf_timer_handler() is the timer interrupt handler.
155  *  you should include code that jumps to this handler from within the
156  *  appropriate interrupt vector entry.
157  *  - collect PC samples
158  *  - when buffer is full, send data to the debug server using system call
159  *     emulation.
160  *  - reset the timer interrupt.
161  ***************************************************************************/
162 #if defined(__ppc)
163 #if defined(__SPE__)    || defined(__ppc440)   || defined(__ppc440ep) ||     \
164     defined(__ppc440gx) || defined(__ppc440gr)
165 /* Force the interrupt handler to be 16-byte aligned. */
166 #pragma alignfunc(16)
167 #endif
168 #endif
__ghs_manprf_timer_handler(void)169 __interrupt void __ghs_manprf_timer_handler(void)
170 {
171     /* General Notes:
172      *  Your target may or may not have separate vectors per each
173      *   interrupt.  Here we assume that the timer is the only interrupt
174      *   handled by this ISR.
175      *  Normally during an ISR, you may want to re-enable interrupts after
176      *   saving critical state.  Here, we do not re-enable interrupts
177      *   (until return).
178      */
179 
180     /* make sure this gets into a register so that only
181      *  the register case need be handled by (asm) macros */
182     register unsigned int counter;
183     /* sample the current program counter */
184     profile_buffer[sample_count++] = GET_EPC();
185     /* if we have filled the PC sample buffer, send its contents to the
186      *  debug server */
187     if (sample_count >= PROFBUFSIZE) flush_buffer();
188     /* reset the decrementer/counter so that the timer interrupt fires
189      *  at the desired time interval */
190     counter = timer_value;
191     /* re-initialize the timer (or decrementer) */
192     TIMER_INIT(counter);
193     /* clear the timer interrupt so it can fire again */
194     CLEAR_TIMER_INTERRUPT();
195 }
196 
197 #endif /* !defined(NO_BUILD_MANPRF) */
198 
199 #endif	/* EMBEDDED && __ELF */
200