1 /*
2 ANSI C Runtime Library
3
4 Copyright 1983-2009 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 /* ind_mprf.c: Machine Independent Call Count Profiling module. */
13
14 #include "inddef.h"
15 #include "ind_exit.h"
16 #include "ind_heap.h"
17 #include "ind_io.h"
18 #include "ind_thrd.h"
19 #include "indos.h"
20
21 /* This is the number of CNT structures we will allocate at one time. */
22 /* When we use up all the current CNT structures, we will allocate another */
23 /* set, and link them together in a linked list. */
24 #if defined(__V850) && (__PTR_BIT==16)
25 #define CNTCHUNKSIZE 128
26 #elif defined(__ARM)
27 #define CNTCHUNKSIZE 256
28 #else
29 #define CNTCHUNKSIZE 1024
30 #endif
31
32 #if __PTR_BIT <= 32
33 typedef struct {
34 int pc; /* This holds the pc of the function. */
35 int *countPtr; /* This points to the call count of the function. */
36 } CNT;
37 #else /* __PTR_BIT */
38 /* 64-bit data */
39
40 typedef struct {
41 char *pc; /* This holds the pc of the function. */
42 int *countPtr; /* This points to the call count of the function. */
43 } CNT;
44 #endif /* __PTR_BIT */
45
46 typedef struct CNT_List {
47 struct CNT_List *next; /* Pointer to the next list node. */
48 int numUsed; /* The number of array elements used. */
49 CNT counts[CNTCHUNKSIZE]; /* The array of count information. */
50 } CNT_List;
51
52 #if __PTR_BIT > 32
53 /*
54 * Use a packed array of 32-bit values to avoid padding that would occur
55 * with 64-bit alignment.
56 */
57 typedef struct {
58 unsigned int pc[2]; /* This holds the pc of the function. */
59 int countPtr; /* This points to the call count of the function. */
60 } CNT_packed;
61
62 typedef struct {
63 unsigned int pc[2];
64 } pc_overlay;
65
66 typedef struct CNT_List_packed {
67 struct CNT_List *next; /* Pointer to the next list node. */
68 int numUsed; /* The number of array elements used. */
69 CNT_packed counts[CNTCHUNKSIZE]; /* The array of count information. */
70 } CNT_List_packed;
71 #endif /* __PTR_BIT */
72
73 /* cntHead points at the head of our counts list. cntTail points at
74 * the tail of the list, since we will insert at the end. (We could insert
75 * at the beginning, but that will cause the output to appear in reverse order,
76 * which is different than the previous behavior, so I kept it in order).
77 * cntFreeList holds all the CNT_List structures that are no longer in use.
78 * This only happens when the list is cleared, or when we dump the list. When
79 * we dump the list, we allocate an extra CNT_List structure to help us make
80 * less calls to write().
81 */
82 static CNT_List *__cntHead__, *__cntTail__, *__cntFreeList__;
83 #ifdef __disable_thread_safe_extensions
84 static int in_mcount;
85 #endif
86
87 /* Macro for printing errors */
88 #define PERROR(msg) write(2, msg, sizeof(msg)-1);
89
90 /* File System I/O Calls to write the file to the host */
91 #if defined(__INTEGRITY) || defined(__INTEGRITY_SHARED_LIBS)
92 #define HOSTIO_CREAT hostio_creat
93 #define HOSTIO_WRITE write
94 #define HOSTIO_CLOSE close
95 #else
96 #define HOSTIO_CREAT __ghs_hostio_creat
97 #define HOSTIO_WRITE __ghs_hostio_write
98 #define HOSTIO_CLOSE __ghs_hostio_close
99 #endif
100
101 #define _0660 0x1b0 /* MISRA C does not allow octal constants */
102 /**
103 * Clears the call count information. This can be used as a command line
104 * procedure call from MULTI so that you can get the call counts for a
105 * specific interval in a run of a program. Just stop the program, clear
106 * the data, run to another point, and dump.
107 *
108 * @author Jimmy
109 */
__ghs_prof_clear_callcounts(void)110 void __ghs_prof_clear_callcounts(void)
111 {
112 CNT_List *p, *next;
113 int i;
114
115 for (p = __cntHead__; p; p = next) {
116 next = p->next;
117
118 /* Clear all the counts to 0 for this set. For good measure, also
119 * set the pc's to 0. */
120 for (i=0; i<p->numUsed; i++) {
121 p->counts[i].pc = 0;
122 *(p->counts[i].countPtr) = 0;
123 }
124
125 /* Now free this set by putting it on the free list. */
126 p->next = __cntFreeList__;
127 __cntFreeList__ = p;
128 }
129 __cntHead__ = 0;
130 __cntTail__ = 0;
131 }
132
133 /**
134 * Dumps the call count information. This function used to be written
135 * as if it were only called once. It trashed the data structures it used,
136 * and also locked up mcount by setting in_mcount and not unsetting it.
137 * But now, MULTI may call this function when it does a profdump, so I fixed
138 * it to behave properly. And I also cleared out all the obfuscated code in
139 * here. You should have seen this function before I rewrote it.
140 *
141 * @author Jimmy
142 */
__ghs_prof_dump_callcounts(void)143 void __ghs_prof_dump_callcounts(void)
144 {
145 #if __PTR_BIT <= 32
146 int h[3]; /* lpc, hpc, count */
147 #else /* __PTR_BIT */
148 int h[5]; /* lpc[0-1], hpc[2-3], count */
149 #endif /* __PTR_BIT */
150 int CNT_size;
151 int fd, totalUsed;
152 static const char monout[] = "mon.out";
153 CNT_List *p;
154
155 /* creat() and write() may have been compiled for profiling */
156 #ifdef __disable_thread_safe_extensions
157 in_mcount = 1;
158 #endif
159
160 for (totalUsed = 0, p = __cntHead__; p; p = p->next)
161 totalUsed += p->numUsed;
162
163 /* construct header for mon.out file */
164 #if __PTR_BIT <= 32
165 h[0] = h[1] = 0; /* no stochastic samples */
166 h[2] = totalUsed;
167 CNT_size = sizeof(CNT);
168 #else /* __PTR_BIT */
169 h[0] = h[1] = h[2] = h[3] = 0; /* no stochastic samples */
170 h[4] = totalUsed;
171 CNT_size = sizeof(CNT_packed);
172 #endif /* __PTR_BIT */
173
174 /* Write out the file. Walk the list of buffer chunks and write each one. */
175
176 /* Use hostio, since the file belongs on the host system, not the target
177 * system.
178 */
179 if ((fd = HOSTIO_CREAT(monout, _0660)) < 0) {
180 PERROR("mcount: could not create mon.out\n");
181 } else {
182 if (HOSTIO_WRITE(fd, h, sizeof(h)) != sizeof(h)) {
183 PERROR("mcount: could not write mon.out\n");
184 } else if (totalUsed > 0) {
185 int i, len;
186 #ifdef USE_STACK
187 #if __PTR_BIT <= 32
188 CNT_List local, *temp = &local;
189 #else /* __PTR_BIT */
190 /* 64-bit Code */
191 CNT_List_packed local, *temp = &local;
192 #endif /* __PTR_BIT */
193 #else
194 #if __PTR_BIT <= 32
195 /* 32-bit Code */
196 CNT_List *temp;
197
198 /* We need to allocate one CNT_List structure so that we can */
199 /* write our buffers out 1024 at a time instead of 1 at a time. */
200 if (__cntFreeList__) {
201 temp = __cntFreeList__;
202 __cntFreeList__ = __cntFreeList__->next;
203 } else {
204 /* Second argument to __ghs_calloc is alignment */
205 temp = (CNT_List *) __ghs_calloc(sizeof(CNT_List), 16);
206 }
207
208 #else /* __PTR_BIT */
209 /* 64-bit Code */
210 static CNT_List_packed *temp = NULL;
211 /* Only allocate once */
212 if( temp == NULL )
213 temp = (CNT_List_packed *) __ghs_calloc(sizeof(CNT_List_packed),
214 16 );
215 #endif /* __PTR_BIT */
216
217 if (!temp) {
218 PERROR("__ghs_prof_dump_callcounts: Could not allocate memory.\n");
219 return;
220 }
221 #endif
222
223 /* Now traverse our list of call count sets, and write */
224 /* them out one set of 1024 at a time. We will copy the */
225 /* sets to temp, with the modification that instead of */
226 /* the pointers to the count, we will fill temp with the */
227 /* actual counts instead. */
228 for (p = __cntHead__; p; p = p->next) {
229 for (i=0;i<p->numUsed;i++) {
230
231 #if __PTR_BIT <= 32
232 temp->counts[i].pc = p->counts[i].pc;
233 #else /* __PTR_BIT */
234 /* 64-bit Code */
235 {
236 pc_overlay *tpc;
237 tpc = (pc_overlay *)(&p->counts[i].pc);
238 temp->counts[i].pc[0] = tpc->pc[0];
239 temp->counts[i].pc[1] = tpc->pc[1];
240 }
241 #endif /* __PTR_BIT */
242 #if defined(__PTR_BIT) && __PTR_BIT < 32 && __PTR_BIT == __SHRT_BIT
243 temp->counts[i].countPtr = (int *)(short)(*p->counts[i].countPtr);
244 #else
245 #if __PTR_BIT <= 32
246 temp->counts[i].countPtr = (int *)(*p->counts[i].countPtr);
247 #else /* __PTR_BIT */
248 temp->counts[i].countPtr = (int)(*p->counts[i].countPtr);
249 #endif /* __PTR_BIT */
250 #endif
251 }
252 /* update the len based on the size */
253 len = p->numUsed * CNT_size;
254 /* Write out up to 1024 at a time. */
255 if (HOSTIO_WRITE(fd, (void *) temp->counts, len) != len) {
256 PERROR("mcount: could not write mon.out\n");
257 break;
258 }
259 }
260
261 #if __PTR_BIT <= 32
262 #ifndef USE_STACK
263 /* Put temp on the free list, since we are done with it. */
264 temp->next = __cntFreeList__;
265 __cntFreeList__ = temp;
266 #endif
267 #endif /* __PTR_BIT */
268 }
269 HOSTIO_CLOSE(fd);
270 }
271 #ifdef __disable_thread_safe_extensions
272 in_mcount = 0;
273 #endif
274 }
275
276 /**
277 * Updates the call count information.
278 *
279 * @param area A pointer to a memory location which holds the
280 * call count for this function. This location should
281 * hold 0 if this is the first time the function has
282 * been called.
283 * @param archead The address of the function.
284 * @author Jimmy
285 */
__ghs_indmcount(int * area,void (* archead)(void))286 void __ghs_indmcount(int *area, void (*archead)(void))
287 {
288 static int nomemory;
289 #ifdef __disable_thread_safe_extensions
290 /* This is not thread-safe; our intent is to prevent infinite recursion. */
291 if (in_mcount)
292 return;
293 in_mcount = 1;
294 #endif
295
296 if (nomemory)
297 return;
298 /* If the count is 0, that means we have not added this function to
299 * our list of counts. Otherwise we already have so all we have to do
300 * is increment the count. */
301 if (!*area) {
302 /* Lock only when changing the call count data structures */
303 __ghs_ProfileLock();
304 /* We need to add this function to our list. First check for
305 * unused slots in cntTail. */
306 if (!__cntTail__ || __cntTail__->numUsed == CNTCHUNKSIZE) {
307 /* Allocate a new set of CNT structures. Either this
308 * is the first set, or we used up all of the previous set. */
309 CNT_List *p;
310
311 /* Get one off the free list first, otherwise allocate one */
312 if (__cntFreeList__) {
313 p = __cntFreeList__;
314 __cntFreeList__ = __cntFreeList__->next;
315 p->next = 0;
316 p->numUsed = 0;
317 } else
318 p = (CNT_List *) __ghs_calloc(sizeof(CNT_List), 16);
319
320 /* Check for any errors. */
321 if (!p) {
322 PERROR("mcount: could not allocate memory\n");
323 __ghs_ProfileUnlock();
324 nomemory = 1;
325 return; /* LEAVE in_mcount SET, we're inactive now. */
326 } else {
327 static struct __GHS_AT_EXIT gae;
328 if (!gae.func) {
329 gae.func = __ghs_prof_dump_callcounts;
330 __ghs_at_exit(&gae);
331 }
332 }
333
334 /* Now insert the new set at the end of the list. */
335 if (__cntTail__) {
336 __cntTail__->next = p;
337 __cntTail__ = p;
338 } else {
339 __cntHead__ = __cntTail__ = p;
340 }
341 }
342 /* Add the function to our list. */
343 #if __PTR_BIT <= 32
344 __cntTail__->counts[__cntTail__->numUsed].pc = (int) archead;
345 #else /* __PTR_BIT */
346 __cntTail__->counts[__cntTail__->numUsed].pc = (char *)archead;
347 #endif /* __PTR_BIT */
348 __cntTail__->counts[__cntTail__->numUsed++].countPtr = (int *) area;
349
350 __ghs_ProfileUnlock();
351 }
352 ++*area;
353
354 #ifdef __disable_thread_safe_extensions
355 in_mcount = 0;
356 #endif
357 }
358