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